ruby-lsp 0.17.16 → 0.18.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -110
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +10 -8
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +14 -6
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +157 -27
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +31 -12
  8. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -2
  9. data/lib/ruby_indexer/test/classes_and_modules_test.rb +10 -10
  10. data/lib/ruby_indexer/test/constant_test.rb +4 -4
  11. data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
  12. data/lib/ruby_indexer/test/index_test.rb +41 -0
  13. data/lib/ruby_indexer/test/method_test.rb +257 -2
  14. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  15. data/lib/ruby_lsp/addon.rb +3 -2
  16. data/lib/ruby_lsp/base_server.rb +21 -1
  17. data/lib/ruby_lsp/document.rb +5 -3
  18. data/lib/ruby_lsp/erb_document.rb +29 -10
  19. data/lib/ruby_lsp/global_state.rb +15 -1
  20. data/lib/ruby_lsp/listeners/code_lens.rb +34 -5
  21. data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
  22. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +28 -0
  23. data/lib/ruby_lsp/listeners/signature_help.rb +55 -24
  24. data/lib/ruby_lsp/rbs_document.rb +5 -4
  25. data/lib/ruby_lsp/requests/code_action_resolve.rb +0 -15
  26. data/lib/ruby_lsp/requests/code_actions.rb +0 -10
  27. data/lib/ruby_lsp/requests/code_lens.rb +1 -11
  28. data/lib/ruby_lsp/requests/completion.rb +3 -20
  29. data/lib/ruby_lsp/requests/completion_resolve.rb +0 -8
  30. data/lib/ruby_lsp/requests/definition.rb +6 -20
  31. data/lib/ruby_lsp/requests/diagnostics.rb +0 -10
  32. data/lib/ruby_lsp/requests/document_highlight.rb +7 -14
  33. data/lib/ruby_lsp/requests/document_link.rb +0 -10
  34. data/lib/ruby_lsp/requests/document_symbol.rb +0 -17
  35. data/lib/ruby_lsp/requests/folding_ranges.rb +0 -10
  36. data/lib/ruby_lsp/requests/formatting.rb +3 -17
  37. data/lib/ruby_lsp/requests/hover.rb +9 -9
  38. data/lib/ruby_lsp/requests/inlay_hints.rb +0 -30
  39. data/lib/ruby_lsp/requests/on_type_formatting.rb +0 -10
  40. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +0 -11
  41. data/lib/ruby_lsp/requests/request.rb +17 -1
  42. data/lib/ruby_lsp/requests/selection_ranges.rb +0 -10
  43. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -23
  44. data/lib/ruby_lsp/requests/show_syntax_tree.rb +0 -11
  45. data/lib/ruby_lsp/requests/signature_help.rb +5 -20
  46. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  47. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +2 -0
  48. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +0 -11
  49. data/lib/ruby_lsp/requests/workspace_symbol.rb +0 -12
  50. data/lib/ruby_lsp/ruby_document.rb +4 -3
  51. data/lib/ruby_lsp/server.rb +23 -8
  52. data/lib/ruby_lsp/setup_bundler.rb +31 -13
  53. data/lib/ruby_lsp/type_inferrer.rb +6 -2
  54. data/lib/ruby_lsp/utils.rb +11 -1
  55. metadata +7 -14
  56. data/lib/ruby_lsp/check_docs.rb +0 -130
@@ -44,6 +44,7 @@ module RubyLsp
44
44
  @group_id_stack = T.let([], T::Array[Integer])
45
45
  # We want to avoid adding code lenses for nested definitions
46
46
  @def_depth = T.let(0, Integer)
47
+ @spec_id = T.let(0, Integer)
47
48
 
48
49
  dispatcher.register(
49
50
  self,
@@ -70,6 +71,7 @@ module RubyLsp
70
71
  name: class_name,
71
72
  command: generate_test_command(group_stack: @group_stack),
72
73
  kind: :group,
74
+ id: generate_fully_qualified_id(group_stack: @group_stack),
73
75
  )
74
76
 
75
77
  @group_id_stack.push(@group_id)
@@ -106,6 +108,7 @@ module RubyLsp
106
108
  name: method_name,
107
109
  command: generate_test_command(method_name: method_name, group_stack: @group_stack),
108
110
  kind: :example,
111
+ id: generate_fully_qualified_id(group_stack: @group_stack, method_name: method_name),
109
112
  )
110
113
  end
111
114
  end
@@ -166,19 +169,20 @@ module RubyLsp
166
169
  @visibility_stack.push([prev_visibility, prev_visibility])
167
170
  if node.name == DESCRIBE_KEYWORD
168
171
  @group_id_stack.pop
172
+ @group_stack.pop
169
173
  end
170
174
  end
171
175
 
172
176
  private
173
177
 
174
- sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
175
- def add_test_code_lens(node, name:, command:, kind:)
178
+ sig { params(node: Prism::Node, name: String, command: String, kind: Symbol, id: String).void }
179
+ def add_test_code_lens(node, name:, command:, kind:, id: name)
176
180
  # don't add code lenses if the test library is not supported or unknown
177
181
  return unless SUPPORTED_TEST_LIBRARIES.include?(@global_state.test_library) && @path
178
182
 
179
183
  arguments = [
180
184
  @path,
181
- name,
185
+ id,
182
186
  command,
183
187
  {
184
188
  start_line: node.location.start_line - 1,
@@ -186,6 +190,7 @@ module RubyLsp
186
190
  end_line: node.location.end_line - 1,
187
191
  end_column: node.location.end_column,
188
192
  },
193
+ name,
189
194
  ]
190
195
 
191
196
  grouping_data = { group_id: @group_id_stack.last, kind: kind }
@@ -247,7 +252,7 @@ module RubyLsp
247
252
  # We know the entire path, do an exact match
248
253
  " --name " + Shellwords.escape(group_stack.join("::")) + "#" + Shellwords.escape(method_name)
249
254
  elsif spec_name
250
- " --name " + "/#{Shellwords.escape(spec_name)}/"
255
+ " --name " + "\"/^#{Shellwords.escape(group_stack.join("::"))}##{Shellwords.escape(spec_name)}$/\""
251
256
  else
252
257
  # Execute all tests of the selected class and tests in
253
258
  # modules/classes nested inside of that class
@@ -282,15 +287,39 @@ module RubyLsp
282
287
 
283
288
  return unless name
284
289
 
290
+ if kind == :example
291
+ # Increment spec_id for each example
292
+ @spec_id += 1
293
+ else
294
+ # Reset spec_id when entering a new group
295
+ @spec_id = 0
296
+ @group_stack.push(name)
297
+ end
298
+
285
299
  if @path
300
+ method_name = format("test_%04d_%s", @spec_id, name) if kind == :example
286
301
  add_test_code_lens(
287
302
  node,
288
303
  name: name,
289
- command: generate_test_command(spec_name: name),
304
+ command: generate_test_command(group_stack: @group_stack, spec_name: method_name),
290
305
  kind: kind,
306
+ id: generate_fully_qualified_id(group_stack: @group_stack, method_name: method_name),
291
307
  )
292
308
  end
293
309
  end
310
+
311
+ sig { params(group_stack: T::Array[String], method_name: T.nilable(String)).returns(String) }
312
+ def generate_fully_qualified_id(group_stack:, method_name: nil)
313
+ if method_name
314
+ # For tests, this will be the test class and method name: `Foo::BarTest#test_baz`.
315
+ # For specs, this will be the nested descriptions and formatted test name: `a::b::c#test_001_foo`.
316
+ group_stack.join("::") + "#" + method_name
317
+ else
318
+ # For tests, this will be the test class: `Foo::BarTest`.
319
+ # For specs, this will be the nested descriptions: `a::b::c`.
320
+ group_stack.join("::")
321
+ end
322
+ end
294
323
  end
295
324
  end
296
325
  end
@@ -242,7 +242,7 @@ module RubyLsp
242
242
  body = statements.body
243
243
  return if body.empty?
244
244
 
245
- add_lines_range(node.location.start_line, T.must(body.last).location.end_line)
245
+ add_lines_range(node.location.start_line, body.last.location.end_line)
246
246
  end
247
247
 
248
248
  sig { params(node: Prism::Node).void }
@@ -41,6 +41,7 @@ module RubyLsp
41
41
  :on_block_node_leave,
42
42
  :on_self_node_enter,
43
43
  :on_module_node_enter,
44
+ :on_local_variable_write_node_enter,
44
45
  :on_local_variable_read_node_enter,
45
46
  :on_block_parameter_node_enter,
46
47
  :on_required_keyword_parameter_node_enter,
@@ -49,6 +50,9 @@ module RubyLsp
49
50
  :on_optional_parameter_node_enter,
50
51
  :on_required_parameter_node_enter,
51
52
  :on_rest_parameter_node_enter,
53
+ :on_local_variable_and_write_node_enter,
54
+ :on_local_variable_operator_write_node_enter,
55
+ :on_local_variable_or_write_node_enter,
52
56
  :on_local_variable_target_node_enter,
53
57
  :on_block_local_variable_node_enter,
54
58
  :on_match_write_node_enter,
@@ -164,6 +168,12 @@ module RubyLsp
164
168
  @response_builder.add_token(node.location, :variable, [:default_library])
165
169
  end
166
170
 
171
+ sig { params(node: Prism::LocalVariableWriteNode).void }
172
+ def on_local_variable_write_node_enter(node)
173
+ type = @current_scope.type_for(node.name)
174
+ @response_builder.add_token(node.name_loc, type) if type == :parameter
175
+ end
176
+
167
177
  sig { params(node: Prism::LocalVariableReadNode).void }
168
178
  def on_local_variable_read_node_enter(node)
169
179
  return if @inside_implicit_node
@@ -177,6 +187,24 @@ module RubyLsp
177
187
  @response_builder.add_token(node.location, @current_scope.type_for(node.name))
178
188
  end
179
189
 
190
+ sig { params(node: Prism::LocalVariableAndWriteNode).void }
191
+ def on_local_variable_and_write_node_enter(node)
192
+ type = @current_scope.type_for(node.name)
193
+ @response_builder.add_token(node.name_loc, type) if type == :parameter
194
+ end
195
+
196
+ sig { params(node: Prism::LocalVariableOperatorWriteNode).void }
197
+ def on_local_variable_operator_write_node_enter(node)
198
+ type = @current_scope.type_for(node.name)
199
+ @response_builder.add_token(node.name_loc, type) if type == :parameter
200
+ end
201
+
202
+ sig { params(node: Prism::LocalVariableOrWriteNode).void }
203
+ def on_local_variable_or_write_node_enter(node)
204
+ type = @current_scope.type_for(node.name)
205
+ @response_builder.add_token(node.name_loc, type) if type == :parameter
206
+ end
207
+
180
208
  sig { params(node: Prism::LocalVariableTargetNode).void }
181
209
  def on_local_variable_target_node_enter(node)
182
210
  # If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
@@ -42,17 +42,46 @@ module RubyLsp
42
42
  target_method = methods.first
43
43
  return unless target_method
44
44
 
45
- parameters = target_method.parameters
46
- name = target_method.name
45
+ signatures = target_method.signatures
47
46
 
48
47
  # If the method doesn't have any parameters, there's no need to show signature help
49
- return if parameters.empty?
48
+ return if signatures.empty?
49
+
50
+ name = target_method.name
51
+ title = +""
52
+
53
+ extra_links = if type.is_a?(TypeInferrer::GuessedType)
54
+ title << "\n\nGuessed receiver: #{type.name}"
55
+ "[Learn more about guessed types](#{GUESSED_TYPES_URL})"
56
+ end
50
57
 
51
- label = "#{name}(#{parameters.map(&:decorated_name).join(", ")})"
58
+ active_signature, active_parameter = determine_active_signature_and_parameter(node, signatures)
52
59
 
60
+ signature_help = Interface::SignatureHelp.new(
61
+ signatures: generate_signatures(signatures, name, methods, title, extra_links),
62
+ active_signature: active_signature,
63
+ active_parameter: active_parameter,
64
+ )
65
+ @response_builder.replace(signature_help)
66
+ end
67
+
68
+ private
69
+
70
+ sig do
71
+ params(node: Prism::CallNode, signatures: T::Array[RubyIndexer::Entry::Signature]).returns([Integer, Integer])
72
+ end
73
+ def determine_active_signature_and_parameter(node, signatures)
53
74
  arguments_node = node.arguments
54
75
  arguments = arguments_node&.arguments || []
55
- active_parameter = (arguments.length - 1).clamp(0, parameters.length - 1)
76
+
77
+ # Find the first signature that matches the current arguments. If the user is invoking a method incorrectly and
78
+ # none of the signatures match, we show the first one
79
+ active_sig_index = signatures.find_index do |signature|
80
+ signature.matches?(arguments)
81
+ end || 0
82
+
83
+ parameter_length = [T.must(signatures[active_sig_index]).parameters.length - 1, 0].max
84
+ active_parameter = (arguments.length - 1).clamp(0, parameter_length)
56
85
 
57
86
  # If there are arguments, then we need to check if there's a trailing comma after the end of the last argument
58
87
  # to advance the active parameter to the next one
@@ -61,27 +90,29 @@ module RubyLsp
61
90
  active_parameter += 1
62
91
  end
63
92
 
64
- title = +""
65
-
66
- extra_links = if type.is_a?(TypeInferrer::GuessedType)
67
- title << "\n\nGuessed receiver: #{type.name}"
68
- "[Learn more about guessed types](#{GUESSED_TYPES_URL})"
69
- end
93
+ [active_sig_index, active_parameter]
94
+ end
70
95
 
71
- signature_help = Interface::SignatureHelp.new(
72
- signatures: [
73
- Interface::SignatureInformation.new(
74
- label: label,
75
- parameters: parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
76
- documentation: Interface::MarkupContent.new(
77
- kind: "markdown",
78
- value: markdown_from_index_entries(title, methods, extra_links: extra_links),
79
- ),
96
+ sig do
97
+ params(
98
+ signatures: T::Array[RubyIndexer::Entry::Signature],
99
+ method_name: String,
100
+ methods: T::Array[RubyIndexer::Entry],
101
+ title: String,
102
+ extra_links: T.nilable(String),
103
+ ).returns(T::Array[Interface::SignatureInformation])
104
+ end
105
+ def generate_signatures(signatures, method_name, methods, title, extra_links)
106
+ signatures.map do |signature|
107
+ Interface::SignatureInformation.new(
108
+ label: "#{method_name}(#{signature.format})",
109
+ parameters: signature.parameters.map { |param| Interface::ParameterInformation.new(label: param.name) },
110
+ documentation: Interface::MarkupContent.new(
111
+ kind: "markdown",
112
+ value: markdown_from_index_entries(title, methods, extra_links: extra_links),
80
113
  ),
81
- ],
82
- active_parameter: active_parameter,
83
- )
84
- @response_builder.replace(signature_help)
114
+ )
115
+ end
85
116
  end
86
117
  end
87
118
  end
@@ -14,18 +14,19 @@ module RubyLsp
14
14
  super
15
15
  end
16
16
 
17
- sig { override.returns(ParseResultType) }
18
- def parse
19
- return @parse_result unless @needs_parsing
17
+ sig { override.returns(T::Boolean) }
18
+ def parse!
19
+ return false unless @needs_parsing
20
20
 
21
21
  @needs_parsing = false
22
22
 
23
23
  _, _, declarations = RBS::Parser.parse_signature(@source)
24
24
  @syntax_error = false
25
25
  @parse_result = declarations
26
+ true
26
27
  rescue RBS::ParsingError
27
28
  @syntax_error = true
28
- @parse_result
29
+ true
29
30
  end
30
31
 
31
32
  sig { override.returns(T::Boolean) }
@@ -3,24 +3,9 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code action resolve demo](../../code_action_resolve.gif)
7
- #
8
6
  # The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
9
7
  # request is used to to resolve the edit field for a given code action, if it is not already provided in the
10
8
  # textDocument/codeAction response. We can use it for scenarios that require more computation such as refactoring.
11
- #
12
- # # Example: Extract to variable
13
- #
14
- # ```ruby
15
- # # Before:
16
- # 1 + 1 # Select the text and use Refactor: Extract Variable
17
- #
18
- # # After:
19
- # new_variable = 1 + 1
20
- # new_variable
21
- #
22
- # ```
23
- #
24
9
  class CodeActionResolve < Request
25
10
  extend T::Sig
26
11
  include Support::Common
@@ -3,19 +3,9 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code actions demo](../../code_actions.gif)
7
- #
8
6
  # The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
9
7
  # request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
10
8
  # specific diagnostic.
11
- #
12
- # # Example
13
- #
14
- # ```ruby
15
- # def say_hello
16
- # puts "Hello" # --> code action: quick fix indentation
17
- # end
18
- # ```
19
9
  class CodeActions < Request
20
10
  extend T::Sig
21
11
 
@@ -7,19 +7,9 @@ require "ruby_lsp/listeners/code_lens"
7
7
 
8
8
  module RubyLsp
9
9
  module Requests
10
- # ![Code lens demo](../../code_lens.gif)
11
- #
12
10
  # The
13
11
  # [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
14
- # request informs the editor of runnable commands such as testing and debugging
15
- #
16
- # # Example
17
- #
18
- # ```ruby
19
- # # Run | Run in Terminal | Debug
20
- # class Test < Minitest::Test
21
- # end
22
- # ```
12
+ # request informs the editor of runnable commands such as testing and debugging.
23
13
  class CodeLens < Request
24
14
  extend T::Sig
25
15
 
@@ -5,27 +5,8 @@ require "ruby_lsp/listeners/completion"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Completion demo](../../completion.gif)
9
- #
10
8
  # The [completion](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
11
9
  # suggests possible completions according to what the developer is typing.
12
- #
13
- # Currently supported targets:
14
- #
15
- # - Classes
16
- # - Modules
17
- # - Constants
18
- # - Require paths
19
- # - Methods invoked on self only
20
- # - Instance variables
21
- #
22
- # # Example
23
- #
24
- # ```ruby
25
- # require "ruby_lsp/requests" # --> completion: suggests `base_request`, `code_actions`, ...
26
- #
27
- # RubyLsp::Requests:: # --> completion: suggests `Completion`, `Hover`, ...
28
- # ```
29
10
  class Completion < Request
30
11
  extend T::Sig
31
12
 
@@ -36,7 +17,7 @@ module RubyLsp
36
17
  def provider
37
18
  Interface::CompletionOptions.new(
38
19
  resolve_provider: true,
39
- trigger_characters: ["/", "\"", "'", ":", "@", "."],
20
+ trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<"],
40
21
  completion_item: {
41
22
  labelDetailsSupport: true,
42
23
  },
@@ -60,6 +41,8 @@ module RubyLsp
60
41
  # Completion always receives the position immediately after the character that was just typed. Here we adjust it
61
42
  # back by 1, so that we find the right node
62
43
  char_position = document.create_scanner.find_char_position(params[:position]) - 1
44
+ delegate_request_if_needed!(global_state, document, char_position)
45
+
63
46
  node_context = RubyDocument.locate(
64
47
  document.parse_result.value,
65
48
  char_position,
@@ -3,8 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Completion resolve demo](../../completion_resolve.gif)
7
- #
8
6
  # The [completionItem/resolve](https://microsoft.github.io/language-server-protocol/specification#completionItem_resolve)
9
7
  # request provides additional information about the currently selected completion. Specifically, the `labelDetails`
10
8
  # and `documentation` fields are provided, which are omitted from the completion items returned by
@@ -15,12 +13,6 @@ module RubyLsp
15
13
  #
16
14
  # At most 10 definitions are included, to ensure low latency during request processing and rendering the completion
17
15
  # item.
18
- #
19
- # # Example
20
- #
21
- # ```ruby
22
- # A # -> as the user cycles through completion items, the documentation will be resolved and displayed
23
- # ```
24
16
  class CompletionResolve < Request
25
17
  extend T::Sig
26
18
  include Requests::Support::Common
@@ -5,27 +5,9 @@ require "ruby_lsp/listeners/definition"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Definition demo](../../definition.gif)
9
- #
10
8
  # The [definition
11
9
  # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
12
10
  # definition of the symbol under the cursor.
13
- #
14
- # Currently supported targets:
15
- #
16
- # - Classes
17
- # - Modules
18
- # - Constants
19
- # - Require paths
20
- # - Methods invoked on self only and on receivers where the type is unknown
21
- # - Instance variables
22
- #
23
- # # Example
24
- #
25
- # ```ruby
26
- # require "some_gem/file" # <- Request go to definition on this string will take you to the file
27
- # Product.new # <- Request go to definition on this class name will take you to its declaration.
28
- # ```
29
11
  class Definition < Request
30
12
  extend T::Sig
31
13
  extend T::Generic
@@ -53,8 +35,12 @@ module RubyLsp
53
35
  )
54
36
  @dispatcher = dispatcher
55
37
 
56
- node_context = document.locate_node(
57
- position,
38
+ char_position = document.create_scanner.find_char_position(position)
39
+ delegate_request_if_needed!(global_state, document, char_position)
40
+
41
+ node_context = RubyDocument.locate(
42
+ document.parse_result.value,
43
+ char_position,
58
44
  node_types: [
59
45
  Prism::CallNode,
60
46
  Prism::ConstantReadNode,
@@ -3,19 +3,9 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Diagnostics demo](../../diagnostics.gif)
7
- #
8
6
  # The
9
7
  # [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
10
8
  # request informs the editor of RuboCop offenses for a given file.
11
- #
12
- # # Example
13
- #
14
- # ```ruby
15
- # def say_hello
16
- # puts "Hello" # --> diagnostics: incorrect indentation
17
- # end
18
- # ```
19
9
  class Diagnostics < Request
20
10
  extend T::Sig
21
11
 
@@ -5,8 +5,6 @@ require "ruby_lsp/listeners/document_highlight"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Document highlight demo](../../document_highlight.gif)
9
- #
10
8
  # The [document highlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
11
9
  # informs the editor all relevant elements of the currently pointed item for highlighting. For example, when
12
10
  # the cursor is on the `F` of the constant `FOO`, the editor should identify other occurrences of `FOO`
@@ -14,29 +12,24 @@ module RubyLsp
14
12
  #
15
13
  # For writable elements like constants or variables, their read/write occurrences should be highlighted differently.
16
14
  # This is achieved by sending different "kind" attributes to the editor (2 for read and 3 for write).
17
- #
18
- # # Example
19
- #
20
- # ```ruby
21
- # FOO = 1 # should be highlighted as "write"
22
- #
23
- # def foo
24
- # FOO # should be highlighted as "read"
25
- # end
26
- # ```
27
15
  class DocumentHighlight < Request
28
16
  extend T::Sig
29
17
 
30
18
  sig do
31
19
  params(
20
+ global_state: GlobalState,
32
21
  document: T.any(RubyDocument, ERBDocument),
33
22
  position: T::Hash[Symbol, T.untyped],
34
23
  dispatcher: Prism::Dispatcher,
35
24
  ).void
36
25
  end
37
- def initialize(document, position, dispatcher)
26
+ def initialize(global_state, document, position, dispatcher)
38
27
  super()
39
- node_context = document.locate_node(position)
28
+ char_position = document.create_scanner.find_char_position(position)
29
+ delegate_request_if_needed!(global_state, document, char_position)
30
+
31
+ node_context = RubyDocument.locate(document.parse_result.value, char_position)
32
+
40
33
  @response_builder = T.let(
41
34
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
42
35
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight],
@@ -5,19 +5,9 @@ require "ruby_lsp/listeners/document_link"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Document link demo](../../document_link.gif)
9
- #
10
8
  # The [document link](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentLink)
11
9
  # makes `# source://PATH_TO_FILE#line` comments in a Ruby/RBI file clickable if the file exists.
12
10
  # When the user clicks the link, it'll open that location.
13
- #
14
- # # Example
15
- #
16
- # ```ruby
17
- # # source://syntax_tree/3.2.1/lib/syntax_tree.rb#51 <- it will be clickable and will take the user to that location
18
- # def format(source, maxwidth = T.unsafe(nil))
19
- # end
20
- # ```
21
11
  class DocumentLink < Request
22
12
  extend T::Sig
23
13
 
@@ -5,29 +5,12 @@ require "ruby_lsp/listeners/document_symbol"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Document symbol demo](../../document_symbol.gif)
9
- #
10
8
  # The [document
11
9
  # symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
12
10
  # informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
13
11
  # this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
14
12
  #
15
13
  # In VS Code, fuzzy symbol search can be accessed by opening the command palette and inserting an `@` symbol.
16
- #
17
- # # Example
18
- #
19
- # ```ruby
20
- # class Person # --> document symbol: class
21
- # attr_reader :age # --> document symbol: field
22
- #
23
- # def initialize
24
- # @age = 0 # --> document symbol: variable
25
- # end
26
- #
27
- # def age # --> document symbol: method
28
- # end
29
- # end
30
- # ```
31
14
  class DocumentSymbol < Request
32
15
  extend T::Sig
33
16
 
@@ -5,18 +5,8 @@ require "ruby_lsp/listeners/folding_ranges"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Folding ranges demo](../../folding_ranges.gif)
9
- #
10
8
  # The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
11
9
  # request informs the editor of the ranges where and how code can be folded.
12
- #
13
- # # Example
14
- #
15
- # ```ruby
16
- # def say_hello # <-- folding range start
17
- # puts "Hello"
18
- # end # <-- folding range end
19
- # ```
20
10
  class FoldingRanges < Request
21
11
  extend T::Sig
22
12
 
@@ -3,25 +3,9 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Formatting symbol demo](../../formatting.gif)
7
- #
8
6
  # The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
9
7
  # request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
10
8
  # registering the ruby-lsp as the Ruby formatter.
11
- #
12
- # The `rubyLsp.formatter` setting specifies which formatter to use.
13
- # If set to `auto` then it behaves as follows:
14
- # * It will use RuboCop if it is part of the bundle.
15
- # * If RuboCop is not available, and `syntax_tree` is a direct dependency, it will use that.
16
- # * Otherwise, no formatting will be applied.
17
- #
18
- # # Example
19
- #
20
- # ```ruby
21
- # def say_hello
22
- # puts "Hello" # --> formatting: fixes the indentation on save
23
- # end
24
- # ```
25
9
  class Formatting < Request
26
10
  extend T::Sig
27
11
 
@@ -58,14 +42,16 @@ module RubyLsp
58
42
  formatted_text = @active_formatter.run_formatting(@uri, @document)
59
43
  return unless formatted_text
60
44
 
45
+ lines = @document.source.lines
61
46
  size = @document.source.size
47
+
62
48
  return if formatted_text.size == size && formatted_text == @document.source
63
49
 
64
50
  [
65
51
  Interface::TextEdit.new(
66
52
  range: Interface::Range.new(
67
53
  start: Interface::Position.new(line: 0, character: 0),
68
- end: Interface::Position.new(line: size, character: size),
54
+ end: Interface::Position.new(line: lines.size, character: 0),
69
55
  ),
70
56
  new_text: formatted_text,
71
57
  ),