ruby-lsp 0.24.1 → 0.25.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/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 +3 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +2 -2
- data/lib/ruby_indexer/test/configuration_test.rb +1 -1
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +2 -2
- data/lib/ruby_lsp/addon.rb +20 -6
- data/lib/ruby_lsp/base_server.rb +9 -5
- data/lib/ruby_lsp/document.rb +151 -50
- data/lib/ruby_lsp/global_state.rb +21 -0
- data/lib/ruby_lsp/internal.rb +0 -2
- data/lib/ruby_lsp/listeners/hover.rb +7 -0
- data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- 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/references.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 +9 -3
- data/lib/ruby_lsp/response_builders/response_builder.rb +3 -3
- data/lib/ruby_lsp/ruby_document.rb +1 -1
- data/lib/ruby_lsp/server.rb +43 -21
- data/lib/ruby_lsp/setup_bundler.rb +48 -22
- data/lib/ruby_lsp/static_docs.rb +1 -0
- data/lib/ruby_lsp/store.rb +0 -10
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +13 -5
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +17 -4
- data/lib/ruby_lsp/utils.rb +44 -5
- data/static_docs/break.md +103 -0
- metadata +2 -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: 7637d6f68616069c2f8c0d870f3daf31808a1cccd8ab32d2bdaf1f67274f9b59
|
4
|
+
data.tar.gz: 7ea1d75ad921adbf01f69a0f5d2fba905673311c13b0999d44c08bdf54737053
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a354535a3853b0826635408fc46dc85cb7cee97c4993012dd983af11b8917121962d97bddf0853406b54f619faa8c2383cccca9638b4a0624309fb1e4c838249
|
7
|
+
data.tar.gz: 9e49e2436c63d90fc373b52ac9f6b65e83964fe108abd0a18fd46b655cd99bd55c471dd198624bb8701890f62175af2c9fcb50fcfa2bbf69e706406b9fa1e472
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.25.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
|
@@ -266,7 +266,7 @@ module RubyIndexer
|
|
266
266
|
def constant_completion_candidates(name, nesting)
|
267
267
|
# If we have a top level reference, then we don't need to include completions inside the current nesting
|
268
268
|
if name.start_with?("::")
|
269
|
-
return @entries_tree.search(name.delete_prefix("::")) #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
269
|
+
return @entries_tree.search(name.delete_prefix("::")) #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
270
270
|
end
|
271
271
|
|
272
272
|
# Otherwise, we have to include every possible constant the user might be referring to. This is essentially the
|
@@ -292,7 +292,7 @@ module RubyIndexer
|
|
292
292
|
# Top level constants
|
293
293
|
entries.concat(@entries_tree.search(name))
|
294
294
|
entries.uniq!
|
295
|
-
entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
295
|
+
entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
296
296
|
end
|
297
297
|
|
298
298
|
# Resolve a constant to its declaration based on its name and the nesting where the reference was found. Parameter
|
@@ -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
|
|
@@ -232,8 +232,8 @@ module RubyIndexer
|
|
232
232
|
signatures = entry.signatures
|
233
233
|
assert_equal(2, signatures.length)
|
234
234
|
|
235
|
-
# def self.select: [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array) -> [ Array[X], Array[Y], Array[Z] ]
|
236
|
-
# | [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, Time::_Timeout? timeout) -> [ Array[X], Array[Y], Array[Z] ]?
|
235
|
+
# def self.select: [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array) -> [ Array[X], Array[Y], Array[Z] ]
|
236
|
+
# | [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, Time::_Timeout? timeout) -> [ Array[X], Array[Y], Array[Z] ]?
|
237
237
|
|
238
238
|
parameters = signatures[0]&.parameters #: as !nil
|
239
239
|
assert_equal([:read_array, :write_array, :error_array], parameters.map(&:name))
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -56,7 +56,13 @@ module RubyLsp
|
|
56
56
|
addon_files = Gem.find_files("ruby_lsp/**/addon.rb")
|
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
|
+
bundle_path = Bundler.bundle_path.to_s
|
64
|
+
project_addons.reject! { |path| path.start_with?(bundle_path) }
|
65
|
+
addon_files.concat(project_addons)
|
60
66
|
end
|
61
67
|
|
62
68
|
errors = addon_files.filter_map do |addon_path|
|
@@ -176,24 +182,32 @@ module RubyLsp
|
|
176
182
|
# reading information into memory or even spawning a separate process
|
177
183
|
# @abstract
|
178
184
|
#: (GlobalState, Thread::Queue) -> void
|
179
|
-
def activate(global_state, outgoing_queue)
|
185
|
+
def activate(global_state, outgoing_queue)
|
186
|
+
raise AbstractMethodInvokedError
|
187
|
+
end
|
180
188
|
|
181
|
-
# Each add-on
|
189
|
+
# Each add-on must implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
|
182
190
|
# child process
|
183
191
|
# @abstract
|
184
192
|
#: -> void
|
185
|
-
def deactivate
|
193
|
+
def deactivate
|
194
|
+
raise AbstractMethodInvokedError
|
195
|
+
end
|
186
196
|
|
187
197
|
# Add-ons should override the `name` method to return the add-on name
|
188
198
|
# @abstract
|
189
199
|
#: -> String
|
190
|
-
def name
|
200
|
+
def name
|
201
|
+
raise AbstractMethodInvokedError
|
202
|
+
end
|
191
203
|
|
192
204
|
# Add-ons should override the `version` method to return a semantic version string representing the add-on's
|
193
205
|
# version. This is used for compatibility checks
|
194
206
|
# @abstract
|
195
207
|
#: -> String
|
196
|
-
def version
|
208
|
+
def version
|
209
|
+
raise AbstractMethodInvokedError
|
210
|
+
end
|
197
211
|
|
198
212
|
# Handle a response from a window/showMessageRequest request. Add-ons must include the addon_name as part of the
|
199
213
|
# 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
|
@@ -128,11 +128,15 @@ module RubyLsp
|
|
128
128
|
|
129
129
|
# @abstract
|
130
130
|
#: (Hash[Symbol, untyped] message) -> void
|
131
|
-
def process_message(message)
|
131
|
+
def process_message(message)
|
132
|
+
raise AbstractMethodInvokedError
|
133
|
+
end
|
132
134
|
|
133
135
|
# @abstract
|
134
136
|
#: -> void
|
135
|
-
def shutdown
|
137
|
+
def shutdown
|
138
|
+
raise AbstractMethodInvokedError
|
139
|
+
end
|
136
140
|
|
137
141
|
#: (Integer id, String message, ?type: Integer) -> void
|
138
142
|
def fail_request_and_notify(id, message, type: Constant::MessageType::INFO)
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -5,7 +5,7 @@ module RubyLsp
|
|
5
5
|
# @abstract
|
6
6
|
#: [ParseResultType]
|
7
7
|
class Document
|
8
|
-
|
8
|
+
class InvalidLocationError < StandardError; end
|
9
9
|
|
10
10
|
# This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
|
11
11
|
# This is the same number used by the TypeScript extension in VS Code
|
@@ -60,7 +60,9 @@ module RubyLsp
|
|
60
60
|
|
61
61
|
# @abstract
|
62
62
|
#: -> Symbol
|
63
|
-
def language_id
|
63
|
+
def language_id
|
64
|
+
raise AbstractMethodInvokedError
|
65
|
+
end
|
64
66
|
|
65
67
|
#: [T] (String request_name) { (Document[ParseResultType] document) -> T } -> T
|
66
68
|
def cache_fetch(request_name, &block)
|
@@ -120,11 +122,15 @@ module RubyLsp
|
|
120
122
|
# Returns `true` if the document was parsed and `false` if nothing needed parsing
|
121
123
|
# @abstract
|
122
124
|
#: -> bool
|
123
|
-
def parse
|
125
|
+
def parse!
|
126
|
+
raise AbstractMethodInvokedError
|
127
|
+
end
|
124
128
|
|
125
129
|
# @abstract
|
126
130
|
#: -> bool
|
127
|
-
def syntax_error
|
131
|
+
def syntax_error?
|
132
|
+
raise AbstractMethodInvokedError
|
133
|
+
end
|
128
134
|
|
129
135
|
#: -> bool
|
130
136
|
def past_expensive_limit?
|
@@ -145,7 +151,14 @@ module RubyLsp
|
|
145
151
|
|
146
152
|
#: -> Scanner
|
147
153
|
def create_scanner
|
148
|
-
|
154
|
+
case @encoding
|
155
|
+
when Encoding::UTF_8
|
156
|
+
Utf8Scanner.new(@source)
|
157
|
+
when Encoding::UTF_16LE
|
158
|
+
Utf16Scanner.new(@source)
|
159
|
+
else
|
160
|
+
Utf32Scanner.new(@source)
|
161
|
+
end
|
149
162
|
end
|
150
163
|
|
151
164
|
# @abstract
|
@@ -163,81 +176,169 @@ module RubyLsp
|
|
163
176
|
class Replace < Edit; end
|
164
177
|
class Delete < Edit; end
|
165
178
|
|
179
|
+
# Parent class for all position scanners. Scanners are used to translate a position given by the editor into a
|
180
|
+
# string index that we can use to find the right place in the document source. The logic for finding the correct
|
181
|
+
# index depends on the encoding negotiated with the editor, so we have different subclasses for each encoding.
|
182
|
+
# See https://microsoft.github.io/language-server-protocol/specification/#positionEncodingKind for more information
|
183
|
+
# @abstract
|
166
184
|
class Scanner
|
167
|
-
extend T::Sig
|
168
|
-
|
169
185
|
LINE_BREAK = 0x0A #: Integer
|
170
186
|
# After character 0xFFFF, UTF-16 considers characters to have length 2 and we have to account for that
|
171
187
|
SURROGATE_PAIR_START = 0xFFFF #: Integer
|
172
188
|
|
173
|
-
#:
|
174
|
-
def initialize
|
189
|
+
#: -> void
|
190
|
+
def initialize
|
175
191
|
@current_line = 0 #: Integer
|
176
192
|
@pos = 0 #: Integer
|
177
|
-
@bytes_or_codepoints = encoding == Encoding::UTF_8 ? source.bytes : source.codepoints #: Array[Integer]
|
178
|
-
@encoding = encoding
|
179
193
|
end
|
180
194
|
|
181
|
-
# Finds the character index inside the source string for a given line and column
|
195
|
+
# Finds the character index inside the source string for a given line and column. This method always returns the
|
196
|
+
# character index regardless of whether we are searching positions based on bytes, code units, or codepoints.
|
197
|
+
# @abstract
|
182
198
|
#: (Hash[Symbol, untyped] position) -> Integer
|
183
199
|
def find_char_position(position)
|
184
|
-
|
200
|
+
raise AbstractMethodInvokedError
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# For the UTF-8 encoding, positions correspond to bytes
|
205
|
+
class Utf8Scanner < Scanner
|
206
|
+
#: (String source) -> void
|
207
|
+
def initialize(source)
|
208
|
+
super()
|
209
|
+
@bytes = source.bytes #: Array[Integer]
|
210
|
+
@character_length = 0 #: Integer
|
211
|
+
end
|
212
|
+
|
213
|
+
# @override
|
214
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
215
|
+
def find_char_position(position)
|
216
|
+
# Each group of bytes is a character. We advance based on the number of bytes to count how many full characters
|
217
|
+
# we have in the requested offset
|
185
218
|
until @current_line == position[:line]
|
186
|
-
|
219
|
+
byte = @bytes[@pos] #: Integer?
|
220
|
+
raise InvalidLocationError unless byte
|
221
|
+
|
222
|
+
until LINE_BREAK == byte
|
223
|
+
@pos += character_byte_length(byte)
|
224
|
+
@character_length += 1
|
225
|
+
byte = @bytes[@pos]
|
226
|
+
raise InvalidLocationError unless byte
|
227
|
+
end
|
228
|
+
|
187
229
|
@pos += 1
|
230
|
+
@character_length += 1
|
188
231
|
@current_line += 1
|
189
232
|
end
|
190
233
|
|
191
|
-
#
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
234
|
+
# @character_length has the number of characters until the beginning of the line. We don't accumulate on it for
|
235
|
+
# the character part because locating the same position twice must return the same value
|
236
|
+
line_byte_offset = 0
|
237
|
+
line_characters = 0
|
238
|
+
|
239
|
+
while line_byte_offset < position[:character]
|
240
|
+
byte = @bytes[@pos + line_byte_offset] #: Integer?
|
241
|
+
raise InvalidLocationError unless byte
|
242
|
+
|
243
|
+
line_byte_offset += character_byte_length(byte)
|
244
|
+
line_characters += 1
|
245
|
+
end
|
246
|
+
|
247
|
+
@character_length + line_characters
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
|
252
|
+
#: (Integer) -> Integer
|
253
|
+
def character_byte_length(byte)
|
254
|
+
if byte < 0x80 # 1-byte character
|
255
|
+
1
|
256
|
+
elsif byte < 0xE0 # 2-byte character
|
257
|
+
2
|
258
|
+
elsif byte < 0xF0 # 3-byte character
|
259
|
+
3
|
260
|
+
else # 4-byte character
|
261
|
+
4
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# For the UTF-16 encoding, positions correspond to UTF-16 code units, which count characters beyond the surrogate
|
267
|
+
# pair as length 2
|
268
|
+
class Utf16Scanner < Scanner
|
269
|
+
#: (String) -> void
|
270
|
+
def initialize(source)
|
271
|
+
super()
|
272
|
+
@codepoints = source.codepoints #: Array[Integer]
|
273
|
+
end
|
274
|
+
|
275
|
+
# @override
|
276
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
277
|
+
def find_char_position(position)
|
278
|
+
# Find the character index for the beginning of the requested line
|
279
|
+
until @current_line == position[:line]
|
280
|
+
codepoint = @codepoints[@pos] #: Integer?
|
281
|
+
raise InvalidLocationError unless codepoint
|
282
|
+
|
283
|
+
until LINE_BREAK == @codepoints[@pos]
|
284
|
+
@pos += 1
|
285
|
+
codepoint = @codepoints[@pos] #: Integer?
|
286
|
+
raise InvalidLocationError unless codepoint
|
211
287
|
end
|
212
288
|
|
213
|
-
@pos
|
214
|
-
|
215
|
-
@pos + position[:character]
|
289
|
+
@pos += 1
|
290
|
+
@current_line += 1
|
216
291
|
end
|
217
292
|
|
218
293
|
# The final position is the beginning of the line plus the requested column. If the encoding is UTF-16, we also
|
219
294
|
# need to adjust for surrogate pairs
|
220
|
-
|
221
|
-
|
295
|
+
line_characters = 0
|
296
|
+
line_code_units = 0
|
297
|
+
|
298
|
+
while line_code_units < position[:character]
|
299
|
+
code_point = @codepoints[@pos + line_characters]
|
300
|
+
raise InvalidLocationError unless code_point
|
301
|
+
|
302
|
+
line_code_units += if code_point > SURROGATE_PAIR_START
|
303
|
+
2 # Surrogate pair, so we skip the next code unit
|
304
|
+
else
|
305
|
+
1 # Single code unit character
|
306
|
+
end
|
307
|
+
|
308
|
+
line_characters += 1
|
222
309
|
end
|
223
310
|
|
224
|
-
|
311
|
+
@pos + line_characters
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# For the UTF-32 encoding, positions correspond directly to codepoints
|
316
|
+
class Utf32Scanner < Scanner
|
317
|
+
#: (String) -> void
|
318
|
+
def initialize(source)
|
319
|
+
super()
|
320
|
+
@codepoints = source.codepoints #: Array[Integer]
|
225
321
|
end
|
226
322
|
|
227
|
-
#
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
323
|
+
# @override
|
324
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
325
|
+
def find_char_position(position)
|
326
|
+
# Find the character index for the beginning of the requested line
|
327
|
+
until @current_line == position[:line]
|
328
|
+
codepoint = @codepoints[@pos] #: Integer?
|
329
|
+
raise InvalidLocationError unless codepoint
|
232
330
|
|
233
|
-
|
234
|
-
|
235
|
-
|
331
|
+
until LINE_BREAK == @codepoints[@pos]
|
332
|
+
@pos += 1
|
333
|
+
codepoint = @codepoints[@pos] #: Integer?
|
334
|
+
raise InvalidLocationError unless codepoint
|
335
|
+
end
|
236
336
|
|
237
|
-
|
337
|
+
@pos += 1
|
338
|
+
@current_line += 1
|
238
339
|
end
|
239
340
|
|
240
|
-
|
341
|
+
@pos + position[:character]
|
241
342
|
end
|
242
343
|
end
|
243
344
|
end
|
@@ -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
|
@@ -7,6 +7,7 @@ module RubyLsp
|
|
7
7
|
include Requests::Support::Common
|
8
8
|
|
9
9
|
ALLOWED_TARGETS = [
|
10
|
+
Prism::BreakNode,
|
10
11
|
Prism::CallNode,
|
11
12
|
Prism::ConstantReadNode,
|
12
13
|
Prism::ConstantWriteNode,
|
@@ -54,6 +55,7 @@ module RubyLsp
|
|
54
55
|
|
55
56
|
dispatcher.register(
|
56
57
|
self,
|
58
|
+
:on_break_node_enter,
|
57
59
|
:on_constant_read_node_enter,
|
58
60
|
:on_constant_write_node_enter,
|
59
61
|
:on_constant_path_node_enter,
|
@@ -84,6 +86,11 @@ module RubyLsp
|
|
84
86
|
)
|
85
87
|
end
|
86
88
|
|
89
|
+
#: (Prism::BreakNode node) -> void
|
90
|
+
def on_break_node_enter(node)
|
91
|
+
handle_keyword_documentation(node.keyword)
|
92
|
+
end
|
93
|
+
|
87
94
|
#: (Prism::StringNode node) -> void
|
88
95
|
def on_string_node_enter(node)
|
89
96
|
if @path && File.basename(@path) == GEMFILE_NAME
|
@@ -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
|