rucoa 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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: