ruby-lsp 0.26.6 → 0.26.8

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: ce859aba34e036ede0a47740d7a1beb65ed4680d58bf95fa17ac85e7c74e8560
4
- data.tar.gz: 49e26820d59a101fb64bbef651741225f4670b892ce7f6317eb7566389e62f8c
3
+ metadata.gz: 54c4bc57589939f89d9db6539395d9036679979455346126c6bb1e1421364a20
4
+ data.tar.gz: debb4a5808a5d0ef964b919fd613743aa452e7ef686fb28c9fd78c1a2d178b21
5
5
  SHA512:
6
- metadata.gz: 7a5dc4ad02c3e8fc462a08fc059e84039b2d63b39e066a46aedfa724837161941a92a8fd198f645818de819a7140e8abe576bc3a770fe355cab4f689ef9d568e
7
- data.tar.gz: 2e7fa7e3d5e409c91fc4e3a05d1c15ea4b3bbabf41e2edd4078acb80e9817e1436b50a0a6656f1c46e7e6eb6d4f19f8b499cb89dc8aa761e7984bbad94cb5263
6
+ metadata.gz: 2de6eb23b63121880662e02ebff976e69023b1fbf57a6c1a34ea620623f68ed9e6628a9e546237e93fd21e2a76050ad3033c08017539b020727e5607304e891c
7
+ data.tar.gz: 1d62a8f58593ad5a04667a6bf4c64ce0111a42de7940b8d6408eaa886055f946760203898e35905d07b163764e4f12cb689264848908e97381b68fbfbc7298b8
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.6
1
+ 0.26.8
data/exe/ruby-lsp CHANGED
@@ -3,7 +3,6 @@
3
3
 
4
4
  require "optparse"
5
5
 
6
- original_args = ARGV.dup
7
6
  options = {}
8
7
  parser = OptionParser.new do |opts|
9
8
  opts.banner = "Usage: ruby-lsp [options]"
@@ -37,6 +36,10 @@ parser = OptionParser.new do |opts|
37
36
  options[:launcher] = true
38
37
  end
39
38
 
39
+ opts.on("--beta", "Use pre-release server gems") do
40
+ options[:beta] = true
41
+ end
42
+
40
43
  opts.on("-h", "--help", "Print this help") do
41
44
  puts opts.help
42
45
  puts
@@ -46,7 +49,7 @@ parser = OptionParser.new do |opts|
46
49
  end
47
50
 
48
51
  begin
49
- parser.parse!
52
+ parser.parse(ARGV)
50
53
  rescue OptionParser::InvalidOption => e
51
54
  warn(e)
52
55
  warn("")
@@ -66,6 +69,8 @@ if ENV["BUNDLE_GEMFILE"].nil?
66
69
  if options[:launcher]
67
70
  flags = []
68
71
  flags << "--debug" if options[:debug]
72
+ flags << "--beta" if options[:beta]
73
+ flags.push("--branch", options[:branch]) if options[:branch]
69
74
  exit exec(Gem.ruby, File.expand_path("ruby-lsp-launcher", __dir__), *flags)
70
75
  end
71
76
 
@@ -85,7 +90,7 @@ if ENV["BUNDLE_GEMFILE"].nil?
85
90
  base_command << " _#{env["BUNDLER_VERSION"]}_"
86
91
  end
87
92
 
88
- exit exec(env, "#{base_command} exec ruby-lsp #{original_args.join(" ")}".strip)
93
+ exit exec(env, "#{base_command} exec ruby-lsp #{ARGV.join(" ")}".strip)
89
94
  end
90
95
 
91
96
  $stdin.sync = true
@@ -20,6 +20,12 @@ reboot = false
20
20
  workspace_uri = ARGV.first
21
21
  raw_initialize_path = File.join(".ruby-lsp", "raw_initialize")
22
22
 
23
+ # Extract CLI options that affect bundle composition (e.g. --branch, --beta) from the arguments passed by the server
24
+ cli_options = {}
25
+ branch_index = ARGV.index("--branch")
26
+ cli_options[:branch] = ARGV[branch_index + 1] if branch_index
27
+ cli_options[:beta] = true if ARGV.include?("--beta")
28
+
23
29
  raw_initialize = if workspace_uri && !workspace_uri.start_with?("--")
24
30
  # If there's an argument without `--`, then it's the server asking to compose the bundle and passing to this
25
31
  # executable the workspace URI. We can't require gems at this point, so we built a fake initialize request manually
@@ -49,16 +55,21 @@ pid = if Gem.win_platform?
49
55
  ["-I", File.expand_path(path)]
50
56
  end
51
57
 
58
+ cli_flags = []
59
+ cli_flags << "--beta" if cli_options[:beta]
60
+ cli_flags.push("--branch", cli_options[:branch]) if cli_options[:branch]
61
+
52
62
  Process.spawn(
53
63
  Gem.ruby,
54
64
  *load_path,
55
65
  File.expand_path("../lib/ruby_lsp/scripts/compose_bundle_windows.rb", __dir__),
56
66
  raw_initialize,
67
+ *cli_flags,
57
68
  )
58
69
  else
59
70
  fork do
60
71
  require_relative "../lib/ruby_lsp/scripts/compose_bundle"
61
- compose(raw_initialize)
72
+ compose(raw_initialize, **cli_options)
62
73
  end
63
74
  end
64
75
 
@@ -101,7 +112,7 @@ begin
101
112
  Bundler.setup
102
113
  $stderr.puts("Composed Bundle set up successfully")
103
114
  end
104
- rescue Bundler::GemNotFound, Bundler::GitError
115
+ rescue Bundler::GemNotFound, Bundler::GitError => e
105
116
  # Sometimes, we successfully set up the bundle, but users either change their Gemfile or uninstall gems from an
106
117
  # external process. If there's no install error, but the gem is still not found, then we need to attempt to start from
107
118
  # scratch
@@ -113,6 +124,10 @@ rescue Bundler::GemNotFound, Bundler::GitError
113
124
  exec(Gem.ruby, __FILE__, *ARGV, "--retry")
114
125
  end
115
126
  end
127
+
128
+ setup_error = e
129
+ $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
130
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
116
131
  rescue StandardError => e
117
132
  setup_error = e
118
133
  $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
@@ -165,18 +165,20 @@ module RubyLsp
165
165
 
166
166
  # Check if the request was cancelled before trying to process it
167
167
  @global_state.synchronize do
168
- if id && @cancelled_requests.include?(id)
168
+ if id && @cancelled_requests.delete(id)
169
169
  send_message(Error.new(
170
170
  id: id,
171
171
  code: Constant::ErrorCodes::REQUEST_CANCELLED,
172
172
  message: "Request #{id} was cancelled",
173
173
  ))
174
- @cancelled_requests.delete(id)
174
+
175
175
  return
176
176
  end
177
177
  end
178
178
 
179
179
  process_message(message)
180
+ # Ensure we remove the request if it got cancelled while it was being processed
181
+ @cancelled_requests.delete(id)
180
182
  end
181
183
 
182
184
  #: ((Result | Error | Notification | Request) message) -> void
@@ -197,7 +199,7 @@ module RubyLsp
197
199
 
198
200
  #: (String message, ?type: Integer) -> void
199
201
  def send_log_message(message, type: Constant::MessageType::LOG)
200
- send_message(Notification.window_log_message(message, type: Constant::MessageType::LOG))
202
+ send_message(Notification.window_log_message(message, type: type))
201
203
  end
202
204
  end
203
205
  end
@@ -94,7 +94,7 @@ module RubyLsp
94
94
 
95
95
  #: (Prism::MatchWriteNode node) -> void
96
96
  def on_match_write_node_leave(node)
97
- @inside_regex_capture = true if node.call.message == "=~"
97
+ @inside_regex_capture = false if node.call.message == "=~"
98
98
  end
99
99
 
100
100
  #: (Prism::DefNode node) -> void
@@ -14,6 +14,12 @@ module RubyLsp
14
14
  fatal: Constant::DiagnosticSeverity::ERROR,
15
15
  }.freeze #: Hash[Symbol, Integer]
16
16
 
17
+ # Cops where adding a `rubocop:disable` inline comment would itself resolve the offense,
18
+ # causing Lint/RedundantCopDisableDirective to flag the disable as unnecessary.
19
+ SELF_RESOLVING_DISABLE_COPS = Set.new([
20
+ "Layout/EmptyComment",
21
+ ]).freeze #: Set[String]
22
+
17
23
  ENHANCED_DOC_URL = begin
18
24
  gem("rubocop", ">= 1.64.0")
19
25
  true
@@ -35,7 +41,7 @@ module RubyLsp
35
41
  code_actions = []
36
42
 
37
43
  code_actions << autocorrect_action if correctable?
38
- code_actions << disable_line_action
44
+ code_actions << disable_line_action unless SELF_RESOLVING_DISABLE_COPS.include?(@offense.cop_name)
39
45
 
40
46
  code_actions
41
47
  end
@@ -1,7 +1,7 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- def compose(raw_initialize)
4
+ def compose(raw_initialize, **options)
5
5
  require_relative "../setup_bundler"
6
6
  require "json"
7
7
  require "uri"
@@ -12,7 +12,7 @@ def compose(raw_initialize)
12
12
  workspace_path = workspace_uri && URI(workspace_uri).to_standardized_path
13
13
  workspace_path ||= Dir.pwd
14
14
 
15
- env = RubyLsp::SetupBundler.new(workspace_path, launcher: true).setup!
15
+ env = RubyLsp::SetupBundler.new(workspace_path, launcher: true, **options).setup!
16
16
 
17
17
  File.open(File.join(".ruby-lsp", "bundle_env"), "w") do |f|
18
18
  f.flock(File::LOCK_EX)
@@ -5,4 +5,8 @@ require_relative "compose_bundle"
5
5
 
6
6
  # When this is invoked on Windows, we pass the raw initialize as an argument to this script. On other platforms, we
7
7
  # invoke the compose method from inside a forked process
8
- compose(ARGV.first)
8
+ options = {}
9
+ options[:beta] = true if ARGV.include?("--beta")
10
+ branch_index = ARGV.index("--branch")
11
+ options[:branch] = ARGV[branch_index + 1] if branch_index
12
+ compose(ARGV.first, **options)
@@ -1436,6 +1436,7 @@ module RubyLsp
1436
1436
  ),
1437
1437
  File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
1438
1438
  @global_state.workspace_uri.to_s,
1439
+ *ARGV,
1439
1440
  chdir: @global_state.workspace_path,
1440
1441
  )
1441
1442
  end
@@ -34,12 +34,14 @@ module RubyLsp
34
34
  # Gems that should be kept up to date in the composed bundle. When updating, any of these gems that are not
35
35
  # already in the user's Gemfile will be updated together.
36
36
  GEMS_TO_UPDATE = ["ruby-lsp", "debug", "prism", "rbs"].freeze #: Array[String]
37
+ RUBY_LSP_MIN_VERSION = "0.18.0" #: String
37
38
 
38
39
  #: (String project_path, **untyped options) -> void
39
40
  def initialize(project_path, **options)
40
41
  @project_path = project_path
41
42
  @branch = options[:branch] #: String?
42
43
  @launcher = options[:launcher] #: bool?
44
+ @beta = options[:beta] #: bool?
43
45
  force_output_to_stderr! if @launcher
44
46
 
45
47
  # Regular bundle paths
@@ -59,7 +61,7 @@ module RubyLsp
59
61
  @custom_dir = Pathname.new(".ruby-lsp").expand_path(@project_path) #: Pathname
60
62
  @custom_gemfile = @custom_dir + @gemfile_name #: Pathname
61
63
  @custom_lockfile = @custom_dir + (@lockfile&.basename || "Gemfile.lock") #: Pathname
62
- @lockfile_hash_path = @custom_dir + "main_lockfile_hash" #: Pathname
64
+ @freshness_hash_path = @custom_dir + "freshness_hash" #: Pathname
63
65
  @last_updated_path = @custom_dir + "last_updated" #: Pathname
64
66
  @error_path = @custom_dir + "install_error" #: Pathname
65
67
  @already_composed_path = @custom_dir + "bundle_is_composed" #: Pathname
@@ -118,17 +120,24 @@ module RubyLsp
118
120
  return run_bundle_install(@custom_gemfile)
119
121
  end
120
122
 
121
- if @lockfile_hash && @custom_lockfile.exist? && @lockfile_hash_path.exist? &&
122
- @lockfile_hash_path.read == @lockfile_hash
123
+ # Our freshness hash determines if we need to copy the lockfile from the main app again and run bundle install
124
+ # from scratch. We use a combination of the main app's lockfile and the composed Gemfile. The goal is to
125
+ # automatically account for CLI arguments which can change the Gemfile we compose. If the CLI arguments or the
126
+ # main lockfile change, we need to make sure we're re-composing.
127
+ freshness_digest = Digest::SHA256.hexdigest("#{@lockfile_hash}#{@custom_gemfile.read}")
128
+
129
+ if @lockfile_hash && @custom_lockfile.exist? && @freshness_hash_path.exist? &&
130
+ @freshness_hash_path.read == freshness_digest
123
131
  $stderr.puts(
124
132
  "Ruby LSP> Skipping composed bundle setup since #{@custom_lockfile} already exists and is up to date",
125
133
  )
126
134
  return run_bundle_install(@custom_gemfile)
127
135
  end
128
136
 
137
+ @needs_update_path.delete if @needs_update_path.exist?
129
138
  FileUtils.cp(@lockfile.to_s, @custom_lockfile.to_s)
130
139
  correct_relative_remote_paths
131
- @lockfile_hash_path.write(@lockfile_hash)
140
+ @freshness_hash_path.write(freshness_digest)
132
141
  run_bundle_install(@custom_gemfile)
133
142
  end
134
143
 
@@ -168,7 +177,8 @@ module RubyLsp
168
177
  end
169
178
 
170
179
  unless @dependencies["ruby-lsp"]
171
- ruby_lsp_entry = +'gem "ruby-lsp", require: false, group: :development'
180
+ version = @beta ? "0.a" : RUBY_LSP_MIN_VERSION
181
+ ruby_lsp_entry = +"gem \"ruby-lsp\", \">= #{version}\", require: false, group: :development"
172
182
  ruby_lsp_entry << ", github: \"Shopify/ruby-lsp\", branch: \"#{@branch}\"" if @branch
173
183
  parts << ruby_lsp_entry
174
184
  end
@@ -238,7 +248,7 @@ module RubyLsp
238
248
  # If no error occurred, then clear previous errors
239
249
  @error_path.delete if @error_path.exist?
240
250
  $stderr.puts("Ruby LSP> Composed bundle installation complete")
241
- rescue Errno::EPIPE, Bundler::HTTPError
251
+ rescue Errno::EPIPE, Bundler::HTTPError, Bundler::InstallError
242
252
  # There are cases where we expect certain errors to happen occasionally, and we don't want to write them to
243
253
  # a file, which would report to telemetry on the next launch.
244
254
  #
@@ -246,6 +256,7 @@ module RubyLsp
246
256
  # install. This situation may happen because, while running bundle install, the server is not yet ready to
247
257
  # receive shutdown requests and we may continue doing work until the process is killed.
248
258
  # - Bundler might also encounter a network error.
259
+ # - Native extension build failures (InstallError) are user environment issues that Ruby LSP cannot resolve.
249
260
  @error_path.delete if @error_path.exist?
250
261
  rescue => e
251
262
  # Write the error object to a file so that we can read it from the parent process
@@ -340,7 +351,7 @@ module RubyLsp
340
351
 
341
352
  Bundler::CLI::Update.new({ conservative: true }, gems).run
342
353
  correct_relative_remote_paths if @custom_lockfile.exist?
343
- @needs_update_path.delete
354
+ @needs_update_path.delete if @needs_update_path.exist?
344
355
  @last_updated_path.write(Time.now.iso8601)
345
356
  env
346
357
  end
@@ -4,13 +4,40 @@
4
4
  require "English"
5
5
  require "json"
6
6
  require "socket"
7
- require "singleton"
8
7
  require "tmpdir"
9
8
  require_relative "../../ruby_indexer/lib/ruby_indexer/uri"
10
9
 
11
10
  module RubyLsp
12
11
  class LspReporter
13
- include Singleton
12
+ @instance = nil #: LspReporter?
13
+
14
+ class << self
15
+ #: -> LspReporter
16
+ def instance
17
+ @instance ||= new
18
+ end
19
+
20
+ #: -> bool
21
+ def start_coverage?
22
+ ENV["RUBY_LSP_TEST_RUNNER"] == "coverage"
23
+ end
24
+
25
+ #: -> bool
26
+ def executed_under_test_runner?
27
+ !!(ENV["RUBY_LSP_TEST_RUNNER"] && ENV["RUBY_LSP_ENV"] != "test")
28
+ end
29
+
30
+ #: (Method | UnboundMethod) -> [URI::Generic, Integer?]?
31
+ def uri_and_line_for(method_object)
32
+ file_path, line = method_object.source_location
33
+ return unless file_path
34
+ return if file_path.start_with?("(eval at ")
35
+
36
+ uri = URI::Generic.from_path(path: File.expand_path(file_path))
37
+ zero_based_line = line ? line - 1 : nil
38
+ [uri, zero_based_line]
39
+ end
40
+ end
14
41
 
15
42
  # https://code.visualstudio.com/api/references/vscode-api#Position
16
43
  #: type position = { line: Integer, character: Integer }
@@ -50,6 +77,8 @@ module RubyLsp
50
77
  end #: IO | StringIO
51
78
 
52
79
  @invoked_shutdown = false #: bool
80
+ @message_queue = Thread::Queue.new #: Thread::Queue
81
+ @writer = Thread.new { write_loop } #: Thread
53
82
  end
54
83
 
55
84
  #: -> void
@@ -68,6 +97,8 @@ module RubyLsp
68
97
  @invoked_shutdown = true
69
98
 
70
99
  send_message("finish")
100
+ @message_queue.close
101
+ @writer.join
71
102
  @io.close
72
103
  end
73
104
 
@@ -96,17 +127,6 @@ module RubyLsp
96
127
  send_message("error", id: id, message: message, uri: uri.to_s)
97
128
  end
98
129
 
99
- #: (Method | UnboundMethod) -> [URI::Generic, Integer?]?
100
- def uri_and_line_for(method_object)
101
- file_path, line = method_object.source_location
102
- return unless file_path
103
- return if file_path.start_with?("(eval at ")
104
-
105
- uri = URI::Generic.from_path(path: File.expand_path(file_path))
106
- zero_based_line = line ? line - 1 : nil
107
- [uri, zero_based_line]
108
- end
109
-
110
130
  # Gather the results returned by Coverage.result and format like the VS Code test explorer expects
111
131
  #
112
132
  # Coverage result format:
@@ -195,23 +215,11 @@ module RubyLsp
195
215
  internal_shutdown unless @invoked_shutdown
196
216
  end
197
217
 
198
- class << self
199
- #: -> bool
200
- def start_coverage?
201
- ENV["RUBY_LSP_TEST_RUNNER"] == "coverage"
202
- end
203
-
204
- #: -> bool
205
- def executed_under_test_runner?
206
- !!(ENV["RUBY_LSP_TEST_RUNNER"] && ENV["RUBY_LSP_ENV"] != "test")
207
- end
208
- end
209
-
210
218
  private
211
219
 
212
- #: (String) -> TCPSocket
220
+ #: (String) -> Socket
213
221
  def socket(port)
214
- socket = TCPSocket.new("localhost", port)
222
+ socket = Socket.tcp("localhost", port)
215
223
  socket.binmode
216
224
  socket.sync = true
217
225
  socket
@@ -220,7 +228,14 @@ module RubyLsp
220
228
  #: (String?, **untyped) -> void
221
229
  def send_message(method_name, **params)
222
230
  json_message = { method: method_name, params: params }.to_json
223
- @io.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
231
+ @message_queue << "Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}"
232
+ end
233
+
234
+ #: -> void
235
+ def write_loop
236
+ while (message = @message_queue.pop)
237
+ @io.write(message)
238
+ end
224
239
  end
225
240
  end
226
241
  end
@@ -89,7 +89,7 @@ module RubyLsp
89
89
  test_class_or_wrapper
90
90
  end
91
91
 
92
- uri, line = LspReporter.instance.uri_and_line_for(klass.instance_method(method_name))
92
+ uri, line = LspReporter.uri_and_line_for(klass.instance_method(method_name))
93
93
  return unless uri
94
94
 
95
95
  id = "#{name}##{handle_spec_test_id(method_name, line)}"
@@ -25,7 +25,7 @@ module RubyLsp
25
25
  def test_started(test)
26
26
  super
27
27
 
28
- uri, line = LspReporter.instance.uri_and_line_for(test.method(test.method_name))
28
+ uri, line = LspReporter.uri_and_line_for(test.method(test.method_name))
29
29
  return unless uri
30
30
 
31
31
  @current_uri = uri
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.6
4
+ version: 0.26.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify