ruby-lsp 0.19.0 → 0.20.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp-check +1 -1
  4. data/lib/core_ext/uri.rb +2 -2
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +85 -36
  6. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +5 -1
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +46 -98
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +7 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +22 -0
  10. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +20 -5
  11. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +76 -14
  12. data/lib/ruby_indexer/test/classes_and_modules_test.rb +12 -0
  13. data/lib/ruby_indexer/test/enhancements_test.rb +5 -7
  14. data/lib/ruby_indexer/test/global_variable_test.rb +49 -0
  15. data/lib/ruby_indexer/test/index_test.rb +3 -0
  16. data/lib/ruby_indexer/test/rbs_indexer_test.rb +14 -0
  17. data/lib/ruby_indexer/test/reference_finder_test.rb +162 -6
  18. data/lib/ruby_lsp/erb_document.rb +20 -2
  19. data/lib/ruby_lsp/internal.rb +3 -1
  20. data/lib/ruby_lsp/listeners/definition.rb +20 -0
  21. data/lib/ruby_lsp/listeners/folding_ranges.rb +3 -3
  22. data/lib/ruby_lsp/requests/code_action_resolve.rb +16 -4
  23. data/lib/ruby_lsp/requests/completion.rb +1 -0
  24. data/lib/ruby_lsp/requests/definition.rb +2 -0
  25. data/lib/ruby_lsp/requests/document_highlight.rb +5 -1
  26. data/lib/ruby_lsp/requests/hover.rb +1 -0
  27. data/lib/ruby_lsp/requests/range_formatting.rb +57 -0
  28. data/lib/ruby_lsp/requests/references.rb +146 -0
  29. data/lib/ruby_lsp/requests/rename.rb +16 -9
  30. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  31. data/lib/ruby_lsp/requests/signature_help.rb +6 -1
  32. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  33. data/lib/ruby_lsp/requests/support/formatter.rb +3 -0
  34. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +6 -0
  35. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +6 -6
  36. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +8 -0
  37. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -2
  38. data/lib/ruby_lsp/response_builders/hover.rb +2 -2
  39. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +14 -8
  40. data/lib/ruby_lsp/response_builders/signature_help.rb +2 -2
  41. data/lib/ruby_lsp/ruby_document.rb +44 -8
  42. data/lib/ruby_lsp/server.rb +63 -2
  43. data/lib/ruby_lsp/type_inferrer.rb +1 -1
  44. metadata +8 -5
@@ -25,10 +25,14 @@ module RubyLsp
25
25
  params(
26
26
  node: Prism::Node,
27
27
  char_position: Integer,
28
+ code_units_cache: T.any(
29
+ T.proc.params(arg0: Integer).returns(Integer),
30
+ Prism::CodeUnitsCache,
31
+ ),
28
32
  node_types: T::Array[T.class_of(Prism::Node)],
29
33
  ).returns(NodeContext)
30
34
  end
31
- def locate(node, char_position, node_types: [])
35
+ def locate(node, char_position, code_units_cache:, node_types: [])
32
36
  queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
33
37
  closest = node
34
38
  parent = T.let(nil, T.nilable(Prism::Node))
@@ -61,16 +65,21 @@ module RubyLsp
61
65
 
62
66
  # Skip if the current node doesn't cover the desired position
63
67
  loc = candidate.location
64
- next unless (loc.start_offset...loc.end_offset).cover?(char_position)
68
+ loc_start_offset = loc.cached_start_code_units_offset(code_units_cache)
69
+ loc_end_offset = loc.cached_end_code_units_offset(code_units_cache)
70
+ next unless (loc_start_offset...loc_end_offset).cover?(char_position)
65
71
 
66
72
  # If the node's start character is already past the position, then we should've found the closest node
67
73
  # already
68
- break if char_position < loc.start_offset
74
+ break if char_position < loc_start_offset
69
75
 
70
76
  # If the candidate starts after the end of the previous nesting level, then we've exited that nesting level
71
77
  # and need to pop the stack
72
78
  previous_level = nesting_nodes.last
73
- nesting_nodes.pop if previous_level && loc.start_offset > previous_level.location.end_offset
79
+ if previous_level &&
80
+ (loc_start_offset > previous_level.location.cached_end_code_units_offset(code_units_cache))
81
+ nesting_nodes.pop
82
+ end
74
83
 
75
84
  # Keep track of the nesting where we found the target. This is used to determine the fully qualified name of
76
85
  # the target when it is a constant
@@ -83,8 +92,10 @@ module RubyLsp
83
92
  if candidate.is_a?(Prism::CallNode)
84
93
  arg_loc = candidate.arguments&.location
85
94
  blk_loc = candidate.block&.location
86
- if (arg_loc && (arg_loc.start_offset...arg_loc.end_offset).cover?(char_position)) ||
87
- (blk_loc && (blk_loc.start_offset...blk_loc.end_offset).cover?(char_position))
95
+ if (arg_loc && (arg_loc.cached_start_code_units_offset(code_units_cache)...
96
+ arg_loc.cached_end_code_units_offset(code_units_cache)).cover?(char_position)) ||
97
+ (blk_loc && (blk_loc.cached_start_code_units_offset(code_units_cache)...
98
+ blk_loc.cached_end_code_units_offset(code_units_cache)).cover?(char_position))
88
99
  call_node = candidate
89
100
  end
90
101
  end
@@ -94,7 +105,9 @@ module RubyLsp
94
105
 
95
106
  # If the current node is narrower than or equal to the previous closest node, then it is more precise
96
107
  closest_loc = closest.location
97
- if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
108
+ closest_node_start_offset = closest_loc.cached_start_code_units_offset(code_units_cache)
109
+ closest_node_end_offset = closest_loc.cached_end_code_units_offset(code_units_cache)
110
+ if loc_end_offset - loc_start_offset <= closest_node_end_offset - closest_node_start_offset
98
111
  parent = closest
99
112
  closest = candidate
100
113
  end
@@ -121,12 +134,30 @@ module RubyLsp
121
134
  end
122
135
  end
123
136
 
137
+ sig do
138
+ returns(T.any(
139
+ T.proc.params(arg0: Integer).returns(Integer),
140
+ Prism::CodeUnitsCache,
141
+ ))
142
+ end
143
+ attr_reader :code_units_cache
144
+
145
+ sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
146
+ def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
147
+ super
148
+ @code_units_cache = T.let(@parse_result.code_units_cache(@encoding), T.any(
149
+ T.proc.params(arg0: Integer).returns(Integer),
150
+ Prism::CodeUnitsCache,
151
+ ))
152
+ end
153
+
124
154
  sig { override.returns(T::Boolean) }
125
155
  def parse!
126
156
  return false unless @needs_parsing
127
157
 
128
158
  @needs_parsing = false
129
159
  @parse_result = Prism.parse(@source)
160
+ @code_units_cache = @parse_result.code_units_cache(@encoding)
130
161
  true
131
162
  end
132
163
 
@@ -201,7 +232,12 @@ module RubyLsp
201
232
  ).returns(NodeContext)
202
233
  end
203
234
  def locate_node(position, node_types: [])
204
- RubyDocument.locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
235
+ RubyDocument.locate(
236
+ @parse_result.value,
237
+ create_scanner.find_char_position(position),
238
+ code_units_cache: @code_units_cache,
239
+ node_types: node_types,
240
+ )
205
241
  end
206
242
  end
207
243
  end
@@ -43,6 +43,8 @@ module RubyLsp
43
43
  text_document_semantic_tokens_range(message)
44
44
  when "textDocument/formatting"
45
45
  text_document_formatting(message)
46
+ when "textDocument/rangeFormatting"
47
+ text_document_range_formatting(message)
46
48
  when "textDocument/documentHighlight"
47
49
  text_document_document_highlight(message)
48
50
  when "textDocument/onTypeFormatting"
@@ -69,6 +71,8 @@ module RubyLsp
69
71
  text_document_prepare_type_hierarchy(message)
70
72
  when "textDocument/rename"
71
73
  text_document_rename(message)
74
+ when "textDocument/references"
75
+ text_document_references(message)
72
76
  when "typeHierarchy/supertypes"
73
77
  type_hierarchy_supertypes(message)
74
78
  when "typeHierarchy/subtypes"
@@ -87,7 +91,16 @@ module RubyLsp
87
91
  id: message[:id],
88
92
  response:
89
93
  Addon.addons.map do |addon|
90
- { name: addon.name, errored: addon.error? }
94
+ version_method = addon.method(:version)
95
+
96
+ # If the add-on doesn't define a `version` method, we'd be calling the abstract method defined by
97
+ # Sorbet, which would raise an error.
98
+ # Therefore, we only call the method if it's defined by the add-on itself
99
+ if version_method.owner != Addon
100
+ version = addon.version
101
+ end
102
+
103
+ { name: addon.name, version: version, errored: addon.error? }
91
104
  end,
92
105
  ),
93
106
  )
@@ -230,6 +243,8 @@ module RubyLsp
230
243
  signature_help_provider: signature_help_provider,
231
244
  type_hierarchy_provider: type_hierarchy_provider,
232
245
  rename_provider: !@global_state.has_type_checker,
246
+ references_provider: !@global_state.has_type_checker,
247
+ document_range_formatting_provider: true,
233
248
  experimental: {
234
249
  addon_detection: true,
235
250
  },
@@ -513,6 +528,34 @@ module RubyLsp
513
528
  send_message(Result.new(id: message[:id], response: request.perform))
514
529
  end
515
530
 
531
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
532
+ def text_document_range_formatting(message)
533
+ # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
534
+ if @global_state.formatter == "none"
535
+ send_empty_response(message[:id])
536
+ return
537
+ end
538
+
539
+ params = message[:params]
540
+ uri = params.dig(:textDocument, :uri)
541
+ # Do not format files outside of the workspace. For example, if someone is looking at a gem's source code, we
542
+ # don't want to format it
543
+ path = uri.to_standardized_path
544
+ unless path.nil? || path.start_with?(@global_state.workspace_path)
545
+ send_empty_response(message[:id])
546
+ return
547
+ end
548
+
549
+ document = @store.get(uri)
550
+ unless document.is_a?(RubyDocument)
551
+ send_empty_response(message[:id])
552
+ return
553
+ end
554
+
555
+ response = Requests::RangeFormatting.new(@global_state, document, params).perform
556
+ send_message(Result.new(id: message[:id], response: response))
557
+ end
558
+
516
559
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
517
560
  def text_document_formatting(message)
518
561
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
@@ -636,6 +679,24 @@ module RubyLsp
636
679
  send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
637
680
  end
638
681
 
682
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
683
+ def text_document_references(message)
684
+ params = message[:params]
685
+ document = @store.get(params.dig(:textDocument, :uri))
686
+
687
+ unless document.is_a?(RubyDocument)
688
+ send_empty_response(message[:id])
689
+ return
690
+ end
691
+
692
+ send_message(
693
+ Result.new(
694
+ id: message[:id],
695
+ response: Requests::References.new(@global_state, @store, document, params).perform,
696
+ ),
697
+ )
698
+ end
699
+
639
700
  sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
640
701
  def sorbet_level(document)
641
702
  return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
@@ -711,7 +772,7 @@ module RubyLsp
711
772
  return
712
773
  end
713
774
 
714
- result = Requests::CodeActionResolve.new(document, params).perform
775
+ result = Requests::CodeActionResolve.new(document, @global_state, params).perform
715
776
 
716
777
  case result
717
778
  when Requests::CodeActionResolve::Error::EmptySelection
@@ -93,7 +93,7 @@ module RubyLsp
93
93
  raw_receiver = if receiver.is_a?(Prism::CallNode)
94
94
  receiver.message
95
95
  else
96
- receiver&.slice
96
+ receiver.slice
97
97
  end
98
98
 
99
99
  if raw_receiver
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-02 00:00:00.000000000 Z
11
+ date: 2024-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.1'
33
+ version: '1.2'
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '2.0'
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: '1.1'
43
+ version: '1.2'
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '2.0'
@@ -111,6 +111,7 @@ files:
111
111
  - lib/ruby_indexer/test/configuration_test.rb
112
112
  - lib/ruby_indexer/test/constant_test.rb
113
113
  - lib/ruby_indexer/test/enhancements_test.rb
114
+ - lib/ruby_indexer/test/global_variable_test.rb
114
115
  - lib/ruby_indexer/test/index_test.rb
115
116
  - lib/ruby_indexer/test/instance_variables_test.rb
116
117
  - lib/ruby_indexer/test/method_test.rb
@@ -154,6 +155,8 @@ files:
154
155
  - lib/ruby_lsp/requests/inlay_hints.rb
155
156
  - lib/ruby_lsp/requests/on_type_formatting.rb
156
157
  - lib/ruby_lsp/requests/prepare_type_hierarchy.rb
158
+ - lib/ruby_lsp/requests/range_formatting.rb
159
+ - lib/ruby_lsp/requests/references.rb
157
160
  - lib/ruby_lsp/requests/rename.rb
158
161
  - lib/ruby_lsp/requests/request.rb
159
162
  - lib/ruby_lsp/requests/selection_ranges.rb
@@ -209,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
212
  - !ruby/object:Gem::Version
210
213
  version: '0'
211
214
  requirements: []
212
- rubygems_version: 3.5.20
215
+ rubygems_version: 3.5.21
213
216
  signing_key:
214
217
  specification_version: 4
215
218
  summary: An opinionated language server for Ruby