ruby-lsp 0.13.3 → 0.14.0

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/VERSION +1 -1
  4. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +4 -8
  5. data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +5 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +4 -2
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -3
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +9 -0
  9. data/lib/ruby_indexer/test/index_test.rb +27 -0
  10. data/lib/ruby_lsp/addon.rb +21 -10
  11. data/lib/ruby_lsp/check_docs.rb +8 -8
  12. data/lib/ruby_lsp/executor.rb +28 -10
  13. data/lib/ruby_lsp/internal.rb +1 -1
  14. data/lib/ruby_lsp/listeners/code_lens.rb +54 -55
  15. data/lib/ruby_lsp/listeners/completion.rb +17 -16
  16. data/lib/ruby_lsp/listeners/definition.rb +10 -16
  17. data/lib/ruby_lsp/listeners/document_highlight.rb +6 -11
  18. data/lib/ruby_lsp/listeners/document_link.rb +6 -12
  19. data/lib/ruby_lsp/listeners/document_symbol.rb +95 -55
  20. data/lib/ruby_lsp/listeners/folding_ranges.rb +19 -23
  21. data/lib/ruby_lsp/listeners/hover.rb +26 -30
  22. data/lib/ruby_lsp/listeners/inlay_hints.rb +7 -13
  23. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -124
  24. data/lib/ruby_lsp/listeners/signature_help.rb +11 -13
  25. data/lib/ruby_lsp/requests/code_lens.rb +9 -17
  26. data/lib/ruby_lsp/requests/completion.rb +7 -9
  27. data/lib/ruby_lsp/requests/definition.rb +10 -22
  28. data/lib/ruby_lsp/requests/document_highlight.rb +7 -5
  29. data/lib/ruby_lsp/requests/document_link.rb +7 -6
  30. data/lib/ruby_lsp/requests/document_symbol.rb +5 -11
  31. data/lib/ruby_lsp/requests/folding_ranges.rb +11 -6
  32. data/lib/ruby_lsp/requests/hover.rb +18 -24
  33. data/lib/ruby_lsp/requests/inlay_hints.rb +7 -8
  34. data/lib/ruby_lsp/requests/on_type_formatting.rb +12 -2
  35. data/lib/ruby_lsp/requests/semantic_highlighting.rb +10 -8
  36. data/lib/ruby_lsp/requests/signature_help.rb +53 -18
  37. data/lib/ruby_lsp/requests/support/common.rb +23 -10
  38. data/lib/ruby_lsp/requests/support/dependency_detector.rb +5 -1
  39. data/lib/ruby_lsp/requests.rb +0 -1
  40. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +29 -0
  41. data/lib/ruby_lsp/response_builders/document_symbol.rb +57 -0
  42. data/lib/ruby_lsp/response_builders/hover.rb +49 -0
  43. data/lib/ruby_lsp/response_builders/response_builder.rb +16 -0
  44. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +199 -0
  45. data/lib/ruby_lsp/response_builders/signature_help.rb +28 -0
  46. data/lib/ruby_lsp/response_builders.rb +13 -0
  47. data/lib/ruby_lsp/server.rb +3 -3
  48. data/lib/ruby_lsp/setup_bundler.rb +30 -5
  49. data/lib/ruby_lsp/store.rb +4 -4
  50. metadata +14 -9
  51. data/lib/ruby_lsp/listener.rb +0 -33
  52. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +0 -73
@@ -3,39 +3,20 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class DocumentSymbol < Listener
6
+ class DocumentSymbol
7
7
  extend T::Sig
8
- extend T::Generic
9
-
10
- ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
8
+ include Requests::Support::Common
11
9
 
12
10
  ATTR_ACCESSORS = T.let([:attr_reader, :attr_writer, :attr_accessor].freeze, T::Array[Symbol])
13
11
 
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
12
+ sig do
13
+ params(
14
+ response_builder: ResponseBuilders::DocumentSymbol,
15
+ dispatcher: Prism::Dispatcher,
16
+ ).void
24
17
  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
18
+ def initialize(response_builder, dispatcher)
19
+ @response_builder = response_builder
39
20
 
40
21
  dispatcher.register(
41
22
  self,
@@ -52,12 +33,13 @@ module RubyLsp
52
33
  :on_class_variable_write_node_enter,
53
34
  :on_singleton_class_node_enter,
54
35
  :on_singleton_class_node_leave,
36
+ :on_alias_method_node_enter,
55
37
  )
56
38
  end
57
39
 
58
40
  sig { params(node: Prism::ClassNode).void }
59
41
  def on_class_node_enter(node)
60
- @stack << create_document_symbol(
42
+ @response_builder << create_document_symbol(
61
43
  name: node.constant_path.location.slice,
62
44
  kind: Constant::SymbolKind::CLASS,
63
45
  range_location: node.location,
@@ -67,14 +49,14 @@ module RubyLsp
67
49
 
68
50
  sig { params(node: Prism::ClassNode).void }
69
51
  def on_class_node_leave(node)
70
- @stack.pop
52
+ @response_builder.pop
71
53
  end
72
54
 
73
55
  sig { params(node: Prism::SingletonClassNode).void }
74
56
  def on_singleton_class_node_enter(node)
75
57
  expression = node.expression
76
58
 
77
- @stack << create_document_symbol(
59
+ @response_builder << create_document_symbol(
78
60
  name: "<< #{expression.slice}",
79
61
  kind: Constant::SymbolKind::NAMESPACE,
80
62
  range_location: node.location,
@@ -84,28 +66,15 @@ module RubyLsp
84
66
 
85
67
  sig { params(node: Prism::SingletonClassNode).void }
86
68
  def on_singleton_class_node_leave(node)
87
- @stack.pop
69
+ @response_builder.pop
88
70
  end
89
71
 
90
72
  sig { params(node: Prism::CallNode).void }
91
73
  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
- )
74
+ if ATTR_ACCESSORS.include?(node.name)
75
+ handle_attr_accessor(node)
76
+ elsif node.name == :alias_method
77
+ handle_alias_method(node)
109
78
  end
110
79
  end
111
80
 
@@ -131,12 +100,12 @@ module RubyLsp
131
100
 
132
101
  sig { params(node: Prism::DefNode).void }
133
102
  def on_def_node_leave(node)
134
- @stack.pop
103
+ @response_builder.pop
135
104
  end
136
105
 
137
106
  sig { params(node: Prism::ModuleNode).void }
138
107
  def on_module_node_enter(node)
139
- @stack << create_document_symbol(
108
+ @response_builder << create_document_symbol(
140
109
  name: node.constant_path.location.slice,
141
110
  kind: Constant::SymbolKind::MODULE,
142
111
  range_location: node.location,
@@ -147,7 +116,7 @@ module RubyLsp
147
116
  sig { params(node: Prism::DefNode).void }
148
117
  def on_def_node_enter(node)
149
118
  receiver = node.receiver
150
- previous_symbol = @stack.last
119
+ previous_symbol = @response_builder.last
151
120
 
152
121
  if receiver.is_a?(Prism::SelfNode)
153
122
  name = "self.#{node.name}"
@@ -167,12 +136,12 @@ module RubyLsp
167
136
  selection_range_location: node.name_loc,
168
137
  )
169
138
 
170
- @stack << symbol
139
+ @response_builder << symbol
171
140
  end
172
141
 
173
142
  sig { params(node: Prism::ModuleNode).void }
174
143
  def on_module_node_leave(node)
175
- @stack.pop
144
+ @response_builder.pop
176
145
  end
177
146
 
178
147
  sig { params(node: Prism::InstanceVariableWriteNode).void }
@@ -195,6 +164,22 @@ module RubyLsp
195
164
  )
196
165
  end
197
166
 
167
+ sig { params(node: Prism::AliasMethodNode).void }
168
+ def on_alias_method_node_enter(node)
169
+ new_name_node = node.new_name
170
+ return unless new_name_node.is_a?(Prism::SymbolNode)
171
+
172
+ name = new_name_node.value
173
+ return unless name
174
+
175
+ create_document_symbol(
176
+ name: name,
177
+ kind: Constant::SymbolKind::METHOD,
178
+ range_location: new_name_node.location,
179
+ selection_range_location: T.must(new_name_node.value_loc),
180
+ )
181
+ end
182
+
198
183
  private
199
184
 
200
185
  sig do
@@ -214,10 +199,65 @@ module RubyLsp
214
199
  children: [],
215
200
  )
216
201
 
217
- T.must(@stack.last).children << symbol
202
+ @response_builder.last.children << symbol
218
203
 
219
204
  symbol
220
205
  end
206
+
207
+ sig { params(node: Prism::CallNode).void }
208
+ def handle_attr_accessor(node)
209
+ return unless node.receiver.nil?
210
+
211
+ arguments = node.arguments
212
+ return unless arguments
213
+
214
+ arguments.arguments.each do |argument|
215
+ next unless argument.is_a?(Prism::SymbolNode)
216
+
217
+ name = argument.value
218
+ next unless name
219
+
220
+ create_document_symbol(
221
+ name: name,
222
+ kind: Constant::SymbolKind::FIELD,
223
+ range_location: argument.location,
224
+ selection_range_location: T.must(argument.value_loc),
225
+ )
226
+ end
227
+ end
228
+
229
+ sig { params(node: Prism::CallNode).void }
230
+ def handle_alias_method(node)
231
+ receiver = node.receiver
232
+ return if receiver && !receiver.is_a?(Prism::SelfNode)
233
+
234
+ arguments = node.arguments
235
+ return unless arguments
236
+
237
+ new_name_argument = arguments.arguments.first
238
+
239
+ if new_name_argument.is_a?(Prism::SymbolNode)
240
+ name = new_name_argument.value
241
+ return unless name
242
+
243
+ create_document_symbol(
244
+ name: name,
245
+ kind: Constant::SymbolKind::METHOD,
246
+ range_location: new_name_argument.location,
247
+ selection_range_location: T.must(new_name_argument.value_loc),
248
+ )
249
+ elsif new_name_argument.is_a?(Prism::StringNode)
250
+ name = new_name_argument.content
251
+ return if name.empty?
252
+
253
+ create_document_symbol(
254
+ name: name,
255
+ kind: Constant::SymbolKind::METHOD,
256
+ range_location: new_name_argument.location,
257
+ selection_range_location: new_name_argument.content_loc,
258
+ )
259
+ end
260
+ end
221
261
  end
222
262
  end
223
263
  end
@@ -3,19 +3,20 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class FoldingRanges < Listener
6
+ class FoldingRanges
7
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)
8
+ include Requests::Support::Common
9
+
10
+ sig do
11
+ params(
12
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::FoldingRange],
13
+ comments: T::Array[Prism::Comment],
14
+ dispatcher: Prism::Dispatcher,
15
+ ).void
16
+ end
17
+ def initialize(response_builder, comments, dispatcher)
18
+ @response_builder = response_builder
17
19
  @requires = T.let([], T::Array[Prism::CallNode])
18
- @finalized_response = T.let(false, T::Boolean)
19
20
  @comments = comments
20
21
 
21
22
  dispatcher.register(
@@ -46,15 +47,10 @@ module RubyLsp
46
47
  )
47
48
  end
48
49
 
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
50
+ sig { void }
51
+ def finalize_response!
52
+ push_comment_ranges
53
+ emit_requires_range
58
54
  end
59
55
 
60
56
  sig { params(node: Prism::IfNode).void }
@@ -203,7 +199,7 @@ module RubyLsp
203
199
  end.each do |chunk|
204
200
  next if chunk.length == 1
205
201
 
206
- @_response << Interface::FoldingRange.new(
202
+ @response_builder << Interface::FoldingRange.new(
207
203
  start_line: T.must(chunk.first).location.start_line - 1,
208
204
  end_line: T.must(chunk.last).location.end_line - 1,
209
205
  kind: "comment",
@@ -214,7 +210,7 @@ module RubyLsp
214
210
  sig { void }
215
211
  def emit_requires_range
216
212
  if @requires.length > 1
217
- @_response << Interface::FoldingRange.new(
213
+ @response_builder << Interface::FoldingRange.new(
218
214
  start_line: T.must(@requires.first).location.start_line - 1,
219
215
  end_line: T.must(@requires.last).location.end_line - 1,
220
216
  kind: "imports",
@@ -260,7 +256,7 @@ module RubyLsp
260
256
  emit_requires_range
261
257
  return if start_line >= end_line
262
258
 
263
- @_response << Interface::FoldingRange.new(
259
+ @response_builder << Interface::FoldingRange.new(
264
260
  start_line: start_line - 1,
265
261
  end_line: end_line - 1,
266
262
  kind: "region",
@@ -3,11 +3,9 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class Hover < Listener
6
+ class Hover
7
7
  extend T::Sig
8
- extend T::Generic
9
-
10
- ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } }
8
+ include Requests::Support::Common
11
9
 
12
10
  ALLOWED_TARGETS = T.let(
13
11
  [
@@ -19,11 +17,17 @@ module RubyLsp
19
17
  T::Array[T.class_of(Prism::Node)],
20
18
  )
21
19
 
22
- sig { override.returns(ResponseType) }
23
- attr_reader :_response
20
+ ALLOWED_REMOTE_PROVIDERS = T.let(
21
+ [
22
+ "https://github.com",
23
+ "https://gitlab.com",
24
+ ].freeze,
25
+ T::Array[String],
26
+ )
24
27
 
25
28
  sig do
26
29
  params(
30
+ response_builder: ResponseBuilders::Hover,
27
31
  uri: URI::Generic,
28
32
  nesting: T::Array[String],
29
33
  index: RubyIndexer::Index,
@@ -31,14 +35,13 @@ module RubyLsp
31
35
  typechecker_enabled: T::Boolean,
32
36
  ).void
33
37
  end
34
- def initialize(uri, nesting, index, dispatcher, typechecker_enabled)
38
+ def initialize(response_builder, uri, nesting, index, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
39
+ @response_builder = response_builder
35
40
  @path = T.let(uri.to_standardized_path, T.nilable(String))
36
41
  @nesting = nesting
37
42
  @index = index
38
43
  @typechecker_enabled = typechecker_enabled
39
- @_response = T.let(nil, ResponseType)
40
44
 
41
- super(dispatcher)
42
45
  dispatcher.register(
43
46
  self,
44
47
  :on_constant_read_node_enter,
@@ -86,12 +89,9 @@ module RubyLsp
86
89
  target_method = @index.resolve_method(message, @nesting.join("::"))
87
90
  return unless target_method
88
91
 
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
- )
92
+ categorized_markdown_from_index_entries(message, target_method).each do |category, content|
93
+ @response_builder.push(content, category: category)
94
+ end
95
95
  end
96
96
 
97
97
  private
@@ -106,10 +106,9 @@ module RubyLsp
106
106
  first_entry = T.must(entries.first)
107
107
  return if first_entry.visibility == :private && first_entry.name != "#{@nesting.join("::")}::#{name}"
108
108
 
109
- @_response = Interface::Hover.new(
110
- range: range_from_location(location),
111
- contents: markdown_from_index_entries(name, entries),
112
- )
109
+ categorized_markdown_from_index_entries(name, entries).each do |category, content|
110
+ @response_builder.push(content, category: category)
111
+ end
113
112
  end
114
113
 
115
114
  sig { params(node: Prism::CallNode).void }
@@ -132,18 +131,15 @@ module RubyLsp
132
131
  # Remove leading whitespace if a heredoc was used for the summary or description
133
132
  info = info.gsub(/^ +/, "")
134
133
 
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
- ),
134
+ remote_url = [spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
135
+ page.start_with?(*ALLOWED_REMOTE_PROVIDERS)
136
+ end
137
+
138
+ @response_builder.push(
139
+ "**#{spec.name}** (#{spec.version}) #{remote_url && " - [open remote](#{remote_url})"}",
140
+ category: :title,
146
141
  )
142
+ @response_builder.push(info, category: :documentation)
147
143
  rescue Gem::MissingSpecError
148
144
  # Do nothing if the spec cannot be found
149
145
  end
@@ -3,28 +3,22 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class InlayHints < Listener
6
+ class InlayHints
7
7
  extend T::Sig
8
- extend T::Generic
9
-
10
- ResponseType = type_member { { fixed: T::Array[Interface::InlayHint] } }
8
+ include Requests::Support::Common
11
9
 
12
10
  RESCUE_STRING_LENGTH = T.let("rescue".length, Integer)
13
11
 
14
- sig { override.returns(ResponseType) }
15
- attr_reader :_response
16
-
17
12
  sig do
18
13
  params(
14
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint],
19
15
  range: T::Range[Integer],
20
16
  hints_configuration: RequestConfig,
21
17
  dispatcher: Prism::Dispatcher,
22
18
  ).void
23
19
  end
24
- def initialize(range, hints_configuration, dispatcher)
25
- super(dispatcher)
26
-
27
- @_response = T.let([], ResponseType)
20
+ def initialize(response_builder, range, hints_configuration, dispatcher)
21
+ @response_builder = response_builder
28
22
  @range = range
29
23
  @hints_configuration = hints_configuration
30
24
 
@@ -39,7 +33,7 @@ module RubyLsp
39
33
  loc = node.location
40
34
  return unless visible?(node, @range)
41
35
 
42
- @_response << Interface::InlayHint.new(
36
+ @response_builder << Interface::InlayHint.new(
43
37
  position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
44
38
  label: "StandardError",
45
39
  padding_left: true,
@@ -68,7 +62,7 @@ module RubyLsp
68
62
  tooltip = "This is a local variable: #{node_name}"
69
63
  end
70
64
 
71
- @_response << Interface::InlayHint.new(
65
+ @response_builder << Interface::InlayHint.new(
72
66
  position: { line: loc.start_line - 1, character: loc.start_column + node_name.length + 1 },
73
67
  label: node_name,
74
68
  padding_left: true,