ruby-lsp 0.17.16 → 0.18.0

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