ruby-lsp 0.23.0 → 0.23.2
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 +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +59 -30
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +21 -10
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +36 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/index_test.rb +2 -1
- data/lib/ruby_indexer/test/instance_variables_test.rb +20 -0
- data/lib/ruby_indexer/test/method_test.rb +71 -8
- data/lib/ruby_lsp/base_server.rb +8 -13
- data/lib/ruby_lsp/document.rb +74 -19
- data/lib/ruby_lsp/erb_document.rb +5 -3
- data/lib/ruby_lsp/global_state.rb +9 -0
- data/lib/ruby_lsp/rbs_document.rb +2 -2
- data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -6
- data/lib/ruby_lsp/requests/completion.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
- data/lib/ruby_lsp/requests/references.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -4
- data/lib/ruby_lsp/requests/signature_help.rb +1 -1
- data/lib/ruby_lsp/ruby_document.rb +75 -6
- data/lib/ruby_lsp/server.rb +95 -81
- data/lib/ruby_lsp/setup_bundler.rb +1 -1
- data/lib/ruby_lsp/store.rb +16 -12
- data/lib/ruby_lsp/type_inferrer.rb +39 -17
- data/lib/ruby_lsp/utils.rb +43 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d295233e64f7b79aa061f243c61c6d5bfb69cbdc5dac64474106473d7033bc8
|
4
|
+
data.tar.gz: 559fbc2fb40b2a8043fefbf332cb4bf94ce8e3953299d6740d1d21034b79e92b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52e0176c0e9516801f94d9bd387ad6acfcd640c500443527448b69f3b55e2627aafa3bea75a60e131c21c0b06b309aba28a98b263344d20ce6406e1e66b69a37
|
7
|
+
data.tar.gz: 52b47878d9c5a71dadd618a5562a30901abf3fe167f71e08d92f1a5418710a4e360043ae08d553975ad7008d7f1fb657a9b650d05a311ddcfc92157a7283ec6c
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.23.
|
1
|
+
0.23.2
|
data/exe/ruby-lsp
CHANGED
@@ -79,7 +79,7 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
79
79
|
end
|
80
80
|
|
81
81
|
bundler_path = File.join(Gem.default_bindir, "bundle")
|
82
|
-
base_command = (File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
|
82
|
+
base_command = (!Gem.win_platform? && File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
|
83
83
|
|
84
84
|
if env["BUNDLER_VERSION"]
|
85
85
|
base_command << " _#{env["BUNDLER_VERSION"]}_"
|
@@ -24,7 +24,7 @@ module RubyIndexer
|
|
24
24
|
@index = index
|
25
25
|
@uri = uri
|
26
26
|
@enhancements = T.let(Enhancement.all(self), T::Array[Enhancement])
|
27
|
-
@visibility_stack = T.let([
|
27
|
+
@visibility_stack = T.let([VisibilityScope.public_scope], T::Array[VisibilityScope])
|
28
28
|
@comments_by_line = T.let(
|
29
29
|
parse_result.comments.to_h do |c|
|
30
30
|
[c.location.start_line, c]
|
@@ -64,7 +64,6 @@ module RubyIndexer
|
|
64
64
|
:on_constant_path_or_write_node_enter,
|
65
65
|
:on_constant_path_operator_write_node_enter,
|
66
66
|
:on_constant_path_and_write_node_enter,
|
67
|
-
:on_constant_or_write_node_enter,
|
68
67
|
:on_constant_write_node_enter,
|
69
68
|
:on_constant_or_write_node_enter,
|
70
69
|
:on_constant_and_write_node_enter,
|
@@ -138,7 +137,7 @@ module RubyIndexer
|
|
138
137
|
|
139
138
|
sig { params(node: Prism::SingletonClassNode).void }
|
140
139
|
def on_singleton_class_node_enter(node)
|
141
|
-
@visibility_stack.push(
|
140
|
+
@visibility_stack.push(VisibilityScope.public_scope)
|
142
141
|
|
143
142
|
current_owner = @owner_stack.last
|
144
143
|
|
@@ -280,15 +279,15 @@ module RubyIndexer
|
|
280
279
|
when :include, :prepend, :extend
|
281
280
|
handle_module_operation(node, message)
|
282
281
|
when :public
|
283
|
-
@visibility_stack.push(
|
282
|
+
@visibility_stack.push(VisibilityScope.public_scope)
|
284
283
|
when :protected
|
285
|
-
@visibility_stack.push(Entry::Visibility::PROTECTED)
|
284
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PROTECTED))
|
286
285
|
when :private
|
287
|
-
@visibility_stack.push(Entry::Visibility::PRIVATE)
|
286
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
|
288
287
|
when :module_function
|
289
288
|
handle_module_function(node)
|
290
289
|
when :private_class_method
|
291
|
-
@visibility_stack.push(Entry::Visibility::PRIVATE)
|
290
|
+
@visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
|
292
291
|
handle_private_class_method(node)
|
293
292
|
end
|
294
293
|
|
@@ -324,42 +323,61 @@ module RubyIndexer
|
|
324
323
|
|
325
324
|
sig { params(node: Prism::DefNode).void }
|
326
325
|
def on_def_node_enter(node)
|
326
|
+
owner = @owner_stack.last
|
327
|
+
return unless owner
|
328
|
+
|
327
329
|
@inside_def = true
|
328
330
|
method_name = node.name.to_s
|
329
331
|
comments = collect_comments(node)
|
332
|
+
scope = current_visibility_scope
|
330
333
|
|
331
334
|
case node.receiver
|
332
335
|
when nil
|
336
|
+
location = Location.from_prism_location(node.location, @code_units_cache)
|
337
|
+
name_location = Location.from_prism_location(node.name_loc, @code_units_cache)
|
338
|
+
signatures = [Entry::Signature.new(list_params(node.parameters))]
|
339
|
+
|
333
340
|
@index.add(Entry::Method.new(
|
334
341
|
method_name,
|
335
342
|
@uri,
|
336
|
-
|
337
|
-
|
343
|
+
location,
|
344
|
+
name_location,
|
338
345
|
comments,
|
339
|
-
|
340
|
-
|
341
|
-
|
346
|
+
signatures,
|
347
|
+
scope.visibility,
|
348
|
+
owner,
|
342
349
|
))
|
343
|
-
when Prism::SelfNode
|
344
|
-
owner = @owner_stack.last
|
345
350
|
|
346
|
-
if
|
351
|
+
if scope.module_func
|
347
352
|
singleton = @index.existing_or_new_singleton_class(owner.name)
|
348
353
|
|
349
354
|
@index.add(Entry::Method.new(
|
350
355
|
method_name,
|
351
356
|
@uri,
|
352
|
-
|
353
|
-
|
357
|
+
location,
|
358
|
+
name_location,
|
354
359
|
comments,
|
355
|
-
|
356
|
-
|
360
|
+
signatures,
|
361
|
+
Entry::Visibility::PUBLIC,
|
357
362
|
singleton,
|
358
363
|
))
|
359
|
-
|
360
|
-
@owner_stack << singleton
|
361
|
-
@stack << "<Class:#{@stack.last}>"
|
362
364
|
end
|
365
|
+
when Prism::SelfNode
|
366
|
+
singleton = @index.existing_or_new_singleton_class(owner.name)
|
367
|
+
|
368
|
+
@index.add(Entry::Method.new(
|
369
|
+
method_name,
|
370
|
+
@uri,
|
371
|
+
Location.from_prism_location(node.location, @code_units_cache),
|
372
|
+
Location.from_prism_location(node.name_loc, @code_units_cache),
|
373
|
+
comments,
|
374
|
+
[Entry::Signature.new(list_params(node.parameters))],
|
375
|
+
scope.visibility,
|
376
|
+
singleton,
|
377
|
+
))
|
378
|
+
|
379
|
+
@owner_stack << singleton
|
380
|
+
@stack << "<Class:#{@stack.last}>"
|
363
381
|
end
|
364
382
|
end
|
365
383
|
|
@@ -834,6 +852,8 @@ module RubyIndexer
|
|
834
852
|
return unless receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
835
853
|
|
836
854
|
comments = collect_comments(node)
|
855
|
+
scope = current_visibility_scope
|
856
|
+
|
837
857
|
arguments.each do |argument|
|
838
858
|
name, loc = case argument
|
839
859
|
when Prism::SymbolNode
|
@@ -850,7 +870,7 @@ module RubyIndexer
|
|
850
870
|
@uri,
|
851
871
|
Location.from_prism_location(loc, @code_units_cache),
|
852
872
|
comments,
|
853
|
-
|
873
|
+
scope.visibility,
|
854
874
|
@owner_stack.last,
|
855
875
|
))
|
856
876
|
end
|
@@ -862,7 +882,7 @@ module RubyIndexer
|
|
862
882
|
@uri,
|
863
883
|
Location.from_prism_location(loc, @code_units_cache),
|
864
884
|
comments,
|
865
|
-
|
885
|
+
scope.visibility,
|
866
886
|
@owner_stack.last,
|
867
887
|
))
|
868
888
|
end
|
@@ -904,11 +924,20 @@ module RubyIndexer
|
|
904
924
|
|
905
925
|
sig { params(node: Prism::CallNode).void }
|
906
926
|
def handle_module_function(node)
|
927
|
+
# Invoking `module_function` in a class raises
|
928
|
+
owner = @owner_stack.last
|
929
|
+
return unless owner.is_a?(Entry::Module)
|
930
|
+
|
907
931
|
arguments_node = node.arguments
|
908
|
-
return unless arguments_node
|
909
932
|
|
910
|
-
|
911
|
-
|
933
|
+
# If `module_function` is invoked without arguments, all methods defined after it become singleton methods and the
|
934
|
+
# visibility for instance methods changes to private
|
935
|
+
unless arguments_node
|
936
|
+
@visibility_stack.push(VisibilityScope.module_function_scope)
|
937
|
+
return
|
938
|
+
end
|
939
|
+
|
940
|
+
owner_name = owner.name
|
912
941
|
|
913
942
|
arguments_node.arguments.each do |argument|
|
914
943
|
method_name = case argument
|
@@ -983,8 +1012,8 @@ module RubyIndexer
|
|
983
1012
|
end
|
984
1013
|
end
|
985
1014
|
|
986
|
-
sig { returns(
|
987
|
-
def
|
1015
|
+
sig { returns(VisibilityScope) }
|
1016
|
+
def current_visibility_scope
|
988
1017
|
T.must(@visibility_stack.last)
|
989
1018
|
end
|
990
1019
|
|
@@ -1091,7 +1120,7 @@ module RubyIndexer
|
|
1091
1120
|
|
1092
1121
|
sig { params(short_name: String, entry: Entry::Namespace).void }
|
1093
1122
|
def advance_namespace_stack(short_name, entry)
|
1094
|
-
@visibility_stack.push(
|
1123
|
+
@visibility_stack.push(VisibilityScope.public_scope)
|
1095
1124
|
@owner_stack << entry
|
1096
1125
|
@index.add(entry)
|
1097
1126
|
@stack << short_name
|
@@ -48,6 +48,8 @@ module RubyIndexer
|
|
48
48
|
)
|
49
49
|
|
50
50
|
@configuration = T.let(RubyIndexer::Configuration.new, Configuration)
|
51
|
+
|
52
|
+
@initial_indexing_completed = T.let(false, T::Boolean)
|
51
53
|
end
|
52
54
|
|
53
55
|
# Register an included `hook` that will be executed when `module_name` is included into any namespace
|
@@ -56,8 +58,8 @@ module RubyIndexer
|
|
56
58
|
(@included_hooks[module_name] ||= []) << hook
|
57
59
|
end
|
58
60
|
|
59
|
-
sig { params(uri: URI::Generic).void }
|
60
|
-
def delete(uri)
|
61
|
+
sig { params(uri: URI::Generic, skip_require_paths_tree: T::Boolean).void }
|
62
|
+
def delete(uri, skip_require_paths_tree: false)
|
61
63
|
key = uri.to_s
|
62
64
|
# For each constant discovered in `path`, delete the associated entry from the index. If there are no entries
|
63
65
|
# left, delete the constant from the index.
|
@@ -80,6 +82,7 @@ module RubyIndexer
|
|
80
82
|
end
|
81
83
|
|
82
84
|
@uris_to_entries.delete(key)
|
85
|
+
return if skip_require_paths_tree
|
83
86
|
|
84
87
|
require_path = uri.require_path
|
85
88
|
@require_paths_tree.delete(require_path) if require_path
|
@@ -357,12 +360,13 @@ module RubyIndexer
|
|
357
360
|
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
358
361
|
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
359
362
|
# behavior and can take appropriate action.
|
360
|
-
|
361
|
-
if @entries.any?
|
363
|
+
if @initial_indexing_completed
|
362
364
|
raise IndexNotEmptyError,
|
363
365
|
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
364
366
|
end
|
365
367
|
|
368
|
+
@initial_indexing_completed = true
|
369
|
+
|
366
370
|
RBSIndexer.new(self).index_ruby_core
|
367
371
|
# Calculate how many paths are worth 1% of progress
|
368
372
|
progress_step = (uris.length / 100.0).ceil
|
@@ -618,17 +622,24 @@ module RubyIndexer
|
|
618
622
|
end
|
619
623
|
|
620
624
|
# Synchronizes a change made to the given URI. This method will ensure that new declarations are indexed, removed
|
621
|
-
# declarations removed and that the ancestor linearization cache is cleared if necessary
|
622
|
-
|
623
|
-
|
625
|
+
# declarations removed and that the ancestor linearization cache is cleared if necessary. If a block is passed, the
|
626
|
+
# consumer of this API has to handle deleting and inserting/updating entries in the index instead of passing the
|
627
|
+
# document's source (used to handle unsaved changes to files)
|
628
|
+
sig do
|
629
|
+
params(uri: URI::Generic, source: T.nilable(String), block: T.nilable(T.proc.params(index: Index).void)).void
|
630
|
+
end
|
631
|
+
def handle_change(uri, source = nil, &block)
|
624
632
|
key = uri.to_s
|
625
633
|
original_entries = @uris_to_entries[key]
|
626
634
|
|
627
|
-
|
628
|
-
|
635
|
+
if block
|
636
|
+
block.call(self)
|
637
|
+
else
|
638
|
+
delete(uri)
|
639
|
+
index_single(uri, T.must(source))
|
640
|
+
end
|
629
641
|
|
630
642
|
updated_entries = @uris_to_entries[key]
|
631
|
-
|
632
643
|
return unless original_entries && updated_entries
|
633
644
|
|
634
645
|
# A change in one ancestor may impact several different others, which could be including that ancestor through
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyIndexer
|
5
|
+
# Represents the visibility scope in a Ruby namespace. This keeps track of whether methods are in a public, private or
|
6
|
+
# protected section, and whether they are module functions.
|
7
|
+
class VisibilityScope
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
class << self
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { returns(T.attached_class) }
|
14
|
+
def module_function_scope
|
15
|
+
new(module_func: true, visibility: Entry::Visibility::PRIVATE)
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { returns(T.attached_class) }
|
19
|
+
def public_scope
|
20
|
+
new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(Entry::Visibility) }
|
25
|
+
attr_reader :visibility
|
26
|
+
|
27
|
+
sig { returns(T::Boolean) }
|
28
|
+
attr_reader :module_func
|
29
|
+
|
30
|
+
sig { params(visibility: Entry::Visibility, module_func: T::Boolean).void }
|
31
|
+
def initialize(visibility: Entry::Visibility::PUBLIC, module_func: false)
|
32
|
+
@visibility = visibility
|
33
|
+
@module_func = module_func
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -5,6 +5,7 @@ require "yaml"
|
|
5
5
|
require "did_you_mean"
|
6
6
|
|
7
7
|
require "ruby_indexer/lib/ruby_indexer/uri"
|
8
|
+
require "ruby_indexer/lib/ruby_indexer/visibility_scope"
|
8
9
|
require "ruby_indexer/lib/ruby_indexer/declaration_listener"
|
9
10
|
require "ruby_indexer/lib/ruby_indexer/reference_finder"
|
10
11
|
require "ruby_indexer/lib/ruby_indexer/enhancement"
|
@@ -2061,7 +2061,8 @@ module RubyIndexer
|
|
2061
2061
|
end
|
2062
2062
|
|
2063
2063
|
def test_prevents_multiple_calls_to_index_all
|
2064
|
-
|
2064
|
+
@index.index_all
|
2065
|
+
|
2065
2066
|
assert_raises(Index::IndexNotEmptyError) do
|
2066
2067
|
@index.index_all
|
2067
2068
|
end
|
@@ -216,5 +216,25 @@ module RubyIndexer
|
|
216
216
|
assert_instance_of(Entry::Class, owner)
|
217
217
|
assert_equal("Foo", owner.name)
|
218
218
|
end
|
219
|
+
|
220
|
+
def test_module_function_does_not_impact_instance_variables
|
221
|
+
# One possible way of implementing `module_function` would be to push a fake singleton class to the stack, so that
|
222
|
+
# methods are inserted into it. However, that would be incorrect because it would then bind instance variables to
|
223
|
+
# the wrong type. This test is here to prevent that from happening.
|
224
|
+
index(<<~RUBY)
|
225
|
+
module Foo
|
226
|
+
module_function
|
227
|
+
|
228
|
+
def something; end
|
229
|
+
|
230
|
+
@a = 123
|
231
|
+
end
|
232
|
+
RUBY
|
233
|
+
|
234
|
+
entry = T.must(@index["@a"]&.first)
|
235
|
+
owner = T.must(entry.owner)
|
236
|
+
assert_instance_of(Entry::SingletonClass, owner)
|
237
|
+
assert_equal("Foo::<Class:Foo>", owner.name)
|
238
|
+
end
|
219
239
|
end
|
220
240
|
end
|
@@ -88,19 +88,21 @@ module RubyIndexer
|
|
88
88
|
|
89
89
|
def test_visibility_tracking
|
90
90
|
index(<<~RUBY)
|
91
|
-
|
92
|
-
|
91
|
+
class Foo
|
92
|
+
private def foo
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
+
def bar; end
|
95
96
|
|
96
|
-
|
97
|
+
protected
|
97
98
|
|
98
|
-
|
99
|
+
def baz; end
|
100
|
+
end
|
99
101
|
RUBY
|
100
102
|
|
101
|
-
assert_entry("foo", Entry::Method, "/fake/path/foo.rb:
|
102
|
-
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:
|
103
|
-
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:
|
103
|
+
assert_entry("foo", Entry::Method, "/fake/path/foo.rb:1-10:2-5", visibility: Entry::Visibility::PRIVATE)
|
104
|
+
assert_entry("bar", Entry::Method, "/fake/path/foo.rb:4-2:4-14", visibility: Entry::Visibility::PUBLIC)
|
105
|
+
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:8-2:8-14", visibility: Entry::Visibility::PROTECTED)
|
104
106
|
end
|
105
107
|
|
106
108
|
def test_visibility_tracking_with_nested_class_or_modules
|
@@ -846,6 +848,67 @@ module RubyIndexer
|
|
846
848
|
assert_signature_matches(entry, "baz(1)")
|
847
849
|
end
|
848
850
|
|
851
|
+
def test_module_function_with_no_arguments
|
852
|
+
index(<<~RUBY)
|
853
|
+
module Foo
|
854
|
+
def bar; end
|
855
|
+
|
856
|
+
module_function
|
857
|
+
|
858
|
+
def baz; end
|
859
|
+
attr_reader :attribute
|
860
|
+
|
861
|
+
public
|
862
|
+
|
863
|
+
def qux; end
|
864
|
+
end
|
865
|
+
RUBY
|
866
|
+
|
867
|
+
entry = T.must(@index["bar"].first)
|
868
|
+
assert_predicate(entry, :public?)
|
869
|
+
assert_equal("Foo", T.must(entry.owner).name)
|
870
|
+
|
871
|
+
instance_baz, singleton_baz = T.must(@index["baz"])
|
872
|
+
assert_predicate(instance_baz, :private?)
|
873
|
+
assert_equal("Foo", T.must(instance_baz.owner).name)
|
874
|
+
|
875
|
+
assert_predicate(singleton_baz, :public?)
|
876
|
+
assert_equal("Foo::<Class:Foo>", T.must(singleton_baz.owner).name)
|
877
|
+
|
878
|
+
# After invoking `public`, the state of `module_function` is reset
|
879
|
+
instance_qux, singleton_qux = T.must(@index["qux"])
|
880
|
+
assert_nil(singleton_qux)
|
881
|
+
assert_predicate(instance_qux, :public?)
|
882
|
+
assert_equal("Foo", T.must(instance_baz.owner).name)
|
883
|
+
|
884
|
+
# Attributes are not turned into class methods, they do become private
|
885
|
+
instance_attribute, singleton_attribute = @index["attribute"]
|
886
|
+
assert_nil(singleton_attribute)
|
887
|
+
assert_equal("Foo", T.must(instance_attribute.owner).name)
|
888
|
+
assert_predicate(instance_attribute, :private?)
|
889
|
+
end
|
890
|
+
|
891
|
+
def test_module_function_does_nothing_in_classes
|
892
|
+
# Invoking `module_function` in a class raises an error. We simply ignore it
|
893
|
+
index(<<~RUBY)
|
894
|
+
class Foo
|
895
|
+
def bar; end
|
896
|
+
|
897
|
+
module_function
|
898
|
+
|
899
|
+
def baz; end
|
900
|
+
end
|
901
|
+
RUBY
|
902
|
+
|
903
|
+
entry = T.must(@index["bar"].first)
|
904
|
+
assert_predicate(entry, :public?)
|
905
|
+
assert_equal("Foo", T.must(entry.owner).name)
|
906
|
+
|
907
|
+
entry = T.must(@index["baz"].first)
|
908
|
+
assert_predicate(entry, :public?)
|
909
|
+
assert_equal("Foo", T.must(entry.owner).name)
|
910
|
+
end
|
911
|
+
|
849
912
|
private
|
850
913
|
|
851
914
|
sig { params(entry: Entry::Method, call_string: String).void }
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -18,22 +18,21 @@ module RubyLsp
|
|
18
18
|
@incoming_queue = T.let(Thread::Queue.new, Thread::Queue)
|
19
19
|
@outgoing_queue = T.let(Thread::Queue.new, Thread::Queue)
|
20
20
|
@cancelled_requests = T.let([], T::Array[Integer])
|
21
|
-
@mutex = T.let(Mutex.new, Mutex)
|
22
21
|
@worker = T.let(new_worker, Thread)
|
23
22
|
@current_request_id = T.let(1, Integer)
|
24
|
-
@
|
23
|
+
@global_state = T.let(GlobalState.new, GlobalState)
|
24
|
+
@store = T.let(Store.new(@global_state), Store)
|
25
25
|
@outgoing_dispatcher = T.let(
|
26
26
|
Thread.new do
|
27
27
|
unless @test_mode
|
28
28
|
while (message = @outgoing_queue.pop)
|
29
|
-
@
|
29
|
+
@global_state.synchronize { @writer.write(message.to_hash) }
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end,
|
33
33
|
Thread,
|
34
34
|
)
|
35
35
|
|
36
|
-
@global_state = T.let(GlobalState.new, GlobalState)
|
37
36
|
Thread.main.priority = 1
|
38
37
|
|
39
38
|
# We read the initialize request in `exe/ruby-lsp` to be able to determine the workspace URI where Bundler should
|
@@ -51,7 +50,7 @@ module RubyLsp
|
|
51
50
|
# source. Altering the source reference during parsing will put the parser in an invalid internal state, since
|
52
51
|
# it started parsing with one source but then it changed in the middle. We don't want to do this for text
|
53
52
|
# synchronization notifications
|
54
|
-
@
|
53
|
+
@global_state.synchronize do
|
55
54
|
uri = message.dig(:params, :textDocument, :uri)
|
56
55
|
|
57
56
|
if uri
|
@@ -95,14 +94,14 @@ module RubyLsp
|
|
95
94
|
"$/cancelRequest"
|
96
95
|
process_message(message)
|
97
96
|
when "shutdown"
|
98
|
-
@
|
97
|
+
@global_state.synchronize do
|
99
98
|
send_log_message("Shutting down Ruby LSP...")
|
100
99
|
shutdown
|
101
100
|
run_shutdown
|
102
101
|
@writer.write(Result.new(id: message[:id], response: nil).to_hash)
|
103
102
|
end
|
104
103
|
when "exit"
|
105
|
-
@
|
104
|
+
@global_state.synchronize do
|
106
105
|
status = @incoming_queue.closed? ? 0 : 1
|
107
106
|
send_log_message("Shutdown complete with status #{status}")
|
108
107
|
exit(status)
|
@@ -157,13 +156,9 @@ module RubyLsp
|
|
157
156
|
id = message[:id]
|
158
157
|
|
159
158
|
# Check if the request was cancelled before trying to process it
|
160
|
-
@
|
159
|
+
@global_state.synchronize do
|
161
160
|
if id && @cancelled_requests.include?(id)
|
162
|
-
send_message(
|
163
|
-
id: id,
|
164
|
-
code: Constant::ErrorCodes::REQUEST_CANCELLED,
|
165
|
-
message: "Request #{id} was cancelled",
|
166
|
-
))
|
161
|
+
send_message(Result.new(id: id, response: nil))
|
167
162
|
@cancelled_requests.delete(id)
|
168
163
|
next
|
169
164
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -40,19 +40,24 @@ module RubyLsp
|
|
40
40
|
sig { returns(Encoding) }
|
41
41
|
attr_reader :encoding
|
42
42
|
|
43
|
+
sig { returns(T.nilable(Edit)) }
|
44
|
+
attr_reader :last_edit
|
45
|
+
|
43
46
|
sig { returns(T.any(Interface::SemanticTokens, Object)) }
|
44
47
|
attr_accessor :semantic_tokens
|
45
48
|
|
46
|
-
sig { params(source: String, version: Integer, uri: URI::Generic,
|
47
|
-
def initialize(source:, version:, uri:,
|
49
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, global_state: GlobalState).void }
|
50
|
+
def initialize(source:, version:, uri:, global_state:)
|
51
|
+
@source = source
|
52
|
+
@version = version
|
53
|
+
@global_state = global_state
|
48
54
|
@cache = T.let(Hash.new(EMPTY_CACHE), T::Hash[String, T.untyped])
|
49
55
|
@semantic_tokens = T.let(EMPTY_CACHE, T.any(Interface::SemanticTokens, Object))
|
50
|
-
@encoding = T.let(encoding, Encoding)
|
51
|
-
@source = T.let(source, String)
|
52
|
-
@version = T.let(version, Integer)
|
56
|
+
@encoding = T.let(global_state.encoding, Encoding)
|
53
57
|
@uri = T.let(uri, URI::Generic)
|
54
58
|
@needs_parsing = T.let(true, T::Boolean)
|
55
59
|
@parse_result = T.let(T.unsafe(nil), ParseResultType)
|
60
|
+
@last_edit = T.let(nil, T.nilable(Edit))
|
56
61
|
parse!
|
57
62
|
end
|
58
63
|
|
@@ -64,7 +69,6 @@ module RubyLsp
|
|
64
69
|
sig { abstract.returns(LanguageId) }
|
65
70
|
def language_id; end
|
66
71
|
|
67
|
-
# TODO: remove this method once all non-positional requests have been migrated to the listener pattern
|
68
72
|
sig do
|
69
73
|
type_parameters(:T)
|
70
74
|
.params(
|
@@ -93,19 +97,34 @@ module RubyLsp
|
|
93
97
|
|
94
98
|
sig { params(edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
|
95
99
|
def push_edits(edits, version:)
|
96
|
-
|
97
|
-
|
98
|
-
|
100
|
+
@global_state.synchronize do
|
101
|
+
edits.each do |edit|
|
102
|
+
range = edit[:range]
|
103
|
+
scanner = create_scanner
|
99
104
|
|
100
|
-
|
101
|
-
|
105
|
+
start_position = scanner.find_char_position(range[:start])
|
106
|
+
end_position = scanner.find_char_position(range[:end])
|
102
107
|
|
103
|
-
|
104
|
-
|
108
|
+
@source[start_position...end_position] = edit[:text]
|
109
|
+
end
|
105
110
|
|
106
|
-
|
107
|
-
|
108
|
-
|
111
|
+
@version = version
|
112
|
+
@needs_parsing = true
|
113
|
+
@cache.clear
|
114
|
+
|
115
|
+
last_edit = edits.last
|
116
|
+
return unless last_edit
|
117
|
+
|
118
|
+
last_edit_range = last_edit[:range]
|
119
|
+
|
120
|
+
@last_edit = if last_edit_range[:start] == last_edit_range[:end]
|
121
|
+
Insert.new(last_edit_range)
|
122
|
+
elsif last_edit[:text].empty?
|
123
|
+
Delete.new(last_edit_range)
|
124
|
+
else
|
125
|
+
Replace.new(last_edit_range)
|
126
|
+
end
|
127
|
+
end
|
109
128
|
end
|
110
129
|
|
111
130
|
# Returns `true` if the document was parsed and `false` if nothing needed parsing
|
@@ -115,16 +134,52 @@ module RubyLsp
|
|
115
134
|
sig { abstract.returns(T::Boolean) }
|
116
135
|
def syntax_error?; end
|
117
136
|
|
137
|
+
sig { returns(T::Boolean) }
|
138
|
+
def past_expensive_limit?
|
139
|
+
@source.length > MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES
|
140
|
+
end
|
141
|
+
|
142
|
+
sig do
|
143
|
+
params(
|
144
|
+
start_pos: T::Hash[Symbol, T.untyped],
|
145
|
+
end_pos: T.nilable(T::Hash[Symbol, T.untyped]),
|
146
|
+
).returns([Integer, T.nilable(Integer)])
|
147
|
+
end
|
148
|
+
def find_index_by_position(start_pos, end_pos = nil)
|
149
|
+
@global_state.synchronize do
|
150
|
+
scanner = create_scanner
|
151
|
+
start_index = scanner.find_char_position(start_pos)
|
152
|
+
end_index = scanner.find_char_position(end_pos) if end_pos
|
153
|
+
[start_index, end_index]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
118
159
|
sig { returns(Scanner) }
|
119
160
|
def create_scanner
|
120
161
|
Scanner.new(@source, @encoding)
|
121
162
|
end
|
122
163
|
|
123
|
-
|
124
|
-
|
125
|
-
|
164
|
+
class Edit
|
165
|
+
extend T::Sig
|
166
|
+
extend T::Helpers
|
167
|
+
|
168
|
+
abstract!
|
169
|
+
|
170
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
171
|
+
attr_reader :range
|
172
|
+
|
173
|
+
sig { params(range: T::Hash[Symbol, T.untyped]).void }
|
174
|
+
def initialize(range)
|
175
|
+
@range = range
|
176
|
+
end
|
126
177
|
end
|
127
178
|
|
179
|
+
class Insert < Edit; end
|
180
|
+
class Replace < Edit; end
|
181
|
+
class Delete < Edit; end
|
182
|
+
|
128
183
|
class Scanner
|
129
184
|
extend T::Sig
|
130
185
|
|