ruby-lsp 0.17.7 → 0.17.8
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/README.md +6 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +25 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +16 -3
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +38 -0
- data/lib/ruby_lsp/base_server.rb +1 -1
- data/lib/ruby_lsp/listeners/completion.rb +67 -6
- data/lib/ruby_lsp/server.rb +67 -37
- data/lib/ruby_lsp/store.rb +4 -0
- metadata +3 -5
- data/exe/ruby-lsp-doctor +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3a8a120b4cf3045cdeec98d2fb417ffdd300947b6426788d827346c43812ffc
|
4
|
+
data.tar.gz: b43a823e71fca99a9cf79b3a6ce19e7a0ec86e499b943aecae1666788859d055
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9662d88d3f37b4fd7b744a69f38f2a20cd4bf00291f63968578e50bef3400916870efdcf58b6e81dd78a6829c4f8a7dd174e6d212ddc3a265420b29eaa2b6d8a
|
7
|
+
data.tar.gz: b788ef9185e02a732cd2dccef6273d8dd3fb471ebb8c2300b3ac78cc48d3952eee378a777cc4cf4ceb927590fff7bcdfc57c95f0924b21e589eb850ec9128641
|
data/README.md
CHANGED
@@ -78,7 +78,12 @@ default gems, except for
|
|
78
78
|
- Gems that only appear under the `:development` group
|
79
79
|
- All Ruby files under `test/**/*.rb`
|
80
80
|
|
81
|
-
|
81
|
+
This behaviour can be overridden and tuned. Learn how to configure it [for VS Code](vscode/README.md#Indexing-Configuration) or [for other editors](EDITORS.md#Indexing-Configuration).
|
82
|
+
|
83
|
+
Note that indexing-dependent behavior, such as definition, hover, completion or workspace symbol will be impacted by
|
84
|
+
the configuration changes.
|
85
|
+
|
86
|
+
The older approach of using a `.index.yml` file has been deprecated and will be removed in a future release.
|
82
87
|
|
83
88
|
```yaml
|
84
89
|
# Exclude files based on a given pattern. Often used to exclude test files or fixtures
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.17.
|
1
|
+
0.17.8
|
data/exe/ruby-lsp
CHANGED
@@ -36,6 +36,10 @@ parser = OptionParser.new do |opts|
|
|
36
36
|
options[:experimental] = true
|
37
37
|
end
|
38
38
|
|
39
|
+
opts.on("--doctor", "Run troubleshooting steps") do
|
40
|
+
options[:doctor] = true
|
41
|
+
end
|
42
|
+
|
39
43
|
opts.on("-h", "--help", "Print this help") do
|
40
44
|
puts opts.help
|
41
45
|
puts
|
@@ -107,4 +111,25 @@ if options[:time_index]
|
|
107
111
|
return
|
108
112
|
end
|
109
113
|
|
114
|
+
if options[:doctor]
|
115
|
+
if File.exist?(".index.yml")
|
116
|
+
begin
|
117
|
+
config = YAML.parse_file(".index.yml").to_ruby
|
118
|
+
rescue => e
|
119
|
+
abort("Error parsing config: #{e.message}")
|
120
|
+
end
|
121
|
+
RubyIndexer.configuration.apply_config(config)
|
122
|
+
end
|
123
|
+
|
124
|
+
index = RubyIndexer::Index.new
|
125
|
+
|
126
|
+
puts "Globbing for indexable files"
|
127
|
+
|
128
|
+
RubyIndexer.configuration.indexables.each do |indexable|
|
129
|
+
puts "indexing: #{indexable.full_path}"
|
130
|
+
index.index_single(indexable)
|
131
|
+
end
|
132
|
+
return
|
133
|
+
end
|
134
|
+
|
110
135
|
RubyLsp::Server.new.start
|
@@ -71,7 +71,7 @@ module RubyIndexer
|
|
71
71
|
|
72
72
|
superclass = node.superclass
|
73
73
|
|
74
|
-
nesting =
|
74
|
+
nesting = actual_nesting(name)
|
75
75
|
|
76
76
|
parent_class = case superclass
|
77
77
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
@@ -119,8 +119,7 @@ module RubyIndexer
|
|
119
119
|
|
120
120
|
comments = collect_comments(node)
|
121
121
|
|
122
|
-
|
123
|
-
entry = Entry::Module.new(nesting, @file_path, node.location, constant_path.location, comments)
|
122
|
+
entry = Entry::Module.new(actual_nesting(name), @file_path, node.location, constant_path.location, comments)
|
124
123
|
|
125
124
|
@owner_stack << entry
|
126
125
|
@index.add(entry)
|
@@ -709,5 +708,19 @@ module RubyIndexer
|
|
709
708
|
:"(#{names_with_commas})"
|
710
709
|
end
|
711
710
|
end
|
711
|
+
|
712
|
+
sig { params(name: String).returns(T::Array[String]) }
|
713
|
+
def actual_nesting(name)
|
714
|
+
nesting = @stack + [name]
|
715
|
+
corrected_nesting = []
|
716
|
+
|
717
|
+
nesting.reverse_each do |name|
|
718
|
+
corrected_nesting.prepend(name.delete_prefix("::"))
|
719
|
+
|
720
|
+
break if name.start_with?("::")
|
721
|
+
end
|
722
|
+
|
723
|
+
corrected_nesting
|
724
|
+
end
|
712
725
|
end
|
713
726
|
end
|
@@ -546,5 +546,43 @@ module RubyIndexer
|
|
546
546
|
assert_equal(7, name_location.start_column)
|
547
547
|
assert_equal(10, name_location.end_column)
|
548
548
|
end
|
549
|
+
|
550
|
+
def test_indexing_namespaces_inside_top_level_references
|
551
|
+
index(<<~RUBY)
|
552
|
+
module ::Foo
|
553
|
+
class Bar
|
554
|
+
end
|
555
|
+
end
|
556
|
+
RUBY
|
557
|
+
|
558
|
+
# We want to explicitly verify that we didn't introduce the leading `::` by accident, but `Index#[]` deletes the
|
559
|
+
# prefix when we use `refute_entry`
|
560
|
+
entries = @index.instance_variable_get(:@entries)
|
561
|
+
refute(entries.key?("::Foo"))
|
562
|
+
refute(entries.key?("::Foo::Bar"))
|
563
|
+
assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:0-0:3-3")
|
564
|
+
assert_entry("Foo::Bar", Entry::Class, "/fake/path/foo.rb:1-2:2-5")
|
565
|
+
end
|
566
|
+
|
567
|
+
def test_indexing_namespaces_inside_nested_top_level_references
|
568
|
+
index(<<~RUBY)
|
569
|
+
class Baz
|
570
|
+
module ::Foo
|
571
|
+
class Bar
|
572
|
+
end
|
573
|
+
|
574
|
+
class ::Qux
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
RUBY
|
579
|
+
|
580
|
+
refute_entry("Baz::Foo")
|
581
|
+
refute_entry("Baz::Foo::Bar")
|
582
|
+
assert_entry("Baz", Entry::Class, "/fake/path/foo.rb:0-0:8-3")
|
583
|
+
assert_entry("Foo", Entry::Module, "/fake/path/foo.rb:1-2:7-5")
|
584
|
+
assert_entry("Foo::Bar", Entry::Class, "/fake/path/foo.rb:2-4:3-7")
|
585
|
+
assert_entry("Qux", Entry::Class, "/fake/path/foo.rb:5-4:6-7")
|
586
|
+
end
|
549
587
|
end
|
550
588
|
end
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -53,7 +53,7 @@ module RubyLsp
|
|
53
53
|
|
54
54
|
# We don't want to try to parse documents on text synchronization notifications
|
55
55
|
@store.get(parsed_uri).parse unless method.start_with?("textDocument/did")
|
56
|
-
rescue
|
56
|
+
rescue Store::NonExistingDocumentError
|
57
57
|
# If we receive a request for a file that no longer exists, we don't want to fail
|
58
58
|
end
|
59
59
|
end
|
@@ -7,6 +7,50 @@ module RubyLsp
|
|
7
7
|
extend T::Sig
|
8
8
|
include Requests::Support::Common
|
9
9
|
|
10
|
+
KEYWORDS = [
|
11
|
+
"alias",
|
12
|
+
"and",
|
13
|
+
"begin",
|
14
|
+
"BEGIN",
|
15
|
+
"break",
|
16
|
+
"case",
|
17
|
+
"class",
|
18
|
+
"def",
|
19
|
+
"defined?",
|
20
|
+
"do",
|
21
|
+
"else",
|
22
|
+
"elsif",
|
23
|
+
"end",
|
24
|
+
"END",
|
25
|
+
"ensure",
|
26
|
+
"false",
|
27
|
+
"for",
|
28
|
+
"if",
|
29
|
+
"in",
|
30
|
+
"module",
|
31
|
+
"next",
|
32
|
+
"nil",
|
33
|
+
"not",
|
34
|
+
"or",
|
35
|
+
"redo",
|
36
|
+
"rescue",
|
37
|
+
"retry",
|
38
|
+
"return",
|
39
|
+
"self",
|
40
|
+
"super",
|
41
|
+
"then",
|
42
|
+
"true",
|
43
|
+
"undef",
|
44
|
+
"unless",
|
45
|
+
"until",
|
46
|
+
"when",
|
47
|
+
"while",
|
48
|
+
"yield",
|
49
|
+
"__ENCODING__",
|
50
|
+
"__FILE__",
|
51
|
+
"__LINE__",
|
52
|
+
].freeze
|
53
|
+
|
10
54
|
sig do
|
11
55
|
params(
|
12
56
|
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
|
@@ -277,7 +321,11 @@ module RubyLsp
|
|
277
321
|
|
278
322
|
sig { params(node: Prism::CallNode, name: String).void }
|
279
323
|
def complete_methods(node, name)
|
280
|
-
|
324
|
+
# If the node has a receiver, then we don't need to provide local nor keyword completions
|
325
|
+
if !@global_state.has_type_checker && !node.receiver
|
326
|
+
add_local_completions(node, name)
|
327
|
+
add_keyword_completions(node, name)
|
328
|
+
end
|
281
329
|
|
282
330
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
283
331
|
return unless type
|
@@ -326,11 +374,6 @@ module RubyLsp
|
|
326
374
|
|
327
375
|
sig { params(node: Prism::CallNode, name: String).void }
|
328
376
|
def add_local_completions(node, name)
|
329
|
-
return if @global_state.has_type_checker
|
330
|
-
|
331
|
-
# If the call node has a receiver, then it cannot possibly be a local variable
|
332
|
-
return if node.receiver
|
333
|
-
|
334
377
|
range = range_from_location(T.must(node.message_loc))
|
335
378
|
|
336
379
|
@node_context.locals_for_scope.each do |local|
|
@@ -349,6 +392,24 @@ module RubyLsp
|
|
349
392
|
end
|
350
393
|
end
|
351
394
|
|
395
|
+
sig { params(node: Prism::CallNode, name: String).void }
|
396
|
+
def add_keyword_completions(node, name)
|
397
|
+
range = range_from_location(T.must(node.message_loc))
|
398
|
+
|
399
|
+
KEYWORDS.each do |keyword|
|
400
|
+
next unless keyword.start_with?(name)
|
401
|
+
|
402
|
+
@response_builder << Interface::CompletionItem.new(
|
403
|
+
label: keyword,
|
404
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: keyword),
|
405
|
+
kind: Constant::CompletionItemKind::KEYWORD,
|
406
|
+
data: {
|
407
|
+
skip_resolve: true,
|
408
|
+
},
|
409
|
+
)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
352
413
|
sig { params(label: String, node: Prism::StringNode).returns(Interface::CompletionItem) }
|
353
414
|
def build_completion(label, node)
|
354
415
|
# We should use the content location as we only replace the content and not the delimiters of the string
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -98,16 +98,27 @@ module RubyLsp
|
|
98
98
|
rescue StandardError, LoadError => e
|
99
99
|
# If an error occurred in a request, we have to return an error response or else the editor will hang
|
100
100
|
if message[:id]
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
101
|
+
# If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
|
102
|
+
# from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
|
103
|
+
# reporting these to our telemetry
|
104
|
+
if e.is_a?(Store::NonExistingDocumentError)
|
105
|
+
send_message(Error.new(
|
106
|
+
id: message[:id],
|
107
|
+
code: Constant::ErrorCodes::INVALID_PARAMS,
|
108
|
+
message: e.full_message,
|
109
|
+
))
|
110
|
+
else
|
111
|
+
send_message(Error.new(
|
112
|
+
id: message[:id],
|
113
|
+
code: Constant::ErrorCodes::INTERNAL_ERROR,
|
114
|
+
message: e.full_message,
|
115
|
+
data: {
|
116
|
+
errorClass: e.class.name,
|
117
|
+
errorMessage: e.message,
|
118
|
+
backtrace: e.backtrace&.join("\n"),
|
119
|
+
},
|
120
|
+
))
|
121
|
+
end
|
111
122
|
end
|
112
123
|
|
113
124
|
$stderr.puts("Error processing #{message[:method]}: #{e.full_message}")
|
@@ -243,6 +254,8 @@ module RubyLsp
|
|
243
254
|
)
|
244
255
|
end
|
245
256
|
|
257
|
+
process_indexing_configuration(options.dig(:initializationOptions, :indexing))
|
258
|
+
|
246
259
|
begin_progress("indexing-progress", "Ruby LSP: indexing files")
|
247
260
|
end
|
248
261
|
|
@@ -251,28 +264,6 @@ module RubyLsp
|
|
251
264
|
load_addons
|
252
265
|
RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
|
253
266
|
|
254
|
-
indexing_config = {}
|
255
|
-
|
256
|
-
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
|
257
|
-
index_path = File.join(@global_state.workspace_path, ".index.yml")
|
258
|
-
|
259
|
-
if File.exist?(index_path)
|
260
|
-
begin
|
261
|
-
indexing_config = YAML.parse_file(index_path).to_ruby
|
262
|
-
rescue Psych::SyntaxError => e
|
263
|
-
message = "Syntax error while loading configuration: #{e.message}"
|
264
|
-
send_message(
|
265
|
-
Notification.new(
|
266
|
-
method: "window/showMessage",
|
267
|
-
params: Interface::ShowMessageParams.new(
|
268
|
-
type: Constant::MessageType::WARNING,
|
269
|
-
message: message,
|
270
|
-
),
|
271
|
-
),
|
272
|
-
)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
267
|
if defined?(Requests::Support::RuboCopFormatter)
|
277
268
|
@global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
|
278
269
|
end
|
@@ -280,7 +271,7 @@ module RubyLsp
|
|
280
271
|
@global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
|
281
272
|
end
|
282
273
|
|
283
|
-
perform_initial_indexing
|
274
|
+
perform_initial_indexing
|
284
275
|
check_formatter_is_available
|
285
276
|
end
|
286
277
|
|
@@ -766,12 +757,10 @@ module RubyLsp
|
|
766
757
|
Addon.addons.each(&:deactivate)
|
767
758
|
end
|
768
759
|
|
769
|
-
sig {
|
770
|
-
def perform_initial_indexing
|
760
|
+
sig { void }
|
761
|
+
def perform_initial_indexing
|
771
762
|
# The begin progress invocation happens during `initialize`, so that the notification is sent before we are
|
772
763
|
# stuck indexing files
|
773
|
-
RubyIndexer.configuration.apply_config(config_hash)
|
774
|
-
|
775
764
|
Thread.new do
|
776
765
|
begin
|
777
766
|
RubyIndexer::RBSIndexer.new(@global_state.index).index_ruby_core
|
@@ -872,5 +861,46 @@ module RubyLsp
|
|
872
861
|
)
|
873
862
|
end
|
874
863
|
end
|
864
|
+
|
865
|
+
sig { params(indexing_options: T.nilable(T::Hash[Symbol, T.untyped])).void }
|
866
|
+
def process_indexing_configuration(indexing_options)
|
867
|
+
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
|
868
|
+
index_path = File.join(@global_state.workspace_path, ".index.yml")
|
869
|
+
|
870
|
+
if File.exist?(index_path)
|
871
|
+
begin
|
872
|
+
RubyIndexer.configuration.apply_config(YAML.parse_file(index_path).to_ruby)
|
873
|
+
send_message(
|
874
|
+
Notification.new(
|
875
|
+
method: "window/showMessage",
|
876
|
+
params: Interface::ShowMessageParams.new(
|
877
|
+
type: Constant::MessageType::WARNING,
|
878
|
+
message: "The .index.yml configuration file is deprecated. " \
|
879
|
+
"Please use editor settings to configure the index",
|
880
|
+
),
|
881
|
+
),
|
882
|
+
)
|
883
|
+
rescue Psych::SyntaxError => e
|
884
|
+
message = "Syntax error while loading configuration: #{e.message}"
|
885
|
+
send_message(
|
886
|
+
Notification.new(
|
887
|
+
method: "window/showMessage",
|
888
|
+
params: Interface::ShowMessageParams.new(
|
889
|
+
type: Constant::MessageType::WARNING,
|
890
|
+
message: message,
|
891
|
+
),
|
892
|
+
),
|
893
|
+
)
|
894
|
+
end
|
895
|
+
return
|
896
|
+
end
|
897
|
+
|
898
|
+
return unless indexing_options
|
899
|
+
|
900
|
+
# The index expects snake case configurations, but VS Code standardizes on camel case settings
|
901
|
+
RubyIndexer.configuration.apply_config(
|
902
|
+
indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase },
|
903
|
+
)
|
904
|
+
end
|
875
905
|
end
|
876
906
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -5,6 +5,8 @@ module RubyLsp
|
|
5
5
|
class Store
|
6
6
|
extend T::Sig
|
7
7
|
|
8
|
+
class NonExistingDocumentError < StandardError; end
|
9
|
+
|
8
10
|
sig { returns(T::Boolean) }
|
9
11
|
attr_accessor :supports_progress
|
10
12
|
|
@@ -45,6 +47,8 @@ module RubyLsp
|
|
45
47
|
end
|
46
48
|
set(uri: uri, source: File.binread(path), version: 0, language_id: language_id)
|
47
49
|
T.must(@state[uri.to_s])
|
50
|
+
rescue Errno::ENOENT
|
51
|
+
raise NonExistingDocumentError, uri.to_s
|
48
52
|
end
|
49
53
|
|
50
54
|
sig do
|
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.17.
|
4
|
+
version: 0.17.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -84,7 +84,6 @@ email:
|
|
84
84
|
executables:
|
85
85
|
- ruby-lsp
|
86
86
|
- ruby-lsp-check
|
87
|
-
- ruby-lsp-doctor
|
88
87
|
extensions: []
|
89
88
|
extra_rdoc_files: []
|
90
89
|
files:
|
@@ -93,7 +92,6 @@ files:
|
|
93
92
|
- VERSION
|
94
93
|
- exe/ruby-lsp
|
95
94
|
- exe/ruby-lsp-check
|
96
|
-
- exe/ruby-lsp-doctor
|
97
95
|
- lib/core_ext/uri.rb
|
98
96
|
- lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
|
99
97
|
- lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
|
@@ -206,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
204
|
- !ruby/object:Gem::Version
|
207
205
|
version: '0'
|
208
206
|
requirements: []
|
209
|
-
rubygems_version: 3.5.
|
207
|
+
rubygems_version: 3.5.15
|
210
208
|
signing_key:
|
211
209
|
specification_version: 4
|
212
210
|
summary: An opinionated language server for Ruby
|
data/exe/ruby-lsp-doctor
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
5
|
-
require "ruby_lsp/internal"
|
6
|
-
|
7
|
-
if File.exist?(".index.yml")
|
8
|
-
begin
|
9
|
-
config = YAML.parse_file(".index.yml").to_ruby
|
10
|
-
rescue => e
|
11
|
-
abort("Error parsing config: #{e.message}")
|
12
|
-
end
|
13
|
-
RubyIndexer.configuration.apply_config(config)
|
14
|
-
end
|
15
|
-
|
16
|
-
index = RubyIndexer::Index.new
|
17
|
-
|
18
|
-
puts "Globbing for indexable files"
|
19
|
-
|
20
|
-
RubyIndexer.configuration.indexables.each do |indexable|
|
21
|
-
puts "indexing: #{indexable.full_path}"
|
22
|
-
index.index_single(indexable)
|
23
|
-
end
|