ruby-lsp 0.21.3 → 0.22.0
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 +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
|