ruby-lsp 0.20.1 → 0.22.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +19 -4
  5. data/exe/ruby-lsp-launcher +124 -0
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
  7. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +233 -59
  8. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +34 -16
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +15 -15
  11. data/lib/ruby_indexer/test/classes_and_modules_test.rb +4 -4
  12. data/lib/ruby_indexer/test/configuration_test.rb +10 -0
  13. data/lib/ruby_indexer/test/constant_test.rb +8 -8
  14. data/lib/ruby_indexer/test/enhancements_test.rb +169 -41
  15. data/lib/ruby_indexer/test/index_test.rb +41 -2
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
  17. data/lib/ruby_indexer/test/method_test.rb +139 -0
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  19. data/lib/ruby_lsp/addon.rb +9 -2
  20. data/lib/ruby_lsp/base_server.rb +14 -5
  21. data/lib/ruby_lsp/client_capabilities.rb +67 -0
  22. data/lib/ruby_lsp/document.rb +1 -1
  23. data/lib/ruby_lsp/global_state.rb +33 -20
  24. data/lib/ruby_lsp/internal.rb +3 -0
  25. data/lib/ruby_lsp/listeners/completion.rb +62 -0
  26. data/lib/ruby_lsp/listeners/definition.rb +48 -13
  27. data/lib/ruby_lsp/listeners/document_highlight.rb +91 -4
  28. data/lib/ruby_lsp/listeners/document_symbol.rb +37 -4
  29. data/lib/ruby_lsp/listeners/hover.rb +52 -0
  30. data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
  31. data/lib/ruby_lsp/requests/completion.rb +7 -1
  32. data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
  33. data/lib/ruby_lsp/requests/definition.rb +28 -11
  34. data/lib/ruby_lsp/requests/document_highlight.rb +7 -1
  35. data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
  36. data/lib/ruby_lsp/requests/hover.rb +26 -6
  37. data/lib/ruby_lsp/requests/rename.rb +1 -1
  38. data/lib/ruby_lsp/requests/request.rb +1 -1
  39. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +12 -1
  40. data/lib/ruby_lsp/scripts/compose_bundle.rb +20 -0
  41. data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +8 -0
  42. data/lib/ruby_lsp/server.rb +85 -55
  43. data/lib/ruby_lsp/setup_bundler.rb +154 -47
  44. data/lib/ruby_lsp/store.rb +0 -4
  45. data/lib/ruby_lsp/utils.rb +63 -0
  46. metadata +8 -3
@@ -41,6 +41,9 @@ module RubyLsp
41
41
  :on_module_node_enter,
42
42
  :on_module_node_leave,
43
43
  :on_instance_variable_write_node_enter,
44
+ :on_instance_variable_operator_write_node_enter,
45
+ :on_instance_variable_or_write_node_enter,
46
+ :on_instance_variable_and_write_node_enter,
44
47
  :on_class_variable_write_node_enter,
45
48
  :on_singleton_class_node_enter,
46
49
  :on_singleton_class_node_leave,
@@ -249,21 +252,51 @@ module RubyLsp
249
252
  @response_builder.pop
250
253
  end
251
254
 
255
+ sig { params(node: Prism::ClassVariableWriteNode).void }
256
+ def on_class_variable_write_node_enter(node)
257
+ create_document_symbol(
258
+ name: node.name.to_s,
259
+ kind: Constant::SymbolKind::VARIABLE,
260
+ range_location: node.name_loc,
261
+ selection_range_location: node.name_loc,
262
+ )
263
+ end
264
+
252
265
  sig { params(node: Prism::InstanceVariableWriteNode).void }
253
266
  def on_instance_variable_write_node_enter(node)
254
267
  create_document_symbol(
255
268
  name: node.name.to_s,
256
- kind: Constant::SymbolKind::VARIABLE,
269
+ kind: Constant::SymbolKind::FIELD,
257
270
  range_location: node.name_loc,
258
271
  selection_range_location: node.name_loc,
259
272
  )
260
273
  end
261
274
 
262
- sig { params(node: Prism::ClassVariableWriteNode).void }
263
- def on_class_variable_write_node_enter(node)
275
+ sig { params(node: Prism::InstanceVariableOperatorWriteNode).void }
276
+ def on_instance_variable_operator_write_node_enter(node)
264
277
  create_document_symbol(
265
278
  name: node.name.to_s,
266
- kind: Constant::SymbolKind::VARIABLE,
279
+ kind: Constant::SymbolKind::FIELD,
280
+ range_location: node.name_loc,
281
+ selection_range_location: node.name_loc,
282
+ )
283
+ end
284
+
285
+ sig { params(node: Prism::InstanceVariableOrWriteNode).void }
286
+ def on_instance_variable_or_write_node_enter(node)
287
+ create_document_symbol(
288
+ name: node.name.to_s,
289
+ kind: Constant::SymbolKind::FIELD,
290
+ range_location: node.name_loc,
291
+ selection_range_location: node.name_loc,
292
+ )
293
+ end
294
+
295
+ sig { params(node: Prism::InstanceVariableAndWriteNode).void }
296
+ def on_instance_variable_and_write_node_enter(node)
297
+ create_document_symbol(
298
+ name: node.name.to_s,
299
+ kind: Constant::SymbolKind::FIELD,
267
300
  range_location: node.name_loc,
268
301
  selection_range_location: node.name_loc,
269
302
  )
@@ -13,6 +13,12 @@ module RubyLsp
13
13
  Prism::ConstantReadNode,
14
14
  Prism::ConstantWriteNode,
15
15
  Prism::ConstantPathNode,
16
+ Prism::GlobalVariableAndWriteNode,
17
+ Prism::GlobalVariableOperatorWriteNode,
18
+ Prism::GlobalVariableOrWriteNode,
19
+ Prism::GlobalVariableReadNode,
20
+ Prism::GlobalVariableTargetNode,
21
+ Prism::GlobalVariableWriteNode,
16
22
  Prism::InstanceVariableReadNode,
17
23
  Prism::InstanceVariableAndWriteNode,
18
24
  Prism::InstanceVariableOperatorWriteNode,
@@ -62,6 +68,12 @@ module RubyLsp
62
68
  :on_constant_write_node_enter,
63
69
  :on_constant_path_node_enter,
64
70
  :on_call_node_enter,
71
+ :on_global_variable_and_write_node_enter,
72
+ :on_global_variable_operator_write_node_enter,
73
+ :on_global_variable_or_write_node_enter,
74
+ :on_global_variable_read_node_enter,
75
+ :on_global_variable_target_node_enter,
76
+ :on_global_variable_write_node_enter,
65
77
  :on_instance_variable_read_node_enter,
66
78
  :on_instance_variable_write_node_enter,
67
79
  :on_instance_variable_and_write_node_enter,
@@ -128,6 +140,36 @@ module RubyLsp
128
140
  handle_method_hover(message)
129
141
  end
130
142
 
143
+ sig { params(node: Prism::GlobalVariableAndWriteNode).void }
144
+ def on_global_variable_and_write_node_enter(node)
145
+ handle_global_variable_hover(node.name.to_s)
146
+ end
147
+
148
+ sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
149
+ def on_global_variable_operator_write_node_enter(node)
150
+ handle_global_variable_hover(node.name.to_s)
151
+ end
152
+
153
+ sig { params(node: Prism::GlobalVariableOrWriteNode).void }
154
+ def on_global_variable_or_write_node_enter(node)
155
+ handle_global_variable_hover(node.name.to_s)
156
+ end
157
+
158
+ sig { params(node: Prism::GlobalVariableReadNode).void }
159
+ def on_global_variable_read_node_enter(node)
160
+ handle_global_variable_hover(node.name.to_s)
161
+ end
162
+
163
+ sig { params(node: Prism::GlobalVariableTargetNode).void }
164
+ def on_global_variable_target_node_enter(node)
165
+ handle_global_variable_hover(node.name.to_s)
166
+ end
167
+
168
+ sig { params(node: Prism::GlobalVariableWriteNode).void }
169
+ def on_global_variable_write_node_enter(node)
170
+ handle_global_variable_hover(node.name.to_s)
171
+ end
172
+
131
173
  sig { params(node: Prism::InstanceVariableReadNode).void }
132
174
  def on_instance_variable_read_node_enter(node)
133
175
  handle_instance_variable_hover(node.name.to_s)
@@ -265,6 +307,16 @@ module RubyLsp
265
307
  # If by any chance we haven't indexed the owner, then there's no way to find the right declaration
266
308
  end
267
309
 
310
+ sig { params(name: String).void }
311
+ def handle_global_variable_hover(name)
312
+ entries = @index[name]
313
+ return unless entries
314
+
315
+ categorized_markdown_from_index_entries(name, entries).each do |category, content|
316
+ @response_builder.push(content, category: category)
317
+ end
318
+ end
319
+
268
320
  sig { params(name: String, location: Prism::Location).void }
269
321
  def generate_hover(name, location)
270
322
  entries = @index.resolve(name, @node_context.nesting)
@@ -121,7 +121,7 @@ module RubyLsp
121
121
  return Error::InvalidTargetRange if closest_node.is_a?(Prism::MissingNode)
122
122
 
123
123
  closest_node_loc = closest_node.location
124
- # If the parent expression is a single line block, then we have to extract it inside of the oneline block
124
+ # If the parent expression is a single line block, then we have to extract it inside of the one-line block
125
125
  if parent_statements.is_a?(Prism::BlockNode) &&
126
126
  parent_statements.location.start_line == parent_statements.location.end_line
127
127
 
@@ -17,7 +17,7 @@ module RubyLsp
17
17
  def provider
18
18
  Interface::CompletionOptions.new(
19
19
  resolve_provider: true,
20
- trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<"],
20
+ trigger_characters: ["/", "\"", "'", ":", "@", ".", "=", "<", "$"],
21
21
  completion_item: {
22
22
  labelDetailsSupport: true,
23
23
  },
@@ -50,6 +50,12 @@ module RubyLsp
50
50
  Prism::CallNode,
51
51
  Prism::ConstantReadNode,
52
52
  Prism::ConstantPathNode,
53
+ Prism::GlobalVariableAndWriteNode,
54
+ Prism::GlobalVariableOperatorWriteNode,
55
+ Prism::GlobalVariableOrWriteNode,
56
+ Prism::GlobalVariableReadNode,
57
+ Prism::GlobalVariableTargetNode,
58
+ Prism::GlobalVariableWriteNode,
53
59
  Prism::InstanceVariableReadNode,
54
60
  Prism::InstanceVariableAndWriteNode,
55
61
  Prism::InstanceVariableOperatorWriteNode,
@@ -34,7 +34,7 @@ module RubyLsp
34
34
 
35
35
  # Based on the spec https://microsoft.github.io/language-server-protocol/specification#textDocument_completion,
36
36
  # a completion resolve request must always return the original completion item without modifying ANY fields
37
- # other than detail and documentation (NOT labelDetails). If we modify anything, the completion behaviour might
37
+ # other than detail and documentation (NOT labelDetails). If we modify anything, the completion behavior might
38
38
  # be broken.
39
39
  #
40
40
  # For example, forgetting to return the `insertText` included in the original item will make the editor use the
@@ -12,12 +12,6 @@ module RubyLsp
12
12
  extend T::Sig
13
13
  extend T::Generic
14
14
 
15
- SPECIAL_METHOD_CALLS = [
16
- :require,
17
- :require_relative,
18
- :autoload,
19
- ].freeze
20
-
21
15
  sig do
22
16
  params(
23
17
  document: T.any(RubyDocument, ERBDocument),
@@ -46,7 +40,12 @@ module RubyLsp
46
40
  Prism::ConstantReadNode,
47
41
  Prism::ConstantPathNode,
48
42
  Prism::BlockArgumentNode,
43
+ Prism::GlobalVariableAndWriteNode,
44
+ Prism::GlobalVariableOperatorWriteNode,
45
+ Prism::GlobalVariableOrWriteNode,
49
46
  Prism::GlobalVariableReadNode,
47
+ Prism::GlobalVariableTargetNode,
48
+ Prism::GlobalVariableWriteNode,
50
49
  Prism::InstanceVariableReadNode,
51
50
  Prism::InstanceVariableAndWriteNode,
52
51
  Prism::InstanceVariableOperatorWriteNode,
@@ -72,11 +71,7 @@ module RubyLsp
72
71
  parent,
73
72
  position,
74
73
  )
75
- elsif target.is_a?(Prism::CallNode) && !SPECIAL_METHOD_CALLS.include?(target.message) && !covers_position?(
76
- target.message_loc, position
77
- )
78
- # If the target is a method call, we need to ensure that the requested position is exactly on top of the
79
- # method identifier. Otherwise, we risk showing definitions for unrelated things
74
+ elsif position_outside_target?(position, target)
80
75
  target = nil
81
76
  # For methods with block arguments using symbol-to-proc
82
77
  elsif target.is_a?(Prism::SymbolNode) && parent.is_a?(Prism::BlockArgumentNode)
@@ -107,6 +102,28 @@ module RubyLsp
107
102
  @dispatcher.dispatch_once(@target) if @target
108
103
  @response_builder.response
109
104
  end
105
+
106
+ private
107
+
108
+ sig { params(position: T::Hash[Symbol, T.untyped], target: T.nilable(Prism::Node)).returns(T::Boolean) }
109
+ def position_outside_target?(position, target)
110
+ case target
111
+ when Prism::GlobalVariableAndWriteNode,
112
+ Prism::GlobalVariableOperatorWriteNode,
113
+ Prism::GlobalVariableOrWriteNode,
114
+ Prism::GlobalVariableWriteNode,
115
+ Prism::InstanceVariableAndWriteNode,
116
+ Prism::InstanceVariableOperatorWriteNode,
117
+ Prism::InstanceVariableOrWriteNode,
118
+ Prism::InstanceVariableWriteNode
119
+
120
+ !covers_position?(target.name_loc, position)
121
+ when Prism::CallNode
122
+ !covers_position?(target.message_loc, position)
123
+ else
124
+ false
125
+ end
126
+ end
110
127
  end
111
128
  end
112
129
  end
@@ -38,7 +38,13 @@ module RubyLsp
38
38
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
39
39
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight],
40
40
  )
41
- Listeners::DocumentHighlight.new(@response_builder, node_context.node, node_context.parent, dispatcher)
41
+ Listeners::DocumentHighlight.new(
42
+ @response_builder,
43
+ node_context.node,
44
+ node_context.parent,
45
+ dispatcher,
46
+ position,
47
+ )
42
48
  end
43
49
 
44
50
  sig { override.returns(T::Array[Interface::DocumentHighlight]) }
@@ -10,7 +10,8 @@ module RubyLsp
10
10
  # informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
11
11
  # this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
12
12
  #
13
- # In VS Code, fuzzy symbol search can be accessed by opening the command palette and inserting an `@` symbol.
13
+ # In VS Code, symbol search known as 'Go To Symbol in Editor' and can be accessed with Ctrl/Cmd-Shift-O,
14
+ # or by opening the command palette and inserting an `@` symbol.
14
15
  class DocumentSymbol < Request
15
16
  extend T::Sig
16
17
 
@@ -46,17 +46,13 @@ module RubyLsp
46
46
  target = node_context.node
47
47
  parent = node_context.parent
48
48
 
49
- if (Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
50
- !Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
51
- (parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
49
+ if should_refine_target?(parent, target)
52
50
  target = determine_target(
53
51
  T.must(target),
54
52
  T.must(parent),
55
53
  position,
56
54
  )
57
- elsif target.is_a?(Prism::CallNode) && target.name != :require && target.name != :require_relative &&
58
- !covers_position?(target.message_loc, position)
59
-
55
+ elsif position_outside_target?(position, target)
60
56
  target = nil
61
57
  end
62
58
 
@@ -89,6 +85,30 @@ module RubyLsp
89
85
  ),
90
86
  )
91
87
  end
88
+
89
+ private
90
+
91
+ sig { params(parent: T.nilable(Prism::Node), target: T.nilable(Prism::Node)).returns(T::Boolean) }
92
+ def should_refine_target?(parent, target)
93
+ (Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
94
+ !Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
95
+ (parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
96
+ end
97
+
98
+ sig { params(position: T::Hash[Symbol, T.untyped], target: T.nilable(Prism::Node)).returns(T::Boolean) }
99
+ def position_outside_target?(position, target)
100
+ case target
101
+ when Prism::GlobalVariableAndWriteNode,
102
+ Prism::GlobalVariableOperatorWriteNode,
103
+ Prism::GlobalVariableOrWriteNode,
104
+ Prism::GlobalVariableWriteNode
105
+ !covers_position?(target.name_loc, position)
106
+ when Prism::CallNode
107
+ !covers_position?(target.message_loc, position)
108
+ else
109
+ false
110
+ end
111
+ end
92
112
  end
93
113
  end
94
114
  end
@@ -72,7 +72,7 @@ module RubyLsp
72
72
 
73
73
  # If the client doesn't support resource operations, such as renaming files, then we can only return the basic
74
74
  # text changes
75
- unless @global_state.supported_resource_operations.include?("rename")
75
+ unless @global_state.client_capabilities.supports_rename?
76
76
  return Interface::WorkspaceEdit.new(changes: changes)
77
77
  end
78
78
 
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  ).void
27
27
  end
28
28
  def delegate_request_if_needed!(global_state, document, char_position)
29
- if global_state.supports_request_delegation &&
29
+ if global_state.client_capabilities.supports_request_delegation &&
30
30
  document.is_a?(ERBDocument) &&
31
31
  document.inside_host_language?(char_position)
32
32
  raise DelegateRequestError
@@ -1,16 +1,27 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ # If there's no top level Gemfile, don't load RuboCop from a global installation
5
+ begin
6
+ Bundler.with_original_env { Bundler.default_gemfile }
7
+ rescue Bundler::GemfileNotFound
8
+ return
9
+ end
10
+
11
+ # Ensure that RuboCop is available
4
12
  begin
5
13
  require "rubocop"
6
14
  rescue LoadError
7
15
  return
8
16
  end
9
17
 
18
+ # Remember to update the version in the documentation (usage/dependency-compatibility section) if you change this
19
+ # Ensure that RuboCop is at least version 1.4.0
10
20
  begin
11
21
  gem("rubocop", ">= 1.4.0")
12
22
  rescue LoadError
13
- raise StandardError, "Incompatible RuboCop version. Ruby LSP requires >= 1.4.0"
23
+ $stderr.puts "Incompatible RuboCop version. Ruby LSP requires >= 1.4.0"
24
+ return
14
25
  end
15
26
 
16
27
  if RuboCop.const_defined?(:LSP) # This condition will be removed when requiring RuboCop >= 1.61.
@@ -0,0 +1,20 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ def compose(raw_initialize)
5
+ require_relative "../setup_bundler"
6
+ require "json"
7
+ require "uri"
8
+ require_relative "../../core_ext/uri"
9
+
10
+ initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
11
+ workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)
12
+ workspace_path = workspace_uri && URI(workspace_uri).to_standardized_path
13
+ workspace_path ||= Dir.pwd
14
+
15
+ env = RubyLsp::SetupBundler.new(workspace_path, launcher: true).setup!
16
+ File.write(
17
+ File.join(".ruby-lsp", "bundle_env"),
18
+ env.map { |k, v| "#{k}=#{v}" }.join("\n"),
19
+ )
20
+ end
@@ -0,0 +1,8 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "compose_bundle"
5
+
6
+ # When this is invoked on Windows, we pass the raw initialize as an argument to this script. On other platforms, we
7
+ # invoke the compose method from inside a forked process
8
+ compose(ARGV.first)
@@ -106,6 +106,8 @@ module RubyLsp
106
106
  )
107
107
  when "$/cancelRequest"
108
108
  @mutex.synchronize { @cancelled_requests << message[:params][:id] }
109
+ when nil
110
+ process_response(message) if message[:result]
109
111
  end
110
112
  rescue DelegateRequestError
111
113
  send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
@@ -138,8 +140,22 @@ module RubyLsp
138
140
  send_log_message("Error processing #{message[:method]}: #{e.full_message}", type: Constant::MessageType::ERROR)
139
141
  end
140
142
 
143
+ # Process responses to requests that were sent to the client
144
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
145
+ def process_response(message)
146
+ case message.dig(:result, :method)
147
+ when "window/showMessageRequest"
148
+ window_show_message_request(message)
149
+ end
150
+ end
151
+
141
152
  sig { params(include_project_addons: T::Boolean).void }
142
153
  def load_addons(include_project_addons: true)
154
+ # If invoking Bundler.setup failed, then the load path will not be configured properly and trying to load add-ons
155
+ # with Gem.find_files will find every single version installed of an add-on, leading to requiring several
156
+ # different versions of the same files. We cannot load add-ons if Bundler.setup failed
157
+ return if @setup_error
158
+
143
159
  errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
144
160
 
145
161
  if errors.any?
@@ -181,8 +197,6 @@ module RubyLsp
181
197
  client_name = options.dig(:clientInfo, :name)
182
198
  @store.client_name = client_name if client_name
183
199
 
184
- progress = options.dig(:capabilities, :window, :workDoneProgress)
185
- @store.supports_progress = progress.nil? ? true : progress
186
200
  configured_features = options.dig(:initializationOptions, :enabledFeatures)
187
201
 
188
202
  configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
@@ -202,6 +216,13 @@ module RubyLsp
202
216
  Hash.new(true)
203
217
  end
204
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
+
205
226
  document_symbol_provider = Requests::DocumentSymbol.provider if enabled_features["documentSymbols"]
206
227
  document_link_provider = Requests::DocumentLink.provider if enabled_features["documentLink"]
207
228
  code_lens_provider = Requests::CodeLens.provider if enabled_features["codeLens"]
@@ -254,12 +275,14 @@ module RubyLsp
254
275
  version: VERSION,
255
276
  },
256
277
  formatter: @global_state.formatter,
278
+ degraded_mode: !!(@install_error || @setup_error),
279
+ bundle_env: bundle_env,
257
280
  }
258
281
 
259
282
  send_message(Result.new(id: message[:id], response: response))
260
283
 
261
284
  # Not every client supports dynamic registration or file watching
262
- if global_state.supports_watching_files
285
+ if @global_state.client_capabilities.supports_watching_files
263
286
  send_message(
264
287
  Request.new(
265
288
  id: @current_request_id,
@@ -290,6 +313,24 @@ module RubyLsp
290
313
  begin_progress("indexing-progress", "Ruby LSP: indexing files")
291
314
 
292
315
  global_state_notifications.each { |notification| send_message(notification) }
316
+
317
+ if @setup_error
318
+ send_message(Notification.telemetry(
319
+ type: "error",
320
+ errorMessage: @setup_error.message,
321
+ errorClass: @setup_error.class,
322
+ stack: @setup_error.backtrace&.join("\n"),
323
+ ))
324
+ end
325
+
326
+ if @install_error
327
+ send_message(Notification.telemetry(
328
+ type: "error",
329
+ errorMessage: @install_error.message,
330
+ errorClass: @install_error.class,
331
+ stack: @install_error.backtrace&.join("\n"),
332
+ ))
333
+ end
293
334
  end
294
335
 
295
336
  sig { void }
@@ -297,20 +338,22 @@ module RubyLsp
297
338
  load_addons
298
339
  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
299
340
 
300
- if defined?(Requests::Support::RuboCopFormatter)
301
- begin
302
- @global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
303
- rescue RuboCop::Error => e
304
- # The user may have provided unknown config switches in .rubocop or
305
- # is trying to load a non-existant config file.
306
- send_message(Notification.window_show_message(
307
- "RuboCop configuration error: #{e.message}. Formatting will not be available.",
308
- type: Constant::MessageType::ERROR,
309
- ))
341
+ unless @setup_error
342
+ if defined?(Requests::Support::RuboCopFormatter)
343
+ begin
344
+ @global_state.register_formatter("rubocop", Requests::Support::RuboCopFormatter.new)
345
+ rescue RuboCop::Error => e
346
+ # The user may have provided unknown config switches in .rubocop or
347
+ # is trying to load a non-existent config file.
348
+ send_message(Notification.window_show_message(
349
+ "RuboCop configuration error: #{e.message}. Formatting will not be available.",
350
+ type: Constant::MessageType::ERROR,
351
+ ))
352
+ end
353
+ end
354
+ if defined?(Requests::Support::SyntaxTreeFormatter)
355
+ @global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
310
356
  end
311
- end
312
- if defined?(Requests::Support::SyntaxTreeFormatter)
313
- @global_state.register_formatter("syntax_tree", Requests::Support::SyntaxTreeFormatter.new)
314
357
  end
315
358
 
316
359
  perform_initial_indexing
@@ -569,6 +612,11 @@ module RubyLsp
569
612
  # don't want to format it
570
613
  path = uri.to_standardized_path
571
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
572
620
  send_empty_response(message[:id])
573
621
  return
574
622
  end
@@ -1017,7 +1065,7 @@ module RubyLsp
1017
1065
 
1018
1066
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
1019
1067
  def workspace_dependencies(message)
1020
- response = begin
1068
+ response = if @global_state.top_level_bundle
1021
1069
  Bundler.with_original_env do
1022
1070
  definition = Bundler.definition
1023
1071
  dep_keys = definition.locked_deps.keys.to_set
@@ -1031,7 +1079,7 @@ module RubyLsp
1031
1079
  }
1032
1080
  end
1033
1081
  end
1034
- rescue Bundler::GemfileNotFound
1082
+ else
1035
1083
  []
1036
1084
  end
1037
1085
 
@@ -1077,7 +1125,7 @@ module RubyLsp
1077
1125
 
1078
1126
  sig { params(id: String, title: String, percentage: Integer).void }
1079
1127
  def begin_progress(id, title, percentage: 0)
1080
- return unless @store.supports_progress
1128
+ return unless @global_state.client_capabilities.supports_progress
1081
1129
 
1082
1130
  send_message(Request.new(
1083
1131
  id: @current_request_id,
@@ -1085,52 +1133,21 @@ module RubyLsp
1085
1133
  params: Interface::WorkDoneProgressCreateParams.new(token: id),
1086
1134
  ))
1087
1135
 
1088
- send_message(Notification.new(
1089
- method: "$/progress",
1090
- params: Interface::ProgressParams.new(
1091
- token: id,
1092
- value: Interface::WorkDoneProgressBegin.new(
1093
- kind: "begin",
1094
- title: title,
1095
- percentage: percentage,
1096
- message: "#{percentage}% completed",
1097
- ),
1098
- ),
1099
- ))
1136
+ send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1100
1137
  end
1101
1138
 
1102
1139
  sig { params(id: String, percentage: Integer).void }
1103
1140
  def progress(id, percentage)
1104
- return unless @store.supports_progress
1141
+ return unless @global_state.client_capabilities.supports_progress
1105
1142
 
1106
- send_message(
1107
- Notification.new(
1108
- method: "$/progress",
1109
- params: Interface::ProgressParams.new(
1110
- token: id,
1111
- value: Interface::WorkDoneProgressReport.new(
1112
- kind: "report",
1113
- percentage: percentage,
1114
- message: "#{percentage}% completed",
1115
- ),
1116
- ),
1117
- ),
1118
- )
1143
+ send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
1119
1144
  end
1120
1145
 
1121
1146
  sig { params(id: String).void }
1122
1147
  def end_progress(id)
1123
- return unless @store.supports_progress
1148
+ return unless @global_state.client_capabilities.supports_progress
1124
1149
 
1125
- send_message(
1126
- Notification.new(
1127
- method: "$/progress",
1128
- params: Interface::ProgressParams.new(
1129
- token: id,
1130
- value: Interface::WorkDoneProgressEnd.new(kind: "end"),
1131
- ),
1132
- ),
1133
- )
1150
+ send_message(Notification.progress_end(id))
1134
1151
  rescue ClosedQueueError
1135
1152
  # If the server was killed and the message queue is already closed, there's no way to end the progress
1136
1153
  # notification
@@ -1138,6 +1155,7 @@ module RubyLsp
1138
1155
 
1139
1156
  sig { void }
1140
1157
  def check_formatter_is_available
1158
+ return if @setup_error
1141
1159
  # Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
1142
1160
  # Syntax Tree will always be available via Ruby LSP so we don't need to check for it.
1143
1161
  return unless @global_state.formatter == "rubocop"
@@ -1194,5 +1212,17 @@ module RubyLsp
1194
1212
  # The index expects snake case configurations, but VS Code standardizes on camel case settings
1195
1213
  configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
1196
1214
  end
1215
+
1216
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
1217
+ def window_show_message_request(message)
1218
+ result = message[:result]
1219
+ return unless result
1220
+
1221
+ addon_name = result[:addon_name]
1222
+ addon = Addon.addons.find { |addon| addon.name == addon_name }
1223
+ return unless addon
1224
+
1225
+ addon.handle_window_show_message_response(result[:title])
1226
+ end
1197
1227
  end
1198
1228
  end