ruby-lsp 0.24.2 → 0.26.0
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 +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +18 -9
- data/exe/ruby-lsp-test-exec +3 -15
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +4 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +9 -0
- data/lib/ruby_indexer/test/configuration_test.rb +1 -2
- data/lib/ruby_indexer/test/index_test.rb +12 -0
- data/lib/ruby_lsp/addon.rb +32 -9
- data/lib/ruby_lsp/base_server.rb +31 -21
- data/lib/ruby_lsp/document.rb +17 -14
- data/lib/ruby_lsp/global_state.rb +21 -0
- data/lib/ruby_lsp/internal.rb +0 -2
- data/lib/ruby_lsp/listeners/completion.rb +5 -2
- data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
- data/lib/ruby_lsp/listeners/test_style.rb +7 -5
- data/lib/ruby_lsp/requests/code_lens.rb +9 -3
- data/lib/ruby_lsp/requests/inlay_hints.rb +3 -3
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +3 -1
- data/lib/ruby_lsp/requests/support/formatter.rb +9 -3
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +8 -2
- data/lib/ruby_lsp/response_builders/response_builder.rb +3 -3
- data/lib/ruby_lsp/server.rb +83 -81
- data/lib/ruby_lsp/setup_bundler.rb +56 -22
- data/lib/ruby_lsp/store.rb +0 -10
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +13 -4
- data/lib/ruby_lsp/utils.rb +44 -5
- metadata +1 -16
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e8b51ca9a97aa0e855fbf1109f07733aae195eb25dba844f79840665b36ac9a
|
4
|
+
data.tar.gz: 79d5c3af7ba9fb03c2da197b4213a861e366b7be5a60e677ac6ca9ae8269dbea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cb30f7427f02460320becb1b883c502ea0fbfd48f5148192e1339350e94836ed6ba825df4b4b0fe0ff8d32325d6196af6a26ef94d566e4e32ecd235d66ed821
|
7
|
+
data.tar.gz: 68d5712e3ae16ecaa602eeccadcab5108d89cce1f1a282b82bd3244b3111131442ee6feda63b1e7049027624c07be25e5f3ae39a00b9df25130f12f43c2dac80
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.26.0
|
data/exe/ruby-lsp
CHANGED
@@ -88,13 +88,17 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
88
88
|
exit exec(env, "#{base_command} exec ruby-lsp #{original_args.join(" ")}".strip)
|
89
89
|
end
|
90
90
|
|
91
|
+
$stdin.sync = true
|
92
|
+
$stdout.sync = true
|
93
|
+
$stderr.sync = true
|
94
|
+
$stdin.binmode
|
95
|
+
$stdout.binmode
|
96
|
+
$stderr.binmode
|
97
|
+
|
91
98
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
92
99
|
|
93
|
-
require "ruby_lsp/load_sorbet"
|
94
100
|
require "ruby_lsp/internal"
|
95
101
|
|
96
|
-
T::Utils.run_all_sig_blocks
|
97
|
-
|
98
102
|
if options[:debug]
|
99
103
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
100
104
|
$stderr.puts "Debugging is not supported on Windows"
|
@@ -147,8 +151,10 @@ if options[:doctor]
|
|
147
151
|
return
|
148
152
|
end
|
149
153
|
|
154
|
+
server = RubyLsp::Server.new
|
155
|
+
|
150
156
|
# Ensure all output goes out stderr by default to allow puts/p/pp to work
|
151
157
|
# without specifying output device.
|
152
158
|
$> = $stderr
|
153
159
|
|
154
|
-
|
160
|
+
server.start
|
data/exe/ruby-lsp-check
CHANGED
@@ -3,13 +3,9 @@
|
|
3
3
|
|
4
4
|
# This executable checks if all automatic LSP requests run successfully on every Ruby file under the current directory
|
5
5
|
|
6
|
-
require "ruby_lsp/load_sorbet"
|
7
|
-
|
8
6
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
9
7
|
require "ruby_lsp/internal"
|
10
8
|
|
11
|
-
T::Utils.run_all_sig_blocks
|
12
|
-
|
13
9
|
files = Dir.glob("#{Dir.pwd}/**/*.rb")
|
14
10
|
|
15
11
|
puts "Verifying that all automatic LSP requests execute successfully. This may take a while..."
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -6,6 +6,13 @@
|
|
6
6
|
# composed bundle
|
7
7
|
# !!!!!!!
|
8
8
|
|
9
|
+
$stdin.sync = true
|
10
|
+
$stdout.sync = true
|
11
|
+
$stderr.sync = true
|
12
|
+
$stdin.binmode
|
13
|
+
$stdout.binmode
|
14
|
+
$stderr.binmode
|
15
|
+
|
9
16
|
setup_error = nil
|
10
17
|
install_error = nil
|
11
18
|
reboot = false
|
@@ -28,7 +35,6 @@ else
|
|
28
35
|
# Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
|
29
36
|
# Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
|
30
37
|
# to ensure that we're setting up the bundle in the right place
|
31
|
-
$stdin.binmode
|
32
38
|
headers = $stdin.gets("\r\n\r\n")
|
33
39
|
content_length = headers[/Content-Length: (\d+)/i, 1].to_i
|
34
40
|
$stdin.read(content_length)
|
@@ -125,11 +131,8 @@ end
|
|
125
131
|
# Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
|
126
132
|
# configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
|
127
133
|
# paths into the load path manually or we may end up requiring the wrong version of the gem
|
128
|
-
require "ruby_lsp/load_sorbet"
|
129
134
|
require "ruby_lsp/internal"
|
130
135
|
|
131
|
-
T::Utils.run_all_sig_blocks
|
132
|
-
|
133
136
|
if ARGV.include?("--debug")
|
134
137
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
135
138
|
$stderr.puts "Debugging is not supported on Windows"
|
@@ -143,22 +146,28 @@ if ARGV.include?("--debug")
|
|
143
146
|
end
|
144
147
|
end
|
145
148
|
|
146
|
-
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
|
147
|
-
$> = $stderr
|
148
|
-
|
149
149
|
initialize_request = JSON.parse(raw_initialize, symbolize_names: true) if raw_initialize
|
150
150
|
|
151
151
|
begin
|
152
|
-
RubyLsp::Server.new(
|
152
|
+
server = RubyLsp::Server.new(
|
153
153
|
install_error: install_error,
|
154
154
|
setup_error: setup_error,
|
155
155
|
initialize_request: initialize_request,
|
156
|
-
)
|
156
|
+
)
|
157
|
+
|
158
|
+
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
|
159
|
+
$> = $stderr
|
160
|
+
|
161
|
+
server.start
|
157
162
|
rescue ArgumentError
|
158
163
|
# If the launcher is booting an outdated version of the server, then the initializer doesn't accept a keyword splat
|
159
164
|
# and we already read the initialize request from the stdin pipe. In this case, we need to process the initialize
|
160
165
|
# request manually and then start the main loop
|
161
166
|
server = RubyLsp::Server.new
|
167
|
+
|
168
|
+
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
|
169
|
+
$> = $stderr
|
170
|
+
|
162
171
|
server.process_message(initialize_request)
|
163
172
|
server.start
|
164
173
|
end
|
data/exe/ruby-lsp-test-exec
CHANGED
@@ -1,18 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
*ENV["RUBYOPT"],
|
8
|
-
"-rbundler/setup",
|
9
|
-
"-r#{File.expand_path("../lib/ruby_lsp/test_reporters/minitest_reporter", __dir__)}",
|
10
|
-
"-r#{File.expand_path("../lib/ruby_lsp/test_reporters/test_unit_reporter", __dir__)}",
|
11
|
-
].join(" ")
|
12
|
-
|
13
|
-
# Replace this process with whatever command was passed. We only want to set RUBYOPT.
|
14
|
-
# The way you use this executable is by prefixing your test command with `ruby-lsp-test-exec`, like so:
|
15
|
-
# ruby-lsp-test-exec bundle exec ruby -Itest test/example_test.rb
|
16
|
-
# ruby-lsp-test-exec bundle exec ruby -Ispec spec/example_spec.rb
|
17
|
-
# ruby-lsp-test-exec bundle exec rspec spec/example_spec.rb
|
18
|
-
exec({ "RUBYOPT" => rubyopt }, *ARGV)
|
4
|
+
# This executable will be removed thanks to the changes in https://github.com/Shopify/ruby-lsp/pull/3661.
|
5
|
+
# Remove this a few months after extension updates have rolled out
|
6
|
+
exec(*ARGV)
|
@@ -294,7 +294,9 @@ module RubyIndexer
|
|
294
294
|
|
295
295
|
# @abstract
|
296
296
|
#: -> Array[Signature]
|
297
|
-
def signatures
|
297
|
+
def signatures
|
298
|
+
raise AbstractMethodInvokedError
|
299
|
+
end
|
298
300
|
|
299
301
|
#: -> String
|
300
302
|
def decorated_parameters
|
@@ -331,6 +333,7 @@ module RubyIndexer
|
|
331
333
|
end
|
332
334
|
|
333
335
|
class Method < Member
|
336
|
+
# @override
|
334
337
|
#: Array[Signature]
|
335
338
|
attr_reader :signatures
|
336
339
|
|
@@ -291,6 +291,15 @@ module RubyIndexer
|
|
291
291
|
|
292
292
|
# Top level constants
|
293
293
|
entries.concat(@entries_tree.search(name))
|
294
|
+
|
295
|
+
# Filter only constants since methods may have names that look like constants
|
296
|
+
entries.each do |definitions|
|
297
|
+
definitions.select! do |entry|
|
298
|
+
entry.is_a?(Entry::Constant) || entry.is_a?(Entry::ConstantAlias) ||
|
299
|
+
entry.is_a?(Entry::Namespace) || entry.is_a?(Entry::UnresolvedConstantAlias)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
294
303
|
entries.uniq!
|
295
304
|
entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
296
305
|
end
|
@@ -20,7 +20,7 @@ module RubyIndexer
|
|
20
20
|
assert(uris.none? { |uri| uri.full_path.include?("test/fixtures") })
|
21
21
|
assert(uris.none? { |uri| uri.full_path.include?(bundle_path.join("minitest-reporters").to_s) })
|
22
22
|
assert(uris.none? { |uri| uri.full_path.include?(bundle_path.join("ansi").to_s) })
|
23
|
-
assert(uris.any? { |uri| uri.full_path.include?(bundle_path.join("
|
23
|
+
assert(uris.any? { |uri| uri.full_path.include?(bundle_path.join("prism").to_s) })
|
24
24
|
assert(uris.none? { |uri| uri.full_path == __FILE__ })
|
25
25
|
end
|
26
26
|
|
@@ -59,7 +59,6 @@ module RubyIndexer
|
|
59
59
|
|
60
60
|
assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
|
61
61
|
assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
|
62
|
-
assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb")
|
63
62
|
end
|
64
63
|
|
65
64
|
def test_indexable_uris_includes_project_files
|
@@ -1983,6 +1983,18 @@ module RubyIndexer
|
|
1983
1983
|
assert_equal(["XQRK"], result.map { |entries| entries.first&.name })
|
1984
1984
|
end
|
1985
1985
|
|
1986
|
+
def test_constant_completion_does_not_confuse_uppercase_methods
|
1987
|
+
index(<<~RUBY)
|
1988
|
+
class Foo
|
1989
|
+
def Qux
|
1990
|
+
end
|
1991
|
+
end
|
1992
|
+
RUBY
|
1993
|
+
|
1994
|
+
candidates = @index.constant_completion_candidates("Q", [])
|
1995
|
+
refute_includes(candidates.flat_map { |entries| entries.map(&:name) }, "Qux")
|
1996
|
+
end
|
1997
|
+
|
1986
1998
|
def test_constant_completion_candidates_for_empty_name
|
1987
1999
|
index(<<~RUBY)
|
1988
2000
|
module Foo
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -57,11 +57,26 @@ module RubyLsp
|
|
57
57
|
|
58
58
|
if include_project_addons
|
59
59
|
project_addons = Dir.glob("#{global_state.workspace_path}/**/ruby_lsp/**/addon.rb")
|
60
|
-
|
61
|
-
# Ignore add-ons from dependencies if the bundle is stored inside the project. We already found those with
|
62
|
-
# `Gem.find_files`
|
63
60
|
bundle_path = Bundler.bundle_path.to_s
|
64
|
-
|
61
|
+
gems_dir = Bundler.bundle_path.join("gems")
|
62
|
+
|
63
|
+
# Create an array of rejection glob patterns to ignore add-ons already discovered through Gem.find_files if
|
64
|
+
# they are also copied inside the workspace for whatever reason. We received reports of projects having gems
|
65
|
+
# installed in vendor/bundle despite BUNDLE_PATH pointing elsewhere. Without this mechanism, we will
|
66
|
+
# double-require the same add-on, potentially for different versions of the same gem, which leads to incorrect
|
67
|
+
# behavior
|
68
|
+
reject_glob_patterns = addon_files.map do |path|
|
69
|
+
relative_gem_path = Pathname.new(path).relative_path_from(gems_dir)
|
70
|
+
first_part, *parts = relative_gem_path.to_s.split(File::SEPARATOR)
|
71
|
+
first_part&.gsub!(/-([0-9.]+)$/, "*")
|
72
|
+
"**/#{first_part}/#{parts.join("/")}"
|
73
|
+
end
|
74
|
+
|
75
|
+
project_addons.reject! do |path|
|
76
|
+
path.start_with?(bundle_path) ||
|
77
|
+
reject_glob_patterns.any? { |pattern| File.fnmatch?(pattern, path, File::Constants::FNM_PATHNAME) }
|
78
|
+
end
|
79
|
+
|
65
80
|
addon_files.concat(project_addons)
|
66
81
|
end
|
67
82
|
|
@@ -182,24 +197,32 @@ module RubyLsp
|
|
182
197
|
# reading information into memory or even spawning a separate process
|
183
198
|
# @abstract
|
184
199
|
#: (GlobalState, Thread::Queue) -> void
|
185
|
-
def activate(global_state, outgoing_queue)
|
200
|
+
def activate(global_state, outgoing_queue)
|
201
|
+
raise AbstractMethodInvokedError
|
202
|
+
end
|
186
203
|
|
187
|
-
# Each add-on
|
204
|
+
# Each add-on must implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
|
188
205
|
# child process
|
189
206
|
# @abstract
|
190
207
|
#: -> void
|
191
|
-
def deactivate
|
208
|
+
def deactivate
|
209
|
+
raise AbstractMethodInvokedError
|
210
|
+
end
|
192
211
|
|
193
212
|
# Add-ons should override the `name` method to return the add-on name
|
194
213
|
# @abstract
|
195
214
|
#: -> String
|
196
|
-
def name
|
215
|
+
def name
|
216
|
+
raise AbstractMethodInvokedError
|
217
|
+
end
|
197
218
|
|
198
219
|
# Add-ons should override the `version` method to return a semantic version string representing the add-on's
|
199
220
|
# version. This is used for compatibility checks
|
200
221
|
# @abstract
|
201
222
|
#: -> String
|
202
|
-
def version
|
223
|
+
def version
|
224
|
+
raise AbstractMethodInvokedError
|
225
|
+
end
|
203
226
|
|
204
227
|
# Handle a response from a window/showMessageRequest request. Add-ons must include the addon_name as part of the
|
205
228
|
# original request so that the response is delegated to the correct add-on and must override this method to handle
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -6,11 +6,11 @@ module RubyLsp
|
|
6
6
|
class BaseServer
|
7
7
|
#: (**untyped options) -> void
|
8
8
|
def initialize(**options)
|
9
|
+
@reader = MessageReader.new(options[:reader] || $stdin) #: MessageReader
|
10
|
+
@writer = MessageWriter.new(options[:writer] || $stdout) #: MessageWriter
|
9
11
|
@test_mode = options[:test_mode] #: bool?
|
10
12
|
@setup_error = options[:setup_error] #: StandardError?
|
11
13
|
@install_error = options[:install_error] #: StandardError?
|
12
|
-
@writer = Transport::Stdio::Writer.new #: Transport::Stdio::Writer
|
13
|
-
@reader = Transport::Stdio::Reader.new #: Transport::Stdio::Reader
|
14
14
|
@incoming_queue = Thread::Queue.new #: Thread::Queue
|
15
15
|
@outgoing_queue = Thread::Queue.new #: Thread::Queue
|
16
16
|
@cancelled_requests = [] #: Array[Integer]
|
@@ -36,7 +36,7 @@ module RubyLsp
|
|
36
36
|
|
37
37
|
#: -> void
|
38
38
|
def start
|
39
|
-
@reader.
|
39
|
+
@reader.each_message do |message|
|
40
40
|
method = message[:method]
|
41
41
|
|
42
42
|
# We must parse the document under a mutex lock or else we might switch threads and accept text edits in the
|
@@ -83,8 +83,7 @@ module RubyLsp
|
|
83
83
|
# The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
|
84
84
|
# else is pushed into the incoming queue
|
85
85
|
case method
|
86
|
-
when "initialize", "initialized", "
|
87
|
-
"rubyLsp/diagnoseState"
|
86
|
+
when "initialize", "initialized", "rubyLsp/diagnoseState"
|
88
87
|
process_message(message)
|
89
88
|
when "shutdown"
|
90
89
|
@global_state.synchronize do
|
@@ -94,26 +93,13 @@ module RubyLsp
|
|
94
93
|
@writer.write(Result.new(id: message[:id], response: nil).to_hash)
|
95
94
|
end
|
96
95
|
when "exit"
|
97
|
-
|
96
|
+
exit(@incoming_queue.closed? ? 0 : 1)
|
98
97
|
else
|
99
98
|
@incoming_queue << message
|
100
99
|
end
|
101
100
|
end
|
102
101
|
end
|
103
102
|
|
104
|
-
#: -> void
|
105
|
-
def run_shutdown
|
106
|
-
@incoming_queue.clear
|
107
|
-
@outgoing_queue.clear
|
108
|
-
@incoming_queue.close
|
109
|
-
@outgoing_queue.close
|
110
|
-
@cancelled_requests.clear
|
111
|
-
|
112
|
-
@worker.terminate
|
113
|
-
@outgoing_dispatcher.terminate
|
114
|
-
@store.clear
|
115
|
-
end
|
116
|
-
|
117
103
|
# This method is only intended to be used in tests! Pops the latest response that would be sent to the client
|
118
104
|
#: -> untyped
|
119
105
|
def pop_response
|
@@ -128,11 +114,35 @@ module RubyLsp
|
|
128
114
|
|
129
115
|
# @abstract
|
130
116
|
#: (Hash[Symbol, untyped] message) -> void
|
131
|
-
def process_message(message)
|
117
|
+
def process_message(message)
|
118
|
+
raise AbstractMethodInvokedError
|
119
|
+
end
|
120
|
+
|
121
|
+
#: -> bool?
|
122
|
+
def test_mode?
|
123
|
+
@test_mode
|
124
|
+
end
|
125
|
+
|
126
|
+
#: -> void
|
127
|
+
def run_shutdown
|
128
|
+
@incoming_queue.clear
|
129
|
+
@outgoing_queue.clear
|
130
|
+
@incoming_queue.close
|
131
|
+
@outgoing_queue.close
|
132
|
+
@cancelled_requests.clear
|
133
|
+
|
134
|
+
@worker.terminate
|
135
|
+
@outgoing_dispatcher.terminate
|
136
|
+
@store.clear
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
132
140
|
|
133
141
|
# @abstract
|
134
142
|
#: -> void
|
135
|
-
def shutdown
|
143
|
+
def shutdown
|
144
|
+
raise AbstractMethodInvokedError
|
145
|
+
end
|
136
146
|
|
137
147
|
#: (Integer id, String message, ?type: Integer) -> void
|
138
148
|
def fail_request_and_notify(id, message, type: Constant::MessageType::INFO)
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -5,9 +5,8 @@ module RubyLsp
|
|
5
5
|
# @abstract
|
6
6
|
#: [ParseResultType]
|
7
7
|
class Document
|
8
|
-
extend T::Generic
|
9
|
-
|
10
8
|
class InvalidLocationError < StandardError; end
|
9
|
+
|
11
10
|
# This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
|
12
11
|
# This is the same number used by the TypeScript extension in VS Code
|
13
12
|
MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES = 100_000
|
@@ -61,7 +60,9 @@ module RubyLsp
|
|
61
60
|
|
62
61
|
# @abstract
|
63
62
|
#: -> Symbol
|
64
|
-
def language_id
|
63
|
+
def language_id
|
64
|
+
raise AbstractMethodInvokedError
|
65
|
+
end
|
65
66
|
|
66
67
|
#: [T] (String request_name) { (Document[ParseResultType] document) -> T } -> T
|
67
68
|
def cache_fetch(request_name, &block)
|
@@ -121,11 +122,15 @@ module RubyLsp
|
|
121
122
|
# Returns `true` if the document was parsed and `false` if nothing needed parsing
|
122
123
|
# @abstract
|
123
124
|
#: -> bool
|
124
|
-
def parse
|
125
|
+
def parse!
|
126
|
+
raise AbstractMethodInvokedError
|
127
|
+
end
|
125
128
|
|
126
129
|
# @abstract
|
127
130
|
#: -> bool
|
128
|
-
def syntax_error
|
131
|
+
def syntax_error?
|
132
|
+
raise AbstractMethodInvokedError
|
133
|
+
end
|
129
134
|
|
130
135
|
#: -> bool
|
131
136
|
def past_expensive_limit?
|
@@ -134,12 +139,10 @@ module RubyLsp
|
|
134
139
|
|
135
140
|
#: (Hash[Symbol, untyped] start_pos, ?Hash[Symbol, untyped]? end_pos) -> [Integer, Integer?]
|
136
141
|
def find_index_by_position(start_pos, end_pos = nil)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
[start_index, end_index]
|
142
|
-
end
|
142
|
+
scanner = create_scanner
|
143
|
+
start_index = scanner.find_char_position(start_pos)
|
144
|
+
end_index = scanner.find_char_position(end_pos) if end_pos
|
145
|
+
[start_index, end_index]
|
143
146
|
end
|
144
147
|
|
145
148
|
private
|
@@ -177,8 +180,6 @@ module RubyLsp
|
|
177
180
|
# See https://microsoft.github.io/language-server-protocol/specification/#positionEncodingKind for more information
|
178
181
|
# @abstract
|
179
182
|
class Scanner
|
180
|
-
extend T::Sig
|
181
|
-
|
182
183
|
LINE_BREAK = 0x0A #: Integer
|
183
184
|
# After character 0xFFFF, UTF-16 considers characters to have length 2 and we have to account for that
|
184
185
|
SURROGATE_PAIR_START = 0xFFFF #: Integer
|
@@ -193,7 +194,9 @@ module RubyLsp
|
|
193
194
|
# character index regardless of whether we are searching positions based on bytes, code units, or codepoints.
|
194
195
|
# @abstract
|
195
196
|
#: (Hash[Symbol, untyped] position) -> Integer
|
196
|
-
def find_char_position(position)
|
197
|
+
def find_char_position(position)
|
198
|
+
raise AbstractMethodInvokedError
|
199
|
+
end
|
197
200
|
end
|
198
201
|
|
199
202
|
# For the UTF-8 encoding, positions correspond to bytes
|
@@ -56,6 +56,17 @@ module RubyLsp
|
|
56
56
|
@enabled_feature_flags = {} #: Hash[Symbol, bool]
|
57
57
|
@mutex = Mutex.new #: Mutex
|
58
58
|
@telemetry_machine_id = nil #: String?
|
59
|
+
@feature_configuration = {
|
60
|
+
inlayHint: RequestConfig.new({
|
61
|
+
enableAll: false,
|
62
|
+
implicitRescue: false,
|
63
|
+
implicitHashValue: false,
|
64
|
+
}),
|
65
|
+
codeLens: RequestConfig.new({
|
66
|
+
enableAll: false,
|
67
|
+
enableTestCodeLens: true,
|
68
|
+
}),
|
69
|
+
} #: Hash[Symbol, RequestConfig]
|
59
70
|
end
|
60
71
|
|
61
72
|
#: [T] { -> T } -> T
|
@@ -175,9 +186,19 @@ module RubyLsp
|
|
175
186
|
@enabled_feature_flags = enabled_flags if enabled_flags
|
176
187
|
|
177
188
|
@telemetry_machine_id = options.dig(:initializationOptions, :telemetryMachineId)
|
189
|
+
|
190
|
+
options.dig(:initializationOptions, :featuresConfiguration)&.each do |feature_name, config|
|
191
|
+
@feature_configuration[feature_name]&.merge!(config)
|
192
|
+
end
|
193
|
+
|
178
194
|
notifications
|
179
195
|
end
|
180
196
|
|
197
|
+
#: (Symbol) -> RequestConfig?
|
198
|
+
def feature_configuration(feature_name)
|
199
|
+
@feature_configuration[feature_name]
|
200
|
+
end
|
201
|
+
|
181
202
|
#: (Symbol flag) -> bool?
|
182
203
|
def enabled_feature?(flag)
|
183
204
|
@enabled_feature_flags[:all] || @enabled_feature_flags[flag]
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -6,8 +6,6 @@
|
|
6
6
|
yarp_require_paths = Gem.loaded_specs["yarp"]&.full_require_paths
|
7
7
|
$LOAD_PATH.delete_if { |path| yarp_require_paths.include?(path) } if yarp_require_paths
|
8
8
|
|
9
|
-
require "sorbet-runtime"
|
10
|
-
|
11
9
|
# Set Bundler's UI level to silent as soon as possible to prevent any prints to STDOUT
|
12
10
|
require "bundler"
|
13
11
|
Bundler.ui.level = :silent
|
@@ -445,11 +445,14 @@ module RubyLsp
|
|
445
445
|
return unless arguments_node
|
446
446
|
|
447
447
|
path_node_to_complete = arguments_node.arguments.first
|
448
|
-
|
449
448
|
return unless path_node_to_complete.is_a?(Prism::StringNode)
|
450
449
|
|
451
|
-
|
450
|
+
# If the file is unsaved (e.g.: untitled:Untitled-1), we can't provide relative completion as we don't know
|
451
|
+
# where the user intends to save it
|
452
|
+
full_path = @uri.to_standardized_path
|
453
|
+
return unless full_path
|
452
454
|
|
455
|
+
origin_dir = Pathname.new(full_path).dirname
|
453
456
|
content = path_node_to_complete.content
|
454
457
|
# if the path is not a directory, glob all possible next characters
|
455
458
|
# for example ../somethi| (where | is the cursor position)
|
@@ -8,10 +8,12 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
RESCUE_STRING_LENGTH = "rescue".length #: Integer
|
10
10
|
|
11
|
-
#: (ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint]
|
12
|
-
def initialize(
|
11
|
+
#: (GlobalState, ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint], Prism::Dispatcher) -> void
|
12
|
+
def initialize(global_state, response_builder, dispatcher)
|
13
13
|
@response_builder = response_builder
|
14
|
-
@hints_configuration =
|
14
|
+
@hints_configuration = ( # rubocop:disable Style/RedundantParentheses
|
15
|
+
global_state.feature_configuration(:inlayHint) #: as !nil
|
16
|
+
) #: RequestConfig
|
15
17
|
|
16
18
|
dispatcher.register(self, :on_rescue_node_enter, :on_implicit_node_enter)
|
17
19
|
end
|
@@ -75,8 +75,9 @@ module RubyLsp
|
|
75
75
|
|
76
76
|
unless full_files.empty?
|
77
77
|
specs, tests = full_files.partition { |path| spec?(path) }
|
78
|
-
|
79
|
-
commands << "#{
|
78
|
+
|
79
|
+
commands << "#{COMMAND} -Itest -e \"ARGV.each { |f| require f }\" #{tests.join(" ")}" if tests.any?
|
80
|
+
commands << "#{COMMAND} -Ispec -e \"ARGV.each { |f| require f }\" #{specs.join(" ")}" if specs.any?
|
80
81
|
end
|
81
82
|
|
82
83
|
commands
|
@@ -113,7 +114,7 @@ module RubyLsp
|
|
113
114
|
end
|
114
115
|
|
115
116
|
load_path = spec?(file_path) ? "-Ispec" : "-Itest"
|
116
|
-
"#{
|
117
|
+
"#{COMMAND} #{load_path} #{file_path} --name \"/#{regex}/\""
|
117
118
|
end
|
118
119
|
|
119
120
|
#: (String, Hash[String, Hash[Symbol, untyped]]) -> Array[String]
|
@@ -124,7 +125,7 @@ module RubyLsp
|
|
124
125
|
Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
|
125
126
|
".*",
|
126
127
|
)
|
127
|
-
command = +"#{
|
128
|
+
command = +"#{COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex}\\$/\""
|
128
129
|
|
129
130
|
unless examples.empty?
|
130
131
|
command << if examples.length == 1
|
@@ -143,13 +144,14 @@ module RubyLsp
|
|
143
144
|
|
144
145
|
MINITEST_REPORTER_PATH = File.expand_path("../test_reporters/minitest_reporter.rb", __dir__) #: String
|
145
146
|
TEST_UNIT_REPORTER_PATH = File.expand_path("../test_reporters/test_unit_reporter.rb", __dir__) #: String
|
146
|
-
ACCESS_MODIFIERS = [:public, :private, :protected].freeze
|
147
147
|
BASE_COMMAND = begin
|
148
148
|
Bundler.with_original_env { Bundler.default_lockfile }
|
149
149
|
"bundle exec ruby"
|
150
150
|
rescue Bundler::GemfileNotFound
|
151
151
|
"ruby"
|
152
152
|
end #: String
|
153
|
+
COMMAND = "#{BASE_COMMAND} -r#{MINITEST_REPORTER_PATH} -r#{TEST_UNIT_REPORTER_PATH}" #: String
|
154
|
+
ACCESS_MODIFIERS = [:public, :private, :protected].freeze
|
153
155
|
|
154
156
|
#: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void
|
155
157
|
def initialize(response_builder, global_state, dispatcher, uri)
|
@@ -27,10 +27,16 @@ module RubyLsp
|
|
27
27
|
@document = document
|
28
28
|
@test_builder = ResponseBuilders::TestCollection.new #: ResponseBuilders::TestCollection
|
29
29
|
uri = document.uri
|
30
|
+
file_path = uri.full_path
|
31
|
+
code_lens_config = global_state.feature_configuration(:codeLens)
|
32
|
+
test_lenses_enabled = (!code_lens_config || code_lens_config.enabled?(:enableTestCodeLens)) &&
|
33
|
+
file_path && File.fnmatch?(TEST_PATH_PATTERN, file_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
30
34
|
|
31
35
|
if global_state.enabled_feature?(:fullTestDiscovery)
|
32
|
-
|
33
|
-
|
36
|
+
if test_lenses_enabled
|
37
|
+
Listeners::TestStyle.new(@test_builder, global_state, dispatcher, uri)
|
38
|
+
Listeners::SpecStyle.new(@test_builder, global_state, dispatcher, uri)
|
39
|
+
end
|
34
40
|
else
|
35
41
|
Listeners::CodeLens.new(@response_builder, global_state, uri, dispatcher)
|
36
42
|
end
|
@@ -38,7 +44,7 @@ module RubyLsp
|
|
38
44
|
Addon.addons.each do |addon|
|
39
45
|
addon.create_code_lens_listener(@response_builder, uri, dispatcher)
|
40
46
|
|
41
|
-
if global_state.enabled_feature?(:fullTestDiscovery)
|
47
|
+
if global_state.enabled_feature?(:fullTestDiscovery) && test_lenses_enabled
|
42
48
|
addon.create_discover_tests_listener(@test_builder, dispatcher, uri)
|
43
49
|
end
|
44
50
|
end
|
@@ -16,13 +16,13 @@ module RubyLsp
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
#: ((RubyDocument | ERBDocument)
|
20
|
-
def initialize(
|
19
|
+
#: (GlobalState, (RubyDocument | ERBDocument), Prism::Dispatcher) -> void
|
20
|
+
def initialize(global_state, document, dispatcher)
|
21
21
|
super()
|
22
22
|
|
23
23
|
@response_builder = ResponseBuilders::CollectionResponseBuilder
|
24
24
|
.new #: ResponseBuilders::CollectionResponseBuilder[Interface::InlayHint]
|
25
|
-
Listeners::InlayHints.new(@response_builder,
|
25
|
+
Listeners::InlayHints.new(global_state, @response_builder, dispatcher)
|
26
26
|
end
|
27
27
|
|
28
28
|
# @override
|