ruby-lsp 0.12.2 → 0.13.0
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 +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
|
# 
|
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 }
|