ruby-lsp 0.23.11 → 0.26.1
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/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +45 -22
- data/exe/ruby-lsp-test-exec +6 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -2
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -6
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +140 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +10 -14
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +107 -236
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +166 -281
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +23 -27
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +25 -57
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +58 -68
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +49 -9
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +185 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +61 -37
- data/lib/ruby_indexer/test/method_test.rb +166 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +21 -21
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +70 -75
- data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
- data/lib/ruby_indexer/test/test_case.rb +9 -3
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +88 -86
- data/lib/ruby_lsp/base_server.rb +59 -54
- data/lib/ruby_lsp/client_capabilities.rb +16 -13
- data/lib/ruby_lsp/document.rb +205 -104
- data/lib/ruby_lsp/erb_document.rb +45 -47
- data/lib/ruby_lsp/global_state.rb +73 -57
- data/lib/ruby_lsp/internal.rb +8 -3
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +81 -76
- data/lib/ruby_lsp/listeners/definition.rb +44 -58
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +50 -70
- data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
- data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
- data/lib/ruby_lsp/listeners/hover.rb +107 -115
- data/lib/ruby_lsp/listeners/inlay_hints.rb +8 -13
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
- data/lib/ruby_lsp/listeners/spec_style.rb +214 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +92 -0
- data/lib/ruby_lsp/listeners/test_style.rb +205 -95
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +10 -11
- data/lib/ruby_lsp/requests/code_action_resolve.rb +65 -61
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +31 -21
- data/lib/ruby_lsp/requests/completion.rb +8 -21
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +8 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +20 -7
- data/lib/ruby_lsp/requests/document_highlight.rb +6 -16
- data/lib/ruby_lsp/requests/document_link.rb +6 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
- data/lib/ruby_lsp/requests/formatting.rb +6 -9
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +85 -0
- data/lib/ruby_lsp/requests/hover.rb +12 -25
- data/lib/ruby_lsp/requests/inlay_hints.rb +8 -19
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +5 -10
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +17 -57
- data/lib/ruby_lsp/requests/rename.rb +27 -51
- data/lib/ruby_lsp/requests/request.rb +13 -25
- data/lib/ruby_lsp/requests/selection_ranges.rb +7 -7
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
- data/lib/ruby_lsp/requests/signature_help.rb +9 -27
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +16 -58
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +6 -9
- data/lib/ruby_lsp/response_builders/document_symbol.rb +15 -21
- data/lib/ruby_lsp/response_builders/hover.rb +12 -18
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +62 -91
- data/lib/ruby_lsp/response_builders/signature_help.rb +6 -8
- data/lib/ruby_lsp/response_builders/test_collection.rb +35 -13
- data/lib/ruby_lsp/ruby_document.rb +32 -98
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +303 -196
- data/lib/ruby_lsp/setup_bundler.rb +121 -82
- data/lib/ruby_lsp/static_docs.rb +12 -7
- data/lib/ruby_lsp/store.rb +21 -49
- data/lib/ruby_lsp/test_helper.rb +3 -16
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +233 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +145 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +92 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +138 -93
- data/static_docs/break.md +103 -0
- metadata +14 -20
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
data/lib/ruby_lsp/document.rb
CHANGED
@@ -2,80 +2,69 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
|
+
# @abstract
|
6
|
+
#: [ParseResultType]
|
5
7
|
class Document
|
6
|
-
class
|
7
|
-
enums do
|
8
|
-
Ruby = new("ruby")
|
9
|
-
ERB = new("erb")
|
10
|
-
RBS = new("rbs")
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
extend T::Sig
|
15
|
-
extend T::Helpers
|
16
|
-
extend T::Generic
|
17
|
-
|
18
|
-
class LocationNotFoundError < StandardError; end
|
19
|
-
ParseResultType = type_member
|
8
|
+
class InvalidLocationError < StandardError; end
|
20
9
|
|
21
10
|
# This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
|
22
11
|
# This is the same number used by the TypeScript extension in VS Code
|
23
12
|
MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES = 100_000
|
24
|
-
EMPTY_CACHE =
|
13
|
+
EMPTY_CACHE = Object.new.freeze #: Object
|
25
14
|
|
26
|
-
|
27
|
-
|
28
|
-
sig { returns(ParseResultType) }
|
15
|
+
#: ParseResultType
|
29
16
|
attr_reader :parse_result
|
30
17
|
|
31
|
-
|
18
|
+
#: String
|
32
19
|
attr_reader :source
|
33
20
|
|
34
|
-
|
21
|
+
#: Integer
|
35
22
|
attr_reader :version
|
36
23
|
|
37
|
-
|
24
|
+
#: URI::Generic
|
38
25
|
attr_reader :uri
|
39
26
|
|
40
|
-
|
27
|
+
#: Encoding
|
41
28
|
attr_reader :encoding
|
42
29
|
|
43
|
-
|
30
|
+
#: Edit?
|
44
31
|
attr_reader :last_edit
|
45
32
|
|
46
|
-
|
33
|
+
#: (Interface::SemanticTokens | Object)
|
47
34
|
attr_accessor :semantic_tokens
|
48
35
|
|
49
|
-
|
36
|
+
#: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
|
50
37
|
def initialize(source:, version:, uri:, global_state:)
|
51
38
|
@source = source
|
52
39
|
@version = version
|
53
40
|
@global_state = global_state
|
54
|
-
@cache =
|
55
|
-
@semantic_tokens =
|
56
|
-
@encoding =
|
57
|
-
@uri =
|
58
|
-
@needs_parsing =
|
59
|
-
@
|
60
|
-
|
41
|
+
@cache = Hash.new(EMPTY_CACHE) #: Hash[String, untyped]
|
42
|
+
@semantic_tokens = EMPTY_CACHE #: (Interface::SemanticTokens | Object)
|
43
|
+
@encoding = global_state.encoding #: Encoding
|
44
|
+
@uri = uri #: URI::Generic
|
45
|
+
@needs_parsing = true #: bool
|
46
|
+
@last_edit = nil #: Edit?
|
47
|
+
|
48
|
+
# Workaround to be able to type parse_result properly. It is immediately set when invoking parse!
|
49
|
+
@parse_result = ( # rubocop:disable Style/RedundantParentheses
|
50
|
+
nil #: as untyped
|
51
|
+
) #: ParseResultType
|
52
|
+
|
61
53
|
parse!
|
62
54
|
end
|
63
55
|
|
64
|
-
|
56
|
+
#: (Document[untyped] other) -> bool
|
65
57
|
def ==(other)
|
66
58
|
self.class == other.class && uri == other.uri && @source == other.source
|
67
59
|
end
|
68
60
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
type_parameters(:T)
|
74
|
-
.params(
|
75
|
-
request_name: String,
|
76
|
-
block: T.proc.params(document: Document[ParseResultType]).returns(T.type_parameter(:T)),
|
77
|
-
).returns(T.type_parameter(:T))
|
61
|
+
# @abstract
|
62
|
+
#: -> Symbol
|
63
|
+
def language_id
|
64
|
+
raise AbstractMethodInvokedError
|
78
65
|
end
|
66
|
+
|
67
|
+
#: [T] (String request_name) { (Document[ParseResultType] document) -> T } -> T
|
79
68
|
def cache_fetch(request_name, &block)
|
80
69
|
cached = @cache[request_name]
|
81
70
|
return cached if cached != EMPTY_CACHE
|
@@ -85,17 +74,22 @@ module RubyLsp
|
|
85
74
|
result
|
86
75
|
end
|
87
76
|
|
88
|
-
|
77
|
+
#: [T] (String request_name, T value) -> T
|
89
78
|
def cache_set(request_name, value)
|
90
79
|
@cache[request_name] = value
|
91
80
|
end
|
92
81
|
|
93
|
-
|
82
|
+
#: (String request_name) -> untyped
|
94
83
|
def cache_get(request_name)
|
95
84
|
@cache[request_name]
|
96
85
|
end
|
97
86
|
|
98
|
-
|
87
|
+
#: (String request_name) -> void
|
88
|
+
def clear_cache(request_name)
|
89
|
+
@cache[request_name] = EMPTY_CACHE
|
90
|
+
end
|
91
|
+
|
92
|
+
#: (Array[Hash[Symbol, untyped]] edits, version: Integer) -> void
|
99
93
|
def push_edits(edits, version:)
|
100
94
|
edits.each do |edit|
|
101
95
|
range = edit[:range]
|
@@ -126,49 +120,51 @@ module RubyLsp
|
|
126
120
|
end
|
127
121
|
|
128
122
|
# Returns `true` if the document was parsed and `false` if nothing needed parsing
|
129
|
-
|
130
|
-
|
123
|
+
# @abstract
|
124
|
+
#: -> bool
|
125
|
+
def parse!
|
126
|
+
raise AbstractMethodInvokedError
|
127
|
+
end
|
131
128
|
|
132
|
-
|
133
|
-
|
129
|
+
# @abstract
|
130
|
+
#: -> bool
|
131
|
+
def syntax_error?
|
132
|
+
raise AbstractMethodInvokedError
|
133
|
+
end
|
134
134
|
|
135
|
-
|
135
|
+
#: -> bool
|
136
136
|
def past_expensive_limit?
|
137
137
|
@source.length > MAXIMUM_CHARACTERS_FOR_EXPENSIVE_FEATURES
|
138
138
|
end
|
139
139
|
|
140
|
-
|
141
|
-
params(
|
142
|
-
start_pos: T::Hash[Symbol, T.untyped],
|
143
|
-
end_pos: T.nilable(T::Hash[Symbol, T.untyped]),
|
144
|
-
).returns([Integer, T.nilable(Integer)])
|
145
|
-
end
|
140
|
+
#: (Hash[Symbol, untyped] start_pos, ?Hash[Symbol, untyped]? end_pos) -> [Integer, Integer?]
|
146
141
|
def find_index_by_position(start_pos, end_pos = nil)
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
[start_index, end_index]
|
152
|
-
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]
|
153
146
|
end
|
154
147
|
|
155
148
|
private
|
156
149
|
|
157
|
-
|
150
|
+
#: -> Scanner
|
158
151
|
def create_scanner
|
159
|
-
|
152
|
+
case @encoding
|
153
|
+
when Encoding::UTF_8
|
154
|
+
Utf8Scanner.new(@source)
|
155
|
+
when Encoding::UTF_16LE
|
156
|
+
Utf16Scanner.new(@source)
|
157
|
+
else
|
158
|
+
Utf32Scanner.new(@source)
|
159
|
+
end
|
160
160
|
end
|
161
161
|
|
162
|
+
# @abstract
|
162
163
|
class Edit
|
163
|
-
|
164
|
-
extend T::Helpers
|
165
|
-
|
166
|
-
abstract!
|
167
|
-
|
168
|
-
sig { returns(T::Hash[Symbol, T.untyped]) }
|
164
|
+
#: Hash[Symbol, untyped]
|
169
165
|
attr_reader :range
|
170
166
|
|
171
|
-
|
167
|
+
#: (Hash[Symbol, untyped] range) -> void
|
172
168
|
def initialize(range)
|
173
169
|
@range = range
|
174
170
|
end
|
@@ -178,33 +174,114 @@ module RubyLsp
|
|
178
174
|
class Replace < Edit; end
|
179
175
|
class Delete < Edit; end
|
180
176
|
|
177
|
+
# Parent class for all position scanners. Scanners are used to translate a position given by the editor into a
|
178
|
+
# string index that we can use to find the right place in the document source. The logic for finding the correct
|
179
|
+
# index depends on the encoding negotiated with the editor, so we have different subclasses for each encoding.
|
180
|
+
# See https://microsoft.github.io/language-server-protocol/specification/#positionEncodingKind for more information
|
181
|
+
# @abstract
|
181
182
|
class Scanner
|
182
|
-
|
183
|
-
|
184
|
-
LINE_BREAK = T.let(0x0A, Integer)
|
183
|
+
LINE_BREAK = 0x0A #: Integer
|
185
184
|
# After character 0xFFFF, UTF-16 considers characters to have length 2 and we have to account for that
|
186
|
-
SURROGATE_PAIR_START =
|
187
|
-
|
188
|
-
|
189
|
-
def initialize
|
190
|
-
@current_line =
|
191
|
-
@pos =
|
192
|
-
|
193
|
-
|
185
|
+
SURROGATE_PAIR_START = 0xFFFF #: Integer
|
186
|
+
|
187
|
+
#: -> void
|
188
|
+
def initialize
|
189
|
+
@current_line = 0 #: Integer
|
190
|
+
@pos = 0 #: Integer
|
191
|
+
end
|
192
|
+
|
193
|
+
# Finds the character index inside the source string for a given line and column. This method always returns the
|
194
|
+
# character index regardless of whether we are searching positions based on bytes, code units, or codepoints.
|
195
|
+
# @abstract
|
196
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
197
|
+
def find_char_position(position)
|
198
|
+
raise AbstractMethodInvokedError
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# For the UTF-8 encoding, positions correspond to bytes
|
203
|
+
class Utf8Scanner < Scanner
|
204
|
+
#: (String source) -> void
|
205
|
+
def initialize(source)
|
206
|
+
super()
|
207
|
+
@bytes = source.bytes #: Array[Integer]
|
208
|
+
@character_length = 0 #: Integer
|
209
|
+
end
|
210
|
+
|
211
|
+
# @override
|
212
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
213
|
+
def find_char_position(position)
|
214
|
+
# Each group of bytes is a character. We advance based on the number of bytes to count how many full characters
|
215
|
+
# we have in the requested offset
|
216
|
+
until @current_line == position[:line]
|
217
|
+
byte = @bytes[@pos] #: Integer?
|
218
|
+
raise InvalidLocationError unless byte
|
219
|
+
|
220
|
+
until LINE_BREAK == byte
|
221
|
+
@pos += character_byte_length(byte)
|
222
|
+
@character_length += 1
|
223
|
+
byte = @bytes[@pos]
|
224
|
+
raise InvalidLocationError unless byte
|
225
|
+
end
|
226
|
+
|
227
|
+
@pos += 1
|
228
|
+
@character_length += 1
|
229
|
+
@current_line += 1
|
230
|
+
end
|
231
|
+
|
232
|
+
# @character_length has the number of characters until the beginning of the line. We don't accumulate on it for
|
233
|
+
# the character part because locating the same position twice must return the same value
|
234
|
+
line_byte_offset = 0
|
235
|
+
line_characters = 0
|
236
|
+
|
237
|
+
while line_byte_offset < position[:character]
|
238
|
+
byte = @bytes[@pos + line_byte_offset] #: Integer?
|
239
|
+
raise InvalidLocationError unless byte
|
240
|
+
|
241
|
+
line_byte_offset += character_byte_length(byte)
|
242
|
+
line_characters += 1
|
243
|
+
end
|
244
|
+
|
245
|
+
@character_length + line_characters
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
#: (Integer) -> Integer
|
251
|
+
def character_byte_length(byte)
|
252
|
+
if byte < 0x80 # 1-byte character
|
253
|
+
1
|
254
|
+
elsif byte < 0xE0 # 2-byte character
|
255
|
+
2
|
256
|
+
elsif byte < 0xF0 # 3-byte character
|
257
|
+
3
|
258
|
+
else # 4-byte character
|
259
|
+
4
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# For the UTF-16 encoding, positions correspond to UTF-16 code units, which count characters beyond the surrogate
|
265
|
+
# pair as length 2
|
266
|
+
class Utf16Scanner < Scanner
|
267
|
+
#: (String) -> void
|
268
|
+
def initialize(source)
|
269
|
+
super()
|
270
|
+
@codepoints = source.codepoints #: Array[Integer]
|
194
271
|
end
|
195
272
|
|
196
|
-
#
|
197
|
-
|
273
|
+
# @override
|
274
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
198
275
|
def find_char_position(position)
|
199
276
|
# Find the character index for the beginning of the requested line
|
200
277
|
until @current_line == position[:line]
|
201
|
-
|
202
|
-
|
278
|
+
codepoint = @codepoints[@pos] #: Integer?
|
279
|
+
raise InvalidLocationError unless codepoint
|
203
280
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
281
|
+
until LINE_BREAK == @codepoints[@pos]
|
282
|
+
@pos += 1
|
283
|
+
codepoint = @codepoints[@pos] #: Integer?
|
284
|
+
raise InvalidLocationError unless codepoint
|
208
285
|
end
|
209
286
|
|
210
287
|
@pos += 1
|
@@ -213,29 +290,53 @@ module RubyLsp
|
|
213
290
|
|
214
291
|
# The final position is the beginning of the line plus the requested column. If the encoding is UTF-16, we also
|
215
292
|
# need to adjust for surrogate pairs
|
216
|
-
|
293
|
+
line_characters = 0
|
294
|
+
line_code_units = 0
|
295
|
+
|
296
|
+
while line_code_units < position[:character]
|
297
|
+
code_point = @codepoints[@pos + line_characters]
|
298
|
+
raise InvalidLocationError unless code_point
|
299
|
+
|
300
|
+
line_code_units += if code_point > SURROGATE_PAIR_START
|
301
|
+
2 # Surrogate pair, so we skip the next code unit
|
302
|
+
else
|
303
|
+
1 # Single code unit character
|
304
|
+
end
|
217
305
|
|
218
|
-
|
219
|
-
requested_position -= utf_16_character_position_correction(@pos, requested_position)
|
306
|
+
line_characters += 1
|
220
307
|
end
|
221
308
|
|
222
|
-
|
309
|
+
@pos + line_characters
|
223
310
|
end
|
311
|
+
end
|
224
312
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
def
|
229
|
-
|
313
|
+
# For the UTF-32 encoding, positions correspond directly to codepoints
|
314
|
+
class Utf32Scanner < Scanner
|
315
|
+
#: (String) -> void
|
316
|
+
def initialize(source)
|
317
|
+
super()
|
318
|
+
@codepoints = source.codepoints #: Array[Integer]
|
319
|
+
end
|
230
320
|
|
231
|
-
|
232
|
-
|
233
|
-
|
321
|
+
# @override
|
322
|
+
#: (Hash[Symbol, untyped] position) -> Integer
|
323
|
+
def find_char_position(position)
|
324
|
+
# Find the character index for the beginning of the requested line
|
325
|
+
until @current_line == position[:line]
|
326
|
+
codepoint = @codepoints[@pos] #: Integer?
|
327
|
+
raise InvalidLocationError unless codepoint
|
234
328
|
|
235
|
-
|
329
|
+
until LINE_BREAK == @codepoints[@pos]
|
330
|
+
@pos += 1
|
331
|
+
codepoint = @codepoints[@pos] #: Integer?
|
332
|
+
raise InvalidLocationError unless codepoint
|
333
|
+
end
|
334
|
+
|
335
|
+
@pos += 1
|
336
|
+
@current_line += 1
|
236
337
|
end
|
237
338
|
|
238
|
-
|
339
|
+
@pos + position[:character]
|
239
340
|
end
|
240
341
|
end
|
241
342
|
end
|
@@ -2,36 +2,26 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
|
+
#: [ParseResultType = Prism::ParseLexResult]
|
5
6
|
class ERBDocument < Document
|
6
|
-
|
7
|
-
extend T::Generic
|
8
|
-
|
9
|
-
ParseResultType = type_member { { fixed: Prism::ParseResult } }
|
10
|
-
|
11
|
-
sig { returns(String) }
|
7
|
+
#: String
|
12
8
|
attr_reader :host_language_source
|
13
9
|
|
14
|
-
|
15
|
-
returns(T.any(
|
16
|
-
T.proc.params(arg0: Integer).returns(Integer),
|
17
|
-
Prism::CodeUnitsCache,
|
18
|
-
))
|
19
|
-
end
|
10
|
+
#: (^(Integer arg0) -> Integer | Prism::CodeUnitsCache)
|
20
11
|
attr_reader :code_units_cache
|
21
12
|
|
22
|
-
|
13
|
+
#: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
|
23
14
|
def initialize(source:, version:, uri:, global_state:)
|
24
15
|
# This has to be initialized before calling super because we call `parse` in the parent constructor, which
|
25
16
|
# overrides this with the proper virtual host language source
|
26
|
-
@host_language_source =
|
17
|
+
@host_language_source = "" #: String
|
27
18
|
super
|
28
|
-
@code_units_cache =
|
29
|
-
|
30
|
-
Prism::CodeUnitsCache,
|
31
|
-
))
|
19
|
+
@code_units_cache =
|
20
|
+
@parse_result.code_units_cache(@encoding) #: (^(Integer arg0) -> Integer | Prism::CodeUnitsCache)
|
32
21
|
end
|
33
22
|
|
34
|
-
|
23
|
+
# @override
|
24
|
+
#: -> bool
|
35
25
|
def parse!
|
36
26
|
return false unless @needs_parsing
|
37
27
|
|
@@ -41,60 +31,60 @@ module RubyLsp
|
|
41
31
|
@host_language_source = scanner.host_language
|
42
32
|
# Use partial script to avoid syntax errors in ERB files where keywords may be used without the full context in
|
43
33
|
# which they will be evaluated
|
44
|
-
@parse_result = Prism.
|
34
|
+
@parse_result = Prism.parse_lex(scanner.ruby, partial_script: true)
|
45
35
|
@code_units_cache = @parse_result.code_units_cache(@encoding)
|
46
36
|
true
|
47
37
|
end
|
48
38
|
|
49
|
-
|
39
|
+
#: -> Prism::ProgramNode
|
40
|
+
def ast
|
41
|
+
@parse_result.value.first
|
42
|
+
end
|
43
|
+
|
44
|
+
# @override
|
45
|
+
#: -> bool
|
50
46
|
def syntax_error?
|
51
47
|
@parse_result.failure?
|
52
48
|
end
|
53
49
|
|
54
|
-
|
50
|
+
# @override
|
51
|
+
#: -> Symbol
|
55
52
|
def language_id
|
56
|
-
|
53
|
+
:erb
|
57
54
|
end
|
58
55
|
|
59
|
-
|
60
|
-
params(
|
61
|
-
position: T::Hash[Symbol, T.untyped],
|
62
|
-
node_types: T::Array[T.class_of(Prism::Node)],
|
63
|
-
).returns(NodeContext)
|
64
|
-
end
|
56
|
+
#: (Hash[Symbol, untyped] position, ?node_types: Array[singleton(Prism::Node)]) -> NodeContext
|
65
57
|
def locate_node(position, node_types: [])
|
66
58
|
char_position, _ = find_index_by_position(position)
|
67
59
|
|
68
60
|
RubyDocument.locate(
|
69
|
-
|
61
|
+
ast,
|
70
62
|
char_position,
|
71
63
|
code_units_cache: @code_units_cache,
|
72
64
|
node_types: node_types,
|
73
65
|
)
|
74
66
|
end
|
75
67
|
|
76
|
-
|
68
|
+
#: (Integer char_position) -> bool?
|
77
69
|
def inside_host_language?(char_position)
|
78
70
|
char = @host_language_source[char_position]
|
79
71
|
char && char != " "
|
80
72
|
end
|
81
73
|
|
82
74
|
class ERBScanner
|
83
|
-
|
84
|
-
|
85
|
-
sig { returns(String) }
|
75
|
+
#: String
|
86
76
|
attr_reader :ruby, :host_language
|
87
77
|
|
88
|
-
|
78
|
+
#: (String source) -> void
|
89
79
|
def initialize(source)
|
90
80
|
@source = source
|
91
|
-
@host_language =
|
92
|
-
@ruby =
|
93
|
-
@current_pos =
|
94
|
-
@inside_ruby =
|
81
|
+
@host_language = +"" #: String
|
82
|
+
@ruby = +"" #: String
|
83
|
+
@current_pos = 0 #: Integer
|
84
|
+
@inside_ruby = false #: bool
|
95
85
|
end
|
96
86
|
|
97
|
-
|
87
|
+
#: -> void
|
98
88
|
def scan
|
99
89
|
while @current_pos < @source.length
|
100
90
|
scan_char
|
@@ -104,7 +94,7 @@ module RubyLsp
|
|
104
94
|
|
105
95
|
private
|
106
96
|
|
107
|
-
|
97
|
+
#: -> void
|
108
98
|
def scan_char
|
109
99
|
char = @source[@current_pos]
|
110
100
|
|
@@ -123,7 +113,9 @@ module RubyLsp
|
|
123
113
|
push_char(" ")
|
124
114
|
end
|
125
115
|
else
|
126
|
-
push_char(
|
116
|
+
push_char(
|
117
|
+
char, #: as !nil
|
118
|
+
)
|
127
119
|
end
|
128
120
|
when "-"
|
129
121
|
if @inside_ruby && next_char == "%" &&
|
@@ -132,7 +124,9 @@ module RubyLsp
|
|
132
124
|
push_char(" ")
|
133
125
|
@inside_ruby = false
|
134
126
|
else
|
135
|
-
push_char(
|
127
|
+
push_char(
|
128
|
+
char, #: as !nil
|
129
|
+
)
|
136
130
|
end
|
137
131
|
when "%"
|
138
132
|
if @inside_ruby && next_char == ">"
|
@@ -140,7 +134,9 @@ module RubyLsp
|
|
140
134
|
@current_pos += 1
|
141
135
|
push_char(" ")
|
142
136
|
else
|
143
|
-
push_char(
|
137
|
+
push_char(
|
138
|
+
char, #: as !nil
|
139
|
+
)
|
144
140
|
end
|
145
141
|
when "\r"
|
146
142
|
@ruby << char
|
@@ -155,11 +151,13 @@ module RubyLsp
|
|
155
151
|
@ruby << char
|
156
152
|
@host_language << char
|
157
153
|
else
|
158
|
-
push_char(
|
154
|
+
push_char(
|
155
|
+
char, #: as !nil
|
156
|
+
)
|
159
157
|
end
|
160
158
|
end
|
161
159
|
|
162
|
-
|
160
|
+
#: (String char) -> void
|
163
161
|
def push_char(char)
|
164
162
|
if @inside_ruby
|
165
163
|
@ruby << char
|
@@ -170,7 +168,7 @@ module RubyLsp
|
|
170
168
|
end
|
171
169
|
end
|
172
170
|
|
173
|
-
|
171
|
+
#: -> String
|
174
172
|
def next_char
|
175
173
|
@source[@current_pos + 1] || ""
|
176
174
|
end
|