ruby-lsp 0.16.2 → 0.16.4

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: a3b197d35d03d1b5b78894bce8b03c6aa76be7586fc7034422415d0656ddea7a
4
- data.tar.gz: 2fd148c0ab5d8b3ac55784722ab49a3be6b1b4885c8b284e8a6cf40c503b4066
3
+ metadata.gz: 94d4dfcda701bdf2ea1dd04e2a271572a139fe3d716e1bd535e2c05bcc9b3d23
4
+ data.tar.gz: b79dfc816bf027e733fa5f44db3e2d1039ae860f9dee88fe996ab1a27089d467
5
5
  SHA512:
6
- metadata.gz: 1994f1d014bc3f70ff96e90d5b3961c279166e5f58be6b38415ec1e2515b7557c6a00f4cde45ba3a66e0d9dd6c1402f7e4754c6f8addb6efb848ec7a65fbadc7
7
- data.tar.gz: ffec2a6c5321eb159ea4d505f9b6c6dd56b64cb16d2f052666b9c15f406d59fa478d9e7f7c9734219874c3ca3bb8dcae9b2cbdd683576a3d74b523cf769298e4
6
+ metadata.gz: 9de1f73feba2284c75294542a0e9ae1196f092004fe83ef815b2e08a1b404daceebc0b3f6f8f59149f936c8f7414821942556bee522776a909c071765dcc3443
7
+ data.tar.gz: b7617a6d508a94e5a06b8d39acb4d2bcc28d02832b19ab8567ed58b6b297aa3fc450dd0a116257fe91fc9545683550456c0d712fc32dc3f3ff716e2cdca2985e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.16.2
1
+ 0.16.4
@@ -59,9 +59,11 @@ module RubyLsp
59
59
  end
60
60
  end
61
61
 
62
- # We need to process shutdown and exit from the main thread in order to close queues and wait for other threads
63
- # to finish. Everything else is pushed into the incoming queue
62
+ # The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
63
+ # else is pushed into the incoming queue
64
64
  case method
65
+ when "initialize", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
66
+ process_message(message)
65
67
  when "shutdown"
66
68
  $stderr.puts("Shutting down Ruby LSP...")
67
69
 
@@ -20,13 +20,13 @@ module RubyLsp
20
20
  sig { returns(URI::Generic) }
21
21
  attr_reader :uri
22
22
 
23
- sig { returns(String) }
23
+ sig { returns(Encoding) }
24
24
  attr_reader :encoding
25
25
 
26
- sig { params(source: String, version: Integer, uri: URI::Generic, encoding: String).void }
27
- def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
26
+ sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
27
+ def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
28
28
  @cache = T.let({}, T::Hash[String, T.untyped])
29
- @encoding = T.let(encoding, String)
29
+ @encoding = T.let(encoding, Encoding)
30
30
  @source = T.let(source, String)
31
31
  @version = T.let(version, Integer)
32
32
  @uri = T.let(uri, URI::Generic)
@@ -187,7 +187,7 @@ module RubyLsp
187
187
  # After character 0xFFFF, UTF-16 considers characters to have length 2 and we have to account for that
188
188
  SURROGATE_PAIR_START = T.let(0xFFFF, Integer)
189
189
 
190
- sig { params(source: String, encoding: String).void }
190
+ sig { params(source: String, encoding: Encoding).void }
191
191
  def initialize(source, encoding)
192
192
  @current_line = T.let(0, Integer)
193
193
  @pos = T.let(0, Integer)
@@ -209,7 +209,7 @@ module RubyLsp
209
209
  # need to adjust for surrogate pairs
210
210
  requested_position = @pos + position[:character]
211
211
 
212
- if @encoding == Constant::PositionEncodingKind::UTF16
212
+ if @encoding == Encoding::UTF_16LE
213
213
  requested_position -= utf_16_character_position_correction(@pos, requested_position)
214
214
  end
215
215
 
@@ -17,15 +17,23 @@ module RubyLsp
17
17
  sig { returns(RubyIndexer::Index) }
18
18
  attr_reader :index
19
19
 
20
+ sig { returns(Encoding) }
21
+ attr_reader :encoding
22
+
23
+ sig { returns(T::Boolean) }
24
+ attr_reader :supports_watching_files
25
+
20
26
  sig { void }
21
27
  def initialize
22
28
  @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
29
+ @encoding = T.let(Encoding::UTF_8, Encoding)
23
30
 
24
31
  @formatter = T.let("auto", String)
25
32
  @test_library = T.let(detect_test_library, String)
26
33
  @typechecker = T.let(detect_typechecker, T::Boolean)
27
34
  @index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
28
35
  @supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
36
+ @supports_watching_files = T.let(false, T::Boolean)
29
37
  end
30
38
 
31
39
  sig { params(identifier: String, instance: Requests::Support::Formatter).void }
@@ -46,6 +54,22 @@ module RubyLsp
46
54
  specified_formatter = options.dig(:initializationOptions, :formatter)
47
55
  @formatter = specified_formatter if specified_formatter
48
56
  @formatter = detect_formatter if @formatter == "auto"
57
+
58
+ encodings = options.dig(:capabilities, :general, :positionEncodings)
59
+ @encoding = if !encodings || encodings.empty?
60
+ Encoding::UTF_16LE
61
+ elsif encodings.include?(Constant::PositionEncodingKind::UTF8)
62
+ Encoding::UTF_8
63
+ elsif encodings.include?(Constant::PositionEncodingKind::UTF16)
64
+ Encoding::UTF_16LE
65
+ else
66
+ Encoding::UTF_32
67
+ end
68
+
69
+ file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
70
+ if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
71
+ @supports_watching_files = true
72
+ end
49
73
  end
50
74
 
51
75
  sig { returns(String) }
@@ -53,6 +77,25 @@ module RubyLsp
53
77
  T.must(@workspace_uri.to_standardized_path)
54
78
  end
55
79
 
80
+ sig { returns(String) }
81
+ def encoding_name
82
+ case @encoding
83
+ when Encoding::UTF_8
84
+ Constant::PositionEncodingKind::UTF8
85
+ when Encoding::UTF_16LE
86
+ Constant::PositionEncodingKind::UTF16
87
+ else
88
+ Constant::PositionEncodingKind::UTF32
89
+ end
90
+ end
91
+
92
+ sig { params(gem_pattern: Regexp).returns(T::Boolean) }
93
+ def direct_dependency?(gem_pattern)
94
+ dependencies.any?(gem_pattern)
95
+ end
96
+
97
+ private
98
+
56
99
  sig { returns(String) }
57
100
  def detect_formatter
58
101
  # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
@@ -70,8 +113,10 @@ module RubyLsp
70
113
  if direct_dependency?(/^rspec/)
71
114
  "rspec"
72
115
  # A Rails app may have a dependency on minitest, but we would instead want to use the Rails test runner provided
73
- # by ruby-lsp-rails.
74
- elsif direct_dependency?(/^rails$/)
116
+ # by ruby-lsp-rails. A Rails app doesn't need to depend on the rails gem itself, individual components like
117
+ # activestorage may be added to the gemfile so that other components aren't downloaded. Check for the presence
118
+ # of bin/rails to support these cases.
119
+ elsif File.exist?(File.join(workspace_path, "bin/rails"))
75
120
  "rails"
76
121
  # NOTE: Intentionally ends with $ to avoid mis-matching minitest-reporters, etc. in a Rails app.
77
122
  elsif direct_dependency?(/^minitest$/)
@@ -83,11 +128,6 @@ module RubyLsp
83
128
  end
84
129
  end
85
130
 
86
- sig { params(gem_pattern: Regexp).returns(T::Boolean) }
87
- def direct_dependency?(gem_pattern)
88
- dependencies.any?(gem_pattern)
89
- end
90
-
91
131
  sig { returns(T::Boolean) }
92
132
  def detect_typechecker
93
133
  return false if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]
@@ -163,7 +163,7 @@ module RubyLsp
163
163
 
164
164
  sig { params(line: String).returns(Integer) }
165
165
  def length_of_line(line)
166
- if @document.encoding == Constant::PositionEncodingKind::UTF16
166
+ if @document.encoding == Encoding::UTF_16LE
167
167
  line_length = 0
168
168
  line.codepoints.each do |codepoint|
169
169
  line_length += 1
@@ -3,15 +3,12 @@
3
3
 
4
4
  return unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
5
5
 
6
- require "singleton"
7
-
8
6
  module RubyLsp
9
7
  module Requests
10
8
  module Support
11
9
  class RuboCopFormatter
12
10
  extend T::Sig
13
11
  include Formatter
14
- include Singleton
15
12
 
16
13
  sig { void }
17
14
  def initialize
@@ -8,15 +8,12 @@ rescue LoadError
8
8
  return
9
9
  end
10
10
 
11
- require "singleton"
12
-
13
11
  module RubyLsp
14
12
  module Requests
15
13
  module Support
16
14
  # :nodoc:
17
15
  class SyntaxTreeFormatter
18
16
  extend T::Sig
19
- include Singleton
20
17
  include Support::Formatter
21
18
 
22
19
  sig { void }
@@ -118,15 +118,6 @@ module RubyLsp
118
118
  client_name = options.dig(:clientInfo, :name)
119
119
  @store.client_name = client_name if client_name
120
120
 
121
- encodings = options.dig(:capabilities, :general, :positionEncodings)
122
- @store.encoding = if encodings.nil? || encodings.empty?
123
- Constant::PositionEncodingKind::UTF16
124
- elsif encodings.include?(Constant::PositionEncodingKind::UTF8)
125
- Constant::PositionEncodingKind::UTF8
126
- else
127
- encodings.first
128
- end
129
-
130
121
  progress = options.dig(:capabilities, :window, :workDoneProgress)
131
122
  @store.supports_progress = progress.nil? ? true : progress
132
123
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
@@ -168,7 +159,7 @@ module RubyLsp
168
159
  change: Constant::TextDocumentSyncKind::INCREMENTAL,
169
160
  open_close: true,
170
161
  ),
171
- position_encoding: @store.encoding,
162
+ position_encoding: @global_state.encoding_name,
172
163
  selection_range_provider: enabled_features["selectionRanges"],
173
164
  hover_provider: hover_provider,
174
165
  document_symbol_provider: document_symbol_provider,
@@ -196,11 +187,8 @@ module RubyLsp
196
187
 
197
188
  send_message(Result.new(id: message[:id], response: response))
198
189
 
199
- # Dynamically registered capabilities
200
- file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
201
-
202
190
  # Not every client supports dynamic registration or file watching
203
- if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
191
+ if global_state.supports_watching_files
204
192
  send_message(
205
193
  Request.new(
206
194
  id: @current_request_id,
@@ -257,10 +245,10 @@ module RubyLsp
257
245
  end
258
246
 
259
247
  if defined?(Requests::Support::RuboCopFormatter)
260
- @global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.instance)
248
+ @global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
261
249
  end
262
250
  if defined?(Requests::Support::SyntaxTreeFormatter)
263
- @global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.instance)
251
+ @global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
264
252
  end
265
253
 
266
254
  perform_initial_indexing(indexing_config)
@@ -269,25 +257,31 @@ module RubyLsp
269
257
 
270
258
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
271
259
  def text_document_did_open(message)
272
- text_document = message.dig(:params, :textDocument)
273
- @store.set(uri: text_document[:uri], source: text_document[:text], version: text_document[:version])
274
- rescue Errno::ENOENT
275
- # If someone re-opens the editor with a file that was deleted or doesn't exist in the current branch, we don't
276
- # want to crash
260
+ @mutex.synchronize do
261
+ text_document = message.dig(:params, :textDocument)
262
+ @store.set(
263
+ uri: text_document[:uri],
264
+ source: text_document[:text],
265
+ version: text_document[:version],
266
+ encoding: @global_state.encoding,
267
+ )
268
+ end
277
269
  end
278
270
 
279
271
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
280
272
  def text_document_did_close(message)
281
- uri = message.dig(:params, :textDocument, :uri)
282
- @store.delete(uri)
273
+ @mutex.synchronize do
274
+ uri = message.dig(:params, :textDocument, :uri)
275
+ @store.delete(uri)
283
276
 
284
- # Clear diagnostics for the closed file, so that they no longer appear in the problems tab
285
- send_message(
286
- Notification.new(
287
- method: "textDocument/publishDiagnostics",
288
- params: Interface::PublishDiagnosticsParams.new(uri: uri.to_s, diagnostics: []),
289
- ),
290
- )
277
+ # Clear diagnostics for the closed file, so that they no longer appear in the problems tab
278
+ send_message(
279
+ Notification.new(
280
+ method: "textDocument/publishDiagnostics",
281
+ params: Interface::PublishDiagnosticsParams.new(uri: uri.to_s, diagnostics: []),
282
+ ),
283
+ )
284
+ end
291
285
  end
292
286
 
293
287
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
@@ -616,6 +610,7 @@ module RubyLsp
616
610
  uri = URI(change[:uri])
617
611
  file_path = uri.to_standardized_path
618
612
  next if file_path.nil? || File.directory?(file_path)
613
+ next unless file_path.end_with?(".rb")
619
614
 
620
615
  load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
621
616
  indexable = RubyIndexer::IndexablePath.new(load_path_entry, file_path)
@@ -19,6 +19,7 @@ module RubyLsp
19
19
  extend T::Sig
20
20
 
21
21
  class BundleNotLocked < StandardError; end
22
+ class BundleInstallFailure < StandardError; end
22
23
 
23
24
  FOUR_HOURS = T.let(4 * 60 * 60, Integer)
24
25
 
@@ -49,6 +50,7 @@ module RubyLsp
49
50
  @last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
50
51
 
51
52
  @dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
53
+ @retry = T.let(false, T::Boolean)
52
54
  end
53
55
 
54
56
  # Sets up the custom bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
@@ -224,7 +226,17 @@ module RubyLsp
224
226
  # Add bundle update
225
227
  $stderr.puts("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
226
228
  $stderr.puts("Ruby LSP> Command: #{command}")
227
- system(env, command)
229
+
230
+ # Try to run the bundle install or update command. If that fails, it normally means that the custom lockfile is in
231
+ # a bad state that no longer reflects the top level one. In that case, we can remove the whole directory, try
232
+ # another time and give up if it fails again
233
+ if !system(env, command) && !@retry && @custom_dir.exist?
234
+ @retry = true
235
+ @custom_dir.rmtree
236
+ $stderr.puts("Ruby LSP> Running bundle install failed. Trying to re-generate the custom bundle from scratch")
237
+ return setup!
238
+ end
239
+
228
240
  [bundle_gemfile.to_s, expanded_path, env["BUNDLE_APP_CONFIG"]]
229
241
  end
230
242
 
@@ -5,9 +5,6 @@ module RubyLsp
5
5
  class Store
6
6
  extend T::Sig
7
7
 
8
- sig { returns(String) }
9
- attr_accessor :encoding
10
-
11
8
  sig { returns(T::Boolean) }
12
9
  attr_accessor :supports_progress
13
10
 
@@ -23,7 +20,6 @@ module RubyLsp
23
20
  sig { void }
24
21
  def initialize
25
22
  @state = T.let({}, T::Hash[String, Document])
26
- @encoding = T.let(Constant::PositionEncodingKind::UTF8, String)
27
23
  @supports_progress = T.let(true, T::Boolean)
28
24
  @experimental_features = T.let(false, T::Boolean)
29
25
  @features_configuration = T.let(
@@ -49,9 +45,9 @@ module RubyLsp
49
45
  T.must(@state[uri.to_s])
50
46
  end
51
47
 
52
- sig { params(uri: URI::Generic, source: String, version: Integer).void }
53
- def set(uri:, source:, version:)
54
- document = RubyDocument.new(source: source, version: version, uri: uri, encoding: @encoding)
48
+ sig { params(uri: URI::Generic, source: String, version: Integer, encoding: Encoding).void }
49
+ def set(uri:, source:, version:, encoding: Encoding::UTF_8)
50
+ document = RubyDocument.new(source: source, version: version, uri: uri, encoding: encoding)
55
51
  @state[uri.to_s] = document
56
52
  end
57
53
 
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.16.2
4
+ version: 0.16.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-05 00:00:00.000000000 Z
11
+ date: 2024-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol