diff options
author | schneems <[email protected]> | 2023-04-14 13:46:28 -0500 |
---|---|---|
committer | Hiroshi SHIBATA <[email protected]> | 2023-05-23 10:05:47 +0900 |
commit | aaf815c626816ccca227cb1f8f2a9b58f18f677a () | |
tree | df1851edb5ed016a1b41124b851b3bf7a6daef40 /lib/syntax_suggest | |
parent | b8bf0a52723322e469e94a4cb96286fd48e754ab (diff) |
[ruby/syntax_suggest] Refactor Scanner logic out of AroundBlockScan introduce history
AroundBlockScan started as a utility class that was meant to be used as a DSL for scanning and making new blocks. As logic got added to this class it became hard to reason about what exactly is being mutated when. I pulled the scanning logic out into it's own class which gives us a clean separation of concerns. This allowed me to remove a lot of accessors that aren't core to the logic provided by AroundBlockScan. In addition to this refactor the ScanHistory class can snapshot a scan. This allows us to be more aggressive with scans in the future as we can now snapshot and rollback if it didn't turn out the way we wanted. The change comes with a minor perf impact: before: 5.092678 0.104299 5.196977 ( 5.226494) after: 5.128536 0.099871 5.228407 ( 5.249542) This represents a 0.996x change in speed (where 1x would be no change and 2x would be twice as fast). This is a 0.38% decrease in performance which is negligible. It's likely coming from the extra blocks being created while scanning. This is negligible and if the history feature works well we might be able to make better block decisions which is means fewer calls to ripper which is the biggest bottleneck. While this doesn't fix https://.com/ruby/syntax_suggest/issues/187 it's a good intermediate step that will hopefully make working on that issue easier. https://.com/ruby/syntax_suggest/commit/ad8487d8aa
-rw-r--r-- | lib/syntax_suggest/around_block_scan.rb | 282 | ||||
-rw-r--r-- | lib/syntax_suggest/block_expand.rb | 9 | ||||
-rw-r--r-- | lib/syntax_suggest/capture_code_context.rb | 6 | ||||
-rw-r--r-- | lib/syntax_suggest/scan_history.rb | 113 |
4 files changed, 259 insertions, 151 deletions
@@ -1,5 +1,7 @@ # frozen_string_literal: true module SyntaxSuggest # This class is useful for exploring contents before and after # a block @@ -24,22 +26,17 @@ module SyntaxSuggest # 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 @orig_indent = block.current_indent - @skip_array = [] - @after_array = [] - @before_array = [] - @stop_after_kw = false - @force_add_hidden = false @force_add_empty = false end # When using this flag, `scan_while` will @@ -89,47 +86,34 @@ module SyntaxSuggest # stopping if we've found a keyword/end mis-match in one direction # or the other. def scan_while - stop_next = false - kw_count = 0 - end_count = 0 - index = before_lines.reverse_each.take_while do |line| - next false if stop_next - next true if @force_add_hidden && line.hidden? - next true if @force_add_empty && line.empty? - kw_count += 1 if line.is_kw? - end_count += 1 if line.is_end? - if @stop_after_kw && kw_count > end_count - stop_next = true - end - yield line - end.last&.index - if index && index < before_index - @before_index = index - end - - stop_next = false - kw_count = 0 - end_count = 0 - index = after_lines.take_while do |line| - next false if stop_next - next true if @force_add_hidden && line.hidden? - next true if @force_add_empty && line.empty? - kw_count += 1 if line.is_kw? - end_count += 1 if line.is_end? - if @stop_after_kw && end_count > kw_count - stop_next = true - end - yield line - end.last&.index - if index && index > after_index - @after_index = index - end self end @@ -160,79 +144,104 @@ module SyntaxSuggest # 4 def eat # 5 end # - def capture_neigr_context lines = [] - kw_count = 0 - end_count = 0 - before_lines.reverse_each do |line| - next if line.empty? - break if line.indent < @orig_indent - next if line.indent != @orig_indent - - kw_count += 1 if line.is_kw? - end_count += 1 if line.is_end? - if kw_count != 0 && kw_count == end_count - lines << line - break - end - - lines << line if line.is_kw? || line.is_end? - end - - lines.reverse! - - kw_count = 0 - end_count = 0 - after_lines.each do |line| - next if line.empty? - break if line.indent < @orig_indent - next if line.indent != @orig_indent - - kw_count += 1 if line.is_kw? - end_count += 1 if line.is_end? - if kw_count != 0 && kw_count == end_count - lines << line - break - end - - lines << line if line.is_kw? || line.is_end? - end lines end # Shows the context around code provided by "falling" indentation # - # Converts: # # it "foo" do # - # into: # # class OH # def hello - # it "foo" do # end # end # def on_falling_indent - last_indent = @orig_indent - before_lines.reverse_each do |line| - next if line.empty? - if line.indent < last_indent - yield line - last_indent = line.indent - end - end - - last_indent = @orig_indent - after_lines.each do |line| - next if line.empty? - if line.indent < last_indent - yield line - last_indent = line.indent - end - end end # Scanning is intentionally conservative because @@ -267,18 +276,24 @@ module SyntaxSuggest return self if kw_count == end_count # nothing to balance # More ends than keywords, check if we can balance expanding up if (end_count - kw_count) == 1 && next_up - return self unless next_up.is_kw? - return self unless next_up.indent >= @orig_indent - - @before_index = next_up.index # More keywords than ends, check if we can balance by expanding down elsif (kw_count - end_count) == 1 && next_down - return self unless next_down.is_end? - return self unless next_down.indent >= @orig_indent - - @after_index = next_down.index end self end @@ -286,19 +301,8 @@ module SyntaxSuggest # Finds code lines at the same or greater indentation and adds them # to the block def scan_neigrs_not_empty - scan_while { |line| line.not_empty? && line.indent >= @orig_indent } - end - - # Returns the next line to be scanned above the current block. - # Returns `nil` if at the top of the document already - def next_up - @code_lines[before_index.pred] - end - - # Returns the next line to be scanned below the current block. - # Returns `nil` if at the bottom of the document already - def next_down - @code_lines[after_index.next] end # Scan blocks based on indentation of next line above/below block @@ -310,11 +314,12 @@ module SyntaxSuggest # the `def/end` lines surrounding a method. def scan_adjacent_indent before_after_indent = [] - before_after_indent << (next_up&.indent || 0) - before_after_indent << (next_down&.indent || 0) - indent = before_after_indent.min - scan_while { |line| line.not_empty? && line.indent >= indent } self end @@ -331,29 +336,12 @@ module SyntaxSuggest # Returns the lines matched by the current scan as an # array of CodeLines def lines - @code_lines[before_index..after_index] - end - - # Gives the index of the first line currently scanned - def before_index - @before_index ||= @orig_before_index - end - - # Gives the index of the last line currently scanned - def after_index - @after_index ||= @orig_after_index - end - - # Returns an array of all the CodeLines that exist before - # the currently scanned block - private def before_lines - @code_lines[0...before_index] || [] end - # Returns an array of all the CodeLines that exist after - # the currently scanned block - private def after_lines - @code_lines[after_index.next..-1] || [] end end end @@ -125,17 +125,20 @@ module SyntaxSuggest # # We try to resolve this edge case with `lookahead_balance_one_line` below. def expand_neigrs(block) - neigrs = AroundBlockScan.new(code_lines: @code_lines, block: block) .force_add_hidden .stop_after_kw .scan_neigrs_not_empty # Slurp up empties - with_empties = neigrs .scan_while { |line| line.empty? } # If next line is kw and it will balance us, take it - expanded_lines = with_empties .lookahead_balance_one_line .lines @@ -55,6 +55,10 @@ module SyntaxSuggest capture_falling_indent(block) end @lines_to_output.select!(&:not_empty?) @lines_to_output.uniq! @lines_to_output.sort! @@ -116,7 +120,7 @@ module SyntaxSuggest return unless block.visible_lines.count == 1 around_lines = AroundBlockScan.new(code_lines: @code_lines, block: block) - .capture_neigr_context around_lines -= block.lines @@ -0,0 +1,113 @@ |