ruby-lsp 0.21.3 → 0.22.0
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 +1 -1
- data/exe/ruby-lsp-launcher +0 -3
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +136 -58
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +31 -28
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +10 -10
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +2 -2
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/constant_test.rb +8 -8
- data/lib/ruby_indexer/test/enhancements_test.rb +134 -38
- data/lib/ruby_indexer/test/index_test.rb +39 -0
- data/lib/ruby_indexer/test/method_test.rb +34 -1
- data/lib/ruby_lsp/global_state.rb +1 -3
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/requests/definition.rb +2 -0
- data/lib/ruby_lsp/requests/hover.rb +2 -0
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +1 -0
- data/lib/ruby_lsp/server.rb +13 -0
- data/lib/ruby_lsp/setup_bundler.rb +20 -20
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e48cef95e466e2a75943921fc08ede1a449e07096e44078ca063bdbee6d3c54b
|
4
|
+
data.tar.gz: c0ce6a0636645674e9e9a87219124b622b1280420c14c4bd12f8f0f0aafc3f32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89d02237ecce7e784bb5275ba4d5fc30b64981ea337b078f3117037b98d8f734e0d7e6266078e76023125dd52bbcb6ded8d04fdac1c29eb8292fd29e401cece4
|
7
|
+
data.tar.gz: 7245d82a481bd9e6a56d0cce9ad03796bd1a6d9e770beb5632fcaf7669ab0af9e9c0c95f0b671d844231af718add21b98011665ac3355fab5647ab12378b60bf
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.22.0
|
data/exe/ruby-lsp
CHANGED
@@ -54,7 +54,7 @@ rescue OptionParser::InvalidOption => e
|
|
54
54
|
exit(1)
|
55
55
|
end
|
56
56
|
|
57
|
-
# When we're running without bundler, then we need to make sure the
|
57
|
+
# When we're running without bundler, then we need to make sure the composed bundle is fully configured and re-execute
|
58
58
|
# using `BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp` so that we have access to the gems that are a part of
|
59
59
|
# the application's bundle
|
60
60
|
if ENV["BUNDLE_GEMFILE"].nil?
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -74,9 +74,6 @@ rescue StandardError => e
|
|
74
74
|
# If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
|
75
75
|
# in degraded mode
|
76
76
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
77
|
-
ensure
|
78
|
-
require "fileutils"
|
79
|
-
FileUtils.rm(bundle_env_path) if File.exist?(bundle_env_path)
|
80
77
|
end
|
81
78
|
|
82
79
|
error_path = File.join(".ruby-lsp", "install_error")
|
@@ -109,6 +109,12 @@ module RubyIndexer
|
|
109
109
|
|
110
110
|
indexables = T.let([], T::Array[IndexablePath])
|
111
111
|
|
112
|
+
# Handle top level files separately. The path below is an optimization to prevent descending down directories that
|
113
|
+
# are going to be excluded anyway, so we need to handle top level scripts separately
|
114
|
+
Dir.glob(File.join(@workspace_path, "*.rb"), flags).each do |path|
|
115
|
+
indexables << IndexablePath.new(nil, path)
|
116
|
+
end
|
117
|
+
|
112
118
|
# Add user specified patterns
|
113
119
|
@included_patterns.each do |pattern|
|
114
120
|
load_path_entry = T.let(nil, T.nilable(String))
|
@@ -18,13 +18,12 @@ module RubyIndexer
|
|
18
18
|
parse_result: Prism::ParseResult,
|
19
19
|
file_path: String,
|
20
20
|
collect_comments: T::Boolean,
|
21
|
-
enhancements: T::Array[Enhancement],
|
22
21
|
).void
|
23
22
|
end
|
24
|
-
def initialize(index, dispatcher, parse_result, file_path, collect_comments: false
|
23
|
+
def initialize(index, dispatcher, parse_result, file_path, collect_comments: false)
|
25
24
|
@index = index
|
26
25
|
@file_path = file_path
|
27
|
-
@enhancements =
|
26
|
+
@enhancements = T.let(Enhancement.all(self), T::Array[Enhancement])
|
28
27
|
@visibility_stack = T.let([Entry::Visibility::PUBLIC], T::Array[Entry::Visibility])
|
29
28
|
@comments_by_line = T.let(
|
30
29
|
parse_result.comments.to_h do |c|
|
@@ -37,6 +36,7 @@ module RubyIndexer
|
|
37
36
|
parse_result.code_units_cache(@index.configuration.encoding),
|
38
37
|
T.any(T.proc.params(arg0: Integer).returns(Integer), Prism::CodeUnitsCache),
|
39
38
|
)
|
39
|
+
@source_lines = T.let(parse_result.source.lines, T::Array[String])
|
40
40
|
|
41
41
|
# The nesting stack we're currently inside. Used to determine the fully qualified name of constants, but only
|
42
42
|
# stored by unresolved aliases which need the original nesting to be lazily resolved
|
@@ -85,15 +85,9 @@ module RubyIndexer
|
|
85
85
|
|
86
86
|
sig { params(node: Prism::ClassNode).void }
|
87
87
|
def on_class_node_enter(node)
|
88
|
-
@visibility_stack.push(Entry::Visibility::PUBLIC)
|
89
88
|
constant_path = node.constant_path
|
90
|
-
name = constant_path.slice
|
91
|
-
|
92
|
-
comments = collect_comments(node)
|
93
|
-
|
94
89
|
superclass = node.superclass
|
95
|
-
|
96
|
-
nesting = actual_nesting(name)
|
90
|
+
nesting = actual_nesting(constant_path.slice)
|
97
91
|
|
98
92
|
parent_class = case superclass
|
99
93
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
@@ -112,53 +106,29 @@ module RubyIndexer
|
|
112
106
|
end
|
113
107
|
end
|
114
108
|
|
115
|
-
|
109
|
+
add_class(
|
116
110
|
nesting,
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
comments,
|
121
|
-
parent_class,
|
111
|
+
node.location,
|
112
|
+
constant_path.location,
|
113
|
+
parent_class_name: parent_class,
|
114
|
+
comments: collect_comments(node),
|
122
115
|
)
|
123
|
-
|
124
|
-
@owner_stack << entry
|
125
|
-
@index.add(entry)
|
126
|
-
@stack << name
|
127
116
|
end
|
128
117
|
|
129
118
|
sig { params(node: Prism::ClassNode).void }
|
130
119
|
def on_class_node_leave(node)
|
131
|
-
|
132
|
-
@owner_stack.pop
|
133
|
-
@visibility_stack.pop
|
120
|
+
pop_namespace_stack
|
134
121
|
end
|
135
122
|
|
136
123
|
sig { params(node: Prism::ModuleNode).void }
|
137
124
|
def on_module_node_enter(node)
|
138
|
-
@visibility_stack.push(Entry::Visibility::PUBLIC)
|
139
125
|
constant_path = node.constant_path
|
140
|
-
|
141
|
-
|
142
|
-
comments = collect_comments(node)
|
143
|
-
|
144
|
-
entry = Entry::Module.new(
|
145
|
-
actual_nesting(name),
|
146
|
-
@file_path,
|
147
|
-
Location.from_prism_location(node.location, @code_units_cache),
|
148
|
-
Location.from_prism_location(constant_path.location, @code_units_cache),
|
149
|
-
comments,
|
150
|
-
)
|
151
|
-
|
152
|
-
@owner_stack << entry
|
153
|
-
@index.add(entry)
|
154
|
-
@stack << name
|
126
|
+
add_module(constant_path.slice, node.location, constant_path.location, comments: collect_comments(node))
|
155
127
|
end
|
156
128
|
|
157
129
|
sig { params(node: Prism::ModuleNode).void }
|
158
130
|
def on_module_node_leave(node)
|
159
|
-
|
160
|
-
@owner_stack.pop
|
161
|
-
@visibility_stack.pop
|
131
|
+
pop_namespace_stack
|
162
132
|
end
|
163
133
|
|
164
134
|
sig { params(node: Prism::SingletonClassNode).void }
|
@@ -200,9 +170,7 @@ module RubyIndexer
|
|
200
170
|
|
201
171
|
sig { params(node: Prism::SingletonClassNode).void }
|
202
172
|
def on_singleton_class_node_leave(node)
|
203
|
-
|
204
|
-
@owner_stack.pop
|
205
|
-
@visibility_stack.pop
|
173
|
+
pop_namespace_stack
|
206
174
|
end
|
207
175
|
|
208
176
|
sig { params(node: Prism::MultiWriteNode).void }
|
@@ -317,7 +285,7 @@ module RubyIndexer
|
|
317
285
|
end
|
318
286
|
|
319
287
|
@enhancements.each do |enhancement|
|
320
|
-
enhancement.on_call_node_enter(
|
288
|
+
enhancement.on_call_node_enter(node)
|
321
289
|
rescue StandardError => e
|
322
290
|
@indexing_errors << <<~MSG
|
323
291
|
Indexing error in #{@file_path} with '#{enhancement.class.name}' on call node enter enhancement: #{e.message}
|
@@ -338,7 +306,7 @@ module RubyIndexer
|
|
338
306
|
end
|
339
307
|
|
340
308
|
@enhancements.each do |enhancement|
|
341
|
-
enhancement.on_call_node_leave(
|
309
|
+
enhancement.on_call_node_leave(node)
|
342
310
|
rescue StandardError => e
|
343
311
|
@indexing_errors << <<~MSG
|
344
312
|
Indexing error in #{@file_path} with '#{enhancement.class.name}' on call node leave enhancement: #{e.message}
|
@@ -463,6 +431,98 @@ module RubyIndexer
|
|
463
431
|
)
|
464
432
|
end
|
465
433
|
|
434
|
+
sig do
|
435
|
+
params(
|
436
|
+
name: String,
|
437
|
+
node_location: Prism::Location,
|
438
|
+
signatures: T::Array[Entry::Signature],
|
439
|
+
visibility: Entry::Visibility,
|
440
|
+
comments: T.nilable(String),
|
441
|
+
).void
|
442
|
+
end
|
443
|
+
def add_method(name, node_location, signatures, visibility: Entry::Visibility::PUBLIC, comments: nil)
|
444
|
+
location = Location.from_prism_location(node_location, @code_units_cache)
|
445
|
+
|
446
|
+
@index.add(Entry::Method.new(
|
447
|
+
name,
|
448
|
+
@file_path,
|
449
|
+
location,
|
450
|
+
location,
|
451
|
+
comments,
|
452
|
+
signatures,
|
453
|
+
visibility,
|
454
|
+
@owner_stack.last,
|
455
|
+
))
|
456
|
+
end
|
457
|
+
|
458
|
+
sig do
|
459
|
+
params(
|
460
|
+
name: String,
|
461
|
+
full_location: Prism::Location,
|
462
|
+
name_location: Prism::Location,
|
463
|
+
comments: T.nilable(String),
|
464
|
+
).void
|
465
|
+
end
|
466
|
+
def add_module(name, full_location, name_location, comments: nil)
|
467
|
+
location = Location.from_prism_location(full_location, @code_units_cache)
|
468
|
+
name_loc = Location.from_prism_location(name_location, @code_units_cache)
|
469
|
+
|
470
|
+
entry = Entry::Module.new(
|
471
|
+
actual_nesting(name),
|
472
|
+
@file_path,
|
473
|
+
location,
|
474
|
+
name_loc,
|
475
|
+
comments,
|
476
|
+
)
|
477
|
+
|
478
|
+
advance_namespace_stack(name, entry)
|
479
|
+
end
|
480
|
+
|
481
|
+
sig do
|
482
|
+
params(
|
483
|
+
name_or_nesting: T.any(String, T::Array[String]),
|
484
|
+
full_location: Prism::Location,
|
485
|
+
name_location: Prism::Location,
|
486
|
+
parent_class_name: T.nilable(String),
|
487
|
+
comments: T.nilable(String),
|
488
|
+
).void
|
489
|
+
end
|
490
|
+
def add_class(name_or_nesting, full_location, name_location, parent_class_name: nil, comments: nil)
|
491
|
+
nesting = name_or_nesting.is_a?(Array) ? name_or_nesting : actual_nesting(name_or_nesting)
|
492
|
+
entry = Entry::Class.new(
|
493
|
+
nesting,
|
494
|
+
@file_path,
|
495
|
+
Location.from_prism_location(full_location, @code_units_cache),
|
496
|
+
Location.from_prism_location(name_location, @code_units_cache),
|
497
|
+
comments,
|
498
|
+
parent_class_name,
|
499
|
+
)
|
500
|
+
|
501
|
+
advance_namespace_stack(T.must(nesting.last), entry)
|
502
|
+
end
|
503
|
+
|
504
|
+
sig { params(block: T.proc.params(index: Index, base: Entry::Namespace).void).void }
|
505
|
+
def register_included_hook(&block)
|
506
|
+
owner = @owner_stack.last
|
507
|
+
return unless owner
|
508
|
+
|
509
|
+
@index.register_included_hook(owner.name) do |index, base|
|
510
|
+
block.call(index, base)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
sig { void }
|
515
|
+
def pop_namespace_stack
|
516
|
+
@stack.pop
|
517
|
+
@owner_stack.pop
|
518
|
+
@visibility_stack.pop
|
519
|
+
end
|
520
|
+
|
521
|
+
sig { returns(T.nilable(Entry::Namespace)) }
|
522
|
+
def current_owner
|
523
|
+
@owner_stack.last
|
524
|
+
end
|
525
|
+
|
466
526
|
private
|
467
527
|
|
468
528
|
sig do
|
@@ -661,8 +721,7 @@ module RubyIndexer
|
|
661
721
|
comments = +""
|
662
722
|
|
663
723
|
start_line = node.location.start_line - 1
|
664
|
-
start_line -= 1 unless
|
665
|
-
|
724
|
+
start_line -= 1 unless comment_exists_at?(start_line)
|
666
725
|
start_line.downto(1) do |line|
|
667
726
|
comment = @comments_by_line[line]
|
668
727
|
break unless comment
|
@@ -683,6 +742,11 @@ module RubyIndexer
|
|
683
742
|
comments
|
684
743
|
end
|
685
744
|
|
745
|
+
sig { params(line: Integer).returns(T::Boolean) }
|
746
|
+
def comment_exists_at?(line)
|
747
|
+
@comments_by_line.key?(line) || !@source_lines[line - 1].to_s.strip.empty?
|
748
|
+
end
|
749
|
+
|
686
750
|
sig { params(name: String).returns(String) }
|
687
751
|
def fully_qualify_name(name)
|
688
752
|
if @stack.empty? || name.start_with?("::")
|
@@ -746,16 +810,22 @@ module RubyIndexer
|
|
746
810
|
return unless arguments
|
747
811
|
|
748
812
|
arguments.each do |node|
|
749
|
-
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
owner.mixin_operations << Entry::Include.new(node.full_name)
|
754
|
-
when :prepend
|
755
|
-
owner.mixin_operations << Entry::Prepend.new(node.full_name)
|
756
|
-
when :extend
|
813
|
+
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode) ||
|
814
|
+
(node.is_a?(Prism::SelfNode) && operation == :extend)
|
815
|
+
|
816
|
+
if node.is_a?(Prism::SelfNode)
|
757
817
|
singleton = @index.existing_or_new_singleton_class(owner.name)
|
758
|
-
singleton.mixin_operations << Entry::Include.new(
|
818
|
+
singleton.mixin_operations << Entry::Include.new(owner.name)
|
819
|
+
else
|
820
|
+
case operation
|
821
|
+
when :include
|
822
|
+
owner.mixin_operations << Entry::Include.new(node.full_name)
|
823
|
+
when :prepend
|
824
|
+
owner.mixin_operations << Entry::Prepend.new(node.full_name)
|
825
|
+
when :extend
|
826
|
+
singleton = @index.existing_or_new_singleton_class(owner.name)
|
827
|
+
singleton.mixin_operations << Entry::Include.new(node.full_name)
|
828
|
+
end
|
759
829
|
end
|
760
830
|
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
761
831
|
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
@@ -910,5 +980,13 @@ module RubyIndexer
|
|
910
980
|
|
911
981
|
corrected_nesting
|
912
982
|
end
|
983
|
+
|
984
|
+
sig { params(short_name: String, entry: Entry::Namespace).void }
|
985
|
+
def advance_namespace_stack(short_name, entry)
|
986
|
+
@visibility_stack.push(Entry::Visibility::PUBLIC)
|
987
|
+
@owner_stack << entry
|
988
|
+
@index.add(entry)
|
989
|
+
@stack << short_name
|
990
|
+
end
|
913
991
|
end
|
914
992
|
end
|
@@ -8,38 +8,41 @@ module RubyIndexer
|
|
8
8
|
|
9
9
|
abstract!
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
@enhancements = T.let([], T::Array[T::Class[Enhancement]])
|
12
|
+
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { params(child: T::Class[Enhancement]).void }
|
17
|
+
def inherited(child)
|
18
|
+
@enhancements << child
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { params(listener: DeclarationListener).returns(T::Array[Enhancement]) }
|
23
|
+
def all(listener)
|
24
|
+
@enhancements.map { |enhancement| enhancement.new(listener) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Only available for testing purposes
|
28
|
+
sig { void }
|
29
|
+
def clear
|
30
|
+
@enhancements.clear
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { params(listener: DeclarationListener).void }
|
35
|
+
def initialize(listener)
|
36
|
+
@listener = listener
|
14
37
|
end
|
15
38
|
|
16
39
|
# The `on_extend` indexing enhancement is invoked whenever an extend is encountered in the code. It can be used to
|
17
40
|
# register for an included callback, similar to what `ActiveSupport::Concern` does in order to auto-extend the
|
18
41
|
# `ClassMethods` modules
|
19
|
-
sig
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
code_units_cache: T.any(
|
25
|
-
T.proc.params(arg0: Integer).returns(Integer),
|
26
|
-
Prism::CodeUnitsCache,
|
27
|
-
),
|
28
|
-
).void
|
29
|
-
end
|
30
|
-
def on_call_node_enter(owner, node, file_path, code_units_cache); end
|
31
|
-
|
32
|
-
sig do
|
33
|
-
overridable.params(
|
34
|
-
owner: T.nilable(Entry::Namespace),
|
35
|
-
node: Prism::CallNode,
|
36
|
-
file_path: String,
|
37
|
-
code_units_cache: T.any(
|
38
|
-
T.proc.params(arg0: Integer).returns(Integer),
|
39
|
-
Prism::CodeUnitsCache,
|
40
|
-
),
|
41
|
-
).void
|
42
|
-
end
|
43
|
-
def on_call_node_leave(owner, node, file_path, code_units_cache); end
|
42
|
+
sig { overridable.params(node: Prism::CallNode).void }
|
43
|
+
def on_call_node_enter(node); end # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
44
|
+
|
45
|
+
sig { overridable.params(node: Prism::CallNode).void }
|
46
|
+
def on_call_node_leave(node); end # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
44
47
|
end
|
45
48
|
end
|
@@ -7,6 +7,7 @@ module RubyIndexer
|
|
7
7
|
|
8
8
|
class UnresolvableAliasError < StandardError; end
|
9
9
|
class NonExistingNamespaceError < StandardError; end
|
10
|
+
class IndexNotEmptyError < StandardError; end
|
10
11
|
|
11
12
|
# The minimum Jaro-Winkler similarity score for an entry to be considered a match for a given fuzzy search query
|
12
13
|
ENTRY_SIMILARITY_THRESHOLD = 0.7
|
@@ -39,9 +40,6 @@ module RubyIndexer
|
|
39
40
|
# Holds the linearized ancestors list for every namespace
|
40
41
|
@ancestors = T.let({}, T::Hash[String, T::Array[String]])
|
41
42
|
|
42
|
-
# List of classes that are enhancing the index
|
43
|
-
@enhancements = T.let([], T::Array[Enhancement])
|
44
|
-
|
45
43
|
# Map of module name to included hooks that have to be executed when we include the given module
|
46
44
|
@included_hooks = T.let(
|
47
45
|
{},
|
@@ -51,12 +49,6 @@ module RubyIndexer
|
|
51
49
|
@configuration = T.let(RubyIndexer::Configuration.new, Configuration)
|
52
50
|
end
|
53
51
|
|
54
|
-
# Register an enhancement to the index. Enhancements must conform to the `Enhancement` interface
|
55
|
-
sig { params(enhancement: Enhancement).void }
|
56
|
-
def register_enhancement(enhancement)
|
57
|
-
@enhancements << enhancement
|
58
|
-
end
|
59
|
-
|
60
52
|
# Register an included `hook` that will be executed when `module_name` is included into any namespace
|
61
53
|
sig { params(module_name: String, hook: T.proc.params(index: Index, base: Entry::Namespace).void).void }
|
62
54
|
def register_included_hook(module_name, &hook)
|
@@ -360,6 +352,15 @@ module RubyIndexer
|
|
360
352
|
).void
|
361
353
|
end
|
362
354
|
def index_all(indexable_paths: @configuration.indexables, &block)
|
355
|
+
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
356
|
+
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
357
|
+
# behavior and can take appropriate action.
|
358
|
+
# binding.break
|
359
|
+
if @entries.any?
|
360
|
+
raise IndexNotEmptyError,
|
361
|
+
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
362
|
+
end
|
363
|
+
|
363
364
|
RBSIndexer.new(self).index_ruby_core
|
364
365
|
# Calculate how many paths are worth 1% of progress
|
365
366
|
progress_step = (indexable_paths.length / 100.0).ceil
|
@@ -386,7 +387,6 @@ module RubyIndexer
|
|
386
387
|
result,
|
387
388
|
indexable_path.full_path,
|
388
389
|
collect_comments: collect_comments,
|
389
|
-
enhancements: @enhancements,
|
390
390
|
)
|
391
391
|
dispatcher.dispatch(result.value)
|
392
392
|
|
@@ -302,10 +302,10 @@ module RubyIndexer
|
|
302
302
|
RUBY
|
303
303
|
|
304
304
|
b_const = @index["A::B"].first
|
305
|
-
|
305
|
+
assert_predicate(b_const, :private?)
|
306
306
|
|
307
307
|
c_const = @index["A::C"].first
|
308
|
-
|
308
|
+
assert_predicate(c_const, :private?)
|
309
309
|
|
310
310
|
d_const = @index["A::D"].first
|
311
311
|
assert_equal(Entry::Visibility::PUBLIC, d_const.visibility)
|
@@ -160,5 +160,15 @@ module RubyIndexer
|
|
160
160
|
)
|
161
161
|
end
|
162
162
|
end
|
163
|
+
|
164
|
+
def test_includes_top_level_files
|
165
|
+
Dir.mktmpdir do |dir|
|
166
|
+
FileUtils.touch(File.join(dir, "find_me.rb"))
|
167
|
+
@config.workspace_path = dir
|
168
|
+
|
169
|
+
indexables = @config.indexables
|
170
|
+
assert(indexables.find { |i| File.basename(i.full_path) == "find_me.rb" })
|
171
|
+
end
|
172
|
+
end
|
163
173
|
end
|
164
174
|
end
|
@@ -130,13 +130,13 @@ module RubyIndexer
|
|
130
130
|
RUBY
|
131
131
|
|
132
132
|
b_const = @index["A::B"].first
|
133
|
-
|
133
|
+
assert_predicate(b_const, :private?)
|
134
134
|
|
135
135
|
c_const = @index["A::C"].first
|
136
|
-
|
136
|
+
assert_predicate(c_const, :private?)
|
137
137
|
|
138
138
|
d_const = @index["A::D"].first
|
139
|
-
|
139
|
+
assert_predicate(d_const, :public?)
|
140
140
|
end
|
141
141
|
|
142
142
|
def test_marking_constants_as_private_reopening_namespaces
|
@@ -163,13 +163,13 @@ module RubyIndexer
|
|
163
163
|
RUBY
|
164
164
|
|
165
165
|
a_const = @index["A::B::CONST_A"].first
|
166
|
-
|
166
|
+
assert_predicate(a_const, :private?)
|
167
167
|
|
168
168
|
b_const = @index["A::B::CONST_B"].first
|
169
|
-
|
169
|
+
assert_predicate(b_const, :private?)
|
170
170
|
|
171
171
|
c_const = @index["A::B::CONST_C"].first
|
172
|
-
|
172
|
+
assert_predicate(c_const, :private?)
|
173
173
|
end
|
174
174
|
|
175
175
|
def test_marking_constants_as_private_with_receiver
|
@@ -187,10 +187,10 @@ module RubyIndexer
|
|
187
187
|
RUBY
|
188
188
|
|
189
189
|
a_const = @index["A::B::CONST_A"].first
|
190
|
-
|
190
|
+
assert_predicate(a_const, :private?)
|
191
191
|
|
192
192
|
b_const = @index["A::B::CONST_B"].first
|
193
|
-
|
193
|
+
assert_predicate(b_const, :private?)
|
194
194
|
end
|
195
195
|
|
196
196
|
def test_indexing_constant_aliases
|
@@ -5,24 +5,28 @@ require_relative "test_case"
|
|
5
5
|
|
6
6
|
module RubyIndexer
|
7
7
|
class EnhancementTest < TestCase
|
8
|
+
def teardown
|
9
|
+
super
|
10
|
+
Enhancement.clear
|
11
|
+
end
|
12
|
+
|
8
13
|
def test_enhancing_indexing_included_hook
|
9
|
-
|
10
|
-
def on_call_node_enter(
|
14
|
+
Class.new(Enhancement) do
|
15
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
16
|
+
owner = @listener.current_owner
|
11
17
|
return unless owner
|
12
|
-
return unless
|
18
|
+
return unless call_node.name == :extend
|
13
19
|
|
14
|
-
arguments =
|
20
|
+
arguments = call_node.arguments&.arguments
|
15
21
|
return unless arguments
|
16
22
|
|
17
|
-
location = Location.from_prism_location(node.location, code_units_cache)
|
18
|
-
|
19
23
|
arguments.each do |node|
|
20
24
|
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
21
25
|
|
22
26
|
module_name = node.full_name
|
23
27
|
next unless module_name == "ActiveSupport::Concern"
|
24
28
|
|
25
|
-
@
|
29
|
+
@listener.register_included_hook do |index, base|
|
26
30
|
class_methods_name = "#{owner.name}::ClassMethods"
|
27
31
|
|
28
32
|
if index.indexed?(class_methods_name)
|
@@ -31,16 +35,11 @@ module RubyIndexer
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
@
|
38
|
+
@listener.add_method(
|
35
39
|
"new_method",
|
36
|
-
|
37
|
-
location,
|
38
|
-
location,
|
39
|
-
nil,
|
40
|
+
call_node.location,
|
40
41
|
[Entry::Signature.new([Entry::RequiredParameter.new(name: :a)])],
|
41
|
-
|
42
|
-
owner,
|
43
|
-
))
|
42
|
+
)
|
44
43
|
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
45
44
|
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
46
45
|
# Do nothing
|
@@ -48,7 +47,6 @@ module RubyIndexer
|
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
51
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
52
50
|
index(<<~RUBY)
|
53
51
|
module ActiveSupport
|
54
52
|
module Concern
|
@@ -96,9 +94,9 @@ module RubyIndexer
|
|
96
94
|
end
|
97
95
|
|
98
96
|
def test_enhancing_indexing_configuration_dsl
|
99
|
-
|
100
|
-
def on_call_node_enter(
|
101
|
-
return unless
|
97
|
+
Class.new(Enhancement) do
|
98
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
99
|
+
return unless @listener.current_owner
|
102
100
|
|
103
101
|
name = node.name
|
104
102
|
return unless name == :has_many
|
@@ -109,22 +107,14 @@ module RubyIndexer
|
|
109
107
|
association_name = arguments.first
|
110
108
|
return unless association_name.is_a?(Prism::SymbolNode)
|
111
109
|
|
112
|
-
|
113
|
-
|
114
|
-
@index.add(Entry::Method.new(
|
110
|
+
@listener.add_method(
|
115
111
|
T.must(association_name.value),
|
116
|
-
|
117
|
-
location,
|
118
|
-
location,
|
119
|
-
nil,
|
112
|
+
association_name.location,
|
120
113
|
[],
|
121
|
-
|
122
|
-
owner,
|
123
|
-
))
|
114
|
+
)
|
124
115
|
end
|
125
116
|
end
|
126
117
|
|
127
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
128
118
|
index(<<~RUBY)
|
129
119
|
module ActiveSupport
|
130
120
|
module Concern
|
@@ -157,8 +147,8 @@ module RubyIndexer
|
|
157
147
|
end
|
158
148
|
|
159
149
|
def test_error_handling_in_on_call_node_enter_enhancement
|
160
|
-
|
161
|
-
def on_call_node_enter(
|
150
|
+
Class.new(Enhancement) do
|
151
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
162
152
|
raise "Error"
|
163
153
|
end
|
164
154
|
|
@@ -169,8 +159,6 @@ module RubyIndexer
|
|
169
159
|
end
|
170
160
|
end
|
171
161
|
|
172
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
173
|
-
|
174
162
|
_stdout, stderr = capture_io do
|
175
163
|
index(<<~RUBY)
|
176
164
|
module ActiveSupport
|
@@ -192,8 +180,8 @@ module RubyIndexer
|
|
192
180
|
end
|
193
181
|
|
194
182
|
def test_error_handling_in_on_call_node_leave_enhancement
|
195
|
-
|
196
|
-
def on_call_node_leave(
|
183
|
+
Class.new(Enhancement) do
|
184
|
+
def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
197
185
|
raise "Error"
|
198
186
|
end
|
199
187
|
|
@@ -204,8 +192,6 @@ module RubyIndexer
|
|
204
192
|
end
|
205
193
|
end
|
206
194
|
|
207
|
-
@index.register_enhancement(enhancement_class.new(@index))
|
208
|
-
|
209
195
|
_stdout, stderr = capture_io do
|
210
196
|
index(<<~RUBY)
|
211
197
|
module ActiveSupport
|
@@ -225,5 +211,115 @@ module RubyIndexer
|
|
225
211
|
# The module should still be indexed
|
226
212
|
assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
|
227
213
|
end
|
214
|
+
|
215
|
+
def test_advancing_namespace_stack_from_enhancement
|
216
|
+
Class.new(Enhancement) do
|
217
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
218
|
+
owner = @listener.current_owner
|
219
|
+
return unless owner
|
220
|
+
|
221
|
+
case call_node.name
|
222
|
+
when :class_methods
|
223
|
+
@listener.add_module("ClassMethods", call_node.location, call_node.location)
|
224
|
+
when :extend
|
225
|
+
arguments = call_node.arguments&.arguments
|
226
|
+
return unless arguments
|
227
|
+
|
228
|
+
arguments.each do |node|
|
229
|
+
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
230
|
+
|
231
|
+
module_name = node.full_name
|
232
|
+
next unless module_name == "ActiveSupport::Concern"
|
233
|
+
|
234
|
+
@listener.register_included_hook do |index, base|
|
235
|
+
class_methods_name = "#{owner.name}::ClassMethods"
|
236
|
+
|
237
|
+
if index.indexed?(class_methods_name)
|
238
|
+
singleton = index.existing_or_new_singleton_class(base.name)
|
239
|
+
singleton.mixin_operations << Entry::Include.new(class_methods_name)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
247
|
+
return unless call_node.name == :class_methods
|
248
|
+
|
249
|
+
@listener.pop_namespace_stack
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
index(<<~RUBY)
|
254
|
+
module ActiveSupport
|
255
|
+
module Concern
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
module MyConcern
|
260
|
+
extend ActiveSupport::Concern
|
261
|
+
|
262
|
+
class_methods do
|
263
|
+
def foo; end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class User
|
268
|
+
include MyConcern
|
269
|
+
end
|
270
|
+
RUBY
|
271
|
+
|
272
|
+
assert_equal(
|
273
|
+
[
|
274
|
+
"User::<Class:User>",
|
275
|
+
"MyConcern::ClassMethods",
|
276
|
+
"Object::<Class:Object>",
|
277
|
+
"BasicObject::<Class:BasicObject>",
|
278
|
+
"Class",
|
279
|
+
"Module",
|
280
|
+
"Object",
|
281
|
+
"Kernel",
|
282
|
+
"BasicObject",
|
283
|
+
],
|
284
|
+
@index.linearized_ancestors_of("User::<Class:User>"),
|
285
|
+
)
|
286
|
+
|
287
|
+
refute_nil(@index.resolve_method("foo", "User::<Class:User>"))
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_creating_anonymous_classes_from_enhancement
|
291
|
+
Class.new(Enhancement) do
|
292
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
293
|
+
case call_node.name
|
294
|
+
when :context
|
295
|
+
arguments = call_node.arguments&.arguments
|
296
|
+
first_argument = arguments&.first
|
297
|
+
return unless first_argument.is_a?(Prism::StringNode)
|
298
|
+
|
299
|
+
@listener.add_class(
|
300
|
+
"<RSpec:#{first_argument.content}>",
|
301
|
+
call_node.location,
|
302
|
+
first_argument.location,
|
303
|
+
)
|
304
|
+
when :subject
|
305
|
+
@listener.add_method("subject", call_node.location, [])
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
310
|
+
return unless call_node.name == :context
|
311
|
+
|
312
|
+
@listener.pop_namespace_stack
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
index(<<~RUBY)
|
317
|
+
context "does something" do
|
318
|
+
subject { call_whatever }
|
319
|
+
end
|
320
|
+
RUBY
|
321
|
+
|
322
|
+
refute_nil(@index.resolve_method("subject", "<RSpec:does something>"))
|
323
|
+
end
|
228
324
|
end
|
229
325
|
end
|
@@ -1672,6 +1672,38 @@ module RubyIndexer
|
|
1672
1672
|
)
|
1673
1673
|
end
|
1674
1674
|
|
1675
|
+
def test_extend_self
|
1676
|
+
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
|
1677
|
+
module Foo
|
1678
|
+
def bar
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
extend self
|
1682
|
+
|
1683
|
+
def baz
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
RUBY
|
1687
|
+
|
1688
|
+
["bar", "baz"].product(["Foo", "Foo::<Class:Foo>"]).each do |method, receiver|
|
1689
|
+
entry = @index.resolve_method(method, receiver)&.first
|
1690
|
+
refute_nil(entry)
|
1691
|
+
assert_equal(method, T.must(entry).name)
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
assert_equal(
|
1695
|
+
[
|
1696
|
+
"Foo::<Class:Foo>",
|
1697
|
+
"Foo",
|
1698
|
+
"Module",
|
1699
|
+
"Object",
|
1700
|
+
"Kernel",
|
1701
|
+
"BasicObject",
|
1702
|
+
],
|
1703
|
+
@index.linearized_ancestors_of("Foo::<Class:Foo>"),
|
1704
|
+
)
|
1705
|
+
end
|
1706
|
+
|
1675
1707
|
def test_linearizing_singleton_ancestors
|
1676
1708
|
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
|
1677
1709
|
module First
|
@@ -2023,5 +2055,12 @@ module RubyIndexer
|
|
2023
2055
|
),
|
2024
2056
|
)
|
2025
2057
|
end
|
2058
|
+
|
2059
|
+
def test_prevents_multiple_calls_to_index_all
|
2060
|
+
# For this test class, `index_all` is already called once in `setup`.
|
2061
|
+
assert_raises(Index::IndexNotEmptyError) do
|
2062
|
+
@index.index_all
|
2063
|
+
end
|
2064
|
+
end
|
2026
2065
|
end
|
2027
2066
|
end
|
@@ -141,7 +141,7 @@ module RubyIndexer
|
|
141
141
|
# The first entry points to the location of the module_function call
|
142
142
|
assert_equal("Test", first_entry.owner.name)
|
143
143
|
assert_instance_of(Entry::Module, first_entry.owner)
|
144
|
-
|
144
|
+
assert_predicate(first_entry, :private?)
|
145
145
|
# The second entry points to the public singleton method
|
146
146
|
assert_equal("Test::<Class:Test>", second_entry.owner.name)
|
147
147
|
assert_instance_of(Entry::SingletonClass, second_entry.owner)
|
@@ -149,6 +149,39 @@ module RubyIndexer
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
+
def test_comments_documentation
|
153
|
+
index(<<~RUBY)
|
154
|
+
# Documentation for Foo
|
155
|
+
|
156
|
+
class Foo
|
157
|
+
# ####################
|
158
|
+
# Documentation for bar
|
159
|
+
# ####################
|
160
|
+
#
|
161
|
+
def bar
|
162
|
+
end
|
163
|
+
|
164
|
+
# test
|
165
|
+
|
166
|
+
# Documentation for baz
|
167
|
+
def baz; end
|
168
|
+
def ban; end
|
169
|
+
end
|
170
|
+
RUBY
|
171
|
+
|
172
|
+
foo_comment = @index["Foo"].first.comments
|
173
|
+
assert_equal("Documentation for Foo", foo_comment)
|
174
|
+
|
175
|
+
bar_comment = @index["bar"].first.comments
|
176
|
+
assert_equal("####################\nDocumentation for bar\n####################\n", bar_comment)
|
177
|
+
|
178
|
+
baz_comment = @index["baz"].first.comments
|
179
|
+
assert_equal("Documentation for baz", baz_comment)
|
180
|
+
|
181
|
+
ban_comment = @index["ban"].first.comments
|
182
|
+
assert_empty(ban_comment)
|
183
|
+
end
|
184
|
+
|
152
185
|
def test_method_with_parameters
|
153
186
|
index(<<~RUBY)
|
154
187
|
class Foo
|
@@ -21,7 +21,7 @@ module RubyLsp
|
|
21
21
|
attr_reader :encoding
|
22
22
|
|
23
23
|
sig { returns(T::Boolean) }
|
24
|
-
attr_reader :
|
24
|
+
attr_reader :top_level_bundle
|
25
25
|
|
26
26
|
sig { returns(TypeInferrer) }
|
27
27
|
attr_reader :type_inferrer
|
@@ -40,7 +40,6 @@ module RubyLsp
|
|
40
40
|
@has_type_checker = T.let(true, T::Boolean)
|
41
41
|
@index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
|
42
42
|
@supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
|
43
|
-
@experimental_features = T.let(false, T::Boolean)
|
44
43
|
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
45
44
|
@addon_settings = T.let({}, T::Hash[String, T.untyped])
|
46
45
|
@top_level_bundle = T.let(
|
@@ -131,7 +130,6 @@ module RubyLsp
|
|
131
130
|
end
|
132
131
|
@index.configuration.encoding = @encoding
|
133
132
|
|
134
|
-
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
135
133
|
@client_capabilities.apply_client_capabilities(options[:capabilities]) if options[:capabilities]
|
136
134
|
|
137
135
|
addon_settings = options.dig(:initializationOptions, :addonSettings)
|
data/lib/ruby_lsp/internal.rb
CHANGED
data/lib/ruby_lsp/server.rb
CHANGED
@@ -216,6 +216,13 @@ module RubyLsp
|
|
216
216
|
Hash.new(true)
|
217
217
|
end
|
218
218
|
|
219
|
+
bundle_env_path = File.join(".ruby-lsp", "bundle_env")
|
220
|
+
bundle_env = if File.exist?(bundle_env_path)
|
221
|
+
env = File.readlines(bundle_env_path).to_h { |line| T.cast(line.chomp.split("=", 2), [String, String]) }
|
222
|
+
FileUtils.rm(bundle_env_path)
|
223
|
+
env
|
224
|
+
end
|
225
|
+
|
219
226
|
document_symbol_provider = Requests::DocumentSymbol.provider if enabled_features["documentSymbols"]
|
220
227
|
document_link_provider = Requests::DocumentLink.provider if enabled_features["documentLink"]
|
221
228
|
code_lens_provider = Requests::CodeLens.provider if enabled_features["codeLens"]
|
@@ -269,6 +276,7 @@ module RubyLsp
|
|
269
276
|
},
|
270
277
|
formatter: @global_state.formatter,
|
271
278
|
degraded_mode: !!(@install_error || @setup_error),
|
279
|
+
bundle_env: bundle_env,
|
272
280
|
}
|
273
281
|
|
274
282
|
send_message(Result.new(id: message[:id], response: response))
|
@@ -604,6 +612,11 @@ module RubyLsp
|
|
604
612
|
# don't want to format it
|
605
613
|
path = uri.to_standardized_path
|
606
614
|
unless path.nil? || path.start_with?(@global_state.workspace_path)
|
615
|
+
send_log_message(<<~MESSAGE)
|
616
|
+
Ignoring formatting request for file outside of the workspace.
|
617
|
+
Workspace path was set by editor as #{@global_state.workspace_path}.
|
618
|
+
File path requested for formatting was #{path}
|
619
|
+
MESSAGE
|
607
620
|
send_empty_response(message[:id])
|
608
621
|
return
|
609
622
|
end
|
@@ -12,7 +12,7 @@ require "digest"
|
|
12
12
|
require "time"
|
13
13
|
require "uri"
|
14
14
|
|
15
|
-
# This file is a script that will configure a
|
15
|
+
# This file is a script that will configure a composed bundle for the Ruby LSP. The composed bundle allows developers to use
|
16
16
|
# the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
|
17
17
|
# exact locked versions of dependencies.
|
18
18
|
|
@@ -62,7 +62,7 @@ module RubyLsp
|
|
62
62
|
@retry = T.let(false, T::Boolean)
|
63
63
|
end
|
64
64
|
|
65
|
-
# Sets up the
|
65
|
+
# Sets up the composed bundle and returns the `BUNDLE_GEMFILE`, `BUNDLE_PATH` and `BUNDLE_APP_CONFIG` that should be
|
66
66
|
# used for running the server
|
67
67
|
sig { returns(T::Hash[String, String]) }
|
68
68
|
def setup!
|
@@ -73,12 +73,12 @@ module RubyLsp
|
|
73
73
|
ignore_file = @custom_dir + ".gitignore"
|
74
74
|
ignore_file.write("*") unless ignore_file.exist?
|
75
75
|
|
76
|
-
# Do not set up a
|
76
|
+
# Do not set up a composed bundle if LSP dependencies are already in the Gemfile
|
77
77
|
if @dependencies["ruby-lsp"] &&
|
78
78
|
@dependencies["debug"] &&
|
79
79
|
(@rails_app ? @dependencies["ruby-lsp-rails"] : true)
|
80
80
|
$stderr.puts(
|
81
|
-
"Ruby LSP> Skipping
|
81
|
+
"Ruby LSP> Skipping composed bundle setup since LSP dependencies are already in #{@gemfile}",
|
82
82
|
)
|
83
83
|
|
84
84
|
return run_bundle_install
|
@@ -96,7 +96,7 @@ module RubyLsp
|
|
96
96
|
|
97
97
|
if @custom_lockfile.exist? && @lockfile_hash_path.exist? && @lockfile_hash_path.read == current_lockfile_hash
|
98
98
|
$stderr.puts(
|
99
|
-
"Ruby LSP> Skipping
|
99
|
+
"Ruby LSP> Skipping composed bundle setup since #{@custom_lockfile} already exists and is up to date",
|
100
100
|
)
|
101
101
|
return run_bundle_install(@custom_gemfile)
|
102
102
|
end
|
@@ -110,8 +110,8 @@ module RubyLsp
|
|
110
110
|
private
|
111
111
|
|
112
112
|
sig { returns(T::Hash[String, T.untyped]) }
|
113
|
-
def
|
114
|
-
@
|
113
|
+
def composed_bundle_dependencies
|
114
|
+
@composed_bundle_dependencies ||= T.let(
|
115
115
|
begin
|
116
116
|
original_bundle_gemfile = ENV["BUNDLE_GEMFILE"]
|
117
117
|
|
@@ -136,8 +136,8 @@ module RubyLsp
|
|
136
136
|
"",
|
137
137
|
]
|
138
138
|
|
139
|
-
# If there's a top level Gemfile, we want to evaluate from the
|
140
|
-
# Gemfile, so if there isn't one we need to add a default source
|
139
|
+
# If there's a top level Gemfile, we want to evaluate from the composed bundle. We get the source from the top
|
140
|
+
# level Gemfile, so if there isn't one we need to add a default source
|
141
141
|
if @gemfile&.exist? && @lockfile&.exist?
|
142
142
|
parts << "eval_gemfile(File.expand_path(\"../#{@gemfile_name}\", __dir__))"
|
143
143
|
else
|
@@ -187,7 +187,7 @@ module RubyLsp
|
|
187
187
|
env = bundler_settings_as_env
|
188
188
|
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
|
189
189
|
|
190
|
-
# If the user has a
|
190
|
+
# If the user has a composed bundle path configured, we need to ensure that we will use the absolute and not
|
191
191
|
# relative version of it when running `bundle install`. This is necessary to avoid installing the gems under the
|
192
192
|
# `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
|
193
193
|
# want to install it in the top level `vendor` and not `.ruby-lsp/vendor`
|
@@ -244,7 +244,7 @@ module RubyLsp
|
|
244
244
|
base_bundle = base_bundle_command(env)
|
245
245
|
|
246
246
|
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
|
247
|
-
# to upgrade them or else we'll produce undesired source control changes. If the
|
247
|
+
# to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
|
248
248
|
# and any of `ruby-lsp`, `ruby-lsp-rails` or `debug` weren't a part of the Gemfile, then we need to run `bundle
|
249
249
|
# install` for the first time to generate the Gemfile.lock with them included or else Bundler will complain that
|
250
250
|
# they're missing. We can only update if the custom `.ruby-lsp/Gemfile.lock` already exists and includes all gems
|
@@ -274,16 +274,16 @@ module RubyLsp
|
|
274
274
|
command << "1>&2"
|
275
275
|
|
276
276
|
# Add bundle update
|
277
|
-
$stderr.puts("Ruby LSP> Running bundle install for the
|
277
|
+
$stderr.puts("Ruby LSP> Running bundle install for the composed bundle. This may take a while...")
|
278
278
|
$stderr.puts("Ruby LSP> Command: #{command}")
|
279
279
|
|
280
|
-
# Try to run the bundle install or update command. If that fails, it normally means that the
|
281
|
-
# a bad state that no longer reflects the top level one. In that case, we can remove the whole directory, try
|
280
|
+
# Try to run the bundle install or update command. If that fails, it normally means that the composed lockfile is
|
281
|
+
# in a bad state that no longer reflects the top level one. In that case, we can remove the whole directory, try
|
282
282
|
# another time and give up if it fails again
|
283
283
|
if !system(env, command) && !@retry && @custom_gemfile.exist?
|
284
284
|
@retry = true
|
285
285
|
@custom_dir.rmtree
|
286
|
-
$stderr.puts("Ruby LSP> Running bundle install failed. Trying to re-generate the
|
286
|
+
$stderr.puts("Ruby LSP> Running bundle install failed. Trying to re-generate the composed bundle from scratch")
|
287
287
|
return setup!
|
288
288
|
end
|
289
289
|
|
@@ -330,14 +330,14 @@ module RubyLsp
|
|
330
330
|
if @rails_app
|
331
331
|
return false if @dependencies.values_at("ruby-lsp", "ruby-lsp-rails", "debug").all?
|
332
332
|
|
333
|
-
# If the
|
334
|
-
# before updating
|
335
|
-
return false if
|
333
|
+
# If the composed lockfile doesn't include `ruby-lsp`, `ruby-lsp-rails` or `debug`, we need to run bundle
|
334
|
+
# install before updating
|
335
|
+
return false if composed_bundle_dependencies.values_at("ruby-lsp", "debug", "ruby-lsp-rails").any?(&:nil?)
|
336
336
|
else
|
337
337
|
return false if @dependencies.values_at("ruby-lsp", "debug").all?
|
338
338
|
|
339
|
-
# If the
|
340
|
-
return false if
|
339
|
+
# If the composed lockfile doesn't include `ruby-lsp` or `debug`, we need to run bundle install before updating
|
340
|
+
return false if composed_bundle_dependencies.values_at("ruby-lsp", "debug").any?(&:nil?)
|
341
341
|
end
|
342
342
|
|
343
343
|
# If the last updated file doesn't exist or was updated more than 4 hours ago, we should update
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -217,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
217
217
|
- !ruby/object:Gem::Version
|
218
218
|
version: '0'
|
219
219
|
requirements: []
|
220
|
-
rubygems_version: 3.5.
|
220
|
+
rubygems_version: 3.5.23
|
221
221
|
signing_key:
|
222
222
|
specification_version: 4
|
223
223
|
summary: An opinionated language server for Ruby
|