ruby-lsp 0.17.7 → 0.17.9
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 +6 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +25 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +16 -3
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +28 -0
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +14 -26
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +38 -0
- data/lib/ruby_indexer/test/index_test.rb +15 -0
- data/lib/ruby_lsp/base_server.rb +1 -1
- data/lib/ruby_lsp/document.rb +27 -4
- data/lib/ruby_lsp/global_state.rb +2 -1
- data/lib/ruby_lsp/listeners/completion.rb +118 -37
- data/lib/ruby_lsp/listeners/definition.rb +23 -11
- data/lib/ruby_lsp/listeners/hover.rb +21 -9
- data/lib/ruby_lsp/listeners/signature_help.rb +13 -6
- data/lib/ruby_lsp/requests/completion.rb +3 -3
- data/lib/ruby_lsp/requests/completion_resolve.rb +10 -3
- data/lib/ruby_lsp/requests/definition.rb +3 -3
- data/lib/ruby_lsp/requests/hover.rb +3 -3
- data/lib/ruby_lsp/requests/signature_help.rb +3 -3
- data/lib/ruby_lsp/requests/support/common.rb +11 -2
- data/lib/ruby_lsp/server.rb +76 -44
- data/lib/ruby_lsp/store.rb +10 -1
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +48 -10
- data/lib/ruby_lsp/utils.rb +1 -0
- metadata +3 -5
- data/exe/ruby-lsp-doctor +0 -23
@@ -20,10 +20,10 @@ module RubyLsp
|
|
20
20
|
uri: URI::Generic,
|
21
21
|
node_context: NodeContext,
|
22
22
|
dispatcher: Prism::Dispatcher,
|
23
|
-
|
23
|
+
sorbet_level: Document::SorbetLevel,
|
24
24
|
).void
|
25
25
|
end
|
26
|
-
def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher,
|
26
|
+
def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
27
27
|
@response_builder = response_builder
|
28
28
|
@global_state = global_state
|
29
29
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
@@ -31,7 +31,7 @@ module RubyLsp
|
|
31
31
|
@language_id = language_id
|
32
32
|
@uri = uri
|
33
33
|
@node_context = node_context
|
34
|
-
@
|
34
|
+
@sorbet_level = sorbet_level
|
35
35
|
|
36
36
|
dispatcher.register(
|
37
37
|
self,
|
@@ -53,6 +53,11 @@ module RubyLsp
|
|
53
53
|
|
54
54
|
sig { params(node: Prism::CallNode).void }
|
55
55
|
def on_call_node_enter(node)
|
56
|
+
# Sorbet can handle go to definition for methods invoked on self on typed true or higher
|
57
|
+
if (@sorbet_level == Document::SorbetLevel::True || @sorbet_level == Document::SorbetLevel::Strict) &&
|
58
|
+
self_receiver?(node)
|
59
|
+
end
|
60
|
+
|
56
61
|
message = node.message
|
57
62
|
return unless message
|
58
63
|
|
@@ -60,7 +65,7 @@ module RubyLsp
|
|
60
65
|
|
61
66
|
# Until we can properly infer the receiver type in erb files (maybe with ruby-lsp-rails),
|
62
67
|
# treating method calls' type as `nil` will allow users to get some completion support first
|
63
|
-
if @language_id == Document::LanguageId::ERB && inferrer_receiver_type == "Object"
|
68
|
+
if @language_id == Document::LanguageId::ERB && inferrer_receiver_type&.name == "Object"
|
64
69
|
inferrer_receiver_type = nil
|
65
70
|
end
|
66
71
|
|
@@ -149,6 +154,9 @@ module RubyLsp
|
|
149
154
|
|
150
155
|
sig { void }
|
151
156
|
def handle_super_node_definition
|
157
|
+
# Sorbet can handle super hover on typed true or higher
|
158
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
159
|
+
|
152
160
|
surrounding_method = @node_context.surrounding_method
|
153
161
|
return unless surrounding_method
|
154
162
|
|
@@ -161,10 +169,14 @@ module RubyLsp
|
|
161
169
|
|
162
170
|
sig { params(name: String).void }
|
163
171
|
def handle_instance_variable_definition(name)
|
172
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
173
|
+
# to provide all features for them
|
174
|
+
return if @sorbet_level == Document::SorbetLevel::Strict
|
175
|
+
|
164
176
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
165
177
|
return unless type
|
166
178
|
|
167
|
-
entries = @index.resolve_instance_variable(name, type)
|
179
|
+
entries = @index.resolve_instance_variable(name, type.name)
|
168
180
|
return unless entries
|
169
181
|
|
170
182
|
entries.each do |entry|
|
@@ -182,10 +194,10 @@ module RubyLsp
|
|
182
194
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
183
195
|
end
|
184
196
|
|
185
|
-
sig { params(message: String, receiver_type: T.nilable(
|
197
|
+
sig { params(message: String, receiver_type: T.nilable(TypeInferrer::Type), inherited_only: T::Boolean).void }
|
186
198
|
def handle_method_definition(message, receiver_type, inherited_only: false)
|
187
199
|
methods = if receiver_type
|
188
|
-
@index.resolve_method(message, receiver_type, inherited_only: inherited_only)
|
200
|
+
@index.resolve_method(message, receiver_type.name, inherited_only: inherited_only)
|
189
201
|
else
|
190
202
|
# If the method doesn't have a receiver, then we provide a few candidates to jump to
|
191
203
|
# But we don't want to provide too many candidates, as it can be overwhelming
|
@@ -196,7 +208,7 @@ module RubyLsp
|
|
196
208
|
|
197
209
|
methods.each do |target_method|
|
198
210
|
file_path = target_method.file_path
|
199
|
-
next if @
|
211
|
+
next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(file_path)
|
200
212
|
|
201
213
|
@response_builder << Interface::LocationLink.new(
|
202
214
|
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -253,10 +265,10 @@ module RubyLsp
|
|
253
265
|
|
254
266
|
entries.each do |entry|
|
255
267
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
256
|
-
# additional behavior on top of jumping to RBIs.
|
257
|
-
#
|
268
|
+
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
269
|
+
# ignore
|
258
270
|
file_path = entry.file_path
|
259
|
-
next if @
|
271
|
+
next if @sorbet_level != Document::SorbetLevel::Ignore && not_in_dependencies?(file_path)
|
260
272
|
|
261
273
|
@response_builder << Interface::LocationLink.new(
|
262
274
|
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -42,17 +42,17 @@ module RubyLsp
|
|
42
42
|
uri: URI::Generic,
|
43
43
|
node_context: NodeContext,
|
44
44
|
dispatcher: Prism::Dispatcher,
|
45
|
-
|
45
|
+
sorbet_level: Document::SorbetLevel,
|
46
46
|
).void
|
47
47
|
end
|
48
|
-
def initialize(response_builder, global_state, uri, node_context, dispatcher,
|
48
|
+
def initialize(response_builder, global_state, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
49
49
|
@response_builder = response_builder
|
50
50
|
@global_state = global_state
|
51
51
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
52
52
|
@type_inferrer = T.let(global_state.type_inferrer, TypeInferrer)
|
53
53
|
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
54
54
|
@node_context = node_context
|
55
|
-
@
|
55
|
+
@sorbet_level = sorbet_level
|
56
56
|
|
57
57
|
dispatcher.register(
|
58
58
|
self,
|
@@ -73,7 +73,7 @@ module RubyLsp
|
|
73
73
|
|
74
74
|
sig { params(node: Prism::ConstantReadNode).void }
|
75
75
|
def on_constant_read_node_enter(node)
|
76
|
-
return if @
|
76
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
77
77
|
|
78
78
|
name = constant_name(node)
|
79
79
|
return if name.nil?
|
@@ -83,14 +83,14 @@ module RubyLsp
|
|
83
83
|
|
84
84
|
sig { params(node: Prism::ConstantWriteNode).void }
|
85
85
|
def on_constant_write_node_enter(node)
|
86
|
-
return if @
|
86
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
87
87
|
|
88
88
|
generate_hover(node.name.to_s, node.name_loc)
|
89
89
|
end
|
90
90
|
|
91
91
|
sig { params(node: Prism::ConstantPathNode).void }
|
92
92
|
def on_constant_path_node_enter(node)
|
93
|
-
return if @
|
93
|
+
return if @sorbet_level != Document::SorbetLevel::Ignore
|
94
94
|
|
95
95
|
name = constant_name(node)
|
96
96
|
return if name.nil?
|
@@ -105,7 +105,7 @@ module RubyLsp
|
|
105
105
|
return
|
106
106
|
end
|
107
107
|
|
108
|
-
return if @
|
108
|
+
return if sorbet_level_true_or_higher?(@sorbet_level) && self_receiver?(node)
|
109
109
|
|
110
110
|
message = node.message
|
111
111
|
return unless message
|
@@ -157,6 +157,9 @@ module RubyLsp
|
|
157
157
|
|
158
158
|
sig { void }
|
159
159
|
def handle_super_node_hover
|
160
|
+
# Sorbet can handle super hover on typed true or higher
|
161
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
162
|
+
|
160
163
|
surrounding_method = @node_context.surrounding_method
|
161
164
|
return unless surrounding_method
|
162
165
|
|
@@ -168,11 +171,16 @@ module RubyLsp
|
|
168
171
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
169
172
|
return unless type
|
170
173
|
|
171
|
-
methods = @index.resolve_method(message, type, inherited_only: inherited_only)
|
174
|
+
methods = @index.resolve_method(message, type.name, inherited_only: inherited_only)
|
172
175
|
return unless methods
|
173
176
|
|
174
177
|
title = "#{message}#{T.must(methods.first).decorated_parameters}"
|
175
178
|
|
179
|
+
if type.is_a?(TypeInferrer::GuessedType)
|
180
|
+
title << "\n\nGuessed receiver: #{type.name}"
|
181
|
+
@response_builder.push("[Learn more about guessed types](#{GUESSED_TYPES_URL})\n", category: :links)
|
182
|
+
end
|
183
|
+
|
176
184
|
categorized_markdown_from_index_entries(title, methods).each do |category, content|
|
177
185
|
@response_builder.push(content, category: category)
|
178
186
|
end
|
@@ -180,10 +188,14 @@ module RubyLsp
|
|
180
188
|
|
181
189
|
sig { params(name: String).void }
|
182
190
|
def handle_instance_variable_hover(name)
|
191
|
+
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
192
|
+
# to provide all features for them
|
193
|
+
return if @sorbet_level == Document::SorbetLevel::Strict
|
194
|
+
|
183
195
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
184
196
|
return unless type
|
185
197
|
|
186
|
-
entries = @index.resolve_instance_variable(name, type)
|
198
|
+
entries = @index.resolve_instance_variable(name, type.name)
|
187
199
|
return unless entries
|
188
200
|
|
189
201
|
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
@@ -13,11 +13,11 @@ module RubyLsp
|
|
13
13
|
global_state: GlobalState,
|
14
14
|
node_context: NodeContext,
|
15
15
|
dispatcher: Prism::Dispatcher,
|
16
|
-
|
16
|
+
sorbet_level: Document::SorbetLevel,
|
17
17
|
).void
|
18
18
|
end
|
19
|
-
def initialize(response_builder, global_state, node_context, dispatcher,
|
20
|
-
@
|
19
|
+
def initialize(response_builder, global_state, node_context, dispatcher, sorbet_level)
|
20
|
+
@sorbet_level = sorbet_level
|
21
21
|
@response_builder = response_builder
|
22
22
|
@global_state = global_state
|
23
23
|
@index = T.let(global_state.index, RubyIndexer::Index)
|
@@ -28,7 +28,7 @@ module RubyLsp
|
|
28
28
|
|
29
29
|
sig { params(node: Prism::CallNode).void }
|
30
30
|
def on_call_node_enter(node)
|
31
|
-
return if @
|
31
|
+
return if sorbet_level_true_or_higher?(@sorbet_level)
|
32
32
|
|
33
33
|
message = node.message
|
34
34
|
return unless message
|
@@ -36,7 +36,7 @@ module RubyLsp
|
|
36
36
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
37
37
|
return unless type
|
38
38
|
|
39
|
-
methods = @index.resolve_method(message, type)
|
39
|
+
methods = @index.resolve_method(message, type.name)
|
40
40
|
return unless methods
|
41
41
|
|
42
42
|
target_method = methods.first
|
@@ -61,6 +61,13 @@ module RubyLsp
|
|
61
61
|
active_parameter += 1
|
62
62
|
end
|
63
63
|
|
64
|
+
title = +""
|
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
|
70
|
+
|
64
71
|
signature_help = Interface::SignatureHelp.new(
|
65
72
|
signatures: [
|
66
73
|
Interface::SignatureInformation.new(
|
@@ -68,7 +75,7 @@ module RubyLsp
|
|
68
75
|
parameters: parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
|
69
76
|
documentation: Interface::MarkupContent.new(
|
70
77
|
kind: "markdown",
|
71
|
-
value: markdown_from_index_entries(
|
78
|
+
value: markdown_from_index_entries(title, methods, extra_links: extra_links),
|
72
79
|
),
|
73
80
|
),
|
74
81
|
],
|
@@ -49,11 +49,11 @@ module RubyLsp
|
|
49
49
|
document: Document,
|
50
50
|
global_state: GlobalState,
|
51
51
|
params: T::Hash[Symbol, T.untyped],
|
52
|
-
|
52
|
+
sorbet_level: Document::SorbetLevel,
|
53
53
|
dispatcher: Prism::Dispatcher,
|
54
54
|
).void
|
55
55
|
end
|
56
|
-
def initialize(document, global_state, params,
|
56
|
+
def initialize(document, global_state, params, sorbet_level, dispatcher)
|
57
57
|
super()
|
58
58
|
@target = T.let(nil, T.nilable(Prism::Node))
|
59
59
|
@dispatcher = dispatcher
|
@@ -84,7 +84,7 @@ module RubyLsp
|
|
84
84
|
@response_builder,
|
85
85
|
global_state,
|
86
86
|
node_context,
|
87
|
-
|
87
|
+
sorbet_level,
|
88
88
|
dispatcher,
|
89
89
|
document.uri,
|
90
90
|
params.dig(:context, :triggerCharacter),
|
@@ -47,7 +47,7 @@ module RubyLsp
|
|
47
47
|
#
|
48
48
|
# For example, forgetting to return the `insertText` included in the original item will make the editor use the
|
49
49
|
# `label` for the text edit instead
|
50
|
-
label = @item[:label]
|
50
|
+
label = @item[:label].dup
|
51
51
|
entries = @index[label] || []
|
52
52
|
|
53
53
|
owner_name = @item.dig(:data, :owner_name)
|
@@ -62,12 +62,19 @@ module RubyLsp
|
|
62
62
|
first_entry = T.must(entries.first)
|
63
63
|
|
64
64
|
if first_entry.is_a?(RubyIndexer::Entry::Member)
|
65
|
-
label = "#{label}#{first_entry.decorated_parameters}"
|
65
|
+
label = +"#{label}#{first_entry.decorated_parameters}"
|
66
|
+
end
|
67
|
+
|
68
|
+
guessed_type = @item.dig(:data, :guessed_type)
|
69
|
+
|
70
|
+
extra_links = if guessed_type
|
71
|
+
label << "\n\nGuessed receiver: #{guessed_type}"
|
72
|
+
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
66
73
|
end
|
67
74
|
|
68
75
|
@item[:documentation] = Interface::MarkupContent.new(
|
69
76
|
kind: "markdown",
|
70
|
-
value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES),
|
77
|
+
value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES, extra_links: extra_links),
|
71
78
|
)
|
72
79
|
|
73
80
|
@item
|
@@ -36,10 +36,10 @@ module RubyLsp
|
|
36
36
|
global_state: GlobalState,
|
37
37
|
position: T::Hash[Symbol, T.untyped],
|
38
38
|
dispatcher: Prism::Dispatcher,
|
39
|
-
|
39
|
+
sorbet_level: Document::SorbetLevel,
|
40
40
|
).void
|
41
41
|
end
|
42
|
-
def initialize(document, global_state, position, dispatcher,
|
42
|
+
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
43
43
|
super()
|
44
44
|
@response_builder = T.let(
|
45
45
|
ResponseBuilders::CollectionResponseBuilder[T.any(Interface::Location, Interface::LocationLink)].new,
|
@@ -96,7 +96,7 @@ module RubyLsp
|
|
96
96
|
document.uri,
|
97
97
|
node_context,
|
98
98
|
dispatcher,
|
99
|
-
|
99
|
+
sorbet_level,
|
100
100
|
)
|
101
101
|
|
102
102
|
Addon.addons.each do |addon|
|
@@ -36,10 +36,10 @@ module RubyLsp
|
|
36
36
|
global_state: GlobalState,
|
37
37
|
position: T::Hash[Symbol, T.untyped],
|
38
38
|
dispatcher: Prism::Dispatcher,
|
39
|
-
|
39
|
+
sorbet_level: Document::SorbetLevel,
|
40
40
|
).void
|
41
41
|
end
|
42
|
-
def initialize(document, global_state, position, dispatcher,
|
42
|
+
def initialize(document, global_state, position, dispatcher, sorbet_level)
|
43
43
|
super()
|
44
44
|
node_context = document.locate_node(position, node_types: Listeners::Hover::ALLOWED_TARGETS)
|
45
45
|
target = node_context.node
|
@@ -65,7 +65,7 @@ module RubyLsp
|
|
65
65
|
@target = T.let(target, T.nilable(Prism::Node))
|
66
66
|
uri = document.uri
|
67
67
|
@response_builder = T.let(ResponseBuilders::Hover.new, ResponseBuilders::Hover)
|
68
|
-
Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher,
|
68
|
+
Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, sorbet_level)
|
69
69
|
Addon.addons.each do |addon|
|
70
70
|
addon.create_hover_listener(@response_builder, node_context, dispatcher)
|
71
71
|
end
|
@@ -46,10 +46,10 @@ module RubyLsp
|
|
46
46
|
position: T::Hash[Symbol, T.untyped],
|
47
47
|
context: T.nilable(T::Hash[Symbol, T.untyped]),
|
48
48
|
dispatcher: Prism::Dispatcher,
|
49
|
-
|
49
|
+
sorbet_level: Document::SorbetLevel,
|
50
50
|
).void
|
51
51
|
end
|
52
|
-
def initialize(document, global_state, position, context, dispatcher,
|
52
|
+
def initialize(document, global_state, position, context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
53
53
|
super()
|
54
54
|
node_context = document.locate_node(
|
55
55
|
{ line: position[:line], character: position[:character] },
|
@@ -61,7 +61,7 @@ module RubyLsp
|
|
61
61
|
@target = T.let(target, T.nilable(Prism::Node))
|
62
62
|
@dispatcher = dispatcher
|
63
63
|
@response_builder = T.let(ResponseBuilders::SignatureHelp.new, ResponseBuilders::SignatureHelp)
|
64
|
-
Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher,
|
64
|
+
Listeners::SignatureHelp.new(@response_builder, global_state, node_context, dispatcher, sorbet_level)
|
65
65
|
end
|
66
66
|
|
67
67
|
sig { override.returns(T.nilable(Interface::SignatureHelp)) }
|
@@ -130,13 +130,17 @@ module RubyLsp
|
|
130
130
|
title: String,
|
131
131
|
entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
|
132
132
|
max_entries: T.nilable(Integer),
|
133
|
+
extra_links: T.nilable(String),
|
133
134
|
).returns(String)
|
134
135
|
end
|
135
|
-
def markdown_from_index_entries(title, entries, max_entries = nil)
|
136
|
+
def markdown_from_index_entries(title, entries, max_entries = nil, extra_links: nil)
|
136
137
|
categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
|
137
138
|
|
139
|
+
markdown = +(categorized_markdown[:title] || "")
|
140
|
+
markdown << "\n\n#{extra_links}" if extra_links
|
141
|
+
|
138
142
|
<<~MARKDOWN.chomp
|
139
|
-
#{
|
143
|
+
#{markdown}
|
140
144
|
|
141
145
|
#{categorized_markdown[:links]}
|
142
146
|
|
@@ -204,6 +208,11 @@ module RubyLsp
|
|
204
208
|
Constant::SymbolKind::FIELD
|
205
209
|
end
|
206
210
|
end
|
211
|
+
|
212
|
+
sig { params(sorbet_level: Document::SorbetLevel).returns(T::Boolean) }
|
213
|
+
def sorbet_level_true_or_higher?(sorbet_level)
|
214
|
+
sorbet_level == Document::SorbetLevel::True || sorbet_level == Document::SorbetLevel::Strict
|
215
|
+
end
|
207
216
|
end
|
208
217
|
end
|
209
218
|
end
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -98,16 +98,27 @@ module RubyLsp
|
|
98
98
|
rescue StandardError, LoadError => e
|
99
99
|
# If an error occurred in a request, we have to return an error response or else the editor will hang
|
100
100
|
if message[:id]
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
101
|
+
# If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
|
102
|
+
# from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
|
103
|
+
# reporting these to our telemetry
|
104
|
+
if e.is_a?(Store::NonExistingDocumentError)
|
105
|
+
send_message(Error.new(
|
106
|
+
id: message[:id],
|
107
|
+
code: Constant::ErrorCodes::INVALID_PARAMS,
|
108
|
+
message: e.full_message,
|
109
|
+
))
|
110
|
+
else
|
111
|
+
send_message(Error.new(
|
112
|
+
id: message[:id],
|
113
|
+
code: Constant::ErrorCodes::INTERNAL_ERROR,
|
114
|
+
message: e.full_message,
|
115
|
+
data: {
|
116
|
+
errorClass: e.class.name,
|
117
|
+
errorMessage: e.message,
|
118
|
+
backtrace: e.backtrace&.join("\n"),
|
119
|
+
},
|
120
|
+
))
|
121
|
+
end
|
111
122
|
end
|
112
123
|
|
113
124
|
$stderr.puts("Error processing #{message[:method]}: #{e.full_message}")
|
@@ -243,6 +254,8 @@ module RubyLsp
|
|
243
254
|
)
|
244
255
|
end
|
245
256
|
|
257
|
+
process_indexing_configuration(options.dig(:initializationOptions, :indexing))
|
258
|
+
|
246
259
|
begin_progress("indexing-progress", "Ruby LSP: indexing files")
|
247
260
|
end
|
248
261
|
|
@@ -251,28 +264,6 @@ module RubyLsp
|
|
251
264
|
load_addons
|
252
265
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
253
266
|
|
254
|
-
indexing_config = {}
|
255
|
-
|
256
|
-
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
|
257
|
-
index_path = File.join(@global_state.workspace_path, ".index.yml")
|
258
|
-
|
259
|
-
if File.exist?(index_path)
|
260
|
-
begin
|
261
|
-
indexing_config = YAML.parse_file(index_path).to_ruby
|
262
|
-
rescue Psych::SyntaxError => e
|
263
|
-
message = "Syntax error while loading configuration: #{e.message}"
|
264
|
-
send_message(
|
265
|
-
Notification.new(
|
266
|
-
method: "window/showMessage",
|
267
|
-
params: Interface::ShowMessageParams.new(
|
268
|
-
type: Constant::MessageType::WARNING,
|
269
|
-
message: message,
|
270
|
-
),
|
271
|
-
),
|
272
|
-
)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
267
|
if defined?(Requests::Support::RuboCopFormatter)
|
277
268
|
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
|
278
269
|
end
|
@@ -280,7 +271,7 @@ module RubyLsp
|
|
280
271
|
@global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
|
281
272
|
end
|
282
273
|
|
283
|
-
perform_initial_indexing
|
274
|
+
perform_initial_indexing
|
284
275
|
check_formatter_is_available
|
285
276
|
end
|
286
277
|
|
@@ -486,15 +477,17 @@ module RubyLsp
|
|
486
477
|
@global_state,
|
487
478
|
params[:position],
|
488
479
|
dispatcher,
|
489
|
-
|
480
|
+
sorbet_level(document),
|
490
481
|
).perform,
|
491
482
|
),
|
492
483
|
)
|
493
484
|
end
|
494
485
|
|
495
|
-
sig { params(document: Document).returns(
|
496
|
-
def
|
497
|
-
@global_state.has_type_checker
|
486
|
+
sig { params(document: Document).returns(Document::SorbetLevel) }
|
487
|
+
def sorbet_level(document)
|
488
|
+
return Document::SorbetLevel::Ignore unless @global_state.has_type_checker
|
489
|
+
|
490
|
+
document.sorbet_level
|
498
491
|
end
|
499
492
|
|
500
493
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
@@ -603,7 +596,7 @@ module RubyLsp
|
|
603
596
|
document,
|
604
597
|
@global_state,
|
605
598
|
params,
|
606
|
-
|
599
|
+
sorbet_level(document),
|
607
600
|
dispatcher,
|
608
601
|
).perform,
|
609
602
|
),
|
@@ -633,7 +626,7 @@ module RubyLsp
|
|
633
626
|
params[:position],
|
634
627
|
params[:context],
|
635
628
|
dispatcher,
|
636
|
-
|
629
|
+
sorbet_level(document),
|
637
630
|
).perform,
|
638
631
|
),
|
639
632
|
)
|
@@ -653,7 +646,7 @@ module RubyLsp
|
|
653
646
|
@global_state,
|
654
647
|
params[:position],
|
655
648
|
dispatcher,
|
656
|
-
|
649
|
+
sorbet_level(document),
|
657
650
|
).perform,
|
658
651
|
),
|
659
652
|
)
|
@@ -766,12 +759,10 @@ module RubyLsp
|
|
766
759
|
Addon.addons.each(&:deactivate)
|
767
760
|
end
|
768
761
|
|
769
|
-
sig {
|
770
|
-
def perform_initial_indexing
|
762
|
+
sig { void }
|
763
|
+
def perform_initial_indexing
|
771
764
|
# The begin progress invocation happens during `initialize`, so that the notification is sent before we are
|
772
765
|
# stuck indexing files
|
773
|
-
RubyIndexer.configuration.apply_config(config_hash)
|
774
|
-
|
775
766
|
Thread.new do
|
776
767
|
begin
|
777
768
|
RubyIndexer::RBSIndexer.new(@global_state.index).index_ruby_core
|
@@ -872,5 +863,46 @@ module RubyLsp
|
|
872
863
|
)
|
873
864
|
end
|
874
865
|
end
|
866
|
+
|
867
|
+
sig { params(indexing_options: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
868
|
+
def process_indexing_configuration(indexing_options)
|
869
|
+
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
|
870
|
+
index_path = File.join(@global_state.workspace_path, ".index.yml")
|
871
|
+
|
872
|
+
if File.exist?(index_path)
|
873
|
+
begin
|
874
|
+
RubyIndexer.configuration.apply_config(YAML.parse_file(index_path).to_ruby)
|
875
|
+
send_message(
|
876
|
+
Notification.new(
|
877
|
+
method: "window/showMessage",
|
878
|
+
params: Interface::ShowMessageParams.new(
|
879
|
+
type: Constant::MessageType::WARNING,
|
880
|
+
message: "The .index.yml configuration file is deprecated. " \
|
881
|
+
"Please use editor settings to configure the index",
|
882
|
+
),
|
883
|
+
),
|
884
|
+
)
|
885
|
+
rescue Psych::SyntaxError => e
|
886
|
+
message = "Syntax error while loading configuration: #{e.message}"
|
887
|
+
send_message(
|
888
|
+
Notification.new(
|
889
|
+
method: "window/showMessage",
|
890
|
+
params: Interface::ShowMessageParams.new(
|
891
|
+
type: Constant::MessageType::WARNING,
|
892
|
+
message: message,
|
893
|
+
),
|
894
|
+
),
|
895
|
+
)
|
896
|
+
end
|
897
|
+
return
|
898
|
+
end
|
899
|
+
|
900
|
+
return unless indexing_options
|
901
|
+
|
902
|
+
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
903
|
+
RubyIndexer.configuration.apply_config(
|
904
|
+
indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase },
|
905
|
+
)
|
906
|
+
end
|
875
907
|
end
|
876
908
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -5,6 +5,8 @@ module RubyLsp
|
|
5
5
|
class Store
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
class NonExistingDocumentError < StandardError; end
|
9
|
+
|
8
10
|
sig { returns(T::Boolean) }
|
9
11
|
attr_accessor :supports_progress
|
10
12
|
|
@@ -36,15 +38,22 @@ module RubyLsp
|
|
36
38
|
document = @state[uri.to_s]
|
37
39
|
return document unless document.nil?
|
38
40
|
|
39
|
-
path
|
41
|
+
# For unsaved files (`untitled:Untitled-1` uris), there's no path to read from. If we don't have the untitled file
|
42
|
+
# already present in the store, then we have to raise non existing document error
|
43
|
+
path = uri.to_standardized_path
|
44
|
+
raise NonExistingDocumentError, uri.to_s unless path
|
45
|
+
|
40
46
|
ext = File.extname(path)
|
41
47
|
language_id = if ext == ".erb" || ext == ".rhtml"
|
42
48
|
Document::LanguageId::ERB
|
43
49
|
else
|
44
50
|
Document::LanguageId::Ruby
|
45
51
|
end
|
52
|
+
|
46
53
|
set(uri: uri, source: File.binread(path), version: 0, language_id: language_id)
|
47
54
|
T.must(@state[uri.to_s])
|
55
|
+
rescue Errno::ENOENT
|
56
|
+
raise NonExistingDocumentError, uri.to_s
|
48
57
|
end
|
49
58
|
|
50
59
|
sig do
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -21,7 +21,7 @@ module RubyLsp
|
|
21
21
|
&block)
|
22
22
|
server = RubyLsp::Server.new(test_mode: true)
|
23
23
|
server.global_state.stubs(:has_type_checker).returns(false) if stub_no_typechecker
|
24
|
-
server.global_state.apply_options({})
|
24
|
+
server.global_state.apply_options({ initializationOptions: { experimentalFeaturesEnabled: true } })
|
25
25
|
language_id = uri.to_s.end_with?(".erb") ? "erb" : "ruby"
|
26
26
|
|
27
27
|
if source
|