Class: IRB::NestingParser::NestingVisitor

Inherits:
Prism::Visitor
  • Object
show all
Defined in:
lib/irb/nesting_parser.rb

Instance Method Summary collapse

Constructor Details

#initializeNestingVisitor

Returns a new instance of NestingVisitor.



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/irb/nesting_parser.rb', line 10

def initialize
  # Array of [column, priority(+1/-1), NestingElem(open) or nil(close)] per line.
  # priority is +1 for open, -1 for close so that close comes before open when sorted.
  # Example:
  #   if cond
  #   else
  #   end
  # `else` closes `if` at column 0 first and then opens `else` at column 0 next.
  @lines = []

  # Array of open heredoc NestingElem per line
  @heredocs = []
end

Instance Method Details

#close(line, column) ⇒ Object



51
52
53
# File 'lib/irb/nesting_parser.rb', line 51

def close(line, column)
  (@lines[line - 1] ||= []) << [column, -1]
end

#close_closing_loc(node) ⇒ Object



77
78
79
# File 'lib/irb/nesting_parser.rb', line 77

def close_closing_loc(node)
  close_location(node.closing_loc) if node.closing_loc && !node.closing.empty?
end

#close_end_keyword_loc(node) ⇒ Object



73
74
75
# File 'lib/irb/nesting_parser.rb', line 73

def close_end_keyword_loc(node)
  close_location(node.end_keyword_loc) if node.end_keyword == 'end'
end

#close_location(location) ⇒ Object



65
66
67
# File 'lib/irb/nesting_parser.rb', line 65

def close_location(location)
  close(location.end_line, location.end_column)
end

#close_location_start(location) ⇒ Object



69
70
71
# File 'lib/irb/nesting_parser.rb', line 69

def close_location_start(location)
  close(location.start_line, location.start_column)
end

#heredoc_open(node) ⇒ Object



42
43
44
45
# File 'lib/irb/nesting_parser.rb', line 42

def heredoc_open(node)
  elem = NestingElem.new([node.location.start_line, node.location.start_column], :on_heredoc_beg, node.opening)
  (@heredocs[node.location.start_line - 1] ||= []) << elem
end

#heredoc_string_like(node, type) ⇒ Object



219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/irb/nesting_parser.rb', line 219

def heredoc_string_like(node, type)
  if node.opening&.start_with?('<<')
    heredoc_open(node)
    # Heredoc closing contains trailing newline. We need to exclude it
    close_location_start(node.closing_loc) if node.closing_loc && !node.closing.empty?
  elsif node.opening
    open_location(node.location, type, node.opening)
    if node.closing && node.closing != ''
      # Closing of `"#{\n` is "\n". We need to treat it as not-closed.
      close_location_start(node.closing_loc) if node.opening.match?(/\n\z/) || node.closing != "\n"
    end
  end
end

#modifier_node?(node, keyword_loc) ⇒ Boolean

Checks if a node (if, while, etc) is a modifier form that does not need end closing. modifier node: ‘a if b`, non-modifier node: `if a; b; end`

Returns:

  • (Boolean)


57
58
59
# File 'lib/irb/nesting_parser.rb', line 57

def modifier_node?(node, keyword_loc)
  !(keyword_loc && node.location.start_line == keyword_loc.start_line && node.location.start_column == keyword_loc.start_column)
end

#nestingsObject



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/irb/nesting_parser.rb', line 24

def nestings
  size = [@lines.size, @heredocs.size].max
  nesting = []
  size.times.map do |line_index|
    @lines[line_index]&.sort_by { |col, pri| [col, pri] }&.each do |col, pri, elem|
      if elem
        nesting << elem
      else
        nesting.pop
      end
    end
    @heredocs[line_index]&.sort_by { |elem| elem.pos[1] }&.reverse_each do |elem|
      nesting << elem
    end
    nesting.dup
  end
end

#open(line, column, elem) ⇒ Object



47
48
49
# File 'lib/irb/nesting_parser.rb', line 47

def open(line, column, elem)
  (@lines[line - 1] ||= []) << [column, +1, elem]
end

#open_location(location, type, tok) ⇒ Object



61
62
63
# File 'lib/irb/nesting_parser.rb', line 61

def open_location(location, type, tok)
  open(location.start_line, location.start_column, NestingElem.new([location.start_line, location.start_column], type, tok))
end

#visit_array_node(node) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/irb/nesting_parser.rb', line 188

def visit_array_node(node)
  super
  type =
    case node.opening
    when nil
      # `x = 1, 2` doesn't have opening
      nil
    when '['
      :bracket
    when /\A%W/
      :on_words_beg
    when /\A%w/
      :on_qwords_beg
    when /\A%I/
      :on_symbols_beg
    when /\A%i/
      :on_qsymbols_beg
    end

  if type
    open_location(node.location, type, node.opening)
    close_closing_loc(node)
  end
end

#visit_begin_node(node) ⇒ Object



174
175
176
177
178
179
180
# File 'lib/irb/nesting_parser.rb', line 174

def visit_begin_node(node)
  super
  if node.begin_keyword
    open_location(node.location, :on_kw, 'begin')
    close_end_keyword_loc(node)
  end
end

#visit_block_node(node) ⇒ Object



182
183
184
185
186
# File 'lib/irb/nesting_parser.rb', line 182

def visit_block_node(node)
  super
  open_location(node.location, node.opening == '{' ? :on_lbrace : :on_kw, node.opening)
  close_closing_loc(node)
end

#visit_block_parameters_node(node) ⇒ Object



290
291
292
293
294
295
296
# File 'lib/irb/nesting_parser.rb', line 290

def visit_block_parameters_node(node)
  super
  if node.opening == '('
    open_location(node.location, :on_lparen, '(')
    close_closing_loc(node)
  end
end

#visit_call_node(node) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/irb/nesting_parser.rb', line 274

def visit_call_node(node)
  super
  type =
    case node.opening
    when '('
      :on_lparen
    when '['
      :on_lbracket
    end

  if type
    open_location(node.opening_loc, type, node.opening)
    close_closing_loc(node)
  end
end

#visit_case_node(node) ⇒ Object Also known as: visit_case_match_node



127
128
129
130
131
132
133
134
135
# File 'lib/irb/nesting_parser.rb', line 127

def visit_case_node(node)
  super
  open_location(node.location, :on_kw, 'case')
  if node.else_clause
    close_location_start(node.else_clause.location)
  else
    close_end_keyword_loc(node)
  end
end

#visit_class_node(node) ⇒ Object Also known as: visit_singleton_class_node



328
329
330
331
332
# File 'lib/irb/nesting_parser.rb', line 328

def visit_class_node(node)
  super
  open_location(node.location, :on_kw, 'class')
  close_end_keyword_loc(node)
end

#visit_def_node(node) ⇒ Object



314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/irb/nesting_parser.rb', line 314

def visit_def_node(node)
  super
  open_location(node.location, :on_kw, 'def')
  if node.lparen == '('
    open_location(node.lparen_loc, :on_lparen, '(')
    close_location(node.rparen_loc) if node.rparen == ')'
  end
  if node.equal
    close_location(node.equal_loc)
  else
    close_end_keyword_loc(node)
  end
end

#visit_else_node(node) ⇒ Object



150
151
152
153
154
155
156
# File 'lib/irb/nesting_parser.rb', line 150

def visit_else_node(node)
  super
  if node.else_keyword == 'else'
    open_location(node.location, :on_kw, 'else')
    close_end_keyword_loc(node)
  end
end

#visit_embedded_statements_node(node) ⇒ Object



233
234
235
236
237
# File 'lib/irb/nesting_parser.rb', line 233

def visit_embedded_statements_node(node)
  super
  open_location(node.location, :on_embexpr_beg, '#{')
  close_closing_loc(node)
end

#visit_ensure_node(node) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/irb/nesting_parser.rb', line 158

def visit_ensure_node(node)
  super
  return if modifier_node?(node, node.ensure_keyword_loc)

  close_location_start(node.location)
  open_location(node.location, :on_kw, 'ensure')
end

#visit_for_node(node) ⇒ Object



81
82
83
84
85
# File 'lib/irb/nesting_parser.rb', line 81

def visit_for_node(node)
  super
  open_location(node.location, :on_kw, 'for')
  close_end_keyword_loc(node)
end

#visit_hash_node(node) ⇒ Object



213
214
215
216
217
# File 'lib/irb/nesting_parser.rb', line 213

def visit_hash_node(node)
  super
  open_location(node.location, :on_lbrace, '{')
  close_closing_loc(node)
end

#visit_if_node(node) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/irb/nesting_parser.rb', line 103

def visit_if_node(node)
  super
  return if !node.if_keyword || modifier_node?(node, node.if_keyword_loc)

  open_location(node.location, :on_kw, node.if_keyword)
  if node.subsequent
    close_location_start(node.subsequent.location)
  else
    close_end_keyword_loc(node)
  end
end

#visit_in_node(node) ⇒ Object



144
145
146
147
148
# File 'lib/irb/nesting_parser.rb', line 144

def visit_in_node(node)
  super
  close_location_start(node.location)
  open_location(node.location, :on_kw, 'in')
end

#visit_interpolated_string_node(node) ⇒ Object Also known as: visit_string_node



239
240
241
242
# File 'lib/irb/nesting_parser.rb', line 239

def visit_interpolated_string_node(node)
  super
  heredoc_string_like(node, :on_tstring_beg)
end

#visit_interpolated_x_string_node(node) ⇒ Object Also known as: visit_x_string_node



245
246
247
248
# File 'lib/irb/nesting_parser.rb', line 245

def visit_interpolated_x_string_node(node)
  super
  heredoc_string_like(node, :on_backtick)
end

#visit_lambda_node(node) ⇒ Object



298
299
300
301
302
# File 'lib/irb/nesting_parser.rb', line 298

def visit_lambda_node(node)
  super
  open_location(node.opening_loc, :on_tlambeg, node.opening)
  close_closing_loc(node)
end

#visit_module_node(node) ⇒ Object



335
336
337
338
339
# File 'lib/irb/nesting_parser.rb', line 335

def visit_module_node(node)
  super
  open_location(node.location, :on_kw, 'module')
  close_end_keyword_loc(node)
end

#visit_parentheses_node(node) ⇒ Object



268
269
270
271
272
# File 'lib/irb/nesting_parser.rb', line 268

def visit_parentheses_node(node)
  super
  open_location(node.location, :on_lparen, '(')
  close_closing_loc(node)
end

#visit_regular_expression_node(node) ⇒ Object Also known as: visit_interpolated_regular_expression_node



261
262
263
264
265
# File 'lib/irb/nesting_parser.rb', line 261

def visit_regular_expression_node(node)
  super
  open_location(node.location, :on_regexp_beg, node.opening)
  close_closing_loc(node)
end

#visit_rescue_node(node) ⇒ Object



166
167
168
169
170
171
172
# File 'lib/irb/nesting_parser.rb', line 166

def visit_rescue_node(node)
  super
  return if modifier_node?(node, node.keyword_loc)

  close_location_start(node.location)
  open_location(node.location, :on_kw, 'rescue')
end

#visit_super_node(node) ⇒ Object Also known as: visit_yield_node, visit_defined_node



304
305
306
307
308
309
310
# File 'lib/irb/nesting_parser.rb', line 304

def visit_super_node(node)
  super
  if node.lparen
    open_location(node.lparen_loc, :on_lparen, '(')
    close_location(node.rparen_loc) if node.rparen == ')'
  end
end

#visit_symbol_node(node) ⇒ Object Also known as: visit_interpolated_symbol_node



251
252
253
254
255
256
257
258
# File 'lib/irb/nesting_parser.rb', line 251

def visit_symbol_node(node)
  super
  unless node.opening.nil? || node.opening.empty? || node.opening == ':'
    # :"sym" or %s[sym]
    open_location(node.location, :on_symbeg, node.opening)
    close_closing_loc(node)
  end
end

#visit_unless_node(node) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/irb/nesting_parser.rb', line 115

def visit_unless_node(node)
  super
  return if modifier_node?(node, node.keyword_loc)

  open_location(node.location, :on_kw, 'unless')
  if node.else_clause
    close_location_start(node.else_clause.location)
  else
    close_end_keyword_loc(node)
  end
end

#visit_until_node(node) ⇒ Object



95
96
97
98
99
100
101
# File 'lib/irb/nesting_parser.rb', line 95

def visit_until_node(node)
  super
  return if modifier_node?(node, node.keyword_loc)

  open_location(node.location, :on_kw, 'until')
  close_closing_loc(node)
end

#visit_when_node(node) ⇒ Object



138
139
140
141
142
# File 'lib/irb/nesting_parser.rb', line 138

def visit_when_node(node)
  super
  close_location_start(node.location)
  open_location(node.location, :on_kw, 'when')
end

#visit_while_node(node) ⇒ Object



87
88
89
90
91
92
93
# File 'lib/irb/nesting_parser.rb', line 87

def visit_while_node(node)
  super
  return if modifier_node?(node, node.keyword_loc)

  open_location(node.location, :on_kw, 'while')
  close_closing_loc(node)
end