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
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "ruby_lsp/
|
4
|
+
require "ruby_lsp/listeners/document_link"
|
5
5
|
|
6
6
|
module RubyLsp
|
7
7
|
module Requests
|
@@ -18,62 +18,20 @@ module RubyLsp
|
|
18
18
|
# def format(source, maxwidth = T.unsafe(nil))
|
19
19
|
# end
|
20
20
|
# ```
|
21
|
-
class DocumentLink <
|
21
|
+
class DocumentLink < Request
|
22
22
|
extend T::Sig
|
23
23
|
extend T::Generic
|
24
24
|
|
25
|
-
ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
|
26
|
-
|
27
|
-
GEM_TO_VERSION_MAP = T.let(
|
28
|
-
[*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
|
29
|
-
[s.name, s.version.to_s]
|
30
|
-
end.to_h.freeze,
|
31
|
-
T::Hash[String, String],
|
32
|
-
)
|
33
|
-
|
34
25
|
class << self
|
35
26
|
extend T::Sig
|
36
27
|
|
37
|
-
sig { returns(
|
38
|
-
def
|
39
|
-
|
40
|
-
begin
|
41
|
-
lookup = {}
|
42
|
-
|
43
|
-
Gem::Specification.stubs.each do |stub|
|
44
|
-
spec = stub.to_spec
|
45
|
-
lookup[spec.name] = {}
|
46
|
-
lookup[spec.name][spec.version.to_s] = {}
|
47
|
-
|
48
|
-
Dir.glob("**/*.rb", base: "#{spec.full_gem_path}/").each do |path|
|
49
|
-
lookup[spec.name][spec.version.to_s][path] = "#{spec.full_gem_path}/#{path}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
Gem::Specification.default_stubs.each do |stub|
|
54
|
-
spec = stub.to_spec
|
55
|
-
lookup[spec.name] = {}
|
56
|
-
lookup[spec.name][spec.version.to_s] = {}
|
57
|
-
prefix_matchers = Regexp.union(spec.require_paths.map do |rp|
|
58
|
-
Regexp.new("^#{rp}/")
|
59
|
-
end)
|
60
|
-
prefix_matcher = Regexp.union(prefix_matchers, //)
|
61
|
-
|
62
|
-
spec.files.each do |file|
|
63
|
-
path = file.sub(prefix_matcher, "")
|
64
|
-
lookup[spec.name][spec.version.to_s][path] = "#{RbConfig::CONFIG["rubylibdir"]}/#{path}"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
lookup
|
69
|
-
end,
|
70
|
-
T.nilable(T::Hash[String, T::Hash[String, T::Hash[String, String]]]),
|
71
|
-
)
|
28
|
+
sig { returns(Interface::DocumentLinkOptions) }
|
29
|
+
def provider
|
30
|
+
Interface::DocumentLinkOptions.new(resolve_provider: false)
|
72
31
|
end
|
73
32
|
end
|
74
33
|
|
75
|
-
|
76
|
-
attr_reader :_response
|
34
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
|
77
35
|
|
78
36
|
sig do
|
79
37
|
params(
|
@@ -83,92 +41,13 @@ module RubyLsp
|
|
83
41
|
).void
|
84
42
|
end
|
85
43
|
def initialize(uri, comments, dispatcher)
|
86
|
-
super(
|
87
|
-
|
88
|
-
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
89
|
-
# in the URI
|
90
|
-
path = uri.to_standardized_path
|
91
|
-
version_match = path ? /(?<=%40)[\d.]+(?=\.rbi$)/.match(path) : nil
|
92
|
-
@gem_version = T.let(version_match && version_match[0], T.nilable(String))
|
93
|
-
@_response = T.let([], T::Array[Interface::DocumentLink])
|
94
|
-
@lines_to_comments = T.let(
|
95
|
-
comments.to_h do |comment|
|
96
|
-
[comment.location.end_line, comment]
|
97
|
-
end,
|
98
|
-
T::Hash[Integer, Prism::Comment],
|
99
|
-
)
|
100
|
-
|
101
|
-
dispatcher.register(
|
102
|
-
self,
|
103
|
-
:on_def_node_enter,
|
104
|
-
:on_class_node_enter,
|
105
|
-
:on_module_node_enter,
|
106
|
-
:on_constant_write_node_enter,
|
107
|
-
:on_constant_path_write_node_enter,
|
108
|
-
)
|
109
|
-
end
|
110
|
-
|
111
|
-
sig { params(node: Prism::DefNode).void }
|
112
|
-
def on_def_node_enter(node)
|
113
|
-
extract_document_link(node)
|
114
|
-
end
|
115
|
-
|
116
|
-
sig { params(node: Prism::ClassNode).void }
|
117
|
-
def on_class_node_enter(node)
|
118
|
-
extract_document_link(node)
|
44
|
+
super()
|
45
|
+
@listener = T.let(Listeners::DocumentLink.new(uri, comments, dispatcher), Listener[ResponseType])
|
119
46
|
end
|
120
47
|
|
121
|
-
sig {
|
122
|
-
def
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
sig { params(node: Prism::ConstantWriteNode).void }
|
127
|
-
def on_constant_write_node_enter(node)
|
128
|
-
extract_document_link(node)
|
129
|
-
end
|
130
|
-
|
131
|
-
sig { params(node: Prism::ConstantPathWriteNode).void }
|
132
|
-
def on_constant_path_write_node_enter(node)
|
133
|
-
extract_document_link(node)
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
sig { params(node: Prism::Node).void }
|
139
|
-
def extract_document_link(node)
|
140
|
-
comment = @lines_to_comments[node.location.start_line - 1]
|
141
|
-
return unless comment
|
142
|
-
|
143
|
-
match = comment.location.slice.match(%r{source://.*#\d+$})
|
144
|
-
return unless match
|
145
|
-
|
146
|
-
uri = T.cast(URI(T.must(match[0])), URI::Source)
|
147
|
-
gem_version = resolve_version(uri)
|
148
|
-
return if gem_version.nil?
|
149
|
-
|
150
|
-
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path))
|
151
|
-
return if file_path.nil?
|
152
|
-
|
153
|
-
@_response << Interface::DocumentLink.new(
|
154
|
-
range: range_from_location(comment.location),
|
155
|
-
target: "file://#{file_path}##{uri.line_number}",
|
156
|
-
tooltip: "Jump to #{file_path}##{uri.line_number}",
|
157
|
-
)
|
158
|
-
end
|
159
|
-
|
160
|
-
# Try to figure out the gem version for a source:// link. The order of precedence is:
|
161
|
-
# 1. The version in the URI
|
162
|
-
# 2. The version in the RBI file name
|
163
|
-
# 3. The version from the gemspec
|
164
|
-
sig { params(uri: URI::Source).returns(T.nilable(String)) }
|
165
|
-
def resolve_version(uri)
|
166
|
-
version = uri.gem_version
|
167
|
-
return version unless version.nil? || version.empty?
|
168
|
-
|
169
|
-
return @gem_version unless @gem_version.nil? || @gem_version.empty?
|
170
|
-
|
171
|
-
GEM_TO_VERSION_MAP[uri.gem_name]
|
48
|
+
sig { override.returns(ResponseType) }
|
49
|
+
def perform
|
50
|
+
@listener.response
|
172
51
|
end
|
173
52
|
end
|
174
53
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/listeners/document_symbol"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
6
8
|
# 
|
@@ -26,232 +28,43 @@ module RubyLsp
|
|
26
28
|
# end
|
27
29
|
# end
|
28
30
|
# ```
|
29
|
-
class DocumentSymbol <
|
31
|
+
class DocumentSymbol < Request
|
30
32
|
extend T::Sig
|
31
33
|
extend T::Generic
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
ATTR_ACCESSORS = T.let([:attr_reader, :attr_writer, :attr_accessor].freeze, T::Array[Symbol])
|
36
|
-
|
37
|
-
class SymbolHierarchyRoot
|
35
|
+
class << self
|
38
36
|
extend T::Sig
|
39
37
|
|
40
|
-
sig { returns(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
sig { returns(Interface::DocumentSymbolClientCapabilities) }
|
39
|
+
def provider
|
40
|
+
Interface::DocumentSymbolClientCapabilities.new(
|
41
|
+
hierarchical_document_symbol_support: true,
|
42
|
+
symbol_kind: {
|
43
|
+
value_set: (Constant::SymbolKind::FILE..Constant::SymbolKind::TYPE_PARAMETER).to_a,
|
44
|
+
},
|
45
|
+
)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
attr_reader :_response
|
49
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
|
51
50
|
|
52
51
|
sig { params(dispatcher: Prism::Dispatcher).void }
|
53
52
|
def initialize(dispatcher)
|
54
|
-
|
55
|
-
@
|
56
|
-
|
57
|
-
[
|
58
|
-
T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
|
59
|
-
)
|
60
|
-
|
61
|
-
super
|
62
|
-
|
63
|
-
dispatcher.register(
|
64
|
-
self,
|
65
|
-
:on_class_node_enter,
|
66
|
-
:on_class_node_leave,
|
67
|
-
:on_call_node_enter,
|
68
|
-
:on_constant_path_write_node_enter,
|
69
|
-
:on_constant_write_node_enter,
|
70
|
-
:on_def_node_enter,
|
71
|
-
:on_def_node_leave,
|
72
|
-
:on_module_node_enter,
|
73
|
-
:on_module_node_leave,
|
74
|
-
:on_instance_variable_write_node_enter,
|
75
|
-
:on_class_variable_write_node_enter,
|
76
|
-
:on_singleton_class_node_enter,
|
77
|
-
:on_singleton_class_node_leave,
|
78
|
-
)
|
79
|
-
end
|
80
|
-
|
81
|
-
sig { override.params(addon: Addon).returns(T.nilable(Listener[ResponseType])) }
|
82
|
-
def initialize_external_listener(addon)
|
83
|
-
addon.create_document_symbol_listener(@dispatcher)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Merges responses from other listeners
|
87
|
-
sig { override.params(other: Listener[ResponseType]).returns(T.self_type) }
|
88
|
-
def merge_response!(other)
|
89
|
-
@_response.concat(other.response)
|
90
|
-
self
|
91
|
-
end
|
92
|
-
|
93
|
-
sig { params(node: Prism::ClassNode).void }
|
94
|
-
def on_class_node_enter(node)
|
95
|
-
@stack << create_document_symbol(
|
96
|
-
name: node.constant_path.location.slice,
|
97
|
-
kind: Constant::SymbolKind::CLASS,
|
98
|
-
range_location: node.location,
|
99
|
-
selection_range_location: node.constant_path.location,
|
100
|
-
)
|
101
|
-
end
|
102
|
-
|
103
|
-
sig { params(node: Prism::ClassNode).void }
|
104
|
-
def on_class_node_leave(node)
|
105
|
-
@stack.pop
|
106
|
-
end
|
107
|
-
|
108
|
-
sig { params(node: Prism::SingletonClassNode).void }
|
109
|
-
def on_singleton_class_node_enter(node)
|
110
|
-
expression = node.expression
|
111
|
-
|
112
|
-
@stack << create_document_symbol(
|
113
|
-
name: "<< #{expression.slice}",
|
114
|
-
kind: Constant::SymbolKind::NAMESPACE,
|
115
|
-
range_location: node.location,
|
116
|
-
selection_range_location: expression.location,
|
117
|
-
)
|
118
|
-
end
|
119
|
-
|
120
|
-
sig { params(node: Prism::SingletonClassNode).void }
|
121
|
-
def on_singleton_class_node_leave(node)
|
122
|
-
@stack.pop
|
123
|
-
end
|
124
|
-
|
125
|
-
sig { params(node: Prism::CallNode).void }
|
126
|
-
def on_call_node_enter(node)
|
127
|
-
return unless ATTR_ACCESSORS.include?(node.name) && node.receiver.nil?
|
128
|
-
|
129
|
-
arguments = node.arguments
|
130
|
-
return unless arguments
|
131
|
-
|
132
|
-
arguments.arguments.each do |argument|
|
133
|
-
next unless argument.is_a?(Prism::SymbolNode)
|
134
|
-
|
135
|
-
name = argument.value
|
136
|
-
next unless name
|
137
|
-
|
138
|
-
create_document_symbol(
|
139
|
-
name: name,
|
140
|
-
kind: Constant::SymbolKind::FIELD,
|
141
|
-
range_location: argument.location,
|
142
|
-
selection_range_location: T.must(argument.value_loc),
|
143
|
-
)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
sig { params(node: Prism::ConstantPathWriteNode).void }
|
148
|
-
def on_constant_path_write_node_enter(node)
|
149
|
-
create_document_symbol(
|
150
|
-
name: node.target.location.slice,
|
151
|
-
kind: Constant::SymbolKind::CONSTANT,
|
152
|
-
range_location: node.location,
|
153
|
-
selection_range_location: node.target.location,
|
154
|
-
)
|
155
|
-
end
|
156
|
-
|
157
|
-
sig { params(node: Prism::ConstantWriteNode).void }
|
158
|
-
def on_constant_write_node_enter(node)
|
159
|
-
create_document_symbol(
|
160
|
-
name: node.name.to_s,
|
161
|
-
kind: Constant::SymbolKind::CONSTANT,
|
162
|
-
range_location: node.location,
|
163
|
-
selection_range_location: node.name_loc,
|
53
|
+
super()
|
54
|
+
@listeners = T.let(
|
55
|
+
[Listeners::DocumentSymbol.new(dispatcher)],
|
56
|
+
T::Array[Listener[ResponseType]],
|
164
57
|
)
|
165
|
-
end
|
166
|
-
|
167
|
-
sig { params(node: Prism::DefNode).void }
|
168
|
-
def on_def_node_leave(node)
|
169
|
-
@stack.pop
|
170
|
-
end
|
171
|
-
|
172
|
-
sig { params(node: Prism::ModuleNode).void }
|
173
|
-
def on_module_node_enter(node)
|
174
|
-
@stack << create_document_symbol(
|
175
|
-
name: node.constant_path.location.slice,
|
176
|
-
kind: Constant::SymbolKind::MODULE,
|
177
|
-
range_location: node.location,
|
178
|
-
selection_range_location: node.constant_path.location,
|
179
|
-
)
|
180
|
-
end
|
181
58
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
previous_symbol = @stack.last
|
186
|
-
|
187
|
-
if receiver.is_a?(Prism::SelfNode)
|
188
|
-
name = "self.#{node.name}"
|
189
|
-
kind = Constant::SymbolKind::FUNCTION
|
190
|
-
elsif previous_symbol.is_a?(Interface::DocumentSymbol) && previous_symbol.name.start_with?("<<")
|
191
|
-
name = node.name.to_s
|
192
|
-
kind = Constant::SymbolKind::FUNCTION
|
193
|
-
else
|
194
|
-
name = node.name.to_s
|
195
|
-
kind = name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
59
|
+
Addon.addons.each do |addon|
|
60
|
+
addon_listener = addon.create_document_symbol_listener(dispatcher)
|
61
|
+
@listeners << addon_listener if addon_listener
|
196
62
|
end
|
197
|
-
|
198
|
-
symbol = create_document_symbol(
|
199
|
-
name: name,
|
200
|
-
kind: kind,
|
201
|
-
range_location: node.location,
|
202
|
-
selection_range_location: node.name_loc,
|
203
|
-
)
|
204
|
-
|
205
|
-
@stack << symbol
|
206
|
-
end
|
207
|
-
|
208
|
-
sig { params(node: Prism::ModuleNode).void }
|
209
|
-
def on_module_node_leave(node)
|
210
|
-
@stack.pop
|
211
63
|
end
|
212
64
|
|
213
|
-
sig {
|
214
|
-
def
|
215
|
-
|
216
|
-
name: node.name.to_s,
|
217
|
-
kind: Constant::SymbolKind::VARIABLE,
|
218
|
-
range_location: node.name_loc,
|
219
|
-
selection_range_location: node.name_loc,
|
220
|
-
)
|
221
|
-
end
|
222
|
-
|
223
|
-
sig { params(node: Prism::ClassVariableWriteNode).void }
|
224
|
-
def on_class_variable_write_node_enter(node)
|
225
|
-
create_document_symbol(
|
226
|
-
name: node.name.to_s,
|
227
|
-
kind: Constant::SymbolKind::VARIABLE,
|
228
|
-
range_location: node.name_loc,
|
229
|
-
selection_range_location: node.name_loc,
|
230
|
-
)
|
231
|
-
end
|
232
|
-
|
233
|
-
private
|
234
|
-
|
235
|
-
sig do
|
236
|
-
params(
|
237
|
-
name: String,
|
238
|
-
kind: Integer,
|
239
|
-
range_location: Prism::Location,
|
240
|
-
selection_range_location: Prism::Location,
|
241
|
-
).returns(Interface::DocumentSymbol)
|
242
|
-
end
|
243
|
-
def create_document_symbol(name:, kind:, range_location:, selection_range_location:)
|
244
|
-
symbol = Interface::DocumentSymbol.new(
|
245
|
-
name: name,
|
246
|
-
kind: kind,
|
247
|
-
range: range_from_location(range_location),
|
248
|
-
selection_range: range_from_location(selection_range_location),
|
249
|
-
children: [],
|
250
|
-
)
|
251
|
-
|
252
|
-
T.must(@stack.last).children << symbol
|
253
|
-
|
254
|
-
symbol
|
65
|
+
sig { override.returns(ResponseType) }
|
66
|
+
def perform
|
67
|
+
@listeners.flat_map(&:response).compact
|
255
68
|
end
|
256
69
|
end
|
257
70
|
end
|