ruby-lsp 0.17.7 → 0.17.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 671bab4fdbd77d1980781f55874d6cd448e1dcd11a6f534a176dfb00b098d2eb
4
- data.tar.gz: bd12fd2ef58588b3b5386fec48c0f561140efff180988f1971b67f635f0e1042
3
+ metadata.gz: e3a8a120b4cf3045cdeec98d2fb417ffdd300947b6426788d827346c43812ffc
4
+ data.tar.gz: b43a823e71fca99a9cf79b3a6ce19e7a0ec86e499b943aecae1666788859d055
5
5
  SHA512:
6
- metadata.gz: f3a2a3115e1745ad7263b7e113ef13795f9a1ed1ee118128a9ab2aaad703b17551acbd929f820700e6650efb76421418a13a78270ac8d3aaff080edc46731aee
7
- data.tar.gz: d29bb4d6c60727eef1a10905762a6d0c8946b7afc8aac6148ef970c8d752365f1577403497702f52a7211689cde7940b3d7cd1e1e0eb03d038e0dc6f7ceed738
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
- By creating a `.index.yml` file, these configurations can be overridden and tuned. Note that indexing dependent behavior, such as definition, hover, completion or workspace symbol will be impacted by the configurations placed here.
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.7
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 = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
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
- nesting = name.start_with?("::") ? [name.delete_prefix("::")] : @stack + [name.delete_prefix("::")]
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
@@ -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 Errno::ENOENT
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
- add_local_completions(node, name)
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
@@ -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
- send_message(Error.new(
102
- id: message[:id],
103
- code: Constant::ErrorCodes::INTERNAL_ERROR,
104
- message: e.full_message,
105
- data: {
106
- errorClass: e.class.name,
107
- errorMessage: e.message,
108
- backtrace: e.backtrace&.join("\n"),
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(indexing_config)
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 { params(config_hash: T::Hash[String, T.untyped]).void }
770
- def perform_initial_indexing(config_hash)
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
@@ -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.7
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 00:00:00.000000000 Z
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.14
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