1. SyntaxSuggest::
  2. CodeSearch

class SyntaxSuggest::CodeSearch

Searches code for a syntax error

There are three main phases in the algorithm:

  1. Sanitize/format input source

  2. Search for invalid blocks

  3. Format invalid blocks into something meaninful

This class handles the part.

The bulk of the heavy lifting is done in:

- CodeFrontier (Holds information for generating blocks and determining if we can stop searching)- ParseBlocksFromLine (Creates blocks into the frontier)- BlockExpand (Expands existing blocks to search more code)

## Syntax error detection

When the frontier holds the syntax error, we can stop searching

search =CodeSearch.new(<<~EOM)  def dog    def lol  endEOMsearch.callsearch.invalid_blocks.map(&:to_s)# =># => ["def lol\n"]

Attributes

code_lines[R]
frontier[R]
invalid_blocks[R]
record_dir[R]

Public Class Methods

Source
# File lib/syntax_suggest/code_search.rb, line 44definitialize(source,record_dir:DEFAULT_VALUE)record_dir =ifrecord_dir==DEFAULT_VALUE    (ENV["SYNTAX_SUGGEST_RECORD_DIR"]||ENV["SYNTAX_SUGGEST_DEBUG"])?"tmp":nilelserecord_direndifrecord_dir@record_dir =SyntaxSuggest.record_dir(record_dir)@write_count =0end@tick =0@source =source@name_tick =Hash.new {|hash,k|hash[k] =0 }@invalid_blocks = []@code_lines =CleanDocument.new(source:source).call.lines@frontier =CodeFrontier.new(code_lines:@code_lines)@block_expand =BlockExpand.new(code_lines:@code_lines)@parse_blocks_from_indent_line =ParseBlocksFromIndentLine.new(code_lines:@code_lines)end

Public Instance Methods

Source
# File lib/syntax_suggest/code_search.rb, line 123defcalluntilfrontier.holds_all_syntax_errors?@tick+=1iffrontier.expand?expand_existingelsecreate_blocks_from_untracked_linesendend@invalid_blocks.concat(frontier.detect_invalid_blocks)@invalid_blocks.sort_by! {|block|block.starts_at }selfend

Main search loop

Source
# File lib/syntax_suggest/code_search.rb, line 100defcreate_blocks_from_untracked_linesmax_indent =frontier.next_indent_line&.indentwhile (line =frontier.next_indent_line)&& (line.indent==max_indent)@parse_blocks_from_indent_line.each_neighbor_block(frontier.next_indent_line)do|block|push(block,name:"add")endendend

Parses the most indented lines into blocks that are marked and added to the frontier

Source
# File lib/syntax_suggest/code_search.rb, line 112defexpand_existingblock =frontier.popreturnunlessblockrecord(block:block,name:"before-expand")block =@block_expand.call(block)push(block,name:"expand")end

Given an already existing block in the frontier, expand it to see if it contains our invalid syntax

Source
# File lib/syntax_suggest/code_search.rb, line 91defpush(block,name:)record(block:block,name:name)block.mark_invisibleifblock.valid?frontier<<blockend
Source
# File lib/syntax_suggest/code_search.rb, line 69defrecord(block:,name:"record")returnunless@record_dir@name_tick[name]+=1filename ="#{@write_count += 1}-#{name}-#{@name_tick[name]}-(#{block.starts_at}__#{block.ends_at}).txt"ifENV["SYNTAX_SUGGEST_DEBUG"]puts"\n\n==== #{filename} ===="puts"\n```#{block.starts_at}..#{block.ends_at}"putsblockputs"```"puts"  block indent:      #{block.current_indent}"end@record_dir.join(filename).open(mode:"a")do|f|document =DisplayCodeWithLineNumbers.new(lines:@code_lines.select(&:visible?),terminal:false,highlight_lines:block.lines    ).callf.write("    Block lines: #{block.starts_at..block.ends_at} (#{name}) \n\n#{document}")endend

Used for debugging