ruby-lsp 0.13.2 → 0.13.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|