ruby-lsp 0.18.3 → 0.19.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/core_ext/uri.rb +9 -4
  4. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +66 -8
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +56 -32
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +3 -2
  8. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +32 -8
  9. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +262 -0
  10. data/lib/ruby_indexer/ruby_indexer.rb +1 -0
  11. data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -0
  12. data/lib/ruby_indexer/test/constant_test.rb +8 -0
  13. data/lib/ruby_indexer/test/enhancements_test.rb +2 -0
  14. data/lib/ruby_indexer/test/instance_variables_test.rb +12 -0
  15. data/lib/ruby_indexer/test/method_test.rb +10 -0
  16. data/lib/ruby_indexer/test/rbs_indexer_test.rb +8 -0
  17. data/lib/ruby_indexer/test/reference_finder_test.rb +86 -0
  18. data/lib/ruby_lsp/addon.rb +79 -17
  19. data/lib/ruby_lsp/base_server.rb +6 -0
  20. data/lib/ruby_lsp/erb_document.rb +3 -2
  21. data/lib/ruby_lsp/global_state.rb +8 -0
  22. data/lib/ruby_lsp/internal.rb +3 -1
  23. data/lib/ruby_lsp/listeners/completion.rb +1 -1
  24. data/lib/ruby_lsp/listeners/hover.rb +57 -0
  25. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +24 -21
  26. data/lib/ruby_lsp/requests/completion_resolve.rb +29 -0
  27. data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
  28. data/lib/ruby_lsp/requests/rename.rb +189 -0
  29. data/lib/ruby_lsp/requests/support/common.rb +2 -2
  30. data/lib/ruby_lsp/requests/support/source_uri.rb +8 -1
  31. data/lib/ruby_lsp/scope.rb +47 -0
  32. data/lib/ruby_lsp/server.rb +64 -32
  33. data/lib/ruby_lsp/static_docs.rb +15 -0
  34. data/lib/ruby_lsp/store.rb +12 -0
  35. data/lib/ruby_lsp/test_helper.rb +1 -1
  36. data/lib/ruby_lsp/type_inferrer.rb +6 -1
  37. data/lib/ruby_lsp/utils.rb +3 -6
  38. data/static_docs/yield.md +81 -0
  39. metadata +19 -8
  40. data/lib/ruby_lsp/parameter_scope.rb +0 -33
@@ -67,6 +67,8 @@ module RubyLsp
67
67
  text_document_definition(message)
68
68
  when "textDocument/prepareTypeHierarchy"
69
69
  text_document_prepare_type_hierarchy(message)
70
+ when "textDocument/rename"
71
+ text_document_rename(message)
70
72
  when "typeHierarchy/supertypes"
71
73
  type_hierarchy_supertypes(message)
72
74
  when "typeHierarchy/subtypes"
@@ -123,9 +125,9 @@ module RubyLsp
123
125
  send_log_message("Error processing #{message[:method]}: #{e.full_message}", type: Constant::MessageType::ERROR)
124
126
  end
125
127
 
126
- sig { void }
127
- def load_addons
128
- errors = Addon.load_addons(@global_state, @outgoing_queue)
128
+ sig { params(include_project_addons: T::Boolean).void }
129
+ def load_addons(include_project_addons: true)
130
+ errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
129
131
 
130
132
  if errors.any?
131
133
  send_log_message(
@@ -142,7 +144,7 @@ module RubyLsp
142
144
  method: "window/showMessage",
143
145
  params: Interface::ShowMessageParams.new(
144
146
  type: Constant::MessageType::WARNING,
145
- message: "Error loading addons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
147
+ message: "Error loading add-ons:\n\n#{errored_addons.map(&:formatted_errors).join("\n\n")}",
146
148
  ),
147
149
  ),
148
150
  )
@@ -227,6 +229,7 @@ module RubyLsp
227
229
  workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.has_type_checker,
228
230
  signature_help_provider: signature_help_provider,
229
231
  type_hierarchy_provider: type_hierarchy_provider,
232
+ rename_provider: !@global_state.has_type_checker,
230
233
  experimental: {
231
234
  addon_detection: true,
232
235
  },
@@ -285,8 +288,9 @@ module RubyLsp
285
288
  rescue RuboCop::Error => e
286
289
  # The user may have provided unknown config switches in .rubocop or
287
290
  # is trying to load a non-existant config file.
288
- send_message(Notification.window_show_error(
291
+ send_message(Notification.window_show_message(
289
292
  "RuboCop configuration error: #{e.message}. Formatting will not be available.",
293
+ type: Constant::MessageType::ERROR,
290
294
  ))
291
295
  end
292
296
  end
@@ -319,14 +323,18 @@ module RubyLsp
319
323
  language_id: language_id,
320
324
  )
321
325
 
322
- if document.past_expensive_limit?
326
+ if document.past_expensive_limit? && text_document[:uri].scheme == "file"
327
+ log_message = <<~MESSAGE
328
+ The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
329
+ diagnostics will be disabled.
330
+ MESSAGE
331
+
323
332
  send_message(
324
333
  Notification.new(
325
- method: "window/showMessage",
326
- params: Interface::ShowMessageParams.new(
334
+ method: "window/logMessage",
335
+ params: Interface::LogMessageParams.new(
327
336
  type: Constant::MessageType::WARNING,
328
- message: "This file is too long. For performance reasons, semantic highlighting and " \
329
- "diagnostics will be disabled",
337
+ message: log_message,
330
338
  ),
331
339
  ),
332
340
  )
@@ -531,10 +539,16 @@ module RubyLsp
531
539
  response = Requests::Formatting.new(@global_state, document).perform
532
540
  send_message(Result.new(id: message[:id], response: response))
533
541
  rescue Requests::Request::InvalidFormatter => error
534
- send_message(Notification.window_show_error("Configuration error: #{error.message}"))
542
+ send_message(Notification.window_show_message(
543
+ "Configuration error: #{error.message}",
544
+ type: Constant::MessageType::ERROR,
545
+ ))
535
546
  send_empty_response(message[:id])
536
547
  rescue StandardError, LoadError => error
537
- send_message(Notification.window_show_error("Formatting error: #{error.message}"))
548
+ send_message(Notification.window_show_message(
549
+ "Formatting error: #{error.message}",
550
+ type: Constant::MessageType::ERROR,
551
+ ))
538
552
  send_empty_response(message[:id])
539
553
  end
540
554
 
@@ -602,6 +616,26 @@ module RubyLsp
602
616
  )
603
617
  end
604
618
 
619
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
620
+ def text_document_rename(message)
621
+ params = message[:params]
622
+ document = @store.get(params.dig(:textDocument, :uri))
623
+
624
+ unless document.is_a?(RubyDocument)
625
+ send_empty_response(message[:id])
626
+ return
627
+ end
628
+
629
+ send_message(
630
+ Result.new(
631
+ id: message[:id],
632
+ response: Requests::Rename.new(@global_state, @store, document, params).perform,
633
+ ),
634
+ )
635
+ rescue Requests::Rename::InvalidNameError => e
636
+ send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
637
+ end
638
+
605
639
  sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
606
640
  def sorbet_level(document)
607
641
  return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
@@ -673,30 +707,19 @@ module RubyLsp
673
707
  document = @store.get(uri)
674
708
 
675
709
  unless document.is_a?(RubyDocument)
676
- send_message(Notification.window_show_error("Code actions are currently only available for Ruby documents"))
677
- raise Requests::CodeActionResolve::CodeActionError
710
+ fail_request_and_notify(message[:id], "Code actions are currently only available for Ruby documents")
711
+ return
678
712
  end
679
713
 
680
714
  result = Requests::CodeActionResolve.new(document, params).perform
681
715
 
682
716
  case result
683
717
  when Requests::CodeActionResolve::Error::EmptySelection
684
- send_message(Notification.window_show_error("Invalid selection for Extract Variable refactor"))
685
- raise Requests::CodeActionResolve::CodeActionError
718
+ fail_request_and_notify(message[:id], "Invalid selection for extract variable refactor")
686
719
  when Requests::CodeActionResolve::Error::InvalidTargetRange
687
- send_message(
688
- Notification.window_show_error(
689
- "Couldn't find an appropriate location to place extracted refactor",
690
- ),
691
- )
692
- raise Requests::CodeActionResolve::CodeActionError
720
+ fail_request_and_notify(message[:id], "Couldn't find an appropriate location to place extracted refactor")
693
721
  when Requests::CodeActionResolve::Error::UnknownCodeAction
694
- send_message(
695
- Notification.window_show_error(
696
- "Unknown code action",
697
- ),
698
- )
699
- raise Requests::CodeActionResolve::CodeActionError
722
+ fail_request_and_notify(message[:id], "Unknown code action")
700
723
  else
701
724
  send_message(Result.new(id: message[:id], response: result))
702
725
  end
@@ -729,10 +752,16 @@ module RubyLsp
729
752
  ),
730
753
  )
731
754
  rescue Requests::Request::InvalidFormatter => error
732
- send_message(Notification.window_show_error("Configuration error: #{error.message}"))
755
+ send_message(Notification.window_show_message(
756
+ "Configuration error: #{error.message}",
757
+ type: Constant::MessageType::ERROR,
758
+ ))
733
759
  send_empty_response(message[:id])
734
760
  rescue StandardError, LoadError => error
735
- send_message(Notification.window_show_error("Error running diagnostics: #{error.message}"))
761
+ send_message(Notification.window_show_message(
762
+ "Error running diagnostics: #{error.message}",
763
+ type: Constant::MessageType::ERROR,
764
+ ))
736
765
  send_empty_response(message[:id])
737
766
  end
738
767
 
@@ -969,7 +998,9 @@ module RubyLsp
969
998
  false
970
999
  end
971
1000
  rescue StandardError => error
972
- send_message(Notification.window_show_error("Error while indexing: #{error.message}"))
1001
+ message = "Error while indexing (see [troubleshooting steps]" \
1002
+ "(https://shopify.github.io/ruby-lsp/troubleshooting#indexing)): #{error.message}"
1003
+ send_message(Notification.window_show_message(message, type: Constant::MessageType::ERROR))
973
1004
  end
974
1005
 
975
1006
  # Indexing produces a high number of short lived object allocations. That might lead to some fragmentation and
@@ -1054,8 +1085,9 @@ module RubyLsp
1054
1085
  @global_state.formatter = "none"
1055
1086
 
1056
1087
  send_message(
1057
- Notification.window_show_error(
1088
+ Notification.window_show_message(
1058
1089
  "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
1090
+ type: Constant::MessageType::ERROR,
1059
1091
  ),
1060
1092
  )
1061
1093
  end
@@ -0,0 +1,15 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ # The path to the `static_docs` directory, where we keep long-form static documentation
6
+ STATIC_DOCS_PATH = T.let(File.join(File.dirname(File.dirname(T.must(__dir__))), "static_docs"), String)
7
+
8
+ # A map of keyword => short documentation to be displayed on hover or completion
9
+ KEYWORD_DOCS = T.let(
10
+ {
11
+ "yield" => "Invokes the passed block with the given arguments",
12
+ }.freeze,
13
+ T::Hash[String, String],
14
+ )
15
+ end
@@ -99,6 +99,18 @@ module RubyLsp
99
99
  @state.delete(uri.to_s)
100
100
  end
101
101
 
102
+ sig { params(uri: URI::Generic).returns(T::Boolean) }
103
+ def key?(uri)
104
+ @state.key?(uri.to_s)
105
+ end
106
+
107
+ sig { params(block: T.proc.params(uri: String, document: Document[T.untyped]).void).void }
108
+ def each(&block)
109
+ @state.each do |uri, document|
110
+ block.call(uri, document)
111
+ end
112
+ end
113
+
102
114
  sig do
103
115
  type_parameters(:T)
104
116
  .params(
@@ -42,7 +42,7 @@ module RubyLsp
42
42
  RubyIndexer::IndexablePath.new(nil, T.must(uri.to_standardized_path)),
43
43
  source,
44
44
  )
45
- server.load_addons if load_addons
45
+ server.load_addons(include_project_addons: false) if load_addons
46
46
  block.call(server, uri)
47
47
  ensure
48
48
  if load_addons
@@ -89,7 +89,12 @@ module RubyLsp
89
89
 
90
90
  Type.new("#{parts.join("::")}::#{last}::<Class:#{last}>")
91
91
  else
92
- raw_receiver = node.receiver&.slice
92
+
93
+ raw_receiver = if receiver.is_a?(Prism::CallNode)
94
+ receiver.message
95
+ else
96
+ receiver&.slice
97
+ end
93
98
 
94
99
  if raw_receiver
95
100
  guessed_name = raw_receiver
@@ -64,14 +64,11 @@ module RubyLsp
64
64
  class << self
65
65
  extend T::Sig
66
66
 
67
- sig { params(message: String).returns(Notification) }
68
- def window_show_error(message)
67
+ sig { params(message: String, type: Integer).returns(Notification) }
68
+ def window_show_message(message, type: Constant::MessageType::INFO)
69
69
  new(
70
70
  method: "window/showMessage",
71
- params: Interface::ShowMessageParams.new(
72
- type: Constant::MessageType::ERROR,
73
- message: message,
74
- ),
71
+ params: Interface::ShowMessageParams.new(type: type, message: message),
75
72
  )
76
73
  end
77
74
 
@@ -0,0 +1,81 @@
1
+ # Yield
2
+
3
+ In Ruby, every method implicitly accepts a block, even when not included in the parameters list.
4
+
5
+ ```ruby
6
+ def foo
7
+ end
8
+
9
+ foo { 123 } # works!
10
+ ```
11
+
12
+ The `yield` keyword is used to invoke the block that was passed with arguments.
13
+
14
+ ```ruby
15
+ # Consider this method call. The block being passed to the method `foo` accepts an argument called `a`.
16
+ # It then takes whatever argument was passed and multiplies it by 2
17
+ foo do |a|
18
+ a * 2
19
+ end
20
+
21
+ # In the `foo` method declaration, we can use `yield` to invoke the block that was passed and provide the block
22
+ # with the value for the `a` argument
23
+ def foo
24
+ # Invoke the block passed to `foo` with the number 10 as the argument `a`
25
+ result = yield(10)
26
+ puts result # Will print 20
27
+ end
28
+ ```
29
+
30
+ If `yield` is used to invoke the block, but no block was passed, that will result in a local jump error.
31
+
32
+ ```ruby
33
+ # If we invoke `foo` without a block, trying to `yield` will fail
34
+ foo
35
+
36
+ # `foo': no block given (yield) (LocalJumpError)
37
+ ```
38
+
39
+ We can decide to use `yield` conditionally by using Ruby's `block_given?` method, which will return `true` if a block
40
+ was passed to the method.
41
+
42
+ ```ruby
43
+ def foo
44
+ # If a block is passed when invoking `foo`, call the block with argument 10 and print the result.
45
+ # Otherwise, just print that no block was passed
46
+ if block_given?
47
+ result = yield(10)
48
+ puts result
49
+ else
50
+ puts "No block passed!"
51
+ end
52
+ end
53
+
54
+ foo do |a|
55
+ a * 2
56
+ end
57
+ # => 20
58
+
59
+ foo
60
+ # => No block passed!
61
+ ```
62
+
63
+ ## Block parameter
64
+
65
+ In addition to implicit blocks, Ruby also allows developers to use explicit block parameters as part of the method's
66
+ signature. In this scenario, we can use the reference to the block directly instead of relying on the `yield` keyword.
67
+
68
+ ```ruby
69
+ # Block parameters are prefixed with & and a name
70
+ def foo(&my_block_param)
71
+ # If a block was passed to `foo`, `my_block_param` will be a `Proc` object. Otherwise, it will be `nil`. We can use
72
+ # that to check for its presence
73
+ if my_block_param
74
+ # Explicit block parameters are invoked using the method `call`, which is present in all `Proc` objects
75
+ result = my_block_param.call(10)
76
+ puts result
77
+ else
78
+ puts "No block passed!"
79
+ end
80
+ end
81
+ ```
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.18.3
4
+ version: 0.19.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-09-24 00:00:00.000000000 Z
11
+ date: 2024-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: prism
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ - - "<"
32
35
  - !ruby/object:Gem::Version
33
- version: '1.0'
36
+ version: '2.0'
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '1.1'
44
+ - - "<"
39
45
  - !ruby/object:Gem::Version
40
- version: '1.0'
46
+ version: '2.0'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rbs
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -99,6 +105,7 @@ files:
99
105
  - lib/ruby_indexer/lib/ruby_indexer/location.rb
100
106
  - lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
101
107
  - lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
108
+ - lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
102
109
  - lib/ruby_indexer/ruby_indexer.rb
103
110
  - lib/ruby_indexer/test/classes_and_modules_test.rb
104
111
  - lib/ruby_indexer/test/configuration_test.rb
@@ -109,6 +116,7 @@ files:
109
116
  - lib/ruby_indexer/test/method_test.rb
110
117
  - lib/ruby_indexer/test/prefix_tree_test.rb
111
118
  - lib/ruby_indexer/test/rbs_indexer_test.rb
119
+ - lib/ruby_indexer/test/reference_finder_test.rb
112
120
  - lib/ruby_indexer/test/test_case.rb
113
121
  - lib/ruby_lsp/addon.rb
114
122
  - lib/ruby_lsp/base_server.rb
@@ -129,7 +137,6 @@ files:
129
137
  - lib/ruby_lsp/listeners/signature_help.rb
130
138
  - lib/ruby_lsp/load_sorbet.rb
131
139
  - lib/ruby_lsp/node_context.rb
132
- - lib/ruby_lsp/parameter_scope.rb
133
140
  - lib/ruby_lsp/rbs_document.rb
134
141
  - lib/ruby_lsp/requests/code_action_resolve.rb
135
142
  - lib/ruby_lsp/requests/code_actions.rb
@@ -147,6 +154,7 @@ files:
147
154
  - lib/ruby_lsp/requests/inlay_hints.rb
148
155
  - lib/ruby_lsp/requests/on_type_formatting.rb
149
156
  - lib/ruby_lsp/requests/prepare_type_hierarchy.rb
157
+ - lib/ruby_lsp/requests/rename.rb
150
158
  - lib/ruby_lsp/requests/request.rb
151
159
  - lib/ruby_lsp/requests/selection_ranges.rb
152
160
  - lib/ruby_lsp/requests/semantic_highlighting.rb
@@ -171,12 +179,15 @@ files:
171
179
  - lib/ruby_lsp/response_builders/semantic_highlighting.rb
172
180
  - lib/ruby_lsp/response_builders/signature_help.rb
173
181
  - lib/ruby_lsp/ruby_document.rb
182
+ - lib/ruby_lsp/scope.rb
174
183
  - lib/ruby_lsp/server.rb
175
184
  - lib/ruby_lsp/setup_bundler.rb
185
+ - lib/ruby_lsp/static_docs.rb
176
186
  - lib/ruby_lsp/store.rb
177
187
  - lib/ruby_lsp/test_helper.rb
178
188
  - lib/ruby_lsp/type_inferrer.rb
179
189
  - lib/ruby_lsp/utils.rb
190
+ - static_docs/yield.md
180
191
  homepage: https://github.com/Shopify/ruby-lsp
181
192
  licenses:
182
193
  - MIT
@@ -198,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
209
  - !ruby/object:Gem::Version
199
210
  version: '0'
200
211
  requirements: []
201
- rubygems_version: 3.5.18
212
+ rubygems_version: 3.5.20
202
213
  signing_key:
203
214
  specification_version: 4
204
215
  summary: An opinionated language server for Ruby
@@ -1,33 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyLsp
5
- class ParameterScope
6
- extend T::Sig
7
-
8
- sig { returns(T.nilable(ParameterScope)) }
9
- attr_reader :parent
10
-
11
- sig { params(parent: T.nilable(ParameterScope)).void }
12
- def initialize(parent = nil)
13
- @parent = parent
14
- @parameters = T.let(Set.new, T::Set[Symbol])
15
- end
16
-
17
- sig { params(name: T.any(String, Symbol)).void }
18
- def <<(name)
19
- @parameters << name.to_sym
20
- end
21
-
22
- sig { params(name: T.any(Symbol, String)).returns(Symbol) }
23
- def type_for(name)
24
- parameter?(name) ? :parameter : :variable
25
- end
26
-
27
- sig { params(name: T.any(Symbol, String)).returns(T::Boolean) }
28
- def parameter?(name)
29
- sym = name.to_sym
30
- @parameters.include?(sym) || (!@parent.nil? && @parent.parameter?(sym))
31
- end
32
- end
33
- end