ruby-lsp 0.16.2 → 0.16.4

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