Skip to content

[changelog skip] Refactor: Immutable CodeBlocks #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ gemspec

gem "rake", "~> 12.0"
gem "rspec", "~> 3.0"
gem "stackprof"
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-support (3.10.0)
stackprof (0.2.16)

PLATFORMS
ruby

DEPENDENCIES
rake (~> 12.0)
rspec (~> 3.0)
stackprof
syntax_search!

BUNDLED WITH
Expand Down
7 changes: 6 additions & 1 deletion lib/syntax_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def self.call(source: , filename: , terminal: false, record_dir: nil)
blocks: blocks,
filename: filename,
terminal: terminal,
code_lines: search.code_lines,
invalid_type: invalid_type(source),
io: $stderr
).call
Expand Down Expand Up @@ -152,5 +153,9 @@ def self.invalid_type(source)
require_relative "syntax_search/code_line"
require_relative "syntax_search/code_block"
require_relative "syntax_search/code_frontier"
require_relative "syntax_search/code_search"
require_relative "syntax_search/display_invalid_blocks"
require_relative "syntax_search/around_block_scan"
require_relative "syntax_search/block_expand"
require_relative "syntax_search/parse_blocks_from_indent_line"

require_relative "syntax_search/code_search"
91 changes: 91 additions & 0 deletions lib/syntax_search/around_block_scan.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# frozen_string_literal: true
#
module SyntaxErrorSearch
# This class is useful for exploring contents before and after
# a block
#
# It searches above and below the passed in block to match for
# whatever criteria you give it:
#
# Example:
#
# def dog
# puts "bark"
# puts "bark"
# end
#
# scan = AroundBlockScan.new(
# code_lines: code_lines
# block: CodeBlock.new(lines: code_lines[1])
# )
#
# scan.scan_while { true }
#
# puts scan.before_index # => 0
# puts scan.after_index # => 3
#
# Contents can also be filtered using AroundBlockScan#skip
#
# To grab the next surrounding indentation use AroundBlockScan#scan_adjacent_indent
class AroundBlockScan
def initialize(code_lines: , block:)
@code_lines = code_lines
@orig_before_index = block.lines.first.index
@orig_after_index = block.lines.last.index
@skip_array = []
@after_array = []
@before_array = []
end

def skip(name)
@skip_array << name
self
end

def scan_while(&block)
@before_index = before_lines.reverse_each.take_while do |line|
next true if @skip_array.detect {|meth| line.send(meth) }

block.call(line)
end.reverse.first&.index

@after_index = after_lines.take_while do |line|
next true if @skip_array.detect {|meth| line.send(meth) }

block.call(line)
end.last&.index
self
end

def scan_adjacent_indent
before_indent = @code_lines[@orig_before_index.pred]&.indent || 0
after_indent = @code_lines[@orig_after_index.next]&.indent || 0

indent = [before_indent, after_indent].min
@before_index = before_index.pred if before_indent >= indent
@after_index = after_index.next if after_indent >= indent

self
end

def code_block
CodeBlock.new(lines: @code_lines[before_index..after_index])
end

def before_index
@before_index || @orig_before_index
end

def after_index
@after_index || @orig_after_index
end

private def before_lines
@code_lines[0...@orig_before_index]
end

private def after_lines
@code_lines[@orig_after_index.next..-1]
end
end
end
78 changes: 78 additions & 0 deletions lib/syntax_search/block_expand.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# frozen_string_literal: true
module SyntaxErrorSearch
# This class is responsible for taking a code block that exists
# at a far indentaion and then iteratively increasing the block
# so that it captures everything within the same indentation block.
#
# def dog
# puts "bow"
# puts "wow"
# end
#
# block = BlockExpand.new(code_lines: code_lines)
# .call(CodeBlock.new(lines: code_lines[1]))
#
# puts block.to_s
# # => puts "bow"
# puts "wow"
#
#
# Once a code block has captured everything at a given indentation level
# then it will expand to capture surrounding indentation.
#
# block = BlockExpand.new(code_lines: code_lines)
# .call(block)
#
# block.to_s
# # => def dog
# puts "bow"
# puts "wow"
# end
#
class BlockExpand
def initialize(code_lines: )
@code_lines = code_lines
end

def call(block)
if (next_block = expand_neighbors(block, grab_empty: true))
return next_block
end

expand_indent(block)
end

def expand_indent(block)
block = AroundBlockScan.new(code_lines: @code_lines, block: block)
.scan_adjacent_indent
.code_block

# Handle if/else/end case
if (next_block = expand_neighbors(block, grab_empty: false))
return next_block
else
return block
end
end

def expand_neighbors(block, grab_empty: true)
scan = AroundBlockScan.new(code_lines: @code_lines, block: block)
.skip(:hidden?)
.scan_while {|line| line.not_empty? && line.indent >= block.current_indent }

# Slurp up empties
if grab_empty
scan = AroundBlockScan.new(code_lines: @code_lines, block: scan.code_block)
.scan_while {|line| line.empty? || line.hidden? }
end

new_block = scan.code_block

if block.lines == new_block.lines
return nil
else
return new_block
end
end
end
end
Loading