ruby-lsp 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8f2f2aa1083dcc700e709ebd0bc3d3aed8f08d80943595ba3c9df332d806709
4
- data.tar.gz: a065d2f6be6539c81ad117bfba7c7b4f90ead0bbce37dd936733218e3135bc67
3
+ metadata.gz: 4d7205054d59bb5a292bc8e2d87a984663ce1f0dac527d6d69d3940bc6e86735
4
+ data.tar.gz: 9b760d79ca5406fe3e78e4bd1276cb507464d72d2fc2e68cf5abf1f9920df1a6
5
5
  SHA512:
6
- metadata.gz: de010f852e08fd72ac60a12cbb9a8666a92945c49b8a14214f0e347c9a6fbacb65770322627db22a30b573ee61f34ec18be56d625768a6c954cf447dbda63368
7
- data.tar.gz: 3f47e62896899091c4a9b166937720cc535f46004827a098bfb7eaf9c31fb39bb35261077b91a01a41baa18a50482d8ffe1b8ebddabaed1ea15bc0d023e76dae
6
+ metadata.gz: bf465a2d9af7e109b21ecd861cb88b556ee872e4d46e292e791f8f85ce131af7791254187bcc68216b9df0f1276296b5cf80e78be7193c11fdf16831b8da4f0f
7
+ data.tar.gz: 57fa298a96b7cd3eeb2be68d21261ae634f8e70d818a5c68b2e7cdcfa464d7d27b16e90aecbfb8192a4a3ff9fecc4fa8b94baa3b3b51d9c7c887f4fba2b07bd5
data/README.md CHANGED
@@ -39,10 +39,14 @@ end
39
39
  See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth details about the
40
40
  [supported features](https://shopify.github.io/ruby-lsp/RubyLsp/Requests.html).
41
41
 
42
+ For creating rich themes for Ruby using the semantic highlighting information, see the [semantic highlighting
43
+ documentation](SEMANTIC_HIGHLIGHTING.md).
44
+
42
45
  ## Learn More
43
46
 
44
47
  * [RubyConf 2022: Improving the development experience with language servers](https://www.youtube.com/watch?v=kEfXPTm1aCI) ([Vinicius Stock](https://github.com/vinistock))
45
48
  * [Remote Ruby: Ruby Language Server with Vinicius Stock](https://remoteruby.com/221)
49
+ * [RubyKaigi 2023: Code indexing - How language servers understand our code](https://www.youtube.com/watch?v=ks3tQojSJLU) ([Vinicius Stock](https://github.com/vinistock))
46
50
 
47
51
  ## Contributing
48
52
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
data/exe/ruby-lsp CHANGED
@@ -20,7 +20,39 @@ end
20
20
 
21
21
  require_relative "../lib/ruby_lsp/internal"
22
22
 
23
- if ARGV.include?("--debug")
23
+ require "optparse"
24
+
25
+ options = {}
26
+ parser = OptionParser.new do |opts|
27
+ opts.banner = "Usage: ruby-lsp [options]"
28
+
29
+ opts.on("--version", "Print ruby-lsp version") do
30
+ puts RubyLsp::VERSION
31
+ exit(0)
32
+ end
33
+
34
+ opts.on("--debug", "Launch the Ruby LSP with a debugger attached") do
35
+ options[:debug] = true
36
+ end
37
+
38
+ opts.on("-h", "--help", "Print this help") do
39
+ puts opts.help
40
+ puts
41
+ puts "See https://shopify.github.io/ruby-lsp/ for more information"
42
+ exit(0)
43
+ end
44
+ end
45
+
46
+ begin
47
+ parser.parse!
48
+ rescue OptionParser::InvalidOption => e
49
+ warn(e)
50
+ warn("")
51
+ warn(parser.help)
52
+ exit(1)
53
+ end
54
+
55
+ if options[:debug]
24
56
  if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
25
57
  puts "Debugging is not supported on Windows"
26
58
  exit 1
@@ -42,7 +42,7 @@ module RubyLsp
42
42
  # Find all classes that inherit from BaseRequest or Listener, which are the ones we want to make sure are
43
43
  # documented
44
44
  features = ObjectSpace.each_object(Class).filter_map do |k|
45
- klass = T.cast(k, Class)
45
+ klass = T.cast(k, T::Class[T.anything])
46
46
  klass if klass < RubyLsp::Requests::BaseRequest || klass < RubyLsp::Listener
47
47
  end
48
48
 
@@ -20,6 +20,7 @@ module RubyLsp
20
20
  # ```
21
21
  class EventEmitter < SyntaxTree::Visitor
22
22
  extend T::Sig
23
+ include SyntaxTree::WithScope
23
24
 
24
25
  sig { void }
25
26
  def initialize
@@ -73,6 +74,12 @@ module RubyLsp
73
74
  @listeners[:after_command]&.each { |l| T.unsafe(l).after_command(node) }
74
75
  end
75
76
 
77
+ sig { override.params(node: SyntaxTree::CommandCall).void }
78
+ def visit_command_call(node)
79
+ @listeners[:on_command_call]&.each { |l| T.unsafe(l).on_command_call(node) }
80
+ super
81
+ end
82
+
76
83
  sig { override.params(node: SyntaxTree::CallNode).void }
77
84
  def visit_call(node)
78
85
  @listeners[:on_call]&.each { |l| T.unsafe(l).on_call(node) }
@@ -116,5 +123,59 @@ module RubyLsp
116
123
  @listeners[:on_comment]&.each { |l| T.unsafe(l).on_comment(node) }
117
124
  super
118
125
  end
126
+
127
+ sig { override.params(node: SyntaxTree::Rescue).void }
128
+ def visit_rescue(node)
129
+ @listeners[:on_rescue]&.each { |l| T.unsafe(l).on_rescue(node) }
130
+ super
131
+ end
132
+
133
+ sig { override.params(node: SyntaxTree::Kw).void }
134
+ def visit_kw(node)
135
+ @listeners[:on_kw]&.each { |l| T.unsafe(l).on_kw(node) }
136
+ super
137
+ end
138
+
139
+ sig { override.params(node: SyntaxTree::Params).void }
140
+ def visit_params(node)
141
+ @listeners[:on_params]&.each { |l| T.unsafe(l).on_params(node) }
142
+ super
143
+ end
144
+
145
+ sig { override.params(node: SyntaxTree::Field).void }
146
+ def visit_field(node)
147
+ @listeners[:on_field]&.each { |l| T.unsafe(l).on_field(node) }
148
+ super
149
+ end
150
+
151
+ sig { override.params(node: SyntaxTree::VarRef).void }
152
+ def visit_var_ref(node)
153
+ @listeners[:on_var_ref]&.each { |l| T.unsafe(l).on_var_ref(node) }
154
+ super
155
+ end
156
+
157
+ sig { override.params(node: SyntaxTree::BlockVar).void }
158
+ def visit_block_var(node)
159
+ @listeners[:on_block_var]&.each { |l| T.unsafe(l).on_block_var(node) }
160
+ super
161
+ end
162
+
163
+ sig { override.params(node: SyntaxTree::LambdaVar).void }
164
+ def visit_lambda_var(node)
165
+ @listeners[:on_lambda_var]&.each { |l| T.unsafe(l).on_lambda_var(node) }
166
+ super
167
+ end
168
+
169
+ sig { override.params(node: SyntaxTree::Binary).void }
170
+ def visit_binary(node)
171
+ super
172
+ @listeners[:after_binary]&.each { |l| T.unsafe(l).after_binary(node) }
173
+ end
174
+
175
+ sig { override.params(node: SyntaxTree::Const).void }
176
+ def visit_const(node)
177
+ @listeners[:on_const]&.each { |l| T.unsafe(l).on_const(node) }
178
+ super
179
+ end
119
180
  end
120
181
  end
@@ -1,6 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "ruby_lsp/requests/support/dependency_detector"
5
+
4
6
  module RubyLsp
5
7
  # This class dispatches a request execution to the right request class. No IO should happen anywhere here!
6
8
  class Executor
@@ -11,6 +13,7 @@ module RubyLsp
11
13
  # Requests that mutate the store must be run sequentially! Parallel requests only receive a temporary copy of the
12
14
  # store
13
15
  @store = store
16
+ @test_library = T.let(DependencyDetector.detected_test_library, String)
14
17
  @message_queue = message_queue
15
18
  end
16
19
 
@@ -81,7 +84,8 @@ module RubyLsp
81
84
  folding_range(uri)
82
85
  when "textDocument/selectionRange"
83
86
  selection_range(uri, request.dig(:params, :positions))
84
- when "textDocument/documentSymbol", "textDocument/documentLink", "textDocument/codeLens"
87
+ when "textDocument/documentSymbol", "textDocument/documentLink", "textDocument/codeLens",
88
+ "textDocument/semanticTokens/full"
85
89
  document = @store.get(uri)
86
90
 
87
91
  # If the response has already been cached by another request, return it
@@ -92,10 +96,11 @@ module RubyLsp
92
96
  emitter = EventEmitter.new
93
97
  document_symbol = Requests::DocumentSymbol.new(emitter, @message_queue)
94
98
  document_link = Requests::DocumentLink.new(uri, emitter, @message_queue)
95
- code_lens = Requests::CodeLens.new(uri, emitter, @message_queue)
99
+ code_lens = Requests::CodeLens.new(uri, emitter, @message_queue, @test_library)
96
100
  code_lens_extensions_listeners = Requests::CodeLens.listeners.map do |l|
97
101
  T.unsafe(l).new(document.uri, emitter, @message_queue)
98
102
  end
103
+ semantic_highlighting = Requests::SemanticHighlighting.new(emitter, @message_queue)
99
104
  emitter.visit(document.tree) if document.parsed?
100
105
 
101
106
  code_lens_extensions_listeners.each { |ext| code_lens.merge_response!(ext) }
@@ -105,9 +110,11 @@ module RubyLsp
105
110
  document.cache_set("textDocument/documentSymbol", document_symbol.response)
106
111
  document.cache_set("textDocument/documentLink", document_link.response)
107
112
  document.cache_set("textDocument/codeLens", code_lens.response)
113
+ document.cache_set(
114
+ "textDocument/semanticTokens/full",
115
+ Requests::Support::SemanticTokenEncoder.new.encode(semantic_highlighting.response),
116
+ )
108
117
  document.cache_get(request[:method])
109
- when "textDocument/semanticTokens/full"
110
- semantic_tokens_full(uri)
111
118
  when "textDocument/semanticTokens/range"
112
119
  semantic_tokens_range(uri, request.dig(:params, :range))
113
120
  when "textDocument/formatting"
@@ -245,19 +252,6 @@ module RubyLsp
245
252
  end
246
253
  end
247
254
 
248
- sig { params(uri: String).returns(Interface::SemanticTokens) }
249
- def semantic_tokens_full(uri)
250
- @store.cache_fetch(uri, "textDocument/semanticTokens/full") do |document|
251
- T.cast(
252
- Requests::SemanticHighlighting.new(
253
- document,
254
- encoder: Requests::Support::SemanticTokenEncoder.new,
255
- ).run,
256
- Interface::SemanticTokens,
257
- )
258
- end
259
- end
260
-
261
255
  sig { params(uri: String).returns(T.nilable(T::Array[Interface::TextEdit])) }
262
256
  def formatting(uri)
263
257
  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
@@ -287,13 +281,18 @@ module RubyLsp
287
281
  Requests::DocumentHighlight.new(@store.get(uri), position).run
288
282
  end
289
283
 
290
- sig { params(uri: String, range: Document::RangeShape).returns(T::Array[Interface::InlayHint]) }
284
+ sig { params(uri: String, range: Document::RangeShape).returns(T.nilable(T::Array[Interface::InlayHint])) }
291
285
  def inlay_hint(uri, range)
292
286
  document = @store.get(uri)
287
+ return if document.syntax_error?
288
+
293
289
  start_line = range.dig(:start, :line)
294
290
  end_line = range.dig(:end, :line)
295
291
 
296
- Requests::InlayHints.new(document, start_line..end_line).run
292
+ emitter = EventEmitter.new
293
+ listener = Requests::InlayHints.new(start_line..end_line, emitter, @message_queue)
294
+ emitter.visit(document.tree)
295
+ listener.response
297
296
  end
298
297
 
299
298
  sig do
@@ -354,14 +353,15 @@ module RubyLsp
354
353
  start_line = range.dig(:start, :line)
355
354
  end_line = range.dig(:end, :line)
356
355
 
357
- T.cast(
358
- Requests::SemanticHighlighting.new(
359
- document,
360
- range: start_line..end_line,
361
- encoder: Requests::Support::SemanticTokenEncoder.new,
362
- ).run,
363
- Interface::SemanticTokens,
356
+ emitter = EventEmitter.new
357
+ listener = Requests::SemanticHighlighting.new(
358
+ emitter,
359
+ @message_queue,
360
+ range: start_line..end_line,
364
361
  )
362
+ emitter.visit(document.tree) if document.parsed?
363
+
364
+ Requests::Support::SemanticTokenEncoder.new.encode(listener.response)
365
365
  end
366
366
 
367
367
  sig do
@@ -421,9 +421,9 @@ module RubyLsp
421
421
  encodings.first
422
422
  end
423
423
 
424
- formatter = options.dig(:initializationOptions, :formatter)
424
+ formatter = options.dig(:initializationOptions, :formatter) || "auto"
425
425
  @store.formatter = if formatter == "auto"
426
- detected_formatter
426
+ DependencyDetector.detected_formatter
427
427
  else
428
428
  formatter
429
429
  end
@@ -536,23 +536,6 @@ module RubyLsp
536
536
  )
537
537
  end
538
538
 
539
- sig { returns(String) }
540
- def detected_formatter
541
- # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
542
- if direct_dependency?(/^rubocop/)
543
- "rubocop"
544
- elsif direct_dependency?(/^syntax_tree$/)
545
- "syntax_tree"
546
- else
547
- "none"
548
- end
549
- end
550
-
551
- sig { params(gem_pattern: Regexp).returns(T::Boolean) }
552
- def direct_dependency?(gem_pattern)
553
- Bundler.locked_gems.dependencies.keys.grep(gem_pattern).any?
554
- end
555
-
556
539
  sig { void }
557
540
  def check_formatter_is_available
558
541
  # Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
@@ -560,11 +543,13 @@ module RubyLsp
560
543
  return unless @store.formatter == "rubocop"
561
544
 
562
545
  unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
546
+ @store.formatter = "none"
547
+
563
548
  @message_queue << Notification.new(
564
549
  message: "window/showMessage",
565
550
  params: Interface::ShowMessageParams.new(
566
551
  type: Constant::MessageType::ERROR,
567
- message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the bundle.",
552
+ message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the Gemfile or gemspec.",
568
553
  ),
569
554
  )
570
555
  end
@@ -6,6 +6,7 @@ require "syntax_tree"
6
6
  require "language_server-protocol"
7
7
  require "benchmark"
8
8
  require "bundler"
9
+ require "uri"
9
10
 
10
11
  require "ruby-lsp"
11
12
  require "ruby_lsp/utils"
@@ -11,12 +11,8 @@ module RubyLsp
11
11
 
12
12
  abstract!
13
13
 
14
- # We must accept rest keyword arguments here, so that the argument count matches when
15
- # SyntaxTree::WithScope#initialize invokes `super` for Sorbet. We don't actually use these parameters for
16
- # anything. We can remove these arguments once we drop support for Ruby 2.7
17
- # https://github.com/ruby-syntax-tree/syntax_tree/blob/4dac90b53df388f726dce50ce638a1ba71cc59f8/lib/syntax_tree/with_scope.rb#L122
18
- sig { params(document: Document, _kwargs: T.untyped).void }
19
- def initialize(document, **_kwargs)
14
+ sig { params(document: Document).void }
15
+ def initialize(document)
20
16
  @document = document
21
17
  super()
22
18
  end
@@ -1,6 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "shellwords"
5
+
4
6
  module RubyLsp
5
7
  module Requests
6
8
  # ![Code lens demo](../../code_lens.gif)
@@ -31,35 +33,63 @@ module RubyLsp
31
33
  sig { override.returns(ResponseType) }
32
34
  attr_reader :response
33
35
 
34
- sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
35
- def initialize(uri, emitter, message_queue)
36
+ sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue, test_library: String).void }
37
+ def initialize(uri, emitter, message_queue, test_library)
36
38
  super(emitter, message_queue)
37
39
 
40
+ @test_library = T.let(test_library, String)
38
41
  @response = T.let([], ResponseType)
39
42
  @path = T.let(T.must(URI(uri).path), String)
40
- @visibility = T.let("public", String)
41
- @prev_visibility = T.let("public", String)
42
-
43
- emitter.register(self, :on_class, :on_def, :on_command, :after_command, :on_call, :after_call, :on_vcall)
43
+ # visibility_stack is a stack of [current_visibility, previous_visibility]
44
+ @visibility_stack = T.let([["public", "public"]], T::Array[T::Array[T.nilable(String)]])
45
+ @class_stack = T.let([], T::Array[String])
46
+
47
+ emitter.register(
48
+ self,
49
+ :on_class,
50
+ :after_class,
51
+ :on_def,
52
+ :on_command,
53
+ :after_command,
54
+ :on_call,
55
+ :after_call,
56
+ :on_vcall,
57
+ )
44
58
  end
45
59
 
46
60
  sig { params(node: SyntaxTree::ClassDeclaration).void }
47
61
  def on_class(node)
62
+ @visibility_stack.push(["public", "public"])
48
63
  class_name = node.constant.constant.value
49
64
  if class_name.end_with?("Test")
50
- add_code_lens(node, name: class_name, command: BASE_COMMAND + @path)
65
+ @class_stack.push(class_name)
66
+ add_test_code_lens(
67
+ node,
68
+ name: class_name,
69
+ command: generate_test_command(class_name: class_name),
70
+ kind: :group,
71
+ )
51
72
  end
52
73
  end
53
74
 
75
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
76
+ def after_class(node)
77
+ @visibility_stack.pop
78
+ @class_stack.pop
79
+ end
80
+
54
81
  sig { params(node: SyntaxTree::DefNode).void }
55
82
  def on_def(node)
56
- if @visibility == "public"
83
+ visibility, _ = @visibility_stack.last
84
+ if visibility == "public"
57
85
  method_name = node.name.value
58
86
  if method_name.start_with?("test_")
59
- add_code_lens(
87
+ class_name = T.must(@class_stack.last)
88
+ add_test_code_lens(
60
89
  node,
61
90
  name: method_name,
62
- command: BASE_COMMAND + @path + " --name " + method_name,
91
+ command: generate_test_command(method_name: method_name, class_name: class_name),
92
+ kind: :example,
63
93
  )
64
94
  end
65
95
  end
@@ -67,15 +97,22 @@ module RubyLsp
67
97
 
68
98
  sig { params(node: SyntaxTree::Command).void }
69
99
  def on_command(node)
70
- if ACCESS_MODIFIERS.include?(node.message.value) && node.arguments.parts.any?
71
- @prev_visibility = @visibility
72
- @visibility = node.message.value
100
+ node_message = node.message.value
101
+ if ACCESS_MODIFIERS.include?(node_message) && node.arguments.parts.any?
102
+ visibility, _ = @visibility_stack.pop
103
+ @visibility_stack.push([node_message, visibility])
104
+ elsif @path.include?("Gemfile") && node_message.include?("gem") && node.arguments.parts.any?
105
+ remote = resolve_gem_remote(node)
106
+ return unless remote
107
+
108
+ add_open_gem_remote_code_lens(node, remote)
73
109
  end
74
110
  end
75
111
 
76
112
  sig { params(node: SyntaxTree::Command).void }
77
113
  def after_command(node)
78
- @visibility = @prev_visibility
114
+ _, prev_visibility = @visibility_stack.pop
115
+ @visibility_stack.push([prev_visibility, prev_visibility])
79
116
  end
80
117
 
81
118
  sig { params(node: SyntaxTree::CallNode).void }
@@ -85,15 +122,16 @@ module RubyLsp
85
122
  if ident
86
123
  ident_value = T.cast(ident, SyntaxTree::Ident).value
87
124
  if ACCESS_MODIFIERS.include?(ident_value)
88
- @prev_visibility = @visibility
89
- @visibility = ident_value
125
+ visibility, _ = @visibility_stack.pop
126
+ @visibility_stack.push([ident_value, visibility])
90
127
  end
91
128
  end
92
129
  end
93
130
 
94
131
  sig { params(node: SyntaxTree::CallNode).void }
95
132
  def after_call(node)
96
- @visibility = @prev_visibility
133
+ _, prev_visibility = @visibility_stack.pop
134
+ @visibility_stack.push([prev_visibility, prev_visibility])
97
135
  end
98
136
 
99
137
  sig { params(node: SyntaxTree::VCall).void }
@@ -101,8 +139,8 @@ module RubyLsp
101
139
  vcall_value = node.value.value
102
140
 
103
141
  if ACCESS_MODIFIERS.include?(vcall_value)
104
- @prev_visibility = vcall_value
105
- @visibility = vcall_value
142
+ @visibility_stack.pop
143
+ @visibility_stack.push([vcall_value, vcall_value])
106
144
  end
107
145
  end
108
146
 
@@ -114,36 +152,86 @@ module RubyLsp
114
152
 
115
153
  private
116
154
 
117
- sig { params(node: SyntaxTree::Node, name: String, command: String).void }
118
- def add_code_lens(node, name:, command:)
155
+ sig { params(node: SyntaxTree::Node, name: String, command: String, kind: Symbol).void }
156
+ def add_test_code_lens(node, name:, command:, kind:)
157
+ arguments = [
158
+ @path,
159
+ name,
160
+ command,
161
+ {
162
+ start_line: node.location.start_line - 1,
163
+ start_column: node.location.start_column,
164
+ end_line: node.location.end_line - 1,
165
+ end_column: node.location.end_column,
166
+ },
167
+ ]
168
+
119
169
  @response << create_code_lens(
120
170
  node,
121
171
  title: "Run",
122
172
  command_name: "rubyLsp.runTest",
123
- path: @path,
124
- name: name,
125
- test_command: command,
126
- type: "test",
173
+ arguments: arguments,
174
+ data: { type: "test", kind: kind },
127
175
  )
128
176
 
129
177
  @response << create_code_lens(
130
178
  node,
131
179
  title: "Run In Terminal",
132
180
  command_name: "rubyLsp.runTestInTerminal",
133
- path: @path,
134
- name: name,
135
- test_command: command,
136
- type: "test_in_terminal",
181
+ arguments: arguments,
182
+ data: { type: "test_in_terminal", kind: kind },
137
183
  )
138
184
 
139
185
  @response << create_code_lens(
140
186
  node,
141
187
  title: "Debug",
142
188
  command_name: "rubyLsp.debugTest",
143
- path: @path,
144
- name: name,
145
- test_command: command,
146
- type: "debug",
189
+ arguments: arguments,
190
+ data: { type: "debug", kind: kind },
191
+ )
192
+ end
193
+
194
+ sig { params(node: SyntaxTree::Command).returns(T.nilable(String)) }
195
+ def resolve_gem_remote(node)
196
+ gem_name = node.arguments.parts.flat_map(&:child_nodes).first.value
197
+ spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name }&.to_spec
198
+ return if spec.nil?
199
+
200
+ [spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
201
+ page.start_with?("https://github.com", "https://gitlab.com")
202
+ end
203
+ end
204
+
205
+ sig { params(class_name: String, method_name: T.nilable(String)).returns(String) }
206
+ def generate_test_command(class_name:, method_name: nil)
207
+ command = BASE_COMMAND + @path
208
+
209
+ case @test_library
210
+ when "minitest"
211
+ command += if method_name
212
+ " --name " + "/#{Shellwords.escape(class_name + "#" + method_name)}/"
213
+ else
214
+ " --name " + "/#{Shellwords.escape(class_name)}/"
215
+ end
216
+ when "test-unit"
217
+ command += " --testcase " + "/#{Shellwords.escape(class_name)}/"
218
+
219
+ if method_name
220
+ command += " --name " + Shellwords.escape(method_name)
221
+ end
222
+ end
223
+
224
+ command
225
+ end
226
+
227
+ sig { params(node: SyntaxTree::Command, remote: String).void }
228
+ def add_open_gem_remote_code_lens(node, remote)
229
+ @response << create_code_lens(
230
+ node,
231
+ title: "Open remote",
232
+ command_name: "rubyLsp.openLink",
233
+ arguments: [remote],
234
+ data: { type: "link" },
147
235
  )
148
236
  end
149
237
  end
@@ -30,7 +30,9 @@ module RubyLsp
30
30
 
31
31
  sig { override.returns(T.nilable(T.all(T::Array[Support::RuboCopDiagnostic], Object))) }
32
32
  def run
33
+ # Running RuboCop is slow, so to avoid excessive runs we only do so if the file is syntactically valid
33
34
  return if @document.syntax_error?
35
+
34
36
  return unless defined?(Support::RuboCopDiagnosticsRunner)
35
37
 
36
38
  # Don't try to run RuboCop diagnostics for files outside the current working directory
@@ -27,7 +27,6 @@ module RubyLsp
27
27
  SyntaxTree::For,
28
28
  SyntaxTree::HashLiteral,
29
29
  SyntaxTree::Heredoc,
30
- SyntaxTree::IfNode,
31
30
  SyntaxTree::ModuleDeclaration,
32
31
  SyntaxTree::SClass,
33
32
  SyntaxTree::UnlessNode,
@@ -42,6 +41,7 @@ module RubyLsp
42
41
 
43
42
  NODES_WITH_STATEMENTS = T.let(
44
43
  [
44
+ SyntaxTree::IfNode,
45
45
  SyntaxTree::Elsif,
46
46
  SyntaxTree::In,
47
47
  SyntaxTree::Rescue,
@@ -52,6 +52,7 @@ module RubyLsp
52
52
 
53
53
  StatementNode = T.type_alias do
54
54
  T.any(
55
+ SyntaxTree::IfNode,
55
56
  SyntaxTree::Elsif,
56
57
  SyntaxTree::In,
57
58
  SyntaxTree::Rescue,