ruby-lsp 0.20.1 → 0.22.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +19 -4
- data/exe/ruby-lsp-launcher +124 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +233 -59
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +34 -16
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +15 -15
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +4 -4
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/constant_test.rb +8 -8
- data/lib/ruby_indexer/test/enhancements_test.rb +169 -41
- data/lib/ruby_indexer/test/index_test.rb +41 -2
- data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
- data/lib/ruby_indexer/test/method_test.rb +139 -0
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/addon.rb +9 -2
- data/lib/ruby_lsp/base_server.rb +14 -5
- data/lib/ruby_lsp/client_capabilities.rb +67 -0
- data/lib/ruby_lsp/document.rb +1 -1
- data/lib/ruby_lsp/global_state.rb +33 -20
- data/lib/ruby_lsp/internal.rb +3 -0
- data/lib/ruby_lsp/listeners/completion.rb +62 -0
- data/lib/ruby_lsp/listeners/definition.rb +48 -13
- data/lib/ruby_lsp/listeners/document_highlight.rb +91 -4
- data/lib/ruby_lsp/listeners/document_symbol.rb +37 -4
- data/lib/ruby_lsp/listeners/hover.rb +52 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/completion.rb +7 -1
- data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/definition.rb +28 -11
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -1
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
- data/lib/ruby_lsp/requests/hover.rb +26 -6
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +12 -1
- data/lib/ruby_lsp/scripts/compose_bundle.rb +20 -0
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +8 -0
- data/lib/ruby_lsp/server.rb +85 -55
- data/lib/ruby_lsp/setup_bundler.rb +154 -47
- data/lib/ruby_lsp/store.rb +0 -4
- data/lib/ruby_lsp/utils.rb +63 -0
- metadata +8 -3
@@ -41,6 +41,9 @@ module RubyLsp
|
|
41
41
|
:on_module_node_enter,
|
42
42
|
:on_module_node_leave,
|
43
43
|
:on_instance_variable_write_node_enter,
|
44
|
+
:on_instance_variable_operator_write_node_enter,
|
45
|
+
:on_instance_variable_or_write_node_enter,
|
46
|
+
:on_instance_variable_and_write_node_enter,
|
44
47
|
:on_class_variable_write_node_enter,
|
45
48
|
:on_singleton_class_node_enter,
|
46
49
|
:on_singleton_class_node_leave,
|
@@ -249,21 +252,51 @@ module RubyLsp
|
|
249
252
|
@response_builder.pop
|
250
253
|
end
|
251
254
|
|
255
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
256
|
+
def on_class_variable_write_node_enter(node)
|
257
|
+
create_document_symbol(
|
258
|
+
name: node.name.to_s,
|
259
|
+
kind: Constant::SymbolKind::VARIABLE,
|
260
|
+
range_location: node.name_loc,
|
261
|
+
selection_range_location: node.name_loc,
|
262
|
+
)
|
263
|
+
end
|
264
|
+
|
252
265
|
sig { params(node: Prism::InstanceVariableWriteNode).void }
|
253
266
|
def on_instance_variable_write_node_enter(node)
|
254
267
|
create_document_symbol(
|
255
268
|
name: node.name.to_s,
|
256
|
-
kind: Constant::SymbolKind::
|
269
|
+
kind: Constant::SymbolKind::FIELD,
|
257
270
|
range_location: node.name_loc,
|
258
271
|
selection_range_location: node.name_loc,
|
259
272
|
)
|
260
273
|
end
|
261
274
|
|
262
|
-
sig { params(node: Prism::
|
263
|
-
def
|
275
|
+
sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
|
276
|
+
def on_instance_variable_operator_write_node_enter(node)
|
264
277
|
create_document_symbol(
|
265
278
|
name: node.name.to_s,
|
266
|
-
kind: Constant::SymbolKind::
|
279
|
+
kind: Constant::SymbolKind::FIELD,
|
280
|
+
range_location: node.name_loc,
|
281
|
+
selection_range_location: node.name_loc,
|
282
|
+
)
|
283
|
+
end
|
284
|
+
|
285
|
+
sig { params(node: Prism::InstanceVariableOrWriteNode).void }
|
286
|
+
def on_instance_variable_or_write_node_enter(node)
|
287
|
+
create_document_symbol(
|
288
|
+
name: node.name.to_s,
|
289
|
+
kind: Constant::SymbolKind::FIELD,
|
290
|
+
range_location: node.name_loc,
|
291
|
+
selection_range_location: node.name_loc,
|
292
|
+
)
|
293
|
+
end
|
294
|
+
|
295
|
+
sig { params(node: Prism::InstanceVariableAndWriteNode).void }
|
296
|
+
def on_instance_variable_and_write_node_enter(node)
|
297
|
+
create_document_symbol(
|
298
|
+
name: node.name.to_s,
|
299
|
+
kind: Constant::SymbolKind::FIELD,
|
267
300
|
range_location: node.name_loc,
|
268
301
|
selection_range_location: node.name_loc,
|
269
302
|
)
|
@@ -13,6 +13,12 @@ module RubyLsp
|
|
13
13
|
Prism::ConstantReadNode,
|
14
14
|
Prism::ConstantWriteNode,
|
15
15
|
Prism::ConstantPathNode,
|
16
|
+
Prism::GlobalVariableAndWriteNode,
|
17
|
+
Prism::GlobalVariableOperatorWriteNode,
|
18
|
+
Prism::GlobalVariableOrWriteNode,
|
19
|
+
Prism::GlobalVariableReadNode,
|
20
|
+
Prism::GlobalVariableTargetNode,
|
21
|
+
Prism::GlobalVariableWriteNode,
|
16
22
|
Prism::InstanceVariableReadNode,
|
17
23
|
Prism::InstanceVariableAndWriteNode,
|
18
24
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -62,6 +68,12 @@ module RubyLsp
|
|
62
68
|
:on_constant_write_node_enter,
|
63
69
|
:on_constant_path_node_enter,
|
64
70
|
:on_call_node_enter,
|
71
|
+
:on_global_variable_and_write_node_enter,
|
72
|
+
:on_global_variable_operator_write_node_enter,
|
73
|
+
:on_global_variable_or_write_node_enter,
|
74
|
+
:on_global_variable_read_node_enter,
|
75
|
+
:on_global_variable_target_node_enter,
|
76
|
+
:on_global_variable_write_node_enter,
|
65
77
|
:on_instance_variable_read_node_enter,
|
66
78
|
:on_instance_variable_write_node_enter,
|
67
79
|
:on_instance_variable_and_write_node_enter,
|
@@ -128,6 +140,36 @@ module RubyLsp
|
|
128
140
|
handle_method_hover(message)
|
129
141
|
end
|
130
142
|
|
143
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
144
|
+
def on_global_variable_and_write_node_enter(node)
|
145
|
+
handle_global_variable_hover(node.name.to_s)
|
146
|
+
end
|
147
|
+
|
148
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
149
|
+
def on_global_variable_operator_write_node_enter(node)
|
150
|
+
handle_global_variable_hover(node.name.to_s)
|
151
|
+
end
|
152
|
+
|
153
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
154
|
+
def on_global_variable_or_write_node_enter(node)
|
155
|
+
handle_global_variable_hover(node.name.to_s)
|
156
|
+
end
|
157
|
+
|
158
|
+
sig { params(node: Prism::GlobalVariableReadNode).void }
|
159
|
+
def on_global_variable_read_node_enter(node)
|
160
|
+
handle_global_variable_hover(node.name.to_s)
|
161
|
+
end
|
162
|
+
|
163
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
164
|
+
def on_global_variable_target_node_enter(node)
|
165
|
+
handle_global_variable_hover(node.name.to_s)
|
166
|
+
end
|
167
|
+
|
168
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
169
|
+
def on_global_variable_write_node_enter(node)
|
170
|
+
handle_global_variable_hover(node.name.to_s)
|
171
|
+
end
|
172
|
+
|
131
173
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
132
174
|
def on_instance_variable_read_node_enter(node)
|
133
175
|
handle_instance_variable_hover(node.name.to_s)
|
@@ -265,6 +307,16 @@ module RubyLsp
|
|
265
307
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
266
308
|
end
|
267
309
|
|
310
|
+
sig { params(name: String).void }
|
311
|
+
def handle_global_variable_hover(name)
|
312
|
+
entries = @index[name]
|
313
|
+
return unless entries
|
314
|
+
|
315
|
+
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
316
|
+
@response_builder.push(content, category: category)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
268
320
|
sig { params(name: String, location: Prism::Location).void }
|
269
321
|
def generate_hover(name, location)
|
270
322
|
entries = @index.resolve(name, @node_context.nesting)
|
@@ -121,7 +121,7 @@ module RubyLsp
|
|
121
121
|
return Error::InvalidTargetRange if closest_node.is_a?(Prism::MissingNode)
|
122
122
|
|
123
123
|
closest_node_loc = closest_node.location
|
124
|
-
# If the parent expression is a single line block, then we have to extract it inside of the
|
124
|
+
# If the parent expression is a single line block, then we have to extract it inside of the one-line block
|
125
125
|
if parent_statements.is_a?(Prism::BlockNode) &&
|
126
126
|
parent_statements.location.start_line == parent_statements.location.end_line
|
127
127
|
|
@@ -17,7 +17,7 @@ module RubyLsp
|
|
17
17
|
def provider
|
18
18
|
Interface::CompletionOptions.new(
|
19
19
|
resolve_provider: true,
|
20
|
-
trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<"],
|
20
|
+
trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<", "$"],
|
21
21
|
completion_item: {
|
22
22
|
labelDetailsSupport: true,
|
23
23
|
},
|
@@ -50,6 +50,12 @@ module RubyLsp
|
|
50
50
|
Prism::CallNode,
|
51
51
|
Prism::ConstantReadNode,
|
52
52
|
Prism::ConstantPathNode,
|
53
|
+
Prism::GlobalVariableAndWriteNode,
|
54
|
+
Prism::GlobalVariableOperatorWriteNode,
|
55
|
+
Prism::GlobalVariableOrWriteNode,
|
56
|
+
Prism::GlobalVariableReadNode,
|
57
|
+
Prism::GlobalVariableTargetNode,
|
58
|
+
Prism::GlobalVariableWriteNode,
|
53
59
|
Prism::InstanceVariableReadNode,
|
54
60
|
Prism::InstanceVariableAndWriteNode,
|
55
61
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
34
34
|
|
35
35
|
# Based on the spec https://microsoft.github.io/language-server-protocol/specification#textDocument_completion,
|
36
36
|
# a completion resolve request must always return the original completion item without modifying ANY fields
|
37
|
-
# other than detail and documentation (NOT labelDetails). If we modify anything, the completion
|
37
|
+
# other than detail and documentation (NOT labelDetails). If we modify anything, the completion behavior might
|
38
38
|
# be broken.
|
39
39
|
#
|
40
40
|
# For example, forgetting to return the `insertText` included in the original item will make the editor use the
|
@@ -12,12 +12,6 @@ module RubyLsp
|
|
12
12
|
extend T::Sig
|
13
13
|
extend T::Generic
|
14
14
|
|
15
|
-
SPECIAL_METHOD_CALLS = [
|
16
|
-
:require,
|
17
|
-
:require_relative,
|
18
|
-
:autoload,
|
19
|
-
].freeze
|
20
|
-
|
21
15
|
sig do
|
22
16
|
params(
|
23
17
|
document: T.any(RubyDocument, ERBDocument),
|
@@ -46,7 +40,12 @@ module RubyLsp
|
|
46
40
|
Prism::ConstantReadNode,
|
47
41
|
Prism::ConstantPathNode,
|
48
42
|
Prism::BlockArgumentNode,
|
43
|
+
Prism::GlobalVariableAndWriteNode,
|
44
|
+
Prism::GlobalVariableOperatorWriteNode,
|
45
|
+
Prism::GlobalVariableOrWriteNode,
|
49
46
|
Prism::GlobalVariableReadNode,
|
47
|
+
Prism::GlobalVariableTargetNode,
|
48
|
+
Prism::GlobalVariableWriteNode,
|
50
49
|
Prism::InstanceVariableReadNode,
|
51
50
|
Prism::InstanceVariableAndWriteNode,
|
52
51
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -72,11 +71,7 @@ module RubyLsp
|
|
72
71
|
parent,
|
73
72
|
position,
|
74
73
|
)
|
75
|
-
elsif
|
76
|
-
target.message_loc, position
|
77
|
-
)
|
78
|
-
# If the target is a method call, we need to ensure that the requested position is exactly on top of the
|
79
|
-
# method identifier. Otherwise, we risk showing definitions for unrelated things
|
74
|
+
elsif position_outside_target?(position, target)
|
80
75
|
target = nil
|
81
76
|
# For methods with block arguments using symbol-to-proc
|
82
77
|
elsif target.is_a?(Prism::SymbolNode) && parent.is_a?(Prism::BlockArgumentNode)
|
@@ -107,6 +102,28 @@ module RubyLsp
|
|
107
102
|
@dispatcher.dispatch_once(@target) if @target
|
108
103
|
@response_builder.response
|
109
104
|
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
sig { params(position: T::Hash[Symbol, T.untyped], target: T.nilable(Prism::Node)).returns(T::Boolean) }
|
109
|
+
def position_outside_target?(position, target)
|
110
|
+
case target
|
111
|
+
when Prism::GlobalVariableAndWriteNode,
|
112
|
+
Prism::GlobalVariableOperatorWriteNode,
|
113
|
+
Prism::GlobalVariableOrWriteNode,
|
114
|
+
Prism::GlobalVariableWriteNode,
|
115
|
+
Prism::InstanceVariableAndWriteNode,
|
116
|
+
Prism::InstanceVariableOperatorWriteNode,
|
117
|
+
Prism::InstanceVariableOrWriteNode,
|
118
|
+
Prism::InstanceVariableWriteNode
|
119
|
+
|
120
|
+
!covers_position?(target.name_loc, position)
|
121
|
+
when Prism::CallNode
|
122
|
+
!covers_position?(target.message_loc, position)
|
123
|
+
else
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
110
127
|
end
|
111
128
|
end
|
112
129
|
end
|
@@ -38,7 +38,13 @@ module RubyLsp
|
|
38
38
|
ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
|
39
39
|
ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight],
|
40
40
|
)
|
41
|
-
Listeners::DocumentHighlight.new(
|
41
|
+
Listeners::DocumentHighlight.new(
|
42
|
+
@response_builder,
|
43
|
+
node_context.node,
|
44
|
+
node_context.parent,
|
45
|
+
dispatcher,
|
46
|
+
position,
|
47
|
+
)
|
42
48
|
end
|
43
49
|
|
44
50
|
sig { override.returns(T::Array[Interface::DocumentHighlight]) }
|
@@ -10,7 +10,8 @@ module RubyLsp
|
|
10
10
|
# informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
|
11
11
|
# this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
|
12
12
|
#
|
13
|
-
# In VS Code,
|
13
|
+
# In VS Code, symbol search known as 'Go To Symbol in Editor' and can be accessed with Ctrl/Cmd-Shift-O,
|
14
|
+
# or by opening the command palette and inserting an `@` symbol.
|
14
15
|
class DocumentSymbol < Request
|
15
16
|
extend T::Sig
|
16
17
|
|
@@ -46,17 +46,13 @@ module RubyLsp
|
|
46
46
|
target = node_context.node
|
47
47
|
parent = node_context.parent
|
48
48
|
|
49
|
-
if
|
50
|
-
!Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
|
51
|
-
(parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
|
49
|
+
if should_refine_target?(parent, target)
|
52
50
|
target = determine_target(
|
53
51
|
T.must(target),
|
54
52
|
T.must(parent),
|
55
53
|
position,
|
56
54
|
)
|
57
|
-
elsif
|
58
|
-
!covers_position?(target.message_loc, position)
|
59
|
-
|
55
|
+
elsif position_outside_target?(position, target)
|
60
56
|
target = nil
|
61
57
|
end
|
62
58
|
|
@@ -89,6 +85,30 @@ module RubyLsp
|
|
89
85
|
),
|
90
86
|
)
|
91
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
sig { params(parent: T.nilable(Prism::Node), target: T.nilable(Prism::Node)).returns(T::Boolean) }
|
92
|
+
def should_refine_target?(parent, target)
|
93
|
+
(Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
|
94
|
+
!Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
|
95
|
+
(parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(position: T::Hash[Symbol, T.untyped], target: T.nilable(Prism::Node)).returns(T::Boolean) }
|
99
|
+
def position_outside_target?(position, target)
|
100
|
+
case target
|
101
|
+
when Prism::GlobalVariableAndWriteNode,
|
102
|
+
Prism::GlobalVariableOperatorWriteNode,
|
103
|
+
Prism::GlobalVariableOrWriteNode,
|
104
|
+
Prism::GlobalVariableWriteNode
|
105
|
+
!covers_position?(target.name_loc, position)
|
106
|
+
when Prism::CallNode
|
107
|
+
!covers_position?(target.message_loc, position)
|
108
|
+
else
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
92
112
|
end
|
93
113
|
end
|
94
114
|
end
|
@@ -72,7 +72,7 @@ module RubyLsp
|
|
72
72
|
|
73
73
|
# If the client doesn't support resource operations, such as renaming files, then we can only return the basic
|
74
74
|
# text changes
|
75
|
-
unless @global_state.
|
75
|
+
unless @global_state.client_capabilities.supports_rename?
|
76
76
|
return Interface::WorkspaceEdit.new(changes: changes)
|
77
77
|
end
|
78
78
|
|
@@ -26,7 +26,7 @@ module RubyLsp
|
|
26
26
|
).void
|
27
27
|
end
|
28
28
|
def delegate_request_if_needed!(global_state, document, char_position)
|
29
|
-
if global_state.supports_request_delegation &&
|
29
|
+
if global_state.client_capabilities.supports_request_delegation &&
|
30
30
|
document.is_a?(ERBDocument) &&
|
31
31
|
document.inside_host_language?(char_position)
|
32
32
|
raise DelegateRequestError
|
@@ -1,16 +1,27 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
# If there's no top level Gemfile, don't load RuboCop from a global installation
|
5
|
+
begin
|
6
|
+
Bundler.with_original_env { Bundler.default_gemfile }
|
7
|
+
rescue Bundler::GemfileNotFound
|
8
|
+
return
|
9
|
+
end
|
10
|
+
|
11
|
+
# Ensure that RuboCop is available
|
4
12
|
begin
|
5
13
|
require "rubocop"
|
6
14
|
rescue LoadError
|
7
15
|
return
|
8
16
|
end
|
9
17
|
|
18
|
+
# Remember to update the version in the documentation (usage/dependency-compatibility section) if you change this
|
19
|
+
# Ensure that RuboCop is at least version 1.4.0
|
10
20
|
begin
|
11
21
|
gem("rubocop", ">= 1.4.0")
|
12
22
|
rescue LoadError
|
13
|
-
|
23
|
+
$stderr.puts "Incompatible RuboCop version. Ruby LSP requires >= 1.4.0"
|
24
|
+
return
|
14
25
|
end
|
15
26
|
|
16
27
|
if RuboCop.const_defined?(:LSP) # This condition will be removed when requiring RuboCop >= 1.61.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
def compose(raw_initialize)
|
5
|
+
require_relative "../setup_bundler"
|
6
|
+
require "json"
|
7
|
+
require "uri"
|
8
|
+
require_relative "../../core_ext/uri"
|
9
|
+
|
10
|
+
initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
|
11
|
+
workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)
|
12
|
+
workspace_path = workspace_uri && URI(workspace_uri).to_standardized_path
|
13
|
+
workspace_path ||= Dir.pwd
|
14
|
+
|
15
|
+
env = RubyLsp::SetupBundler.new(workspace_path, launcher: true).setup!
|
16
|
+
File.write(
|
17
|
+
File.join(".ruby-lsp", "bundle_env"),
|
18
|
+
env.map { |k, v| "#{k}=#{v}" }.join("\n"),
|
19
|
+
)
|
20
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "compose_bundle"
|
5
|
+
|
6
|
+
# When this is invoked on Windows, we pass the raw initialize as an argument to this script. On other platforms, we
|
7
|
+
# invoke the compose method from inside a forked process
|
8
|
+
compose(ARGV.first)
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -106,6 +106,8 @@ module RubyLsp
|
|
106
106
|
)
|
107
107
|
when "$/cancelRequest"
|
108
108
|
@mutex.synchronize { @cancelled_requests << message[:params][:id] }
|
109
|
+
when nil
|
110
|
+
process_response(message) if message[:result]
|
109
111
|
end
|
110
112
|
rescue DelegateRequestError
|
111
113
|
send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
|
@@ -138,8 +140,22 @@ module RubyLsp
|
|
138
140
|
send_log_message("Error processing #{message[:method]}: #{e.full_message}", type: Constant::MessageType::ERROR)
|
139
141
|
end
|
140
142
|
|
143
|
+
# Process responses to requests that were sent to the client
|
144
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
145
|
+
def process_response(message)
|
146
|
+
case message.dig(:result, :method)
|
147
|
+
when "window/showMessageRequest"
|
148
|
+
window_show_message_request(message)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
141
152
|
sig { params(include_project_addons: T::Boolean).void }
|
142
153
|
def load_addons(include_project_addons: true)
|
154
|
+
# If invoking Bundler.setup failed, then the load path will not be configured properly and trying to load add-ons
|
155
|
+
# with Gem.find_files will find every single version installed of an add-on, leading to requiring several
|
156
|
+
# different versions of the same files. We cannot load add-ons if Bundler.setup failed
|
157
|
+
return if @setup_error
|
158
|
+
|
143
159
|
errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
|
144
160
|
|
145
161
|
if errors.any?
|
@@ -181,8 +197,6 @@ module RubyLsp
|
|
181
197
|
client_name = options.dig(:clientInfo, :name)
|
182
198
|
@store.client_name = client_name if client_name
|
183
199
|
|
184
|
-
progress = options.dig(:capabilities, :window, :workDoneProgress)
|
185
|
-
@store.supports_progress = progress.nil? ? true : progress
|
186
200
|
configured_features = options.dig(:initializationOptions, :enabledFeatures)
|
187
201
|
|
188
202
|
configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
|
@@ -202,6 +216,13 @@ module RubyLsp
|
|
202
216
|
Hash.new(true)
|
203
217
|
end
|
204
218
|
|
219
|
+
bundle_env_path = File.join(".ruby-lsp", "bundle_env")
|
220
|
+
bundle_env = if File.exist?(bundle_env_path)
|
221
|
+
env = File.readlines(bundle_env_path).to_h { |line| T.cast(line.chomp.split("=", 2), [String, String]) }
|
222
|
+
FileUtils.rm(bundle_env_path)
|
223
|
+
env
|
224
|
+
end
|
225
|
+
|
205
226
|
document_symbol_provider = Requests::DocumentSymbol.provider if enabled_features["documentSymbols"]
|
206
227
|
document_link_provider = Requests::DocumentLink.provider if enabled_features["documentLink"]
|
207
228
|
code_lens_provider = Requests::CodeLens.provider if enabled_features["codeLens"]
|
@@ -254,12 +275,14 @@ module RubyLsp
|
|
254
275
|
version: VERSION,
|
255
276
|
},
|
256
277
|
formatter: @global_state.formatter,
|
278
|
+
degraded_mode: !!(@install_error || @setup_error),
|
279
|
+
bundle_env: bundle_env,
|
257
280
|
}
|
258
281
|
|
259
282
|
send_message(Result.new(id: message[:id], response: response))
|
260
283
|
|
261
284
|
# Not every client supports dynamic registration or file watching
|
262
|
-
if global_state.supports_watching_files
|
285
|
+
if @global_state.client_capabilities.supports_watching_files
|
263
286
|
send_message(
|
264
287
|
Request.new(
|
265
288
|
id: @current_request_id,
|
@@ -290,6 +313,24 @@ module RubyLsp
|
|
290
313
|
begin_progress("indexing-progress", "Ruby LSP: indexing files")
|
291
314
|
|
292
315
|
global_state_notifications.each { |notification| send_message(notification) }
|
316
|
+
|
317
|
+
if @setup_error
|
318
|
+
send_message(Notification.telemetry(
|
319
|
+
type: "error",
|
320
|
+
errorMessage: @setup_error.message,
|
321
|
+
errorClass: @setup_error.class,
|
322
|
+
stack: @setup_error.backtrace&.join("\n"),
|
323
|
+
))
|
324
|
+
end
|
325
|
+
|
326
|
+
if @install_error
|
327
|
+
send_message(Notification.telemetry(
|
328
|
+
type: "error",
|
329
|
+
errorMessage: @install_error.message,
|
330
|
+
errorClass: @install_error.class,
|
331
|
+
stack: @install_error.backtrace&.join("\n"),
|
332
|
+
))
|
333
|
+
end
|
293
334
|
end
|
294
335
|
|
295
336
|
sig { void }
|
@@ -297,20 +338,22 @@ module RubyLsp
|
|
297
338
|
load_addons
|
298
339
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
299
340
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
341
|
+
unless @setup_error
|
342
|
+
if defined?(Requests::Support::RuboCopFormatter)
|
343
|
+
begin
|
344
|
+
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
|
345
|
+
rescue RuboCop::Error => e
|
346
|
+
# The user may have provided unknown config switches in .rubocop or
|
347
|
+
# is trying to load a non-existent config file.
|
348
|
+
send_message(Notification.window_show_message(
|
349
|
+
"RuboCop configuration error: #{e.message}. Formatting will not be available.",
|
350
|
+
type: Constant::MessageType::ERROR,
|
351
|
+
))
|
352
|
+
end
|
353
|
+
end
|
354
|
+
if defined?(Requests::Support::SyntaxTreeFormatter)
|
355
|
+
@global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
|
310
356
|
end
|
311
|
-
end
|
312
|
-
if defined?(Requests::Support::SyntaxTreeFormatter)
|
313
|
-
@global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
|
314
357
|
end
|
315
358
|
|
316
359
|
perform_initial_indexing
|
@@ -569,6 +612,11 @@ module RubyLsp
|
|
569
612
|
# don't want to format it
|
570
613
|
path = uri.to_standardized_path
|
571
614
|
unless path.nil? || path.start_with?(@global_state.workspace_path)
|
615
|
+
send_log_message(<<~MESSAGE)
|
616
|
+
Ignoring formatting request for file outside of the workspace.
|
617
|
+
Workspace path was set by editor as #{@global_state.workspace_path}.
|
618
|
+
File path requested for formatting was #{path}
|
619
|
+
MESSAGE
|
572
620
|
send_empty_response(message[:id])
|
573
621
|
return
|
574
622
|
end
|
@@ -1017,7 +1065,7 @@ module RubyLsp
|
|
1017
1065
|
|
1018
1066
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
1019
1067
|
def workspace_dependencies(message)
|
1020
|
-
response =
|
1068
|
+
response = if @global_state.top_level_bundle
|
1021
1069
|
Bundler.with_original_env do
|
1022
1070
|
definition = Bundler.definition
|
1023
1071
|
dep_keys = definition.locked_deps.keys.to_set
|
@@ -1031,7 +1079,7 @@ module RubyLsp
|
|
1031
1079
|
}
|
1032
1080
|
end
|
1033
1081
|
end
|
1034
|
-
|
1082
|
+
else
|
1035
1083
|
[]
|
1036
1084
|
end
|
1037
1085
|
|
@@ -1077,7 +1125,7 @@ module RubyLsp
|
|
1077
1125
|
|
1078
1126
|
sig { params(id: String, title: String, percentage: Integer).void }
|
1079
1127
|
def begin_progress(id, title, percentage: 0)
|
1080
|
-
return unless @
|
1128
|
+
return unless @global_state.client_capabilities.supports_progress
|
1081
1129
|
|
1082
1130
|
send_message(Request.new(
|
1083
1131
|
id: @current_request_id,
|
@@ -1085,52 +1133,21 @@ module RubyLsp
|
|
1085
1133
|
params: Interface::WorkDoneProgressCreateParams.new(token: id),
|
1086
1134
|
))
|
1087
1135
|
|
1088
|
-
send_message(Notification.
|
1089
|
-
method: "$/progress",
|
1090
|
-
params: Interface::ProgressParams.new(
|
1091
|
-
token: id,
|
1092
|
-
value: Interface::WorkDoneProgressBegin.new(
|
1093
|
-
kind: "begin",
|
1094
|
-
title: title,
|
1095
|
-
percentage: percentage,
|
1096
|
-
message: "#{percentage}% completed",
|
1097
|
-
),
|
1098
|
-
),
|
1099
|
-
))
|
1136
|
+
send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
|
1100
1137
|
end
|
1101
1138
|
|
1102
1139
|
sig { params(id: String, percentage: Integer).void }
|
1103
1140
|
def progress(id, percentage)
|
1104
|
-
return unless @
|
1141
|
+
return unless @global_state.client_capabilities.supports_progress
|
1105
1142
|
|
1106
|
-
send_message(
|
1107
|
-
Notification.new(
|
1108
|
-
method: "$/progress",
|
1109
|
-
params: Interface::ProgressParams.new(
|
1110
|
-
token: id,
|
1111
|
-
value: Interface::WorkDoneProgressReport.new(
|
1112
|
-
kind: "report",
|
1113
|
-
percentage: percentage,
|
1114
|
-
message: "#{percentage}% completed",
|
1115
|
-
),
|
1116
|
-
),
|
1117
|
-
),
|
1118
|
-
)
|
1143
|
+
send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
|
1119
1144
|
end
|
1120
1145
|
|
1121
1146
|
sig { params(id: String).void }
|
1122
1147
|
def end_progress(id)
|
1123
|
-
return unless @
|
1148
|
+
return unless @global_state.client_capabilities.supports_progress
|
1124
1149
|
|
1125
|
-
send_message(
|
1126
|
-
Notification.new(
|
1127
|
-
method: "$/progress",
|
1128
|
-
params: Interface::ProgressParams.new(
|
1129
|
-
token: id,
|
1130
|
-
value: Interface::WorkDoneProgressEnd.new(kind: "end"),
|
1131
|
-
),
|
1132
|
-
),
|
1133
|
-
)
|
1150
|
+
send_message(Notification.progress_end(id))
|
1134
1151
|
rescue ClosedQueueError
|
1135
1152
|
# If the server was killed and the message queue is already closed, there's no way to end the progress
|
1136
1153
|
# notification
|
@@ -1138,6 +1155,7 @@ module RubyLsp
|
|
1138
1155
|
|
1139
1156
|
sig { void }
|
1140
1157
|
def check_formatter_is_available
|
1158
|
+
return if @setup_error
|
1141
1159
|
# Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
|
1142
1160
|
# Syntax Tree will always be available via Ruby LSP so we don't need to check for it.
|
1143
1161
|
return unless @global_state.formatter == "rubocop"
|
@@ -1194,5 +1212,17 @@ module RubyLsp
|
|
1194
1212
|
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
1195
1213
|
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
|
1196
1214
|
end
|
1215
|
+
|
1216
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
1217
|
+
def window_show_message_request(message)
|
1218
|
+
result = message[:result]
|
1219
|
+
return unless result
|
1220
|
+
|
1221
|
+
addon_name = result[:addon_name]
|
1222
|
+
addon = Addon.addons.find { |addon| addon.name == addon_name }
|
1223
|
+
return unless addon
|
1224
|
+
|
1225
|
+
addon.handle_window_show_message_response(result[:title])
|
1226
|
+
end
|
1197
1227
|
end
|
1198
1228
|
end
|