ruby-lsp 0.17.17 → 0.18.3
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/README.md +4 -110
- data/VERSION +1 -1
- data/exe/ruby-lsp +5 -11
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +14 -6
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +162 -27
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +110 -8
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -2
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +24 -10
- data/lib/ruby_indexer/test/constant_test.rb +4 -4
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
- data/lib/ruby_indexer/test/index_test.rb +68 -0
- data/lib/ruby_indexer/test/method_test.rb +257 -2
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/base_server.rb +21 -1
- data/lib/ruby_lsp/document.rb +5 -3
- data/lib/ruby_lsp/erb_document.rb +29 -10
- data/lib/ruby_lsp/global_state.rb +4 -3
- data/lib/ruby_lsp/internal.rb +40 -2
- data/lib/ruby_lsp/listeners/code_lens.rb +34 -5
- data/lib/ruby_lsp/listeners/completion.rb +20 -6
- data/lib/ruby_lsp/listeners/inlay_hints.rb +1 -16
- data/lib/ruby_lsp/listeners/signature_help.rb +55 -24
- data/lib/ruby_lsp/rbs_document.rb +5 -4
- data/lib/ruby_lsp/requests/code_action_resolve.rb +0 -15
- data/lib/ruby_lsp/requests/code_actions.rb +0 -10
- data/lib/ruby_lsp/requests/code_lens.rb +1 -11
- data/lib/ruby_lsp/requests/completion.rb +3 -20
- data/lib/ruby_lsp/requests/completion_resolve.rb +2 -10
- data/lib/ruby_lsp/requests/definition.rb +6 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -10
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -14
- data/lib/ruby_lsp/requests/document_link.rb +0 -10
- data/lib/ruby_lsp/requests/document_symbol.rb +0 -17
- data/lib/ruby_lsp/requests/folding_ranges.rb +0 -10
- data/lib/ruby_lsp/requests/formatting.rb +0 -16
- data/lib/ruby_lsp/requests/hover.rb +9 -9
- data/lib/ruby_lsp/requests/inlay_hints.rb +2 -35
- data/lib/ruby_lsp/requests/on_type_formatting.rb +0 -10
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +0 -11
- data/lib/ruby_lsp/requests/request.rb +17 -1
- data/lib/ruby_lsp/requests/selection_ranges.rb +0 -10
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -23
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +0 -11
- data/lib/ruby_lsp/requests/signature_help.rb +5 -20
- data/lib/ruby_lsp/requests/support/common.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -0
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +2 -0
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +0 -11
- data/lib/ruby_lsp/requests/workspace_symbol.rb +0 -12
- data/lib/ruby_lsp/ruby_document.rb +4 -3
- data/lib/ruby_lsp/server.rb +45 -11
- data/lib/ruby_lsp/setup_bundler.rb +33 -15
- data/lib/ruby_lsp/type_inferrer.rb +8 -10
- data/lib/ruby_lsp/utils.rb +11 -1
- metadata +3 -6
- data/lib/ruby_lsp/check_docs.rb +0 -130
- data/lib/ruby_lsp/requests.rb +0 -64
- data/lib/ruby_lsp/response_builders.rb +0 -13
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -31,6 +31,7 @@ module RubyLsp
|
|
31
31
|
Thread,
|
32
32
|
)
|
33
33
|
|
34
|
+
@global_state = T.let(GlobalState.new, GlobalState)
|
34
35
|
Thread.main.priority = 1
|
35
36
|
end
|
36
37
|
|
@@ -52,7 +53,26 @@ module RubyLsp
|
|
52
53
|
message[:params][:textDocument][:uri] = parsed_uri
|
53
54
|
|
54
55
|
# We don't want to try to parse documents on text synchronization notifications
|
55
|
-
|
56
|
+
unless method.start_with?("textDocument/did")
|
57
|
+
document = @store.get(parsed_uri)
|
58
|
+
|
59
|
+
# If the client supports request delegation and we're working with an ERB document and there was
|
60
|
+
# something to parse, then we have to maintain the client updated about the virtual state of the host
|
61
|
+
# language source
|
62
|
+
if document.parse! && @global_state.supports_request_delegation && document.is_a?(ERBDocument)
|
63
|
+
send_message(
|
64
|
+
Notification.new(
|
65
|
+
method: "delegate/textDocument/virtualState",
|
66
|
+
params: {
|
67
|
+
textDocument: {
|
68
|
+
uri: uri,
|
69
|
+
text: document.host_language_source,
|
70
|
+
},
|
71
|
+
},
|
72
|
+
),
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
56
76
|
rescue Store::NonExistingDocumentError
|
57
77
|
# If we receive a request for a file that no longer exists, we don't want to fail
|
58
78
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -51,7 +51,8 @@ module RubyLsp
|
|
51
51
|
@version = T.let(version, Integer)
|
52
52
|
@uri = T.let(uri, URI::Generic)
|
53
53
|
@needs_parsing = T.let(true, T::Boolean)
|
54
|
-
@parse_result = T.let(
|
54
|
+
@parse_result = T.let(T.unsafe(nil), ParseResultType)
|
55
|
+
parse!
|
55
56
|
end
|
56
57
|
|
57
58
|
sig { params(other: Document[T.untyped]).returns(T::Boolean) }
|
@@ -106,8 +107,9 @@ module RubyLsp
|
|
106
107
|
@cache.clear
|
107
108
|
end
|
108
109
|
|
109
|
-
|
110
|
-
|
110
|
+
# Returns `true` if the document was parsed and `false` if nothing needed parsing
|
111
|
+
sig { abstract.returns(T::Boolean) }
|
112
|
+
def parse!; end
|
111
113
|
|
112
114
|
sig { abstract.returns(T::Boolean) }
|
113
115
|
def syntax_error?; end
|
@@ -6,17 +6,30 @@ module RubyLsp
|
|
6
6
|
extend T::Sig
|
7
7
|
extend T::Generic
|
8
8
|
|
9
|
+
sig { returns(String) }
|
10
|
+
attr_reader :host_language_source
|
11
|
+
|
9
12
|
ParseResultType = type_member { { fixed: Prism::ParseResult } }
|
10
13
|
|
11
|
-
sig {
|
12
|
-
def
|
13
|
-
|
14
|
+
sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
|
15
|
+
def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
|
16
|
+
# This has to be initialized before calling super because we call `parse` in the parent constructor, which
|
17
|
+
# overrides this with the proper virtual host language source
|
18
|
+
@host_language_source = T.let("", String)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { override.returns(T::Boolean) }
|
23
|
+
def parse!
|
24
|
+
return false unless @needs_parsing
|
14
25
|
|
15
26
|
@needs_parsing = false
|
16
27
|
scanner = ERBScanner.new(@source)
|
17
28
|
scanner.scan
|
29
|
+
@host_language_source = scanner.host_language
|
18
30
|
# assigning empty scopes to turn Prism into eval mode
|
19
31
|
@parse_result = Prism.parse(scanner.ruby, scopes: [[]])
|
32
|
+
true
|
20
33
|
end
|
21
34
|
|
22
35
|
sig { override.returns(T::Boolean) }
|
@@ -39,16 +52,22 @@ module RubyLsp
|
|
39
52
|
RubyDocument.locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
|
40
53
|
end
|
41
54
|
|
55
|
+
sig { params(char_position: Integer).returns(T.nilable(T::Boolean)) }
|
56
|
+
def inside_host_language?(char_position)
|
57
|
+
char = @host_language_source[char_position]
|
58
|
+
char && char != " "
|
59
|
+
end
|
60
|
+
|
42
61
|
class ERBScanner
|
43
62
|
extend T::Sig
|
44
63
|
|
45
64
|
sig { returns(String) }
|
46
|
-
attr_reader :ruby, :
|
65
|
+
attr_reader :ruby, :host_language
|
47
66
|
|
48
67
|
sig { params(source: String).void }
|
49
68
|
def initialize(source)
|
50
69
|
@source = source
|
51
|
-
@
|
70
|
+
@host_language = T.let(+"", String)
|
52
71
|
@ruby = T.let(+"", String)
|
53
72
|
@current_pos = T.let(0, Integer)
|
54
73
|
@inside_ruby = T.let(false, T::Boolean)
|
@@ -104,16 +123,16 @@ module RubyLsp
|
|
104
123
|
end
|
105
124
|
when "\r"
|
106
125
|
@ruby << char
|
107
|
-
@
|
126
|
+
@host_language << char
|
108
127
|
|
109
128
|
if next_char == "\n"
|
110
129
|
@ruby << next_char
|
111
|
-
@
|
130
|
+
@host_language << next_char
|
112
131
|
@current_pos += 1
|
113
132
|
end
|
114
133
|
when "\n"
|
115
134
|
@ruby << char
|
116
|
-
@
|
135
|
+
@host_language << char
|
117
136
|
else
|
118
137
|
push_char(T.must(char))
|
119
138
|
end
|
@@ -123,10 +142,10 @@ module RubyLsp
|
|
123
142
|
def push_char(char)
|
124
143
|
if @inside_ruby
|
125
144
|
@ruby << char
|
126
|
-
@
|
145
|
+
@host_language << " " * char.length
|
127
146
|
else
|
128
147
|
@ruby << " " * char.length
|
129
|
-
@
|
148
|
+
@host_language << char
|
130
149
|
end
|
131
150
|
end
|
132
151
|
|
@@ -21,7 +21,7 @@ module RubyLsp
|
|
21
21
|
attr_reader :encoding
|
22
22
|
|
23
23
|
sig { returns(T::Boolean) }
|
24
|
-
attr_reader :supports_watching_files, :experimental_features
|
24
|
+
attr_reader :supports_watching_files, :experimental_features, :supports_request_delegation
|
25
25
|
|
26
26
|
sig { returns(TypeInferrer) }
|
27
27
|
attr_reader :type_inferrer
|
@@ -39,8 +39,9 @@ module RubyLsp
|
|
39
39
|
@supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
|
40
40
|
@supports_watching_files = T.let(false, T::Boolean)
|
41
41
|
@experimental_features = T.let(false, T::Boolean)
|
42
|
-
@type_inferrer = T.let(TypeInferrer.new(@index
|
42
|
+
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
43
43
|
@addon_settings = T.let({}, T::Hash[String, T.untyped])
|
44
|
+
@supports_request_delegation = T.let(false, T::Boolean)
|
44
45
|
end
|
45
46
|
|
46
47
|
sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
@@ -123,7 +124,6 @@ module RubyLsp
|
|
123
124
|
end
|
124
125
|
|
125
126
|
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
126
|
-
@type_inferrer.experimental_features = @experimental_features
|
127
127
|
|
128
128
|
addon_settings = options.dig(:initializationOptions, :addonSettings)
|
129
129
|
if addon_settings
|
@@ -131,6 +131,7 @@ module RubyLsp
|
|
131
131
|
@addon_settings.merge!(addon_settings)
|
132
132
|
end
|
133
133
|
|
134
|
+
@supports_request_delegation = options.dig(:capabilities, :experimental, :requestDelegation) || false
|
134
135
|
notifications
|
135
136
|
end
|
136
137
|
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -30,8 +30,6 @@ require "ruby_lsp/parameter_scope"
|
|
30
30
|
require "ruby_lsp/global_state"
|
31
31
|
require "ruby_lsp/server"
|
32
32
|
require "ruby_lsp/type_inferrer"
|
33
|
-
require "ruby_lsp/requests"
|
34
|
-
require "ruby_lsp/response_builders"
|
35
33
|
require "ruby_lsp/node_context"
|
36
34
|
require "ruby_lsp/document"
|
37
35
|
require "ruby_lsp/ruby_document"
|
@@ -39,6 +37,46 @@ require "ruby_lsp/erb_document"
|
|
39
37
|
require "ruby_lsp/rbs_document"
|
40
38
|
require "ruby_lsp/store"
|
41
39
|
require "ruby_lsp/addon"
|
40
|
+
|
41
|
+
# Response builders
|
42
|
+
require "ruby_lsp/response_builders/response_builder"
|
43
|
+
require "ruby_lsp/response_builders/collection_response_builder"
|
44
|
+
require "ruby_lsp/response_builders/document_symbol"
|
45
|
+
require "ruby_lsp/response_builders/hover"
|
46
|
+
require "ruby_lsp/response_builders/semantic_highlighting"
|
47
|
+
require "ruby_lsp/response_builders/signature_help"
|
48
|
+
|
49
|
+
# Request support
|
50
|
+
require "ruby_lsp/requests/support/selection_range"
|
51
|
+
require "ruby_lsp/requests/support/annotation"
|
52
|
+
require "ruby_lsp/requests/support/sorbet"
|
53
|
+
require "ruby_lsp/requests/support/common"
|
54
|
+
require "ruby_lsp/requests/support/formatter"
|
42
55
|
require "ruby_lsp/requests/support/rubocop_runner"
|
43
56
|
require "ruby_lsp/requests/support/rubocop_formatter"
|
44
57
|
require "ruby_lsp/requests/support/syntax_tree_formatter"
|
58
|
+
|
59
|
+
# Requests
|
60
|
+
require "ruby_lsp/requests/request"
|
61
|
+
require "ruby_lsp/requests/code_action_resolve"
|
62
|
+
require "ruby_lsp/requests/code_actions"
|
63
|
+
require "ruby_lsp/requests/code_lens"
|
64
|
+
require "ruby_lsp/requests/completion_resolve"
|
65
|
+
require "ruby_lsp/requests/completion"
|
66
|
+
require "ruby_lsp/requests/definition"
|
67
|
+
require "ruby_lsp/requests/diagnostics"
|
68
|
+
require "ruby_lsp/requests/document_highlight"
|
69
|
+
require "ruby_lsp/requests/document_link"
|
70
|
+
require "ruby_lsp/requests/document_symbol"
|
71
|
+
require "ruby_lsp/requests/folding_ranges"
|
72
|
+
require "ruby_lsp/requests/formatting"
|
73
|
+
require "ruby_lsp/requests/hover"
|
74
|
+
require "ruby_lsp/requests/inlay_hints"
|
75
|
+
require "ruby_lsp/requests/on_type_formatting"
|
76
|
+
require "ruby_lsp/requests/prepare_type_hierarchy"
|
77
|
+
require "ruby_lsp/requests/selection_ranges"
|
78
|
+
require "ruby_lsp/requests/semantic_highlighting"
|
79
|
+
require "ruby_lsp/requests/show_syntax_tree"
|
80
|
+
require "ruby_lsp/requests/signature_help"
|
81
|
+
require "ruby_lsp/requests/type_hierarchy_supertypes"
|
82
|
+
require "ruby_lsp/requests/workspace_symbol"
|
@@ -44,6 +44,7 @@ module RubyLsp
|
|
44
44
|
@group_id_stack = T.let([], T::Array[Integer])
|
45
45
|
# We want to avoid adding code lenses for nested definitions
|
46
46
|
@def_depth = T.let(0, Integer)
|
47
|
+
@spec_id = T.let(0, Integer)
|
47
48
|
|
48
49
|
dispatcher.register(
|
49
50
|
self,
|
@@ -70,6 +71,7 @@ module RubyLsp
|
|
70
71
|
name: class_name,
|
71
72
|
command: generate_test_command(group_stack: @group_stack),
|
72
73
|
kind: :group,
|
74
|
+
id: generate_fully_qualified_id(group_stack: @group_stack),
|
73
75
|
)
|
74
76
|
|
75
77
|
@group_id_stack.push(@group_id)
|
@@ -106,6 +108,7 @@ module RubyLsp
|
|
106
108
|
name: method_name,
|
107
109
|
command: generate_test_command(method_name: method_name, group_stack: @group_stack),
|
108
110
|
kind: :example,
|
111
|
+
id: generate_fully_qualified_id(group_stack: @group_stack, method_name: method_name),
|
109
112
|
)
|
110
113
|
end
|
111
114
|
end
|
@@ -166,19 +169,20 @@ module RubyLsp
|
|
166
169
|
@visibility_stack.push([prev_visibility, prev_visibility])
|
167
170
|
if node.name == DESCRIBE_KEYWORD
|
168
171
|
@group_id_stack.pop
|
172
|
+
@group_stack.pop
|
169
173
|
end
|
170
174
|
end
|
171
175
|
|
172
176
|
private
|
173
177
|
|
174
|
-
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
|
175
|
-
def add_test_code_lens(node, name:, command:, kind:)
|
178
|
+
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol, id: String).void }
|
179
|
+
def add_test_code_lens(node, name:, command:, kind:, id: name)
|
176
180
|
# don't add code lenses if the test library is not supported or unknown
|
177
181
|
return unless SUPPORTED_TEST_LIBRARIES.include?(@global_state.test_library) && @path
|
178
182
|
|
179
183
|
arguments = [
|
180
184
|
@path,
|
181
|
-
|
185
|
+
id,
|
182
186
|
command,
|
183
187
|
{
|
184
188
|
start_line: node.location.start_line - 1,
|
@@ -186,6 +190,7 @@ module RubyLsp
|
|
186
190
|
end_line: node.location.end_line - 1,
|
187
191
|
end_column: node.location.end_column,
|
188
192
|
},
|
193
|
+
name,
|
189
194
|
]
|
190
195
|
|
191
196
|
grouping_data = { group_id: @group_id_stack.last, kind: kind }
|
@@ -247,7 +252,7 @@ module RubyLsp
|
|
247
252
|
# We know the entire path, do an exact match
|
248
253
|
" --name " + Shellwords.escape(group_stack.join("::")) + "#" + Shellwords.escape(method_name)
|
249
254
|
elsif spec_name
|
250
|
-
" --name " + "
|
255
|
+
" --name " + "\"/^#{Shellwords.escape(group_stack.join("::"))}##{Shellwords.escape(spec_name)}$/\""
|
251
256
|
else
|
252
257
|
# Execute all tests of the selected class and tests in
|
253
258
|
# modules/classes nested inside of that class
|
@@ -282,15 +287,39 @@ module RubyLsp
|
|
282
287
|
|
283
288
|
return unless name
|
284
289
|
|
290
|
+
if kind == :example
|
291
|
+
# Increment spec_id for each example
|
292
|
+
@spec_id += 1
|
293
|
+
else
|
294
|
+
# Reset spec_id when entering a new group
|
295
|
+
@spec_id = 0
|
296
|
+
@group_stack.push(name)
|
297
|
+
end
|
298
|
+
|
285
299
|
if @path
|
300
|
+
method_name = format("test_%04d_%s", @spec_id, name) if kind == :example
|
286
301
|
add_test_code_lens(
|
287
302
|
node,
|
288
303
|
name: name,
|
289
|
-
command: generate_test_command(spec_name:
|
304
|
+
command: generate_test_command(group_stack: @group_stack, spec_name: method_name),
|
290
305
|
kind: kind,
|
306
|
+
id: generate_fully_qualified_id(group_stack: @group_stack, method_name: method_name),
|
291
307
|
)
|
292
308
|
end
|
293
309
|
end
|
310
|
+
|
311
|
+
sig { params(group_stack: T::Array[String], method_name: T.nilable(String)).returns(String) }
|
312
|
+
def generate_fully_qualified_id(group_stack:, method_name: nil)
|
313
|
+
if method_name
|
314
|
+
# For tests, this will be the test class and method name: `Foo::BarTest#test_baz`.
|
315
|
+
# For specs, this will be the nested descriptions and formatted test name: `a::b::c#test_001_foo`.
|
316
|
+
group_stack.join("::") + "#" + method_name
|
317
|
+
else
|
318
|
+
# For tests, this will be the test class: `Foo::BarTest`.
|
319
|
+
# For specs, this will be the nested descriptions: `a::b::c`.
|
320
|
+
group_stack.join("::")
|
321
|
+
end
|
322
|
+
end
|
294
323
|
end
|
295
324
|
end
|
296
325
|
end
|
@@ -104,7 +104,7 @@ module RubyLsp
|
|
104
104
|
name = constant_name(node)
|
105
105
|
return if name.nil?
|
106
106
|
|
107
|
-
candidates = @index.
|
107
|
+
candidates = @index.constant_completion_candidates(name, @node_context.nesting)
|
108
108
|
candidates.each do |entries|
|
109
109
|
complete_name = T.must(entries.first).name
|
110
110
|
@response_builder << build_entry_completion(
|
@@ -124,7 +124,13 @@ module RubyLsp
|
|
124
124
|
# no sigil, Sorbet will still provide completion for constants
|
125
125
|
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
126
126
|
|
127
|
-
name =
|
127
|
+
name = begin
|
128
|
+
node.full_name
|
129
|
+
rescue Prism::ConstantPathNode::MissingNodesInConstantPathError
|
130
|
+
node.slice
|
131
|
+
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError
|
132
|
+
nil
|
133
|
+
end
|
128
134
|
return if name.nil?
|
129
135
|
|
130
136
|
constant_path_completion(name, range_from_location(node.location))
|
@@ -230,7 +236,7 @@ module RubyLsp
|
|
230
236
|
|
231
237
|
real_namespace = @index.follow_aliased_namespace(T.must(namespace_entries.first).name)
|
232
238
|
|
233
|
-
candidates = @index.
|
239
|
+
candidates = @index.constant_completion_candidates(
|
234
240
|
"#{real_namespace}::#{incomplete_name}",
|
235
241
|
top_level_reference ? [] : nesting,
|
236
242
|
)
|
@@ -240,8 +246,16 @@ module RubyLsp
|
|
240
246
|
first_entry = T.must(entries.first)
|
241
247
|
next if first_entry.private? && !first_entry.name.start_with?("#{nesting}::")
|
242
248
|
|
243
|
-
|
244
|
-
full_name = aliased_namespace
|
249
|
+
entry_name = first_entry.name
|
250
|
+
full_name = if aliased_namespace != real_namespace
|
251
|
+
constant_name = entry_name.delete_prefix("#{real_namespace}::")
|
252
|
+
aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
|
253
|
+
elsif !entry_name.start_with?(aliased_namespace)
|
254
|
+
*_, short_name = entry_name.split("::")
|
255
|
+
"#{aliased_namespace}::#{short_name}"
|
256
|
+
else
|
257
|
+
entry_name
|
258
|
+
end
|
245
259
|
|
246
260
|
@response_builder << build_entry_completion(
|
247
261
|
full_name,
|
@@ -545,7 +559,7 @@ module RubyLsp
|
|
545
559
|
sig { params(entry_name: String).returns(T::Boolean) }
|
546
560
|
def top_level?(entry_name)
|
547
561
|
nesting = @node_context.nesting
|
548
|
-
nesting.length.downto(0)
|
562
|
+
nesting.length.downto(0) do |i|
|
549
563
|
prefix = T.must(nesting[0...i]).join("::")
|
550
564
|
full_name = prefix.empty? ? entry_name : "#{prefix}::#{entry_name}"
|
551
565
|
next if full_name == entry_name
|
@@ -12,14 +12,12 @@ module RubyLsp
|
|
12
12
|
sig do
|
13
13
|
params(
|
14
14
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint],
|
15
|
-
range: T::Range[Integer],
|
16
15
|
hints_configuration: RequestConfig,
|
17
16
|
dispatcher: Prism::Dispatcher,
|
18
17
|
).void
|
19
18
|
end
|
20
|
-
def initialize(response_builder,
|
19
|
+
def initialize(response_builder, hints_configuration, dispatcher)
|
21
20
|
@response_builder = response_builder
|
22
|
-
@range = range
|
23
21
|
@hints_configuration = hints_configuration
|
24
22
|
|
25
23
|
dispatcher.register(self, :on_rescue_node_enter, :on_implicit_node_enter)
|
@@ -31,7 +29,6 @@ module RubyLsp
|
|
31
29
|
return unless node.exceptions.empty?
|
32
30
|
|
33
31
|
loc = node.location
|
34
|
-
return unless visible?(node, @range)
|
35
32
|
|
36
33
|
@response_builder << Interface::InlayHint.new(
|
37
34
|
position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
|
@@ -44,7 +41,6 @@ module RubyLsp
|
|
44
41
|
sig { params(node: Prism::ImplicitNode).void }
|
45
42
|
def on_implicit_node_enter(node)
|
46
43
|
return unless @hints_configuration.enabled?(:implicitHashValue)
|
47
|
-
return unless visible?(node, @range)
|
48
44
|
|
49
45
|
node_value = node.value
|
50
46
|
loc = node.location
|
@@ -69,17 +65,6 @@ module RubyLsp
|
|
69
65
|
tooltip: tooltip,
|
70
66
|
)
|
71
67
|
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
sig { params(node: T.nilable(Prism::Node), range: T.nilable(T::Range[Integer])).returns(T::Boolean) }
|
76
|
-
def visible?(node, range)
|
77
|
-
return true if range.nil?
|
78
|
-
return false if node.nil?
|
79
|
-
|
80
|
-
loc = node.location
|
81
|
-
range.cover?(loc.start_line - 1) && range.cover?(loc.end_line - 1)
|
82
|
-
end
|
83
68
|
end
|
84
69
|
end
|
85
70
|
end
|
@@ -42,17 +42,46 @@ module RubyLsp
|
|
42
42
|
target_method = methods.first
|
43
43
|
return unless target_method
|
44
44
|
|
45
|
-
|
46
|
-
name = target_method.name
|
45
|
+
signatures = target_method.signatures
|
47
46
|
|
48
47
|
# If the method doesn't have any parameters, there's no need to show signature help
|
49
|
-
return if
|
48
|
+
return if signatures.empty?
|
49
|
+
|
50
|
+
name = target_method.name
|
51
|
+
title = +""
|
52
|
+
|
53
|
+
extra_links = if type.is_a?(TypeInferrer::GuessedType)
|
54
|
+
title << "\n\nGuessed receiver: #{type.name}"
|
55
|
+
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
56
|
+
end
|
50
57
|
|
51
|
-
|
58
|
+
active_signature, active_parameter = determine_active_signature_and_parameter(node, signatures)
|
52
59
|
|
60
|
+
signature_help = Interface::SignatureHelp.new(
|
61
|
+
signatures: generate_signatures(signatures, name, methods, title, extra_links),
|
62
|
+
active_signature: active_signature,
|
63
|
+
active_parameter: active_parameter,
|
64
|
+
)
|
65
|
+
@response_builder.replace(signature_help)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
sig do
|
71
|
+
params(node: Prism::CallNode, signatures: T::Array[RubyIndexer::Entry::Signature]).returns([Integer, Integer])
|
72
|
+
end
|
73
|
+
def determine_active_signature_and_parameter(node, signatures)
|
53
74
|
arguments_node = node.arguments
|
54
75
|
arguments = arguments_node&.arguments || []
|
55
|
-
|
76
|
+
|
77
|
+
# Find the first signature that matches the current arguments. If the user is invoking a method incorrectly and
|
78
|
+
# none of the signatures match, we show the first one
|
79
|
+
active_sig_index = signatures.find_index do |signature|
|
80
|
+
signature.matches?(arguments)
|
81
|
+
end || 0
|
82
|
+
|
83
|
+
parameter_length = [T.must(signatures[active_sig_index]).parameters.length - 1, 0].max
|
84
|
+
active_parameter = (arguments.length - 1).clamp(0, parameter_length)
|
56
85
|
|
57
86
|
# If there are arguments, then we need to check if there's a trailing comma after the end of the last argument
|
58
87
|
# to advance the active parameter to the next one
|
@@ -61,27 +90,29 @@ module RubyLsp
|
|
61
90
|
active_parameter += 1
|
62
91
|
end
|
63
92
|
|
64
|
-
|
65
|
-
|
66
|
-
extra_links = if type.is_a?(TypeInferrer::GuessedType)
|
67
|
-
title << "\n\nGuessed receiver: #{type.name}"
|
68
|
-
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
69
|
-
end
|
93
|
+
[active_sig_index, active_parameter]
|
94
|
+
end
|
70
95
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
96
|
+
sig do
|
97
|
+
params(
|
98
|
+
signatures: T::Array[RubyIndexer::Entry::Signature],
|
99
|
+
method_name: String,
|
100
|
+
methods: T::Array[RubyIndexer::Entry],
|
101
|
+
title: String,
|
102
|
+
extra_links: T.nilable(String),
|
103
|
+
).returns(T::Array[Interface::SignatureInformation])
|
104
|
+
end
|
105
|
+
def generate_signatures(signatures, method_name, methods, title, extra_links)
|
106
|
+
signatures.map do |signature|
|
107
|
+
Interface::SignatureInformation.new(
|
108
|
+
label: "#{method_name}(#{signature.format})",
|
109
|
+
parameters: signature.parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
|
110
|
+
documentation: Interface::MarkupContent.new(
|
111
|
+
kind: "markdown",
|
112
|
+
value: markdown_from_index_entries(title, methods, extra_links: extra_links),
|
80
113
|
),
|
81
|
-
|
82
|
-
|
83
|
-
)
|
84
|
-
@response_builder.replace(signature_help)
|
114
|
+
)
|
115
|
+
end
|
85
116
|
end
|
86
117
|
end
|
87
118
|
end
|
@@ -14,18 +14,19 @@ module RubyLsp
|
|
14
14
|
super
|
15
15
|
end
|
16
16
|
|
17
|
-
sig { override.returns(
|
18
|
-
def parse
|
19
|
-
return
|
17
|
+
sig { override.returns(T::Boolean) }
|
18
|
+
def parse!
|
19
|
+
return false unless @needs_parsing
|
20
20
|
|
21
21
|
@needs_parsing = false
|
22
22
|
|
23
23
|
_, _, declarations = RBS::Parser.parse_signature(@source)
|
24
24
|
@syntax_error = false
|
25
25
|
@parse_result = declarations
|
26
|
+
true
|
26
27
|
rescue RBS::ParsingError
|
27
28
|
@syntax_error = true
|
28
|
-
|
29
|
+
true
|
29
30
|
end
|
30
31
|
|
31
32
|
sig { override.returns(T::Boolean) }
|
@@ -3,24 +3,9 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
|
-
#
|
8
6
|
# The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
|
9
7
|
# request is used to to resolve the edit field for a given code action, if it is not already provided in the
|
10
8
|
# textDocument/codeAction response. We can use it for scenarios that require more computation such as refactoring.
|
11
|
-
#
|
12
|
-
# # Example: Extract to variable
|
13
|
-
#
|
14
|
-
# ```ruby
|
15
|
-
# # Before:
|
16
|
-
# 1 + 1 # Select the text and use Refactor: Extract Variable
|
17
|
-
#
|
18
|
-
# # After:
|
19
|
-
# new_variable = 1 + 1
|
20
|
-
# new_variable
|
21
|
-
#
|
22
|
-
# ```
|
23
|
-
#
|
24
9
|
class CodeActionResolve < Request
|
25
10
|
extend T::Sig
|
26
11
|
include Support::Common
|
@@ -3,19 +3,9 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
-
# 
|
7
|
-
#
|
8
6
|
# The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
|
9
7
|
# request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
|
10
8
|
# specific diagnostic.
|
11
|
-
#
|
12
|
-
# # Example
|
13
|
-
#
|
14
|
-
# ```ruby
|
15
|
-
# def say_hello
|
16
|
-
# puts "Hello" # --> code action: quick fix indentation
|
17
|
-
# end
|
18
|
-
# ```
|
19
9
|
class CodeActions < Request
|
20
10
|
extend T::Sig
|
21
11
|
|
@@ -7,19 +7,9 @@ require "ruby_lsp/listeners/code_lens"
|
|
7
7
|
|
8
8
|
module RubyLsp
|
9
9
|
module Requests
|
10
|
-
# 
|
11
|
-
#
|
12
10
|
# The
|
13
11
|
# [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
|
14
|
-
# request informs the editor of runnable commands such as testing and debugging
|
15
|
-
#
|
16
|
-
# # Example
|
17
|
-
#
|
18
|
-
# ```ruby
|
19
|
-
# # Run | Run in Terminal | Debug
|
20
|
-
# class Test < Minitest::Test
|
21
|
-
# end
|
22
|
-
# ```
|
12
|
+
# request informs the editor of runnable commands such as testing and debugging.
|
23
13
|
class CodeLens < Request
|
24
14
|
extend T::Sig
|
25
15
|
|