ruby-lsp 0.13.2 → 0.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -0
  3. data/VERSION +1 -1
  4. data/lib/ruby_lsp/check_docs.rb +3 -3
  5. data/lib/ruby_lsp/document.rb +12 -0
  6. data/lib/ruby_lsp/executor.rb +77 -266
  7. data/lib/ruby_lsp/listener.rb +1 -50
  8. data/lib/ruby_lsp/listeners/code_lens.rb +233 -0
  9. data/lib/ruby_lsp/listeners/completion.rb +275 -0
  10. data/lib/ruby_lsp/listeners/definition.rb +158 -0
  11. data/lib/ruby_lsp/listeners/document_highlight.rb +556 -0
  12. data/lib/ruby_lsp/listeners/document_link.rb +162 -0
  13. data/lib/ruby_lsp/listeners/document_symbol.rb +223 -0
  14. data/lib/ruby_lsp/listeners/folding_ranges.rb +271 -0
  15. data/lib/ruby_lsp/listeners/hover.rb +152 -0
  16. data/lib/ruby_lsp/listeners/inlay_hints.rb +80 -0
  17. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +430 -0
  18. data/lib/ruby_lsp/listeners/signature_help.rb +74 -0
  19. data/lib/ruby_lsp/requests/code_action_resolve.rb +4 -4
  20. data/lib/ruby_lsp/requests/code_actions.rb +13 -4
  21. data/lib/ruby_lsp/requests/code_lens.rb +21 -221
  22. data/lib/ruby_lsp/requests/completion.rb +64 -244
  23. data/lib/ruby_lsp/requests/definition.rb +34 -147
  24. data/lib/ruby_lsp/requests/diagnostics.rb +17 -5
  25. data/lib/ruby_lsp/requests/document_highlight.rb +12 -536
  26. data/lib/ruby_lsp/requests/document_link.rb +11 -132
  27. data/lib/ruby_lsp/requests/document_symbol.rb +23 -210
  28. data/lib/ruby_lsp/requests/folding_ranges.rb +16 -252
  29. data/lib/ruby_lsp/requests/formatting.rb +4 -4
  30. data/lib/ruby_lsp/requests/hover.rb +48 -92
  31. data/lib/ruby_lsp/requests/inlay_hints.rb +23 -56
  32. data/lib/ruby_lsp/requests/on_type_formatting.rb +16 -4
  33. data/lib/ruby_lsp/requests/request.rb +17 -0
  34. data/lib/ruby_lsp/requests/selection_ranges.rb +4 -3
  35. data/lib/ruby_lsp/requests/semantic_highlighting.rb +21 -408
  36. data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -4
  37. data/lib/ruby_lsp/requests/signature_help.rb +43 -51
  38. data/lib/ruby_lsp/requests/support/common.rb +3 -2
  39. data/lib/ruby_lsp/requests/support/dependency_detector.rb +2 -0
  40. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
  41. data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
  42. data/lib/ruby_lsp/requests.rb +1 -1
  43. data/lib/ruby_lsp/utils.rb +8 -0
  44. metadata +15 -4
  45. 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/requests/support/source_uri"
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 < Listener
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(T::Hash[String, T::Hash[String, T::Hash[String, String]]]) }
38
- def gem_paths
39
- @gem_paths ||= T.let(
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
- sig { override.returns(ResponseType) }
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(dispatcher)
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 { params(node: Prism::ModuleNode).void }
122
- def on_module_node_enter(node)
123
- extract_document_link(node)
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
  # ![Document symbol demo](../../document_symbol.gif)
@@ -26,232 +28,43 @@ module RubyLsp
26
28
  # end
27
29
  # end
28
30
  # ```
29
- class DocumentSymbol < ExtensibleListener
31
+ class DocumentSymbol < Request
30
32
  extend T::Sig
31
33
  extend T::Generic
32
34
 
33
- ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
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(T::Array[Interface::DocumentSymbol]) }
41
- attr_reader :children
42
-
43
- sig { void }
44
- def initialize
45
- @children = T.let([], T::Array[Interface::DocumentSymbol])
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
- sig { override.returns(T::Array[Interface::DocumentSymbol]) }
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
- @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
55
- @_response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
56
- @stack = T.let(
57
- [@root],
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
- sig { params(node: Prism::DefNode).void }
183
- def on_def_node_enter(node)
184
- receiver = node.receiver
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 { params(node: Prism::InstanceVariableWriteNode).void }
214
- def on_instance_variable_write_node_enter(node)
215
- create_document_symbol(
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