ruby-lsp 0.17.13 → 0.17.15
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 +2 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +28 -9
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +5 -3
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +21 -0
- data/lib/ruby_indexer/test/configuration_test.rb +41 -7
- data/lib/ruby_lsp/document.rb +9 -114
- data/lib/ruby_lsp/erb_document.rb +16 -2
- data/lib/ruby_lsp/global_state.rb +1 -1
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listeners/definition.rb +8 -5
- data/lib/ruby_lsp/rbs_document.rb +41 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +34 -18
- data/lib/ruby_lsp/requests/code_actions.rb +1 -1
- data/lib/ruby_lsp/requests/completion.rb +2 -2
- data/lib/ruby_lsp/requests/definition.rb +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +119 -1
- data/lib/ruby_lsp/server.rb +96 -8
- data/lib/ruby_lsp/setup_bundler.rb +18 -7
- data/lib/ruby_lsp/store.rb +10 -6
- metadata +3 -2
@@ -23,7 +23,8 @@ module RubyLsp
|
|
23
23
|
class SelectionRanges < Request
|
24
24
|
extend T::Sig
|
25
25
|
include Support::Common
|
26
|
-
|
26
|
+
|
27
|
+
sig { params(document: T.any(RubyDocument, ERBDocument)).void }
|
27
28
|
def initialize(document)
|
28
29
|
super()
|
29
30
|
@document = document
|
@@ -20,7 +20,7 @@ module RubyLsp
|
|
20
20
|
class ShowSyntaxTree < Request
|
21
21
|
extend T::Sig
|
22
22
|
|
23
|
-
sig { params(document:
|
23
|
+
sig { params(document: RubyDocument, range: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
24
24
|
def initialize(document, range)
|
25
25
|
super()
|
26
26
|
@document = document
|
@@ -10,13 +10,13 @@ module RubyLsp
|
|
10
10
|
|
11
11
|
interface!
|
12
12
|
|
13
|
-
sig { abstract.params(uri: URI::Generic, document:
|
13
|
+
sig { abstract.params(uri: URI::Generic, document: RubyDocument).returns(T.nilable(String)) }
|
14
14
|
def run_formatting(uri, document); end
|
15
15
|
|
16
16
|
sig do
|
17
17
|
abstract.params(
|
18
18
|
uri: URI::Generic,
|
19
|
-
document:
|
19
|
+
document: RubyDocument,
|
20
20
|
).returns(T.nilable(T::Array[Interface::Diagnostic]))
|
21
21
|
end
|
22
22
|
def run_diagnostic(uri, document); end
|
@@ -31,7 +31,7 @@ module RubyLsp
|
|
31
31
|
|
32
32
|
# TODO: avoid passing document once we have alternative ways to get at
|
33
33
|
# encoding and file source
|
34
|
-
sig { params(document:
|
34
|
+
sig { params(document: RubyDocument, offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
|
35
35
|
def initialize(document, offense, uri)
|
36
36
|
@document = document
|
37
37
|
@offense = offense
|
@@ -17,7 +17,7 @@ module RubyLsp
|
|
17
17
|
@format_runner = T.let(RuboCopRunner.new("-a"), RuboCopRunner)
|
18
18
|
end
|
19
19
|
|
20
|
-
sig { override.params(uri: URI::Generic, document:
|
20
|
+
sig { override.params(uri: URI::Generic, document: RubyDocument).returns(T.nilable(String)) }
|
21
21
|
def run_formatting(uri, document)
|
22
22
|
filename = T.must(uri.to_standardized_path || uri.opaque)
|
23
23
|
|
@@ -29,7 +29,7 @@ module RubyLsp
|
|
29
29
|
sig do
|
30
30
|
override.params(
|
31
31
|
uri: URI::Generic,
|
32
|
-
document:
|
32
|
+
document: RubyDocument,
|
33
33
|
).returns(T.nilable(T::Array[Interface::Diagnostic]))
|
34
34
|
end
|
35
35
|
def run_diagnostic(uri, document)
|
@@ -29,7 +29,7 @@ module RubyLsp
|
|
29
29
|
)
|
30
30
|
end
|
31
31
|
|
32
|
-
sig { override.params(uri: URI::Generic, document:
|
32
|
+
sig { override.params(uri: URI::Generic, document: RubyDocument).returns(T.nilable(String)) }
|
33
33
|
def run_formatting(uri, document)
|
34
34
|
path = uri.to_standardized_path
|
35
35
|
return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
|
@@ -40,7 +40,7 @@ module RubyLsp
|
|
40
40
|
sig do
|
41
41
|
override.params(
|
42
42
|
uri: URI::Generic,
|
43
|
-
document:
|
43
|
+
document: RubyDocument,
|
44
44
|
).returns(T.nilable(T::Array[Interface::Diagnostic]))
|
45
45
|
end
|
46
46
|
def run_diagnostic(uri, document)
|
@@ -3,6 +3,11 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
class RubyDocument < Document
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Generic
|
8
|
+
|
9
|
+
ParseResultType = type_member { { fixed: Prism::ParseResult } }
|
10
|
+
|
6
11
|
class SorbetLevel < T::Enum
|
7
12
|
enums do
|
8
13
|
None = new("none")
|
@@ -13,7 +18,110 @@ module RubyLsp
|
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
|
-
|
21
|
+
class << self
|
22
|
+
extend T::Sig
|
23
|
+
|
24
|
+
sig do
|
25
|
+
params(
|
26
|
+
node: Prism::Node,
|
27
|
+
char_position: Integer,
|
28
|
+
node_types: T::Array[T.class_of(Prism::Node)],
|
29
|
+
).returns(NodeContext)
|
30
|
+
end
|
31
|
+
def locate(node, char_position, node_types: [])
|
32
|
+
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
|
33
|
+
closest = node
|
34
|
+
parent = T.let(nil, T.nilable(Prism::Node))
|
35
|
+
nesting_nodes = T.let(
|
36
|
+
[],
|
37
|
+
T::Array[T.any(
|
38
|
+
Prism::ClassNode,
|
39
|
+
Prism::ModuleNode,
|
40
|
+
Prism::SingletonClassNode,
|
41
|
+
Prism::DefNode,
|
42
|
+
Prism::BlockNode,
|
43
|
+
Prism::LambdaNode,
|
44
|
+
Prism::ProgramNode,
|
45
|
+
)],
|
46
|
+
)
|
47
|
+
|
48
|
+
nesting_nodes << node if node.is_a?(Prism::ProgramNode)
|
49
|
+
call_node = T.let(nil, T.nilable(Prism::CallNode))
|
50
|
+
|
51
|
+
until queue.empty?
|
52
|
+
candidate = queue.shift
|
53
|
+
|
54
|
+
# Skip nil child nodes
|
55
|
+
next if candidate.nil?
|
56
|
+
|
57
|
+
# Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
|
58
|
+
# same order as the visiting mechanism, which means searching the child nodes before moving on to the next
|
59
|
+
# sibling
|
60
|
+
T.unsafe(queue).unshift(*candidate.child_nodes)
|
61
|
+
|
62
|
+
# Skip if the current node doesn't cover the desired position
|
63
|
+
loc = candidate.location
|
64
|
+
next unless (loc.start_offset...loc.end_offset).cover?(char_position)
|
65
|
+
|
66
|
+
# If the node's start character is already past the position, then we should've found the closest node
|
67
|
+
# already
|
68
|
+
break if char_position < loc.start_offset
|
69
|
+
|
70
|
+
# If the candidate starts after the end of the previous nesting level, then we've exited that nesting level
|
71
|
+
# and need to pop the stack
|
72
|
+
previous_level = nesting_nodes.last
|
73
|
+
nesting_nodes.pop if previous_level && loc.start_offset > previous_level.location.end_offset
|
74
|
+
|
75
|
+
# Keep track of the nesting where we found the target. This is used to determine the fully qualified name of
|
76
|
+
# the target when it is a constant
|
77
|
+
case candidate
|
78
|
+
when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode, Prism::BlockNode,
|
79
|
+
Prism::LambdaNode
|
80
|
+
nesting_nodes << candidate
|
81
|
+
end
|
82
|
+
|
83
|
+
if candidate.is_a?(Prism::CallNode)
|
84
|
+
arg_loc = candidate.arguments&.location
|
85
|
+
blk_loc = candidate.block&.location
|
86
|
+
if (arg_loc && (arg_loc.start_offset...arg_loc.end_offset).cover?(char_position)) ||
|
87
|
+
(blk_loc && (blk_loc.start_offset...blk_loc.end_offset).cover?(char_position))
|
88
|
+
call_node = candidate
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# If there are node types to filter by, and the current node is not one of those types, then skip it
|
93
|
+
next if node_types.any? && node_types.none? { |type| candidate.class == type }
|
94
|
+
|
95
|
+
# If the current node is narrower than or equal to the previous closest node, then it is more precise
|
96
|
+
closest_loc = closest.location
|
97
|
+
if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
|
98
|
+
parent = closest
|
99
|
+
closest = candidate
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# When targeting the constant part of a class/module definition, we do not want the nesting to be duplicated.
|
104
|
+
# That is, when targeting Bar in the following example:
|
105
|
+
#
|
106
|
+
# ```ruby
|
107
|
+
# class Foo::Bar; end
|
108
|
+
# ```
|
109
|
+
# The correct target is `Foo::Bar` with an empty nesting. `Foo::Bar` should not appear in the nesting stack,
|
110
|
+
# even though the class/module node does indeed enclose the target, because it would lead to incorrect behavior
|
111
|
+
if closest.is_a?(Prism::ConstantReadNode) || closest.is_a?(Prism::ConstantPathNode)
|
112
|
+
last_level = nesting_nodes.last
|
113
|
+
|
114
|
+
if (last_level.is_a?(Prism::ModuleNode) || last_level.is_a?(Prism::ClassNode)) &&
|
115
|
+
last_level.constant_path == closest
|
116
|
+
nesting_nodes.pop
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
NodeContext.new(closest, parent, nesting_nodes, call_node)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
sig { override.returns(ParseResultType) }
|
17
125
|
def parse
|
18
126
|
return @parse_result unless @needs_parsing
|
19
127
|
|
@@ -84,5 +192,15 @@ module RubyLsp
|
|
84
192
|
end
|
85
193
|
end
|
86
194
|
end
|
195
|
+
|
196
|
+
sig do
|
197
|
+
params(
|
198
|
+
position: T::Hash[Symbol, T.untyped],
|
199
|
+
node_types: T::Array[T.class_of(Prism::Node)],
|
200
|
+
).returns(NodeContext)
|
201
|
+
end
|
202
|
+
def locate_node(position, node_types: [])
|
203
|
+
RubyDocument.locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
|
204
|
+
end
|
87
205
|
end
|
88
206
|
end
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -298,9 +298,12 @@ module RubyLsp
|
|
298
298
|
language_id = case text_document[:languageId]
|
299
299
|
when "erb", "eruby"
|
300
300
|
Document::LanguageId::ERB
|
301
|
+
when "rbs"
|
302
|
+
Document::LanguageId::RBS
|
301
303
|
else
|
302
304
|
Document::LanguageId::Ruby
|
303
305
|
end
|
306
|
+
|
304
307
|
@store.set(
|
305
308
|
uri: text_document[:uri],
|
306
309
|
source: text_document[:text],
|
@@ -341,7 +344,12 @@ module RubyLsp
|
|
341
344
|
def text_document_selection_range(message)
|
342
345
|
uri = message.dig(:params, :textDocument, :uri)
|
343
346
|
ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
|
344
|
-
|
347
|
+
case document
|
348
|
+
when RubyDocument, ERBDocument
|
349
|
+
Requests::SelectionRanges.new(document).perform
|
350
|
+
else
|
351
|
+
[]
|
352
|
+
end
|
345
353
|
end
|
346
354
|
|
347
355
|
# Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
|
@@ -363,6 +371,11 @@ module RubyLsp
|
|
363
371
|
uri = URI(message.dig(:params, :textDocument, :uri))
|
364
372
|
document = @store.get(uri)
|
365
373
|
|
374
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
375
|
+
send_empty_response(message[:id])
|
376
|
+
return
|
377
|
+
end
|
378
|
+
|
366
379
|
# If the response has already been cached by another request, return it
|
367
380
|
cached_response = document.cache_get(message[:method])
|
368
381
|
if cached_response
|
@@ -407,6 +420,12 @@ module RubyLsp
|
|
407
420
|
range = params[:range]
|
408
421
|
uri = params.dig(:textDocument, :uri)
|
409
422
|
document = @store.get(uri)
|
423
|
+
|
424
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
425
|
+
send_empty_response(message[:id])
|
426
|
+
return
|
427
|
+
end
|
428
|
+
|
410
429
|
start_line = range.dig(:start, :line)
|
411
430
|
end_line = range.dig(:end, :line)
|
412
431
|
|
@@ -436,6 +455,10 @@ module RubyLsp
|
|
436
455
|
end
|
437
456
|
|
438
457
|
document = @store.get(uri)
|
458
|
+
unless document.is_a?(RubyDocument)
|
459
|
+
send_empty_response(message[:id])
|
460
|
+
return
|
461
|
+
end
|
439
462
|
|
440
463
|
response = Requests::Formatting.new(@global_state, document).perform
|
441
464
|
send_message(Result.new(id: message[:id], response: response))
|
@@ -452,6 +475,12 @@ module RubyLsp
|
|
452
475
|
params = message[:params]
|
453
476
|
dispatcher = Prism::Dispatcher.new
|
454
477
|
document = @store.get(params.dig(:textDocument, :uri))
|
478
|
+
|
479
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
480
|
+
send_empty_response(message[:id])
|
481
|
+
return
|
482
|
+
end
|
483
|
+
|
455
484
|
request = Requests::DocumentHighlight.new(document, params[:position], dispatcher)
|
456
485
|
dispatcher.dispatch(document.parse_result.value)
|
457
486
|
send_message(Result.new(id: message[:id], response: request.perform))
|
@@ -462,6 +491,11 @@ module RubyLsp
|
|
462
491
|
params = message[:params]
|
463
492
|
document = @store.get(params.dig(:textDocument, :uri))
|
464
493
|
|
494
|
+
unless document.is_a?(RubyDocument)
|
495
|
+
send_empty_response(message[:id])
|
496
|
+
return
|
497
|
+
end
|
498
|
+
|
465
499
|
send_message(
|
466
500
|
Result.new(
|
467
501
|
id: message[:id],
|
@@ -481,6 +515,11 @@ module RubyLsp
|
|
481
515
|
dispatcher = Prism::Dispatcher.new
|
482
516
|
document = @store.get(params.dig(:textDocument, :uri))
|
483
517
|
|
518
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
519
|
+
send_empty_response(message[:id])
|
520
|
+
return
|
521
|
+
end
|
522
|
+
|
484
523
|
send_message(
|
485
524
|
Result.new(
|
486
525
|
id: message[:id],
|
@@ -495,7 +534,7 @@ module RubyLsp
|
|
495
534
|
)
|
496
535
|
end
|
497
536
|
|
498
|
-
sig { params(document: Document).returns(RubyDocument::SorbetLevel) }
|
537
|
+
sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
|
499
538
|
def sorbet_level(document)
|
500
539
|
return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
|
501
540
|
return RubyDocument::SorbetLevel::Ignore unless document.is_a?(RubyDocument)
|
@@ -509,6 +548,12 @@ module RubyLsp
|
|
509
548
|
hints_configurations = T.must(@store.features_configuration.dig(:inlayHint))
|
510
549
|
dispatcher = Prism::Dispatcher.new
|
511
550
|
document = @store.get(params.dig(:textDocument, :uri))
|
551
|
+
|
552
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
553
|
+
send_empty_response(message[:id])
|
554
|
+
return
|
555
|
+
end
|
556
|
+
|
512
557
|
request = Requests::InlayHints.new(document, params[:range], hints_configurations, dispatcher)
|
513
558
|
dispatcher.visit(document.parse_result.value)
|
514
559
|
send_message(Result.new(id: message[:id], response: request.perform))
|
@@ -519,6 +564,11 @@ module RubyLsp
|
|
519
564
|
params = message[:params]
|
520
565
|
document = @store.get(params.dig(:textDocument, :uri))
|
521
566
|
|
567
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
568
|
+
send_empty_response(message[:id])
|
569
|
+
return
|
570
|
+
end
|
571
|
+
|
522
572
|
send_message(
|
523
573
|
Result.new(
|
524
574
|
id: message[:id],
|
@@ -581,7 +631,10 @@ module RubyLsp
|
|
581
631
|
document = @store.get(uri)
|
582
632
|
|
583
633
|
response = document.cache_fetch("textDocument/diagnostic") do |document|
|
584
|
-
|
634
|
+
case document
|
635
|
+
when RubyDocument
|
636
|
+
Requests::Diagnostics.new(@global_state, document).perform
|
637
|
+
end
|
585
638
|
end
|
586
639
|
|
587
640
|
send_message(
|
@@ -604,6 +657,11 @@ module RubyLsp
|
|
604
657
|
dispatcher = Prism::Dispatcher.new
|
605
658
|
document = @store.get(params.dig(:textDocument, :uri))
|
606
659
|
|
660
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
661
|
+
send_empty_response(message[:id])
|
662
|
+
return
|
663
|
+
end
|
664
|
+
|
607
665
|
send_message(
|
608
666
|
Result.new(
|
609
667
|
id: message[:id],
|
@@ -632,6 +690,11 @@ module RubyLsp
|
|
632
690
|
dispatcher = Prism::Dispatcher.new
|
633
691
|
document = @store.get(params.dig(:textDocument, :uri))
|
634
692
|
|
693
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
694
|
+
send_empty_response(message[:id])
|
695
|
+
return
|
696
|
+
end
|
697
|
+
|
635
698
|
send_message(
|
636
699
|
Result.new(
|
637
700
|
id: message[:id],
|
@@ -653,6 +716,11 @@ module RubyLsp
|
|
653
716
|
dispatcher = Prism::Dispatcher.new
|
654
717
|
document = @store.get(params.dig(:textDocument, :uri))
|
655
718
|
|
719
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
720
|
+
send_empty_response(message[:id])
|
721
|
+
return
|
722
|
+
end
|
723
|
+
|
656
724
|
send_message(
|
657
725
|
Result.new(
|
658
726
|
id: message[:id],
|
@@ -710,9 +778,16 @@ module RubyLsp
|
|
710
778
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
711
779
|
def text_document_show_syntax_tree(message)
|
712
780
|
params = message[:params]
|
781
|
+
document = @store.get(params.dig(:textDocument, :uri))
|
782
|
+
|
783
|
+
unless document.is_a?(RubyDocument)
|
784
|
+
send_empty_response(message[:id])
|
785
|
+
return
|
786
|
+
end
|
787
|
+
|
713
788
|
response = {
|
714
789
|
ast: Requests::ShowSyntaxTree.new(
|
715
|
-
|
790
|
+
document,
|
716
791
|
params[:range],
|
717
792
|
).perform,
|
718
793
|
}
|
@@ -722,11 +797,19 @@ module RubyLsp
|
|
722
797
|
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
723
798
|
def text_document_prepare_type_hierarchy(message)
|
724
799
|
params = message[:params]
|
800
|
+
document = @store.get(params.dig(:textDocument, :uri))
|
801
|
+
|
802
|
+
unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
803
|
+
send_empty_response(message[:id])
|
804
|
+
return
|
805
|
+
end
|
806
|
+
|
725
807
|
response = Requests::PrepareTypeHierarchy.new(
|
726
|
-
|
808
|
+
document,
|
727
809
|
@global_state.index,
|
728
810
|
params[:position],
|
729
811
|
).perform
|
812
|
+
|
730
813
|
send_message(Result.new(id: message[:id], response: response))
|
731
814
|
end
|
732
815
|
|
@@ -793,6 +876,11 @@ module RubyLsp
|
|
793
876
|
send_message(Notification.window_show_error("Error while indexing: #{error.message}"))
|
794
877
|
end
|
795
878
|
|
879
|
+
# Indexing produces a high number of short lived object allocations. That might lead to some fragmentation and
|
880
|
+
# an unnecessarily expanded heap. Compacting ensures that the heap is as small as possible and that future
|
881
|
+
# allocations and garbage collections are faster
|
882
|
+
GC.compact unless @test_mode
|
883
|
+
|
796
884
|
# Always end the progress notification even if indexing failed or else it never goes away and the user has no
|
797
885
|
# way of dismissing it
|
798
886
|
end_progress("indexing-progress")
|
@@ -912,10 +1000,10 @@ module RubyLsp
|
|
912
1000
|
|
913
1001
|
return unless indexing_options
|
914
1002
|
|
1003
|
+
configuration = @global_state.index.configuration
|
1004
|
+
configuration.workspace_path = @global_state.workspace_path
|
915
1005
|
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
916
|
-
|
917
|
-
indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase },
|
918
|
-
)
|
1006
|
+
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
|
919
1007
|
end
|
920
1008
|
end
|
921
1009
|
end
|
@@ -43,13 +43,14 @@ module RubyLsp
|
|
43
43
|
@gemfile_name = T.let(@gemfile&.basename&.to_s || "Gemfile", String)
|
44
44
|
|
45
45
|
# Custom bundle paths
|
46
|
-
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(
|
46
|
+
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(@project_path), Pathname)
|
47
47
|
@custom_gemfile = T.let(@custom_dir + @gemfile_name, Pathname)
|
48
48
|
@custom_lockfile = T.let(@custom_dir + (@lockfile&.basename || "Gemfile.lock"), Pathname)
|
49
49
|
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
|
50
50
|
@last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
|
51
51
|
|
52
52
|
@dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
|
53
|
+
@rails_app = T.let(rails_app?, T::Boolean)
|
53
54
|
@retry = T.let(false, T::Boolean)
|
54
55
|
end
|
55
56
|
|
@@ -62,7 +63,7 @@ module RubyLsp
|
|
62
63
|
# Do not set up a custom bundle if LSP dependencies are already in the Gemfile
|
63
64
|
if @dependencies["ruby-lsp"] &&
|
64
65
|
@dependencies["debug"] &&
|
65
|
-
(@
|
66
|
+
(@rails_app ? @dependencies["ruby-lsp-rails"] : true)
|
66
67
|
$stderr.puts(
|
67
68
|
"Ruby LSP> Skipping custom bundle setup since LSP dependencies are already in #{@gemfile}",
|
68
69
|
)
|
@@ -148,7 +149,7 @@ module RubyLsp
|
|
148
149
|
parts << 'gem "debug", require: false, group: :development, platforms: :mri'
|
149
150
|
end
|
150
151
|
|
151
|
-
if @
|
152
|
+
if @rails_app && !@dependencies["ruby-lsp-rails"]
|
152
153
|
parts << 'gem "ruby-lsp-rails", require: false, group: :development'
|
153
154
|
end
|
154
155
|
|
@@ -182,14 +183,14 @@ module RubyLsp
|
|
182
183
|
# `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
|
183
184
|
# want to install it in the top level `vendor` and not `.ruby-lsp/vendor`
|
184
185
|
path = Bundler.settings["path"]
|
185
|
-
expanded_path = File.expand_path(path,
|
186
|
+
expanded_path = File.expand_path(path, @project_path) if path
|
186
187
|
|
187
188
|
# Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
|
188
189
|
env = {}
|
189
190
|
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
190
191
|
env["BUNDLE_PATH"] = expanded_path if expanded_path
|
191
192
|
|
192
|
-
local_config_path = File.join(
|
193
|
+
local_config_path = File.join(@project_path, ".bundle")
|
193
194
|
env["BUNDLE_APP_CONFIG"] = local_config_path if Dir.exist?(local_config_path)
|
194
195
|
|
195
196
|
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
|
@@ -209,7 +210,7 @@ module RubyLsp
|
|
209
210
|
command << " && bundle update "
|
210
211
|
command << "ruby-lsp " unless @dependencies["ruby-lsp"]
|
211
212
|
command << "debug " unless @dependencies["debug"]
|
212
|
-
command << "ruby-lsp-rails " if @
|
213
|
+
command << "ruby-lsp-rails " if @rails_app && !@dependencies["ruby-lsp-rails"]
|
213
214
|
command << "--pre" if @experimental
|
214
215
|
command.delete_suffix!(" ")
|
215
216
|
command << ")"
|
@@ -244,7 +245,7 @@ module RubyLsp
|
|
244
245
|
def should_bundle_update?
|
245
246
|
# If `ruby-lsp`, `ruby-lsp-rails` and `debug` are in the Gemfile, then we shouldn't try to upgrade them or else it
|
246
247
|
# will produce version control changes
|
247
|
-
if @
|
248
|
+
if @rails_app
|
248
249
|
return false if @dependencies.values_at("ruby-lsp", "ruby-lsp-rails", "debug").all?
|
249
250
|
|
250
251
|
# If the custom lockfile doesn't include `ruby-lsp`, `ruby-lsp-rails` or `debug`, we need to run bundle install
|
@@ -280,5 +281,15 @@ module RubyLsp
|
|
280
281
|
|
281
282
|
@custom_lockfile.write(content)
|
282
283
|
end
|
284
|
+
|
285
|
+
# Detects if the project is a Rails app by looking if the superclass of the main class is `Rails::Application`
|
286
|
+
sig { returns(T::Boolean) }
|
287
|
+
def rails_app?
|
288
|
+
config = Pathname.new("config/application.rb").expand_path
|
289
|
+
application_contents = config.read(external_encoding: Encoding::UTF_8) if config.exist?
|
290
|
+
return false unless application_contents
|
291
|
+
|
292
|
+
/class .* < (::)?Rails::Application/.match?(application_contents)
|
293
|
+
end
|
283
294
|
end
|
284
295
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -18,7 +18,7 @@ module RubyLsp
|
|
18
18
|
|
19
19
|
sig { void }
|
20
20
|
def initialize
|
21
|
-
@state = T.let({}, T::Hash[String, Document])
|
21
|
+
@state = T.let({}, T::Hash[String, Document[T.untyped]])
|
22
22
|
@supports_progress = T.let(true, T::Boolean)
|
23
23
|
@features_configuration = T.let(
|
24
24
|
{
|
@@ -33,7 +33,7 @@ module RubyLsp
|
|
33
33
|
@client_name = T.let("Unknown", String)
|
34
34
|
end
|
35
35
|
|
36
|
-
sig { params(uri: URI::Generic).returns(Document) }
|
36
|
+
sig { params(uri: URI::Generic).returns(Document[T.untyped]) }
|
37
37
|
def get(uri)
|
38
38
|
document = @state[uri.to_s]
|
39
39
|
return document unless document.nil?
|
@@ -44,8 +44,11 @@ module RubyLsp
|
|
44
44
|
raise NonExistingDocumentError, uri.to_s unless path
|
45
45
|
|
46
46
|
ext = File.extname(path)
|
47
|
-
language_id =
|
47
|
+
language_id = case ext
|
48
|
+
when ".erb", ".rhtml"
|
48
49
|
Document::LanguageId::ERB
|
50
|
+
when ".rbs"
|
51
|
+
Document::LanguageId::RBS
|
49
52
|
else
|
50
53
|
Document::LanguageId::Ruby
|
51
54
|
end
|
@@ -66,13 +69,14 @@ module RubyLsp
|
|
66
69
|
).void
|
67
70
|
end
|
68
71
|
def set(uri:, source:, version:, language_id:, encoding: Encoding::UTF_8)
|
69
|
-
|
72
|
+
@state[uri.to_s] = case language_id
|
70
73
|
when Document::LanguageId::ERB
|
71
74
|
ERBDocument.new(source: source, version: version, uri: uri, encoding: encoding)
|
75
|
+
when Document::LanguageId::RBS
|
76
|
+
RBSDocument.new(source: source, version: version, uri: uri, encoding: encoding)
|
72
77
|
else
|
73
78
|
RubyDocument.new(source: source, version: version, uri: uri, encoding: encoding)
|
74
79
|
end
|
75
|
-
@state[uri.to_s] = document
|
76
80
|
end
|
77
81
|
|
78
82
|
sig { params(uri: URI::Generic, edits: T::Array[T::Hash[Symbol, T.untyped]], version: Integer).void }
|
@@ -100,7 +104,7 @@ module RubyLsp
|
|
100
104
|
.params(
|
101
105
|
uri: URI::Generic,
|
102
106
|
request_name: String,
|
103
|
-
block: T.proc.params(document: Document).returns(T.type_parameter(:T)),
|
107
|
+
block: T.proc.params(document: Document[T.untyped]).returns(T.type_parameter(:T)),
|
104
108
|
).returns(T.type_parameter(:T))
|
105
109
|
end
|
106
110
|
def cache_fetch(uri, request_name, &block)
|