textbringer 24 → 26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
- require "ripper"
1
+ require "set"
2
+ require "prism"
2
3
 
3
4
  module Textbringer
4
5
  CONFIG[:ruby_indent_level] = 2
@@ -10,81 +11,6 @@ module Textbringer
10
11
  (?:Gem|Rake|Cap|Thor|Vagrant|Guard|Pod)file)\z/ix
11
12
  self.interpreter_name_pattern = /ruby/i
12
13
 
13
- define_syntax :comment, /
14
- (?: \#.*(?:\\\n.*)*(?:\z|(?<!\\)\n) ) |
15
- (?: ^=begin (?:.|\n)* (?> ^=end \b ) )
16
- /x
17
-
18
- define_syntax :keyword, /
19
- (?<![$@.]) \b (?: (?:
20
- class | module | def | undef | begin | rescue | ensure | end |
21
- if | unless | then | elsif | else | case | when | while | until |
22
- for | break | next | redo | retry | in | do | return | yield |
23
- super | self | nil | true | false | and | or | not | alias
24
- ) \b (?![!?]) | defined\? )
25
- /x
26
-
27
- define_syntax :string, /
28
- (?: (?<! [a-zA-Z] ) \?
29
- (:?
30
- [^\\\s] |
31
- \\ [0-7]{1,3} |
32
- \\x [0-9a-fA-F]{2} |
33
- \\u [0-9a-fA-F]{4} |
34
- \\u \{ [0-9a-fA-F]+ \} |
35
- \\C - . |
36
- \\M - . |
37
- \\ .
38
- )
39
- ) |
40
- (?: %[qQrwWsiIx]?\{ (?: [^\\}] | \\ . )* \} ) |
41
- (?: %[qQrwWsiIx]?\( (?: [^\\)] | \\ . )* \) ) |
42
- (?: %[qQrwWsiIx]?\[ (?: [^\\\]] | \\ . )* \] ) |
43
- (?: %[qQrwWsiIx]?< (?: [^\\>] | \\ . )* > ) |
44
- (?:
45
- %[qQrwWsiIx]?
46
- (?<string_delimiter>[^{(\[<a-zA-Z0-9\s\u{0100}-\u{10ffff}])
47
- (?: (?! \k<string_delimiter> ) [^\\] | \\ . )*
48
- \k<string_delimiter>
49
- ) |
50
- (?:
51
- (?<! \$ )
52
- " (?: [^\\"] | \\ . )* "
53
- ) |
54
- (?:
55
- (?<! \$ )
56
- ' (?: [^\\'] | \\ . )* '
57
- ) |
58
- (?:
59
- (?<! [$.] | def | def \s )
60
- ` (?: [^\\`] | \\ . )* `
61
- ) |
62
- (?:
63
- (?<=
64
- ^ |
65
- \b and | \b or | \b while | \b until | \b unless | \b if |
66
- \b elsif | \b when | \b not | \b then | \b else |
67
- [;~=!|&(,\[<>?:*+-]
68
- ) \s*
69
- \/ (?: [^\\\/] | \\ . )* \/[iomxneus]*
70
- ) |
71
- (?:
72
- (?<! class | class \s | [\]})"'.] | :: | \w )
73
- <<[\-~]?(?<heredoc_quote>['"`]?)
74
- (?<heredoc_terminator>
75
- (?> [_a-zA-Z\u{0100}-\u{10ffff}]
76
- [_a-zA-Z0-9\u{0100}-\u{10ffff}]* )
77
- )
78
- \k<heredoc_quote>
79
- (?> (?:.|\n)*? ^ [\ \t]* \k<heredoc_terminator> $ )
80
- ) |
81
- (?:
82
- (?<! : ) :
83
- [_a-zA-Z\u{0100}-\u{10ffff}]
84
- [_a-zA-Z0-9\u{0100}-\u{10ffff}]*
85
- )
86
- /x
87
-
88
14
  def comment_start
89
15
  "#"
90
16
  end
@@ -93,17 +19,23 @@ module Textbringer
93
19
  super(buffer)
94
20
  @buffer[:indent_level] = CONFIG[:ruby_indent_level]
95
21
  @buffer[:indent_tabs_mode] = CONFIG[:ruby_indent_tabs_mode]
22
+ @prism_version = nil
23
+ @prism_tokens = nil
24
+ @prism_ast = nil
25
+ @prism_method_name_locs = nil
26
+ @literal_levels = nil
27
+ @literal_levels_version = nil
96
28
  end
97
29
 
98
30
  def forward_definition(n = number_prefix_arg || 1)
99
- tokens = Ripper.lex(@buffer.to_s)
31
+ tokens = filter_prism_tokens_line_and_type
100
32
  @buffer.forward_line
101
33
  n.times do |i|
102
- tokens = tokens.drop_while { |(l, _), e, t|
34
+ tokens = tokens.drop_while { |l, type|
103
35
  l < @buffer.current_line ||
104
- e != :on_kw || /\A(?:class|module|def)\z/ !~ t
36
+ !DEFINITION_KEYWORDS.include?(type)
105
37
  }
106
- (line,), = tokens.first
38
+ line, = tokens.first
107
39
  if line.nil?
108
40
  @buffer.end_of_buffer
109
41
  break
@@ -117,14 +49,14 @@ module Textbringer
117
49
  end
118
50
 
119
51
  def backward_definition(n = number_prefix_arg || 1)
120
- tokens = Ripper.lex(@buffer.to_s).reverse
52
+ tokens = filter_prism_tokens_line_and_type.reverse
121
53
  @buffer.beginning_of_line
122
54
  n.times do |i|
123
- tokens = tokens.drop_while { |(l, _), e, t|
55
+ tokens = tokens.drop_while { |l, type|
124
56
  l >= @buffer.current_line ||
125
- e != :on_kw || /\A(?:class|module|def)\z/ !~ t
57
+ !DEFINITION_KEYWORDS.include?(type)
126
58
  }
127
- (line,), = tokens.first
59
+ line, = tokens.first
128
60
  if line.nil?
129
61
  @buffer.beginning_of_buffer
130
62
  break
@@ -175,8 +107,167 @@ module Textbringer
175
107
  end
176
108
  end
177
109
 
110
+ def highlight(ctx)
111
+ ensure_prism_tokens
112
+ return unless @prism_tokens
113
+ ensure_method_name_locs
114
+ base_pos = ctx.buffer.point_min
115
+ hl_start = ctx.highlight_start
116
+ hl_end = ctx.highlight_end
117
+ in_symbol = false
118
+ after_class_or_module = false
119
+ @prism_tokens.each do |token_info|
120
+ token = token_info[0]
121
+ type = token.type
122
+ offset = token.location.start_offset
123
+ length = token.location.length
124
+ pos = base_pos + offset
125
+ pos_end = pos + length
126
+ break if pos >= hl_end
127
+ if pos_end > hl_start
128
+ face_name = PRISM_TOKEN_FACES[type]
129
+ if in_symbol
130
+ face_name = :string if face_name.nil? || face_name == :constant ||
131
+ face_name == :keyword || face_name == :operator
132
+ elsif @prism_method_name_locs.key?(offset)
133
+ face_name = :function_name
134
+ elsif face_name == :constant &&
135
+ (after_class_or_module || token.location.slice.match?(/\p{Lower}/))
136
+ face_name = :type
137
+ end
138
+ if face_name && (face = Face[face_name])
139
+ ctx.highlight(pos, pos_end, face)
140
+ end
141
+ end
142
+ in_symbol = type == :SYMBOL_BEGIN
143
+ after_class_or_module = (type == :KEYWORD_CLASS || type == :KEYWORD_MODULE) ||
144
+ (after_class_or_module && !(type == :NEWLINE || type == :SEMICOLON))
145
+ end
146
+ end
147
+
178
148
  private
179
149
 
150
+ PRISM_TOKEN_FACES = {
151
+ # Keywords
152
+ KEYWORD_ALIAS: :keyword, KEYWORD_AND: :keyword, KEYWORD_BEGIN: :keyword,
153
+ KEYWORD_BEGIN_UPCASE: :keyword, KEYWORD_BREAK: :keyword,
154
+ KEYWORD_CASE: :keyword, KEYWORD_CLASS: :keyword, KEYWORD_DEF: :keyword,
155
+ KEYWORD_DEFINED: :keyword, KEYWORD_DO: :keyword,
156
+ KEYWORD_DO_LOOP: :keyword, KEYWORD_ELSE: :keyword,
157
+ KEYWORD_ELSIF: :keyword, KEYWORD_END: :keyword,
158
+ KEYWORD_END_UPCASE: :keyword, KEYWORD_ENSURE: :keyword,
159
+ KEYWORD_FALSE: :builtin, KEYWORD_FOR: :keyword, KEYWORD_IF: :keyword,
160
+ KEYWORD_IF_MODIFIER: :keyword, KEYWORD_IN: :keyword,
161
+ KEYWORD_MODULE: :keyword, KEYWORD_NEXT: :keyword,
162
+ KEYWORD_NIL: :builtin,
163
+ KEYWORD_NOT: :keyword, KEYWORD_OR: :keyword, KEYWORD_REDO: :keyword,
164
+ KEYWORD_RESCUE: :keyword, KEYWORD_RESCUE_MODIFIER: :keyword,
165
+ KEYWORD_RETRY: :keyword, KEYWORD_RETURN: :keyword,
166
+ KEYWORD_SELF: :builtin, KEYWORD_SUPER: :builtin,
167
+ KEYWORD_THEN: :keyword, KEYWORD_TRUE: :builtin,
168
+ KEYWORD_UNDEF: :keyword,
169
+ KEYWORD_UNLESS: :keyword, KEYWORD_UNLESS_MODIFIER: :keyword,
170
+ KEYWORD_UNTIL: :keyword, KEYWORD_UNTIL_MODIFIER: :keyword,
171
+ KEYWORD_WHEN: :keyword, KEYWORD_WHILE: :keyword,
172
+ KEYWORD_WHILE_MODIFIER: :keyword, KEYWORD_YIELD: :keyword,
173
+ KEYWORD___FILE__: :builtin, KEYWORD___LINE__: :builtin,
174
+ KEYWORD___ENCODING__: :builtin,
175
+
176
+ # Comments
177
+ COMMENT: :comment, EMBDOC_BEGIN: :comment, EMBDOC_LINE: :comment,
178
+ EMBDOC_END: :comment,
179
+
180
+ # Strings and string-like
181
+ STRING_BEGIN: :string, STRING_CONTENT: :string, STRING_END: :string,
182
+ SYMBOL_BEGIN: :string, REGEXP_BEGIN: :string, REGEXP_END: :string,
183
+ PERCENT_LOWER_I: :string, PERCENT_UPPER_I: :string,
184
+ PERCENT_LOWER_W: :string, PERCENT_UPPER_W: :string,
185
+ PERCENT_LOWER_X: :string,
186
+ HEREDOC_START: :string, HEREDOC_END: :string,
187
+ LABEL: :property,
188
+
189
+ # Numbers
190
+ INTEGER: :number, FLOAT: :number,
191
+ INTEGER_RATIONAL: :number, FLOAT_RATIONAL: :number,
192
+ INTEGER_IMAGINARY: :number, FLOAT_IMAGINARY: :number,
193
+ INTEGER_RATIONAL_IMAGINARY: :number, FLOAT_RATIONAL_IMAGINARY: :number,
194
+
195
+ # Constants
196
+ CONSTANT: :constant,
197
+
198
+ # Variables
199
+ INSTANCE_VARIABLE: :variable, CLASS_VARIABLE: :variable,
200
+ GLOBAL_VARIABLE: :variable,
201
+
202
+ # Operators
203
+ PLUS: :operator, MINUS: :operator, STAR: :operator, SLASH: :operator,
204
+ PERCENT: :operator, STAR_STAR: :operator,
205
+ EQUAL: :operator, EQUAL_EQUAL: :operator, BANG_EQUAL: :operator,
206
+ LESS: :operator, GREATER: :operator,
207
+ LESS_EQUAL: :operator, GREATER_EQUAL: :operator,
208
+ LESS_EQUAL_GREATER: :operator, EQUAL_EQUAL_EQUAL: :operator,
209
+ EQUAL_TILDE: :operator, BANG_TILDE: :operator,
210
+ AMPERSAND_AMPERSAND: :operator, PIPE_PIPE: :operator,
211
+ BANG: :operator, TILDE: :operator,
212
+ LESS_LESS: :operator, GREATER_GREATER: :operator,
213
+ AMPERSAND: :operator, PIPE: :operator, CARET: :operator,
214
+ PLUS_EQUAL: :operator, MINUS_EQUAL: :operator,
215
+ STAR_EQUAL: :operator, SLASH_EQUAL: :operator,
216
+ PERCENT_EQUAL: :operator, STAR_STAR_EQUAL: :operator,
217
+ AMPERSAND_EQUAL: :operator, PIPE_EQUAL: :operator,
218
+ CARET_EQUAL: :operator,
219
+ AMPERSAND_AMPERSAND_EQUAL: :operator, PIPE_PIPE_EQUAL: :operator,
220
+ LESS_LESS_EQUAL: :operator, GREATER_GREATER_EQUAL: :operator,
221
+ DOT_DOT: :operator, DOT_DOT_DOT: :operator,
222
+ EQUAL_GREATER: :operator, UMINUS: :operator, UPLUS: :operator,
223
+ USTAR: :operator, USTAR_STAR: :operator, UAMPERSAND: :operator,
224
+
225
+ # Punctuation
226
+ DOT: :punctuation, COLON_COLON: :punctuation,
227
+ SEMICOLON: :punctuation, COMMA: :punctuation,
228
+ PARENTHESIS_LEFT: :punctuation, PARENTHESIS_RIGHT: :punctuation,
229
+ BRACKET_LEFT: :punctuation, BRACKET_LEFT_ARRAY: :punctuation,
230
+ BRACKET_RIGHT: :punctuation,
231
+ BRACE_LEFT: :punctuation, BRACE_RIGHT: :punctuation,
232
+ QUESTION_MARK: :punctuation, COLON: :punctuation,
233
+ LAMBDA_BEGIN: :punctuation,
234
+
235
+ # Method names (e.g. block_given?, is_a?)
236
+ METHOD_NAME: :function_name,
237
+ }.freeze
238
+
239
+ DEFINITION_KEYWORDS = %i[KEYWORD_CLASS KEYWORD_MODULE KEYWORD_DEF].to_set
240
+
241
+ LITERAL_BEGIN_TYPES = %i[STRING_BEGIN HEREDOC_START REGEXP_BEGIN EMBDOC_BEGIN].to_set
242
+ LITERAL_END_TYPES = %i[STRING_END HEREDOC_END REGEXP_END EMBDOC_END].to_set
243
+
244
+ CONTINUATION_OPERATOR_TYPES = %i[
245
+ PLUS MINUS STAR SLASH PERCENT STAR_STAR
246
+ EQUAL EQUAL_EQUAL BANG_EQUAL
247
+ LESS GREATER LESS_EQUAL GREATER_EQUAL LESS_EQUAL_GREATER
248
+ EQUAL_TILDE BANG_TILDE
249
+ AMPERSAND_AMPERSAND PIPE_PIPE
250
+ BANG TILDE
251
+ LESS_LESS GREATER_GREATER
252
+ AMPERSAND CARET
253
+ PLUS_EQUAL MINUS_EQUAL STAR_EQUAL SLASH_EQUAL PERCENT_EQUAL
254
+ STAR_STAR_EQUAL AMPERSAND_EQUAL PIPE_EQUAL CARET_EQUAL
255
+ AMPERSAND_AMPERSAND_EQUAL PIPE_PIPE_EQUAL
256
+ LESS_LESS_EQUAL GREATER_GREATER_EQUAL
257
+ EQUAL_GREATER
258
+ ].to_set
259
+
260
+ OPERATORS = %i(!= !~ =~ == === <=> > >= < <= & | ^ >> << - + % / * ** -@ +@ ~ ! [] []=)
261
+
262
+ BLOCK_END = {
263
+ EMBEXPR_BEGIN: :EMBEXPR_END,
264
+ BRACE_LEFT: :BRACE_RIGHT,
265
+ PARENTHESIS_LEFT: :PARENTHESIS_RIGHT,
266
+ BRACKET_LEFT: :BRACKET_RIGHT,
267
+ BRACKET_LEFT_ARRAY: :BRACKET_RIGHT,
268
+ LAMBDA_BEGIN: :BRACE_RIGHT,
269
+ }
270
+
180
271
  INDENT_BEG_RE = /^([ \t]*)(class|module|def|if|unless|case|while|until|for|begin)\b/
181
272
 
182
273
  def space_width(s)
@@ -187,8 +278,7 @@ module Textbringer
187
278
  loop do
188
279
  @buffer.re_search_backward(INDENT_BEG_RE)
189
280
  space = @buffer.match_string(1)
190
- s = @buffer.substring(@buffer.point_min, @buffer.point)
191
- if PartialLiteralAnalyzer.in_literal?(s)
281
+ if in_literal?(@buffer.point)
192
282
  next
193
283
  end
194
284
  return space_width(space)
@@ -199,19 +289,12 @@ module Textbringer
199
289
  end
200
290
 
201
291
  def lex(source)
202
- line_count = source.count("\n")
203
- s = source
204
- lineno = 1
205
- tokens = []
206
- loop do
207
- lexer = Ripper::Lexer.new(s, "-", lineno)
208
- tokens.concat(lexer.lex)
209
- last_line = tokens.dig(-1, 0, 0)
210
- return tokens if last_line.nil? || last_line >= line_count
211
- s = source.sub(/(.*\n?){#{last_line}}/, "")
212
- return tokens if last_line + 1 <= lineno
213
- lineno = last_line + 1
214
- end
292
+ Prism.lex(source).value.filter_map { |token, _state|
293
+ type = token.type
294
+ next if type == :EOF
295
+ loc = token.location
296
+ [[loc.start_line, loc.start_column], type, token.value]
297
+ }
215
298
  end
216
299
 
217
300
  def calculate_indentation
@@ -227,19 +310,20 @@ module Textbringer
227
310
  start_line = @buffer.current_line
228
311
  tokens = lex(@buffer.substring(start_pos, bol_pos))
229
312
  _, event, text = tokens.last
230
- if event == :on_nl
313
+ if event == :NEWLINE || event == :IGNORED_NEWLINE
231
314
  _, event, text = tokens[-2]
232
315
  end
233
- if event == :on_tstring_beg ||
234
- event == :on_heredoc_beg ||
235
- event == :on_regexp_beg ||
236
- (event == :on_regexp_end && text.size > 1) ||
237
- event == :on_tstring_content
316
+ if event == :STRING_BEGIN ||
317
+ event == :HEREDOC_START ||
318
+ (event == :HEREDOC_END && text.empty?) ||
319
+ event == :REGEXP_BEGIN ||
320
+ event == :STRING_CONTENT ||
321
+ event == :HEREDOC_CONTENT
238
322
  return nil
239
323
  end
240
324
  i, extra_end_count = find_nearest_beginning_token(tokens)
241
325
  (line, column), event, = i ? tokens[i] : nil
242
- if event == :on_lparen && tokens.dig(i + 1, 1) != :on_ignored_nl
326
+ if event == :PARENTHESIS_LEFT && tokens.dig(i + 1, 1) != :IGNORED_NEWLINE
243
327
  return column + 1
244
328
  end
245
329
  if line
@@ -268,101 +352,87 @@ module Textbringer
268
352
  if @buffer.looking_at?(/[ \t]*([}\])]|(end|else|elsif|when|in|rescue|ensure)\b)/)
269
353
  indentation -= @buffer[:indent_level]
270
354
  end
271
- _, last_event, last_text = tokens.reverse_each.find { |_, e, _|
272
- e != :on_sp && e != :on_nl && e != :on_ignored_nl
355
+ _, last_event, = tokens.reverse_each.find { |_, e, _|
356
+ e != :NEWLINE && e != :IGNORED_NEWLINE
273
357
  }
274
358
  if start_with_period ||
275
- (last_event == :on_op && last_text != "|") ||
276
- (last_event == :on_kw && /\A(and|or)\z/.match?(last_text)) ||
277
- last_event == :on_period ||
278
- (last_event == :on_comma && event != :on_lbrace &&
279
- event != :on_lparen && event != :on_lbracket) ||
280
- last_event == :on_label
359
+ CONTINUATION_OPERATOR_TYPES.include?(last_event) ||
360
+ last_event == :KEYWORD_AND || last_event == :KEYWORD_OR ||
361
+ last_event == :DOT ||
362
+ (last_event == :COMMA && event != :BRACE_LEFT &&
363
+ event != :PARENTHESIS_LEFT && event != :BRACKET_LEFT &&
364
+ event != :BRACKET_LEFT_ARRAY) ||
365
+ last_event == :LABEL
281
366
  indentation += @buffer[:indent_level]
282
367
  end
283
368
  indentation
284
369
  end
285
370
  end
286
371
 
287
- BLOCK_END = {
288
- '#{' => "}",
289
- "{" => "}",
290
- "(" => ")",
291
- "[" => "]"
292
- }
293
-
294
372
  def find_nearest_beginning_token(tokens)
295
373
  stack = []
296
374
  (tokens.size - 1).downto(0) do |i|
297
375
  (line, ), event, text = tokens[i]
298
376
  case event
299
- when :on_kw
300
- _, prev_event, _ = tokens[i - 1]
301
- next if prev_event == :on_symbeg
302
- case text
303
- when "class", "module", "def", "if", "unless", "case",
304
- "do", "for", "while", "until", "begin"
305
- if /\A(if|unless|while|until)\z/.match?(text) &&
306
- modifier?(tokens, i)
307
- next
308
- end
309
- if text == "def" && endless_method_def?(tokens, i)
310
- next
311
- end
312
- if stack.empty?
313
- return i
314
- end
315
- if stack.last != "end"
316
- raise EditorError, "#{@buffer.name}:#{line}: Unmatched #{text}"
317
- end
318
- stack.pop
319
- when "end"
320
- stack.push(text)
377
+ when :KEYWORD_CLASS, :KEYWORD_MODULE, :KEYWORD_DEF,
378
+ :KEYWORD_IF, :KEYWORD_UNLESS, :KEYWORD_CASE,
379
+ :KEYWORD_DO, :KEYWORD_DO_LOOP, :KEYWORD_FOR,
380
+ :KEYWORD_WHILE, :KEYWORD_UNTIL, :KEYWORD_BEGIN
381
+ if i > 0
382
+ _, prev_event, _ = tokens[i - 1]
383
+ next if prev_event == :SYMBOL_BEGIN
384
+ end
385
+ if event == :KEYWORD_DEF && endless_method_def?(tokens, i)
386
+ next
387
+ end
388
+ if stack.empty?
389
+ return i
390
+ end
391
+ if stack.last != :KEYWORD_END
392
+ raise EditorError, "#{@buffer.name}:#{line}: Unmatched #{text}"
321
393
  end
322
- when :on_rbrace, :on_rparen, :on_rbracket, :on_embexpr_end
323
- stack.push(text)
324
- when :on_lbrace, :on_lparen, :on_lbracket, :on_tlambeg, :on_embexpr_beg
394
+ stack.pop
395
+ when :KEYWORD_END
396
+ if i > 0
397
+ _, prev_event, _ = tokens[i - 1]
398
+ next if prev_event == :SYMBOL_BEGIN
399
+ end
400
+ stack.push(:KEYWORD_END)
401
+ when :BRACE_RIGHT, :PARENTHESIS_RIGHT, :BRACKET_RIGHT, :EMBEXPR_END
402
+ stack.push(event)
403
+ when :BRACE_LEFT, :PARENTHESIS_LEFT, :BRACKET_LEFT,
404
+ :BRACKET_LEFT_ARRAY, :LAMBDA_BEGIN, :EMBEXPR_BEGIN
325
405
  if stack.empty?
326
406
  return i
327
407
  end
328
- if stack.last != BLOCK_END[text]
408
+ if stack.last != BLOCK_END[event]
329
409
  raise EditorError, "#{@buffer.name}:#{line}: Unmatched #{text}"
330
410
  end
331
411
  stack.pop
332
412
  end
333
413
  end
334
- return nil, stack.grep_v(/[)\]]/).size
335
- end
336
-
337
- def modifier?(tokens, i)
338
- (line,), = tokens[i]
339
- ts = tokens[0...i].reverse_each.take_while { |(l,_),| l == line }
340
- t = ts.find { |_, e| e != :on_sp }
341
- t && !(t[1] == :on_op && t[2] == "=")
414
+ return nil, stack.count { |t| t != :PARENTHESIS_RIGHT && t != :BRACKET_RIGHT }
342
415
  end
343
416
 
344
417
  def endless_method_def?(tokens, i)
345
418
  ts = tokens.drop(i + 1)
346
- ts.shift while ts[0][1] == :on_sp
347
419
  _, event = ts.shift
348
- return false if event != :on_ident
349
- ts.shift while ts[0][1] == :on_sp
350
- if ts[0][1] == :on_lparen
420
+ return false if event != :IDENTIFIER && event != :METHOD_NAME
421
+ if ts[0][1] == :PARENTHESIS_LEFT
351
422
  ts.shift
352
423
  count = 1
353
424
  while count > 0
354
425
  _, event = ts.shift
355
426
  return false if event.nil?
356
427
  case event
357
- when :on_lparen
358
- count +=1
359
- when :on_rparen
360
- count -=1
428
+ when :PARENTHESIS_LEFT
429
+ count += 1
430
+ when :PARENTHESIS_RIGHT
431
+ count -= 1
361
432
  end
362
433
  end
363
- ts.shift while ts[0][1] == :on_sp
364
434
  end
365
- ts[0][1] == :on_op && ts[0][2] == "="
435
+ ts[0][1] == :EQUAL
366
436
  rescue NoMethodError # no token
367
437
  return false
368
438
  end
@@ -395,31 +465,86 @@ module Textbringer
395
465
  nil
396
466
  end
397
467
 
398
- class PartialLiteralAnalyzer < Ripper
399
- def self.in_literal?(src)
400
- new(src).in_literal?
401
- end
468
+ def in_literal?(byte_offset)
469
+ ensure_prism_tokens
470
+ ensure_literal_levels
471
+ return false if @literal_levels.empty?
472
+ i = @literal_levels.bsearch_index { |offset, _| offset > byte_offset }
473
+ i = i ? i - 1 : @literal_levels.size - 1
474
+ return false if i < 0
475
+ @literal_levels[i][1] > 0
476
+ end
402
477
 
403
- def in_literal?
404
- @literal_level = 0
405
- parse
406
- @literal_level > 0
478
+ def filter_prism_tokens_line_and_type
479
+ ensure_prism_tokens
480
+ @prism_tokens.filter_map { |token, _state|
481
+ type = token.type
482
+ next if type == :EOF
483
+ [token.location.start_line, type]
484
+ }
485
+ end
486
+
487
+ def ensure_prism_tokens
488
+ return if @prism_version == @buffer.version
489
+ source = @buffer.to_s
490
+ if source.valid_encoding?
491
+ result = Prism.parse_lex(source)
492
+ @prism_ast, @prism_tokens = result.value
493
+ else
494
+ @prism_ast = nil
495
+ @prism_tokens = []
407
496
  end
497
+ @prism_method_name_locs = nil
498
+ @prism_version = @buffer.version
499
+ @literal_levels_version = nil
500
+ end
408
501
 
409
- private
502
+ def ensure_method_name_locs
503
+ return if @prism_method_name_locs
504
+ @prism_method_name_locs = {}
505
+ return unless @prism_ast
506
+ collect_method_name_locs(@prism_ast)
507
+ end
410
508
 
411
- %w(embdoc heredoc tstring regexp
412
- symbols qsymbols words qwords).each do |name|
413
- define_method("on_#{name}_beg") do |token|
414
- @literal_level += 1
415
- end
509
+ def collect_method_name_locs(node)
510
+ if node.type == :def_node
511
+ @prism_method_name_locs[node.name_loc.start_offset] = true
512
+ elsif node.type == :alias_method_node
513
+ add_alias_method_name_locs(node.new_name)
514
+ add_alias_method_name_locs(node.old_name)
515
+ elsif (node.type == :call_node &&
516
+ !(node.call_operator_loc.nil? && OPERATORS.include?(node.name)) && # exclude operators
517
+ !((node.call_operator_loc.nil? || node.call_operator_loc.slice == "::") && # exclude constants
518
+ /\A\p{Upper}/.match?(node.name))) ||
519
+ node.type == :call_operator_write_node ||
520
+ node.type == :call_and_write_node ||
521
+ node.type == :call_or_write_node
522
+ @prism_method_name_locs[node.message_loc.start_offset] = true
523
+ end
524
+ node.compact_child_nodes.each { |child| collect_method_name_locs(child) }
525
+ end
526
+
527
+ def add_alias_method_name_locs(node)
528
+ if node.type == :symbol_node && node.opening_loc.nil?
529
+ @prism_method_name_locs[node.value_loc.start_offset] = true
416
530
  end
531
+ end
417
532
 
418
- %w(embdoc heredoc tstring regexp).each do |name|
419
- define_method("on_#{name}_end") do |token|
420
- @literal_level -= 1
533
+ def ensure_literal_levels
534
+ return if @literal_levels_version == @prism_version
535
+ level = 0
536
+ @literal_levels = []
537
+ @prism_tokens&.each do |token, _state|
538
+ type = token.type
539
+ if LITERAL_BEGIN_TYPES.include?(type)
540
+ level += 1
541
+ elsif LITERAL_END_TYPES.include?(type)
542
+ next if type == :HEREDOC_END && token.value.empty?
543
+ level -= 1
421
544
  end
545
+ @literal_levels << [token.location.start_offset, level]
422
546
  end
547
+ @literal_levels_version = @prism_version
423
548
  end
424
549
  end
425
550
  end