ruby-lsp 0.17.16 → 0.17.17

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc4416ea8876d8207b6c1955cd47af5f3312a0f0dc3644563bfd35138a903535
4
- data.tar.gz: 5961dd8ca49539c20049509c0afed8b2442c3d16ec12d1fb84c9e45dbd027760
3
+ metadata.gz: e4b4c9204834ddf87c84009003c5eef5872211eaef6ed64022a93a2749e1849e
4
+ data.tar.gz: 0d770cf2f67f90fb974a9fca08d9fb4dea1a73d668984ff96e5cc5785d7c5766
5
5
  SHA512:
6
- metadata.gz: 4b77547221c1750871ff6e1a3635b0ae6f945b86862f484d20eb7587a1feb9830e1d1d7770f16c2e7efeb70e6a953529041f8acc8fe3d7c30940687d20563f96
7
- data.tar.gz: 1093a1a6ac45e8627f26a6c9dbb0017079d2804904fe9a4303e3698d4131c6eeedf2bbafac93e6475f676860624b9c53d482c52cfe8380e67ed05ce525e99957
6
+ metadata.gz: 3534227d67761a6738c5e2ae4a06a08964a0836c4a30c9d306917efdd93aae1285d6ed03992e88486a44e5d3f7e7271997e56bbb12ab908599687c6a1e622cb4
7
+ data.tar.gz: 5345bcf362b6206a0be392eebe5db2175fe9d06b1a38f113fe11d979e5fdcc48851cb47e5f016ae49726ffc73432e81ef801522588cee79ebb36fe545e02d965
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.17.16
1
+ 0.17.17
data/exe/ruby-lsp CHANGED
@@ -98,16 +98,17 @@ if options[:debug]
98
98
  end
99
99
 
100
100
  if options[:time_index]
101
- require "benchmark"
102
-
103
101
  index = RubyIndexer::Index.new
104
102
 
105
- result = Benchmark.realtime { index.index_all }
103
+ time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
104
+ index.index_all
105
+ elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_start
106
+
106
107
  entries = index.instance_variable_get(:@entries)
107
108
  entries_by_entry_type = entries.values.flatten.group_by(&:class)
108
109
 
109
110
  puts <<~MSG
110
- Ruby LSP v#{RubyLsp::VERSION}: Indexing took #{result.round(5)} seconds and generated:
111
+ Ruby LSP v#{RubyLsp::VERSION}: Indexing took #{elapsed_time.round(5)} seconds and generated:
111
112
  - #{entries_by_entry_type.sort_by { |k, _| k.to_s }.map { |k, v| "#{k.name.split("::").last}: #{v.size}" }.join("\n- ")}
112
113
  MSG
113
114
  return
@@ -232,7 +232,7 @@ module RubyIndexer
232
232
  next unless ancestor_index && (!existing_entry || ancestor_index < existing_entry_index)
233
233
 
234
234
  if entry.is_a?(Entry::UnresolvedMethodAlias)
235
- resolved_alias = resolve_method_alias(entry, receiver_name)
235
+ resolved_alias = resolve_method_alias(entry, receiver_name, [])
236
236
  hash[entry_name] = [resolved_alias, ancestor_index] if resolved_alias.is_a?(Entry::MethodAlias)
237
237
  else
238
238
  hash[entry_name] = [entry, ancestor_index]
@@ -394,16 +394,18 @@ module RubyIndexer
394
394
  real_parts.join("::")
395
395
  end
396
396
 
397
- # Attempts to find methods for a resolved fully qualified receiver name.
397
+ # Attempts to find methods for a resolved fully qualified receiver name. Do not provide the `seen_names` parameter
398
+ # as it is used only internally to prevent infinite loops when resolving circular aliases
398
399
  # Returns `nil` if the method does not exist on that receiver
399
400
  sig do
400
401
  params(
401
402
  method_name: String,
402
403
  receiver_name: String,
404
+ seen_names: T::Array[String],
403
405
  inherited_only: T::Boolean,
404
406
  ).returns(T.nilable(T::Array[T.any(Entry::Member, Entry::MethodAlias)]))
405
407
  end
406
- def resolve_method(method_name, receiver_name, inherited_only: false)
408
+ def resolve_method(method_name, receiver_name, seen_names = [], inherited_only: false)
407
409
  method_entries = self[method_name]
408
410
  return unless method_entries
409
411
 
@@ -418,7 +420,7 @@ module RubyIndexer
418
420
  when Entry::UnresolvedMethodAlias
419
421
  # Resolve aliases lazily as we find them
420
422
  if entry.owner&.name == ancestor
421
- resolved_alias = resolve_method_alias(entry, receiver_name)
423
+ resolved_alias = resolve_method_alias(entry, receiver_name, seen_names)
422
424
  resolved_alias if resolved_alias.is_a?(Entry::MethodAlias)
423
425
  end
424
426
  end
@@ -614,6 +616,17 @@ module RubyIndexer
614
616
  singleton
615
617
  end
616
618
 
619
+ sig do
620
+ type_parameters(:T).params(
621
+ path: String,
622
+ type: T::Class[T.all(T.type_parameter(:T), Entry)],
623
+ ).returns(T.nilable(T::Array[T.type_parameter(:T)]))
624
+ end
625
+ def entries_for(path, type)
626
+ entries = @files_to_entries[path]
627
+ entries&.grep(type)
628
+ end
629
+
617
630
  private
618
631
 
619
632
  # Runs the registered included hooks
@@ -919,16 +932,21 @@ module RubyIndexer
919
932
  params(
920
933
  entry: Entry::UnresolvedMethodAlias,
921
934
  receiver_name: String,
935
+ seen_names: T::Array[String],
922
936
  ).returns(T.any(Entry::MethodAlias, Entry::UnresolvedMethodAlias))
923
937
  end
924
- def resolve_method_alias(entry, receiver_name)
925
- return entry if entry.new_name == entry.old_name
938
+ def resolve_method_alias(entry, receiver_name, seen_names)
939
+ new_name = entry.new_name
940
+ return entry if new_name == entry.old_name
941
+ return entry if seen_names.include?(new_name)
942
+
943
+ seen_names << new_name
926
944
 
927
- target_method_entries = resolve_method(entry.old_name, receiver_name)
945
+ target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
928
946
  return entry unless target_method_entries
929
947
 
930
948
  resolved_alias = Entry::MethodAlias.new(T.must(target_method_entries.first), entry)
931
- original_entries = T.must(@entries[entry.new_name])
949
+ original_entries = T.must(@entries[new_name])
932
950
  original_entries.delete(entry)
933
951
  original_entries << resolved_alias
934
952
  resolved_alias
@@ -1822,5 +1822,46 @@ module RubyIndexer
1822
1822
  @index.linearized_ancestors_of("Foo::Child::<Class:Child>"),
1823
1823
  )
1824
1824
  end
1825
+
1826
+ def test_resolving_circular_method_aliases_on_class_reopen
1827
+ index(<<~RUBY)
1828
+ class Foo
1829
+ alias bar ==
1830
+ def ==(other) = true
1831
+ end
1832
+
1833
+ class Foo
1834
+ alias == bar
1835
+ end
1836
+ RUBY
1837
+
1838
+ method = @index.resolve_method("==", "Foo").first
1839
+ assert_kind_of(Entry::Method, method)
1840
+ assert_equal("==", method.name)
1841
+
1842
+ candidates = @index.method_completion_candidates("=", "Foo")
1843
+ assert_equal(["==", "==="], candidates.map(&:name))
1844
+ end
1845
+
1846
+ def test_entries_for
1847
+ index(<<~RUBY)
1848
+ class Foo; end
1849
+
1850
+ module Bar
1851
+ def my_def; end
1852
+ def self.my_singleton_def; end
1853
+ end
1854
+ RUBY
1855
+
1856
+ entries = @index.entries_for("/fake/path/foo.rb", Entry)
1857
+ assert_equal(["Foo", "Bar", "my_def", "Bar::<Class:Bar>", "my_singleton_def"], entries.map(&:name))
1858
+
1859
+ entries = @index.entries_for("/fake/path/foo.rb", RubyIndexer::Entry::Namespace)
1860
+ assert_equal(["Foo", "Bar", "Bar::<Class:Bar>"], entries.map(&:name))
1861
+ end
1862
+
1863
+ def test_entries_for_returns_nil_if_no_matches
1864
+ assert_nil(@index.entries_for("non_existing_file.rb", Entry::Namespace))
1865
+ end
1825
1866
  end
1826
1867
  end
@@ -30,6 +30,8 @@ module RubyLsp
30
30
  # Addon instances that have declared a handler to accept file watcher events
31
31
  @file_watcher_addons = T.let([], T::Array[Addon])
32
32
 
33
+ AddonNotFoundError = Class.new(StandardError)
34
+
33
35
  class << self
34
36
  extend T::Sig
35
37
 
@@ -78,11 +80,10 @@ module RubyLsp
78
80
  errors
79
81
  end
80
82
 
81
- # Intended for use by tests for addons
82
83
  sig { params(addon_name: String).returns(Addon) }
83
84
  def get(addon_name)
84
85
  addon = addons.find { |addon| addon.name == addon_name }
85
- raise "Could not find addon '#{addon_name}'" unless addon
86
+ raise AddonNotFoundError, "Could not find addon '#{addon_name}'" unless addon
86
87
 
87
88
  addon
88
89
  end
@@ -40,6 +40,12 @@ module RubyLsp
40
40
  @supports_watching_files = T.let(false, T::Boolean)
41
41
  @experimental_features = T.let(false, T::Boolean)
42
42
  @type_inferrer = T.let(TypeInferrer.new(@index, @experimental_features), TypeInferrer)
43
+ @addon_settings = T.let({}, T::Hash[String, T.untyped])
44
+ end
45
+
46
+ sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
47
+ def settings_for_addon(addon_name)
48
+ @addon_settings[addon_name]
43
49
  end
44
50
 
45
51
  sig { params(identifier: String, instance: Requests::Support::Formatter).void }
@@ -119,6 +125,12 @@ module RubyLsp
119
125
  @experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
120
126
  @type_inferrer.experimental_features = @experimental_features
121
127
 
128
+ addon_settings = options.dig(:initializationOptions, :addonSettings)
129
+ if addon_settings
130
+ addon_settings.transform_keys!(&:to_s)
131
+ @addon_settings.merge!(addon_settings)
132
+ end
133
+
122
134
  notifications
123
135
  end
124
136
 
@@ -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.
@@ -58,14 +58,16 @@ module RubyLsp
58
58
  formatted_text = @active_formatter.run_formatting(@uri, @document)
59
59
  return unless formatted_text
60
60
 
61
+ lines = @document.source.lines
61
62
  size = @document.source.size
63
+
62
64
  return if formatted_text.size == size && formatted_text == @document.source
63
65
 
64
66
  [
65
67
  Interface::TextEdit.new(
66
68
  range: Interface::Range.new(
67
69
  start: Interface::Position.new(line: 0, character: 0),
68
- end: Interface::Position.new(line: size, character: size),
70
+ end: Interface::Position.new(line: lines.size, character: 0),
69
71
  ),
70
72
  new_text: formatted_text,
71
73
  ),
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.17.16
4
+ version: 0.17.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-26 00:00:00.000000000 Z
11
+ date: 2024-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -28,22 +28,16 @@ dependencies:
28
28
  name: prism
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 0.29.0
34
- - - "<"
31
+ - - "~>"
35
32
  - !ruby/object:Gem::Version
36
- version: '0.31'
33
+ version: '1.0'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 0.29.0
44
- - - "<"
38
+ - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: '0.31'
40
+ version: '1.0'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rbs
49
43
  requirement: !ruby/object:Gem::Requirement