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,162 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "ruby_lsp/requests/support/source_uri"
|
5
|
+
|
6
|
+
module RubyLsp
|
7
|
+
module Listeners
|
8
|
+
class DocumentLink < Listener
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Generic
|
11
|
+
|
12
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
|
13
|
+
|
14
|
+
GEM_TO_VERSION_MAP = T.let(
|
15
|
+
[*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
|
16
|
+
[s.name, s.version.to_s]
|
17
|
+
end.to_h.freeze,
|
18
|
+
T::Hash[String, String],
|
19
|
+
)
|
20
|
+
|
21
|
+
class << self
|
22
|
+
extend T::Sig
|
23
|
+
|
24
|
+
sig { returns(T::Hash[String, T::Hash[String, T::Hash[String, String]]]) }
|
25
|
+
def gem_paths
|
26
|
+
@gem_paths ||= T.let(
|
27
|
+
begin
|
28
|
+
lookup = {}
|
29
|
+
|
30
|
+
Gem::Specification.stubs.each do |stub|
|
31
|
+
spec = stub.to_spec
|
32
|
+
lookup[spec.name] = {}
|
33
|
+
lookup[spec.name][spec.version.to_s] = {}
|
34
|
+
|
35
|
+
Dir.glob("**/*.rb", base: "#{spec.full_gem_path}/").each do |path|
|
36
|
+
lookup[spec.name][spec.version.to_s][path] = "#{spec.full_gem_path}/#{path}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Gem::Specification.default_stubs.each do |stub|
|
41
|
+
spec = stub.to_spec
|
42
|
+
lookup[spec.name] = {}
|
43
|
+
lookup[spec.name][spec.version.to_s] = {}
|
44
|
+
prefix_matchers = Regexp.union(spec.require_paths.map do |rp|
|
45
|
+
Regexp.new("^#{rp}/")
|
46
|
+
end)
|
47
|
+
prefix_matcher = Regexp.union(prefix_matchers, //)
|
48
|
+
|
49
|
+
spec.files.each do |file|
|
50
|
+
path = file.sub(prefix_matcher, "")
|
51
|
+
lookup[spec.name][spec.version.to_s][path] = "#{RbConfig::CONFIG["rubylibdir"]}/#{path}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
lookup
|
56
|
+
end,
|
57
|
+
T.nilable(T::Hash[String, T::Hash[String, T::Hash[String, String]]]),
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { override.returns(ResponseType) }
|
63
|
+
attr_reader :_response
|
64
|
+
|
65
|
+
sig do
|
66
|
+
params(
|
67
|
+
uri: URI::Generic,
|
68
|
+
comments: T::Array[Prism::Comment],
|
69
|
+
dispatcher: Prism::Dispatcher,
|
70
|
+
).void
|
71
|
+
end
|
72
|
+
def initialize(uri, comments, dispatcher)
|
73
|
+
super(dispatcher)
|
74
|
+
|
75
|
+
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
76
|
+
# in the URI
|
77
|
+
path = uri.to_standardized_path
|
78
|
+
version_match = path ? /(?<=%40)[\d.]+(?=\.rbi$)/.match(path) : nil
|
79
|
+
@gem_version = T.let(version_match && version_match[0], T.nilable(String))
|
80
|
+
@_response = T.let([], T::Array[Interface::DocumentLink])
|
81
|
+
@lines_to_comments = T.let(
|
82
|
+
comments.to_h do |comment|
|
83
|
+
[comment.location.end_line, comment]
|
84
|
+
end,
|
85
|
+
T::Hash[Integer, Prism::Comment],
|
86
|
+
)
|
87
|
+
|
88
|
+
dispatcher.register(
|
89
|
+
self,
|
90
|
+
:on_def_node_enter,
|
91
|
+
:on_class_node_enter,
|
92
|
+
:on_module_node_enter,
|
93
|
+
:on_constant_write_node_enter,
|
94
|
+
:on_constant_path_write_node_enter,
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(node: Prism::DefNode).void }
|
99
|
+
def on_def_node_enter(node)
|
100
|
+
extract_document_link(node)
|
101
|
+
end
|
102
|
+
|
103
|
+
sig { params(node: Prism::ClassNode).void }
|
104
|
+
def on_class_node_enter(node)
|
105
|
+
extract_document_link(node)
|
106
|
+
end
|
107
|
+
|
108
|
+
sig { params(node: Prism::ModuleNode).void }
|
109
|
+
def on_module_node_enter(node)
|
110
|
+
extract_document_link(node)
|
111
|
+
end
|
112
|
+
|
113
|
+
sig { params(node: Prism::ConstantWriteNode).void }
|
114
|
+
def on_constant_write_node_enter(node)
|
115
|
+
extract_document_link(node)
|
116
|
+
end
|
117
|
+
|
118
|
+
sig { params(node: Prism::ConstantPathWriteNode).void }
|
119
|
+
def on_constant_path_write_node_enter(node)
|
120
|
+
extract_document_link(node)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
sig { params(node: Prism::Node).void }
|
126
|
+
def extract_document_link(node)
|
127
|
+
comment = @lines_to_comments[node.location.start_line - 1]
|
128
|
+
return unless comment
|
129
|
+
|
130
|
+
match = comment.location.slice.match(%r{source://.*#\d+$})
|
131
|
+
return unless match
|
132
|
+
|
133
|
+
uri = T.cast(URI(T.must(match[0])), URI::Source)
|
134
|
+
gem_version = resolve_version(uri)
|
135
|
+
return if gem_version.nil?
|
136
|
+
|
137
|
+
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path))
|
138
|
+
return if file_path.nil?
|
139
|
+
|
140
|
+
@_response << Interface::DocumentLink.new(
|
141
|
+
range: range_from_location(comment.location),
|
142
|
+
target: "file://#{file_path}##{uri.line_number}",
|
143
|
+
tooltip: "Jump to #{file_path}##{uri.line_number}",
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Try to figure out the gem version for a source:// link. The order of precedence is:
|
148
|
+
# 1. The version in the URI
|
149
|
+
# 2. The version in the RBI file name
|
150
|
+
# 3. The version from the gemspec
|
151
|
+
sig { params(uri: URI::Source).returns(T.nilable(String)) }
|
152
|
+
def resolve_version(uri)
|
153
|
+
version = uri.gem_version
|
154
|
+
return version unless version.nil? || version.empty?
|
155
|
+
|
156
|
+
return @gem_version unless @gem_version.nil? || @gem_version.empty?
|
157
|
+
|
158
|
+
GEM_TO_VERSION_MAP[uri.gem_name]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class DocumentSymbol < Listener
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
|
11
|
+
|
12
|
+
ATTR_ACCESSORS = T.let([:attr_reader, :attr_writer, :attr_accessor].freeze, T::Array[Symbol])
|
13
|
+
|
14
|
+
class SymbolHierarchyRoot
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { returns(T::Array[Interface::DocumentSymbol]) }
|
18
|
+
attr_reader :children
|
19
|
+
|
20
|
+
sig { void }
|
21
|
+
def initialize
|
22
|
+
@children = T.let([], T::Array[Interface::DocumentSymbol])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.returns(T::Array[Interface::DocumentSymbol]) }
|
27
|
+
attr_reader :_response
|
28
|
+
|
29
|
+
sig { params(dispatcher: Prism::Dispatcher).void }
|
30
|
+
def initialize(dispatcher)
|
31
|
+
@root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
|
32
|
+
@_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
|
33
|
+
@stack = T.let(
|
34
|
+
[@root],
|
35
|
+
T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
|
36
|
+
)
|
37
|
+
|
38
|
+
super
|
39
|
+
|
40
|
+
dispatcher.register(
|
41
|
+
self,
|
42
|
+
:on_class_node_enter,
|
43
|
+
:on_class_node_leave,
|
44
|
+
:on_call_node_enter,
|
45
|
+
:on_constant_path_write_node_enter,
|
46
|
+
:on_constant_write_node_enter,
|
47
|
+
:on_def_node_enter,
|
48
|
+
:on_def_node_leave,
|
49
|
+
:on_module_node_enter,
|
50
|
+
:on_module_node_leave,
|
51
|
+
:on_instance_variable_write_node_enter,
|
52
|
+
:on_class_variable_write_node_enter,
|
53
|
+
:on_singleton_class_node_enter,
|
54
|
+
:on_singleton_class_node_leave,
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { params(node: Prism::ClassNode).void }
|
59
|
+
def on_class_node_enter(node)
|
60
|
+
@stack << create_document_symbol(
|
61
|
+
name: node.constant_path.location.slice,
|
62
|
+
kind: Constant::SymbolKind::CLASS,
|
63
|
+
range_location: node.location,
|
64
|
+
selection_range_location: node.constant_path.location,
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
sig { params(node: Prism::ClassNode).void }
|
69
|
+
def on_class_node_leave(node)
|
70
|
+
@stack.pop
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { params(node: Prism::SingletonClassNode).void }
|
74
|
+
def on_singleton_class_node_enter(node)
|
75
|
+
expression = node.expression
|
76
|
+
|
77
|
+
@stack << create_document_symbol(
|
78
|
+
name: "<< #{expression.slice}",
|
79
|
+
kind: Constant::SymbolKind::NAMESPACE,
|
80
|
+
range_location: node.location,
|
81
|
+
selection_range_location: expression.location,
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
sig { params(node: Prism::SingletonClassNode).void }
|
86
|
+
def on_singleton_class_node_leave(node)
|
87
|
+
@stack.pop
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { params(node: Prism::CallNode).void }
|
91
|
+
def on_call_node_enter(node)
|
92
|
+
return unless ATTR_ACCESSORS.include?(node.name) && node.receiver.nil?
|
93
|
+
|
94
|
+
arguments = node.arguments
|
95
|
+
return unless arguments
|
96
|
+
|
97
|
+
arguments.arguments.each do |argument|
|
98
|
+
next unless argument.is_a?(Prism::SymbolNode)
|
99
|
+
|
100
|
+
name = argument.value
|
101
|
+
next unless name
|
102
|
+
|
103
|
+
create_document_symbol(
|
104
|
+
name: name,
|
105
|
+
kind: Constant::SymbolKind::FIELD,
|
106
|
+
range_location: argument.location,
|
107
|
+
selection_range_location: T.must(argument.value_loc),
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
sig { params(node: Prism::ConstantPathWriteNode).void }
|
113
|
+
def on_constant_path_write_node_enter(node)
|
114
|
+
create_document_symbol(
|
115
|
+
name: node.target.location.slice,
|
116
|
+
kind: Constant::SymbolKind::CONSTANT,
|
117
|
+
range_location: node.location,
|
118
|
+
selection_range_location: node.target.location,
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
sig { params(node: Prism::ConstantWriteNode).void }
|
123
|
+
def on_constant_write_node_enter(node)
|
124
|
+
create_document_symbol(
|
125
|
+
name: node.name.to_s,
|
126
|
+
kind: Constant::SymbolKind::CONSTANT,
|
127
|
+
range_location: node.location,
|
128
|
+
selection_range_location: node.name_loc,
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
sig { params(node: Prism::DefNode).void }
|
133
|
+
def on_def_node_leave(node)
|
134
|
+
@stack.pop
|
135
|
+
end
|
136
|
+
|
137
|
+
sig { params(node: Prism::ModuleNode).void }
|
138
|
+
def on_module_node_enter(node)
|
139
|
+
@stack << create_document_symbol(
|
140
|
+
name: node.constant_path.location.slice,
|
141
|
+
kind: Constant::SymbolKind::MODULE,
|
142
|
+
range_location: node.location,
|
143
|
+
selection_range_location: node.constant_path.location,
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
sig { params(node: Prism::DefNode).void }
|
148
|
+
def on_def_node_enter(node)
|
149
|
+
receiver = node.receiver
|
150
|
+
previous_symbol = @stack.last
|
151
|
+
|
152
|
+
if receiver.is_a?(Prism::SelfNode)
|
153
|
+
name = "self.#{node.name}"
|
154
|
+
kind = Constant::SymbolKind::FUNCTION
|
155
|
+
elsif previous_symbol.is_a?(Interface::DocumentSymbol) && previous_symbol.name.start_with?("<<")
|
156
|
+
name = node.name.to_s
|
157
|
+
kind = Constant::SymbolKind::FUNCTION
|
158
|
+
else
|
159
|
+
name = node.name.to_s
|
160
|
+
kind = name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
161
|
+
end
|
162
|
+
|
163
|
+
symbol = create_document_symbol(
|
164
|
+
name: name,
|
165
|
+
kind: kind,
|
166
|
+
range_location: node.location,
|
167
|
+
selection_range_location: node.name_loc,
|
168
|
+
)
|
169
|
+
|
170
|
+
@stack << symbol
|
171
|
+
end
|
172
|
+
|
173
|
+
sig { params(node: Prism::ModuleNode).void }
|
174
|
+
def on_module_node_leave(node)
|
175
|
+
@stack.pop
|
176
|
+
end
|
177
|
+
|
178
|
+
sig { params(node: Prism::InstanceVariableWriteNode).void }
|
179
|
+
def on_instance_variable_write_node_enter(node)
|
180
|
+
create_document_symbol(
|
181
|
+
name: node.name.to_s,
|
182
|
+
kind: Constant::SymbolKind::VARIABLE,
|
183
|
+
range_location: node.name_loc,
|
184
|
+
selection_range_location: node.name_loc,
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
sig { params(node: Prism::ClassVariableWriteNode).void }
|
189
|
+
def on_class_variable_write_node_enter(node)
|
190
|
+
create_document_symbol(
|
191
|
+
name: node.name.to_s,
|
192
|
+
kind: Constant::SymbolKind::VARIABLE,
|
193
|
+
range_location: node.name_loc,
|
194
|
+
selection_range_location: node.name_loc,
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
sig do
|
201
|
+
params(
|
202
|
+
name: String,
|
203
|
+
kind: Integer,
|
204
|
+
range_location: Prism::Location,
|
205
|
+
selection_range_location: Prism::Location,
|
206
|
+
).returns(Interface::DocumentSymbol)
|
207
|
+
end
|
208
|
+
def create_document_symbol(name:, kind:, range_location:, selection_range_location:)
|
209
|
+
symbol = Interface::DocumentSymbol.new(
|
210
|
+
name: name,
|
211
|
+
kind: kind,
|
212
|
+
range: range_from_location(range_location),
|
213
|
+
selection_range: range_from_location(selection_range_location),
|
214
|
+
children: [],
|
215
|
+
)
|
216
|
+
|
217
|
+
T.must(@stack.last).children << symbol
|
218
|
+
|
219
|
+
symbol
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Listeners
|
6
|
+
class FoldingRanges < Listener
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
ResponseType = type_member { { fixed: T::Array[Interface::FoldingRange] } }
|
11
|
+
|
12
|
+
sig { params(comments: T::Array[Prism::Comment], dispatcher: Prism::Dispatcher).void }
|
13
|
+
def initialize(comments, dispatcher)
|
14
|
+
super(dispatcher)
|
15
|
+
|
16
|
+
@_response = T.let([], ResponseType)
|
17
|
+
@requires = T.let([], T::Array[Prism::CallNode])
|
18
|
+
@finalized_response = T.let(false, T::Boolean)
|
19
|
+
@comments = comments
|
20
|
+
|
21
|
+
dispatcher.register(
|
22
|
+
self,
|
23
|
+
:on_if_node_enter,
|
24
|
+
:on_in_node_enter,
|
25
|
+
:on_rescue_node_enter,
|
26
|
+
:on_when_node_enter,
|
27
|
+
:on_interpolated_string_node_enter,
|
28
|
+
:on_array_node_enter,
|
29
|
+
:on_block_node_enter,
|
30
|
+
:on_case_node_enter,
|
31
|
+
:on_case_match_node_enter,
|
32
|
+
:on_class_node_enter,
|
33
|
+
:on_module_node_enter,
|
34
|
+
:on_for_node_enter,
|
35
|
+
:on_hash_node_enter,
|
36
|
+
:on_singleton_class_node_enter,
|
37
|
+
:on_unless_node_enter,
|
38
|
+
:on_until_node_enter,
|
39
|
+
:on_while_node_enter,
|
40
|
+
:on_else_node_enter,
|
41
|
+
:on_ensure_node_enter,
|
42
|
+
:on_begin_node_enter,
|
43
|
+
:on_def_node_enter,
|
44
|
+
:on_call_node_enter,
|
45
|
+
:on_lambda_node_enter,
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
sig { override.returns(ResponseType) }
|
50
|
+
def _response
|
51
|
+
unless @finalized_response
|
52
|
+
push_comment_ranges
|
53
|
+
emit_requires_range
|
54
|
+
@finalized_response = true
|
55
|
+
end
|
56
|
+
|
57
|
+
@_response
|
58
|
+
end
|
59
|
+
|
60
|
+
sig { params(node: Prism::IfNode).void }
|
61
|
+
def on_if_node_enter(node)
|
62
|
+
add_statements_range(node)
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { params(node: Prism::InNode).void }
|
66
|
+
def on_in_node_enter(node)
|
67
|
+
add_statements_range(node)
|
68
|
+
end
|
69
|
+
|
70
|
+
sig { params(node: Prism::RescueNode).void }
|
71
|
+
def on_rescue_node_enter(node)
|
72
|
+
add_statements_range(node)
|
73
|
+
end
|
74
|
+
|
75
|
+
sig { params(node: Prism::WhenNode).void }
|
76
|
+
def on_when_node_enter(node)
|
77
|
+
add_statements_range(node)
|
78
|
+
end
|
79
|
+
|
80
|
+
sig { params(node: Prism::InterpolatedStringNode).void }
|
81
|
+
def on_interpolated_string_node_enter(node)
|
82
|
+
opening_loc = node.opening_loc || node.location
|
83
|
+
closing_loc = node.closing_loc || node.parts.last&.location || node.location
|
84
|
+
|
85
|
+
add_lines_range(opening_loc.start_line, closing_loc.start_line - 1)
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { params(node: Prism::ArrayNode).void }
|
89
|
+
def on_array_node_enter(node)
|
90
|
+
add_simple_range(node)
|
91
|
+
end
|
92
|
+
|
93
|
+
sig { params(node: Prism::BlockNode).void }
|
94
|
+
def on_block_node_enter(node)
|
95
|
+
add_simple_range(node)
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(node: Prism::CaseNode).void }
|
99
|
+
def on_case_node_enter(node)
|
100
|
+
add_simple_range(node)
|
101
|
+
end
|
102
|
+
|
103
|
+
sig { params(node: Prism::CaseMatchNode).void }
|
104
|
+
def on_case_match_node_enter(node)
|
105
|
+
add_simple_range(node)
|
106
|
+
end
|
107
|
+
|
108
|
+
sig { params(node: Prism::ClassNode).void }
|
109
|
+
def on_class_node_enter(node)
|
110
|
+
add_simple_range(node)
|
111
|
+
end
|
112
|
+
|
113
|
+
sig { params(node: Prism::ModuleNode).void }
|
114
|
+
def on_module_node_enter(node)
|
115
|
+
add_simple_range(node)
|
116
|
+
end
|
117
|
+
|
118
|
+
sig { params(node: Prism::ForNode).void }
|
119
|
+
def on_for_node_enter(node)
|
120
|
+
add_simple_range(node)
|
121
|
+
end
|
122
|
+
|
123
|
+
sig { params(node: Prism::HashNode).void }
|
124
|
+
def on_hash_node_enter(node)
|
125
|
+
add_simple_range(node)
|
126
|
+
end
|
127
|
+
|
128
|
+
sig { params(node: Prism::SingletonClassNode).void }
|
129
|
+
def on_singleton_class_node_enter(node)
|
130
|
+
add_simple_range(node)
|
131
|
+
end
|
132
|
+
|
133
|
+
sig { params(node: Prism::UnlessNode).void }
|
134
|
+
def on_unless_node_enter(node)
|
135
|
+
add_simple_range(node)
|
136
|
+
end
|
137
|
+
|
138
|
+
sig { params(node: Prism::UntilNode).void }
|
139
|
+
def on_until_node_enter(node)
|
140
|
+
add_simple_range(node)
|
141
|
+
end
|
142
|
+
|
143
|
+
sig { params(node: Prism::WhileNode).void }
|
144
|
+
def on_while_node_enter(node)
|
145
|
+
add_simple_range(node)
|
146
|
+
end
|
147
|
+
|
148
|
+
sig { params(node: Prism::ElseNode).void }
|
149
|
+
def on_else_node_enter(node)
|
150
|
+
add_simple_range(node)
|
151
|
+
end
|
152
|
+
|
153
|
+
sig { params(node: Prism::EnsureNode).void }
|
154
|
+
def on_ensure_node_enter(node)
|
155
|
+
add_simple_range(node)
|
156
|
+
end
|
157
|
+
|
158
|
+
sig { params(node: Prism::BeginNode).void }
|
159
|
+
def on_begin_node_enter(node)
|
160
|
+
add_simple_range(node)
|
161
|
+
end
|
162
|
+
|
163
|
+
sig { params(node: Prism::DefNode).void }
|
164
|
+
def on_def_node_enter(node)
|
165
|
+
params = node.parameters
|
166
|
+
parameter_loc = params&.location
|
167
|
+
location = node.location
|
168
|
+
|
169
|
+
if params && parameter_loc.end_line > location.start_line
|
170
|
+
# Multiline parameters
|
171
|
+
add_lines_range(location.start_line, parameter_loc.end_line)
|
172
|
+
add_lines_range(parameter_loc.end_line + 1, location.end_line - 1)
|
173
|
+
else
|
174
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
sig { params(node: Prism::CallNode).void }
|
179
|
+
def on_call_node_enter(node)
|
180
|
+
# If we find a require, don't visit the child nodes (prevent `super`), so that we can keep accumulating into
|
181
|
+
# the `@requires` array and then push the range whenever we find a node that isn't a CallNode
|
182
|
+
if require?(node)
|
183
|
+
@requires << node
|
184
|
+
return
|
185
|
+
end
|
186
|
+
|
187
|
+
location = node.location
|
188
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
189
|
+
end
|
190
|
+
|
191
|
+
sig { params(node: Prism::LambdaNode).void }
|
192
|
+
def on_lambda_node_enter(node)
|
193
|
+
add_simple_range(node)
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
sig { void }
|
199
|
+
def push_comment_ranges
|
200
|
+
# Group comments that are on consecutive lines and then push ranges for each group that has at least 2 comments
|
201
|
+
@comments.chunk_while do |this, other|
|
202
|
+
this.location.end_line + 1 == other.location.start_line
|
203
|
+
end.each do |chunk|
|
204
|
+
next if chunk.length == 1
|
205
|
+
|
206
|
+
@_response << Interface::FoldingRange.new(
|
207
|
+
start_line: T.must(chunk.first).location.start_line - 1,
|
208
|
+
end_line: T.must(chunk.last).location.end_line - 1,
|
209
|
+
kind: "comment",
|
210
|
+
)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
sig { void }
|
215
|
+
def emit_requires_range
|
216
|
+
if @requires.length > 1
|
217
|
+
@_response << Interface::FoldingRange.new(
|
218
|
+
start_line: T.must(@requires.first).location.start_line - 1,
|
219
|
+
end_line: T.must(@requires.last).location.end_line - 1,
|
220
|
+
kind: "imports",
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
@requires.clear
|
225
|
+
end
|
226
|
+
|
227
|
+
sig { params(node: Prism::CallNode).returns(T::Boolean) }
|
228
|
+
def require?(node)
|
229
|
+
message = node.message
|
230
|
+
return false unless message == "require" || message == "require_relative"
|
231
|
+
|
232
|
+
receiver = node.receiver
|
233
|
+
return false unless receiver.nil? || receiver.slice == "Kernel"
|
234
|
+
|
235
|
+
arguments = node.arguments&.arguments
|
236
|
+
return false unless arguments
|
237
|
+
|
238
|
+
arguments.length == 1 && arguments.first.is_a?(Prism::StringNode)
|
239
|
+
end
|
240
|
+
|
241
|
+
sig { params(node: T.any(Prism::IfNode, Prism::InNode, Prism::RescueNode, Prism::WhenNode)).void }
|
242
|
+
def add_statements_range(node)
|
243
|
+
statements = node.statements
|
244
|
+
return unless statements
|
245
|
+
|
246
|
+
body = statements.body
|
247
|
+
return if body.empty?
|
248
|
+
|
249
|
+
add_lines_range(node.location.start_line, T.must(body.last).location.end_line)
|
250
|
+
end
|
251
|
+
|
252
|
+
sig { params(node: Prism::Node).void }
|
253
|
+
def add_simple_range(node)
|
254
|
+
location = node.location
|
255
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
256
|
+
end
|
257
|
+
|
258
|
+
sig { params(start_line: Integer, end_line: Integer).void }
|
259
|
+
def add_lines_range(start_line, end_line)
|
260
|
+
emit_requires_range
|
261
|
+
return if start_line >= end_line
|
262
|
+
|
263
|
+
@_response << Interface::FoldingRange.new(
|
264
|
+
start_line: start_line - 1,
|
265
|
+
end_line: end_line - 1,
|
266
|
+
kind: "region",
|
267
|
+
)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|