ruby-lsp 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +44 -52
- data/VERSION +1 -1
- data/exe/ruby-lsp +12 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +62 -0
- data/lib/ruby_lsp/document.rb +13 -4
- data/lib/ruby_lsp/executor.rb +70 -26
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/requests/base_request.rb +15 -6
- data/lib/ruby_lsp/requests/code_action_resolve.rb +40 -19
- data/lib/ruby_lsp/requests/code_actions.rb +5 -4
- data/lib/ruby_lsp/requests/diagnostics.rb +4 -4
- data/lib/ruby_lsp/requests/document_highlight.rb +3 -3
- data/lib/ruby_lsp/requests/document_link.rb +7 -7
- data/lib/ruby_lsp/requests/document_symbol.rb +14 -11
- data/lib/ruby_lsp/requests/folding_ranges.rb +38 -11
- data/lib/ruby_lsp/requests/formatting.rb +18 -5
- data/lib/ruby_lsp/requests/hover.rb +7 -6
- data/lib/ruby_lsp/requests/inlay_hints.rb +5 -4
- data/lib/ruby_lsp/requests/path_completion.rb +9 -3
- data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +72 -8
- data/lib/ruby_lsp/requests/support/highlight_target.rb +5 -4
- data/lib/ruby_lsp/requests/support/rails_document_client.rb +7 -6
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -1
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
- data/lib/ruby_lsp/requests/support/sorbet.rb +5 -15
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +39 -0
- data/lib/ruby_lsp/server.rb +4 -1
- data/lib/ruby_lsp/store.rb +11 -7
- data/lib/ruby_lsp/utils.rb +3 -0
- metadata +7 -5
@@ -21,16 +21,15 @@ module RubyLsp
|
|
21
21
|
|
22
22
|
sig do
|
23
23
|
params(
|
24
|
-
uri: String,
|
25
24
|
document: Document,
|
26
25
|
range: Document::RangeShape,
|
27
26
|
context: T::Hash[Symbol, T.untyped],
|
28
27
|
).void
|
29
28
|
end
|
30
|
-
def initialize(
|
29
|
+
def initialize(document, range, context)
|
31
30
|
super(document)
|
32
31
|
|
33
|
-
@uri = uri
|
32
|
+
@uri = T.let(document.uri, String)
|
34
33
|
@range = range
|
35
34
|
@context = context
|
36
35
|
end
|
@@ -49,7 +48,9 @@ module RubyLsp
|
|
49
48
|
code_action if diagnostic.dig(:data, :correctable) && cover?(range)
|
50
49
|
end
|
51
50
|
|
52
|
-
|
51
|
+
# Only add refactor actions if there's a non empty selection in the editor
|
52
|
+
code_actions << refactor_code_action(@range, @uri) unless @range.dig(:start) == @range.dig(:end)
|
53
|
+
code_actions
|
53
54
|
end
|
54
55
|
|
55
56
|
private
|
@@ -21,11 +21,11 @@ module RubyLsp
|
|
21
21
|
class Diagnostics < BaseRequest
|
22
22
|
extend T::Sig
|
23
23
|
|
24
|
-
sig { params(
|
25
|
-
def initialize(
|
24
|
+
sig { params(document: Document).void }
|
25
|
+
def initialize(document)
|
26
26
|
super(document)
|
27
27
|
|
28
|
-
@uri = uri
|
28
|
+
@uri = T.let(document.uri, String)
|
29
29
|
end
|
30
30
|
|
31
31
|
sig { override.returns(T.nilable(T.all(T::Array[Support::RuboCopDiagnostic], Object))) }
|
@@ -34,7 +34,7 @@ module RubyLsp
|
|
34
34
|
return unless defined?(Support::RuboCopDiagnosticsRunner)
|
35
35
|
|
36
36
|
# Don't try to run RuboCop diagnostics for files outside the current working directory
|
37
|
-
return unless @uri.
|
37
|
+
return unless @uri.start_with?(WORKSPACE_URI)
|
38
38
|
|
39
39
|
Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
|
40
40
|
end
|
@@ -29,14 +29,14 @@ module RubyLsp
|
|
29
29
|
def initialize(document, position)
|
30
30
|
super(document)
|
31
31
|
|
32
|
-
@highlights = T.let([], T::Array[
|
32
|
+
@highlights = T.let([], T::Array[Interface::DocumentHighlight])
|
33
33
|
return unless document.parsed?
|
34
34
|
|
35
35
|
position = document.create_scanner.find_char_position(position)
|
36
36
|
@target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
|
37
37
|
end
|
38
38
|
|
39
|
-
sig { override.returns(T.all(T::Array[
|
39
|
+
sig { override.returns(T.all(T::Array[Interface::DocumentHighlight], Object)) }
|
40
40
|
def run
|
41
41
|
# no @target means the target is not highlightable
|
42
42
|
visit(@document.tree) if @document.parsed? && @target
|
@@ -90,7 +90,7 @@ module RubyLsp
|
|
90
90
|
sig { params(match: Support::HighlightTarget::HighlightMatch).void }
|
91
91
|
def add_highlight(match)
|
92
92
|
range = range_from_syntax_tree_node(match.node)
|
93
|
-
@highlights <<
|
93
|
+
@highlights << Interface::DocumentHighlight.new(range: range, kind: match.type)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
@@ -69,18 +69,18 @@ module RubyLsp
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
sig { params(
|
73
|
-
def initialize(
|
72
|
+
sig { params(document: Document).void }
|
73
|
+
def initialize(document)
|
74
74
|
super(document)
|
75
75
|
|
76
76
|
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
77
77
|
# in the URI
|
78
|
-
version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(uri)
|
78
|
+
version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(document.uri)
|
79
79
|
@gem_version = T.let(version_match && version_match[0], T.nilable(String))
|
80
|
-
@links = T.let([], T::Array[
|
80
|
+
@links = T.let([], T::Array[Interface::DocumentLink])
|
81
81
|
end
|
82
82
|
|
83
|
-
sig { override.returns(T.all(T::Array[
|
83
|
+
sig { override.returns(T.all(T::Array[Interface::DocumentLink], Object)) }
|
84
84
|
def run
|
85
85
|
visit(@document.tree) if @document.parsed?
|
86
86
|
@links
|
@@ -91,12 +91,12 @@ module RubyLsp
|
|
91
91
|
match = node.value.match(%r{source://.*#\d+$})
|
92
92
|
return unless match
|
93
93
|
|
94
|
-
uri = T.cast(URI(match[0]), URI::Source)
|
94
|
+
uri = T.cast(URI(T.must(match[0])), URI::Source)
|
95
95
|
gem_version = T.must(resolve_version(uri))
|
96
96
|
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
|
97
97
|
return if file_path.nil?
|
98
98
|
|
99
|
-
@links <<
|
99
|
+
@links << Interface::DocumentLink.new(
|
100
100
|
range: range_from_syntax_tree_node(node),
|
101
101
|
target: "file://#{file_path}##{uri.line_number}",
|
102
102
|
tooltip: "Jump to #{file_path}##{uri.line_number}",
|
@@ -66,12 +66,12 @@ module RubyLsp
|
|
66
66
|
class SymbolHierarchyRoot
|
67
67
|
extend T::Sig
|
68
68
|
|
69
|
-
sig { returns(T::Array[
|
69
|
+
sig { returns(T::Array[Interface::DocumentSymbol]) }
|
70
70
|
attr_reader :children
|
71
71
|
|
72
72
|
sig { void }
|
73
73
|
def initialize
|
74
|
-
@children = T.let([], T::Array[
|
74
|
+
@children = T.let([], T::Array[Interface::DocumentSymbol])
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -82,11 +82,11 @@ module RubyLsp
|
|
82
82
|
@root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
|
83
83
|
@stack = T.let(
|
84
84
|
[@root],
|
85
|
-
T::Array[T.any(SymbolHierarchyRoot,
|
85
|
+
T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
|
86
86
|
)
|
87
87
|
end
|
88
88
|
|
89
|
-
sig { override.returns(T.all(T::Array[
|
89
|
+
sig { override.returns(T.all(T::Array[Interface::DocumentSymbol], Object)) }
|
90
90
|
def run
|
91
91
|
visit(@document.tree) if @document.parsed?
|
92
92
|
@root.children
|
@@ -108,7 +108,7 @@ module RubyLsp
|
|
108
108
|
|
109
109
|
sig { override.params(node: SyntaxTree::Command).void }
|
110
110
|
def visit_command(node)
|
111
|
-
return unless ATTR_ACCESSORS.include?(node.message.value)
|
111
|
+
return visit(node.arguments) unless ATTR_ACCESSORS.include?(node.message.value)
|
112
112
|
|
113
113
|
node.arguments.parts.each do |argument|
|
114
114
|
next unless argument.is_a?(SyntaxTree::SymbolLiteral)
|
@@ -134,7 +134,9 @@ module RubyLsp
|
|
134
134
|
|
135
135
|
sig { override.params(node: SyntaxTree::DefNode).void }
|
136
136
|
def visit_def(node)
|
137
|
-
|
137
|
+
target = node.target
|
138
|
+
|
139
|
+
if target.is_a?(SyntaxTree::VarRef) && target.value.is_a?(SyntaxTree::Kw) && target.value.value == "self"
|
138
140
|
name = "self.#{node.name.value}"
|
139
141
|
kind = :method
|
140
142
|
else
|
@@ -180,7 +182,8 @@ module RubyLsp
|
|
180
182
|
|
181
183
|
sig { override.params(node: SyntaxTree::VarField).void }
|
182
184
|
def visit_var_field(node)
|
183
|
-
|
185
|
+
value = node.value
|
186
|
+
kind = case value
|
184
187
|
when SyntaxTree::Const
|
185
188
|
:constant
|
186
189
|
when SyntaxTree::CVar, SyntaxTree::IVar
|
@@ -190,10 +193,10 @@ module RubyLsp
|
|
190
193
|
end
|
191
194
|
|
192
195
|
create_document_symbol(
|
193
|
-
name:
|
196
|
+
name: value.value,
|
194
197
|
kind: kind,
|
195
198
|
range_node: node,
|
196
|
-
selection_range_node:
|
199
|
+
selection_range_node: value,
|
197
200
|
)
|
198
201
|
end
|
199
202
|
|
@@ -205,10 +208,10 @@ module RubyLsp
|
|
205
208
|
kind: Symbol,
|
206
209
|
range_node: SyntaxTree::Node,
|
207
210
|
selection_range_node: SyntaxTree::Node,
|
208
|
-
).returns(
|
211
|
+
).returns(Interface::DocumentSymbol)
|
209
212
|
end
|
210
213
|
def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
|
211
|
-
symbol =
|
214
|
+
symbol = Interface::DocumentSymbol.new(
|
212
215
|
name: name,
|
213
216
|
kind: SYMBOL_KIND[kind],
|
214
217
|
range: range_from_syntax_tree_node(range_node),
|
@@ -24,7 +24,6 @@ module RubyLsp
|
|
24
24
|
SyntaxTree::BlockNode,
|
25
25
|
SyntaxTree::Case,
|
26
26
|
SyntaxTree::ClassDeclaration,
|
27
|
-
SyntaxTree::Command,
|
28
27
|
SyntaxTree::For,
|
29
28
|
SyntaxTree::HashLiteral,
|
30
29
|
SyntaxTree::Heredoc,
|
@@ -64,11 +63,11 @@ module RubyLsp
|
|
64
63
|
def initialize(document)
|
65
64
|
super
|
66
65
|
|
67
|
-
@ranges = T.let([], T::Array[
|
66
|
+
@ranges = T.let([], T::Array[Interface::FoldingRange])
|
68
67
|
@partial_range = T.let(nil, T.nilable(PartialRange))
|
69
68
|
end
|
70
69
|
|
71
|
-
sig { override.returns(T.all(T::Array[
|
70
|
+
sig { override.returns(T.all(T::Array[Interface::FoldingRange], Object)) }
|
72
71
|
def run
|
73
72
|
if @document.parsed?
|
74
73
|
visit(@document.tree)
|
@@ -100,6 +99,11 @@ module RubyLsp
|
|
100
99
|
add_call_range(node)
|
101
100
|
return
|
102
101
|
end
|
102
|
+
when SyntaxTree::Command
|
103
|
+
unless same_lines_for_command_and_block?(node)
|
104
|
+
location = node.location
|
105
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
106
|
+
end
|
103
107
|
when SyntaxTree::DefNode
|
104
108
|
add_def_range(node)
|
105
109
|
when SyntaxTree::StringConcat
|
@@ -110,6 +114,17 @@ module RubyLsp
|
|
110
114
|
super
|
111
115
|
end
|
112
116
|
|
117
|
+
# This is to prevent duplicate ranges
|
118
|
+
sig { params(node: T.any(SyntaxTree::Command, SyntaxTree::CommandCall)).returns(T::Boolean) }
|
119
|
+
def same_lines_for_command_and_block?(node)
|
120
|
+
node_block = node.block
|
121
|
+
return false unless node_block
|
122
|
+
|
123
|
+
location = node.location
|
124
|
+
block_location = node_block.location
|
125
|
+
block_location.start_line == location.start_line && block_location.end_line == location.end_line
|
126
|
+
end
|
127
|
+
|
113
128
|
class PartialRange
|
114
129
|
extend T::Sig
|
115
130
|
|
@@ -146,9 +161,9 @@ module RubyLsp
|
|
146
161
|
node.is_a?(SyntaxTree::Comment) && @end_line + 1 != node.location.start_line - 1
|
147
162
|
end
|
148
163
|
|
149
|
-
sig { returns(
|
164
|
+
sig { returns(Interface::FoldingRange) }
|
150
165
|
def to_range
|
151
|
-
|
166
|
+
Interface::FoldingRange.new(
|
152
167
|
start_line: @start_line,
|
153
168
|
end_line: @end_line,
|
154
169
|
kind: @kind,
|
@@ -205,7 +220,8 @@ module RubyLsp
|
|
205
220
|
|
206
221
|
sig { params(node: T.any(SyntaxTree::CallNode, SyntaxTree::CommandCall)).void }
|
207
222
|
def add_call_range(node)
|
208
|
-
receiver = T.let(node.receiver, SyntaxTree::Node)
|
223
|
+
receiver = T.let(node.receiver, T.nilable(SyntaxTree::Node))
|
224
|
+
|
209
225
|
loop do
|
210
226
|
case receiver
|
211
227
|
when SyntaxTree::CallNode
|
@@ -223,18 +239,27 @@ module RubyLsp
|
|
223
239
|
end
|
224
240
|
end
|
225
241
|
|
226
|
-
|
242
|
+
if receiver
|
243
|
+
unless node.is_a?(SyntaxTree::CommandCall) && same_lines_for_command_and_block?(node)
|
244
|
+
add_lines_range(
|
245
|
+
receiver.location.start_line,
|
246
|
+
node.location.end_line - 1,
|
247
|
+
)
|
248
|
+
end
|
249
|
+
end
|
227
250
|
|
228
251
|
visit(node.arguments)
|
252
|
+
visit(node.block) if node.is_a?(SyntaxTree::CommandCall)
|
229
253
|
end
|
230
254
|
|
231
255
|
sig { params(node: SyntaxTree::DefNode).void }
|
232
256
|
def add_def_range(node)
|
233
257
|
# For an endless method with no arguments, `node.params` returns `nil` for Ruby 3.0, but a `Syntax::Params`
|
234
258
|
# for Ruby 3.1
|
235
|
-
|
259
|
+
params = node.params
|
260
|
+
return unless params
|
236
261
|
|
237
|
-
params_location =
|
262
|
+
params_location = params.location
|
238
263
|
|
239
264
|
if params_location.start_line < params_location.end_line
|
240
265
|
add_lines_range(params_location.end_line, node.location.end_line - 1)
|
@@ -253,7 +278,9 @@ module RubyLsp
|
|
253
278
|
|
254
279
|
sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
|
255
280
|
def add_statements_range(node, statements)
|
256
|
-
|
281
|
+
return if statements.empty?
|
282
|
+
|
283
|
+
add_lines_range(node.location.start_line, T.must(statements.body.last).location.end_line)
|
257
284
|
end
|
258
285
|
|
259
286
|
sig { params(node: SyntaxTree::StringConcat).void }
|
@@ -268,7 +295,7 @@ module RubyLsp
|
|
268
295
|
def add_lines_range(start_line, end_line)
|
269
296
|
return if start_line >= end_line
|
270
297
|
|
271
|
-
@ranges <<
|
298
|
+
@ranges << Interface::FoldingRange.new(
|
272
299
|
start_line: start_line - 1,
|
273
300
|
end_line: end_line - 1,
|
274
301
|
kind: "region",
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "ruby_lsp/requests/support/rubocop_formatting_runner"
|
5
|
+
require "ruby_lsp/requests/support/syntax_tree_formatting_runner"
|
5
6
|
|
6
7
|
module RubyLsp
|
7
8
|
module Requests
|
@@ -22,14 +23,23 @@ module RubyLsp
|
|
22
23
|
# ```
|
23
24
|
class Formatting < BaseRequest
|
24
25
|
class Error < StandardError; end
|
26
|
+
class InvalidFormatter < StandardError; end
|
25
27
|
|
26
28
|
extend T::Sig
|
27
29
|
|
28
|
-
sig { params(
|
29
|
-
def initialize(
|
30
|
+
sig { params(document: Document, formatter: String).void }
|
31
|
+
def initialize(document, formatter: "auto")
|
30
32
|
super(document)
|
31
33
|
|
32
|
-
@uri = uri
|
34
|
+
@uri = T.let(document.uri, String)
|
35
|
+
@formatter = T.let(
|
36
|
+
if formatter == "auto"
|
37
|
+
defined?(Support::RuboCopFormattingRunner) ? "rubocop" : "syntax_tree"
|
38
|
+
else
|
39
|
+
formatter
|
40
|
+
end,
|
41
|
+
String,
|
42
|
+
)
|
33
43
|
end
|
34
44
|
|
35
45
|
sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
|
@@ -60,10 +70,13 @@ module RubyLsp
|
|
60
70
|
|
61
71
|
sig { returns(T.nilable(String)) }
|
62
72
|
def formatted_file
|
63
|
-
|
73
|
+
case @formatter
|
74
|
+
when "rubocop"
|
64
75
|
Support::RuboCopFormattingRunner.instance.run(@uri, @document)
|
76
|
+
when "syntax_tree"
|
77
|
+
Support::SyntaxTreeFormattingRunner.instance.run(@uri, @document)
|
65
78
|
else
|
66
|
-
|
79
|
+
raise InvalidFormatter, "Unknown formatter: #{@formatter}"
|
67
80
|
end
|
68
81
|
end
|
69
82
|
end
|
@@ -36,7 +36,7 @@ module RubyLsp
|
|
36
36
|
@position = T.let(document.create_scanner.find_char_position(position), Integer)
|
37
37
|
end
|
38
38
|
|
39
|
-
sig { override.returns(T.nilable(
|
39
|
+
sig { override.returns(T.nilable(Interface::Hover)) }
|
40
40
|
def run
|
41
41
|
return unless @document.parsed?
|
42
42
|
|
@@ -48,9 +48,10 @@ module RubyLsp
|
|
48
48
|
message = target.message
|
49
49
|
generate_rails_document_link_hover(message.value, message)
|
50
50
|
when SyntaxTree::CallNode
|
51
|
-
|
51
|
+
message = target.message
|
52
|
+
return if message.is_a?(Symbol)
|
52
53
|
|
53
|
-
generate_rails_document_link_hover(
|
54
|
+
generate_rails_document_link_hover(message.value, message)
|
54
55
|
when SyntaxTree::ConstPathRef
|
55
56
|
constant_name = full_constant_name(target)
|
56
57
|
generate_rails_document_link_hover(constant_name, target)
|
@@ -60,18 +61,18 @@ module RubyLsp
|
|
60
61
|
private
|
61
62
|
|
62
63
|
sig do
|
63
|
-
params(name: String, node: SyntaxTree::Node).returns(T.nilable(
|
64
|
+
params(name: String, node: SyntaxTree::Node).returns(T.nilable(Interface::Hover))
|
64
65
|
end
|
65
66
|
def generate_rails_document_link_hover(name, node)
|
66
67
|
urls = Support::RailsDocumentClient.generate_rails_document_urls(name)
|
67
68
|
|
68
69
|
return if urls.empty?
|
69
70
|
|
70
|
-
contents =
|
71
|
+
contents = Interface::MarkupContent.new(
|
71
72
|
kind: "markdown",
|
72
73
|
value: urls.join("\n\n"),
|
73
74
|
)
|
74
|
-
|
75
|
+
Interface::Hover.new(
|
75
76
|
range: range_from_syntax_tree_node(node),
|
76
77
|
contents: contents,
|
77
78
|
)
|
@@ -25,11 +25,11 @@ module RubyLsp
|
|
25
25
|
def initialize(document, range)
|
26
26
|
super(document)
|
27
27
|
|
28
|
-
@hints = T.let([], T::Array[
|
28
|
+
@hints = T.let([], T::Array[Interface::InlayHint])
|
29
29
|
@range = range
|
30
30
|
end
|
31
31
|
|
32
|
-
sig { override.returns(T.all(T::Array[
|
32
|
+
sig { override.returns(T.all(T::Array[Interface::InlayHint], Object)) }
|
33
33
|
def run
|
34
34
|
visit(@document.tree) if @document.parsed?
|
35
35
|
@hints
|
@@ -37,12 +37,13 @@ module RubyLsp
|
|
37
37
|
|
38
38
|
sig { override.params(node: SyntaxTree::Rescue).void }
|
39
39
|
def visit_rescue(node)
|
40
|
-
|
40
|
+
exception = node.exception
|
41
|
+
return unless exception.nil? || exception.exceptions.nil?
|
41
42
|
|
42
43
|
loc = node.location
|
43
44
|
return unless visible?(node, @range)
|
44
45
|
|
45
|
-
@hints <<
|
46
|
+
@hints << Interface::InlayHint.new(
|
46
47
|
position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
|
47
48
|
label: "StandardError",
|
48
49
|
padding_left: true,
|
@@ -63,13 +63,19 @@ module RubyLsp
|
|
63
63
|
|
64
64
|
case matched
|
65
65
|
when SyntaxTree::Command, SyntaxTree::CallNode, SyntaxTree::CommandCall
|
66
|
-
|
66
|
+
message = matched.message
|
67
|
+
return if message.is_a?(Symbol)
|
68
|
+
return unless message.value == "require"
|
67
69
|
|
68
70
|
args = matched.arguments
|
69
71
|
args = args.arguments if args.is_a?(SyntaxTree::ArgParen)
|
72
|
+
return if args.nil? || args.is_a?(SyntaxTree::ArgsForward)
|
70
73
|
|
71
|
-
|
72
|
-
return unless
|
74
|
+
argument = args.parts.first
|
75
|
+
return unless argument.is_a?(SyntaxTree::StringLiteral)
|
76
|
+
|
77
|
+
path_node = argument.parts.first
|
78
|
+
return unless path_node.is_a?(SyntaxTree::TStringContent)
|
73
79
|
return unless (path_node.location.start_char..path_node.location.end_char).cover?(position)
|
74
80
|
|
75
81
|
path_node
|
@@ -100,12 +100,12 @@ module RubyLsp
|
|
100
100
|
end
|
101
101
|
def create_selection_range(location, parent = nil)
|
102
102
|
RubyLsp::Requests::Support::SelectionRange.new(
|
103
|
-
range:
|
104
|
-
start:
|
103
|
+
range: Interface::Range.new(
|
104
|
+
start: Interface::Position.new(
|
105
105
|
line: location.start_line - 1,
|
106
106
|
character: location.start_column,
|
107
107
|
),
|
108
|
-
end:
|
108
|
+
end: Interface::Position.new(
|
109
109
|
line: location.end_line - 1,
|
110
110
|
character: location.end_column,
|
111
111
|
),
|
@@ -122,7 +122,7 @@ module RubyLsp
|
|
122
122
|
sig do
|
123
123
|
override.returns(
|
124
124
|
T.any(
|
125
|
-
|
125
|
+
Interface::SemanticTokens,
|
126
126
|
T.all(T::Array[SemanticToken], Object),
|
127
127
|
),
|
128
128
|
)
|
@@ -143,7 +143,7 @@ module RubyLsp
|
|
143
143
|
visit(node.receiver)
|
144
144
|
|
145
145
|
message = node.message
|
146
|
-
if message
|
146
|
+
if !message.is_a?(Symbol) && !special_method?(message.value)
|
147
147
|
type = Support::Sorbet.annotation?(node) ? :type : :method
|
148
148
|
|
149
149
|
add_token(message.location, type)
|
@@ -160,6 +160,7 @@ module RubyLsp
|
|
160
160
|
add_token(node.message.location, :method)
|
161
161
|
end
|
162
162
|
visit(node.arguments)
|
163
|
+
visit(node.block)
|
163
164
|
end
|
164
165
|
|
165
166
|
sig { override.params(node: SyntaxTree::CommandCall).void }
|
@@ -167,8 +168,10 @@ module RubyLsp
|
|
167
168
|
return super unless visible?(node, @range)
|
168
169
|
|
169
170
|
visit(node.receiver)
|
170
|
-
|
171
|
+
message = node.message
|
172
|
+
add_token(message.location, :method) unless message.is_a?(Symbol)
|
171
173
|
visit(node.arguments)
|
174
|
+
visit(node.block)
|
172
175
|
end
|
173
176
|
|
174
177
|
sig { override.params(node: SyntaxTree::Const).void }
|
@@ -203,7 +206,7 @@ module RubyLsp
|
|
203
206
|
def visit_params(node)
|
204
207
|
return super unless visible?(node, @range)
|
205
208
|
|
206
|
-
node.keywords.each do |keyword
|
209
|
+
node.keywords.each do |keyword, *|
|
207
210
|
location = keyword.location
|
208
211
|
add_token(location_without_colon(location), :parameter)
|
209
212
|
end
|
@@ -213,7 +216,7 @@ module RubyLsp
|
|
213
216
|
end
|
214
217
|
|
215
218
|
rest = node.keyword_rest
|
216
|
-
if rest && !rest.is_a?(SyntaxTree::ArgsForward)
|
219
|
+
if rest && !rest.is_a?(SyntaxTree::ArgsForward) && !rest.is_a?(Symbol)
|
217
220
|
name = rest.name
|
218
221
|
add_token(name.location, :parameter) if name
|
219
222
|
end
|
@@ -240,9 +243,13 @@ module RubyLsp
|
|
240
243
|
when SyntaxTree::Ident
|
241
244
|
type = type_for_local(value)
|
242
245
|
add_token(value.location, type)
|
246
|
+
when Symbol
|
247
|
+
# do nothing
|
243
248
|
else
|
244
249
|
visit(value)
|
245
250
|
end
|
251
|
+
|
252
|
+
super
|
246
253
|
end
|
247
254
|
|
248
255
|
sig { override.params(node: SyntaxTree::VarRef).void }
|
@@ -255,27 +262,84 @@ module RubyLsp
|
|
255
262
|
when SyntaxTree::Ident
|
256
263
|
type = type_for_local(value)
|
257
264
|
add_token(value.location, type)
|
265
|
+
when Symbol
|
266
|
+
# do nothing
|
258
267
|
else
|
259
268
|
visit(value)
|
260
269
|
end
|
261
270
|
end
|
262
271
|
|
272
|
+
# All block locals are variables. E.g.: [].each do |x; block_local|
|
273
|
+
sig { override.params(node: SyntaxTree::BlockVar).void }
|
274
|
+
def visit_block_var(node)
|
275
|
+
node.locals.each { |local| add_token(local.location, :variable) }
|
276
|
+
super
|
277
|
+
end
|
278
|
+
|
279
|
+
# All lambda locals are variables. E.g.: ->(x; lambda_local) {}
|
280
|
+
sig { override.params(node: SyntaxTree::LambdaVar).void }
|
281
|
+
def visit_lambda_var(node)
|
282
|
+
node.locals.each { |local| add_token(local.location, :variable) }
|
283
|
+
super
|
284
|
+
end
|
285
|
+
|
263
286
|
sig { override.params(node: SyntaxTree::VCall).void }
|
264
287
|
def visit_vcall(node)
|
265
288
|
return super unless visible?(node, @range)
|
266
289
|
|
267
|
-
|
290
|
+
# A VCall may exist as a local in the current_scope. This happens when used named capture groups in a regexp
|
291
|
+
ident = node.value
|
292
|
+
value = ident.value
|
293
|
+
local = current_scope.find_local(value)
|
294
|
+
return if local.nil? && special_method?(value)
|
295
|
+
|
296
|
+
type = if local
|
297
|
+
:variable
|
298
|
+
elsif Support::Sorbet.annotation?(node)
|
299
|
+
:type
|
300
|
+
else
|
301
|
+
:method
|
302
|
+
end
|
268
303
|
|
269
|
-
type = Support::Sorbet.annotation?(node) ? :type : :method
|
270
304
|
add_token(node.value.location, type)
|
271
305
|
end
|
272
306
|
|
307
|
+
sig { override.params(node: SyntaxTree::Binary).void }
|
308
|
+
def visit_binary(node)
|
309
|
+
# It's important to visit the regexp first in the WithScope module
|
310
|
+
super
|
311
|
+
|
312
|
+
# You can only capture local variables with regexp by using the =~ operator
|
313
|
+
return unless node.operator == :=~
|
314
|
+
|
315
|
+
left = node.left
|
316
|
+
# The regexp needs to be on the left hand side of the =~ for local variable capture
|
317
|
+
return unless left.is_a?(SyntaxTree::RegexpLiteral)
|
318
|
+
|
319
|
+
parts = left.parts
|
320
|
+
return unless parts.one?
|
321
|
+
|
322
|
+
content = parts.first
|
323
|
+
return unless content.is_a?(SyntaxTree::TStringContent)
|
324
|
+
|
325
|
+
# For each capture name we find in the regexp, look for a local in the current_scope
|
326
|
+
Regexp.new(content.value, Regexp::FIXEDENCODING).names.each do |name|
|
327
|
+
local = current_scope.find_local(name)
|
328
|
+
next unless local
|
329
|
+
|
330
|
+
local.definitions.each { |definition| add_token(definition, :variable) }
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
273
334
|
sig { override.params(node: SyntaxTree::ClassDeclaration).void }
|
274
335
|
def visit_class(node)
|
275
336
|
return super unless visible?(node, @range)
|
276
337
|
|
277
338
|
add_token(node.constant.location, :class, [:declaration])
|
278
|
-
|
339
|
+
|
340
|
+
superclass = node.superclass
|
341
|
+
add_token(superclass.location, :class) if superclass
|
342
|
+
|
279
343
|
visit(node.bodystmt)
|
280
344
|
end
|
281
345
|
|
@@ -7,8 +7,8 @@ module RubyLsp
|
|
7
7
|
class HighlightTarget
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
READ =
|
11
|
-
WRITE =
|
10
|
+
READ = Constant::DocumentHighlightKind::READ
|
11
|
+
WRITE = Constant::DocumentHighlightKind::WRITE
|
12
12
|
|
13
13
|
class HighlightMatch
|
14
14
|
extend T::Sig
|
@@ -84,10 +84,11 @@ module RubyLsp
|
|
84
84
|
SyntaxTree::KwRestParam, SyntaxTree::BlockArg
|
85
85
|
node.name&.value
|
86
86
|
when SyntaxTree::VarField, SyntaxTree::VarRef, SyntaxTree::VCall
|
87
|
-
node.value
|
87
|
+
value = node.value
|
88
|
+
value.value unless value.nil? || value.is_a?(Symbol)
|
88
89
|
when SyntaxTree::CallNode, SyntaxTree::Command, SyntaxTree::CommandCall
|
89
90
|
message = node.message
|
90
|
-
message
|
91
|
+
message.value unless message.is_a?(Symbol)
|
91
92
|
when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
|
92
93
|
node.constant.constant.value
|
93
94
|
end
|