ruby-lsp 0.23.2 → 0.23.3

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: 5d295233e64f7b79aa061f243c61c6d5bfb69cbdc5dac64474106473d7033bc8
4
- data.tar.gz: 559fbc2fb40b2a8043fefbf332cb4bf94ce8e3953299d6740d1d21034b79e92b
3
+ metadata.gz: 95642b8034d20684e2596755f391591d6dfffc9021e23a6e2efd0b9a0e500eb0
4
+ data.tar.gz: 6db469fda24144ce71eb0c2d8434251b3bf01a800035d85bea5e8e6801cf5120
5
5
  SHA512:
6
- metadata.gz: 52e0176c0e9516801f94d9bd387ad6acfcd640c500443527448b69f3b55e2627aafa3bea75a60e131c21c0b06b309aba28a98b263344d20ce6406e1e66b69a37
7
- data.tar.gz: 52b47878d9c5a71dadd618a5562a30901abf3fe167f71e08d92f1a5418710a4e360043ae08d553975ad7008d7f1fb657a9b650d05a311ddcfc92157a7283ec6c
6
+ metadata.gz: 69365797e36c6597b43b5067da2e10b6c634cdb9121081db0e7c16c657fe12e2aa72042ba7beb07480b1cbb4a24c9a37a7a71f5a49a1be42923adb7a85a80715
7
+ data.tar.gz: 22695e4a4fd4e72738b43c32e9a693ed9895033e82240317700cde5583537b0ad6e7d8ce530a110624c57278d5a08c4eeb207ae010a4db99b3885bc241c5caa4
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.2
1
+ 0.23.3
@@ -45,6 +45,12 @@ rescue Errno::ECHILD
45
45
  # In theory, the child process can finish before we even get to the wait call, but that is not an error
46
46
  end
47
47
 
48
+ error_path = File.join(".ruby-lsp", "install_error")
49
+
50
+ install_error = if File.exist?(error_path)
51
+ Marshal.load(File.read(error_path))
52
+ end
53
+
48
54
  begin
49
55
  bundle_env_path = File.join(".ruby-lsp", "bundle_env")
50
56
  # We can't require `bundler/setup` because that file prematurely exits the process if setup fails. However, we can't
@@ -67,21 +73,21 @@ begin
67
73
  rescue StandardError => e
68
74
  # If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most
69
75
  # features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be
70
- # missing, but we respect the LSP life cycle
71
- setup_error = e
72
- $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
76
+ # missing, but we respect the LSP life cycle.
77
+ #
78
+ # If an install error occurred and one of the gems is not installed, Bundler.setup is guaranteed to fail with
79
+ # `Bundler::GemNotFound`. We don't set `setup_error` here because that would essentially report the same problem twice
80
+ # to telemetry, which is not useful
81
+ unless install_error && e.is_a?(Bundler::GemNotFound)
82
+ setup_error = e
83
+ $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
84
+ end
73
85
 
74
86
  # If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
75
87
  # in degraded mode
76
88
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
77
89
  end
78
90
 
79
- error_path = File.join(".ruby-lsp", "install_error")
80
-
81
- install_error = if File.exist?(error_path)
82
- Marshal.load(File.read(error_path))
83
- end
84
-
85
91
  # Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
86
92
  # configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
87
93
  # paths into the load path manually or we may end up requiring the wrong version of the gem
@@ -287,7 +287,6 @@ module RubyIndexer
287
287
  when :module_function
288
288
  handle_module_function(node)
289
289
  when :private_class_method
290
- @visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
291
290
  handle_private_class_method(node)
292
291
  end
293
292
 
@@ -975,39 +974,42 @@ module RubyIndexer
975
974
 
976
975
  sig { params(node: Prism::CallNode).void }
977
976
  def handle_private_class_method(node)
978
- node.arguments&.arguments&.each do |argument|
979
- string_or_symbol_nodes = case argument
980
- when Prism::StringNode, Prism::SymbolNode
981
- [argument]
982
- when Prism::ArrayNode
983
- argument.elements
984
- else
985
- []
986
- end
977
+ arguments = node.arguments&.arguments
978
+ return unless arguments
987
979
 
988
- unless string_or_symbol_nodes.empty?
989
- # pop the visibility off since there isn't a method definition following `private_class_method`
990
- @visibility_stack.pop
991
- end
980
+ # If we're passing a method definition directly to `private_class_method`, push a new private scope. That will be
981
+ # applied when the indexer finds the method definition and then popped on `call_node_leave`
982
+ if arguments.first.is_a?(Prism::DefNode)
983
+ @visibility_stack.push(VisibilityScope.new(visibility: Entry::Visibility::PRIVATE))
984
+ return
985
+ end
992
986
 
993
- string_or_symbol_nodes.each do |string_or_symbol_node|
994
- method_name = case string_or_symbol_node
995
- when Prism::StringNode
996
- string_or_symbol_node.content
997
- when Prism::SymbolNode
998
- string_or_symbol_node.value
999
- end
1000
- next unless method_name
987
+ owner_name = @owner_stack.last&.name
988
+ return unless owner_name
1001
989
 
1002
- owner_name = @owner_stack.last&.name
1003
- next unless owner_name
990
+ # private_class_method accepts strings, symbols or arrays of strings and symbols as arguments. Here we build a
991
+ # single list of all of the method names that have to be made private
992
+ arrays, others = T.cast(
993
+ arguments.partition { |argument| argument.is_a?(Prism::ArrayNode) },
994
+ [T::Array[Prism::ArrayNode], T::Array[Prism::Node]],
995
+ )
996
+ arrays.each { |array| others.concat(array.elements) }
1004
997
 
1005
- entries = @index.resolve_method(method_name, @index.existing_or_new_singleton_class(owner_name).name)
1006
- next unless entries
998
+ names = others.filter_map do |argument|
999
+ case argument
1000
+ when Prism::StringNode
1001
+ argument.unescaped
1002
+ when Prism::SymbolNode
1003
+ argument.value
1004
+ end
1005
+ end
1007
1006
 
1008
- entries.each do |entry|
1009
- entry.visibility = Entry::Visibility::PRIVATE
1010
- end
1007
+ names.each do |name|
1008
+ entries = @index.resolve_method(name, @index.existing_or_new_singleton_class(owner_name).name)
1009
+ next unless entries
1010
+
1011
+ entries.each do |entry|
1012
+ entry.visibility = Entry::Visibility::PRIVATE
1011
1013
  end
1012
1014
  end
1013
1015
  end
@@ -909,6 +909,21 @@ module RubyIndexer
909
909
  assert_equal("Foo", T.must(entry.owner).name)
910
910
  end
911
911
 
912
+ def test_making_several_class_methods_private
913
+ index(<<~RUBY)
914
+ class Foo
915
+ def self.bar; end
916
+ def self.baz; end
917
+ def self.qux; end
918
+
919
+ private_class_method :bar, :baz, :qux
920
+
921
+ def initialize
922
+ end
923
+ end
924
+ RUBY
925
+ end
926
+
912
927
  private
913
928
 
914
929
  sig { params(entry: Entry::Method, call_string: String).void }
@@ -90,8 +90,7 @@ module RubyLsp
90
90
  # The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
91
91
  # else is pushed into the incoming queue
92
92
  case method
93
- when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange",
94
- "$/cancelRequest"
93
+ when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
95
94
  process_message(message)
96
95
  when "shutdown"
97
96
  @global_state.synchronize do
@@ -97,33 +97,31 @@ module RubyLsp
97
97
 
98
98
  sig { params(edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
99
99
  def push_edits(edits, version:)
100
- @global_state.synchronize do
101
- edits.each do |edit|
102
- range = edit[:range]
103
- scanner = create_scanner
100
+ edits.each do |edit|
101
+ range = edit[:range]
102
+ scanner = create_scanner
104
103
 
105
- start_position = scanner.find_char_position(range[:start])
106
- end_position = scanner.find_char_position(range[:end])
104
+ start_position = scanner.find_char_position(range[:start])
105
+ end_position = scanner.find_char_position(range[:end])
107
106
 
108
- @source[start_position...end_position] = edit[:text]
109
- end
107
+ @source[start_position...end_position] = edit[:text]
108
+ end
110
109
 
111
- @version = version
112
- @needs_parsing = true
113
- @cache.clear
110
+ @version = version
111
+ @needs_parsing = true
112
+ @cache.clear
114
113
 
115
- last_edit = edits.last
116
- return unless last_edit
114
+ last_edit = edits.last
115
+ return unless last_edit
117
116
 
118
- last_edit_range = last_edit[:range]
117
+ last_edit_range = last_edit[:range]
119
118
 
120
- @last_edit = if last_edit_range[:start] == last_edit_range[:end]
121
- Insert.new(last_edit_range)
122
- elsif last_edit[:text].empty?
123
- Delete.new(last_edit_range)
124
- else
125
- Replace.new(last_edit_range)
126
- end
119
+ @last_edit = if last_edit_range[:start] == last_edit_range[:end]
120
+ Insert.new(last_edit_range)
121
+ elsif last_edit[:text].empty?
122
+ Delete.new(last_edit_range)
123
+ else
124
+ Replace.new(last_edit_range)
127
125
  end
128
126
  end
129
127
 
@@ -330,7 +330,8 @@ module RubyLsp
330
330
 
331
331
  methods.each do |target_method|
332
332
  uri = target_method.uri
333
- next if sorbet_level_true_or_higher?(@sorbet_level) && not_in_dependencies?(T.must(uri.full_path))
333
+ full_path = uri.full_path
334
+ next if sorbet_level_true_or_higher?(@sorbet_level) && (!full_path || not_in_dependencies?(full_path))
334
335
 
335
336
  @response_builder << Interface::LocationLink.new(
336
337
  target_uri: uri.to_s,
@@ -403,7 +404,11 @@ module RubyLsp
403
404
  # additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
404
405
  # ignore
405
406
  uri = entry.uri
406
- next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(T.must(uri.full_path))
407
+ full_path = uri.full_path
408
+
409
+ if @sorbet_level != RubyDocument::SorbetLevel::Ignore && (!full_path || not_in_dependencies?(full_path))
410
+ next
411
+ end
407
412
 
408
413
  @response_builder << Interface::LocationLink.new(
409
414
  target_uri: uri.to_s,
@@ -116,8 +116,8 @@ module RubyLsp
116
116
  T.must(@global_state.index[fully_qualified_name]).each do |entry|
117
117
  # Do not rename files that are not part of the workspace
118
118
  uri = entry.uri
119
- file_path = T.must(uri.full_path)
120
- next unless file_path.start_with?(@global_state.workspace_path)
119
+ file_path = uri.full_path
120
+ next unless file_path&.start_with?(@global_state.workspace_path)
121
121
 
122
122
  case entry
123
123
  when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
@@ -22,10 +22,10 @@ module RubyLsp
22
22
  def perform
23
23
  @index.fuzzy_search(@query).filter_map do |entry|
24
24
  uri = entry.uri
25
- file_path = T.must(uri.full_path)
25
+ file_path = uri.full_path
26
26
 
27
27
  # We only show symbols declared in the workspace
28
- in_dependencies = !not_in_dependencies?(file_path)
28
+ in_dependencies = file_path && !not_in_dependencies?(file_path)
29
29
  next if in_dependencies
30
30
 
31
31
  # We should never show private symbols when searching the entire workspace
@@ -359,48 +359,52 @@ module RubyLsp
359
359
 
360
360
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
361
361
  def text_document_did_open(message)
362
- text_document = message.dig(:params, :textDocument)
363
- language_id = case text_document[:languageId]
364
- when "erb", "eruby"
365
- Document::LanguageId::ERB
366
- when "rbs"
367
- Document::LanguageId::RBS
368
- else
369
- Document::LanguageId::Ruby
370
- end
362
+ @global_state.synchronize do
363
+ text_document = message.dig(:params, :textDocument)
364
+ language_id = case text_document[:languageId]
365
+ when "erb", "eruby"
366
+ Document::LanguageId::ERB
367
+ when "rbs"
368
+ Document::LanguageId::RBS
369
+ else
370
+ Document::LanguageId::Ruby
371
+ end
371
372
 
372
- document = @store.set(
373
- uri: text_document[:uri],
374
- source: text_document[:text],
375
- version: text_document[:version],
376
- language_id: language_id,
377
- )
373
+ document = @store.set(
374
+ uri: text_document[:uri],
375
+ source: text_document[:text],
376
+ version: text_document[:version],
377
+ language_id: language_id,
378
+ )
378
379
 
379
- if document.past_expensive_limit? && text_document[:uri].scheme == "file"
380
- log_message = <<~MESSAGE
381
- The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
382
- diagnostics will be disabled.
383
- MESSAGE
380
+ if document.past_expensive_limit? && text_document[:uri].scheme == "file"
381
+ log_message = <<~MESSAGE
382
+ The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
383
+ diagnostics will be disabled.
384
+ MESSAGE
384
385
 
385
- send_message(
386
- Notification.new(
387
- method: "window/logMessage",
388
- params: Interface::LogMessageParams.new(
389
- type: Constant::MessageType::WARNING,
390
- message: log_message,
386
+ send_message(
387
+ Notification.new(
388
+ method: "window/logMessage",
389
+ params: Interface::LogMessageParams.new(
390
+ type: Constant::MessageType::WARNING,
391
+ message: log_message,
392
+ ),
391
393
  ),
392
- ),
393
- )
394
+ )
395
+ end
394
396
  end
395
397
  end
396
398
 
397
399
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
398
400
  def text_document_did_close(message)
399
- uri = message.dig(:params, :textDocument, :uri)
400
- @store.delete(uri)
401
+ @global_state.synchronize do
402
+ uri = message.dig(:params, :textDocument, :uri)
403
+ @store.delete(uri)
401
404
 
402
- # Clear diagnostics for the closed file, so that they no longer appear in the problems tab
403
- send_message(Notification.publish_diagnostics(uri.to_s, []))
405
+ # Clear diagnostics for the closed file, so that they no longer appear in the problems tab
406
+ send_message(Notification.publish_diagnostics(uri.to_s, []))
407
+ end
404
408
  end
405
409
 
406
410
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
@@ -408,7 +412,9 @@ module RubyLsp
408
412
  params = message[:params]
409
413
  text_document = params[:textDocument]
410
414
 
411
- @store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
415
+ @global_state.synchronize do
416
+ @store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
417
+ end
412
418
  end
413
419
 
414
420
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
@@ -1254,10 +1260,10 @@ module RubyLsp
1254
1260
  return
1255
1261
  end
1256
1262
 
1257
- return unless indexing_options
1258
-
1259
1263
  configuration = @global_state.index.configuration
1260
1264
  configuration.workspace_path = @global_state.workspace_path
1265
+ return unless indexing_options
1266
+
1261
1267
  # The index expects snake case configurations, but VS Code standardizes on camel case settings
1262
1268
  configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
1263
1269
  end
@@ -65,15 +65,13 @@ module RubyLsp
65
65
  ).returns(Document[T.untyped])
66
66
  end
67
67
  def set(uri:, source:, version:, language_id:)
68
- @global_state.synchronize do
69
- @state[uri.to_s] = case language_id
70
- when Document::LanguageId::ERB
71
- ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
72
- when Document::LanguageId::RBS
73
- RBSDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
74
- else
75
- RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
76
- end
68
+ @state[uri.to_s] = case language_id
69
+ when Document::LanguageId::ERB
70
+ ERBDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
71
+ when Document::LanguageId::RBS
72
+ RBSDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
73
+ else
74
+ RubyDocument.new(source: source, version: version, uri: uri, global_state: @global_state)
77
75
  end
78
76
  end
79
77
 
@@ -94,9 +92,7 @@ module RubyLsp
94
92
 
95
93
  sig { params(uri: URI::Generic).void }
96
94
  def delete(uri)
97
- @global_state.synchronize do
98
- @state.delete(uri.to_s)
99
- end
95
+ @state.delete(uri.to_s)
100
96
  end
101
97
 
102
98
  sig { params(uri: URI::Generic).returns(T::Boolean) }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.2
4
+ version: 0.23.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-08 00:00:00.000000000 Z
10
+ date: 2025-01-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: language_server-protocol