ruby-lsp 0.17.12 → 0.17.14
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 +6 -4
- data/exe/ruby-lsp-check +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +6 -1
- data/lib/ruby_indexer/ruby_indexer.rb +0 -8
- data/lib/ruby_indexer/test/configuration_test.rb +1 -1
- data/lib/ruby_lsp/addon.rb +9 -4
- data/lib/ruby_lsp/base_server.rb +7 -2
- data/lib/ruby_lsp/document.rb +9 -178
- data/lib/ruby_lsp/erb_document.rb +16 -2
- data/lib/ruby_lsp/global_state.rb +34 -14
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listeners/completion.rb +5 -5
- data/lib/ruby_lsp/listeners/definition.rb +3 -3
- data/lib/ruby_lsp/listeners/hover.rb +5 -5
- data/lib/ruby_lsp/listeners/signature_help.rb +1 -1
- data/lib/ruby_lsp/rbs_document.rb +41 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +37 -21
- data/lib/ruby_lsp/requests/code_actions.rb +3 -3
- data/lib/ruby_lsp/requests/completion.rb +3 -3
- data/lib/ruby_lsp/requests/definition.rb +2 -2
- data/lib/ruby_lsp/requests/diagnostics.rb +1 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/formatting.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +2 -2
- data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
- data/lib/ruby_lsp/requests/selection_ranges.rb +2 -1
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
- data/lib/ruby_lsp/requests/signature_help.rb +2 -2
- data/lib/ruby_lsp/requests/support/common.rb +2 -2
- data/lib/ruby_lsp/requests/support/formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +2 -2
- data/lib/ruby_lsp/ruby_document.rb +183 -1
- data/lib/ruby_lsp/server.rb +125 -15
- data/lib/ruby_lsp/setup_bundler.rb +15 -4
- data/lib/ruby_lsp/store.rb +10 -6
- data/lib/ruby_lsp/utils.rb +12 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17c576dbc88c8c4012eb777b15df7df630a827d7692f6bceec06b68178b25357
|
4
|
+
data.tar.gz: f023f22a818616b1c8c6fb1de77d0637e7bdae37b60b46e941b297aaa95729a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0848d63fef1677ccf276182cdea5a8304e4a1e04a7ec4936f8f083c27bc62f575fe3f0a207f75805cf3c8eb106e43d5d1afe2df366028980cb5eee6ed97c14b
|
7
|
+
data.tar.gz: d57869c1ee7dcec65becd9ee4fc225c91e946e697c3aab8a42d2453c1a85bcc91cd8e0a6364d810f8109c2b97102f101d24bf8986d2e4b3905b56368e260b0d3
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.17.
|
1
|
+
0.17.14
|
data/exe/ruby-lsp
CHANGED
@@ -81,6 +81,8 @@ require "ruby_lsp/load_sorbet"
|
|
81
81
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
82
82
|
require "ruby_lsp/internal"
|
83
83
|
|
84
|
+
T::Utils.run_all_sig_blocks
|
85
|
+
|
84
86
|
if options[:debug]
|
85
87
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
86
88
|
$stderr.puts "Debugging is not supported on Windows"
|
@@ -112,20 +114,20 @@ if options[:time_index]
|
|
112
114
|
end
|
113
115
|
|
114
116
|
if options[:doctor]
|
117
|
+
index = RubyIndexer::Index.new
|
118
|
+
|
115
119
|
if File.exist?(".index.yml")
|
116
120
|
begin
|
117
121
|
config = YAML.parse_file(".index.yml").to_ruby
|
118
122
|
rescue => e
|
119
123
|
abort("Error parsing config: #{e.message}")
|
120
124
|
end
|
121
|
-
|
125
|
+
index.configuration.apply_config(config)
|
122
126
|
end
|
123
127
|
|
124
|
-
index = RubyIndexer::Index.new
|
125
|
-
|
126
128
|
puts "Globbing for indexable files"
|
127
129
|
|
128
|
-
|
130
|
+
index.configuration.indexables.each do |indexable|
|
129
131
|
puts "indexing: #{indexable.full_path}"
|
130
132
|
index.index_single(indexable)
|
131
133
|
end
|
data/exe/ruby-lsp-check
CHANGED
@@ -44,7 +44,7 @@ puts "\n"
|
|
44
44
|
puts "Verifying that indexing executes successfully. This may take a while..."
|
45
45
|
|
46
46
|
index = RubyIndexer::Index.new
|
47
|
-
indexables =
|
47
|
+
indexables = index.configuration.indexables
|
48
48
|
|
49
49
|
indexables.each_with_index do |indexable, i|
|
50
50
|
index.index_single(indexable)
|
@@ -552,7 +552,7 @@ module RubyIndexer
|
|
552
552
|
comment_content = comment.location.slice.chomp
|
553
553
|
|
554
554
|
# invalid encodings would raise an "invalid byte sequence" exception
|
555
|
-
if !comment_content.valid_encoding? || comment_content.match?(
|
555
|
+
if !comment_content.valid_encoding? || comment_content.match?(@index.configuration.magic_comment_regex)
|
556
556
|
next
|
557
557
|
end
|
558
558
|
|
@@ -11,6 +11,9 @@ module RubyIndexer
|
|
11
11
|
# The minimum Jaro-Winkler similarity score for an entry to be considered a match for a given fuzzy search query
|
12
12
|
ENTRY_SIMILARITY_THRESHOLD = 0.7
|
13
13
|
|
14
|
+
sig { returns(Configuration) }
|
15
|
+
attr_reader :configuration
|
16
|
+
|
14
17
|
sig { void }
|
15
18
|
def initialize
|
16
19
|
# Holds all entries in the index using the following format:
|
@@ -44,6 +47,8 @@ module RubyIndexer
|
|
44
47
|
{},
|
45
48
|
T::Hash[String, T::Array[T.proc.params(index: Index, base: Entry::Namespace).void]],
|
46
49
|
)
|
50
|
+
|
51
|
+
@configuration = T.let(RubyIndexer::Configuration.new, Configuration)
|
47
52
|
end
|
48
53
|
|
49
54
|
# Register an enhancement to the index. Enhancements must conform to the `Enhancement` interface
|
@@ -296,7 +301,7 @@ module RubyIndexer
|
|
296
301
|
block: T.nilable(T.proc.params(progress: Integer).returns(T::Boolean)),
|
297
302
|
).void
|
298
303
|
end
|
299
|
-
def index_all(indexable_paths:
|
304
|
+
def index_all(indexable_paths: @configuration.indexables, &block)
|
300
305
|
RBSIndexer.new(self).index_ruby_core
|
301
306
|
# Calculate how many paths are worth 1% of progress
|
302
307
|
progress_step = (indexable_paths.length / 100.0).ceil
|
@@ -15,12 +15,4 @@ require "ruby_indexer/lib/ruby_indexer/location"
|
|
15
15
|
require "ruby_indexer/lib/ruby_indexer/rbs_indexer"
|
16
16
|
|
17
17
|
module RubyIndexer
|
18
|
-
@configuration = T.let(Configuration.new, Configuration)
|
19
|
-
|
20
|
-
class << self
|
21
|
-
extend T::Sig
|
22
|
-
|
23
|
-
sig { returns(Configuration) }
|
24
|
-
attr_reader :configuration
|
25
|
-
end
|
26
18
|
end
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -49,15 +49,18 @@ module RubyLsp
|
|
49
49
|
super
|
50
50
|
end
|
51
51
|
|
52
|
-
# Discovers and loads all addons. Returns
|
53
|
-
sig
|
52
|
+
# Discovers and loads all addons. Returns a list of errors when trying to require addons
|
53
|
+
sig do
|
54
|
+
params(global_state: GlobalState, outgoing_queue: Thread::Queue).returns(T::Array[StandardError])
|
55
|
+
end
|
54
56
|
def load_addons(global_state, outgoing_queue)
|
55
57
|
# Require all addons entry points, which should be placed under
|
56
58
|
# `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
|
57
|
-
Gem.find_files("ruby_lsp/**/addon.rb").
|
59
|
+
errors = Gem.find_files("ruby_lsp/**/addon.rb").filter_map do |addon|
|
58
60
|
require File.expand_path(addon)
|
61
|
+
nil
|
59
62
|
rescue => e
|
60
|
-
|
63
|
+
e
|
61
64
|
end
|
62
65
|
|
63
66
|
# Instantiate all discovered addon classes
|
@@ -71,6 +74,8 @@ module RubyLsp
|
|
71
74
|
rescue => e
|
72
75
|
addon.add_error(e)
|
73
76
|
end
|
77
|
+
|
78
|
+
errors
|
74
79
|
end
|
75
80
|
|
76
81
|
# Intended for use by tests for addons
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -65,7 +65,7 @@ module RubyLsp
|
|
65
65
|
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
66
66
|
process_message(message)
|
67
67
|
when "shutdown"
|
68
|
-
|
68
|
+
send_log_message("Shutting down Ruby LSP...")
|
69
69
|
|
70
70
|
shutdown
|
71
71
|
|
@@ -76,7 +76,7 @@ module RubyLsp
|
|
76
76
|
when "exit"
|
77
77
|
@mutex.synchronize do
|
78
78
|
status = @incoming_queue.closed? ? 0 : 1
|
79
|
-
|
79
|
+
send_log_message("Shutdown complete with status #{status}")
|
80
80
|
exit(status)
|
81
81
|
end
|
82
82
|
else
|
@@ -145,5 +145,10 @@ module RubyLsp
|
|
145
145
|
def send_empty_response(id)
|
146
146
|
send_message(Result.new(id: id, response: nil))
|
147
147
|
end
|
148
|
+
|
149
|
+
sig { params(message: String, type: Integer).void }
|
150
|
+
def send_log_message(message, type: Constant::MessageType::LOG)
|
151
|
+
send_message(Notification.window_log_message(message, type: Constant::MessageType::LOG))
|
152
|
+
end
|
148
153
|
end
|
149
154
|
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -7,25 +7,19 @@ module RubyLsp
|
|
7
7
|
enums do
|
8
8
|
Ruby = new("ruby")
|
9
9
|
ERB = new("erb")
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
class SorbetLevel < T::Enum
|
14
|
-
enums do
|
15
|
-
None = new("none")
|
16
|
-
Ignore = new("ignore")
|
17
|
-
False = new("false")
|
18
|
-
True = new("true")
|
19
|
-
Strict = new("strict")
|
10
|
+
RBS = new("rbs")
|
20
11
|
end
|
21
12
|
end
|
22
13
|
|
23
14
|
extend T::Sig
|
24
15
|
extend T::Helpers
|
16
|
+
extend T::Generic
|
17
|
+
|
18
|
+
ParseResultType = type_member
|
25
19
|
|
26
20
|
abstract!
|
27
21
|
|
28
|
-
sig { returns(
|
22
|
+
sig { returns(ParseResultType) }
|
29
23
|
attr_reader :parse_result
|
30
24
|
|
31
25
|
sig { returns(String) }
|
@@ -48,10 +42,10 @@ module RubyLsp
|
|
48
42
|
@version = T.let(version, Integer)
|
49
43
|
@uri = T.let(uri, URI::Generic)
|
50
44
|
@needs_parsing = T.let(true, T::Boolean)
|
51
|
-
@parse_result = T.let(parse,
|
45
|
+
@parse_result = T.let(parse, ParseResultType)
|
52
46
|
end
|
53
47
|
|
54
|
-
sig { params(other: Document).returns(T::Boolean) }
|
48
|
+
sig { params(other: Document[T.untyped]).returns(T::Boolean) }
|
55
49
|
def ==(other)
|
56
50
|
self.class == other.class && uri == other.uri && @source == other.source
|
57
51
|
end
|
@@ -64,7 +58,7 @@ module RubyLsp
|
|
64
58
|
type_parameters(:T)
|
65
59
|
.params(
|
66
60
|
request_name: String,
|
67
|
-
block: T.proc.params(document: Document).returns(T.type_parameter(:T)),
|
61
|
+
block: T.proc.params(document: Document[ParseResultType]).returns(T.type_parameter(:T)),
|
68
62
|
).returns(T.type_parameter(:T))
|
69
63
|
end
|
70
64
|
def cache_fetch(request_name, &block)
|
@@ -103,7 +97,7 @@ module RubyLsp
|
|
103
97
|
@cache.clear
|
104
98
|
end
|
105
99
|
|
106
|
-
sig { abstract.returns(
|
100
|
+
sig { abstract.returns(ParseResultType) }
|
107
101
|
def parse; end
|
108
102
|
|
109
103
|
sig { abstract.returns(T::Boolean) }
|
@@ -114,169 +108,6 @@ module RubyLsp
|
|
114
108
|
Scanner.new(@source, @encoding)
|
115
109
|
end
|
116
110
|
|
117
|
-
sig do
|
118
|
-
params(
|
119
|
-
position: T::Hash[Symbol, T.untyped],
|
120
|
-
node_types: T::Array[T.class_of(Prism::Node)],
|
121
|
-
).returns(NodeContext)
|
122
|
-
end
|
123
|
-
def locate_node(position, node_types: [])
|
124
|
-
locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
|
125
|
-
end
|
126
|
-
|
127
|
-
sig do
|
128
|
-
params(
|
129
|
-
node: Prism::Node,
|
130
|
-
char_position: Integer,
|
131
|
-
node_types: T::Array[T.class_of(Prism::Node)],
|
132
|
-
).returns(NodeContext)
|
133
|
-
end
|
134
|
-
def locate(node, char_position, node_types: [])
|
135
|
-
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
|
136
|
-
closest = node
|
137
|
-
parent = T.let(nil, T.nilable(Prism::Node))
|
138
|
-
nesting_nodes = T.let(
|
139
|
-
[],
|
140
|
-
T::Array[T.any(
|
141
|
-
Prism::ClassNode,
|
142
|
-
Prism::ModuleNode,
|
143
|
-
Prism::SingletonClassNode,
|
144
|
-
Prism::DefNode,
|
145
|
-
Prism::BlockNode,
|
146
|
-
Prism::LambdaNode,
|
147
|
-
Prism::ProgramNode,
|
148
|
-
)],
|
149
|
-
)
|
150
|
-
|
151
|
-
nesting_nodes << node if node.is_a?(Prism::ProgramNode)
|
152
|
-
call_node = T.let(nil, T.nilable(Prism::CallNode))
|
153
|
-
|
154
|
-
until queue.empty?
|
155
|
-
candidate = queue.shift
|
156
|
-
|
157
|
-
# Skip nil child nodes
|
158
|
-
next if candidate.nil?
|
159
|
-
|
160
|
-
# Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
|
161
|
-
# same order as the visiting mechanism, which means searching the child nodes before moving on to the next
|
162
|
-
# sibling
|
163
|
-
T.unsafe(queue).unshift(*candidate.child_nodes)
|
164
|
-
|
165
|
-
# Skip if the current node doesn't cover the desired position
|
166
|
-
loc = candidate.location
|
167
|
-
next unless (loc.start_offset...loc.end_offset).cover?(char_position)
|
168
|
-
|
169
|
-
# If the node's start character is already past the position, then we should've found the closest node
|
170
|
-
# already
|
171
|
-
break if char_position < loc.start_offset
|
172
|
-
|
173
|
-
# If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
|
174
|
-
# need to pop the stack
|
175
|
-
previous_level = nesting_nodes.last
|
176
|
-
nesting_nodes.pop if previous_level && loc.start_offset > previous_level.location.end_offset
|
177
|
-
|
178
|
-
# Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
|
179
|
-
# target when it is a constant
|
180
|
-
case candidate
|
181
|
-
when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode, Prism::BlockNode,
|
182
|
-
Prism::LambdaNode
|
183
|
-
nesting_nodes << candidate
|
184
|
-
end
|
185
|
-
|
186
|
-
if candidate.is_a?(Prism::CallNode)
|
187
|
-
arg_loc = candidate.arguments&.location
|
188
|
-
blk_loc = candidate.block&.location
|
189
|
-
if (arg_loc && (arg_loc.start_offset...arg_loc.end_offset).cover?(char_position)) ||
|
190
|
-
(blk_loc && (blk_loc.start_offset...blk_loc.end_offset).cover?(char_position))
|
191
|
-
call_node = candidate
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# If there are node types to filter by, and the current node is not one of those types, then skip it
|
196
|
-
next if node_types.any? && node_types.none? { |type| candidate.class == type }
|
197
|
-
|
198
|
-
# If the current node is narrower than or equal to the previous closest node, then it is more precise
|
199
|
-
closest_loc = closest.location
|
200
|
-
if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
|
201
|
-
parent = closest
|
202
|
-
closest = candidate
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
# When targeting the constant part of a class/module definition, we do not want the nesting to be duplicated. That
|
207
|
-
# is, when targeting Bar in the following example:
|
208
|
-
#
|
209
|
-
# ```ruby
|
210
|
-
# class Foo::Bar; end
|
211
|
-
# ```
|
212
|
-
# The correct target is `Foo::Bar` with an empty nesting. `Foo::Bar` should not appear in the nesting stack, even
|
213
|
-
# though the class/module node does indeed enclose the target, because it would lead to incorrect behavior
|
214
|
-
if closest.is_a?(Prism::ConstantReadNode) || closest.is_a?(Prism::ConstantPathNode)
|
215
|
-
last_level = nesting_nodes.last
|
216
|
-
|
217
|
-
if (last_level.is_a?(Prism::ModuleNode) || last_level.is_a?(Prism::ClassNode)) &&
|
218
|
-
last_level.constant_path == closest
|
219
|
-
nesting_nodes.pop
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
NodeContext.new(closest, parent, nesting_nodes, call_node)
|
224
|
-
end
|
225
|
-
|
226
|
-
sig do
|
227
|
-
params(
|
228
|
-
range: T::Hash[Symbol, T.untyped],
|
229
|
-
node_types: T::Array[T.class_of(Prism::Node)],
|
230
|
-
).returns(T.nilable(Prism::Node))
|
231
|
-
end
|
232
|
-
def locate_first_within_range(range, node_types: [])
|
233
|
-
scanner = create_scanner
|
234
|
-
start_position = scanner.find_char_position(range[:start])
|
235
|
-
end_position = scanner.find_char_position(range[:end])
|
236
|
-
desired_range = (start_position...end_position)
|
237
|
-
queue = T.let(@parse_result.value.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
|
238
|
-
|
239
|
-
until queue.empty?
|
240
|
-
candidate = queue.shift
|
241
|
-
|
242
|
-
# Skip nil child nodes
|
243
|
-
next if candidate.nil?
|
244
|
-
|
245
|
-
# Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
|
246
|
-
# same order as the visiting mechanism, which means searching the child nodes before moving on to the next
|
247
|
-
# sibling
|
248
|
-
T.unsafe(queue).unshift(*candidate.child_nodes)
|
249
|
-
|
250
|
-
# Skip if the current node doesn't cover the desired position
|
251
|
-
loc = candidate.location
|
252
|
-
|
253
|
-
if desired_range.cover?(loc.start_offset...loc.end_offset) &&
|
254
|
-
(node_types.empty? || node_types.any? { |type| candidate.class == type })
|
255
|
-
return candidate
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
sig { returns(SorbetLevel) }
|
261
|
-
def sorbet_level
|
262
|
-
sigil = parse_result.magic_comments.find do |comment|
|
263
|
-
comment.key == "typed"
|
264
|
-
end&.value
|
265
|
-
|
266
|
-
case sigil
|
267
|
-
when "ignore"
|
268
|
-
SorbetLevel::Ignore
|
269
|
-
when "false"
|
270
|
-
SorbetLevel::False
|
271
|
-
when "true"
|
272
|
-
SorbetLevel::True
|
273
|
-
when "strict", "strong"
|
274
|
-
SorbetLevel::Strict
|
275
|
-
else
|
276
|
-
SorbetLevel::None
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
111
|
class Scanner
|
281
112
|
extend T::Sig
|
282
113
|
|
@@ -4,15 +4,19 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
class ERBDocument < Document
|
6
6
|
extend T::Sig
|
7
|
+
extend T::Generic
|
7
8
|
|
8
|
-
|
9
|
+
ParseResultType = type_member { { fixed: Prism::ParseResult } }
|
10
|
+
|
11
|
+
sig { override.returns(ParseResultType) }
|
9
12
|
def parse
|
10
13
|
return @parse_result unless @needs_parsing
|
11
14
|
|
12
15
|
@needs_parsing = false
|
13
16
|
scanner = ERBScanner.new(@source)
|
14
17
|
scanner.scan
|
15
|
-
|
18
|
+
# assigning empty scopes to turn Prism into eval mode
|
19
|
+
@parse_result = Prism.parse(scanner.ruby, scopes: [[]])
|
16
20
|
end
|
17
21
|
|
18
22
|
sig { override.returns(T::Boolean) }
|
@@ -25,6 +29,16 @@ module RubyLsp
|
|
25
29
|
LanguageId::ERB
|
26
30
|
end
|
27
31
|
|
32
|
+
sig do
|
33
|
+
params(
|
34
|
+
position: T::Hash[Symbol, T.untyped],
|
35
|
+
node_types: T::Array[T.class_of(Prism::Node)],
|
36
|
+
).returns(NodeContext)
|
37
|
+
end
|
38
|
+
def locate_node(position, node_types: [])
|
39
|
+
RubyDocument.locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
|
40
|
+
end
|
41
|
+
|
28
42
|
class ERBScanner
|
29
43
|
extend T::Sig
|
30
44
|
|
@@ -57,21 +57,48 @@ module RubyLsp
|
|
57
57
|
@linters.filter_map { |name| @supported_formatters[name] }
|
58
58
|
end
|
59
59
|
|
60
|
-
|
60
|
+
# Applies the options provided by the editor and returns an array of notifications to send back to the client
|
61
|
+
sig { params(options: T::Hash[Symbol, T.untyped]).returns(T::Array[Notification]) }
|
61
62
|
def apply_options(options)
|
63
|
+
notifications = []
|
62
64
|
direct_dependencies = gather_direct_dependencies
|
63
65
|
all_dependencies = gather_direct_and_indirect_dependencies
|
64
66
|
workspace_uri = options.dig(:workspaceFolders, 0, :uri)
|
65
67
|
@workspace_uri = URI(workspace_uri) if workspace_uri
|
66
68
|
|
67
69
|
specified_formatter = options.dig(:initializationOptions, :formatter)
|
68
|
-
|
69
|
-
|
70
|
+
|
71
|
+
if specified_formatter
|
72
|
+
@formatter = specified_formatter
|
73
|
+
|
74
|
+
if specified_formatter != "auto"
|
75
|
+
notifications << Notification.window_log_message("Using formatter specified by user: #{@formatter}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
if @formatter == "auto"
|
80
|
+
@formatter = detect_formatter(direct_dependencies, all_dependencies)
|
81
|
+
notifications << Notification.window_log_message("Auto detected formatter: #{@formatter}")
|
82
|
+
end
|
70
83
|
|
71
84
|
specified_linters = options.dig(:initializationOptions, :linters)
|
72
85
|
@linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)
|
86
|
+
|
87
|
+
notifications << if specified_linters
|
88
|
+
Notification.window_log_message("Using linters specified by user: #{@linters.join(", ")}")
|
89
|
+
else
|
90
|
+
Notification.window_log_message("Auto detected linters: #{@linters.join(", ")}")
|
91
|
+
end
|
92
|
+
|
73
93
|
@test_library = detect_test_library(direct_dependencies)
|
74
|
-
|
94
|
+
notifications << Notification.window_log_message("Detected test library: #{@test_library}")
|
95
|
+
|
96
|
+
@has_type_checker = detect_typechecker(all_dependencies)
|
97
|
+
if @has_type_checker
|
98
|
+
notifications << Notification.window_log_message(
|
99
|
+
"Ruby LSP detected this is a Sorbet project and will defer to the Sorbet LSP for some functionality",
|
100
|
+
)
|
101
|
+
end
|
75
102
|
|
76
103
|
encodings = options.dig(:capabilities, :general, :positionEncodings)
|
77
104
|
@encoding = if !encodings || encodings.empty?
|
@@ -91,6 +118,8 @@ module RubyLsp
|
|
91
118
|
|
92
119
|
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
93
120
|
@type_inferrer.experimental_features = @experimental_features
|
121
|
+
|
122
|
+
notifications
|
94
123
|
end
|
95
124
|
|
96
125
|
sig { returns(String) }
|
@@ -163,16 +192,7 @@ module RubyLsp
|
|
163
192
|
def detect_typechecker(dependencies)
|
164
193
|
return false if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]
|
165
194
|
|
166
|
-
|
167
|
-
ruby_lsp_env_is_test = (ENV["RUBY_LSP_ENV"] == "test")
|
168
|
-
Bundler.with_original_env do
|
169
|
-
sorbet_static_detected = dependencies.any?(/^sorbet-static/)
|
170
|
-
# Don't show message while running tests, since it's noisy
|
171
|
-
if sorbet_static_detected && !ruby_lsp_env_is_test
|
172
|
-
$stderr.puts("Ruby LSP detected this is a Sorbet project so will defer to Sorbet LSP for some functionality")
|
173
|
-
end
|
174
|
-
sorbet_static_detected
|
175
|
-
end
|
195
|
+
dependencies.any?(/^sorbet-static/)
|
176
196
|
rescue Bundler::GemfileNotFound
|
177
197
|
false
|
178
198
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -36,6 +36,7 @@ require "ruby_lsp/node_context"
|
|
36
36
|
require "ruby_lsp/document"
|
37
37
|
require "ruby_lsp/ruby_document"
|
38
38
|
require "ruby_lsp/erb_document"
|
39
|
+
require "ruby_lsp/rbs_document"
|
39
40
|
require "ruby_lsp/store"
|
40
41
|
require "ruby_lsp/addon"
|
41
42
|
require "ruby_lsp/requests/support/rubocop_runner"
|
@@ -56,7 +56,7 @@ module RubyLsp
|
|
56
56
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
57
57
|
global_state: GlobalState,
|
58
58
|
node_context: NodeContext,
|
59
|
-
sorbet_level:
|
59
|
+
sorbet_level: RubyDocument::SorbetLevel,
|
60
60
|
dispatcher: Prism::Dispatcher,
|
61
61
|
uri: URI::Generic,
|
62
62
|
trigger_character: T.nilable(String),
|
@@ -99,7 +99,7 @@ module RubyLsp
|
|
99
99
|
def on_constant_read_node_enter(node)
|
100
100
|
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
101
101
|
# no sigil, Sorbet will still provide completion for constants
|
102
|
-
return if @sorbet_level !=
|
102
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
103
103
|
|
104
104
|
name = constant_name(node)
|
105
105
|
return if name.nil?
|
@@ -122,7 +122,7 @@ module RubyLsp
|
|
122
122
|
def on_constant_path_node_enter(node)
|
123
123
|
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
124
124
|
# no sigil, Sorbet will still provide completion for constants
|
125
|
-
return if @sorbet_level !=
|
125
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
126
126
|
|
127
127
|
name = constant_name(node)
|
128
128
|
return if name.nil?
|
@@ -134,7 +134,7 @@ module RubyLsp
|
|
134
134
|
def on_call_node_enter(node)
|
135
135
|
# The only scenario where Sorbet doesn't provide constant completion is on ignored files. Even if the file has
|
136
136
|
# no sigil, Sorbet will still provide completion for constants
|
137
|
-
if @sorbet_level ==
|
137
|
+
if @sorbet_level == RubyDocument::SorbetLevel::Ignore
|
138
138
|
receiver = node.receiver
|
139
139
|
|
140
140
|
# When writing `Foo::`, the AST assigns a method call node (because you can use that syntax to invoke
|
@@ -257,7 +257,7 @@ module RubyLsp
|
|
257
257
|
def handle_instance_variable_completion(name, location)
|
258
258
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
259
259
|
# to provide all features for them
|
260
|
-
return if @sorbet_level ==
|
260
|
+
return if @sorbet_level == RubyDocument::SorbetLevel::Strict
|
261
261
|
|
262
262
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
263
263
|
return unless type
|
@@ -20,7 +20,7 @@ module RubyLsp
|
|
20
20
|
uri: URI::Generic,
|
21
21
|
node_context: NodeContext,
|
22
22
|
dispatcher: Prism::Dispatcher,
|
23
|
-
sorbet_level:
|
23
|
+
sorbet_level: RubyDocument::SorbetLevel,
|
24
24
|
).void
|
25
25
|
end
|
26
26
|
def initialize(response_builder, global_state, language_id, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
@@ -181,7 +181,7 @@ module RubyLsp
|
|
181
181
|
def handle_instance_variable_definition(name)
|
182
182
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
183
183
|
# to provide all features for them
|
184
|
-
return if @sorbet_level ==
|
184
|
+
return if @sorbet_level == RubyDocument::SorbetLevel::Strict
|
185
185
|
|
186
186
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
187
187
|
return unless type
|
@@ -289,7 +289,7 @@ module RubyLsp
|
|
289
289
|
# additional behavior on top of jumping to RBIs. The only sigil where Sorbet cannot handle constants is typed
|
290
290
|
# ignore
|
291
291
|
file_path = entry.file_path
|
292
|
-
next if @sorbet_level !=
|
292
|
+
next if @sorbet_level != RubyDocument::SorbetLevel::Ignore && not_in_dependencies?(file_path)
|
293
293
|
|
294
294
|
@response_builder << Interface::LocationLink.new(
|
295
295
|
target_uri: URI::Generic.from_path(path: file_path).to_s,
|
@@ -42,7 +42,7 @@ module RubyLsp
|
|
42
42
|
uri: URI::Generic,
|
43
43
|
node_context: NodeContext,
|
44
44
|
dispatcher: Prism::Dispatcher,
|
45
|
-
sorbet_level:
|
45
|
+
sorbet_level: RubyDocument::SorbetLevel,
|
46
46
|
).void
|
47
47
|
end
|
48
48
|
def initialize(response_builder, global_state, uri, node_context, dispatcher, sorbet_level) # rubocop:disable Metrics/ParameterLists
|
@@ -73,7 +73,7 @@ module RubyLsp
|
|
73
73
|
|
74
74
|
sig { params(node: Prism::ConstantReadNode).void }
|
75
75
|
def on_constant_read_node_enter(node)
|
76
|
-
return if @sorbet_level !=
|
76
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
77
77
|
|
78
78
|
name = constant_name(node)
|
79
79
|
return if name.nil?
|
@@ -83,14 +83,14 @@ module RubyLsp
|
|
83
83
|
|
84
84
|
sig { params(node: Prism::ConstantWriteNode).void }
|
85
85
|
def on_constant_write_node_enter(node)
|
86
|
-
return if @sorbet_level !=
|
86
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
87
87
|
|
88
88
|
generate_hover(node.name.to_s, node.name_loc)
|
89
89
|
end
|
90
90
|
|
91
91
|
sig { params(node: Prism::ConstantPathNode).void }
|
92
92
|
def on_constant_path_node_enter(node)
|
93
|
-
return if @sorbet_level !=
|
93
|
+
return if @sorbet_level != RubyDocument::SorbetLevel::Ignore
|
94
94
|
|
95
95
|
name = constant_name(node)
|
96
96
|
return if name.nil?
|
@@ -193,7 +193,7 @@ module RubyLsp
|
|
193
193
|
def handle_instance_variable_hover(name)
|
194
194
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
195
195
|
# to provide all features for them
|
196
|
-
return if @sorbet_level ==
|
196
|
+
return if @sorbet_level == RubyDocument::SorbetLevel::Strict
|
197
197
|
|
198
198
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
199
199
|
return unless type
|
@@ -13,7 +13,7 @@ module RubyLsp
|
|
13
13
|
global_state: GlobalState,
|
14
14
|
node_context: NodeContext,
|
15
15
|
dispatcher: Prism::Dispatcher,
|
16
|
-
sorbet_level:
|
16
|
+
sorbet_level: RubyDocument::SorbetLevel,
|
17
17
|
).void
|
18
18
|
end
|
19
19
|
def initialize(response_builder, global_state, node_context, dispatcher, sorbet_level)
|