ruby-lsp 0.13.2 → 0.13.4
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 +30 -0
- data/VERSION +1 -1
- data/lib/ruby_lsp/check_docs.rb +3 -3
- data/lib/ruby_lsp/document.rb +12 -0
- data/lib/ruby_lsp/executor.rb +77 -266
- data/lib/ruby_lsp/listener.rb +1 -50
- data/lib/ruby_lsp/listeners/code_lens.rb +233 -0
- data/lib/ruby_lsp/listeners/completion.rb +275 -0
- data/lib/ruby_lsp/listeners/definition.rb +158 -0
- data/lib/ruby_lsp/listeners/document_highlight.rb +556 -0
- data/lib/ruby_lsp/listeners/document_link.rb +162 -0
- data/lib/ruby_lsp/listeners/document_symbol.rb +223 -0
- data/lib/ruby_lsp/listeners/folding_ranges.rb +271 -0
- data/lib/ruby_lsp/listeners/hover.rb +152 -0
- data/lib/ruby_lsp/listeners/inlay_hints.rb +80 -0
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +430 -0
- data/lib/ruby_lsp/listeners/signature_help.rb +74 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +4 -4
- data/lib/ruby_lsp/requests/code_actions.rb +13 -4
- data/lib/ruby_lsp/requests/code_lens.rb +21 -221
- data/lib/ruby_lsp/requests/completion.rb +64 -244
- data/lib/ruby_lsp/requests/definition.rb +34 -147
- data/lib/ruby_lsp/requests/diagnostics.rb +17 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +12 -536
- data/lib/ruby_lsp/requests/document_link.rb +11 -132
- data/lib/ruby_lsp/requests/document_symbol.rb +23 -210
- data/lib/ruby_lsp/requests/folding_ranges.rb +16 -252
- data/lib/ruby_lsp/requests/formatting.rb +4 -4
- data/lib/ruby_lsp/requests/hover.rb +48 -92
- data/lib/ruby_lsp/requests/inlay_hints.rb +23 -56
- data/lib/ruby_lsp/requests/on_type_formatting.rb +16 -4
- data/lib/ruby_lsp/requests/request.rb +17 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +4 -3
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +21 -408
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -4
- data/lib/ruby_lsp/requests/signature_help.rb +43 -51
- data/lib/ruby_lsp/requests/support/common.rb +3 -2
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +2 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
- data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
- data/lib/ruby_lsp/requests.rb +1 -1
- data/lib/ruby_lsp/utils.rb +8 -0
- metadata +17 -6
- data/lib/ruby_lsp/requests/base_request.rb +0 -24
@@ -0,0 +1,152 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class Hover < Listener
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } }
|
11
|
+
|
12
|
+
ALLOWED_TARGETS = T.let(
|
13
|
+
[
|
14
|
+
Prism::CallNode,
|
15
|
+
Prism::ConstantReadNode,
|
16
|
+
Prism::ConstantWriteNode,
|
17
|
+
Prism::ConstantPathNode,
|
18
|
+
],
|
19
|
+
T::Array[T.class_of(Prism::Node)],
|
20
|
+
)
|
21
|
+
|
22
|
+
sig { override.returns(ResponseType) }
|
23
|
+
attr_reader :_response
|
24
|
+
|
25
|
+
sig do
|
26
|
+
params(
|
27
|
+
uri: URI::Generic,
|
28
|
+
nesting: T::Array[String],
|
29
|
+
index: RubyIndexer::Index,
|
30
|
+
dispatcher: Prism::Dispatcher,
|
31
|
+
typechecker_enabled: T::Boolean,
|
32
|
+
).void
|
33
|
+
end
|
34
|
+
def initialize(uri, nesting, index, dispatcher, typechecker_enabled)
|
35
|
+
@path = T.let(uri.to_standardized_path, T.nilable(String))
|
36
|
+
@nesting = nesting
|
37
|
+
@index = index
|
38
|
+
@typechecker_enabled = typechecker_enabled
|
39
|
+
@_response = T.let(nil, ResponseType)
|
40
|
+
|
41
|
+
super(dispatcher)
|
42
|
+
dispatcher.register(
|
43
|
+
self,
|
44
|
+
:on_constant_read_node_enter,
|
45
|
+
:on_constant_write_node_enter,
|
46
|
+
:on_constant_path_node_enter,
|
47
|
+
:on_call_node_enter,
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
sig { params(node: Prism::ConstantReadNode).void }
|
52
|
+
def on_constant_read_node_enter(node)
|
53
|
+
return if @typechecker_enabled
|
54
|
+
|
55
|
+
generate_hover(node.slice, node.location)
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { params(node: Prism::ConstantWriteNode).void }
|
59
|
+
def on_constant_write_node_enter(node)
|
60
|
+
return if DependencyDetector.instance.typechecker
|
61
|
+
|
62
|
+
generate_hover(node.name.to_s, node.name_loc)
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { params(node: Prism::ConstantPathNode).void }
|
66
|
+
def on_constant_path_node_enter(node)
|
67
|
+
return if DependencyDetector.instance.typechecker
|
68
|
+
|
69
|
+
generate_hover(node.slice, node.location)
|
70
|
+
end
|
71
|
+
|
72
|
+
sig { params(node: Prism::CallNode).void }
|
73
|
+
def on_call_node_enter(node)
|
74
|
+
return unless self_receiver?(node)
|
75
|
+
|
76
|
+
if @path && File.basename(@path) == GEMFILE_NAME && node.name == :gem
|
77
|
+
generate_gem_hover(node)
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
return if @typechecker_enabled
|
82
|
+
|
83
|
+
message = node.message
|
84
|
+
return unless message
|
85
|
+
|
86
|
+
target_method = @index.resolve_method(message, @nesting.join("::"))
|
87
|
+
return unless target_method
|
88
|
+
|
89
|
+
location = target_method.location
|
90
|
+
|
91
|
+
@_response = Interface::Hover.new(
|
92
|
+
range: range_from_location(location),
|
93
|
+
contents: markdown_from_index_entries(message, target_method),
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
sig { params(name: String, location: Prism::Location).void }
|
100
|
+
def generate_hover(name, location)
|
101
|
+
entries = @index.resolve(name, @nesting)
|
102
|
+
return unless entries
|
103
|
+
|
104
|
+
# We should only show hover for private constants if the constant is defined in the same namespace as the
|
105
|
+
# reference
|
106
|
+
first_entry = T.must(entries.first)
|
107
|
+
return if first_entry.visibility == :private && first_entry.name != "#{@nesting.join("::")}::#{name}"
|
108
|
+
|
109
|
+
@_response = Interface::Hover.new(
|
110
|
+
range: range_from_location(location),
|
111
|
+
contents: markdown_from_index_entries(name, entries),
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
sig { params(node: Prism::CallNode).void }
|
116
|
+
def generate_gem_hover(node)
|
117
|
+
first_argument = node.arguments&.arguments&.first
|
118
|
+
return unless first_argument.is_a?(Prism::StringNode)
|
119
|
+
|
120
|
+
spec = Gem::Specification.find_by_name(first_argument.content)
|
121
|
+
return unless spec
|
122
|
+
|
123
|
+
info = T.let(
|
124
|
+
[
|
125
|
+
spec.description,
|
126
|
+
spec.summary,
|
127
|
+
"This rubygem does not have a description or summary.",
|
128
|
+
].find { |text| !text.nil? && !text.empty? },
|
129
|
+
String,
|
130
|
+
)
|
131
|
+
|
132
|
+
# Remove leading whitespace if a heredoc was used for the summary or description
|
133
|
+
info = info.gsub(/^ +/, "")
|
134
|
+
|
135
|
+
markdown = <<~MARKDOWN
|
136
|
+
**#{spec.name}** (#{spec.version})
|
137
|
+
#{info}
|
138
|
+
MARKDOWN
|
139
|
+
|
140
|
+
@_response = Interface::Hover.new(
|
141
|
+
range: range_from_location(node.location),
|
142
|
+
contents: Interface::MarkupContent.new(
|
143
|
+
kind: Constant::MarkupKind::MARKDOWN,
|
144
|
+
value: markdown,
|
145
|
+
),
|
146
|
+
)
|
147
|
+
rescue Gem::MissingSpecError
|
148
|
+
# Do nothing if the spec cannot be found
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class InlayHints < Listener
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
ResponseType = type_member { { fixed: T::Array[Interface::InlayHint] } }
|
11
|
+
|
12
|
+
RESCUE_STRING_LENGTH = T.let("rescue".length, Integer)
|
13
|
+
|
14
|
+
sig { override.returns(ResponseType) }
|
15
|
+
attr_reader :_response
|
16
|
+
|
17
|
+
sig do
|
18
|
+
params(
|
19
|
+
range: T::Range[Integer],
|
20
|
+
hints_configuration: RequestConfig,
|
21
|
+
dispatcher: Prism::Dispatcher,
|
22
|
+
).void
|
23
|
+
end
|
24
|
+
def initialize(range, hints_configuration, dispatcher)
|
25
|
+
super(dispatcher)
|
26
|
+
|
27
|
+
@_response = T.let([], ResponseType)
|
28
|
+
@range = range
|
29
|
+
@hints_configuration = hints_configuration
|
30
|
+
|
31
|
+
dispatcher.register(self, :on_rescue_node_enter, :on_implicit_node_enter)
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { params(node: Prism::RescueNode).void }
|
35
|
+
def on_rescue_node_enter(node)
|
36
|
+
return unless @hints_configuration.enabled?(:implicitRescue)
|
37
|
+
return unless node.exceptions.empty?
|
38
|
+
|
39
|
+
loc = node.location
|
40
|
+
return unless visible?(node, @range)
|
41
|
+
|
42
|
+
@_response << Interface::InlayHint.new(
|
43
|
+
position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
|
44
|
+
label: "StandardError",
|
45
|
+
padding_left: true,
|
46
|
+
tooltip: "StandardError is implied in a bare rescue",
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { params(node: Prism::ImplicitNode).void }
|
51
|
+
def on_implicit_node_enter(node)
|
52
|
+
return unless @hints_configuration.enabled?(:implicitHashValue)
|
53
|
+
return unless visible?(node, @range)
|
54
|
+
|
55
|
+
node_value = node.value
|
56
|
+
loc = node.location
|
57
|
+
tooltip = ""
|
58
|
+
node_name = ""
|
59
|
+
case node_value
|
60
|
+
when Prism::CallNode
|
61
|
+
node_name = node_value.name
|
62
|
+
tooltip = "This is a method call. Method name: #{node_name}"
|
63
|
+
when Prism::ConstantReadNode
|
64
|
+
node_name = node_value.name
|
65
|
+
tooltip = "This is a constant: #{node_name}"
|
66
|
+
when Prism::LocalVariableReadNode
|
67
|
+
node_name = node_value.name
|
68
|
+
tooltip = "This is a local variable: #{node_name}"
|
69
|
+
end
|
70
|
+
|
71
|
+
@_response << Interface::InlayHint.new(
|
72
|
+
position: { line: loc.start_line - 1, character: loc.start_column + node_name.length + 1 },
|
73
|
+
label: node_name,
|
74
|
+
padding_left: true,
|
75
|
+
tooltip: tooltip,
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,430 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class SemanticHighlighting < Listener
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
ResponseType = type_member { { fixed: T::Array[SemanticToken] } }
|
11
|
+
|
12
|
+
TOKEN_TYPES = T.let(
|
13
|
+
{
|
14
|
+
namespace: 0,
|
15
|
+
type: 1,
|
16
|
+
class: 2,
|
17
|
+
enum: 3,
|
18
|
+
interface: 4,
|
19
|
+
struct: 5,
|
20
|
+
typeParameter: 6,
|
21
|
+
parameter: 7,
|
22
|
+
variable: 8,
|
23
|
+
property: 9,
|
24
|
+
enumMember: 10,
|
25
|
+
event: 11,
|
26
|
+
function: 12,
|
27
|
+
method: 13,
|
28
|
+
macro: 14,
|
29
|
+
keyword: 15,
|
30
|
+
modifier: 16,
|
31
|
+
comment: 17,
|
32
|
+
string: 18,
|
33
|
+
number: 19,
|
34
|
+
regexp: 20,
|
35
|
+
operator: 21,
|
36
|
+
decorator: 22,
|
37
|
+
}.freeze,
|
38
|
+
T::Hash[Symbol, Integer],
|
39
|
+
)
|
40
|
+
|
41
|
+
TOKEN_MODIFIERS = T.let(
|
42
|
+
{
|
43
|
+
declaration: 0,
|
44
|
+
definition: 1,
|
45
|
+
readonly: 2,
|
46
|
+
static: 3,
|
47
|
+
deprecated: 4,
|
48
|
+
abstract: 5,
|
49
|
+
async: 6,
|
50
|
+
modification: 7,
|
51
|
+
documentation: 8,
|
52
|
+
default_library: 9,
|
53
|
+
}.freeze,
|
54
|
+
T::Hash[Symbol, Integer],
|
55
|
+
)
|
56
|
+
|
57
|
+
SPECIAL_RUBY_METHODS = T.let(
|
58
|
+
[
|
59
|
+
Module.instance_methods(false),
|
60
|
+
Kernel.instance_methods(false),
|
61
|
+
Kernel.methods(false),
|
62
|
+
Bundler::Dsl.instance_methods(false),
|
63
|
+
Module.private_instance_methods(false),
|
64
|
+
].flatten.map(&:to_s),
|
65
|
+
T::Array[String],
|
66
|
+
)
|
67
|
+
|
68
|
+
class SemanticToken
|
69
|
+
extend T::Sig
|
70
|
+
|
71
|
+
sig { returns(Prism::Location) }
|
72
|
+
attr_reader :location
|
73
|
+
|
74
|
+
sig { returns(Integer) }
|
75
|
+
attr_reader :length
|
76
|
+
|
77
|
+
sig { returns(Integer) }
|
78
|
+
attr_reader :type
|
79
|
+
|
80
|
+
sig { returns(T::Array[Integer]) }
|
81
|
+
attr_reader :modifier
|
82
|
+
|
83
|
+
sig { params(location: Prism::Location, length: Integer, type: Integer, modifier: T::Array[Integer]).void }
|
84
|
+
def initialize(location:, length:, type:, modifier:)
|
85
|
+
@location = location
|
86
|
+
@length = length
|
87
|
+
@type = type
|
88
|
+
@modifier = modifier
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
sig { override.returns(ResponseType) }
|
93
|
+
attr_reader :_response
|
94
|
+
|
95
|
+
sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void }
|
96
|
+
def initialize(dispatcher, range: nil)
|
97
|
+
super(dispatcher)
|
98
|
+
|
99
|
+
@_response = T.let([], ResponseType)
|
100
|
+
@range = range
|
101
|
+
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
102
|
+
@current_scope = T.let(ParameterScope.new, ParameterScope)
|
103
|
+
@inside_regex_capture = T.let(false, T::Boolean)
|
104
|
+
|
105
|
+
dispatcher.register(
|
106
|
+
self,
|
107
|
+
:on_call_node_enter,
|
108
|
+
:on_class_node_enter,
|
109
|
+
:on_def_node_enter,
|
110
|
+
:on_def_node_leave,
|
111
|
+
:on_block_node_enter,
|
112
|
+
:on_block_node_leave,
|
113
|
+
:on_self_node_enter,
|
114
|
+
:on_module_node_enter,
|
115
|
+
:on_local_variable_write_node_enter,
|
116
|
+
:on_local_variable_read_node_enter,
|
117
|
+
:on_block_parameter_node_enter,
|
118
|
+
:on_required_keyword_parameter_node_enter,
|
119
|
+
:on_optional_keyword_parameter_node_enter,
|
120
|
+
:on_keyword_rest_parameter_node_enter,
|
121
|
+
:on_optional_parameter_node_enter,
|
122
|
+
:on_required_parameter_node_enter,
|
123
|
+
:on_rest_parameter_node_enter,
|
124
|
+
:on_constant_read_node_enter,
|
125
|
+
:on_constant_write_node_enter,
|
126
|
+
:on_constant_and_write_node_enter,
|
127
|
+
:on_constant_operator_write_node_enter,
|
128
|
+
:on_constant_or_write_node_enter,
|
129
|
+
:on_constant_target_node_enter,
|
130
|
+
:on_local_variable_and_write_node_enter,
|
131
|
+
:on_local_variable_operator_write_node_enter,
|
132
|
+
:on_local_variable_or_write_node_enter,
|
133
|
+
:on_local_variable_target_node_enter,
|
134
|
+
:on_block_local_variable_node_enter,
|
135
|
+
:on_match_write_node_enter,
|
136
|
+
:on_match_write_node_leave,
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
sig { params(node: Prism::CallNode).void }
|
141
|
+
def on_call_node_enter(node)
|
142
|
+
return unless visible?(node, @range)
|
143
|
+
|
144
|
+
message = node.message
|
145
|
+
return unless message
|
146
|
+
|
147
|
+
# We can't push a semantic token for [] and []= because the argument inside the brackets is a part of
|
148
|
+
# the message_loc
|
149
|
+
return if message.start_with?("[") && (message.end_with?("]") || message.end_with?("]="))
|
150
|
+
return if message == "=~"
|
151
|
+
return if special_method?(message)
|
152
|
+
|
153
|
+
type = Requests::Support::Sorbet.annotation?(node) ? :type : :method
|
154
|
+
add_token(T.must(node.message_loc), type)
|
155
|
+
end
|
156
|
+
|
157
|
+
sig { params(node: Prism::MatchWriteNode).void }
|
158
|
+
def on_match_write_node_enter(node)
|
159
|
+
call = node.call
|
160
|
+
|
161
|
+
if call.message == "=~"
|
162
|
+
@inside_regex_capture = true
|
163
|
+
process_regexp_locals(call)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
sig { params(node: Prism::MatchWriteNode).void }
|
168
|
+
def on_match_write_node_leave(node)
|
169
|
+
@inside_regex_capture = true if node.call.message == "=~"
|
170
|
+
end
|
171
|
+
|
172
|
+
sig { params(node: Prism::ConstantReadNode).void }
|
173
|
+
def on_constant_read_node_enter(node)
|
174
|
+
return unless visible?(node, @range)
|
175
|
+
# When finding a module or class definition, we will have already pushed a token related to this constant. We
|
176
|
+
# need to look at the previous two tokens and if they match this locatione exactly, avoid pushing another token
|
177
|
+
# on top of the previous one
|
178
|
+
return if @_response.last(2).any? { |token| token.location == node.location }
|
179
|
+
|
180
|
+
add_token(node.location, :namespace)
|
181
|
+
end
|
182
|
+
|
183
|
+
sig { params(node: Prism::ConstantWriteNode).void }
|
184
|
+
def on_constant_write_node_enter(node)
|
185
|
+
return unless visible?(node, @range)
|
186
|
+
|
187
|
+
add_token(node.name_loc, :namespace)
|
188
|
+
end
|
189
|
+
|
190
|
+
sig { params(node: Prism::ConstantAndWriteNode).void }
|
191
|
+
def on_constant_and_write_node_enter(node)
|
192
|
+
return unless visible?(node, @range)
|
193
|
+
|
194
|
+
add_token(node.name_loc, :namespace)
|
195
|
+
end
|
196
|
+
|
197
|
+
sig { params(node: Prism::ConstantOperatorWriteNode).void }
|
198
|
+
def on_constant_operator_write_node_enter(node)
|
199
|
+
return unless visible?(node, @range)
|
200
|
+
|
201
|
+
add_token(node.name_loc, :namespace)
|
202
|
+
end
|
203
|
+
|
204
|
+
sig { params(node: Prism::ConstantOrWriteNode).void }
|
205
|
+
def on_constant_or_write_node_enter(node)
|
206
|
+
return unless visible?(node, @range)
|
207
|
+
|
208
|
+
add_token(node.name_loc, :namespace)
|
209
|
+
end
|
210
|
+
|
211
|
+
sig { params(node: Prism::ConstantTargetNode).void }
|
212
|
+
def on_constant_target_node_enter(node)
|
213
|
+
return unless visible?(node, @range)
|
214
|
+
|
215
|
+
add_token(node.location, :namespace)
|
216
|
+
end
|
217
|
+
|
218
|
+
sig { params(node: Prism::DefNode).void }
|
219
|
+
def on_def_node_enter(node)
|
220
|
+
@current_scope = ParameterScope.new(@current_scope)
|
221
|
+
return unless visible?(node, @range)
|
222
|
+
|
223
|
+
add_token(node.name_loc, :method, [:declaration])
|
224
|
+
end
|
225
|
+
|
226
|
+
sig { params(node: Prism::DefNode).void }
|
227
|
+
def on_def_node_leave(node)
|
228
|
+
@current_scope = T.must(@current_scope.parent)
|
229
|
+
end
|
230
|
+
|
231
|
+
sig { params(node: Prism::BlockNode).void }
|
232
|
+
def on_block_node_enter(node)
|
233
|
+
@current_scope = ParameterScope.new(@current_scope)
|
234
|
+
end
|
235
|
+
|
236
|
+
sig { params(node: Prism::BlockNode).void }
|
237
|
+
def on_block_node_leave(node)
|
238
|
+
@current_scope = T.must(@current_scope.parent)
|
239
|
+
end
|
240
|
+
|
241
|
+
sig { params(node: Prism::BlockLocalVariableNode).void }
|
242
|
+
def on_block_local_variable_node_enter(node)
|
243
|
+
add_token(node.location, :variable)
|
244
|
+
end
|
245
|
+
|
246
|
+
sig { params(node: Prism::BlockParameterNode).void }
|
247
|
+
def on_block_parameter_node_enter(node)
|
248
|
+
name = node.name
|
249
|
+
@current_scope << name.to_sym if name
|
250
|
+
end
|
251
|
+
|
252
|
+
sig { params(node: Prism::RequiredKeywordParameterNode).void }
|
253
|
+
def on_required_keyword_parameter_node_enter(node)
|
254
|
+
@current_scope << node.name
|
255
|
+
return unless visible?(node, @range)
|
256
|
+
|
257
|
+
location = node.name_loc
|
258
|
+
add_token(location.copy(length: location.length - 1), :parameter)
|
259
|
+
end
|
260
|
+
|
261
|
+
sig { params(node: Prism::OptionalKeywordParameterNode).void }
|
262
|
+
def on_optional_keyword_parameter_node_enter(node)
|
263
|
+
@current_scope << node.name
|
264
|
+
return unless visible?(node, @range)
|
265
|
+
|
266
|
+
location = node.name_loc
|
267
|
+
add_token(location.copy(length: location.length - 1), :parameter)
|
268
|
+
end
|
269
|
+
|
270
|
+
sig { params(node: Prism::KeywordRestParameterNode).void }
|
271
|
+
def on_keyword_rest_parameter_node_enter(node)
|
272
|
+
name = node.name
|
273
|
+
|
274
|
+
if name
|
275
|
+
@current_scope << name.to_sym
|
276
|
+
|
277
|
+
add_token(T.must(node.name_loc), :parameter) if visible?(node, @range)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
sig { params(node: Prism::OptionalParameterNode).void }
|
282
|
+
def on_optional_parameter_node_enter(node)
|
283
|
+
@current_scope << node.name
|
284
|
+
return unless visible?(node, @range)
|
285
|
+
|
286
|
+
add_token(node.name_loc, :parameter)
|
287
|
+
end
|
288
|
+
|
289
|
+
sig { params(node: Prism::RequiredParameterNode).void }
|
290
|
+
def on_required_parameter_node_enter(node)
|
291
|
+
@current_scope << node.name
|
292
|
+
return unless visible?(node, @range)
|
293
|
+
|
294
|
+
add_token(node.location, :parameter)
|
295
|
+
end
|
296
|
+
|
297
|
+
sig { params(node: Prism::RestParameterNode).void }
|
298
|
+
def on_rest_parameter_node_enter(node)
|
299
|
+
name = node.name
|
300
|
+
|
301
|
+
if name
|
302
|
+
@current_scope << name.to_sym
|
303
|
+
|
304
|
+
add_token(T.must(node.name_loc), :parameter) if visible?(node, @range)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
sig { params(node: Prism::SelfNode).void }
|
309
|
+
def on_self_node_enter(node)
|
310
|
+
return unless visible?(node, @range)
|
311
|
+
|
312
|
+
add_token(node.location, :variable, [:default_library])
|
313
|
+
end
|
314
|
+
|
315
|
+
sig { params(node: Prism::LocalVariableWriteNode).void }
|
316
|
+
def on_local_variable_write_node_enter(node)
|
317
|
+
return unless visible?(node, @range)
|
318
|
+
|
319
|
+
add_token(node.name_loc, @current_scope.type_for(node.name))
|
320
|
+
end
|
321
|
+
|
322
|
+
sig { params(node: Prism::LocalVariableReadNode).void }
|
323
|
+
def on_local_variable_read_node_enter(node)
|
324
|
+
return unless visible?(node, @range)
|
325
|
+
|
326
|
+
# Numbered parameters
|
327
|
+
if /_\d+/.match?(node.name)
|
328
|
+
add_token(node.location, :parameter)
|
329
|
+
return
|
330
|
+
end
|
331
|
+
|
332
|
+
add_token(node.location, @current_scope.type_for(node.name))
|
333
|
+
end
|
334
|
+
|
335
|
+
sig { params(node: Prism::LocalVariableAndWriteNode).void }
|
336
|
+
def on_local_variable_and_write_node_enter(node)
|
337
|
+
return unless visible?(node, @range)
|
338
|
+
|
339
|
+
add_token(node.name_loc, @current_scope.type_for(node.name))
|
340
|
+
end
|
341
|
+
|
342
|
+
sig { params(node: Prism::LocalVariableOperatorWriteNode).void }
|
343
|
+
def on_local_variable_operator_write_node_enter(node)
|
344
|
+
return unless visible?(node, @range)
|
345
|
+
|
346
|
+
add_token(node.name_loc, @current_scope.type_for(node.name))
|
347
|
+
end
|
348
|
+
|
349
|
+
sig { params(node: Prism::LocalVariableOrWriteNode).void }
|
350
|
+
def on_local_variable_or_write_node_enter(node)
|
351
|
+
return unless visible?(node, @range)
|
352
|
+
|
353
|
+
add_token(node.name_loc, @current_scope.type_for(node.name))
|
354
|
+
end
|
355
|
+
|
356
|
+
sig { params(node: Prism::LocalVariableTargetNode).void }
|
357
|
+
def on_local_variable_target_node_enter(node)
|
358
|
+
# If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
|
359
|
+
# Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
|
360
|
+
# the entire regex as a local variable. We process these captures in process_regexp_locals instead and then
|
361
|
+
# prevent pushing local variable target tokens. See https://github.com/ruby/prism/issues/1912
|
362
|
+
return if @inside_regex_capture
|
363
|
+
|
364
|
+
return unless visible?(node, @range)
|
365
|
+
|
366
|
+
add_token(node.location, @current_scope.type_for(node.name))
|
367
|
+
end
|
368
|
+
|
369
|
+
sig { params(node: Prism::ClassNode).void }
|
370
|
+
def on_class_node_enter(node)
|
371
|
+
return unless visible?(node, @range)
|
372
|
+
|
373
|
+
add_token(node.constant_path.location, :class, [:declaration])
|
374
|
+
|
375
|
+
superclass = node.superclass
|
376
|
+
add_token(superclass.location, :class) if superclass
|
377
|
+
end
|
378
|
+
|
379
|
+
sig { params(node: Prism::ModuleNode).void }
|
380
|
+
def on_module_node_enter(node)
|
381
|
+
return unless visible?(node, @range)
|
382
|
+
|
383
|
+
add_token(node.constant_path.location, :namespace, [:declaration])
|
384
|
+
end
|
385
|
+
|
386
|
+
private
|
387
|
+
|
388
|
+
sig { params(location: Prism::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
|
389
|
+
def add_token(location, type, modifiers = [])
|
390
|
+
length = location.end_offset - location.start_offset
|
391
|
+
modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
|
392
|
+
@_response.push(
|
393
|
+
SemanticToken.new(
|
394
|
+
location: location,
|
395
|
+
length: length,
|
396
|
+
type: T.must(TOKEN_TYPES[type]),
|
397
|
+
modifier: modifiers_indices,
|
398
|
+
),
|
399
|
+
)
|
400
|
+
end
|
401
|
+
|
402
|
+
# Textmate provides highlighting for a subset of these special Ruby-specific methods. We want to utilize that
|
403
|
+
# highlighting, so we avoid making a semantic token for it.
|
404
|
+
sig { params(method_name: String).returns(T::Boolean) }
|
405
|
+
def special_method?(method_name)
|
406
|
+
SPECIAL_RUBY_METHODS.include?(method_name)
|
407
|
+
end
|
408
|
+
|
409
|
+
sig { params(node: Prism::CallNode).void }
|
410
|
+
def process_regexp_locals(node)
|
411
|
+
receiver = node.receiver
|
412
|
+
|
413
|
+
# The regexp needs to be the receiver of =~ for local variable capture
|
414
|
+
return unless receiver.is_a?(Prism::RegularExpressionNode)
|
415
|
+
|
416
|
+
content = receiver.content
|
417
|
+
loc = receiver.content_loc
|
418
|
+
|
419
|
+
# For each capture name we find in the regexp, look for a local in the current_scope
|
420
|
+
Regexp.new(content, Regexp::FIXEDENCODING).names.each do |name|
|
421
|
+
# The +3 is to compensate for the "(?<" part of the capture name
|
422
|
+
capture_name_offset = T.must(content.index("(?<#{name}>")) + 3
|
423
|
+
local_var_loc = loc.copy(start_offset: loc.start_offset + capture_name_offset, length: name.length)
|
424
|
+
|
425
|
+
add_token(local_var_loc, @current_scope.type_for(name))
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|