ruby-lsp 0.12.2 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp-check +20 -4
- data/exe/ruby-lsp-doctor +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/{visitor.rb → collector.rb} +144 -61
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +9 -4
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +89 -12
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +22 -4
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/index_test.rb +64 -0
- data/lib/ruby_indexer/test/method_test.rb +80 -0
- data/lib/ruby_lsp/addon.rb +9 -13
- data/lib/ruby_lsp/document.rb +7 -9
- data/lib/ruby_lsp/executor.rb +54 -51
- data/lib/ruby_lsp/internal.rb +4 -0
- data/lib/ruby_lsp/listener.rb +4 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -4
- data/lib/ruby_lsp/requests/code_lens.rb +16 -7
- data/lib/ruby_lsp/requests/completion.rb +60 -8
- data/lib/ruby_lsp/requests/definition.rb +55 -29
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +20 -11
- data/lib/ruby_lsp/requests/document_link.rb +2 -3
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
- data/lib/ruby_lsp/requests/formatting.rb +0 -5
- data/lib/ruby_lsp/requests/hover.rb +23 -4
- data/lib/ruby_lsp/requests/inlay_hints.rb +42 -4
- data/lib/ruby_lsp/requests/on_type_formatting.rb +18 -4
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +41 -16
- data/lib/ruby_lsp/requests/support/common.rb +22 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
- data/lib/ruby_lsp/requests/workspace_symbol.rb +6 -11
- data/lib/ruby_lsp/ruby_document.rb +14 -0
- data/lib/ruby_lsp/setup_bundler.rb +2 -0
- data/lib/ruby_lsp/store.rb +5 -3
- data/lib/ruby_lsp/utils.rb +8 -3
- metadata +8 -7
@@ -6,9 +6,14 @@ module RubyLsp
|
|
6
6
|
# ![Completion demo](../../completion.gif)
|
7
7
|
#
|
8
8
|
# The [completion](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
|
9
|
-
# suggests possible completions according to what the developer is typing.
|
10
|
-
#
|
11
|
-
#
|
9
|
+
# suggests possible completions according to what the developer is typing.
|
10
|
+
#
|
11
|
+
# Currently supported targets:
|
12
|
+
# - Classes
|
13
|
+
# - Modules
|
14
|
+
# - Constants
|
15
|
+
# - Require paths
|
16
|
+
# - Methods invoked on self only
|
12
17
|
#
|
13
18
|
# # Example
|
14
19
|
#
|
@@ -31,11 +36,10 @@ module RubyLsp
|
|
31
36
|
index: RubyIndexer::Index,
|
32
37
|
nesting: T::Array[String],
|
33
38
|
dispatcher: Prism::Dispatcher,
|
34
|
-
message_queue: Thread::Queue,
|
35
39
|
).void
|
36
40
|
end
|
37
|
-
def initialize(index, nesting, dispatcher
|
38
|
-
super(dispatcher
|
41
|
+
def initialize(index, nesting, dispatcher)
|
42
|
+
super(dispatcher)
|
39
43
|
@_response = T.let([], ResponseType)
|
40
44
|
@index = index
|
41
45
|
@nesting = nesting
|
@@ -45,6 +49,7 @@ module RubyLsp
|
|
45
49
|
:on_string_node_enter,
|
46
50
|
:on_constant_path_node_enter,
|
47
51
|
:on_constant_read_node_enter,
|
52
|
+
:on_call_node_enter,
|
48
53
|
)
|
49
54
|
end
|
50
55
|
|
@@ -118,17 +123,64 @@ module RubyLsp
|
|
118
123
|
end
|
119
124
|
end
|
120
125
|
|
126
|
+
sig { params(node: Prism::CallNode).void }
|
127
|
+
def on_call_node_enter(node)
|
128
|
+
return if DependencyDetector.instance.typechecker
|
129
|
+
return unless self_receiver?(node)
|
130
|
+
|
131
|
+
name = node.message
|
132
|
+
return unless name
|
133
|
+
|
134
|
+
receiver_entries = @index[@nesting.join("::")]
|
135
|
+
return unless receiver_entries
|
136
|
+
|
137
|
+
receiver = T.must(receiver_entries.first)
|
138
|
+
|
139
|
+
@index.prefix_search(name).each do |entries|
|
140
|
+
entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
|
141
|
+
next unless entry
|
142
|
+
|
143
|
+
@_response << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
121
147
|
private
|
122
148
|
|
149
|
+
sig do
|
150
|
+
params(
|
151
|
+
entry: RubyIndexer::Entry::Member,
|
152
|
+
node: Prism::CallNode,
|
153
|
+
).returns(Interface::CompletionItem)
|
154
|
+
end
|
155
|
+
def build_method_completion(entry, node)
|
156
|
+
name = entry.name
|
157
|
+
parameters = entry.parameters
|
158
|
+
new_text = parameters.empty? ? name : "#{name}(#{parameters.map(&:name).join(", ")})"
|
159
|
+
|
160
|
+
Interface::CompletionItem.new(
|
161
|
+
label: name,
|
162
|
+
filter_text: name,
|
163
|
+
text_edit: Interface::TextEdit.new(range: range_from_node(node), new_text: new_text),
|
164
|
+
kind: Constant::CompletionItemKind::METHOD,
|
165
|
+
label_details: Interface::CompletionItemLabelDetails.new(
|
166
|
+
description: entry.file_name,
|
167
|
+
),
|
168
|
+
documentation: markdown_from_index_entries(name, entry),
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
123
172
|
sig { params(label: String, node: Prism::StringNode).returns(Interface::CompletionItem) }
|
124
173
|
def build_completion(label, node)
|
174
|
+
# We should use the content location as we only replace the content and not the delimiters of the string
|
175
|
+
loc = node.content_loc
|
176
|
+
|
125
177
|
Interface::CompletionItem.new(
|
126
178
|
label: label,
|
127
179
|
text_edit: Interface::TextEdit.new(
|
128
|
-
range:
|
180
|
+
range: range_from_location(loc),
|
129
181
|
new_text: label,
|
130
182
|
),
|
131
|
-
kind: Constant::CompletionItemKind::
|
183
|
+
kind: Constant::CompletionItemKind::FILE,
|
132
184
|
)
|
133
185
|
end
|
134
186
|
|
@@ -9,7 +9,12 @@ module RubyLsp
|
|
9
9
|
# request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
|
10
10
|
# definition of the symbol under the cursor.
|
11
11
|
#
|
12
|
-
# Currently
|
12
|
+
# Currently supported targets:
|
13
|
+
# - Classes
|
14
|
+
# - Modules
|
15
|
+
# - Constants
|
16
|
+
# - Require paths
|
17
|
+
# - Methods invoked on self only
|
13
18
|
#
|
14
19
|
# # Example
|
15
20
|
#
|
@@ -32,16 +37,15 @@ module RubyLsp
|
|
32
37
|
nesting: T::Array[String],
|
33
38
|
index: RubyIndexer::Index,
|
34
39
|
dispatcher: Prism::Dispatcher,
|
35
|
-
message_queue: Thread::Queue,
|
36
40
|
).void
|
37
41
|
end
|
38
|
-
def initialize(uri, nesting, index, dispatcher
|
42
|
+
def initialize(uri, nesting, index, dispatcher)
|
39
43
|
@uri = uri
|
40
44
|
@nesting = nesting
|
41
45
|
@index = index
|
42
46
|
@_response = T.let(nil, ResponseType)
|
43
47
|
|
44
|
-
super(dispatcher
|
48
|
+
super(dispatcher)
|
45
49
|
|
46
50
|
dispatcher.register(
|
47
51
|
self,
|
@@ -53,7 +57,7 @@ module RubyLsp
|
|
53
57
|
|
54
58
|
sig { override.params(addon: Addon).returns(T.nilable(RubyLsp::Listener[ResponseType])) }
|
55
59
|
def initialize_external_listener(addon)
|
56
|
-
addon.create_definition_listener(@uri, @nesting, @index, @dispatcher
|
60
|
+
addon.create_definition_listener(@uri, @nesting, @index, @dispatcher)
|
57
61
|
end
|
58
62
|
|
59
63
|
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
@@ -75,8 +79,52 @@ module RubyLsp
|
|
75
79
|
sig { params(node: Prism::CallNode).void }
|
76
80
|
def on_call_node_enter(node)
|
77
81
|
message = node.name
|
78
|
-
return unless message == :require || message == :require_relative
|
79
82
|
|
83
|
+
if message == :require || message == :require_relative
|
84
|
+
handle_require_definition(node)
|
85
|
+
else
|
86
|
+
handle_method_definition(node)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { params(node: Prism::ConstantPathNode).void }
|
91
|
+
def on_constant_path_node_enter(node)
|
92
|
+
find_in_index(node.slice)
|
93
|
+
end
|
94
|
+
|
95
|
+
sig { params(node: Prism::ConstantReadNode).void }
|
96
|
+
def on_constant_read_node_enter(node)
|
97
|
+
find_in_index(node.slice)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
sig { params(node: Prism::CallNode).void }
|
103
|
+
def handle_method_definition(node)
|
104
|
+
return unless self_receiver?(node)
|
105
|
+
|
106
|
+
message = node.message
|
107
|
+
return unless message
|
108
|
+
|
109
|
+
target_method = @index.resolve_method(message, @nesting.join("::"))
|
110
|
+
return unless target_method
|
111
|
+
|
112
|
+
location = target_method.location
|
113
|
+
file_path = target_method.file_path
|
114
|
+
return if defined_in_gem?(file_path)
|
115
|
+
|
116
|
+
@_response = Interface::Location.new(
|
117
|
+
uri: URI::Generic.from_path(path: file_path).to_s,
|
118
|
+
range: Interface::Range.new(
|
119
|
+
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
120
|
+
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
121
|
+
),
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
sig { params(node: Prism::CallNode).void }
|
126
|
+
def handle_require_definition(node)
|
127
|
+
message = node.name
|
80
128
|
arguments = node.arguments
|
81
129
|
return unless arguments
|
82
130
|
|
@@ -116,18 +164,6 @@ module RubyLsp
|
|
116
164
|
end
|
117
165
|
end
|
118
166
|
|
119
|
-
sig { params(node: Prism::ConstantPathNode).void }
|
120
|
-
def on_constant_path_node_enter(node)
|
121
|
-
find_in_index(node.slice)
|
122
|
-
end
|
123
|
-
|
124
|
-
sig { params(node: Prism::ConstantReadNode).void }
|
125
|
-
def on_constant_read_node_enter(node)
|
126
|
-
find_in_index(node.slice)
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
|
131
167
|
sig { params(value: String).void }
|
132
168
|
def find_in_index(value)
|
133
169
|
entries = @index.resolve(value, @nesting)
|
@@ -138,23 +174,13 @@ module RubyLsp
|
|
138
174
|
first_entry = T.must(entries.first)
|
139
175
|
return if first_entry.visibility == :private && first_entry.name != "#{@nesting.join("::")}::#{value}"
|
140
176
|
|
141
|
-
bundle_path = begin
|
142
|
-
Bundler.bundle_path.to_s
|
143
|
-
rescue Bundler::GemfileNotFound
|
144
|
-
nil
|
145
|
-
end
|
146
|
-
|
147
177
|
@_response = entries.filter_map do |entry|
|
148
178
|
location = entry.location
|
149
179
|
# If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
|
150
180
|
# additional behavior on top of jumping to RBIs. Sorbet can already handle go to definition for all constants
|
151
181
|
# in the project, even if the files are typed false
|
152
182
|
file_path = entry.file_path
|
153
|
-
if
|
154
|
-
!file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
|
155
|
-
|
156
|
-
next
|
157
|
-
end
|
183
|
+
next if defined_in_gem?(file_path)
|
158
184
|
|
159
185
|
Interface::Location.new(
|
160
186
|
uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -32,13 +32,8 @@ module RubyLsp
|
|
32
32
|
def run
|
33
33
|
# Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
|
34
34
|
return syntax_error_diagnostics if @document.syntax_error?
|
35
|
-
|
36
35
|
return unless defined?(Support::RuboCopDiagnosticsRunner)
|
37
36
|
|
38
|
-
# Don't try to run RuboCop diagnostics for files outside the current working directory
|
39
|
-
path = @uri.to_standardized_path
|
40
|
-
return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
|
41
|
-
|
42
37
|
Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document).map!(&:to_lsp_diagnostic)
|
43
38
|
end
|
44
39
|
|
@@ -97,7 +97,8 @@ module RubyLsp
|
|
97
97
|
Prism::LocalVariableWriteNode,
|
98
98
|
Prism::BlockParameterNode,
|
99
99
|
Prism::RequiredParameterNode,
|
100
|
-
Prism::
|
100
|
+
Prism::RequiredKeywordParameterNode,
|
101
|
+
Prism::OptionalKeywordParameterNode,
|
101
102
|
Prism::RestParameterNode,
|
102
103
|
Prism::OptionalParameterNode,
|
103
104
|
Prism::KeywordRestParameterNode,
|
@@ -113,11 +114,10 @@ module RubyLsp
|
|
113
114
|
target: T.nilable(Prism::Node),
|
114
115
|
parent: T.nilable(Prism::Node),
|
115
116
|
dispatcher: Prism::Dispatcher,
|
116
|
-
message_queue: Thread::Queue,
|
117
117
|
).void
|
118
118
|
end
|
119
|
-
def initialize(target, parent, dispatcher
|
120
|
-
super(dispatcher
|
119
|
+
def initialize(target, parent, dispatcher)
|
120
|
+
super(dispatcher)
|
121
121
|
|
122
122
|
@_response = T.let([], T::Array[Interface::DocumentHighlight])
|
123
123
|
|
@@ -137,8 +137,9 @@ module RubyLsp
|
|
137
137
|
Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode, Prism::ClassVariableWriteNode,
|
138
138
|
Prism::LocalVariableAndWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode,
|
139
139
|
Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode, Prism::LocalVariableWriteNode,
|
140
|
-
Prism::CallNode, Prism::BlockParameterNode, Prism::
|
141
|
-
Prism::
|
140
|
+
Prism::CallNode, Prism::BlockParameterNode, Prism::RequiredKeywordParameterNode,
|
141
|
+
Prism::RequiredKeywordParameterNode, Prism::KeywordRestParameterNode, Prism::OptionalParameterNode,
|
142
|
+
Prism::RequiredParameterNode, Prism::RestParameterNode
|
142
143
|
target
|
143
144
|
end
|
144
145
|
|
@@ -171,7 +172,8 @@ module RubyLsp
|
|
171
172
|
:on_constant_path_and_write_node_enter,
|
172
173
|
:on_constant_path_operator_write_node_enter,
|
173
174
|
:on_local_variable_write_node_enter,
|
174
|
-
:
|
175
|
+
:on_required_keyword_parameter_node_enter,
|
176
|
+
:on_optional_keyword_parameter_node_enter,
|
175
177
|
:on_rest_parameter_node_enter,
|
176
178
|
:on_optional_parameter_node_enter,
|
177
179
|
:on_keyword_rest_parameter_node_enter,
|
@@ -359,8 +361,15 @@ module RubyLsp
|
|
359
361
|
add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
|
360
362
|
end
|
361
363
|
|
362
|
-
sig { params(node: Prism::
|
363
|
-
def
|
364
|
+
sig { params(node: Prism::RequiredKeywordParameterNode).void }
|
365
|
+
def on_required_keyword_parameter_node_enter(node)
|
366
|
+
return unless matches?(node, LOCAL_NODES)
|
367
|
+
|
368
|
+
add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
|
369
|
+
end
|
370
|
+
|
371
|
+
sig { params(node: Prism::OptionalKeywordParameterNode).void }
|
372
|
+
def on_optional_keyword_parameter_node_enter(node)
|
364
373
|
return unless matches?(node, LOCAL_NODES)
|
365
374
|
|
366
375
|
add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc)
|
@@ -551,8 +560,8 @@ module RubyLsp
|
|
551
560
|
Prism::ClassVariableTargetNode, Prism::ClassVariableWriteNode, Prism::LocalVariableAndWriteNode,
|
552
561
|
Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode, Prism::LocalVariableReadNode,
|
553
562
|
Prism::LocalVariableTargetNode, Prism::LocalVariableWriteNode, Prism::DefNode, Prism::BlockParameterNode,
|
554
|
-
Prism::
|
555
|
-
Prism::RequiredParameterNode, Prism::RestParameterNode
|
563
|
+
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode, Prism::KeywordRestParameterNode,
|
564
|
+
Prism::OptionalParameterNode, Prism::RequiredParameterNode, Prism::RestParameterNode
|
556
565
|
|
557
566
|
node.name.to_s
|
558
567
|
when Prism::CallNode
|
@@ -80,11 +80,10 @@ module RubyLsp
|
|
80
80
|
uri: URI::Generic,
|
81
81
|
comments: T::Array[Prism::Comment],
|
82
82
|
dispatcher: Prism::Dispatcher,
|
83
|
-
message_queue: Thread::Queue,
|
84
83
|
).void
|
85
84
|
end
|
86
|
-
def initialize(uri, comments, dispatcher
|
87
|
-
super(dispatcher
|
85
|
+
def initialize(uri, comments, dispatcher)
|
86
|
+
super(dispatcher)
|
88
87
|
|
89
88
|
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
90
89
|
# in the URI
|
@@ -49,8 +49,8 @@ module RubyLsp
|
|
49
49
|
sig { override.returns(T::Array[Interface::DocumentSymbol]) }
|
50
50
|
attr_reader :_response
|
51
51
|
|
52
|
-
sig { params(dispatcher: Prism::Dispatcher
|
53
|
-
def initialize(dispatcher
|
52
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
53
|
+
def initialize(dispatcher)
|
54
54
|
@root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
|
55
55
|
@_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
|
56
56
|
@stack = T.let(
|
@@ -80,7 +80,7 @@ module RubyLsp
|
|
80
80
|
|
81
81
|
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
82
82
|
def initialize_external_listener(addon)
|
83
|
-
addon.create_document_symbol_listener(@dispatcher
|
83
|
+
addon.create_document_symbol_listener(@dispatcher)
|
84
84
|
end
|
85
85
|
|
86
86
|
# Merges responses from other listeners
|
@@ -21,9 +21,9 @@ module RubyLsp
|
|
21
21
|
|
22
22
|
ResponseType = type_member { { fixed: T::Array[Interface::FoldingRange] } }
|
23
23
|
|
24
|
-
sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher
|
25
|
-
def initialize(comments, dispatcher
|
26
|
-
super(dispatcher
|
24
|
+
sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher).void }
|
25
|
+
def initialize(comments, dispatcher)
|
26
|
+
super(dispatcher)
|
27
27
|
|
28
28
|
@_response = T.let([], ResponseType)
|
29
29
|
@requires = T.let([], T::Array[Prism::CallNode])
|
@@ -40,6 +40,7 @@ module RubyLsp
|
|
40
40
|
:on_array_node_enter,
|
41
41
|
:on_block_node_enter,
|
42
42
|
:on_case_node_enter,
|
43
|
+
:on_case_match_node_enter,
|
43
44
|
:on_class_node_enter,
|
44
45
|
:on_module_node_enter,
|
45
46
|
:on_for_node_enter,
|
@@ -51,7 +52,6 @@ module RubyLsp
|
|
51
52
|
:on_else_node_enter,
|
52
53
|
:on_ensure_node_enter,
|
53
54
|
:on_begin_node_enter,
|
54
|
-
:on_string_concat_node_enter,
|
55
55
|
:on_def_node_enter,
|
56
56
|
:on_call_node_enter,
|
57
57
|
:on_lambda_node_enter,
|
@@ -91,10 +91,10 @@ module RubyLsp
|
|
91
91
|
|
92
92
|
sig { params(node: Prism::InterpolatedStringNode).void }
|
93
93
|
def on_interpolated_string_node_enter(node)
|
94
|
-
opening_loc = node.opening_loc
|
95
|
-
closing_loc = node.closing_loc
|
94
|
+
opening_loc = node.opening_loc || node.location
|
95
|
+
closing_loc = node.closing_loc || node.parts.last&.location || node.location
|
96
96
|
|
97
|
-
add_lines_range(opening_loc.start_line, closing_loc.
|
97
|
+
add_lines_range(opening_loc.start_line, closing_loc.start_line - 1)
|
98
98
|
end
|
99
99
|
|
100
100
|
sig { params(node: Prism::ArrayNode).void }
|
@@ -112,6 +112,11 @@ module RubyLsp
|
|
112
112
|
add_simple_range(node)
|
113
113
|
end
|
114
114
|
|
115
|
+
sig { params(node: Prism::CaseMatchNode).void }
|
116
|
+
def on_case_match_node_enter(node)
|
117
|
+
add_simple_range(node)
|
118
|
+
end
|
119
|
+
|
115
120
|
sig { params(node: Prism::ClassNode).void }
|
116
121
|
def on_class_node_enter(node)
|
117
122
|
add_simple_range(node)
|
@@ -167,14 +172,6 @@ module RubyLsp
|
|
167
172
|
add_simple_range(node)
|
168
173
|
end
|
169
174
|
|
170
|
-
sig { params(node: Prism::StringConcatNode).void }
|
171
|
-
def on_string_concat_node_enter(node)
|
172
|
-
left = T.let(node.left, Prism::Node)
|
173
|
-
left = left.left while left.is_a?(Prism::StringConcatNode)
|
174
|
-
|
175
|
-
add_lines_range(left.location.start_line, node.right.location.end_line - 1)
|
176
|
-
end
|
177
|
-
|
178
175
|
sig { params(node: Prism::DefNode).void }
|
179
176
|
def on_def_node_enter(node)
|
180
177
|
params = node.parameters
|
@@ -64,11 +64,6 @@ module RubyLsp
|
|
64
64
|
sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
|
65
65
|
def run
|
66
66
|
return if @formatter == "none"
|
67
|
-
|
68
|
-
# Don't try to format files outside the current working directory
|
69
|
-
path = @uri.to_standardized_path
|
70
|
-
return unless path.nil? || path.start_with?(T.must(WORKSPACE_URI.to_standardized_path))
|
71
|
-
|
72
67
|
return if @document.syntax_error?
|
73
68
|
|
74
69
|
formatted_text = formatted_file
|
@@ -37,26 +37,26 @@ module RubyLsp
|
|
37
37
|
index: RubyIndexer::Index,
|
38
38
|
nesting: T::Array[String],
|
39
39
|
dispatcher: Prism::Dispatcher,
|
40
|
-
message_queue: Thread::Queue,
|
41
40
|
).void
|
42
41
|
end
|
43
|
-
def initialize(index, nesting, dispatcher
|
42
|
+
def initialize(index, nesting, dispatcher)
|
44
43
|
@index = index
|
45
44
|
@nesting = nesting
|
46
45
|
@_response = T.let(nil, ResponseType)
|
47
46
|
|
48
|
-
super(dispatcher
|
47
|
+
super(dispatcher)
|
49
48
|
dispatcher.register(
|
50
49
|
self,
|
51
50
|
:on_constant_read_node_enter,
|
52
51
|
:on_constant_write_node_enter,
|
53
52
|
:on_constant_path_node_enter,
|
53
|
+
:on_call_node_enter,
|
54
54
|
)
|
55
55
|
end
|
56
56
|
|
57
57
|
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
58
58
|
def initialize_external_listener(addon)
|
59
|
-
addon.create_hover_listener(@nesting, @index, @dispatcher
|
59
|
+
addon.create_hover_listener(@nesting, @index, @dispatcher)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Merges responses from other hover listeners
|
@@ -95,6 +95,25 @@ module RubyLsp
|
|
95
95
|
generate_hover(node.slice, node.location)
|
96
96
|
end
|
97
97
|
|
98
|
+
sig { params(node: Prism::CallNode).void }
|
99
|
+
def on_call_node_enter(node)
|
100
|
+
return if DependencyDetector.instance.typechecker
|
101
|
+
return unless self_receiver?(node)
|
102
|
+
|
103
|
+
message = node.message
|
104
|
+
return unless message
|
105
|
+
|
106
|
+
target_method = @index.resolve_method(message, @nesting.join("::"))
|
107
|
+
return unless target_method
|
108
|
+
|
109
|
+
location = target_method.location
|
110
|
+
|
111
|
+
@_response = Interface::Hover.new(
|
112
|
+
range: range_from_location(location),
|
113
|
+
contents: markdown_from_index_entries(message, target_method),
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
98
117
|
private
|
99
118
|
|
100
119
|
sig { params(name: String, location: Prism::Location).void }
|
@@ -18,6 +18,16 @@ module RubyLsp
|
|
18
18
|
# puts "handle some rescue"
|
19
19
|
# end
|
20
20
|
# ```
|
21
|
+
#
|
22
|
+
# # Example
|
23
|
+
#
|
24
|
+
# ```ruby
|
25
|
+
# var = "foo"
|
26
|
+
# {
|
27
|
+
# var: var, # Label "var" goes here in cases where the value is omitted
|
28
|
+
# a: "hello",
|
29
|
+
# }
|
30
|
+
# ```
|
21
31
|
class InlayHints < Listener
|
22
32
|
extend T::Sig
|
23
33
|
extend T::Generic
|
@@ -29,14 +39,14 @@ module RubyLsp
|
|
29
39
|
sig { override.returns(ResponseType) }
|
30
40
|
attr_reader :_response
|
31
41
|
|
32
|
-
sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher
|
33
|
-
def initialize(range, dispatcher
|
34
|
-
super(dispatcher
|
42
|
+
sig { params(range: T::Range[Integer], dispatcher: Prism::Dispatcher).void }
|
43
|
+
def initialize(range, dispatcher)
|
44
|
+
super(dispatcher)
|
35
45
|
|
36
46
|
@_response = T.let([], ResponseType)
|
37
47
|
@range = range
|
38
48
|
|
39
|
-
dispatcher.register(self, :on_rescue_node_enter)
|
49
|
+
dispatcher.register(self, :on_rescue_node_enter, :on_implicit_node_enter)
|
40
50
|
end
|
41
51
|
|
42
52
|
sig { params(node: Prism::RescueNode).void }
|
@@ -53,6 +63,34 @@ module RubyLsp
|
|
53
63
|
tooltip: "StandardError is implied in a bare rescue",
|
54
64
|
)
|
55
65
|
end
|
66
|
+
|
67
|
+
sig { params(node: Prism::ImplicitNode).void }
|
68
|
+
def on_implicit_node_enter(node)
|
69
|
+
return unless visible?(node, @range)
|
70
|
+
|
71
|
+
node_value = node.value
|
72
|
+
loc = node.location
|
73
|
+
tooltip = ""
|
74
|
+
node_name = ""
|
75
|
+
case node_value
|
76
|
+
when Prism::CallNode
|
77
|
+
node_name = node_value.name
|
78
|
+
tooltip = "This is a method call. Method name: #{node_name}"
|
79
|
+
when Prism::ConstantReadNode
|
80
|
+
node_name = node_value.name
|
81
|
+
tooltip = "This is a constant: #{node_name}"
|
82
|
+
when Prism::LocalVariableReadNode
|
83
|
+
node_name = node_value.name
|
84
|
+
tooltip = "This is a local variable: #{node_name}"
|
85
|
+
end
|
86
|
+
|
87
|
+
@_response << Interface::InlayHint.new(
|
88
|
+
position: { line: loc.start_line - 1, character: loc.start_column + node_name.length + 1 },
|
89
|
+
label: node_name,
|
90
|
+
padding_left: true,
|
91
|
+
tooltip: tooltip,
|
92
|
+
)
|
93
|
+
end
|
56
94
|
end
|
57
95
|
end
|
58
96
|
end
|
@@ -20,8 +20,8 @@ module RubyLsp
|
|
20
20
|
|
21
21
|
END_REGEXES = T.let(
|
22
22
|
[
|
23
|
-
|
24
|
-
/.*\
|
23
|
+
/\b(if|unless|for|while|class|module|until|def|case)\b.*/,
|
24
|
+
/.*\s\bdo\b/,
|
25
25
|
],
|
26
26
|
T::Array[Regexp],
|
27
27
|
)
|
@@ -51,7 +51,14 @@ module RubyLsp
|
|
51
51
|
if (comment_match = @previous_line.match(/^#(\s*)/))
|
52
52
|
handle_comment_line(T.must(comment_match[1]))
|
53
53
|
elsif @document.syntax_error?
|
54
|
-
|
54
|
+
match = /(?<=<<(-|~))(?<quote>['"`]?)(?<delimiter>\w+)\k<quote>/.match(@previous_line)
|
55
|
+
heredoc_delimiter = match && match.named_captures["delimiter"]
|
56
|
+
|
57
|
+
if heredoc_delimiter
|
58
|
+
handle_heredoc_end(heredoc_delimiter)
|
59
|
+
else
|
60
|
+
handle_statement_end
|
61
|
+
end
|
55
62
|
end
|
56
63
|
end
|
57
64
|
|
@@ -121,10 +128,17 @@ module RubyLsp
|
|
121
128
|
end
|
122
129
|
end
|
123
130
|
|
131
|
+
sig { params(delimiter: String).void }
|
132
|
+
def handle_heredoc_end(delimiter)
|
133
|
+
indents = " " * @indentation
|
134
|
+
add_edit_with_text("\n")
|
135
|
+
add_edit_with_text("#{indents}#{delimiter}")
|
136
|
+
move_cursor_to(@position[:line], @indentation + 2)
|
137
|
+
end
|
138
|
+
|
124
139
|
sig { params(spaces: String).void }
|
125
140
|
def handle_comment_line(spaces)
|
126
141
|
add_edit_with_text("##{spaces}")
|
127
|
-
move_cursor_to(@position[:line], @indentation + spaces.size + 1)
|
128
142
|
end
|
129
143
|
|
130
144
|
sig { params(text: String, position: Document::PositionShape).void }
|