ruby-lsp 0.17.13 → 0.17.15
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|