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.
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 +17 -6
  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