ruby-lsp 0.16.6 → 0.17.0

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