rucoa 0.12.0 → 0.13.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5097a136a7c60eb420569df3c2c115de4716e4e33c2db0ed14e402aca3b7a09
4
- data.tar.gz: '0609d97e8b5167b1c0f1217a517c995f6c0ef13a6aef52f483e5c3cbe850e0c8'
3
+ metadata.gz: 3829019e74a6fedcb5548ce3630b3c145c12ab40c0df99b63fd9747975f0a71d
4
+ data.tar.gz: 4cd7373ed7239adeb955caf7c373fe5da6d90ca0d92dc34801d2179db0387bbf
5
5
  SHA512:
6
- metadata.gz: c793f79584557be25e5584343d56d8ab94d195ddd0eadf32f96e0426e3db3749d0f4a502e9d682d7ed17f5926754bdfba9ea4620e60b323224d5d6a2b5802d12
7
- data.tar.gz: 42c2e6927d684f63a541c22b2c3d56404acf126d48d665d4573b7def31c3e11a2ff92ce230441e9d2cfd5ee508174a0bcb1b965ab22d1a38156efeed1c6c1995
6
+ metadata.gz: a3bbe0f9cb45652ab45706b3afa87853ba3e4d8e33b5894080772a054463bc3828db454da0a0af292982f4de9b8fa18a0353d1cf527e6961455d08c68eeeae63
7
+ data.tar.gz: 3b23e8f98a5a57a7f236f22ba7e8e606a43f477e789646740a99cdbb30f0bf5adb21779c1ea2e0f828112e32e4dd242eae28fde14120870d8163f0bc1adbc769
data/.rubocop.yml CHANGED
@@ -8,6 +8,9 @@ AllCops:
8
8
  NewCops: enable
9
9
  TargetRubyVersion: 2.7
10
10
 
11
+ Gemspec/RequireMFA:
12
+ Enabled: false
13
+
11
14
  Layout/LineLength:
12
15
  Enabled: false
13
16
 
@@ -47,6 +50,9 @@ Sevencop/MethodDefinitionArgumentsMultiline:
47
50
  Sevencop/MethodDefinitionKeywordArgumentOrdered:
48
51
  Enabled: true
49
52
 
53
+ Sevencop/RequireOrdered:
54
+ Enabled: true
55
+
50
56
  Style/Documentation:
51
57
  Enabled: false
52
58
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rucoa (0.12.0)
4
+ rucoa (0.13.0)
5
5
  parser
6
6
  rbs
7
7
  rubocop
@@ -54,7 +54,7 @@ GEM
54
54
  rubocop-rspec (2.13.2)
55
55
  rubocop (~> 1.33)
56
56
  ruby-progressbar (1.11.0)
57
- sevencop (0.15.0)
57
+ sevencop (0.16.0)
58
58
  rubocop
59
59
  unicode-display_width (2.3.0)
60
60
  webrick (1.7.0)
data/README.md CHANGED
@@ -35,6 +35,12 @@ Run "Format Document" command or enable "Format On Save" in the settings to auto
35
35
 
36
36
  ![demo](images/document-formatting.gif)
37
37
 
38
+ ### Highlight
39
+
40
+ Highlights corresponding keywords.
41
+
42
+ ![demo](images/document-highlight.gif)
43
+
38
44
  ### Selection
39
45
 
40
46
  Run "Expand Selection" command to select appropriate ranges.
@@ -66,10 +72,6 @@ Provides completion items for constant names and method names.
66
72
 
67
73
  Provides "Go to Definition" command to jump to the definition.
68
74
 
69
- ### Highlight
70
-
71
- Highlights corresponding keywords.
72
-
73
75
  ### Hover
74
76
 
75
77
  Shows documentation for the symbol under the cursor.
Binary file
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module Rucoa
4
6
  module Handlers
5
7
  class TextDocumentDocumentHighlightHandler < Base
6
8
  include HandlerConcerns::TextDocumentPositionParameters
7
9
  include HandlerConcerns::TextDocumentUriParameters
8
10
 
9
- DOCUMENT_HIGHLIGHT_KIND_READ = 2
10
- DOCUMENT_HIGHLIGHT_KIND_TEXT = 1
11
- DOCUMENT_HIGHLIGHT_KIND_WRITE = 3
12
-
13
11
  # @return [void]
14
12
  def call
15
13
  respond(document_highlights)
@@ -17,21 +15,11 @@ module Rucoa
17
15
 
18
16
  private
19
17
 
20
- # @return [Array<Hash>]
18
+ # @return [Array]
21
19
  def document_highlights
22
- parser_ranges.map do |parser_range|
23
- {
24
- 'kind' => DOCUMENT_HIGHLIGHT_KIND_TEXT,
25
- 'range' => Range.from_parser_range(parser_range).to_vscode_range
26
- }
27
- end
28
- end
29
-
30
- # @return [Array<Parser::Source::Range>]
31
- def parser_ranges
32
20
  return [] unless reponsible?
33
21
 
34
- NodeToRangesMappers::AnyMapper.call(node)
22
+ NodeToHighlightsMappers::AnyMapper.call(node).map(&:to_vscode_highlight)
35
23
  end
36
24
 
37
25
  # @return [Boolean]
@@ -39,11 +27,57 @@ module Rucoa
39
27
  configuration.enables_highlight?
40
28
  end
41
29
 
42
- module NodeToRangesMappers
30
+ module Highlights
31
+ class Base
32
+ # @param parser_range [Parser::Source::Range]
33
+ def initialize(parser_range:)
34
+ @parser_range = parser_range
35
+ end
36
+
37
+ private
38
+
39
+ # @return [Hash]
40
+ def vscode_range
41
+ Range.from_parser_range(@parser_range).to_vscode_range
42
+ end
43
+ end
44
+
45
+ class TextHighlight < Base
46
+ # @return [Hash]
47
+ def to_vscode_highlight
48
+ {
49
+ kind: 1,
50
+ range: vscode_range
51
+ }
52
+ end
53
+ end
54
+
55
+ class ReadHighlight < Base
56
+ # @return [Hash]
57
+ def to_vscode_highlight
58
+ {
59
+ kind: 2,
60
+ range: vscode_range
61
+ }
62
+ end
63
+ end
64
+
65
+ class WriteHighlight < Base
66
+ # @return [Hash]
67
+ def to_vscode_highlight
68
+ {
69
+ kind: 3,
70
+ range: vscode_range
71
+ }
72
+ end
73
+ end
74
+ end
75
+
76
+ module NodeToHighlightsMappers
43
77
  class Base
44
78
  class << self
45
79
  # @param node [Rucoa::Nodes::Base]
46
- # @return [Array<Parser::Source::Range>]
80
+ # @return [Array]
47
81
  def call(node)
48
82
  new(node).call
49
83
  end
@@ -54,14 +88,14 @@ module Rucoa
54
88
  @node = node
55
89
  end
56
90
 
57
- # @return [Array<Parser::Source::Range>]
91
+ # @return [Array]
58
92
  def call
59
93
  raise ::NotImplementedError
60
94
  end
61
95
  end
62
96
 
63
97
  class AnyMapper < Base
64
- # @return [Array<Parser::Source::Range>]
98
+ # @return [Array]
65
99
  def call
66
100
  case @node
67
101
  when Nodes::BeginNode, Nodes::BlockNode
@@ -70,14 +104,22 @@ module Rucoa
70
104
  CaseMapper.call(@node)
71
105
  when Nodes::ClassNode, Nodes::ModuleNode
72
106
  ModuleMapper.call(@node)
107
+ when Nodes::CvarNode, Nodes::CvasgnNode
108
+ ClassVariableMapper.call(@node)
73
109
  when Nodes::DefNode
74
110
  DefMapper.call(@node)
75
111
  when Nodes::EnsureNode, Nodes::ResbodyNode, Nodes::RescueNode, Nodes::WhenNode
76
112
  AnyMapper.call(@node.parent)
77
113
  when Nodes::ForNode
78
114
  ForMapper.call(@node)
115
+ when Nodes::GvarNode, Nodes::GvasgnNode
116
+ GlobalVariableMapper.call(@node)
79
117
  when Nodes::IfNode
80
118
  IfMapper.call(@node)
119
+ when Nodes::IvarNode, Nodes::IvasgnNode
120
+ InstanceVariableMapper.call(@node)
121
+ when Nodes::ArgNode, Nodes::LvarNode, Nodes::LvasgnNode
122
+ LocalVariableMapper.call(@node)
81
123
  when Nodes::SendNode
82
124
  SendMapper.call(@node)
83
125
  when Nodes::UntilNode, Nodes::WhileNode
@@ -89,7 +131,7 @@ module Rucoa
89
131
  end
90
132
 
91
133
  class BeginMapper < Base
92
- # @return [Array<Parser::Source::Range>]
134
+ # @return [Array]
93
135
  def call
94
136
  [
95
137
  range_begin,
@@ -97,7 +139,9 @@ module Rucoa
97
139
  range_else,
98
140
  range_ensure,
99
141
  range_end
100
- ].compact
142
+ ].compact.map do |parser_range|
143
+ Highlights::TextHighlight.new(parser_range: parser_range)
144
+ end
101
145
  end
102
146
 
103
147
  private
@@ -126,7 +170,7 @@ module Rucoa
126
170
  @node.ensure.location.keyword
127
171
  end
128
172
 
129
- # @return [Array<Parser::Source::Range>]
173
+ # @return [Array]
130
174
  def ranges_resbody
131
175
  return [] unless rescue_node
132
176
 
@@ -151,19 +195,21 @@ module Rucoa
151
195
  end
152
196
 
153
197
  class CaseMapper < Base
154
- # @return [Array<Parser::Source::Range>]
198
+ # @return [Array]
155
199
  def call
156
200
  [
157
201
  @node.location.keyword,
158
202
  *ranges_when,
159
203
  @node.location.else,
160
204
  @node.location.end
161
- ].compact
205
+ ].compact.map do |parser_range|
206
+ Highlights::TextHighlight.new(parser_range: parser_range)
207
+ end
162
208
  end
163
209
 
164
210
  private
165
211
 
166
- # @return [Array<Parser::Source::Range>]
212
+ # @return [Array]
167
213
  def ranges_when
168
214
  @node.whens.map do |when_node|
169
215
  when_node.location.keyword
@@ -172,22 +218,35 @@ module Rucoa
172
218
  end
173
219
 
174
220
  class IfMapper < Base
175
- # @return [Array<Parser::Source::Range>]
221
+ # @return [Array]
176
222
  def call
177
223
  return AnyMapper.call(@node.parent) if @node.elsif?
224
+ return [] if @node.modifier?
178
225
 
179
226
  [
180
- @node.location.keyword,
181
- *ranges_elsif,
182
- @node.location.else,
183
- @node.location.end
227
+ highlight_keyword,
228
+ *highlights_elsif,
229
+ highlight_else,
230
+ highlight_end
184
231
  ].compact
185
232
  end
186
233
 
187
234
  private
188
235
 
189
- # @return [Array<Parser::Source::Range>]
190
- def ranges_elsif
236
+ def highlight_else
237
+ Highlights::TextHighlight.new(parser_range: @node.location.else) if @node.location.else
238
+ end
239
+
240
+ def highlight_end
241
+ Highlights::TextHighlight.new(parser_range: @node.location.end)
242
+ end
243
+
244
+ def highlight_keyword
245
+ Highlights::TextHighlight.new(parser_range: @node.location.keyword)
246
+ end
247
+
248
+ # @return [Array]
249
+ def highlights_elsif
191
250
  return [] unless @node.elsif
192
251
 
193
252
  ElsifMapper.call(@node.elsif)
@@ -195,38 +254,250 @@ module Rucoa
195
254
  end
196
255
 
197
256
  class ElsifMapper < IfMapper
198
- # @return [Array<Parser::Source::Range>]
257
+ # @return [Array]
199
258
  def call
200
259
  [
201
- *ranges_elsif,
202
- @node.location.else
260
+ *highlights_elsif,
261
+ highlight_else
203
262
  ].compact
204
263
  end
205
264
  end
206
265
 
207
266
  class ForMapper < Base
208
- # @return [Array<Parser::Source::Range>]
267
+ # @return [Array]
209
268
  def call
210
269
  [
211
270
  @node.location.keyword,
212
271
  @node.location.in,
213
272
  @node.location.end
273
+ ].compact.map do |parser_range|
274
+ Highlights::TextHighlight.new(parser_range: parser_range)
275
+ end
276
+ end
277
+ end
278
+
279
+ class GlobalVariableMapper < Base
280
+ # @return [Array]
281
+ def call
282
+ nodes.map do |node|
283
+ case node
284
+ when Nodes::GvarNode
285
+ Highlights::ReadHighlight
286
+ when Nodes::GvasgnNode
287
+ Highlights::WriteHighlight
288
+ end.new(parser_range: node.location.name)
289
+ end
290
+ end
291
+
292
+ private
293
+
294
+ # @return [Rucoa::Nodes::Base, nil]
295
+ def global_variable_scopable_node
296
+ @node.ancestors.last
297
+ end
298
+
299
+ # @return [Enumerable<Rucoa::Nodes::Base>]
300
+ def nodes
301
+ return [] unless global_variable_scopable_node
302
+
303
+ global_variable_scopable_node.each_descendant_node(:gvar, :gvasgn).select do |node|
304
+ node.name == @node.name
305
+ end
306
+ end
307
+ end
308
+
309
+ class InstanceVariableMapper < Base
310
+ # @return [Array]
311
+ def call
312
+ nodes.map do |node|
313
+ case node
314
+ when Nodes::IvarNode
315
+ Highlights::ReadHighlight
316
+ when Nodes::IvasgnNode
317
+ Highlights::WriteHighlight
318
+ end.new(parser_range: node.location.name)
319
+ end
320
+ end
321
+
322
+ private
323
+
324
+ # @return [Rucoa::Nodes::Base, nil]
325
+ def instance_variable_scopable_node
326
+ @instance_variable_scopable_node ||= @node.each_ancestor(:class, :module).first
327
+ end
328
+
329
+ # @todo Stop descendant searching if scope boundary node is found (e.g. class, def, etc.).
330
+ # @return [Enumerable<Rucoa::Nodes::Base>]
331
+ def nodes
332
+ return [] unless instance_variable_scopable_node
333
+
334
+ instance_variable_scopable_node.each_descendant_node(:ivar, :ivasgn).select do |node|
335
+ node.name == @node.name
336
+ end
337
+ end
338
+ end
339
+
340
+ class ClassVariableMapper < Base
341
+ # @return [Array]
342
+ def call
343
+ nodes.map do |node|
344
+ case node
345
+ when Nodes::CvarNode
346
+ Highlights::ReadHighlight
347
+ when Nodes::CvasgnNode
348
+ Highlights::WriteHighlight
349
+ end.new(parser_range: node.location.name)
350
+ end
351
+ end
352
+
353
+ private
354
+
355
+ # @return [Rucoa::Nodes::Base, nil]
356
+ def instance_variable_scopable_node
357
+ @node.each_ancestor(:class, :module).first
358
+ end
359
+
360
+ # @return [Enumerable<Rucoa::Nodes::Base>]
361
+ def nodes
362
+ return [] unless instance_variable_scopable_node
363
+
364
+ instance_variable_scopable_node.each_descendant_node(:cvar, :cvasgn).select do |node|
365
+ node.name == @node.name
366
+ end
367
+ end
368
+ end
369
+
370
+ class LocalVariableMapper < Base
371
+ SCOPE_BOUNDARY_NODE_TYPES = ::Set[
372
+ :class,
373
+ :def,
374
+ :defs,
375
+ :module,
376
+ ]
377
+
378
+ # @return [Array]
379
+ def call
380
+ nodes.map do |node|
381
+ case node
382
+ when Nodes::LvarNode
383
+ Highlights::ReadHighlight
384
+ when Nodes::ArgNode, Nodes::LvasgnNode
385
+ Highlights::WriteHighlight
386
+ end.new(parser_range: node.location.name)
387
+ end
388
+ end
389
+
390
+ private
391
+
392
+ # @return [Rucoa::Nodes::ArgNode, Rucoa::Nodes::LvasgnNode, nil]
393
+ def assignment_node
394
+ @assignment_node ||=
395
+ case @node
396
+ when Nodes::ArgNode, Nodes::LvasgnNode
397
+ @node
398
+ when Nodes::LvarNode
399
+ (
400
+ [@node] + @node.ancestors.take_while do |node|
401
+ !SCOPE_BOUNDARY_NODE_TYPES.include?(node.type)
402
+ end
403
+ ).find do |node|
404
+ case node
405
+ when Nodes::ArgNode, Nodes::LvasgnNode
406
+ node.name == @node.name
407
+ when Nodes::BlockNode, Nodes::DefNode
408
+ target = node.arguments.find do |argument|
409
+ argument.name == @node.name
410
+ end
411
+ break target if target
412
+ else
413
+ target = node.previous_sibling_nodes.reverse.find do |sibling_node|
414
+ lvasgn_node = [
415
+ sibling_node,
416
+ *sibling_node.descendant_nodes
417
+ ].reverse.find do |sibling_or_sibling_descendant|
418
+ case sibling_or_sibling_descendant
419
+ when Nodes::LvasgnNode
420
+ break sibling_or_sibling_descendant if sibling_or_sibling_descendant.name == @node.name
421
+ end
422
+ end
423
+ break lvasgn_node if lvasgn_node
424
+ end
425
+ break target if target
426
+ end
427
+ end || @node.each_ancestor(:def, :defs).first&.then do |node|
428
+ break node.arguments.find do |argument|
429
+ argument.name == @node.name
430
+ end
431
+ end
432
+ end
433
+ end
434
+
435
+ # @return [Enumerable<Rucoa::Nodes::ArgNode, Rucoa::Nodes::LvarNode, Rucoa::Nodes::LvasgnNode>]
436
+ def nodes
437
+ [
438
+ assignment_node,
439
+ *reference_nodes
214
440
  ].compact
215
441
  end
442
+
443
+ # @return [Enumerable<Rucoa::Nodes::LvarNode>]
444
+ def reference_nodes
445
+ return [] unless assignment_node
446
+
447
+ case assignment_node
448
+ when Nodes::ArgNode
449
+ assignment_node.each_ancestor(:block, :def, :defs).first.body_children
450
+ when Nodes::LvasgnNode
451
+ (
452
+ [assignment_node] + assignment_node.ancestors.take_while do |node|
453
+ !SCOPE_BOUNDARY_NODE_TYPES.include?(node.type)
454
+ end
455
+ ).flat_map(&:next_sibling_nodes)
456
+ end.flat_map do |node|
457
+ [
458
+ node,
459
+ *node.descendant_nodes
460
+ ]
461
+ end.take_while do |node| # FIXME: flat_map and take_while are not correct solution for shadowing.
462
+ case node
463
+ when Nodes::ArgNode, Nodes::LvasgnNode
464
+ node.equal?(assignment_node) || node.name != assignment_node.name
465
+ else
466
+ true
467
+ end
468
+ end.select do |node|
469
+ case node
470
+ when Nodes::LvarNode
471
+ node.name == @node.name
472
+ end
473
+ end
474
+ end
475
+
476
+ class UnshadowedNodeVisitor
477
+ def initialize(
478
+ node:,
479
+ &block
480
+ )
481
+ @block = block
482
+ @node = node
483
+ end
484
+ end
216
485
  end
217
486
 
218
487
  class ModuleMapper < Base
219
- # @return [Array<Parser::Source::Range>]
488
+ # @return [Array]
220
489
  def call
221
490
  [
222
491
  @node.location.keyword,
223
492
  @node.location.end
224
- ]
493
+ ].map do |parser_range|
494
+ Highlights::TextHighlight.new(parser_range: parser_range)
495
+ end
225
496
  end
226
497
  end
227
498
 
228
499
  class SendMapper < Base
229
- # @return [Array<Parser::Source::Range>]
500
+ # @return [Array]
230
501
  def call
231
502
  return [] unless @node.block
232
503
 
@@ -235,19 +506,23 @@ module Rucoa
235
506
  end
236
507
 
237
508
  class WhenMapper < Base
238
- # @return [Array<Parser::Source::Range>]
509
+ # @return [Array]
239
510
  def call
240
511
  CaseMapper.call(@node.parent)
241
512
  end
242
513
  end
243
514
 
244
515
  class WhileMapper < Base
245
- # @return [Array<Parser::Source::Range>]
516
+ # @return [Array]
246
517
  def call
518
+ return [] if @node.modifier?
519
+
247
520
  [
248
521
  @node.location.keyword,
249
522
  @node.location.end
250
- ]
523
+ ].map do |parser_range|
524
+ Highlights::TextHighlight.new(parser_range: parser_range)
525
+ end
251
526
  end
252
527
  end
253
528
  end
@@ -38,7 +38,7 @@ module Rucoa
38
38
  line: 1
39
39
  ),
40
40
  Position.new(
41
- column: @source.content.lines.last.length,
41
+ column: @source.content.lines.last&.length || 0,
42
42
  line: @source.content.lines.count + 1
43
43
  )
44
44
  ).to_vscode_range
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module NodeConcerns
5
+ module Modifier
6
+ # @return [Boolean]
7
+ # @example returns true on modifier if node
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY,
10
+ # 1 if true
11
+ # RUBY
12
+ # uri: 'file:///path/to/example.rb'
13
+ # ).node_at(
14
+ # Rucoa::Position.new(
15
+ # column: 2,
16
+ # line: 1
17
+ # )
18
+ # )
19
+ # expect(node).to be_modifier
20
+ # @example returns false on non-modifier if node
21
+ # node = Rucoa::Source.new(
22
+ # content: <<~RUBY,
23
+ # if true
24
+ # 1
25
+ # end
26
+ # RUBY
27
+ # uri: 'file:///path/to/example.rb'
28
+ # ).root_node
29
+ # expect(node).not_to be_modifier
30
+ def modifier?
31
+ location.end.nil?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module NodeConcerns
5
+ module Variable
6
+ # @return [String]
7
+ # @example returns variable name
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY,
10
+ # foo = 1
11
+ # foo
12
+ # RUBY
13
+ # uri: 'file:///path/to/example.rb'
14
+ # ).node_at(
15
+ # Rucoa::Position.new(
16
+ # column: 0,
17
+ # line: 2
18
+ # )
19
+ # )
20
+ # expect(node.name).to eq('foo')
21
+ def name
22
+ children[0].to_s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -3,7 +3,9 @@
3
3
  module Rucoa
4
4
  module NodeConcerns
5
5
  autoload :Body, 'rucoa/node_concerns/body'
6
+ autoload :Modifier, 'rucoa/node_concerns/modifier'
6
7
  autoload :QualifiedName, 'rucoa/node_concerns/qualified_name'
7
8
  autoload :Rescue, 'rucoa/node_concerns/rescue'
9
+ autoload :Variable, 'rucoa/node_concerns/variable'
8
10
  end
9
11
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class ArgNode < Base
6
+ # @return [String]
7
+ # @example returns variable name
8
+ # node = Rucoa::Source.new(
9
+ # content: <<~RUBY,
10
+ # def foo(bar)
11
+ # end
12
+ # RUBY
13
+ # uri: 'file:///path/to/example.rb'
14
+ # ).node_at(
15
+ # Rucoa::Position.new(
16
+ # column: 8,
17
+ # line: 1
18
+ # )
19
+ # )
20
+ # expect(node.name).to eq('bar')
21
+ def name
22
+ children[0].to_s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class ArgsNode < Base
6
+ include ::Enumerable
7
+
8
+ # @note For `Enumerable`.
9
+ def each(&block)
10
+ children.each(&block)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -20,8 +20,13 @@ module Rucoa
20
20
  end
21
21
 
22
22
  # @return [Array<Rucoa::Nodes::Base>]
23
- def descendants
24
- each_descendant.to_a
23
+ def child_nodes
24
+ each_child_node.to_a
25
+ end
26
+
27
+ # @return [Array<Rucoa::Nodes::Base>]
28
+ def descendant_nodes
29
+ each_descendant_node.to_a
25
30
  end
26
31
 
27
32
  # @param types [Array<Symbol>]
@@ -53,13 +58,13 @@ module Rucoa
53
58
  # @param types [Array<Symbol>]
54
59
  # @return [Rucoa::Nodes::Base] if a block is given
55
60
  # @return [Enumerator] if no block is given
56
- def each_descendant(
61
+ def each_descendant_node(
57
62
  *types,
58
63
  &block
59
64
  )
60
65
  return to_enum(__method__, *types) unless block
61
66
 
62
- visit_descendants(types, &block)
67
+ visit_descendant_nodes(types, &block)
63
68
  self
64
69
  end
65
70
 
@@ -126,6 +131,32 @@ module Rucoa
126
131
  module_nesting.first || 'Object'
127
132
  end
128
133
 
134
+ # @return [Array<Rucoa::Nodes::Base>]
135
+ # @example returns next siblings
136
+ # node = Rucoa::Source.new(
137
+ # content: <<~RUBY,
138
+ # def foo
139
+ # a
140
+ # b
141
+ # c
142
+ # d
143
+ # e
144
+ # end
145
+ # RUBY
146
+ # uri: 'file:///path/to/example.rb'
147
+ # ).node_at(
148
+ # Rucoa::Position.new(
149
+ # column: 2,
150
+ # line: 4
151
+ # )
152
+ # )
153
+ # expect(node.next_sibling_nodes.map(&:name)).to eq(%w[d e])
154
+ def next_sibling_nodes
155
+ return [] unless parent
156
+
157
+ parent.child_nodes[(sibling_node_index + 1)..]
158
+ end
159
+
129
160
  # @return [Rucoa::Nodes::Base, nil]
130
161
  def parent
131
162
  @mutable_attributes[:parent]
@@ -136,6 +167,32 @@ module Rucoa
136
167
  @mutable_attributes[:parent] = node
137
168
  end
138
169
 
170
+ # @return [Array<Rucoa::Nodes::Base>]
171
+ # @example returns previous siblings
172
+ # node = Rucoa::Source.new(
173
+ # content: <<~RUBY,
174
+ # def foo
175
+ # a
176
+ # b
177
+ # c
178
+ # d
179
+ # e
180
+ # end
181
+ # RUBY
182
+ # uri: 'file:///path/to/example.rb'
183
+ # ).node_at(
184
+ # Rucoa::Position.new(
185
+ # column: 2,
186
+ # line: 4
187
+ # )
188
+ # )
189
+ # expect(node.previous_sibling_nodes.map(&:name)).to eq(%w[a b])
190
+ def previous_sibling_nodes
191
+ return [] unless parent
192
+
193
+ parent.child_nodes[0...sibling_node_index]
194
+ end
195
+
139
196
  # @note Override.
140
197
  # Some nodes change their type depending on the context.
141
198
  # For example, `const` node can be `casgn` node.
@@ -155,21 +212,28 @@ module Rucoa
155
212
 
156
213
  protected
157
214
 
158
- # Visit all descendants.
215
+ # Visit all descendant_nodes.
159
216
  # @param types [Array<Symbol>]
160
217
  # @return [void]
161
- def visit_descendants(
218
+ def visit_descendant_nodes(
162
219
  types,
163
220
  &block
164
221
  )
165
222
  each_child_node do |child|
166
223
  yield(child) if types.empty? || types.include?(child.type)
167
- child.visit_descendants(types, &block)
224
+ child.visit_descendant_nodes(types, &block)
168
225
  end
169
226
  end
170
227
 
171
228
  private
172
229
 
230
+ # @return [Integer, nil]
231
+ def sibling_node_index
232
+ parent&.child_nodes&.index do |child|
233
+ child.equal?(self)
234
+ end
235
+ end
236
+
173
237
  # Visits all ancestors.
174
238
  # @param types [Array<Symbol>]
175
239
  # @return [void]
@@ -6,6 +6,20 @@ module Rucoa
6
6
  include NodeConcerns::Body
7
7
  include NodeConcerns::Rescue
8
8
 
9
+ # @return [Array<Rucoa::Nodes::ArgNode>]
10
+ # @example returns arguments
11
+ # node = Rucoa::Source.new(
12
+ # content: <<~RUBY,
13
+ # foo do |bar, baz|
14
+ # end
15
+ # RUBY
16
+ # uri: 'file:///path/to/example.rb'
17
+ # ).root_node
18
+ # expect(node.arguments.map(&:name)).to eq(%w[bar baz])
19
+ def arguments
20
+ children[-2]
21
+ end
22
+
9
23
  # @return [Rucoa::Nodes::SendNode]
10
24
  # @example returns send node
11
25
  # node = Rucoa::Source.new(
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class CvarNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class CvasgnNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -6,6 +6,20 @@ module Rucoa
6
6
  include NodeConcerns::Body
7
7
  include NodeConcerns::Rescue
8
8
 
9
+ # @return [Array<Rucoa::Nodes::ArgNode>]
10
+ # @example returns arguments
11
+ # node = Rucoa::Source.new(
12
+ # content: <<~RUBY,
13
+ # def foo(bar, baz)
14
+ # end
15
+ # RUBY
16
+ # uri: 'file:///path/to/example.rb'
17
+ # ).root_node
18
+ # expect(node.arguments.map(&:name)).to eq(%w[bar baz])
19
+ def arguments
20
+ children[-2]
21
+ end
22
+
9
23
  # @return [String]
10
24
  def method_marker
11
25
  if singleton?
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class GvarNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class GvasgnNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -3,6 +3,8 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class IfNode < Base
6
+ include NodeConcerns::Modifier
7
+
6
8
  # @return [Rucoa::Nodes::Base, nil]
7
9
  def branch_else
8
10
  children[2]
@@ -25,7 +27,7 @@ module Rucoa
25
27
 
26
28
  # @return [Boolean]
27
29
  def elsif?
28
- parent.is_a?(Nodes::IfNode) && eql?(parent.elsif)
30
+ parent.is_a?(Nodes::IfNode) && equal?(parent.elsif)
29
31
  end
30
32
  end
31
33
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class IvarNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class IvasgnNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -3,24 +3,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class LvarNode < Base
6
- # @return [String]
7
- # @example returns local variable name
8
- # node = Rucoa::Source.new(
9
- # content: <<~RUBY,
10
- # foo = 1
11
- # foo
12
- # RUBY
13
- # uri: 'file:///path/to/example.rb'
14
- # ).node_at(
15
- # Rucoa::Position.new(
16
- # column: 2,
17
- # line: 2
18
- # )
19
- # )
20
- # expect(node.name).to eq('foo')
21
- def name
22
- children[0].to_s
23
- end
6
+ include NodeConcerns::Variable
24
7
  end
25
8
  end
26
9
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rucoa
4
+ module Nodes
5
+ class LvasgnNode < Base
6
+ include NodeConcerns::Variable
7
+ end
8
+ end
9
+ end
@@ -97,7 +97,7 @@ module Rucoa
97
97
 
98
98
  # @return [Boolean]
99
99
  def called_with_block?
100
- parent.is_a?(Nodes::BlockNode) && eql?(parent.send_node)
100
+ parent.is_a?(Nodes::BlockNode) && equal?(parent.send_node)
101
101
  end
102
102
  end
103
103
  end
@@ -3,6 +3,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class UntilNode < Base
6
+ include NodeConcerns::Modifier
6
7
  end
7
8
  end
8
9
  end
@@ -3,6 +3,7 @@
3
3
  module Rucoa
4
4
  module Nodes
5
5
  class WhileNode < Base
6
+ include NodeConcerns::Modifier
6
7
  end
7
8
  end
8
9
  end
data/lib/rucoa/nodes.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Rucoa
4
4
  module Nodes
5
+ autoload :ArgNode, 'rucoa/nodes/arg_node'
6
+ autoload :ArgsNode, 'rucoa/nodes/args_node'
5
7
  autoload :Base, 'rucoa/nodes/base'
6
8
  autoload :BeginNode, 'rucoa/nodes/begin_node'
7
9
  autoload :BlockNode, 'rucoa/nodes/block_node'
@@ -10,11 +12,18 @@ module Rucoa
10
12
  autoload :CbaseNode, 'rucoa/nodes/cbase_node'
11
13
  autoload :ClassNode, 'rucoa/nodes/class_node'
12
14
  autoload :ConstNode, 'rucoa/nodes/const_node'
15
+ autoload :CvarNode, 'rucoa/nodes/cvar_node'
16
+ autoload :CvasgnNode, 'rucoa/nodes/cvasgn_node'
13
17
  autoload :DefNode, 'rucoa/nodes/def_node'
14
18
  autoload :EnsureNode, 'rucoa/nodes/ensure_node'
15
19
  autoload :ForNode, 'rucoa/nodes/for_node'
20
+ autoload :GvarNode, 'rucoa/nodes/gvar_node'
21
+ autoload :GvasgnNode, 'rucoa/nodes/gvasgn_node'
16
22
  autoload :IfNode, 'rucoa/nodes/if_node'
23
+ autoload :IvarNode, 'rucoa/nodes/ivar_node'
24
+ autoload :IvasgnNode, 'rucoa/nodes/ivasgn_node'
17
25
  autoload :LvarNode, 'rucoa/nodes/lvar_node'
26
+ autoload :LvasgnNode, 'rucoa/nodes/lvasgn_node'
18
27
  autoload :ModuleNode, 'rucoa/nodes/module_node'
19
28
  autoload :ResbodyNode, 'rucoa/nodes/resbody_node'
20
29
  autoload :RescueNode, 'rucoa/nodes/rescue_node'
@@ -5,6 +5,8 @@ require 'parser/current'
5
5
  module Rucoa
6
6
  class ParserBuilder < ::Parser::Builders::Default
7
7
  NODE_CLASS_BY_TYPE = {
8
+ arg: Nodes::ArgNode,
9
+ args: Nodes::ArgsNode,
8
10
  begin: Nodes::BeginNode,
9
11
  block: Nodes::BlockNode,
10
12
  case: Nodes::CaseNode,
@@ -13,13 +15,21 @@ module Rucoa
13
15
  class: Nodes::ClassNode,
14
16
  const: Nodes::ConstNode,
15
17
  csend: Nodes::SendNode,
18
+ cvar: Nodes::CvarNode,
19
+ cvasgn: Nodes::CvasgnNode,
16
20
  def: Nodes::DefNode,
17
21
  defs: Nodes::DefNode,
18
22
  ensure: Nodes::EnsureNode,
19
23
  for: Nodes::ForNode,
24
+ gvar: Nodes::GvarNode,
25
+ gvasgn: Nodes::GvasgnNode,
20
26
  if: Nodes::IfNode,
27
+ ivar: Nodes::IvarNode,
28
+ ivasgn: Nodes::IvasgnNode,
29
+ kwarg: Nodes::ArgNode,
21
30
  kwbegin: Nodes::BeginNode,
22
31
  lvar: Nodes::LvarNode,
32
+ lvasgn: Nodes::LvasgnNode,
23
33
  module: Nodes::ModuleNode,
24
34
  resbody: Nodes::ResbodyNode,
25
35
  rescue: Nodes::RescueNode,
data/lib/rucoa/source.rb CHANGED
@@ -122,7 +122,7 @@ module Rucoa
122
122
  def root_and_descendant_nodes
123
123
  return [] unless root_node
124
124
 
125
- [root_node, *root_node.descendants]
125
+ [root_node, *root_node.descendant_nodes]
126
126
  end
127
127
 
128
128
  # @return [URI]
data/lib/rucoa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rucoa
4
- VERSION = '0.12.0'
4
+ VERSION = '0.13.0'
5
5
  end
@@ -35,7 +35,7 @@ module Rucoa
35
35
  def call
36
36
  [
37
37
  @root_node,
38
- *@root_node.descendants
38
+ *@root_node.descendant_nodes
39
39
  ].flat_map do |node|
40
40
  [
41
41
  DefinitionGenerators::ClassDefinitionGenerator,
data/rucoa.gemspec CHANGED
@@ -16,7 +16,6 @@ Gem::Specification.new do |spec|
16
16
  spec.metadata['homepage_uri'] = spec.homepage
17
17
  spec.metadata['source_code_uri'] = spec.homepage
18
18
  spec.metadata['changelog_uri'] = "#{spec.homepage}/releases"
19
- spec.metadata['rubygems_mfa_required'] = 'true'
20
19
 
21
20
  spec.files = Dir.chdir(__dir__) do
22
21
  `git ls-files -z`.split("\x0").reject do |f|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rucoa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-28 00:00:00.000000000 Z
11
+ date: 2022-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -86,6 +86,7 @@ files:
86
86
  - exe/rucoa
87
87
  - images/diagnostics.gif
88
88
  - images/document-formatting.gif
89
+ - images/document-highlight.gif
89
90
  - images/document-symbol.gif
90
91
  - images/selection-ranges.gif
91
92
  - lib/rucoa.rb
@@ -131,10 +132,14 @@ files:
131
132
  - lib/rucoa/message_writer.rb
132
133
  - lib/rucoa/node_concerns.rb
133
134
  - lib/rucoa/node_concerns/body.rb
135
+ - lib/rucoa/node_concerns/modifier.rb
134
136
  - lib/rucoa/node_concerns/qualified_name.rb
135
137
  - lib/rucoa/node_concerns/rescue.rb
138
+ - lib/rucoa/node_concerns/variable.rb
136
139
  - lib/rucoa/node_inspector.rb
137
140
  - lib/rucoa/nodes.rb
141
+ - lib/rucoa/nodes/arg_node.rb
142
+ - lib/rucoa/nodes/args_node.rb
138
143
  - lib/rucoa/nodes/base.rb
139
144
  - lib/rucoa/nodes/begin_node.rb
140
145
  - lib/rucoa/nodes/block_node.rb
@@ -143,11 +148,18 @@ files:
143
148
  - lib/rucoa/nodes/cbase_node.rb
144
149
  - lib/rucoa/nodes/class_node.rb
145
150
  - lib/rucoa/nodes/const_node.rb
151
+ - lib/rucoa/nodes/cvar_node.rb
152
+ - lib/rucoa/nodes/cvasgn_node.rb
146
153
  - lib/rucoa/nodes/def_node.rb
147
154
  - lib/rucoa/nodes/ensure_node.rb
148
155
  - lib/rucoa/nodes/for_node.rb
156
+ - lib/rucoa/nodes/gvar_node.rb
157
+ - lib/rucoa/nodes/gvasgn_node.rb
149
158
  - lib/rucoa/nodes/if_node.rb
159
+ - lib/rucoa/nodes/ivar_node.rb
160
+ - lib/rucoa/nodes/ivasgn_node.rb
150
161
  - lib/rucoa/nodes/lvar_node.rb
162
+ - lib/rucoa/nodes/lvasgn_node.rb
151
163
  - lib/rucoa/nodes/module_node.rb
152
164
  - lib/rucoa/nodes/resbody_node.rb
153
165
  - lib/rucoa/nodes/rescue_node.rb
@@ -199,7 +211,6 @@ metadata:
199
211
  homepage_uri: https://github.com/r7kamura/rucoa
200
212
  source_code_uri: https://github.com/r7kamura/rucoa
201
213
  changelog_uri: https://github.com/r7kamura/rucoa/releases
202
- rubygems_mfa_required: 'true'
203
214
  post_install_message:
204
215
  rdoc_options: []
205
216
  require_paths: