ruby-lsp 0.13.2 → 0.13.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 +30 -0
- data/VERSION +1 -1
- data/lib/ruby_lsp/check_docs.rb +3 -3
- data/lib/ruby_lsp/document.rb +12 -0
- data/lib/ruby_lsp/executor.rb +77 -266
- data/lib/ruby_lsp/listener.rb +1 -50
- data/lib/ruby_lsp/listeners/code_lens.rb +233 -0
- data/lib/ruby_lsp/listeners/completion.rb +275 -0
- data/lib/ruby_lsp/listeners/definition.rb +158 -0
- data/lib/ruby_lsp/listeners/document_highlight.rb +556 -0
- data/lib/ruby_lsp/listeners/document_link.rb +162 -0
- data/lib/ruby_lsp/listeners/document_symbol.rb +223 -0
- data/lib/ruby_lsp/listeners/folding_ranges.rb +271 -0
- data/lib/ruby_lsp/listeners/hover.rb +152 -0
- data/lib/ruby_lsp/listeners/inlay_hints.rb +80 -0
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +430 -0
- data/lib/ruby_lsp/listeners/signature_help.rb +74 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +4 -4
- data/lib/ruby_lsp/requests/code_actions.rb +13 -4
- data/lib/ruby_lsp/requests/code_lens.rb +21 -221
- data/lib/ruby_lsp/requests/completion.rb +64 -244
- data/lib/ruby_lsp/requests/definition.rb +34 -147
- data/lib/ruby_lsp/requests/diagnostics.rb +17 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +12 -536
- data/lib/ruby_lsp/requests/document_link.rb +11 -132
- data/lib/ruby_lsp/requests/document_symbol.rb +23 -210
- data/lib/ruby_lsp/requests/folding_ranges.rb +16 -252
- data/lib/ruby_lsp/requests/formatting.rb +4 -4
- data/lib/ruby_lsp/requests/hover.rb +48 -92
- data/lib/ruby_lsp/requests/inlay_hints.rb +23 -56
- data/lib/ruby_lsp/requests/on_type_formatting.rb +16 -4
- data/lib/ruby_lsp/requests/request.rb +17 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +4 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +21 -408
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -4
- data/lib/ruby_lsp/requests/signature_help.rb +43 -51
- data/lib/ruby_lsp/requests/support/common.rb +3 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +2 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
- data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
- data/lib/ruby_lsp/requests.rb +1 -1
- data/lib/ruby_lsp/utils.rb +8 -0
- metadata +15 -4
- data/lib/ruby_lsp/requests/base_request.rb +0 -24
@@ -0,0 +1,74 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class SignatureHelp < Listener
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
ResponseType = type_member { { fixed: T.nilable(T.any(Interface::SignatureHelp, T::Hash[Symbol, T.untyped])) } }
|
11
|
+
|
12
|
+
sig { override.returns(ResponseType) }
|
13
|
+
attr_reader :_response
|
14
|
+
|
15
|
+
sig do
|
16
|
+
params(
|
17
|
+
nesting: T::Array[String],
|
18
|
+
index: RubyIndexer::Index,
|
19
|
+
dispatcher: Prism::Dispatcher,
|
20
|
+
).void
|
21
|
+
end
|
22
|
+
def initialize(nesting, index, dispatcher)
|
23
|
+
@nesting = nesting
|
24
|
+
@index = index
|
25
|
+
@_response = T.let(nil, ResponseType)
|
26
|
+
|
27
|
+
super(dispatcher)
|
28
|
+
dispatcher.register(self, :on_call_node_enter)
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { params(node: Prism::CallNode).void }
|
32
|
+
def on_call_node_enter(node)
|
33
|
+
return if DependencyDetector.instance.typechecker
|
34
|
+
return unless self_receiver?(node)
|
35
|
+
|
36
|
+
message = node.message
|
37
|
+
return unless message
|
38
|
+
|
39
|
+
target_method = @index.resolve_method(message, @nesting.join("::"))
|
40
|
+
return unless target_method
|
41
|
+
|
42
|
+
parameters = target_method.parameters
|
43
|
+
name = target_method.name
|
44
|
+
|
45
|
+
# If the method doesn't have any parameters, there's no need to show signature help
|
46
|
+
return if parameters.empty?
|
47
|
+
|
48
|
+
label = "#{name}(#{parameters.map(&:decorated_name).join(", ")})"
|
49
|
+
|
50
|
+
arguments_node = node.arguments
|
51
|
+
arguments = arguments_node&.arguments || []
|
52
|
+
active_parameter = (arguments.length - 1).clamp(0, parameters.length - 1)
|
53
|
+
|
54
|
+
# If there are arguments, then we need to check if there's a trailing comma after the end of the last argument
|
55
|
+
# to advance the active parameter to the next one
|
56
|
+
if arguments_node &&
|
57
|
+
node.slice.byteslice(arguments_node.location.end_offset - node.location.start_offset) == ","
|
58
|
+
active_parameter += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
@_response = Interface::SignatureHelp.new(
|
62
|
+
signatures: [
|
63
|
+
Interface::SignatureInformation.new(
|
64
|
+
label: label,
|
65
|
+
parameters: parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
|
66
|
+
documentation: markdown_from_index_entries("", target_method),
|
67
|
+
),
|
68
|
+
],
|
69
|
+
active_parameter: active_parameter,
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -21,7 +21,7 @@ module RubyLsp
|
|
21
21
|
#
|
22
22
|
# ```
|
23
23
|
#
|
24
|
-
class CodeActionResolve <
|
24
|
+
class CodeActionResolve < Request
|
25
25
|
extend T::Sig
|
26
26
|
NEW_VARIABLE_NAME = "new_variable"
|
27
27
|
|
@@ -36,13 +36,13 @@ module RubyLsp
|
|
36
36
|
|
37
37
|
sig { params(document: Document, code_action: T::Hash[Symbol, T.untyped]).void }
|
38
38
|
def initialize(document, code_action)
|
39
|
-
super(
|
40
|
-
|
39
|
+
super()
|
40
|
+
@document = document
|
41
41
|
@code_action = code_action
|
42
42
|
end
|
43
43
|
|
44
44
|
sig { override.returns(T.any(Interface::CodeAction, Error)) }
|
45
|
-
def
|
45
|
+
def perform
|
46
46
|
return Error::EmptySelection if @document.source.empty?
|
47
47
|
|
48
48
|
source_range = @code_action.dig(:data, :range)
|
@@ -16,9 +16,18 @@ module RubyLsp
|
|
16
16
|
# puts "Hello" # --> code action: quick fix indentation
|
17
17
|
# end
|
18
18
|
# ```
|
19
|
-
class CodeActions <
|
19
|
+
class CodeActions < Request
|
20
20
|
extend T::Sig
|
21
21
|
|
22
|
+
class << self
|
23
|
+
extend T::Sig
|
24
|
+
|
25
|
+
sig { returns(Interface::CodeActionOptions) }
|
26
|
+
def provider
|
27
|
+
Interface::CodeActionOptions.new(resolve_provider: true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
22
31
|
sig do
|
23
32
|
params(
|
24
33
|
document: Document,
|
@@ -27,15 +36,15 @@ module RubyLsp
|
|
27
36
|
).void
|
28
37
|
end
|
29
38
|
def initialize(document, range, context)
|
30
|
-
super(
|
31
|
-
|
39
|
+
super()
|
40
|
+
@document = document
|
32
41
|
@uri = T.let(document.uri, URI::Generic)
|
33
42
|
@range = range
|
34
43
|
@context = context
|
35
44
|
end
|
36
45
|
|
37
46
|
sig { override.returns(T.nilable(T.all(T::Array[Interface::CodeAction], Object))) }
|
38
|
-
def
|
47
|
+
def perform
|
39
48
|
diagnostics = @context[:diagnostics]
|
40
49
|
|
41
50
|
code_actions = diagnostics.flat_map do |diagnostic|
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require "shellwords"
|
5
5
|
|
6
|
+
require "ruby_lsp/listeners/code_lens"
|
7
|
+
|
6
8
|
module RubyLsp
|
7
9
|
module Requests
|
8
10
|
# 
|
@@ -22,34 +24,20 @@ module RubyLsp
|
|
22
24
|
# class Test < Minitest::Test
|
23
25
|
# end
|
24
26
|
# ```
|
25
|
-
class CodeLens <
|
27
|
+
class CodeLens < Request
|
26
28
|
extend T::Sig
|
27
29
|
extend T::Generic
|
28
30
|
|
29
|
-
|
31
|
+
class << self
|
32
|
+
extend T::Sig
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
"ruby"
|
37
|
-
end + " -Itest ",
|
38
|
-
String,
|
39
|
-
)
|
40
|
-
GEMFILE_NAME = T.let(
|
41
|
-
begin
|
42
|
-
Bundler.with_original_env { Bundler.default_gemfile.basename.to_s }
|
43
|
-
rescue Bundler::GemfileNotFound
|
44
|
-
"Gemfile"
|
45
|
-
end,
|
46
|
-
String,
|
47
|
-
)
|
48
|
-
ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
|
49
|
-
SUPPORTED_TEST_LIBRARIES = T.let(["minitest", "test-unit"], T::Array[String])
|
34
|
+
sig { returns(Interface::CodeLensOptions) }
|
35
|
+
def provider
|
36
|
+
Interface::CodeLensOptions.new(resolve_provider: false)
|
37
|
+
end
|
38
|
+
end
|
50
39
|
|
51
|
-
|
52
|
-
attr_reader :_response
|
40
|
+
ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
|
53
41
|
|
54
42
|
sig do
|
55
43
|
params(
|
@@ -59,209 +47,21 @@ module RubyLsp
|
|
59
47
|
).void
|
60
48
|
end
|
61
49
|
def initialize(uri, lenses_configuration, dispatcher)
|
62
|
-
|
63
|
-
@
|
64
|
-
|
65
|
-
|
66
|
-
@visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
|
67
|
-
@class_stack = T.let([], T::Array[String])
|
68
|
-
@group_id = T.let(1, Integer)
|
69
|
-
@group_id_stack = T.let([], T::Array[Integer])
|
70
|
-
@lenses_configuration = lenses_configuration
|
71
|
-
|
72
|
-
super(dispatcher)
|
73
|
-
|
74
|
-
dispatcher.register(
|
75
|
-
self,
|
76
|
-
:on_class_node_enter,
|
77
|
-
:on_class_node_leave,
|
78
|
-
:on_def_node_enter,
|
79
|
-
:on_call_node_enter,
|
80
|
-
:on_call_node_leave,
|
81
|
-
)
|
82
|
-
end
|
83
|
-
|
84
|
-
sig { params(node: Prism::ClassNode).void }
|
85
|
-
def on_class_node_enter(node)
|
86
|
-
@visibility_stack.push([:public, :public])
|
87
|
-
class_name = node.constant_path.slice
|
88
|
-
@class_stack.push(class_name)
|
89
|
-
|
90
|
-
if @path && class_name.end_with?("Test")
|
91
|
-
add_test_code_lens(
|
92
|
-
node,
|
93
|
-
name: class_name,
|
94
|
-
command: generate_test_command(class_name: class_name),
|
95
|
-
kind: :group,
|
96
|
-
)
|
97
|
-
end
|
98
|
-
|
99
|
-
@group_id_stack.push(@group_id)
|
100
|
-
@group_id += 1
|
101
|
-
end
|
102
|
-
|
103
|
-
sig { params(node: Prism::ClassNode).void }
|
104
|
-
def on_class_node_leave(node)
|
105
|
-
@visibility_stack.pop
|
106
|
-
@class_stack.pop
|
107
|
-
@group_id_stack.pop
|
108
|
-
end
|
109
|
-
|
110
|
-
sig { params(node: Prism::DefNode).void }
|
111
|
-
def on_def_node_enter(node)
|
112
|
-
class_name = @class_stack.last
|
113
|
-
return unless class_name&.end_with?("Test")
|
114
|
-
|
115
|
-
visibility, _ = @visibility_stack.last
|
116
|
-
if visibility == :public
|
117
|
-
method_name = node.name.to_s
|
118
|
-
if @path && method_name.start_with?("test_")
|
119
|
-
add_test_code_lens(
|
120
|
-
node,
|
121
|
-
name: method_name,
|
122
|
-
command: generate_test_command(method_name: method_name, class_name: class_name),
|
123
|
-
kind: :example,
|
124
|
-
)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
sig { params(node: Prism::CallNode).void }
|
130
|
-
def on_call_node_enter(node)
|
131
|
-
name = node.name
|
132
|
-
arguments = node.arguments
|
133
|
-
|
134
|
-
# If we found `private` by itself or `private def foo`
|
135
|
-
if ACCESS_MODIFIERS.include?(name)
|
136
|
-
if arguments.nil?
|
137
|
-
@visibility_stack.pop
|
138
|
-
@visibility_stack.push([name, name])
|
139
|
-
elsif arguments.arguments.first.is_a?(Prism::DefNode)
|
140
|
-
visibility, _ = @visibility_stack.pop
|
141
|
-
@visibility_stack.push([name, visibility])
|
142
|
-
end
|
143
|
-
|
144
|
-
return
|
145
|
-
end
|
146
|
-
|
147
|
-
if @path&.include?(GEMFILE_NAME) && name == :gem && arguments
|
148
|
-
return unless @lenses_configuration.enabled?(:gemfileLinks)
|
149
|
-
|
150
|
-
first_argument = arguments.arguments.first
|
151
|
-
return unless first_argument.is_a?(Prism::StringNode)
|
152
|
-
|
153
|
-
remote = resolve_gem_remote(first_argument)
|
154
|
-
return unless remote
|
155
|
-
|
156
|
-
add_open_gem_remote_code_lens(node, remote)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
sig { params(node: Prism::CallNode).void }
|
161
|
-
def on_call_node_leave(node)
|
162
|
-
_, prev_visibility = @visibility_stack.pop
|
163
|
-
@visibility_stack.push([prev_visibility, prev_visibility])
|
164
|
-
end
|
165
|
-
|
166
|
-
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
167
|
-
def initialize_external_listener(addon)
|
168
|
-
addon.create_code_lens_listener(@uri, @dispatcher)
|
169
|
-
end
|
170
|
-
|
171
|
-
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
172
|
-
def merge_response!(other)
|
173
|
-
@_response.concat(other.response)
|
174
|
-
self
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
|
179
|
-
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
|
180
|
-
def add_test_code_lens(node, name:, command:, kind:)
|
181
|
-
# don't add code lenses if the test library is not supported or unknown
|
182
|
-
return unless SUPPORTED_TEST_LIBRARIES.include?(DependencyDetector.instance.detected_test_library) && @path
|
183
|
-
|
184
|
-
arguments = [
|
185
|
-
@path,
|
186
|
-
name,
|
187
|
-
command,
|
188
|
-
{
|
189
|
-
start_line: node.location.start_line - 1,
|
190
|
-
start_column: node.location.start_column,
|
191
|
-
end_line: node.location.end_line - 1,
|
192
|
-
end_column: node.location.end_column,
|
193
|
-
},
|
194
|
-
]
|
195
|
-
|
196
|
-
grouping_data = { group_id: @group_id_stack.last, kind: kind }
|
197
|
-
grouping_data[:id] = @group_id if kind == :group
|
198
|
-
|
199
|
-
@_response << create_code_lens(
|
200
|
-
node,
|
201
|
-
title: "Run",
|
202
|
-
command_name: "rubyLsp.runTest",
|
203
|
-
arguments: arguments,
|
204
|
-
data: { type: "test", **grouping_data },
|
50
|
+
super()
|
51
|
+
@listeners = T.let(
|
52
|
+
[Listeners::CodeLens.new(uri, lenses_configuration, dispatcher)],
|
53
|
+
T::Array[Listener[ResponseType]],
|
205
54
|
)
|
206
55
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
command_name: "rubyLsp.runTestInTerminal",
|
211
|
-
arguments: arguments,
|
212
|
-
data: { type: "test_in_terminal", **grouping_data },
|
213
|
-
)
|
214
|
-
|
215
|
-
@_response << create_code_lens(
|
216
|
-
node,
|
217
|
-
title: "Debug",
|
218
|
-
command_name: "rubyLsp.debugTest",
|
219
|
-
arguments: arguments,
|
220
|
-
data: { type: "debug", **grouping_data },
|
221
|
-
)
|
222
|
-
end
|
223
|
-
|
224
|
-
sig { params(gem_name: Prism::StringNode).returns(T.nilable(String)) }
|
225
|
-
def resolve_gem_remote(gem_name)
|
226
|
-
spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name.content }&.to_spec
|
227
|
-
return if spec.nil?
|
228
|
-
|
229
|
-
[spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
|
230
|
-
page.start_with?("https://github.com", "https://gitlab.com")
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
sig { params(class_name: String, method_name: T.nilable(String)).returns(String) }
|
235
|
-
def generate_test_command(class_name:, method_name: nil)
|
236
|
-
command = BASE_COMMAND + T.must(@path)
|
237
|
-
|
238
|
-
case DependencyDetector.instance.detected_test_library
|
239
|
-
when "minitest"
|
240
|
-
command += if method_name
|
241
|
-
" --name " + "/#{Shellwords.escape(class_name + "#" + method_name)}/"
|
242
|
-
else
|
243
|
-
" --name " + "/#{Shellwords.escape(class_name)}/"
|
244
|
-
end
|
245
|
-
when "test-unit"
|
246
|
-
command += " --testcase " + "/#{Shellwords.escape(class_name)}/"
|
247
|
-
|
248
|
-
if method_name
|
249
|
-
command += " --name " + Shellwords.escape(method_name)
|
250
|
-
end
|
56
|
+
Addon.addons.each do |addon|
|
57
|
+
addon_listener = addon.create_code_lens_listener(uri, dispatcher)
|
58
|
+
@listeners << addon_listener if addon_listener
|
251
59
|
end
|
252
|
-
|
253
|
-
command
|
254
60
|
end
|
255
61
|
|
256
|
-
sig {
|
257
|
-
def
|
258
|
-
@
|
259
|
-
node,
|
260
|
-
title: "Open remote",
|
261
|
-
command_name: "rubyLsp.openLink",
|
262
|
-
arguments: [remote],
|
263
|
-
data: { type: "link" },
|
264
|
-
)
|
62
|
+
sig { override.returns(ResponseType) }
|
63
|
+
def perform
|
64
|
+
@listeners.flat_map(&:response)
|
265
65
|
end
|
266
66
|
end
|
267
67
|
end
|