ruby-lsp 0.12.4 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp-check +20 -4
- data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +36 -2
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +83 -12
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/index_test.rb +11 -0
- data/lib/ruby_indexer/test/method_test.rb +66 -0
- data/lib/ruby_lsp/addon.rb +9 -13
- data/lib/ruby_lsp/executor.rb +25 -27
- data/lib/ruby_lsp/listener.rb +4 -5
- data/lib/ruby_lsp/requests/code_lens.rb +16 -7
- data/lib/ruby_lsp/requests/completion.rb +16 -10
- data/lib/ruby_lsp/requests/definition.rb +3 -4
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +2 -3
- data/lib/ruby_lsp/requests/document_link.rb +2 -3
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
- data/lib/ruby_lsp/requests/formatting.rb +0 -5
- data/lib/ruby_lsp/requests/hover.rb +3 -4
- data/lib/ruby_lsp/requests/inlay_hints.rb +3 -3
- data/lib/ruby_lsp/requests/on_type_formatting.rb +2 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +28 -11
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
- data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -0
- data/lib/ruby_lsp/store.rb +4 -0
- data/lib/ruby_lsp/utils.rb +0 -4
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6bace664e1540c1333df9dabfb2e7bdd8bda0f910033b47b188e4a82ddacb15
|
4
|
+
data.tar.gz: be9e5da0492b26b5cb6e07074d01d7874104b70286c1578342b9639158fed147
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d8e61433686271a68dafe8db79765ed509a115ab6f06060d7669d279f48218fe2eada2c12e3b45f155d27a41774771681cdab92700d27f426e75ed89287ca03
|
7
|
+
data.tar.gz: 611c050b751050934d0fd93e9e52089fe63fc4d947be75a440dfa0465933814d2682268a10135d036c2cee83fa739156d2b11e42e679ecbed7de62acfee9d621
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.13.0
|
data/exe/ruby-lsp-check
CHANGED
@@ -17,8 +17,6 @@ end
|
|
17
17
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
18
18
|
require "ruby_lsp/internal"
|
19
19
|
|
20
|
-
RubyLsp::Addon.load_addons
|
21
|
-
|
22
20
|
T::Utils.run_all_sig_blocks
|
23
21
|
|
24
22
|
files = Dir.glob("#{Dir.pwd}/**/*.rb")
|
@@ -28,6 +26,7 @@ puts "Verifying that all automatic LSP requests execute successfully. This may t
|
|
28
26
|
errors = {}
|
29
27
|
store = RubyLsp::Store.new
|
30
28
|
message_queue = Thread::Queue.new
|
29
|
+
RubyLsp::Addon.load_addons(message_queue)
|
31
30
|
executor = RubyLsp::Executor.new(store, message_queue)
|
32
31
|
|
33
32
|
files.each_with_index do |file, index|
|
@@ -50,13 +49,30 @@ end
|
|
50
49
|
puts "\n"
|
51
50
|
message_queue.close
|
52
51
|
|
52
|
+
# Indexing
|
53
|
+
puts "Verifying that indexing executes successfully. This may take a while..."
|
54
|
+
|
55
|
+
index = RubyIndexer::Index.new
|
56
|
+
indexables = RubyIndexer.configuration.indexables
|
57
|
+
|
58
|
+
indexables.each_with_index do |indexable, i|
|
59
|
+
result = Prism.parse(File.read(indexable.full_path))
|
60
|
+
collector = RubyIndexer::Collector.new(index, result, indexable.full_path)
|
61
|
+
collector.collect(result.value)
|
62
|
+
rescue => e
|
63
|
+
errors[indexable.full_path] = e
|
64
|
+
ensure
|
65
|
+
print("\033[M\033[0KIndexed #{i + 1}/#{indexables.length}") unless ENV["CI"]
|
66
|
+
end
|
67
|
+
puts "\n"
|
68
|
+
|
53
69
|
if errors.empty?
|
54
|
-
puts "All
|
70
|
+
puts "All operations completed successfully!"
|
55
71
|
exit
|
56
72
|
end
|
57
73
|
|
58
74
|
puts <<~ERRORS
|
59
|
-
Errors while executing
|
75
|
+
Errors while executing:
|
60
76
|
|
61
77
|
#{errors.map { |file, error| "#{file}: #{error.message}" }.join("\n")}
|
62
78
|
ERRORS
|
@@ -141,8 +141,18 @@ module RubyIndexer
|
|
141
141
|
|
142
142
|
sig { params(node: Prism::CallNode).void }
|
143
143
|
def handle_call_node(node)
|
144
|
-
message = node.
|
145
|
-
|
144
|
+
message = node.name
|
145
|
+
|
146
|
+
case message
|
147
|
+
when :private_constant
|
148
|
+
handle_private_constant(node)
|
149
|
+
when :attr_reader
|
150
|
+
handle_attribute(node, reader: true, writer: false)
|
151
|
+
when :attr_writer
|
152
|
+
handle_attribute(node, reader: false, writer: true)
|
153
|
+
when :attr_accessor
|
154
|
+
handle_attribute(node, reader: true, writer: true)
|
155
|
+
end
|
146
156
|
end
|
147
157
|
|
148
158
|
sig { params(node: Prism::DefNode).void }
|
@@ -312,5 +322,29 @@ module RubyIndexer
|
|
312
322
|
"#{@stack.join("::")}::#{name}"
|
313
323
|
end.delete_prefix("::")
|
314
324
|
end
|
325
|
+
|
326
|
+
sig { params(node: Prism::CallNode, reader: T::Boolean, writer: T::Boolean).void }
|
327
|
+
def handle_attribute(node, reader:, writer:)
|
328
|
+
arguments = node.arguments&.arguments
|
329
|
+
return unless arguments
|
330
|
+
|
331
|
+
receiver = node.receiver
|
332
|
+
return unless receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
333
|
+
|
334
|
+
comments = collect_comments(node)
|
335
|
+
arguments.each do |argument|
|
336
|
+
name, loc = case argument
|
337
|
+
when Prism::SymbolNode
|
338
|
+
[argument.value, argument.value_loc]
|
339
|
+
when Prism::StringNode
|
340
|
+
[argument.content, argument.content_loc]
|
341
|
+
end
|
342
|
+
|
343
|
+
next unless name && loc
|
344
|
+
|
345
|
+
@index << Entry::Accessor.new(name, @file_path, loc, comments, @current_owner) if reader
|
346
|
+
@index << Entry::Accessor.new("#{name}=", @file_path, loc, comments, @current_owner) if writer
|
347
|
+
end
|
348
|
+
end
|
315
349
|
end
|
316
350
|
end
|
@@ -120,7 +120,7 @@ module RubyIndexer
|
|
120
120
|
IndexablePath.new(RbConfig::CONFIG["rubylibdir"], path)
|
121
121
|
end,
|
122
122
|
)
|
123
|
-
|
123
|
+
elsif pathname.extname == ".rb"
|
124
124
|
# If the default_path is a Ruby file, we index it
|
125
125
|
indexables << IndexablePath.new(RbConfig::CONFIG["rubylibdir"], default_path)
|
126
126
|
end
|
@@ -90,20 +90,69 @@ module RubyIndexer
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
+
# A required method parameter, e.g. `def foo(a)`
|
93
94
|
class RequiredParameter < Parameter
|
94
95
|
end
|
95
96
|
|
96
|
-
|
97
|
+
# An optional method parameter, e.g. `def foo(a = 123)`
|
98
|
+
class OptionalParameter < Parameter
|
99
|
+
end
|
100
|
+
|
101
|
+
# An required keyword method parameter, e.g. `def foo(a:)`
|
102
|
+
class KeywordParameter < Parameter
|
103
|
+
end
|
104
|
+
|
105
|
+
# An optional keyword method parameter, e.g. `def foo(a: 123)`
|
106
|
+
class OptionalKeywordParameter < Parameter
|
107
|
+
end
|
108
|
+
|
109
|
+
class Member < Entry
|
97
110
|
extend T::Sig
|
98
111
|
extend T::Helpers
|
99
|
-
abstract!
|
100
112
|
|
101
|
-
|
102
|
-
attr_reader :parameters
|
113
|
+
abstract!
|
103
114
|
|
104
115
|
sig { returns(T.nilable(Entry::Namespace)) }
|
105
116
|
attr_reader :owner
|
106
117
|
|
118
|
+
sig do
|
119
|
+
params(
|
120
|
+
name: String,
|
121
|
+
file_path: String,
|
122
|
+
location: Prism::Location,
|
123
|
+
comments: T::Array[String],
|
124
|
+
owner: T.nilable(Entry::Namespace),
|
125
|
+
).void
|
126
|
+
end
|
127
|
+
def initialize(name, file_path, location, comments, owner)
|
128
|
+
super(name, file_path, location, comments)
|
129
|
+
@owner = owner
|
130
|
+
end
|
131
|
+
|
132
|
+
sig { abstract.returns(T::Array[Parameter]) }
|
133
|
+
def parameters; end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Accessor < Member
|
137
|
+
extend T::Sig
|
138
|
+
|
139
|
+
sig { override.returns(T::Array[Parameter]) }
|
140
|
+
def parameters
|
141
|
+
params = []
|
142
|
+
params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
|
143
|
+
params
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Method < Member
|
148
|
+
extend T::Sig
|
149
|
+
extend T::Helpers
|
150
|
+
|
151
|
+
abstract!
|
152
|
+
|
153
|
+
sig { override.returns(T::Array[Parameter]) }
|
154
|
+
attr_reader :parameters
|
155
|
+
|
107
156
|
sig do
|
108
157
|
params(
|
109
158
|
name: String,
|
@@ -115,9 +164,9 @@ module RubyIndexer
|
|
115
164
|
).void
|
116
165
|
end
|
117
166
|
def initialize(name, file_path, location, comments, parameters_node, owner) # rubocop:disable Metrics/ParameterLists
|
118
|
-
super(name, file_path, location, comments)
|
167
|
+
super(name, file_path, location, comments, owner)
|
168
|
+
|
119
169
|
@parameters = T.let(list_params(parameters_node), T::Array[Parameter])
|
120
|
-
@owner = owner
|
121
170
|
end
|
122
171
|
|
123
172
|
private
|
@@ -126,20 +175,42 @@ module RubyIndexer
|
|
126
175
|
def list_params(parameters_node)
|
127
176
|
return [] unless parameters_node
|
128
177
|
|
129
|
-
|
178
|
+
parameters = []
|
179
|
+
|
180
|
+
parameters_node.requireds.each do |required|
|
130
181
|
name = parameter_name(required)
|
131
182
|
next unless name
|
132
183
|
|
133
|
-
RequiredParameter.new(name: name)
|
184
|
+
parameters << RequiredParameter.new(name: name)
|
134
185
|
end
|
135
|
-
end
|
136
186
|
|
137
|
-
|
138
|
-
|
187
|
+
parameters_node.optionals.each do |optional|
|
188
|
+
name = parameter_name(optional)
|
189
|
+
next unless name
|
190
|
+
|
191
|
+
parameters << OptionalParameter.new(name: name)
|
192
|
+
end
|
193
|
+
|
194
|
+
parameters_node.keywords.each do |keyword|
|
195
|
+
name = parameter_name(keyword)
|
196
|
+
next unless name
|
197
|
+
|
198
|
+
case keyword
|
199
|
+
when Prism::RequiredKeywordParameterNode
|
200
|
+
parameters << KeywordParameter.new(name: name)
|
201
|
+
when Prism::OptionalKeywordParameterNode
|
202
|
+
parameters << OptionalKeywordParameter.new(name: name)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
parameters
|
139
207
|
end
|
208
|
+
|
209
|
+
sig { params(node: Prism::Node).returns(T.nilable(Symbol)) }
|
140
210
|
def parameter_name(node)
|
141
211
|
case node
|
142
|
-
when Prism::RequiredParameterNode
|
212
|
+
when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
|
213
|
+
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode
|
143
214
|
node.name
|
144
215
|
when Prism::MultiTargetNode
|
145
216
|
names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
|
@@ -84,6 +84,16 @@ module RubyIndexer
|
|
84
84
|
)
|
85
85
|
end
|
86
86
|
|
87
|
+
def test_indexables_does_not_include_non_ruby_files_inside_rubylibdir
|
88
|
+
path = Pathname.new(RbConfig::CONFIG["rubylibdir"]).join("extra_file.txt").to_s
|
89
|
+
FileUtils.touch(path)
|
90
|
+
indexables = @config.indexables
|
91
|
+
|
92
|
+
assert(indexables.none? { |indexable| indexable.full_path == path })
|
93
|
+
ensure
|
94
|
+
FileUtils.rm(T.must(path))
|
95
|
+
end
|
96
|
+
|
87
97
|
def test_paths_are_unique
|
88
98
|
@config.load_config
|
89
99
|
indexables = @config.indexables
|
@@ -246,5 +246,16 @@ module RubyIndexer
|
|
246
246
|
entry = T.must(entries.first).first
|
247
247
|
assert_equal("baz", entry.name)
|
248
248
|
end
|
249
|
+
|
250
|
+
def test_indexing_prism_fixtures_succeeds
|
251
|
+
fixtures = Dir.glob("test/fixtures/prism/test/prism/fixtures/**/*.txt")
|
252
|
+
|
253
|
+
fixtures.each do |fixture|
|
254
|
+
indexable_path = IndexablePath.new("", fixture)
|
255
|
+
@index.index_single(indexable_path)
|
256
|
+
end
|
257
|
+
|
258
|
+
refute_empty(@index.instance_variable_get(:@entries))
|
259
|
+
end
|
249
260
|
end
|
250
261
|
end
|
@@ -70,6 +70,42 @@ module RubyIndexer
|
|
70
70
|
assert_instance_of(Entry::RequiredParameter, parameter)
|
71
71
|
end
|
72
72
|
|
73
|
+
def test_method_with_optional_parameters
|
74
|
+
index(<<~RUBY)
|
75
|
+
class Foo
|
76
|
+
def bar(a = 123)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
|
82
|
+
entry = T.must(@index["bar"].first)
|
83
|
+
assert_equal(1, entry.parameters.length)
|
84
|
+
parameter = entry.parameters.first
|
85
|
+
assert_equal(:a, parameter.name)
|
86
|
+
assert_instance_of(Entry::OptionalParameter, parameter)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_method_with_keyword_parameters
|
90
|
+
index(<<~RUBY)
|
91
|
+
class Foo
|
92
|
+
def bar(a:, b: 123)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
|
97
|
+
assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
|
98
|
+
entry = T.must(@index["bar"].first)
|
99
|
+
assert_equal(2, entry.parameters.length)
|
100
|
+
a, b = entry.parameters
|
101
|
+
|
102
|
+
assert_equal(:a, a.name)
|
103
|
+
assert_instance_of(Entry::KeywordParameter, a)
|
104
|
+
|
105
|
+
assert_equal(:b, b.name)
|
106
|
+
assert_instance_of(Entry::OptionalKeywordParameter, b)
|
107
|
+
end
|
108
|
+
|
73
109
|
def test_keeps_track_of_method_owner
|
74
110
|
index(<<~RUBY)
|
75
111
|
class Foo
|
@@ -83,5 +119,35 @@ module RubyIndexer
|
|
83
119
|
|
84
120
|
assert_equal("Foo", owner_name)
|
85
121
|
end
|
122
|
+
|
123
|
+
def test_keeps_track_of_attributes
|
124
|
+
index(<<~RUBY)
|
125
|
+
class Foo
|
126
|
+
# Hello there
|
127
|
+
attr_reader :bar, :other
|
128
|
+
attr_writer :baz
|
129
|
+
attr_accessor :qux
|
130
|
+
end
|
131
|
+
RUBY
|
132
|
+
|
133
|
+
assert_entry("bar", Entry::Accessor, "/fake/path/foo.rb:2-15:2-18")
|
134
|
+
assert_equal("Hello there", @index["bar"].first.comments.join("\n"))
|
135
|
+
assert_entry("other", Entry::Accessor, "/fake/path/foo.rb:2-21:2-26")
|
136
|
+
assert_equal("Hello there", @index["other"].first.comments.join("\n"))
|
137
|
+
assert_entry("baz=", Entry::Accessor, "/fake/path/foo.rb:3-15:3-18")
|
138
|
+
assert_entry("qux", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
|
139
|
+
assert_entry("qux=", Entry::Accessor, "/fake/path/foo.rb:4-17:4-20")
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_ignores_attributes_invoked_on_constant
|
143
|
+
index(<<~RUBY)
|
144
|
+
class Foo
|
145
|
+
end
|
146
|
+
|
147
|
+
Foo.attr_reader :bar
|
148
|
+
RUBY
|
149
|
+
|
150
|
+
assert_no_entry("bar")
|
151
|
+
end
|
86
152
|
end
|
87
153
|
end
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -41,8 +41,8 @@ module RubyLsp
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Discovers and loads all addons. Returns the list of activated addons
|
44
|
-
sig { returns(T::Array[Addon]) }
|
45
|
-
def load_addons
|
44
|
+
sig { params(message_queue: Thread::Queue).returns(T::Array[Addon]) }
|
45
|
+
def load_addons(message_queue)
|
46
46
|
# Require all addons entry points, which should be placed under
|
47
47
|
# `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
|
48
48
|
Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
|
@@ -55,7 +55,7 @@ module RubyLsp
|
|
55
55
|
# Activate each one of the discovered addons. If any problems occur in the addons, we don't want to
|
56
56
|
# fail to boot the server
|
57
57
|
addons.each do |addon|
|
58
|
-
addon.activate
|
58
|
+
addon.activate(message_queue)
|
59
59
|
nil
|
60
60
|
rescue => e
|
61
61
|
addon.add_error(e)
|
@@ -94,8 +94,8 @@ module RubyLsp
|
|
94
94
|
|
95
95
|
# Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
|
96
96
|
# reading information into memory or even spawning a separate process
|
97
|
-
sig { abstract.void }
|
98
|
-
def activate; end
|
97
|
+
sig { abstract.params(message_queue: Thread::Queue).void }
|
98
|
+
def activate(message_queue); end
|
99
99
|
|
100
100
|
# Each addon should implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
|
101
101
|
# child process
|
@@ -111,10 +111,9 @@ module RubyLsp
|
|
111
111
|
overridable.params(
|
112
112
|
uri: URI::Generic,
|
113
113
|
dispatcher: Prism::Dispatcher,
|
114
|
-
message_queue: Thread::Queue,
|
115
114
|
).returns(T.nilable(Listener[T::Array[Interface::CodeLens]]))
|
116
115
|
end
|
117
|
-
def create_code_lens_listener(uri, dispatcher
|
116
|
+
def create_code_lens_listener(uri, dispatcher); end
|
118
117
|
|
119
118
|
# Creates a new Hover listener. This method is invoked on every Hover request
|
120
119
|
sig do
|
@@ -122,19 +121,17 @@ module RubyLsp
|
|
122
121
|
nesting: T::Array[String],
|
123
122
|
index: RubyIndexer::Index,
|
124
123
|
dispatcher: Prism::Dispatcher,
|
125
|
-
message_queue: Thread::Queue,
|
126
124
|
).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
|
127
125
|
end
|
128
|
-
def create_hover_listener(nesting, index, dispatcher
|
126
|
+
def create_hover_listener(nesting, index, dispatcher); end
|
129
127
|
|
130
128
|
# Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
|
131
129
|
sig do
|
132
130
|
overridable.params(
|
133
131
|
dispatcher: Prism::Dispatcher,
|
134
|
-
message_queue: Thread::Queue,
|
135
132
|
).returns(T.nilable(Listener[T::Array[Interface::DocumentSymbol]]))
|
136
133
|
end
|
137
|
-
def create_document_symbol_listener(dispatcher
|
134
|
+
def create_document_symbol_listener(dispatcher); end
|
138
135
|
|
139
136
|
# Creates a new Definition listener. This method is invoked on every Definition request
|
140
137
|
sig do
|
@@ -143,9 +140,8 @@ module RubyLsp
|
|
143
140
|
nesting: T::Array[String],
|
144
141
|
index: RubyIndexer::Index,
|
145
142
|
dispatcher: Prism::Dispatcher,
|
146
|
-
message_queue: Thread::Queue,
|
147
143
|
).returns(T.nilable(Listener[T.nilable(T.any(T::Array[Interface::Location], Interface::Location))]))
|
148
144
|
end
|
149
|
-
def create_definition_listener(uri, nesting, index, dispatcher
|
145
|
+
def create_definition_listener(uri, nesting, index, dispatcher); end
|
150
146
|
end
|
151
147
|
end
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -41,7 +41,7 @@ module RubyLsp
|
|
41
41
|
when "initialize"
|
42
42
|
initialize_request(request.dig(:params))
|
43
43
|
when "initialized"
|
44
|
-
Addon.load_addons
|
44
|
+
Addon.load_addons(@message_queue)
|
45
45
|
|
46
46
|
errored_addons = Addon.addons.select(&:error?)
|
47
47
|
|
@@ -95,12 +95,12 @@ module RubyLsp
|
|
95
95
|
|
96
96
|
# Run listeners for the document
|
97
97
|
dispatcher = Prism::Dispatcher.new
|
98
|
-
folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher
|
99
|
-
document_symbol = Requests::DocumentSymbol.new(dispatcher
|
100
|
-
document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher
|
101
|
-
code_lens = Requests::CodeLens.new(uri, dispatcher
|
98
|
+
folding_range = Requests::FoldingRanges.new(document.parse_result.comments, dispatcher)
|
99
|
+
document_symbol = Requests::DocumentSymbol.new(dispatcher)
|
100
|
+
document_link = Requests::DocumentLink.new(uri, document.comments, dispatcher)
|
101
|
+
code_lens = Requests::CodeLens.new(uri, dispatcher)
|
102
102
|
|
103
|
-
semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher
|
103
|
+
semantic_highlighting = Requests::SemanticHighlighting.new(dispatcher)
|
104
104
|
dispatcher.dispatch(document.tree)
|
105
105
|
|
106
106
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
@@ -265,13 +265,7 @@ module RubyLsp
|
|
265
265
|
target = parent if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
|
266
266
|
|
267
267
|
dispatcher = Prism::Dispatcher.new
|
268
|
-
base_listener = Requests::Definition.new(
|
269
|
-
uri,
|
270
|
-
nesting,
|
271
|
-
@index,
|
272
|
-
dispatcher,
|
273
|
-
@message_queue,
|
274
|
-
)
|
268
|
+
base_listener = Requests::Definition.new(uri, nesting, @index, dispatcher)
|
275
269
|
dispatcher.dispatch_once(target)
|
276
270
|
base_listener.response
|
277
271
|
end
|
@@ -297,7 +291,7 @@ module RubyLsp
|
|
297
291
|
|
298
292
|
# Instantiate all listeners
|
299
293
|
dispatcher = Prism::Dispatcher.new
|
300
|
-
hover = Requests::Hover.new(@index, nesting, dispatcher
|
294
|
+
hover = Requests::Hover.new(@index, nesting, dispatcher)
|
301
295
|
|
302
296
|
# Emit events for all listeners
|
303
297
|
dispatcher.dispatch_once(target)
|
@@ -355,6 +349,11 @@ module RubyLsp
|
|
355
349
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
356
350
|
return if @store.formatter == "none"
|
357
351
|
|
352
|
+
# Do not format files outside of the workspace. For example, if someone is looking at a gem's source code, we
|
353
|
+
# don't want to format it
|
354
|
+
path = uri.to_standardized_path
|
355
|
+
return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
|
356
|
+
|
358
357
|
Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
|
359
358
|
end
|
360
359
|
|
@@ -380,7 +379,7 @@ module RubyLsp
|
|
380
379
|
|
381
380
|
target, parent = document.locate_node(position)
|
382
381
|
dispatcher = Prism::Dispatcher.new
|
383
|
-
listener = Requests::DocumentHighlight.new(target, parent, dispatcher
|
382
|
+
listener = Requests::DocumentHighlight.new(target, parent, dispatcher)
|
384
383
|
dispatcher.visit(document.tree)
|
385
384
|
listener.response
|
386
385
|
end
|
@@ -393,7 +392,7 @@ module RubyLsp
|
|
393
392
|
end_line = range.dig(:end, :line)
|
394
393
|
|
395
394
|
dispatcher = Prism::Dispatcher.new
|
396
|
-
listener = Requests::InlayHints.new(start_line..end_line, dispatcher
|
395
|
+
listener = Requests::InlayHints.new(start_line..end_line, dispatcher)
|
397
396
|
dispatcher.visit(document.tree)
|
398
397
|
listener.response
|
399
398
|
end
|
@@ -443,6 +442,11 @@ module RubyLsp
|
|
443
442
|
|
444
443
|
sig { params(uri: URI::Generic).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
|
445
444
|
def diagnostic(uri)
|
445
|
+
# Do not compute diagnostics for files outside of the workspace. For example, if someone is looking at a gem's
|
446
|
+
# source code, we don't want to show diagnostics for it
|
447
|
+
path = uri.to_standardized_path
|
448
|
+
return unless path.nil? || path.start_with?(T.must(@store.workspace_uri.to_standardized_path))
|
449
|
+
|
446
450
|
response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
|
447
451
|
Requests::Diagnostics.new(document).run
|
448
452
|
end
|
@@ -457,11 +461,7 @@ module RubyLsp
|
|
457
461
|
end_line = range.dig(:end, :line)
|
458
462
|
|
459
463
|
dispatcher = Prism::Dispatcher.new
|
460
|
-
listener = Requests::SemanticHighlighting.new(
|
461
|
-
dispatcher,
|
462
|
-
@message_queue,
|
463
|
-
range: start_line..end_line,
|
464
|
-
)
|
464
|
+
listener = Requests::SemanticHighlighting.new(dispatcher, range: start_line..end_line)
|
465
465
|
dispatcher.visit(document.tree)
|
466
466
|
|
467
467
|
Requests::Support::SemanticTokenEncoder.new.encode(listener.response)
|
@@ -513,12 +513,7 @@ module RubyLsp
|
|
513
513
|
return unless target
|
514
514
|
|
515
515
|
dispatcher = Prism::Dispatcher.new
|
516
|
-
listener = Requests::Completion.new(
|
517
|
-
@index,
|
518
|
-
nesting,
|
519
|
-
dispatcher,
|
520
|
-
@message_queue,
|
521
|
-
)
|
516
|
+
listener = Requests::Completion.new(@index, nesting, dispatcher)
|
522
517
|
dispatcher.dispatch_once(target)
|
523
518
|
listener.response
|
524
519
|
end
|
@@ -583,6 +578,9 @@ module RubyLsp
|
|
583
578
|
def initialize_request(options)
|
584
579
|
@store.clear
|
585
580
|
|
581
|
+
workspace_uri = options.dig(:workspaceFolders, 0, :uri)
|
582
|
+
@store.workspace_uri = URI(workspace_uri) if workspace_uri
|
583
|
+
|
586
584
|
encodings = options.dig(:capabilities, :general, :positionEncodings)
|
587
585
|
@store.encoding = if encodings.nil? || encodings.empty?
|
588
586
|
Constant::PositionEncodingKind::UTF16
|
data/lib/ruby_lsp/listener.rb
CHANGED
@@ -14,10 +14,9 @@ module RubyLsp
|
|
14
14
|
|
15
15
|
abstract!
|
16
16
|
|
17
|
-
sig { params(dispatcher: Prism::Dispatcher
|
18
|
-
def initialize(dispatcher
|
17
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
18
|
+
def initialize(dispatcher)
|
19
19
|
@dispatcher = dispatcher
|
20
|
-
@message_queue = message_queue
|
21
20
|
end
|
22
21
|
|
23
22
|
sig { returns(ResponseType) }
|
@@ -43,8 +42,8 @@ module RubyLsp
|
|
43
42
|
# When inheriting from ExtensibleListener, the `super` of constructor must be called **after** the subclass's own
|
44
43
|
# ivars have been initialized. This is because the constructor of ExtensibleListener calls
|
45
44
|
# `initialize_external_listener` which may depend on the subclass's ivars.
|
46
|
-
sig { params(dispatcher: Prism::Dispatcher
|
47
|
-
def initialize(dispatcher
|
45
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
46
|
+
def initialize(dispatcher)
|
48
47
|
super
|
49
48
|
@response_merged = T.let(false, T::Boolean)
|
50
49
|
@external_listeners = T.let(
|
@@ -47,16 +47,18 @@ module RubyLsp
|
|
47
47
|
sig { override.returns(ResponseType) }
|
48
48
|
attr_reader :_response
|
49
49
|
|
50
|
-
sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher
|
51
|
-
def initialize(uri, dispatcher
|
50
|
+
sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher).void }
|
51
|
+
def initialize(uri, dispatcher)
|
52
52
|
@uri = T.let(uri, URI::Generic)
|
53
53
|
@_response = T.let([], ResponseType)
|
54
54
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
55
55
|
# visibility_stack is a stack of [current_visibility, previous_visibility]
|
56
56
|
@visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
|
57
57
|
@class_stack = T.let([], T::Array[String])
|
58
|
+
@group_id = T.let(1, Integer)
|
59
|
+
@group_id_stack = T.let([], T::Array[Integer])
|
58
60
|
|
59
|
-
super(dispatcher
|
61
|
+
super(dispatcher)
|
60
62
|
|
61
63
|
dispatcher.register(
|
62
64
|
self,
|
@@ -82,12 +84,16 @@ module RubyLsp
|
|
82
84
|
kind: :group,
|
83
85
|
)
|
84
86
|
end
|
87
|
+
|
88
|
+
@group_id_stack.push(@group_id)
|
89
|
+
@group_id += 1
|
85
90
|
end
|
86
91
|
|
87
92
|
sig { params(node: Prism::ClassNode).void }
|
88
93
|
def on_class_node_leave(node)
|
89
94
|
@visibility_stack.pop
|
90
95
|
@class_stack.pop
|
96
|
+
@group_id_stack.pop
|
91
97
|
end
|
92
98
|
|
93
99
|
sig { params(node: Prism::DefNode).void }
|
@@ -146,7 +152,7 @@ module RubyLsp
|
|
146
152
|
|
147
153
|
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
148
154
|
def initialize_external_listener(addon)
|
149
|
-
addon.create_code_lens_listener(@uri, @dispatcher
|
155
|
+
addon.create_code_lens_listener(@uri, @dispatcher)
|
150
156
|
end
|
151
157
|
|
152
158
|
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
@@ -174,12 +180,15 @@ module RubyLsp
|
|
174
180
|
},
|
175
181
|
]
|
176
182
|
|
183
|
+
grouping_data = { group_id: @group_id_stack.last, kind: kind }
|
184
|
+
grouping_data[:id] = @group_id if kind == :group
|
185
|
+
|
177
186
|
@_response << create_code_lens(
|
178
187
|
node,
|
179
188
|
title: "Run",
|
180
189
|
command_name: "rubyLsp.runTest",
|
181
190
|
arguments: arguments,
|
182
|
-
data: { type: "test",
|
191
|
+
data: { type: "test", **grouping_data },
|
183
192
|
)
|
184
193
|
|
185
194
|
@_response << create_code_lens(
|
@@ -187,7 +196,7 @@ module RubyLsp
|
|
187
196
|
title: "Run In Terminal",
|
188
197
|
command_name: "rubyLsp.runTestInTerminal",
|
189
198
|
arguments: arguments,
|
190
|
-
data: { type: "test_in_terminal",
|
199
|
+
data: { type: "test_in_terminal", **grouping_data },
|
191
200
|
)
|
192
201
|
|
193
202
|
@_response << create_code_lens(
|
@@ -195,7 +204,7 @@ module RubyLsp
|
|
195
204
|
title: "Debug",
|
196
205
|
command_name: "rubyLsp.debugTest",
|
197
206
|
arguments: arguments,
|
198
|
-
data: { type: "debug",
|
207
|
+
data: { type: "debug", **grouping_data },
|
199
208
|
)
|
200
209
|
end
|
201
210
|
|
@@ -36,11 +36,10 @@ module RubyLsp
|
|
36
36
|
index: RubyIndexer::Index,
|
37
37
|
nesting: T::Array[String],
|
38
38
|
dispatcher: Prism::Dispatcher,
|
39
|
-
message_queue: Thread::Queue,
|
40
39
|
).void
|
41
40
|
end
|
42
|
-
def initialize(index, nesting, dispatcher
|
43
|
-
super(dispatcher
|
41
|
+
def initialize(index, nesting, dispatcher)
|
42
|
+
super(dispatcher)
|
44
43
|
@_response = T.let([], ResponseType)
|
45
44
|
@index = index
|
46
45
|
@nesting = nesting
|
@@ -137,18 +136,22 @@ module RubyLsp
|
|
137
136
|
|
138
137
|
receiver = T.must(receiver_entries.first)
|
139
138
|
|
140
|
-
|
141
|
-
|
142
|
-
entry = entries.find { |e| e.owner&.name == receiver.name }
|
139
|
+
@index.prefix_search(name).each do |entries|
|
140
|
+
entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
|
143
141
|
next unless entry
|
144
142
|
|
145
|
-
@_response << build_method_completion(entry, node)
|
143
|
+
@_response << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
|
146
144
|
end
|
147
145
|
end
|
148
146
|
|
149
147
|
private
|
150
148
|
|
151
|
-
sig
|
149
|
+
sig do
|
150
|
+
params(
|
151
|
+
entry: RubyIndexer::Entry::Member,
|
152
|
+
node: Prism::CallNode,
|
153
|
+
).returns(Interface::CompletionItem)
|
154
|
+
end
|
152
155
|
def build_method_completion(entry, node)
|
153
156
|
name = entry.name
|
154
157
|
parameters = entry.parameters
|
@@ -168,13 +171,16 @@ module RubyLsp
|
|
168
171
|
|
169
172
|
sig { params(label: String, node: Prism::StringNode).returns(Interface::CompletionItem) }
|
170
173
|
def build_completion(label, node)
|
174
|
+
# We should use the content location as we only replace the content and not the delimiters of the string
|
175
|
+
loc = node.content_loc
|
176
|
+
|
171
177
|
Interface::CompletionItem.new(
|
172
178
|
label: label,
|
173
179
|
text_edit: Interface::TextEdit.new(
|
174
|
-
range:
|
180
|
+
range: range_from_location(loc),
|
175
181
|
new_text: label,
|
176
182
|
),
|
177
|
-
kind: Constant::CompletionItemKind::
|
183
|
+
kind: Constant::CompletionItemKind::FILE,
|
178
184
|
)
|
179
185
|
end
|
180
186
|
|
@@ -37,16 +37,15 @@ module RubyLsp
|
|
37
37
|
nesting: T::Array[String],
|
38
38
|
index: RubyIndexer::Index,
|
39
39
|
dispatcher: Prism::Dispatcher,
|
40
|
-
message_queue: Thread::Queue,
|
41
40
|
).void
|
42
41
|
end
|
43
|
-
def initialize(uri, nesting, index, dispatcher
|
42
|
+
def initialize(uri, nesting, index, dispatcher)
|
44
43
|
@uri = uri
|
45
44
|
@nesting = nesting
|
46
45
|
@index = index
|
47
46
|
@_response = T.let(nil, ResponseType)
|
48
47
|
|
49
|
-
super(dispatcher
|
48
|
+
super(dispatcher)
|
50
49
|
|
51
50
|
dispatcher.register(
|
52
51
|
self,
|
@@ -58,7 +57,7 @@ module RubyLsp
|
|
58
57
|
|
59
58
|
sig { override.params(addon: Addon).returns(T.nilable(RubyLsp::Listener[ResponseType])) }
|
60
59
|
def initialize_external_listener(addon)
|
61
|
-
addon.create_definition_listener(@uri, @nesting, @index, @dispatcher
|
60
|
+
addon.create_definition_listener(@uri, @nesting, @index, @dispatcher)
|
62
61
|
end
|
63
62
|
|
64
63
|
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
@@ -32,13 +32,8 @@ module RubyLsp
|
|
32
32
|
def run
|
33
33
|
# Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
|
34
34
|
return syntax_error_diagnostics if @document.syntax_error?
|
35
|
-
|
36
35
|
return unless defined?(Support::RuboCopDiagnosticsRunner)
|
37
36
|
|
38
|
-
# Don't try to run RuboCop diagnostics for files outside the current working directory
|
39
|
-
path = @uri.to_standardized_path
|
40
|
-
return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
|
41
|
-
|
42
37
|
Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document).map!(&:to_lsp_diagnostic)
|
43
38
|
end
|
44
39
|
|
@@ -114,11 +114,10 @@ module RubyLsp
|
|
114
114
|
target: T.nilable(Prism::Node),
|
115
115
|
parent: T.nilable(Prism::Node),
|
116
116
|
dispatcher: Prism::Dispatcher,
|
117
|
-
message_queue: Thread::Queue,
|
118
117
|
).void
|
119
118
|
end
|
120
|
-
def initialize(target, parent, dispatcher
|
121
|
-
super(dispatcher
|
119
|
+
def initialize(target, parent, dispatcher)
|
120
|
+
super(dispatcher)
|
122
121
|
|
123
122
|
@_response = T.let([], T::Array[Interface::DocumentHighlight])
|
124
123
|
|
@@ -80,11 +80,10 @@ module RubyLsp
|
|
80
80
|
uri: URI::Generic,
|
81
81
|
comments: T::Array[Prism::Comment],
|
82
82
|
dispatcher: Prism::Dispatcher,
|
83
|
-
message_queue: Thread::Queue,
|
84
83
|
).void
|
85
84
|
end
|
86
|
-
def initialize(uri, comments, dispatcher
|
87
|
-
super(dispatcher
|
85
|
+
def initialize(uri, comments, dispatcher)
|
86
|
+
super(dispatcher)
|
88
87
|
|
89
88
|
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
90
89
|
# in the URI
|
@@ -49,8 +49,8 @@ module RubyLsp
|
|
49
49
|
sig { override.returns(T::Array[Interface::DocumentSymbol]) }
|
50
50
|
attr_reader :_response
|
51
51
|
|
52
|
-
sig { params(dispatcher: Prism::Dispatcher
|
53
|
-
def initialize(dispatcher
|
52
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
53
|
+
def initialize(dispatcher)
|
54
54
|
@root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
|
55
55
|
@_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
|
56
56
|
@stack = T.let(
|
@@ -80,7 +80,7 @@ module RubyLsp
|
|
80
80
|
|
81
81
|
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
82
82
|
def initialize_external_listener(addon)
|
83
|
-
addon.create_document_symbol_listener(@dispatcher
|
83
|
+
addon.create_document_symbol_listener(@dispatcher)
|
84
84
|
end
|
85
85
|
|
86
86
|
# Merges responses from other listeners
|
@@ -21,9 +21,9 @@ module RubyLsp
|
|
21
21
|
|
22
22
|
ResponseType = type_member { { fixed: T::Array[Interface::FoldingRange] } }
|
23
23
|
|
24
|
-
sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher
|
25
|
-
def initialize(comments, dispatcher
|
26
|
-
super(dispatcher
|
24
|
+
sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher).void }
|
25
|
+
def initialize(comments, dispatcher)
|
26
|
+
super(dispatcher)
|
27
27
|
|
28
28
|
@_response = T.let([], ResponseType)
|
29
29
|
@requires = T.let([], T::Array[Prism::CallNode])
|
@@ -40,6 +40,7 @@ module RubyLsp
|
|
40
40
|
:on_array_node_enter,
|
41
41
|
:on_block_node_enter,
|
42
42
|
:on_case_node_enter,
|
43
|
+
:on_case_match_node_enter,
|
43
44
|
:on_class_node_enter,
|
44
45
|
:on_module_node_enter,
|
45
46
|
:on_for_node_enter,
|
@@ -51,7 +52,6 @@ module RubyLsp
|
|
51
52
|
:on_else_node_enter,
|
52
53
|
:on_ensure_node_enter,
|
53
54
|
:on_begin_node_enter,
|
54
|
-
:on_string_concat_node_enter,
|
55
55
|
:on_def_node_enter,
|
56
56
|
:on_call_node_enter,
|
57
57
|
:on_lambda_node_enter,
|
@@ -91,10 +91,10 @@ module RubyLsp
|
|
91
91
|
|
92
92
|
sig { params(node: Prism::InterpolatedStringNode).void }
|
93
93
|
def on_interpolated_string_node_enter(node)
|
94
|
-
opening_loc = node.opening_loc
|
95
|
-
closing_loc = node.closing_loc
|
94
|
+
opening_loc = node.opening_loc || node.location
|
95
|
+
closing_loc = node.closing_loc || node.parts.last&.location || node.location
|
96
96
|
|
97
|
-
add_lines_range(opening_loc.start_line, closing_loc.start_line - 1)
|
97
|
+
add_lines_range(opening_loc.start_line, closing_loc.start_line - 1)
|
98
98
|
end
|
99
99
|
|
100
100
|
sig { params(node: Prism::ArrayNode).void }
|
@@ -112,6 +112,11 @@ module RubyLsp
|
|
112
112
|
add_simple_range(node)
|
113
113
|
end
|
114
114
|
|
115
|
+
sig { params(node: Prism::CaseMatchNode).void }
|
116
|
+
def on_case_match_node_enter(node)
|
117
|
+
add_simple_range(node)
|
118
|
+
end
|
119
|
+
|
115
120
|
sig { params(node: Prism::ClassNode).void }
|
116
121
|
def on_class_node_enter(node)
|
117
122
|
add_simple_range(node)
|
@@ -167,14 +172,6 @@ module RubyLsp
|
|
167
172
|
add_simple_range(node)
|
168
173
|
end
|
169
174
|
|
170
|
-
sig { params(node: Prism::StringConcatNode).void }
|
171
|
-
def on_string_concat_node_enter(node)
|
172
|
-
left = T.let(node.left, Prism::Node)
|
173
|
-
left = left.left while left.is_a?(Prism::StringConcatNode)
|
174
|
-
|
175
|
-
add_lines_range(left.location.start_line, node.right.location.end_line - 1)
|
176
|
-
end
|
177
|
-
|
178
175
|
sig { params(node: Prism::DefNode).void }
|
179
176
|
def on_def_node_enter(node)
|
180
177
|
params = node.parameters
|
@@ -64,11 +64,6 @@ module RubyLsp
|
|
64
64
|
sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
|
65
65
|
def run
|
66
66
|
return if @formatter == "none"
|
67
|
-
|
68
|
-
# Don't try to format files outside the current working directory
|
69
|
-
path = @uri.to_standardized_path
|
70
|
-
return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
|
71
|
-
|
72
67
|
return if @document.syntax_error?
|
73
68
|
|
74
69
|
formatted_text = formatted_file
|
@@ -37,15 +37,14 @@ module RubyLsp
|
|
37
37
|
index: RubyIndexer::Index,
|
38
38
|
nesting: T::Array[String],
|
39
39
|
dispatcher: Prism::Dispatcher,
|
40
|
-
message_queue: Thread::Queue,
|
41
40
|
).void
|
42
41
|
end
|
43
|
-
def initialize(index, nesting, dispatcher
|
42
|
+
def initialize(index, nesting, dispatcher)
|
44
43
|
@index = index
|
45
44
|
@nesting = nesting
|
46
45
|
@_response = T.let(nil, ResponseType)
|
47
46
|
|
48
|
-
super(dispatcher
|
47
|
+
super(dispatcher)
|
49
48
|
dispatcher.register(
|
50
49
|
self,
|
51
50
|
:on_constant_read_node_enter,
|
@@ -57,7 +56,7 @@ module RubyLsp
|
|
57
56
|
|
58
57
|
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
59
58
|
def initialize_external_listener(addon)
|
60
|
-
addon.create_hover_listener(@nesting, @index, @dispatcher
|
59
|
+
addon.create_hover_listener(@nesting, @index, @dispatcher)
|
61
60
|
end
|
62
61
|
|
63
62
|
# Merges responses from other hover listeners
|
@@ -39,9 +39,9 @@ module RubyLsp
|
|
39
39
|
sig { override.returns(ResponseType) }
|
40
40
|
attr_reader :_response
|
41
41
|
|
42
|
-
sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher
|
43
|
-
def initialize(range, dispatcher
|
44
|
-
super(dispatcher
|
42
|
+
sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher).void }
|
43
|
+
def initialize(range, dispatcher)
|
44
|
+
super(dispatcher)
|
45
45
|
|
46
46
|
@_response = T.let([], ResponseType)
|
47
47
|
@range = range
|
@@ -20,8 +20,8 @@ module RubyLsp
|
|
20
20
|
|
21
21
|
END_REGEXES = T.let(
|
22
22
|
[
|
23
|
-
|
24
|
-
/.*\
|
23
|
+
/\b(if|unless|for|while|class|module|until|def|case)\b.*/,
|
24
|
+
/.*\s\bdo\b/,
|
25
25
|
],
|
26
26
|
T::Array[Regexp],
|
27
27
|
)
|
@@ -139,7 +139,6 @@ module RubyLsp
|
|
139
139
|
sig { params(spaces: String).void }
|
140
140
|
def handle_comment_line(spaces)
|
141
141
|
add_edit_with_text("##{spaces}")
|
142
|
-
move_cursor_to(@position[:line], @indentation + spaces.size + 1)
|
143
142
|
end
|
144
143
|
|
145
144
|
sig { params(text: String, position: Document::PositionShape).void }
|
@@ -107,20 +107,15 @@ module RubyLsp
|
|
107
107
|
sig { override.returns(ResponseType) }
|
108
108
|
attr_reader :_response
|
109
109
|
|
110
|
-
sig
|
111
|
-
|
112
|
-
|
113
|
-
message_queue: Thread::Queue,
|
114
|
-
range: T.nilable(T::Range[Integer]),
|
115
|
-
).void
|
116
|
-
end
|
117
|
-
def initialize(dispatcher, message_queue, range: nil)
|
118
|
-
super(dispatcher, message_queue)
|
110
|
+
sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void }
|
111
|
+
def initialize(dispatcher, range: nil)
|
112
|
+
super(dispatcher)
|
119
113
|
|
120
114
|
@_response = T.let([], ResponseType)
|
121
115
|
@range = range
|
122
116
|
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
123
117
|
@current_scope = T.let(ParameterScope.new, ParameterScope)
|
118
|
+
@inside_regex_capture = T.let(false, T::Boolean)
|
124
119
|
|
125
120
|
dispatcher.register(
|
126
121
|
self,
|
@@ -152,6 +147,8 @@ module RubyLsp
|
|
152
147
|
:on_local_variable_or_write_node_enter,
|
153
148
|
:on_local_variable_target_node_enter,
|
154
149
|
:on_block_local_variable_node_enter,
|
150
|
+
:on_match_write_node_enter,
|
151
|
+
:on_match_write_node_leave,
|
155
152
|
)
|
156
153
|
end
|
157
154
|
|
@@ -165,14 +162,28 @@ module RubyLsp
|
|
165
162
|
# We can't push a semantic token for [] and []= because the argument inside the brackets is a part of
|
166
163
|
# the message_loc
|
167
164
|
return if message.start_with?("[") && (message.end_with?("]") || message.end_with?("]="))
|
168
|
-
|
169
|
-
return process_regexp_locals(node) if message == "=~"
|
165
|
+
return if message == "=~"
|
170
166
|
return if special_method?(message)
|
171
167
|
|
172
168
|
type = Support::Sorbet.annotation?(node) ? :type : :method
|
173
169
|
add_token(T.must(node.message_loc), type)
|
174
170
|
end
|
175
171
|
|
172
|
+
sig { params(node: Prism::MatchWriteNode).void }
|
173
|
+
def on_match_write_node_enter(node)
|
174
|
+
call = node.call
|
175
|
+
|
176
|
+
if call.message == "=~"
|
177
|
+
@inside_regex_capture = true
|
178
|
+
process_regexp_locals(call)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
sig { params(node: Prism::MatchWriteNode).void }
|
183
|
+
def on_match_write_node_leave(node)
|
184
|
+
@inside_regex_capture = true if node.call.message == "=~"
|
185
|
+
end
|
186
|
+
|
176
187
|
sig { params(node: Prism::ConstantReadNode).void }
|
177
188
|
def on_constant_read_node_enter(node)
|
178
189
|
return unless visible?(node, @range)
|
@@ -359,6 +370,12 @@ module RubyLsp
|
|
359
370
|
|
360
371
|
sig { params(node: Prism::LocalVariableTargetNode).void }
|
361
372
|
def on_local_variable_target_node_enter(node)
|
373
|
+
# If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
|
374
|
+
# Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
|
375
|
+
# the entire regex as a local variable. We process these captures in process_regexp_locals instead and then
|
376
|
+
# prevent pushing local variable target tokens. See https://github.com/ruby/prism/issues/1912
|
377
|
+
return if @inside_regex_capture
|
378
|
+
|
362
379
|
return unless visible?(node, @range)
|
363
380
|
|
364
381
|
add_token(node.location, @current_scope.type_for(node.name))
|
@@ -34,15 +34,10 @@ module RubyLsp
|
|
34
34
|
|
35
35
|
sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
|
36
36
|
def run(uri, document)
|
37
|
-
|
38
|
-
|
39
|
-
return if @options.ignore_files.any? { |pattern| File.fnmatch(pattern, relative_path) }
|
37
|
+
path = uri.to_standardized_path
|
38
|
+
return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
|
40
39
|
|
41
|
-
SyntaxTree.format(
|
42
|
-
document.source,
|
43
|
-
@options.print_width,
|
44
|
-
options: @options.formatter_options,
|
45
|
-
)
|
40
|
+
SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
|
46
41
|
end
|
47
42
|
end
|
48
43
|
end
|
@@ -75,6 +75,8 @@ module RubyLsp
|
|
75
75
|
Constant::SymbolKind::CONSTANT
|
76
76
|
when RubyIndexer::Entry::Method
|
77
77
|
entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
78
|
+
when RubyIndexer::Entry::Accessor
|
79
|
+
Constant::SymbolKind::PROPERTY
|
78
80
|
end
|
79
81
|
end
|
80
82
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -17,6 +17,9 @@ module RubyLsp
|
|
17
17
|
sig { returns(T::Boolean) }
|
18
18
|
attr_accessor :experimental_features
|
19
19
|
|
20
|
+
sig { returns(URI::Generic) }
|
21
|
+
attr_accessor :workspace_uri
|
22
|
+
|
20
23
|
sig { void }
|
21
24
|
def initialize
|
22
25
|
@state = T.let({}, T::Hash[String, Document])
|
@@ -24,6 +27,7 @@ module RubyLsp
|
|
24
27
|
@formatter = T.let("auto", String)
|
25
28
|
@supports_progress = T.let(true, T::Boolean)
|
26
29
|
@experimental_features = T.let(false, T::Boolean)
|
30
|
+
@workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
27
31
|
end
|
28
32
|
|
29
33
|
sig { params(uri: URI::Generic).returns(Document) }
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -4,10 +4,6 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
# Used to indicate that a request shouldn't return a response
|
6
6
|
VOID = T.let(Object.new.freeze, Object)
|
7
|
-
|
8
|
-
# This freeze is not redundant since the interpolated string is mutable
|
9
|
-
WORKSPACE_URI = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
10
|
-
|
11
7
|
BUNDLE_PATH = T.let(
|
12
8
|
begin
|
13
9
|
Bundler.bundle_path.to_s
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -30,20 +30,20 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.18.0
|
34
34
|
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: '0.
|
36
|
+
version: '0.19'
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.
|
43
|
+
version: 0.18.0
|
44
44
|
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '0.
|
46
|
+
version: '0.19'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: sorbet-runtime
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|