ruby-lsp 0.19.0 → 0.19.1
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-check +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +7 -0
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +5 -3
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +20 -0
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +76 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +12 -0
- data/lib/ruby_indexer/test/index_test.rb +3 -0
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +14 -0
- data/lib/ruby_indexer/test/reference_finder_test.rb +162 -6
- data/lib/ruby_lsp/erb_document.rb +6 -1
- data/lib/ruby_lsp/internal.rb +3 -1
- data/lib/ruby_lsp/requests/code_action_resolve.rb +9 -3
- data/lib/ruby_lsp/requests/completion.rb +1 -0
- data/lib/ruby_lsp/requests/definition.rb +1 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +1 -0
- data/lib/ruby_lsp/requests/range_formatting.rb +55 -0
- data/lib/ruby_lsp/requests/references.rb +146 -0
- data/lib/ruby_lsp/requests/rename.rb +16 -9
- data/lib/ruby_lsp/requests/signature_help.rb +6 -1
- data/lib/ruby_lsp/requests/support/formatter.rb +3 -0
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +6 -0
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +8 -0
- data/lib/ruby_lsp/ruby_document.rb +23 -8
- data/lib/ruby_lsp/server.rb +63 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d900634d5328c8b4b5fcf799f807456bec3cdb94d6294c80fa9152bcf45aedde
|
4
|
+
data.tar.gz: b7661867d38331ec158312b10c7f530cbd58994ee94e8ff592f478e7e05efcb7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f7955f676bc5f231f990f04d8aea1a767165d4f2cac1e9514928014cd8a09eb68c60b4cd226a94b554dac88d77c2117ae3a3ea76bb66e68980974f1685d8970
|
7
|
+
data.tar.gz: 1b914d5607e5730139780cd1f172923db7878a7a0a4098daa1e17df8a1a7a2d8b6731b092baf7eeefc9f55605305e4d70d8cca8489f68a17bab532026c38e8b2
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.19.
|
1
|
+
0.19.1
|
data/exe/ruby-lsp-check
CHANGED
@@ -32,7 +32,7 @@ files.each_with_index do |file, index|
|
|
32
32
|
})
|
33
33
|
|
34
34
|
result = server.pop_response
|
35
|
-
errors[file] = result
|
35
|
+
errors[file] = result if result.is_a?(RubyLsp::Error)
|
36
36
|
ensure
|
37
37
|
server.process_message({ method: "textDocument/didClose", params: { textDocument: { uri: uri } } })
|
38
38
|
server.pop_response
|
@@ -110,6 +110,10 @@ module RubyIndexer
|
|
110
110
|
else
|
111
111
|
""
|
112
112
|
end
|
113
|
+
rescue Errno::ENOENT
|
114
|
+
# If the file was deleted, but the entry hasn't been removed yet (could happen due to concurrency), then we do
|
115
|
+
# not want to fail. Just set the comments to an empty string
|
116
|
+
""
|
113
117
|
end
|
114
118
|
end
|
115
119
|
|
@@ -525,6 +529,9 @@ module RubyIndexer
|
|
525
529
|
end
|
526
530
|
end
|
527
531
|
|
532
|
+
# Represents a global variable e.g.: $DEBUG
|
533
|
+
class GlobalVariable < Entry; end
|
534
|
+
|
528
535
|
# Represents an instance variable e.g.: @a = 1
|
529
536
|
class InstanceVariable < Entry
|
530
537
|
sig { returns(T.nilable(Entry::Namespace)) }
|
@@ -677,11 +677,13 @@ module RubyIndexer
|
|
677
677
|
sig do
|
678
678
|
type_parameters(:T).params(
|
679
679
|
path: String,
|
680
|
-
type: T::Class[T.all(T.type_parameter(:T), Entry)],
|
681
|
-
).returns(T.nilable(T::Array[T.type_parameter(:T)]))
|
680
|
+
type: T.nilable(T::Class[T.all(T.type_parameter(:T), Entry)]),
|
681
|
+
).returns(T.nilable(T.any(T::Array[Entry], T::Array[T.type_parameter(:T)])))
|
682
682
|
end
|
683
|
-
def entries_for(path, type)
|
683
|
+
def entries_for(path, type = nil)
|
684
684
|
entries = @files_to_entries[path]
|
685
|
+
return entries unless type
|
686
|
+
|
685
687
|
entries&.grep(type)
|
686
688
|
end
|
687
689
|
|
@@ -44,6 +44,8 @@ module RubyIndexer
|
|
44
44
|
when RBS::AST::Declarations::Constant
|
45
45
|
namespace_nesting = declaration.name.namespace.path.map(&:to_s)
|
46
46
|
handle_constant(declaration, namespace_nesting, pathname.to_s)
|
47
|
+
when RBS::AST::Declarations::Global
|
48
|
+
handle_global_variable(declaration, pathname)
|
47
49
|
else # rubocop:disable Style/EmptyElse
|
48
50
|
# Other kinds not yet handled
|
49
51
|
end
|
@@ -271,6 +273,23 @@ module RubyIndexer
|
|
271
273
|
))
|
272
274
|
end
|
273
275
|
|
276
|
+
sig { params(declaration: RBS::AST::Declarations::Global, pathname: Pathname).void }
|
277
|
+
def handle_global_variable(declaration, pathname)
|
278
|
+
name = declaration.name.to_s
|
279
|
+
file_path = pathname.to_s
|
280
|
+
location = to_ruby_indexer_location(declaration.location)
|
281
|
+
comments = comments_to_string(declaration)
|
282
|
+
encoding = @index.configuration.encoding
|
283
|
+
|
284
|
+
@index.add(Entry::GlobalVariable.new(
|
285
|
+
name,
|
286
|
+
file_path,
|
287
|
+
location,
|
288
|
+
comments,
|
289
|
+
encoding,
|
290
|
+
))
|
291
|
+
end
|
292
|
+
|
274
293
|
sig { params(member: RBS::AST::Members::Alias, owner_entry: Entry::Namespace).void }
|
275
294
|
def handle_signature_alias(member, owner_entry)
|
276
295
|
file_path = member.location.buffer.name
|
@@ -294,6 +313,7 @@ module RubyIndexer
|
|
294
313
|
RBS::AST::Declarations::Class,
|
295
314
|
RBS::AST::Declarations::Module,
|
296
315
|
RBS::AST::Declarations::Constant,
|
316
|
+
RBS::AST::Declarations::Global,
|
297
317
|
RBS::AST::Members::MethodDefinition,
|
298
318
|
RBS::AST::Members::Alias,
|
299
319
|
)).returns(T.nilable(String))
|
@@ -5,6 +5,38 @@ module RubyIndexer
|
|
5
5
|
class ReferenceFinder
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
class Target
|
9
|
+
extend T::Helpers
|
10
|
+
|
11
|
+
abstract!
|
12
|
+
end
|
13
|
+
|
14
|
+
class ConstTarget < Target
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { returns(String) }
|
18
|
+
attr_reader :fully_qualified_name
|
19
|
+
|
20
|
+
sig { params(fully_qualified_name: String).void }
|
21
|
+
def initialize(fully_qualified_name)
|
22
|
+
super()
|
23
|
+
@fully_qualified_name = fully_qualified_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class MethodTarget < Target
|
28
|
+
extend T::Sig
|
29
|
+
|
30
|
+
sig { returns(String) }
|
31
|
+
attr_reader :method_name
|
32
|
+
|
33
|
+
sig { params(method_name: String).void }
|
34
|
+
def initialize(method_name)
|
35
|
+
super()
|
36
|
+
@method_name = method_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
8
40
|
class Reference
|
9
41
|
extend T::Sig
|
10
42
|
|
@@ -14,26 +46,29 @@ module RubyIndexer
|
|
14
46
|
sig { returns(Prism::Location) }
|
15
47
|
attr_reader :location
|
16
48
|
|
17
|
-
sig {
|
18
|
-
|
49
|
+
sig { returns(T::Boolean) }
|
50
|
+
attr_reader :declaration
|
51
|
+
|
52
|
+
sig { params(name: String, location: Prism::Location, declaration: T::Boolean).void }
|
53
|
+
def initialize(name, location, declaration:)
|
19
54
|
@name = name
|
20
55
|
@location = location
|
56
|
+
@declaration = declaration
|
21
57
|
end
|
22
58
|
end
|
23
59
|
|
24
|
-
sig { returns(T::Array[Reference]) }
|
25
|
-
attr_reader :references
|
26
|
-
|
27
60
|
sig do
|
28
61
|
params(
|
29
|
-
|
62
|
+
target: Target,
|
30
63
|
index: RubyIndexer::Index,
|
31
64
|
dispatcher: Prism::Dispatcher,
|
65
|
+
include_declarations: T::Boolean,
|
32
66
|
).void
|
33
67
|
end
|
34
|
-
def initialize(
|
35
|
-
@
|
68
|
+
def initialize(target, index, dispatcher, include_declarations: true)
|
69
|
+
@target = target
|
36
70
|
@index = index
|
71
|
+
@include_declarations = include_declarations
|
37
72
|
@stack = T.let([], T::Array[String])
|
38
73
|
@references = T.let([], T::Array[Reference])
|
39
74
|
|
@@ -59,17 +94,25 @@ module RubyIndexer
|
|
59
94
|
:on_constant_or_write_node_enter,
|
60
95
|
:on_constant_and_write_node_enter,
|
61
96
|
:on_constant_operator_write_node_enter,
|
97
|
+
:on_call_node_enter,
|
62
98
|
)
|
63
99
|
end
|
64
100
|
|
101
|
+
sig { returns(T::Array[Reference]) }
|
102
|
+
def references
|
103
|
+
return @references if @include_declarations
|
104
|
+
|
105
|
+
@references.reject(&:declaration)
|
106
|
+
end
|
107
|
+
|
65
108
|
sig { params(node: Prism::ClassNode).void }
|
66
109
|
def on_class_node_enter(node)
|
67
110
|
constant_path = node.constant_path
|
68
111
|
name = constant_path.slice
|
69
112
|
nesting = actual_nesting(name)
|
70
113
|
|
71
|
-
if nesting.join("::") == @fully_qualified_name
|
72
|
-
@references << Reference.new(name, constant_path.location)
|
114
|
+
if @target.is_a?(ConstTarget) && nesting.join("::") == @target.fully_qualified_name
|
115
|
+
@references << Reference.new(name, constant_path.location, declaration: true)
|
73
116
|
end
|
74
117
|
|
75
118
|
@stack << name
|
@@ -86,8 +129,8 @@ module RubyIndexer
|
|
86
129
|
name = constant_path.slice
|
87
130
|
nesting = actual_nesting(name)
|
88
131
|
|
89
|
-
if nesting.join("::") == @fully_qualified_name
|
90
|
-
@references << Reference.new(name, constant_path.location)
|
132
|
+
if @target.is_a?(ConstTarget) && nesting.join("::") == @target.fully_qualified_name
|
133
|
+
@references << Reference.new(name, constant_path.location, declaration: true)
|
91
134
|
end
|
92
135
|
|
93
136
|
@stack << name
|
@@ -203,6 +246,10 @@ module RubyIndexer
|
|
203
246
|
|
204
247
|
sig { params(node: Prism::DefNode).void }
|
205
248
|
def on_def_node_enter(node)
|
249
|
+
if @target.is_a?(MethodTarget) && (name = node.name.to_s) == @target.method_name
|
250
|
+
@references << Reference.new(name, node.name_loc, declaration: true)
|
251
|
+
end
|
252
|
+
|
206
253
|
if node.receiver.is_a?(Prism::SelfNode)
|
207
254
|
@stack << "<Class:#{@stack.last}>"
|
208
255
|
end
|
@@ -215,6 +262,13 @@ module RubyIndexer
|
|
215
262
|
end
|
216
263
|
end
|
217
264
|
|
265
|
+
sig { params(node: Prism::CallNode).void }
|
266
|
+
def on_call_node_enter(node)
|
267
|
+
if @target.is_a?(MethodTarget) && (name = node.name.to_s) == @target.method_name
|
268
|
+
@references << Reference.new(name, T.must(node.message_loc), declaration: false)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
218
272
|
private
|
219
273
|
|
220
274
|
sig { params(name: String).returns(T::Array[String]) }
|
@@ -233,13 +287,21 @@ module RubyIndexer
|
|
233
287
|
|
234
288
|
sig { params(name: String, location: Prism::Location).void }
|
235
289
|
def collect_constant_references(name, location)
|
290
|
+
return unless @target.is_a?(ConstTarget)
|
291
|
+
|
236
292
|
entries = @index.resolve(name, @stack)
|
237
293
|
return unless entries
|
238
294
|
|
295
|
+
previous_reference = @references.last
|
296
|
+
|
239
297
|
entries.each do |entry|
|
240
|
-
next unless entry.name == @fully_qualified_name
|
298
|
+
next unless entry.name == @target.fully_qualified_name
|
299
|
+
|
300
|
+
# When processing a class/module declaration, we eagerly handle the constant reference. To avoid duplicates,
|
301
|
+
# when we find the constant node defining the namespace, then we have to check if it wasn't already added
|
302
|
+
next if previous_reference&.location == location
|
241
303
|
|
242
|
-
@references << Reference.new(name, location)
|
304
|
+
@references << Reference.new(name, location, declaration: false)
|
243
305
|
end
|
244
306
|
end
|
245
307
|
|
@@ -630,5 +630,17 @@ module RubyIndexer
|
|
630
630
|
its namespace nesting, and the surrounding CallNode (e.g. a method call).
|
631
631
|
COMMENTS
|
632
632
|
end
|
633
|
+
|
634
|
+
def test_lazy_comment_fetching_does_not_fail_if_file_gets_deleted
|
635
|
+
indexable = IndexablePath.new("#{Dir.pwd}/lib", "lib/ruby_lsp/does_not_exist.rb")
|
636
|
+
|
637
|
+
@index.index_single(indexable, <<~RUBY, collect_comments: false)
|
638
|
+
class Foo
|
639
|
+
end
|
640
|
+
RUBY
|
641
|
+
|
642
|
+
entry = @index["Foo"].first
|
643
|
+
assert_empty(entry.comments)
|
644
|
+
end
|
633
645
|
end
|
634
646
|
end
|
@@ -1858,6 +1858,9 @@ module RubyIndexer
|
|
1858
1858
|
|
1859
1859
|
entries = @index.entries_for("/fake/path/foo.rb", RubyIndexer::Entry::Namespace)
|
1860
1860
|
assert_equal(["Foo", "Bar", "Bar::<Class:Bar>"], entries.map(&:name))
|
1861
|
+
|
1862
|
+
entries = @index.entries_for("/fake/path/foo.rb")
|
1863
|
+
assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
|
1861
1864
|
end
|
1862
1865
|
|
1863
1866
|
def test_entries_for_returns_nil_if_no_matches
|
@@ -76,6 +76,20 @@ module RubyIndexer
|
|
76
76
|
assert_operator(entry.location.end_column, :>, 0)
|
77
77
|
end
|
78
78
|
|
79
|
+
def test_index_global_declaration
|
80
|
+
entries = @index["$DEBUG"]
|
81
|
+
refute_nil(entries)
|
82
|
+
assert_equal(1, entries.length)
|
83
|
+
|
84
|
+
entry = entries.first
|
85
|
+
|
86
|
+
assert_instance_of(Entry::GlobalVariable, entry)
|
87
|
+
assert_equal("$DEBUG", entry.name)
|
88
|
+
assert_match(%r{/gems/rbs-.*/core/global_variables.rbs}, entry.file_path)
|
89
|
+
assert_operator(entry.location.start_column, :<, entry.location.end_column)
|
90
|
+
assert_equal(entry.location.start_line, entry.location.end_line)
|
91
|
+
end
|
92
|
+
|
79
93
|
def test_attaches_correct_owner_to_singleton_methods
|
80
94
|
entries = @index["basename"]
|
81
95
|
refute_nil(entries)
|
@@ -6,7 +6,7 @@ require "test_helper"
|
|
6
6
|
module RubyIndexer
|
7
7
|
class ReferenceFinderTest < Minitest::Test
|
8
8
|
def test_finds_constant_references
|
9
|
-
refs =
|
9
|
+
refs = find_const_references("Foo::Bar", <<~RUBY)
|
10
10
|
module Foo
|
11
11
|
class Bar
|
12
12
|
end
|
@@ -28,7 +28,7 @@ module RubyIndexer
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_finds_constant_references_inside_singleton_contexts
|
31
|
-
refs =
|
31
|
+
refs = find_const_references("Foo::<Class:Foo>::Bar", <<~RUBY)
|
32
32
|
class Foo
|
33
33
|
class << self
|
34
34
|
class Bar
|
@@ -47,7 +47,7 @@ module RubyIndexer
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_finds_top_level_constant_references
|
50
|
-
refs =
|
50
|
+
refs = find_const_references("Bar", <<~RUBY)
|
51
51
|
class Bar
|
52
52
|
end
|
53
53
|
|
@@ -70,17 +70,173 @@ module RubyIndexer
|
|
70
70
|
assert_equal(8, refs[2].location.start_line)
|
71
71
|
end
|
72
72
|
|
73
|
+
def test_finds_method_references
|
74
|
+
refs = find_method_references("foo", <<~RUBY)
|
75
|
+
class Bar
|
76
|
+
def foo
|
77
|
+
end
|
78
|
+
|
79
|
+
def baz
|
80
|
+
foo
|
81
|
+
end
|
82
|
+
end
|
83
|
+
RUBY
|
84
|
+
|
85
|
+
assert_equal(2, refs.size)
|
86
|
+
|
87
|
+
assert_equal("foo", refs[0].name)
|
88
|
+
assert_equal(2, refs[0].location.start_line)
|
89
|
+
|
90
|
+
assert_equal("foo", refs[1].name)
|
91
|
+
assert_equal(6, refs[1].location.start_line)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_does_not_mismatch_on_readers_and_writers
|
95
|
+
refs = find_method_references("foo", <<~RUBY)
|
96
|
+
class Bar
|
97
|
+
def foo
|
98
|
+
end
|
99
|
+
|
100
|
+
def foo=(value)
|
101
|
+
end
|
102
|
+
|
103
|
+
def baz
|
104
|
+
self.foo = 1
|
105
|
+
self.foo
|
106
|
+
end
|
107
|
+
end
|
108
|
+
RUBY
|
109
|
+
|
110
|
+
# We want to match `foo` but not `foo=`
|
111
|
+
assert_equal(2, refs.size)
|
112
|
+
|
113
|
+
assert_equal("foo", refs[0].name)
|
114
|
+
assert_equal(2, refs[0].location.start_line)
|
115
|
+
|
116
|
+
assert_equal("foo", refs[1].name)
|
117
|
+
assert_equal(10, refs[1].location.start_line)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_matches_writers
|
121
|
+
refs = find_method_references("foo=", <<~RUBY)
|
122
|
+
class Bar
|
123
|
+
def foo
|
124
|
+
end
|
125
|
+
|
126
|
+
def foo=(value)
|
127
|
+
end
|
128
|
+
|
129
|
+
def baz
|
130
|
+
self.foo = 1
|
131
|
+
self.foo
|
132
|
+
end
|
133
|
+
end
|
134
|
+
RUBY
|
135
|
+
|
136
|
+
# We want to match `foo=` but not `foo`
|
137
|
+
assert_equal(2, refs.size)
|
138
|
+
|
139
|
+
assert_equal("foo=", refs[0].name)
|
140
|
+
assert_equal(5, refs[0].location.start_line)
|
141
|
+
|
142
|
+
assert_equal("foo=", refs[1].name)
|
143
|
+
assert_equal(9, refs[1].location.start_line)
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_find_inherited_methods
|
147
|
+
refs = find_method_references("foo", <<~RUBY)
|
148
|
+
class Bar
|
149
|
+
def foo
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Baz < Bar
|
154
|
+
super.foo
|
155
|
+
end
|
156
|
+
RUBY
|
157
|
+
|
158
|
+
assert_equal(2, refs.size)
|
159
|
+
|
160
|
+
assert_equal("foo", refs[0].name)
|
161
|
+
assert_equal(2, refs[0].location.start_line)
|
162
|
+
|
163
|
+
assert_equal("foo", refs[1].name)
|
164
|
+
assert_equal(7, refs[1].location.start_line)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_finds_methods_created_in_mixins
|
168
|
+
refs = find_method_references("foo", <<~RUBY)
|
169
|
+
module Mixin
|
170
|
+
def foo
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Bar
|
175
|
+
include Mixin
|
176
|
+
end
|
177
|
+
|
178
|
+
Bar.foo
|
179
|
+
RUBY
|
180
|
+
|
181
|
+
assert_equal(2, refs.size)
|
182
|
+
|
183
|
+
assert_equal("foo", refs[0].name)
|
184
|
+
assert_equal(2, refs[0].location.start_line)
|
185
|
+
|
186
|
+
assert_equal("foo", refs[1].name)
|
187
|
+
assert_equal(10, refs[1].location.start_line)
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_finds_singleton_methods
|
191
|
+
# The current implementation matches on both `Bar.foo` and `Bar#foo` even though they are different
|
192
|
+
|
193
|
+
refs = find_method_references("foo", <<~RUBY)
|
194
|
+
class Bar
|
195
|
+
class << self
|
196
|
+
def foo
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def foo
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
Bar.foo
|
205
|
+
RUBY
|
206
|
+
|
207
|
+
assert_equal(3, refs.size)
|
208
|
+
|
209
|
+
assert_equal("foo", refs[0].name)
|
210
|
+
assert_equal(3, refs[0].location.start_line)
|
211
|
+
|
212
|
+
assert_equal("foo", refs[1].name)
|
213
|
+
assert_equal(7, refs[1].location.start_line)
|
214
|
+
|
215
|
+
assert_equal("foo", refs[2].name)
|
216
|
+
assert_equal(11, refs[2].location.start_line)
|
217
|
+
end
|
218
|
+
|
73
219
|
private
|
74
220
|
|
75
|
-
def
|
221
|
+
def find_const_references(const_name, source)
|
222
|
+
target = ReferenceFinder::ConstTarget.new(const_name)
|
223
|
+
find_references(target, source)
|
224
|
+
end
|
225
|
+
|
226
|
+
def find_method_references(method_name, source)
|
227
|
+
target = ReferenceFinder::MethodTarget.new(method_name)
|
228
|
+
find_references(target, source)
|
229
|
+
end
|
230
|
+
|
231
|
+
def find_references(target, source)
|
76
232
|
file_path = "/fake.rb"
|
77
233
|
index = Index.new
|
78
234
|
index.index_single(IndexablePath.new(nil, file_path), source)
|
79
235
|
parse_result = Prism.parse(source)
|
80
236
|
dispatcher = Prism::Dispatcher.new
|
81
|
-
finder = ReferenceFinder.new(
|
237
|
+
finder = ReferenceFinder.new(target, index, dispatcher)
|
82
238
|
dispatcher.visit(parse_result.value)
|
83
|
-
finder.references
|
239
|
+
finder.references
|
84
240
|
end
|
85
241
|
end
|
86
242
|
end
|
@@ -50,7 +50,12 @@ module RubyLsp
|
|
50
50
|
).returns(NodeContext)
|
51
51
|
end
|
52
52
|
def locate_node(position, node_types: [])
|
53
|
-
RubyDocument.locate(
|
53
|
+
RubyDocument.locate(
|
54
|
+
@parse_result.value,
|
55
|
+
create_scanner.find_char_position(position),
|
56
|
+
node_types: node_types,
|
57
|
+
encoding: @encoding,
|
58
|
+
)
|
54
59
|
end
|
55
60
|
|
56
61
|
sig { params(char_position: Integer).returns(T.nilable(T::Boolean)) }
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -75,10 +75,12 @@ require "ruby_lsp/requests/hover"
|
|
75
75
|
require "ruby_lsp/requests/inlay_hints"
|
76
76
|
require "ruby_lsp/requests/on_type_formatting"
|
77
77
|
require "ruby_lsp/requests/prepare_type_hierarchy"
|
78
|
+
require "ruby_lsp/requests/range_formatting"
|
79
|
+
require "ruby_lsp/requests/references"
|
80
|
+
require "ruby_lsp/requests/rename"
|
78
81
|
require "ruby_lsp/requests/selection_ranges"
|
79
82
|
require "ruby_lsp/requests/semantic_highlighting"
|
80
83
|
require "ruby_lsp/requests/show_syntax_tree"
|
81
84
|
require "ruby_lsp/requests/signature_help"
|
82
85
|
require "ruby_lsp/requests/type_hierarchy_supertypes"
|
83
86
|
require "ruby_lsp/requests/workspace_symbol"
|
84
|
-
require "ruby_lsp/requests/rename"
|
@@ -23,10 +23,11 @@ module RubyLsp
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
sig { params(document: RubyDocument, code_action: T::Hash[Symbol, T.untyped]).void }
|
27
|
-
def initialize(document, code_action)
|
26
|
+
sig { params(document: RubyDocument, global_state: GlobalState, code_action: T::Hash[Symbol, T.untyped]).void }
|
27
|
+
def initialize(document, global_state, code_action)
|
28
28
|
super()
|
29
29
|
@document = document
|
30
|
+
@global_state = global_state
|
30
31
|
@code_action = code_action
|
31
32
|
end
|
32
33
|
|
@@ -191,7 +192,12 @@ module RubyLsp
|
|
191
192
|
extracted_source = T.must(@document.source[start_index...end_index])
|
192
193
|
|
193
194
|
# Find the closest method declaration node, so that we place the refactor in a valid position
|
194
|
-
node_context = RubyDocument.locate(
|
195
|
+
node_context = RubyDocument.locate(
|
196
|
+
@document.parse_result.value,
|
197
|
+
start_index,
|
198
|
+
node_types: [Prism::DefNode],
|
199
|
+
encoding: @global_state.encoding,
|
200
|
+
)
|
195
201
|
closest_node = node_context.node
|
196
202
|
return Error::InvalidTargetRange unless closest_node
|
197
203
|
|
@@ -28,7 +28,7 @@ module RubyLsp
|
|
28
28
|
char_position = document.create_scanner.find_char_position(position)
|
29
29
|
delegate_request_if_needed!(global_state, document, char_position)
|
30
30
|
|
31
|
-
node_context = RubyDocument.locate(document.parse_result.value, char_position)
|
31
|
+
node_context = RubyDocument.locate(document.parse_result.value, char_position, encoding: global_state.encoding)
|
32
32
|
|
33
33
|
@response_builder = T.let(
|
34
34
|
ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# The [range formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting)
|
7
|
+
# is used to format a selection or to format on paste.
|
8
|
+
class RangeFormatting < Request
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(global_state: GlobalState, document: RubyDocument, params: T::Hash[Symbol, T.untyped]).void }
|
12
|
+
def initialize(global_state, document, params)
|
13
|
+
super()
|
14
|
+
@document = document
|
15
|
+
@uri = T.let(document.uri, URI::Generic)
|
16
|
+
@params = params
|
17
|
+
@active_formatter = T.let(global_state.active_formatter, T.nilable(Support::Formatter))
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.returns(T.nilable(T::Array[Interface::TextEdit])) }
|
21
|
+
def perform
|
22
|
+
return unless @active_formatter
|
23
|
+
return if @document.syntax_error?
|
24
|
+
|
25
|
+
target = @document.locate_first_within_range(@params[:range])
|
26
|
+
return unless target
|
27
|
+
|
28
|
+
location = target.location
|
29
|
+
|
30
|
+
formatted_text = @active_formatter.run_range_formatting(
|
31
|
+
@uri,
|
32
|
+
target.slice,
|
33
|
+
location.start_column / 2,
|
34
|
+
)
|
35
|
+
return unless formatted_text
|
36
|
+
|
37
|
+
[
|
38
|
+
Interface::TextEdit.new(
|
39
|
+
range: Interface::Range.new(
|
40
|
+
start: Interface::Position.new(
|
41
|
+
line: location.start_line - 1,
|
42
|
+
character: location.start_code_units_column(@document.encoding),
|
43
|
+
),
|
44
|
+
end: Interface::Position.new(
|
45
|
+
line: location.end_line - 1,
|
46
|
+
character: location.end_code_units_column(@document.encoding),
|
47
|
+
),
|
48
|
+
),
|
49
|
+
new_text: formatted_text.strip,
|
50
|
+
),
|
51
|
+
]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# The
|
7
|
+
# [references](https://microsoft.github.io/language-server-protocol/specification#textDocument_references)
|
8
|
+
# request finds all references for the selected symbol.
|
9
|
+
class References < Request
|
10
|
+
extend T::Sig
|
11
|
+
include Support::Common
|
12
|
+
|
13
|
+
sig do
|
14
|
+
params(
|
15
|
+
global_state: GlobalState,
|
16
|
+
store: Store,
|
17
|
+
document: T.any(RubyDocument, ERBDocument),
|
18
|
+
params: T::Hash[Symbol, T.untyped],
|
19
|
+
).void
|
20
|
+
end
|
21
|
+
def initialize(global_state, store, document, params)
|
22
|
+
super()
|
23
|
+
@global_state = global_state
|
24
|
+
@store = store
|
25
|
+
@document = document
|
26
|
+
@params = params
|
27
|
+
@locations = T.let([], T::Array[Interface::Location])
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { override.returns(T::Array[Interface::Location]) }
|
31
|
+
def perform
|
32
|
+
position = @params[:position]
|
33
|
+
char_position = @document.create_scanner.find_char_position(position)
|
34
|
+
|
35
|
+
node_context = RubyDocument.locate(
|
36
|
+
@document.parse_result.value,
|
37
|
+
char_position,
|
38
|
+
node_types: [
|
39
|
+
Prism::ConstantReadNode,
|
40
|
+
Prism::ConstantPathNode,
|
41
|
+
Prism::ConstantPathTargetNode,
|
42
|
+
Prism::CallNode,
|
43
|
+
Prism::DefNode,
|
44
|
+
],
|
45
|
+
encoding: @global_state.encoding,
|
46
|
+
)
|
47
|
+
target = node_context.node
|
48
|
+
parent = node_context.parent
|
49
|
+
return @locations if !target || target.is_a?(Prism::ProgramNode)
|
50
|
+
|
51
|
+
if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
|
52
|
+
target = determine_target(
|
53
|
+
target,
|
54
|
+
parent,
|
55
|
+
position,
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
target = T.cast(
|
60
|
+
target,
|
61
|
+
T.any(
|
62
|
+
Prism::ConstantReadNode,
|
63
|
+
Prism::ConstantPathNode,
|
64
|
+
Prism::ConstantPathTargetNode,
|
65
|
+
Prism::CallNode,
|
66
|
+
Prism::DefNode,
|
67
|
+
),
|
68
|
+
)
|
69
|
+
|
70
|
+
reference_target = create_reference_target(target, node_context)
|
71
|
+
return @locations unless reference_target
|
72
|
+
|
73
|
+
Dir.glob(File.join(@global_state.workspace_path, "**/*.rb")).each do |path|
|
74
|
+
uri = URI::Generic.from_path(path: path)
|
75
|
+
# If the document is being managed by the client, then we should use whatever is present in the store instead
|
76
|
+
# of reading from disk
|
77
|
+
next if @store.key?(uri)
|
78
|
+
|
79
|
+
parse_result = Prism.parse_file(path)
|
80
|
+
collect_references(reference_target, parse_result, uri)
|
81
|
+
end
|
82
|
+
|
83
|
+
@store.each do |_uri, document|
|
84
|
+
collect_references(reference_target, document.parse_result, document.uri)
|
85
|
+
end
|
86
|
+
|
87
|
+
@locations
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
sig do
|
93
|
+
params(
|
94
|
+
target_node: T.any(
|
95
|
+
Prism::ConstantReadNode,
|
96
|
+
Prism::ConstantPathNode,
|
97
|
+
Prism::ConstantPathTargetNode,
|
98
|
+
Prism::CallNode,
|
99
|
+
Prism::DefNode,
|
100
|
+
),
|
101
|
+
node_context: NodeContext,
|
102
|
+
).returns(T.nilable(RubyIndexer::ReferenceFinder::Target))
|
103
|
+
end
|
104
|
+
def create_reference_target(target_node, node_context)
|
105
|
+
case target_node
|
106
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
|
107
|
+
name = constant_name(target_node)
|
108
|
+
return unless name
|
109
|
+
|
110
|
+
entries = @global_state.index.resolve(name, node_context.nesting)
|
111
|
+
return unless entries
|
112
|
+
|
113
|
+
fully_qualified_name = T.must(entries.first).name
|
114
|
+
RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
|
115
|
+
when Prism::CallNode, Prism::DefNode
|
116
|
+
RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
sig do
|
121
|
+
params(
|
122
|
+
target: RubyIndexer::ReferenceFinder::Target,
|
123
|
+
parse_result: Prism::ParseResult,
|
124
|
+
uri: URI::Generic,
|
125
|
+
).void
|
126
|
+
end
|
127
|
+
def collect_references(target, parse_result, uri)
|
128
|
+
dispatcher = Prism::Dispatcher.new
|
129
|
+
finder = RubyIndexer::ReferenceFinder.new(
|
130
|
+
target,
|
131
|
+
@global_state.index,
|
132
|
+
dispatcher,
|
133
|
+
include_declarations: @params.dig(:context, :includeDeclaration) || true,
|
134
|
+
)
|
135
|
+
dispatcher.visit(parse_result.value)
|
136
|
+
|
137
|
+
finder.references.each do |reference|
|
138
|
+
@locations << Interface::Location.new(
|
139
|
+
uri: uri.to_s,
|
140
|
+
range: range_from_location(reference.location),
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -37,6 +37,7 @@ module RubyLsp
|
|
37
37
|
@document.parse_result.value,
|
38
38
|
char_position,
|
39
39
|
node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
|
40
|
+
encoding: @global_state.encoding,
|
40
41
|
)
|
41
42
|
target = node_context.node
|
42
43
|
parent = node_context.parent
|
@@ -66,7 +67,8 @@ module RubyLsp
|
|
66
67
|
end
|
67
68
|
|
68
69
|
fully_qualified_name = T.must(entries.first).name
|
69
|
-
|
70
|
+
reference_target = RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
|
71
|
+
changes = collect_text_edits(reference_target, name)
|
70
72
|
|
71
73
|
# If the client doesn't support resource operations, such as renaming files, then we can only return the basic
|
72
74
|
# text changes
|
@@ -127,8 +129,13 @@ module RubyLsp
|
|
127
129
|
end
|
128
130
|
end
|
129
131
|
|
130
|
-
sig
|
131
|
-
|
132
|
+
sig do
|
133
|
+
params(
|
134
|
+
target: RubyIndexer::ReferenceFinder::Target,
|
135
|
+
name: String,
|
136
|
+
).returns(T::Hash[String, T::Array[Interface::TextEdit]])
|
137
|
+
end
|
138
|
+
def collect_text_edits(target, name)
|
132
139
|
changes = {}
|
133
140
|
|
134
141
|
Dir.glob(File.join(@global_state.workspace_path, "**/*.rb")).each do |path|
|
@@ -138,12 +145,12 @@ module RubyLsp
|
|
138
145
|
next if @store.key?(uri)
|
139
146
|
|
140
147
|
parse_result = Prism.parse_file(path)
|
141
|
-
edits = collect_changes(
|
148
|
+
edits = collect_changes(target, parse_result, name, uri)
|
142
149
|
changes[uri.to_s] = edits unless edits.empty?
|
143
150
|
end
|
144
151
|
|
145
152
|
@store.each do |uri, document|
|
146
|
-
edits = collect_changes(
|
153
|
+
edits = collect_changes(target, document.parse_result, name, document.uri)
|
147
154
|
changes[uri] = edits unless edits.empty?
|
148
155
|
end
|
149
156
|
|
@@ -152,18 +159,18 @@ module RubyLsp
|
|
152
159
|
|
153
160
|
sig do
|
154
161
|
params(
|
155
|
-
|
162
|
+
target: RubyIndexer::ReferenceFinder::Target,
|
156
163
|
parse_result: Prism::ParseResult,
|
157
164
|
name: String,
|
158
165
|
uri: URI::Generic,
|
159
166
|
).returns(T::Array[Interface::TextEdit])
|
160
167
|
end
|
161
|
-
def collect_changes(
|
168
|
+
def collect_changes(target, parse_result, name, uri)
|
162
169
|
dispatcher = Prism::Dispatcher.new
|
163
|
-
finder = RubyIndexer::ReferenceFinder.new(
|
170
|
+
finder = RubyIndexer::ReferenceFinder.new(target, @global_state.index, dispatcher)
|
164
171
|
dispatcher.visit(parse_result.value)
|
165
172
|
|
166
|
-
finder.references.
|
173
|
+
finder.references.map do |reference|
|
167
174
|
adjust_reference_for_edit(name, reference)
|
168
175
|
end
|
169
176
|
end
|
@@ -39,7 +39,12 @@ module RubyLsp
|
|
39
39
|
char_position = document.create_scanner.find_char_position(position)
|
40
40
|
delegate_request_if_needed!(global_state, document, char_position)
|
41
41
|
|
42
|
-
node_context = RubyDocument.locate(
|
42
|
+
node_context = RubyDocument.locate(
|
43
|
+
document.parse_result.value,
|
44
|
+
char_position,
|
45
|
+
node_types: [Prism::CallNode],
|
46
|
+
encoding: global_state.encoding,
|
47
|
+
)
|
43
48
|
|
44
49
|
target = adjust_for_nested_target(node_context.node, node_context.parent, position)
|
45
50
|
|
@@ -13,6 +13,9 @@ module RubyLsp
|
|
13
13
|
sig { abstract.params(uri: URI::Generic, document: RubyDocument).returns(T.nilable(String)) }
|
14
14
|
def run_formatting(uri, document); end
|
15
15
|
|
16
|
+
sig { abstract.params(uri: URI::Generic, source: String, base_indentation: Integer).returns(T.nilable(String)) }
|
17
|
+
def run_range_formatting(uri, source, base_indentation); end
|
18
|
+
|
16
19
|
sig do
|
17
20
|
abstract.params(
|
18
21
|
uri: URI::Generic,
|
@@ -28,6 +28,12 @@ module RubyLsp
|
|
28
28
|
@format_runner.formatted_source
|
29
29
|
end
|
30
30
|
|
31
|
+
# RuboCop does not support range formatting
|
32
|
+
sig { override.params(uri: URI::Generic, source: String, base_indentation: Integer).returns(T.nilable(String)) }
|
33
|
+
def run_range_formatting(uri, source, base_indentation)
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
31
37
|
sig do
|
32
38
|
override.params(
|
33
39
|
uri: URI::Generic,
|
@@ -37,6 +37,14 @@ module RubyLsp
|
|
37
37
|
SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
|
38
38
|
end
|
39
39
|
|
40
|
+
sig { override.params(uri: URI::Generic, source: String, base_indentation: Integer).returns(T.nilable(String)) }
|
41
|
+
def run_range_formatting(uri, source, base_indentation)
|
42
|
+
path = uri.to_standardized_path
|
43
|
+
return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
|
44
|
+
|
45
|
+
SyntaxTree.format(source, @options.print_width, base_indentation, options: @options.formatter_options)
|
46
|
+
end
|
47
|
+
|
40
48
|
sig do
|
41
49
|
override.params(
|
42
50
|
uri: URI::Generic,
|
@@ -26,9 +26,10 @@ module RubyLsp
|
|
26
26
|
node: Prism::Node,
|
27
27
|
char_position: Integer,
|
28
28
|
node_types: T::Array[T.class_of(Prism::Node)],
|
29
|
+
encoding: Encoding,
|
29
30
|
).returns(NodeContext)
|
30
31
|
end
|
31
|
-
def locate(node, char_position, node_types: [])
|
32
|
+
def locate(node, char_position, node_types: [], encoding: Encoding::UTF_8)
|
32
33
|
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
|
33
34
|
closest = node
|
34
35
|
parent = T.let(nil, T.nilable(Prism::Node))
|
@@ -61,16 +62,21 @@ module RubyLsp
|
|
61
62
|
|
62
63
|
# Skip if the current node doesn't cover the desired position
|
63
64
|
loc = candidate.location
|
64
|
-
|
65
|
+
loc_start_offset = loc.start_code_units_offset(encoding)
|
66
|
+
loc_end_offset = loc.end_code_units_offset(encoding)
|
67
|
+
next unless (loc_start_offset...loc_end_offset).cover?(char_position)
|
65
68
|
|
66
69
|
# If the node's start character is already past the position, then we should've found the closest node
|
67
70
|
# already
|
68
|
-
break if char_position <
|
71
|
+
break if char_position < loc_start_offset
|
69
72
|
|
70
73
|
# If the candidate starts after the end of the previous nesting level, then we've exited that nesting level
|
71
74
|
# and need to pop the stack
|
72
75
|
previous_level = nesting_nodes.last
|
73
|
-
|
76
|
+
if previous_level &&
|
77
|
+
(loc_start_offset > previous_level.location.end_code_units_offset(encoding))
|
78
|
+
nesting_nodes.pop
|
79
|
+
end
|
74
80
|
|
75
81
|
# Keep track of the nesting where we found the target. This is used to determine the fully qualified name of
|
76
82
|
# the target when it is a constant
|
@@ -83,8 +89,10 @@ module RubyLsp
|
|
83
89
|
if candidate.is_a?(Prism::CallNode)
|
84
90
|
arg_loc = candidate.arguments&.location
|
85
91
|
blk_loc = candidate.block&.location
|
86
|
-
if (arg_loc && (arg_loc.
|
87
|
-
|
92
|
+
if (arg_loc && (arg_loc.start_code_units_offset(encoding)...
|
93
|
+
arg_loc.end_code_units_offset(encoding)).cover?(char_position)) ||
|
94
|
+
(blk_loc && (blk_loc.start_code_units_offset(encoding)...
|
95
|
+
blk_loc.end_code_units_offset(encoding)).cover?(char_position))
|
88
96
|
call_node = candidate
|
89
97
|
end
|
90
98
|
end
|
@@ -94,7 +102,9 @@ module RubyLsp
|
|
94
102
|
|
95
103
|
# If the current node is narrower than or equal to the previous closest node, then it is more precise
|
96
104
|
closest_loc = closest.location
|
97
|
-
|
105
|
+
closest_node_start_offset = closest_loc.start_code_units_offset(encoding)
|
106
|
+
closest_node_end_offset = closest_loc.end_code_units_offset(encoding)
|
107
|
+
if loc_end_offset - loc_start_offset <= closest_node_end_offset - closest_node_start_offset
|
98
108
|
parent = closest
|
99
109
|
closest = candidate
|
100
110
|
end
|
@@ -201,7 +211,12 @@ module RubyLsp
|
|
201
211
|
).returns(NodeContext)
|
202
212
|
end
|
203
213
|
def locate_node(position, node_types: [])
|
204
|
-
RubyDocument.locate(
|
214
|
+
RubyDocument.locate(
|
215
|
+
@parse_result.value,
|
216
|
+
create_scanner.find_char_position(position),
|
217
|
+
node_types: node_types,
|
218
|
+
encoding: @encoding,
|
219
|
+
)
|
205
220
|
end
|
206
221
|
end
|
207
222
|
end
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -43,6 +43,8 @@ module RubyLsp
|
|
43
43
|
text_document_semantic_tokens_range(message)
|
44
44
|
when "textDocument/formatting"
|
45
45
|
text_document_formatting(message)
|
46
|
+
when "textDocument/rangeFormatting"
|
47
|
+
text_document_range_formatting(message)
|
46
48
|
when "textDocument/documentHighlight"
|
47
49
|
text_document_document_highlight(message)
|
48
50
|
when "textDocument/onTypeFormatting"
|
@@ -69,6 +71,8 @@ module RubyLsp
|
|
69
71
|
text_document_prepare_type_hierarchy(message)
|
70
72
|
when "textDocument/rename"
|
71
73
|
text_document_rename(message)
|
74
|
+
when "textDocument/references"
|
75
|
+
text_document_references(message)
|
72
76
|
when "typeHierarchy/supertypes"
|
73
77
|
type_hierarchy_supertypes(message)
|
74
78
|
when "typeHierarchy/subtypes"
|
@@ -87,7 +91,16 @@ module RubyLsp
|
|
87
91
|
id: message[:id],
|
88
92
|
response:
|
89
93
|
Addon.addons.map do |addon|
|
90
|
-
|
94
|
+
version_method = addon.method(:version)
|
95
|
+
|
96
|
+
# If the add-on doesn't define a `version` method, we'd be calling the abstract method defined by
|
97
|
+
# Sorbet, which would raise an error.
|
98
|
+
# Therefore, we only call the method if it's defined by the add-on itself
|
99
|
+
if version_method.owner != Addon
|
100
|
+
version = addon.version
|
101
|
+
end
|
102
|
+
|
103
|
+
{ name: addon.name, version: version, errored: addon.error? }
|
91
104
|
end,
|
92
105
|
),
|
93
106
|
)
|
@@ -230,6 +243,8 @@ module RubyLsp
|
|
230
243
|
signature_help_provider: signature_help_provider,
|
231
244
|
type_hierarchy_provider: type_hierarchy_provider,
|
232
245
|
rename_provider: !@global_state.has_type_checker,
|
246
|
+
references_provider: !@global_state.has_type_checker,
|
247
|
+
document_range_formatting_provider: true,
|
233
248
|
experimental: {
|
234
249
|
addon_detection: true,
|
235
250
|
},
|
@@ -513,6 +528,34 @@ module RubyLsp
|
|
513
528
|
send_message(Result.new(id: message[:id], response: request.perform))
|
514
529
|
end
|
515
530
|
|
531
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
532
|
+
def text_document_range_formatting(message)
|
533
|
+
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
534
|
+
if @global_state.formatter == "none"
|
535
|
+
send_empty_response(message[:id])
|
536
|
+
return
|
537
|
+
end
|
538
|
+
|
539
|
+
params = message[:params]
|
540
|
+
uri = params.dig(:textDocument, :uri)
|
541
|
+
# Do not format files outside of the workspace. For example, if someone is looking at a gem's source code, we
|
542
|
+
# don't want to format it
|
543
|
+
path = uri.to_standardized_path
|
544
|
+
unless path.nil? || path.start_with?(@global_state.workspace_path)
|
545
|
+
send_empty_response(message[:id])
|
546
|
+
return
|
547
|
+
end
|
548
|
+
|
549
|
+
document = @store.get(uri)
|
550
|
+
unless document.is_a?(RubyDocument)
|
551
|
+
send_empty_response(message[:id])
|
552
|
+
return
|
553
|
+
end
|
554
|
+
|
555
|
+
response = Requests::RangeFormatting.new(@global_state, document, params).perform
|
556
|
+
send_message(Result.new(id: message[:id], response: response))
|
557
|
+
end
|
558
|
+
|
516
559
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
517
560
|
def text_document_formatting(message)
|
518
561
|
# If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
|
@@ -636,6 +679,24 @@ module RubyLsp
|
|
636
679
|
send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
|
637
680
|
end
|
638
681
|
|
682
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
683
|
+
def text_document_references(message)
|
684
|
+
params = message[:params]
|
685
|
+
document = @store.get(params.dig(:textDocument, :uri))
|
686
|
+
|
687
|
+
unless document.is_a?(RubyDocument)
|
688
|
+
send_empty_response(message[:id])
|
689
|
+
return
|
690
|
+
end
|
691
|
+
|
692
|
+
send_message(
|
693
|
+
Result.new(
|
694
|
+
id: message[:id],
|
695
|
+
response: Requests::References.new(@global_state, @store, document, params).perform,
|
696
|
+
),
|
697
|
+
)
|
698
|
+
end
|
699
|
+
|
639
700
|
sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
|
640
701
|
def sorbet_level(document)
|
641
702
|
return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
|
@@ -711,7 +772,7 @@ module RubyLsp
|
|
711
772
|
return
|
712
773
|
end
|
713
774
|
|
714
|
-
result = Requests::CodeActionResolve.new(document, params).perform
|
775
|
+
result = Requests::CodeActionResolve.new(document, @global_state, params).perform
|
715
776
|
|
716
777
|
case result
|
717
778
|
when Requests::CodeActionResolve::Error::EmptySelection
|
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.19.
|
4
|
+
version: 0.19.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -154,6 +154,8 @@ files:
|
|
154
154
|
- lib/ruby_lsp/requests/inlay_hints.rb
|
155
155
|
- lib/ruby_lsp/requests/on_type_formatting.rb
|
156
156
|
- lib/ruby_lsp/requests/prepare_type_hierarchy.rb
|
157
|
+
- lib/ruby_lsp/requests/range_formatting.rb
|
158
|
+
- lib/ruby_lsp/requests/references.rb
|
157
159
|
- lib/ruby_lsp/requests/rename.rb
|
158
160
|
- lib/ruby_lsp/requests/request.rb
|
159
161
|
- lib/ruby_lsp/requests/selection_ranges.rb
|