ruby-lsp 0.16.6 → 0.17.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +21 -4
  5. data/exe/ruby-lsp-check +1 -3
  6. data/exe/ruby-lsp-doctor +1 -4
  7. data/lib/core_ext/uri.rb +3 -0
  8. data/lib/ruby_indexer/lib/ruby_indexer/{collector.rb → declaration_listener.rb} +258 -140
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +101 -12
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +187 -12
  11. data/lib/ruby_indexer/ruby_indexer.rb +1 -1
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +106 -10
  13. data/lib/ruby_indexer/test/configuration_test.rb +4 -5
  14. data/lib/ruby_indexer/test/constant_test.rb +11 -8
  15. data/lib/ruby_indexer/test/index_test.rb +528 -0
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +131 -0
  17. data/lib/ruby_indexer/test/method_test.rb +93 -0
  18. data/lib/ruby_indexer/test/test_case.rb +3 -1
  19. data/lib/ruby_lsp/addon.rb +8 -8
  20. data/lib/ruby_lsp/document.rb +3 -3
  21. data/lib/ruby_lsp/internal.rb +1 -0
  22. data/lib/ruby_lsp/listeners/code_lens.rb +11 -0
  23. data/lib/ruby_lsp/listeners/completion.rb +144 -51
  24. data/lib/ruby_lsp/listeners/definition.rb +77 -12
  25. data/lib/ruby_lsp/listeners/document_highlight.rb +1 -1
  26. data/lib/ruby_lsp/listeners/document_link.rb +1 -1
  27. data/lib/ruby_lsp/listeners/hover.rb +60 -6
  28. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +59 -3
  29. data/lib/ruby_lsp/listeners/signature_help.rb +4 -4
  30. data/lib/ruby_lsp/node_context.rb +28 -0
  31. data/lib/ruby_lsp/requests/code_action_resolve.rb +73 -2
  32. data/lib/ruby_lsp/requests/code_actions.rb +16 -15
  33. data/lib/ruby_lsp/requests/completion.rb +22 -13
  34. data/lib/ruby_lsp/requests/completion_resolve.rb +26 -10
  35. data/lib/ruby_lsp/requests/definition.rb +21 -5
  36. data/lib/ruby_lsp/requests/document_highlight.rb +2 -2
  37. data/lib/ruby_lsp/requests/hover.rb +5 -6
  38. data/lib/ruby_lsp/requests/on_type_formatting.rb +8 -4
  39. data/lib/ruby_lsp/requests/signature_help.rb +3 -3
  40. data/lib/ruby_lsp/requests/support/common.rb +20 -1
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -1
  42. data/lib/ruby_lsp/server.rb +10 -4
  43. metadata +10 -8
@@ -12,10 +12,13 @@ module RubyIndexer
12
12
  class ::Bar
13
13
  FOO = 2
14
14
  end
15
+
16
+ BAR = 3 if condition
15
17
  RUBY
16
18
 
17
19
  assert_entry("FOO", Entry::Constant, "/fake/path/foo.rb:0-0:0-7")
18
20
  assert_entry("Bar::FOO", Entry::Constant, "/fake/path/foo.rb:3-2:3-9")
21
+ assert_entry("BAR", Entry::Constant, "/fake/path/foo.rb:6-0:6-7")
19
22
  end
20
23
 
21
24
  def test_constant_or_writes
@@ -119,13 +122,13 @@ module RubyIndexer
119
122
  RUBY
120
123
 
121
124
  b_const = @index["A::B"].first
122
- assert_equal(:private, b_const.visibility)
125
+ assert_equal(Entry::Visibility::PRIVATE, b_const.visibility)
123
126
 
124
127
  c_const = @index["A::C"].first
125
- assert_equal(:private, c_const.visibility)
128
+ assert_equal(Entry::Visibility::PRIVATE, c_const.visibility)
126
129
 
127
130
  d_const = @index["A::D"].first
128
- assert_equal(:public, d_const.visibility)
131
+ assert_equal(Entry::Visibility::PUBLIC, d_const.visibility)
129
132
  end
130
133
 
131
134
  def test_marking_constants_as_private_reopening_namespaces
@@ -152,13 +155,13 @@ module RubyIndexer
152
155
  RUBY
153
156
 
154
157
  a_const = @index["A::B::CONST_A"].first
155
- assert_equal(:private, a_const.visibility)
158
+ assert_equal(Entry::Visibility::PRIVATE, a_const.visibility)
156
159
 
157
160
  b_const = @index["A::B::CONST_B"].first
158
- assert_equal(:private, b_const.visibility)
161
+ assert_equal(Entry::Visibility::PRIVATE, b_const.visibility)
159
162
 
160
163
  c_const = @index["A::B::CONST_C"].first
161
- assert_equal(:private, c_const.visibility)
164
+ assert_equal(Entry::Visibility::PRIVATE, c_const.visibility)
162
165
  end
163
166
 
164
167
  def test_marking_constants_as_private_with_receiver
@@ -176,10 +179,10 @@ module RubyIndexer
176
179
  RUBY
177
180
 
178
181
  a_const = @index["A::B::CONST_A"].first
179
- assert_equal(:private, a_const.visibility)
182
+ assert_equal(Entry::Visibility::PRIVATE, a_const.visibility)
180
183
 
181
184
  b_const = @index["A::B::CONST_B"].first
182
- assert_equal(:private, b_const.visibility)
185
+ assert_equal(Entry::Visibility::PRIVATE, b_const.visibility)
183
186
  end
184
187
 
185
188
  def test_indexing_constant_aliases
@@ -299,6 +299,10 @@ module RubyIndexer
299
299
  end
300
300
 
301
301
  def test_indexing_prism_fixtures_succeeds
302
+ unless Dir.exist?("test/fixtures/prism/test/prism/fixtures")
303
+ raise "Prism fixtures not found. Run `git submodule update --init` to fetch them."
304
+ end
305
+
302
306
  fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt")
303
307
 
304
308
  fixtures.each do |fixture|
@@ -313,5 +317,529 @@ module RubyIndexer
313
317
  @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"))
314
318
  assert_empty(@index.instance_variable_get(:@entries))
315
319
  end
320
+
321
+ def test_linearized_ancestors_basic_ordering
322
+ index(<<~RUBY)
323
+ module A; end
324
+ module B; end
325
+
326
+ class Foo
327
+ prepend A
328
+ prepend B
329
+ end
330
+
331
+ class Bar
332
+ include A
333
+ include B
334
+ end
335
+ RUBY
336
+
337
+ assert_equal(
338
+ [
339
+ "B",
340
+ "A",
341
+ "Foo",
342
+ # "Object",
343
+ # "Kernel",
344
+ # "BasicObject",
345
+ ],
346
+ @index.linearized_ancestors_of("Foo"),
347
+ )
348
+
349
+ assert_equal(
350
+ [
351
+ "Bar",
352
+ "B",
353
+ "A",
354
+ # "Object",
355
+ # "Kernel",
356
+ # "BasicObject",
357
+ ],
358
+ @index.linearized_ancestors_of("Bar"),
359
+ )
360
+ end
361
+
362
+ def test_linearized_ancestors
363
+ index(<<~RUBY)
364
+ module A; end
365
+ module B; end
366
+ module C; end
367
+
368
+ module D
369
+ include A
370
+ end
371
+
372
+ module E
373
+ prepend B
374
+ end
375
+
376
+ module F
377
+ include C
378
+ include A
379
+ end
380
+
381
+ class Bar
382
+ prepend F
383
+ end
384
+
385
+ class Foo < Bar
386
+ include E
387
+ prepend D
388
+ end
389
+ RUBY
390
+
391
+ # Object, Kernel and BasicObject are intentionally commented out for now until we develop a strategy for indexing
392
+ # declarations made in C code
393
+ assert_equal(
394
+ [
395
+ "D",
396
+ "A",
397
+ "Foo",
398
+ "B",
399
+ "E",
400
+ "F",
401
+ "A",
402
+ "C",
403
+ "Bar",
404
+ # "Object",
405
+ # "Kernel",
406
+ # "BasicObject",
407
+ ],
408
+ @index.linearized_ancestors_of("Foo"),
409
+ )
410
+ end
411
+
412
+ def test_linearized_ancestors_duplicates
413
+ index(<<~RUBY)
414
+ module A; end
415
+ module B
416
+ include A
417
+ end
418
+
419
+ class Foo
420
+ include B
421
+ include A
422
+ end
423
+
424
+ class Bar
425
+ prepend B
426
+ prepend A
427
+ end
428
+ RUBY
429
+
430
+ assert_equal(
431
+ [
432
+ "Foo",
433
+ "B",
434
+ "A",
435
+ # "Object",
436
+ # "Kernel",
437
+ # "BasicObject",
438
+ ],
439
+ @index.linearized_ancestors_of("Foo"),
440
+ )
441
+
442
+ assert_equal(
443
+ [
444
+ "B",
445
+ "A",
446
+ "Bar",
447
+ # "Object",
448
+ # "Kernel",
449
+ # "BasicObject",
450
+ ],
451
+ @index.linearized_ancestors_of("Bar"),
452
+ )
453
+ end
454
+
455
+ def test_linearizing_ancestors_is_cached
456
+ index(<<~RUBY)
457
+ module C; end
458
+ module A; end
459
+ module B
460
+ include A
461
+ end
462
+
463
+ class Foo
464
+ include B
465
+ include A
466
+ end
467
+ RUBY
468
+
469
+ @index.linearized_ancestors_of("Foo")
470
+ ancestors = @index.instance_variable_get(:@ancestors)
471
+ assert(ancestors.key?("Foo"))
472
+ assert(ancestors.key?("A"))
473
+ assert(ancestors.key?("B"))
474
+ refute(ancestors.key?("C"))
475
+ end
476
+
477
+ def test_duplicate_prepend_include
478
+ index(<<~RUBY)
479
+ module A; end
480
+
481
+ class Foo
482
+ prepend A
483
+ include A
484
+ end
485
+
486
+ class Bar
487
+ include A
488
+ prepend A
489
+ end
490
+ RUBY
491
+
492
+ assert_equal(
493
+ [
494
+ "A",
495
+ "Foo",
496
+ # "Object",
497
+ # "Kernel",
498
+ # "BasicObject",
499
+ ],
500
+ @index.linearized_ancestors_of("Foo"),
501
+ )
502
+
503
+ assert_equal(
504
+ [
505
+ "A",
506
+ "Bar",
507
+ "A",
508
+ # "Object",
509
+ # "Kernel",
510
+ # "BasicObject",
511
+ ],
512
+ @index.linearized_ancestors_of("Bar"),
513
+ )
514
+ end
515
+
516
+ def test_linearizing_ancestors_handles_circular_parent_class
517
+ index(<<~RUBY)
518
+ class Foo < Foo
519
+ end
520
+ RUBY
521
+
522
+ assert_equal(
523
+ [
524
+ "Foo",
525
+ # "Object",
526
+ # "Kernel",
527
+ # "BasicObject",
528
+ ],
529
+ @index.linearized_ancestors_of("Foo"),
530
+ )
531
+ end
532
+
533
+ def test_ancestors_linearization_complex_prepend_duplication
534
+ index(<<~RUBY)
535
+ module A; end
536
+ module B
537
+ prepend A
538
+ end
539
+ module C
540
+ prepend B
541
+ end
542
+
543
+ class Foo
544
+ prepend A
545
+ prepend C
546
+ end
547
+ RUBY
548
+
549
+ assert_equal(
550
+ [
551
+ "A",
552
+ "B",
553
+ "C",
554
+ "Foo",
555
+ # "Object",
556
+ # "Kernel",
557
+ # "BasicObject",
558
+ ],
559
+ @index.linearized_ancestors_of("Foo"),
560
+ )
561
+ end
562
+
563
+ def test_ancestors_linearization_complex_include_duplication
564
+ index(<<~RUBY)
565
+ module A; end
566
+ module B
567
+ include A
568
+ end
569
+ module C
570
+ include B
571
+ end
572
+
573
+ class Foo
574
+ include A
575
+ include C
576
+ end
577
+ RUBY
578
+
579
+ assert_equal(
580
+ [
581
+ "Foo",
582
+ "C",
583
+ "B",
584
+ "A",
585
+ # "Object",
586
+ # "Kernel",
587
+ # "BasicObject",
588
+ ],
589
+ @index.linearized_ancestors_of("Foo"),
590
+ )
591
+ end
592
+
593
+ def test_linearizing_ancestors_that_need_to_be_resolved
594
+ index(<<~RUBY)
595
+ module Foo
596
+ module Baz
597
+ end
598
+ module Qux
599
+ end
600
+
601
+ class Something; end
602
+
603
+ class Bar < Something
604
+ include Baz
605
+ prepend Qux
606
+ end
607
+ end
608
+ RUBY
609
+
610
+ assert_equal(
611
+ [
612
+ "Foo::Qux",
613
+ "Foo::Bar",
614
+ "Foo::Baz",
615
+ "Foo::Something",
616
+ # "Object",
617
+ # "Kernel",
618
+ # "BasicObject",
619
+ ],
620
+ @index.linearized_ancestors_of("Foo::Bar"),
621
+ )
622
+ end
623
+
624
+ def test_linearizing_ancestors_for_non_existing_namespaces
625
+ index(<<~RUBY)
626
+ module Kernel
627
+ def Array(a); end
628
+ end
629
+ RUBY
630
+
631
+ assert_raises(Index::NonExistingNamespaceError) do
632
+ @index.linearized_ancestors_of("Foo")
633
+ end
634
+
635
+ assert_raises(Index::NonExistingNamespaceError) do
636
+ @index.linearized_ancestors_of("Array")
637
+ end
638
+ end
639
+
640
+ def test_linearizing_circular_ancestors
641
+ index(<<~RUBY)
642
+ module M1
643
+ include M2
644
+ end
645
+
646
+ module M2
647
+ include M1
648
+ end
649
+
650
+ module A1
651
+ include A2
652
+ end
653
+
654
+ module A2
655
+ include A3
656
+ end
657
+
658
+ module A3
659
+ include A1
660
+ end
661
+
662
+ class Foo < Foo
663
+ include Foo
664
+ end
665
+
666
+ module Bar
667
+ include Bar
668
+ end
669
+ RUBY
670
+
671
+ assert_equal(["M2", "M1"], @index.linearized_ancestors_of("M2"))
672
+ assert_equal(["A3", "A1", "A2"], @index.linearized_ancestors_of("A3"))
673
+ assert_equal(["Foo"], @index.linearized_ancestors_of("Foo"))
674
+ assert_equal(["Bar"], @index.linearized_ancestors_of("Bar"))
675
+ end
676
+
677
+ def test_linearizing_circular_aliased_dependency
678
+ index(<<~RUBY)
679
+ module A
680
+ end
681
+
682
+ ALIAS = A
683
+
684
+ module A
685
+ include ALIAS
686
+ end
687
+ RUBY
688
+
689
+ assert_equal(["A", "ALIAS"], @index.linearized_ancestors_of("A"))
690
+ end
691
+
692
+ def test_resolving_an_inherited_method
693
+ index(<<~RUBY)
694
+ module Foo
695
+ def baz; end
696
+ end
697
+
698
+ class Bar
699
+ def qux; end
700
+ end
701
+
702
+ class Wow < Bar
703
+ include Foo
704
+ end
705
+ RUBY
706
+
707
+ entry = T.must(@index.resolve_method("baz", "Wow")&.first)
708
+ assert_equal("baz", entry.name)
709
+ assert_equal("Foo", T.must(entry.owner).name)
710
+
711
+ entry = T.must(@index.resolve_method("qux", "Wow")&.first)
712
+ assert_equal("qux", entry.name)
713
+ assert_equal("Bar", T.must(entry.owner).name)
714
+ end
715
+
716
+ def test_resolving_an_inherited_method_lands_on_first_match
717
+ index(<<~RUBY)
718
+ module Foo
719
+ def qux; end
720
+ end
721
+
722
+ class Bar
723
+ def qux; end
724
+ end
725
+
726
+ class Wow < Bar
727
+ prepend Foo
728
+
729
+ def qux; end
730
+ end
731
+ RUBY
732
+
733
+ entries = T.must(@index.resolve_method("qux", "Wow"))
734
+ assert_equal(1, entries.length)
735
+
736
+ entry = T.must(entries.first)
737
+ assert_equal("qux", entry.name)
738
+ assert_equal("Foo", T.must(entry.owner).name)
739
+ end
740
+
741
+ def test_handle_change_clears_ancestor_cache_if_tree_changed
742
+ Dir.mktmpdir do |dir|
743
+ Dir.chdir(dir) do
744
+ # Write the original file
745
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
746
+ module Foo
747
+ end
748
+
749
+ class Bar
750
+ include Foo
751
+ end
752
+ RUBY
753
+
754
+ indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
755
+ @index.index_single(indexable_path)
756
+
757
+ assert_equal(["Bar", "Foo"], @index.linearized_ancestors_of("Bar"))
758
+
759
+ # Remove include to invalidate the ancestor tree
760
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
761
+ module Foo
762
+ end
763
+
764
+ class Bar
765
+ end
766
+ RUBY
767
+
768
+ @index.handle_change(indexable_path)
769
+ assert_empty(@index.instance_variable_get(:@ancestors))
770
+ assert_equal(["Bar"], @index.linearized_ancestors_of("Bar"))
771
+ end
772
+ end
773
+ end
774
+
775
+ def test_handle_change_does_not_clear_ancestor_cache_if_tree_not_changed
776
+ Dir.mktmpdir do |dir|
777
+ Dir.chdir(dir) do
778
+ # Write the original file
779
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
780
+ module Foo
781
+ end
782
+
783
+ class Bar
784
+ include Foo
785
+ end
786
+ RUBY
787
+
788
+ indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
789
+ @index.index_single(indexable_path)
790
+
791
+ assert_equal(["Bar", "Foo"], @index.linearized_ancestors_of("Bar"))
792
+
793
+ # Remove include to invalidate the ancestor tree
794
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
795
+ module Foo
796
+ end
797
+
798
+ class Bar
799
+ include Foo
800
+
801
+ def baz; end
802
+ end
803
+ RUBY
804
+
805
+ @index.handle_change(indexable_path)
806
+ refute_empty(@index.instance_variable_get(:@ancestors))
807
+ assert_equal(["Bar", "Foo"], @index.linearized_ancestors_of("Bar"))
808
+ end
809
+ end
810
+ end
811
+
812
+ def test_handle_change_clears_ancestor_cache_if_parent_class_changed
813
+ Dir.mktmpdir do |dir|
814
+ Dir.chdir(dir) do
815
+ # Write the original file
816
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
817
+ class Foo
818
+ end
819
+
820
+ class Bar < Foo
821
+ end
822
+ RUBY
823
+
824
+ indexable_path = IndexablePath.new(nil, File.join(dir, "foo.rb"))
825
+ @index.index_single(indexable_path)
826
+
827
+ assert_equal(["Bar", "Foo"], @index.linearized_ancestors_of("Bar"))
828
+
829
+ # Remove include to invalidate the ancestor tree
830
+ File.write(File.join(dir, "foo.rb"), <<~RUBY)
831
+ class Foo
832
+ end
833
+
834
+ class Bar
835
+ end
836
+ RUBY
837
+
838
+ @index.handle_change(indexable_path)
839
+ assert_empty(@index.instance_variable_get(:@ancestors))
840
+ assert_equal(["Bar"], @index.linearized_ancestors_of("Bar"))
841
+ end
842
+ end
843
+ end
316
844
  end
317
845
  end
@@ -0,0 +1,131 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "test_case"
5
+
6
+ module RubyIndexer
7
+ class InstanceVariableTest < TestCase
8
+ def test_instance_variable_write
9
+ index(<<~RUBY)
10
+ module Foo
11
+ class Bar
12
+ def initialize
13
+ # Hello
14
+ @a = 1
15
+ end
16
+ end
17
+ end
18
+ RUBY
19
+
20
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
21
+
22
+ entry = T.must(@index["@a"]&.first)
23
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
24
+ end
25
+
26
+ def test_instance_variable_and_write
27
+ index(<<~RUBY)
28
+ module Foo
29
+ class Bar
30
+ def initialize
31
+ # Hello
32
+ @a &&= value
33
+ end
34
+ end
35
+ end
36
+ RUBY
37
+
38
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
39
+
40
+ entry = T.must(@index["@a"]&.first)
41
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
42
+ end
43
+
44
+ def test_instance_variable_operator_write
45
+ index(<<~RUBY)
46
+ module Foo
47
+ class Bar
48
+ def initialize
49
+ # Hello
50
+ @a += value
51
+ end
52
+ end
53
+ end
54
+ RUBY
55
+
56
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
57
+
58
+ entry = T.must(@index["@a"]&.first)
59
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
60
+ end
61
+
62
+ def test_instance_variable_or_write
63
+ index(<<~RUBY)
64
+ module Foo
65
+ class Bar
66
+ def initialize
67
+ # Hello
68
+ @a ||= value
69
+ end
70
+ end
71
+ end
72
+ RUBY
73
+
74
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
75
+
76
+ entry = T.must(@index["@a"]&.first)
77
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
78
+ end
79
+
80
+ def test_instance_variable_target
81
+ index(<<~RUBY)
82
+ module Foo
83
+ class Bar
84
+ def initialize
85
+ # Hello
86
+ @a, @b = [1, 2]
87
+ end
88
+ end
89
+ end
90
+ RUBY
91
+
92
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:4-6:4-8")
93
+ assert_entry("@b", Entry::InstanceVariable, "/fake/path/foo.rb:4-10:4-12")
94
+
95
+ entry = T.must(@index["@a"]&.first)
96
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
97
+
98
+ entry = T.must(@index["@b"]&.first)
99
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
100
+ end
101
+
102
+ def test_empty_name_instance_variables
103
+ index(<<~RUBY)
104
+ module Foo
105
+ class Bar
106
+ def initialize
107
+ @ = 123
108
+ end
109
+ end
110
+ end
111
+ RUBY
112
+
113
+ refute_entry("@")
114
+ end
115
+
116
+ def test_class_instance_variables
117
+ index(<<~RUBY)
118
+ module Foo
119
+ class Bar
120
+ @a = 123
121
+ end
122
+ end
123
+ RUBY
124
+
125
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:2-4:2-6")
126
+
127
+ entry = T.must(@index["@a"]&.first)
128
+ assert_equal("Foo::Bar", T.must(entry.owner).name)
129
+ end
130
+ end
131
+ end