Class: SyntaxSuggest::Visitor

Inherits:
Prism::Visitor
  • Object
show all
Defined in:
lib/syntax_suggest/visitor.rb

Overview

Walks the Prism AST to extract structural info that cannot be reliably determined from tokens alone.

Such as the location of lines that must be logically joined so the search algorithm will treat them as one. Example:

source = <<~RUBY
  User                        # 1
    .where(name: "Earlopain") # 2
    .first                    # 3
RUBY
ast, _tokens = Prism.parse_lex(source).value
visitor = Visitor.new
visitor.visit(ast)
visitor.consecutive_lines # => Set[2, 1]

This output means that line 1 and line 2 need to be joined with their next line.

And determining the location of “endless” method definitions. For example:

source = <<~RUBY
  def cube(x)
    x * x * x
  end
  def square(x) = x * x # 1
RUBY

ast, _tokens = Prism.parse_lex(source).value
visitor = Visitor.new
visitor.visit(ast)
visitor.endless_def_keyword_offsets # => Set[28]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeVisitor

Returns a new instance of Visitor.



38
39
40
41
# File 'lib/syntax_suggest/visitor.rb', line 38

def initialize
  @endless_def_keyword_offsets = Set.new
  @consecutive_lines = Set.new
end

Instance Attribute Details

#consecutive_linesObject (readonly)

Returns the value of attribute consecutive_lines.



36
37
38
# File 'lib/syntax_suggest/visitor.rb', line 36

def consecutive_lines
  @consecutive_lines
end

#endless_def_keyword_offsetsObject (readonly)

Returns the value of attribute endless_def_keyword_offsets.



36
37
38
# File 'lib/syntax_suggest/visitor.rb', line 36

def endless_def_keyword_offsets
  @endless_def_keyword_offsets
end

Instance Method Details

#visit_call_node(node) ⇒ Object

Called by Prism::Visitor for every method-call node in the AST (e.g. ‘foo.bar`, `foo.bar.baz`).



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/syntax_suggest/visitor.rb', line 45

def visit_call_node(node)
  receiver_loc = node.receiver&.location
  call_operator_loc = node.call_operator_loc
  message_loc = node.message_loc
  if receiver_loc && call_operator_loc && message_loc
    # dot-leading (dot on the next line)
    #   foo        # line 1 - consecutive
    #     .bar     # line 2
    if receiver_loc.end_line != call_operator_loc.start_line && call_operator_loc.start_line == message_loc.start_line
      (receiver_loc.end_line..call_operator_loc.start_line - 1).each do |line|
        @consecutive_lines << line
      end
    end

    # dot-trailing (dot on the same line as the receiver)
    #   foo.       # line 1 - consecutive
    #     bar      # line 2
    if receiver_loc.end_line == call_operator_loc.start_line && call_operator_loc.start_line != message_loc.start_line
      (call_operator_loc.start_line..message_loc.start_line - 1).each do |line|
        @consecutive_lines << line
      end
    end
  end
  super
end

#visit_def_node(node) ⇒ Object

Called by Prism::Visitor for every ‘def` node in the AST. Records the keyword start location for endless method definitions like `def foo = 123`. These are valid without a matching `end`, so Token must exclude them when deciding if a line is a keyword.



75
76
77
78
# File 'lib/syntax_suggest/visitor.rb', line 75

def visit_def_node(node)
  @endless_def_keyword_offsets << node.def_keyword_loc.start_offset if node.equal_loc
  super
end