ruby-lsp 0.13.3 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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,