ruby-lsp 0.26.2 → 0.26.9
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/VERSION +1 -1
- data/exe/ruby-lsp +7 -10
- data/exe/ruby-lsp-launcher +16 -3
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +3 -2
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +19 -0
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +33 -27
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +7 -2
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +6 -2
- data/lib/ruby_lsp/base_server.rb +23 -12
- data/lib/ruby_lsp/global_state.rb +65 -33
- data/lib/ruby_lsp/listeners/definition.rb +34 -14
- data/lib/ruby_lsp/listeners/document_link.rb +62 -23
- data/lib/ruby_lsp/listeners/hover.rb +2 -2
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +2 -2
- data/lib/ruby_lsp/requests/code_action_resolve.rb +33 -11
- data/lib/ruby_lsp/requests/code_actions.rb +20 -5
- data/lib/ruby_lsp/requests/completion_resolve.rb +8 -6
- data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +7 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +6 -10
- data/lib/ruby_lsp/requests/support/source_uri.rb +7 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +20 -12
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +3 -3
- data/lib/ruby_lsp/scripts/compose_bundle.rb +3 -3
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +3 -1
- data/lib/ruby_lsp/server.rb +5 -2
- data/lib/ruby_lsp/setup_bundler.rb +68 -37
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +50 -27
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +1 -1
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +1 -1
- metadata +3 -16
- data/lib/ruby_indexer/test/class_variables_test.rb +0 -140
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +0 -770
- data/lib/ruby_indexer/test/configuration_test.rb +0 -279
- data/lib/ruby_indexer/test/constant_test.rb +0 -402
- data/lib/ruby_indexer/test/enhancements_test.rb +0 -325
- data/lib/ruby_indexer/test/global_variable_test.rb +0 -49
- data/lib/ruby_indexer/test/index_test.rb +0 -2276
- data/lib/ruby_indexer/test/instance_variables_test.rb +0 -264
- data/lib/ruby_indexer/test/method_test.rb +0 -990
- data/lib/ruby_indexer/test/prefix_tree_test.rb +0 -150
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +0 -381
- data/lib/ruby_indexer/test/reference_finder_test.rb +0 -395
- data/lib/ruby_indexer/test/test_case.rb +0 -57
- data/lib/ruby_indexer/test/uri_test.rb +0 -85
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
|
+
# Holds the detected value and the reason for detection
|
|
6
|
+
class DetectionResult
|
|
7
|
+
#: String
|
|
8
|
+
attr_reader :value
|
|
9
|
+
|
|
10
|
+
#: String
|
|
11
|
+
attr_reader :reason
|
|
12
|
+
|
|
13
|
+
#: (String value, String reason) -> void
|
|
14
|
+
def initialize(value, reason)
|
|
15
|
+
@value = value
|
|
16
|
+
@reason = reason
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
5
20
|
class GlobalState
|
|
6
21
|
#: String
|
|
7
22
|
attr_reader :test_library
|
|
@@ -122,8 +137,11 @@ module RubyLsp
|
|
|
122
137
|
end
|
|
123
138
|
|
|
124
139
|
if @formatter == "auto"
|
|
125
|
-
|
|
126
|
-
|
|
140
|
+
formatter_result = detect_formatter(direct_dependencies, all_dependencies)
|
|
141
|
+
@formatter = formatter_result.value
|
|
142
|
+
notifications << Notification.window_log_message(
|
|
143
|
+
"Auto detected formatter: #{@formatter} (#{formatter_result.reason})",
|
|
144
|
+
)
|
|
127
145
|
end
|
|
128
146
|
|
|
129
147
|
specified_linters = options.dig(:initializationOptions, :linters)
|
|
@@ -144,21 +162,28 @@ module RubyLsp
|
|
|
144
162
|
specified_linters << "rubocop_internal"
|
|
145
163
|
end
|
|
146
164
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
Notification.window_log_message("Using linters specified by user: #{@linters.join(", ")}")
|
|
165
|
+
if specified_linters
|
|
166
|
+
@linters = specified_linters
|
|
167
|
+
notifications << Notification.window_log_message("Using linters specified by user: #{@linters.join(", ")}")
|
|
151
168
|
else
|
|
152
|
-
|
|
169
|
+
linter_results = detect_linters(direct_dependencies, all_dependencies)
|
|
170
|
+
@linters = linter_results.map(&:value)
|
|
171
|
+
linter_messages = linter_results.map { |r| "#{r.value} (#{r.reason})" }
|
|
172
|
+
notifications << Notification.window_log_message("Auto detected linters: #{linter_messages.join(", ")}")
|
|
153
173
|
end
|
|
154
174
|
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
test_library_result = detect_test_library(direct_dependencies)
|
|
176
|
+
@test_library = test_library_result.value
|
|
177
|
+
notifications << Notification.window_log_message(
|
|
178
|
+
"Detected test library: #{@test_library} (#{test_library_result.reason})",
|
|
179
|
+
)
|
|
157
180
|
|
|
158
|
-
|
|
159
|
-
|
|
181
|
+
typechecker_result = detect_typechecker(all_dependencies)
|
|
182
|
+
@has_type_checker = !typechecker_result.nil?
|
|
183
|
+
if typechecker_result
|
|
160
184
|
notifications << Notification.window_log_message(
|
|
161
|
-
"Ruby LSP detected this is a Sorbet project and will defer to the
|
|
185
|
+
"Ruby LSP detected this is a Sorbet project (#{typechecker_result.reason}) and will defer to the " \
|
|
186
|
+
"Sorbet LSP for some functionality",
|
|
162
187
|
)
|
|
163
188
|
end
|
|
164
189
|
|
|
@@ -228,60 +253,67 @@ module RubyLsp
|
|
|
228
253
|
|
|
229
254
|
private
|
|
230
255
|
|
|
231
|
-
#: (Array[String] direct_dependencies, Array[String] all_dependencies) ->
|
|
256
|
+
#: (Array[String] direct_dependencies, Array[String] all_dependencies) -> DetectionResult
|
|
232
257
|
def detect_formatter(direct_dependencies, all_dependencies)
|
|
233
258
|
# NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
|
|
234
|
-
|
|
259
|
+
if direct_dependencies.any?(/^rubocop/)
|
|
260
|
+
return DetectionResult.new("rubocop_internal", "direct dependency matching /^rubocop/")
|
|
261
|
+
end
|
|
235
262
|
|
|
236
|
-
|
|
237
|
-
|
|
263
|
+
if direct_dependencies.include?("syntax_tree")
|
|
264
|
+
return DetectionResult.new("syntax_tree", "direct dependency")
|
|
265
|
+
end
|
|
238
266
|
|
|
239
|
-
|
|
240
|
-
|
|
267
|
+
if all_dependencies.include?("rubocop") && dot_rubocop_yml_present
|
|
268
|
+
return DetectionResult.new("rubocop_internal", "transitive dependency with .rubocop.yml present")
|
|
269
|
+
end
|
|
241
270
|
|
|
242
|
-
"none"
|
|
271
|
+
DetectionResult.new("none", "no formatter detected")
|
|
243
272
|
end
|
|
244
273
|
|
|
245
274
|
# Try to detect if there are linters in the project's dependencies. For auto-detection, we always only consider a
|
|
246
275
|
# single linter. To have multiple linters running, the user must configure them manually
|
|
247
|
-
#: (Array[String] dependencies, Array[String] all_dependencies) -> Array[
|
|
276
|
+
#: (Array[String] dependencies, Array[String] all_dependencies) -> Array[DetectionResult]
|
|
248
277
|
def detect_linters(dependencies, all_dependencies)
|
|
249
|
-
linters = []
|
|
278
|
+
linters = [] #: Array[DetectionResult]
|
|
250
279
|
|
|
251
|
-
if dependencies.any?(/^rubocop/)
|
|
252
|
-
linters << "rubocop_internal"
|
|
280
|
+
if dependencies.any?(/^rubocop/)
|
|
281
|
+
linters << DetectionResult.new("rubocop_internal", "direct dependency matching /^rubocop/")
|
|
282
|
+
elsif all_dependencies.include?("rubocop") && dot_rubocop_yml_present
|
|
283
|
+
linters << DetectionResult.new("rubocop_internal", "transitive dependency with .rubocop.yml present")
|
|
253
284
|
end
|
|
254
285
|
|
|
255
286
|
linters
|
|
256
287
|
end
|
|
257
288
|
|
|
258
|
-
#: (Array[String] dependencies) ->
|
|
289
|
+
#: (Array[String] dependencies) -> DetectionResult
|
|
259
290
|
def detect_test_library(dependencies)
|
|
260
291
|
if dependencies.any?(/^rspec/)
|
|
261
|
-
"rspec"
|
|
292
|
+
DetectionResult.new("rspec", "direct dependency matching /^rspec/")
|
|
262
293
|
# A Rails app may have a dependency on minitest, but we would instead want to use the Rails test runner provided
|
|
263
294
|
# by ruby-lsp-rails. A Rails app doesn't need to depend on the rails gem itself, individual components like
|
|
264
295
|
# activestorage may be added to the gemfile so that other components aren't downloaded. Check for the presence
|
|
265
296
|
# of bin/rails to support these cases.
|
|
266
297
|
elsif bin_rails_present
|
|
267
|
-
"rails"
|
|
298
|
+
DetectionResult.new("rails", "bin/rails present")
|
|
268
299
|
# NOTE: Intentionally ends with $ to avoid mis-matching minitest-reporters, etc. in a Rails app.
|
|
269
300
|
elsif dependencies.any?(/^minitest$/)
|
|
270
|
-
"minitest"
|
|
301
|
+
DetectionResult.new("minitest", "direct dependency matching /^minitest$/")
|
|
271
302
|
elsif dependencies.any?(/^test-unit/)
|
|
272
|
-
"test-unit"
|
|
303
|
+
DetectionResult.new("test-unit", "direct dependency matching /^test-unit/")
|
|
273
304
|
else
|
|
274
|
-
"unknown"
|
|
305
|
+
DetectionResult.new("unknown", "no test library detected")
|
|
275
306
|
end
|
|
276
307
|
end
|
|
277
308
|
|
|
278
|
-
#: (Array[String] dependencies) ->
|
|
309
|
+
#: (Array[String] dependencies) -> DetectionResult?
|
|
279
310
|
def detect_typechecker(dependencies)
|
|
280
|
-
return
|
|
311
|
+
return if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]
|
|
312
|
+
return if dependencies.none?(/^sorbet-static/)
|
|
281
313
|
|
|
282
|
-
|
|
314
|
+
DetectionResult.new("sorbet", "sorbet-static in dependencies")
|
|
283
315
|
rescue Bundler::GemfileNotFound
|
|
284
|
-
|
|
316
|
+
nil
|
|
285
317
|
end
|
|
286
318
|
|
|
287
319
|
#: -> bool
|
|
@@ -71,24 +71,26 @@ module RubyLsp
|
|
|
71
71
|
|
|
72
72
|
#: (Prism::StringNode node) -> void
|
|
73
73
|
def on_string_node_enter(node)
|
|
74
|
-
enclosing_call
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
with_enclosing_call(node) do |enclosing_call, name|
|
|
75
|
+
case name
|
|
76
|
+
when :require, :require_relative
|
|
77
|
+
handle_require_definition(node, name)
|
|
78
|
+
when :send, :public_send
|
|
79
|
+
handle_send_or_public_send_definition(enclosing_call, node) { node.content }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
#: (Prism::SymbolNode node) -> void
|
|
84
85
|
def on_symbol_node_enter(node)
|
|
85
|
-
enclosing_call
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
with_enclosing_call(node) do |enclosing_call, name|
|
|
87
|
+
case name
|
|
88
|
+
when :autoload
|
|
89
|
+
handle_autoload_definition(enclosing_call)
|
|
90
|
+
when :send, :public_send
|
|
91
|
+
handle_send_or_public_send_definition(enclosing_call, node) { node.unescaped }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
#: (Prism::BlockArgumentNode node) -> void
|
|
@@ -220,6 +222,24 @@ module RubyLsp
|
|
|
220
222
|
|
|
221
223
|
private
|
|
222
224
|
|
|
225
|
+
#: (Prism::Node node) { (Prism::CallNode, Symbol) -> void } -> void
|
|
226
|
+
def with_enclosing_call(node, &block)
|
|
227
|
+
enclosing_call = @node_context.call_node
|
|
228
|
+
return unless enclosing_call
|
|
229
|
+
|
|
230
|
+
block.call(enclosing_call, enclosing_call.name)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
#: (Prism::CallNode enclosing_call, Prism::Node node) { -> String } -> void
|
|
234
|
+
def handle_send_or_public_send_definition(enclosing_call, node, &block)
|
|
235
|
+
first_argument = enclosing_call.arguments&.arguments&.first
|
|
236
|
+
return unless first_argument.eql?(node)
|
|
237
|
+
|
|
238
|
+
method_name = block.call
|
|
239
|
+
|
|
240
|
+
handle_method_definition(method_name, nil)
|
|
241
|
+
end
|
|
242
|
+
|
|
223
243
|
#: -> void
|
|
224
244
|
def handle_super_node_definition
|
|
225
245
|
# Sorbet can handle super hover on typed true or higher
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "ruby_lsp/requests/support/source_uri"
|
|
5
|
+
require "ruby_lsp/requests/support/package_url"
|
|
5
6
|
|
|
6
7
|
module RubyLsp
|
|
7
8
|
module Listeners
|
|
@@ -59,6 +60,7 @@ module RubyLsp
|
|
|
59
60
|
@lines_to_comments = comments.to_h do |comment|
|
|
60
61
|
[comment.location.end_line, comment]
|
|
61
62
|
end #: Hash[Integer, Prism::Comment]
|
|
63
|
+
@sig_comments = {} #: Hash[Integer, Prism::Comment]
|
|
62
64
|
|
|
63
65
|
dispatcher.register(
|
|
64
66
|
self,
|
|
@@ -67,9 +69,20 @@ module RubyLsp
|
|
|
67
69
|
:on_module_node_enter,
|
|
68
70
|
:on_constant_write_node_enter,
|
|
69
71
|
:on_constant_path_write_node_enter,
|
|
72
|
+
:on_call_node_enter,
|
|
70
73
|
)
|
|
71
74
|
end
|
|
72
75
|
|
|
76
|
+
#: (Prism::CallNode node) -> void
|
|
77
|
+
def on_call_node_enter(node)
|
|
78
|
+
return unless node.name == :sig
|
|
79
|
+
|
|
80
|
+
comment = @lines_to_comments[node.location.start_line - 1]
|
|
81
|
+
return unless comment
|
|
82
|
+
|
|
83
|
+
@sig_comments[node.location.end_line] = comment
|
|
84
|
+
end
|
|
85
|
+
|
|
73
86
|
#: (Prism::DefNode node) -> void
|
|
74
87
|
def on_def_node_enter(node)
|
|
75
88
|
extract_document_link(node)
|
|
@@ -99,55 +112,81 @@ module RubyLsp
|
|
|
99
112
|
|
|
100
113
|
#: (Prism::Node node) -> void
|
|
101
114
|
def extract_document_link(node)
|
|
102
|
-
comment = @lines_to_comments[node.location.start_line - 1]
|
|
115
|
+
comment = @lines_to_comments[node.location.start_line - 1] || @sig_comments[node.location.start_line - 1]
|
|
103
116
|
return unless comment
|
|
104
117
|
|
|
105
|
-
match = comment.location.slice.match(%r{source://.*#\d
|
|
118
|
+
match = comment.location.slice.match(%r{(source://.*#\d+|pkg:gem/.*#.*)$})
|
|
106
119
|
return unless match
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
121
|
+
uri_string = match[0] #: as !nil
|
|
122
|
+
|
|
123
|
+
file_path, line_number = if uri_string.start_with?("pkg:gem/")
|
|
124
|
+
parse_package_url(uri_string)
|
|
125
|
+
else
|
|
126
|
+
parse_source_uri(uri_string)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
return unless file_path
|
|
130
|
+
|
|
131
|
+
@response_builder << Interface::DocumentLink.new(
|
|
132
|
+
range: range_from_location(comment.location),
|
|
133
|
+
target: "file://#{file_path}##{line_number}",
|
|
134
|
+
tooltip: "Jump to #{file_path}##{line_number}",
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
#: (String uri_string) -> [String, String]?
|
|
139
|
+
def parse_package_url(uri_string)
|
|
140
|
+
purl = PackageURL.parse(uri_string) #: as PackageURL?
|
|
141
|
+
return unless purl
|
|
142
|
+
|
|
143
|
+
gem_version = resolve_version(purl.version, purl.name)
|
|
144
|
+
return if gem_version.nil?
|
|
145
|
+
|
|
146
|
+
path, line_number = purl.subpath.split(":", 2)
|
|
147
|
+
return unless path
|
|
148
|
+
|
|
149
|
+
gem_name = purl.name
|
|
150
|
+
file_path = self.class.gem_paths.dig(gem_name, gem_version, CGI.unescape(path))
|
|
151
|
+
return if file_path.nil?
|
|
152
|
+
|
|
153
|
+
[file_path, line_number]
|
|
154
|
+
rescue PackageURL::InvalidPackageURL
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
#: (String uri_string) -> [String, String]?
|
|
159
|
+
def parse_source_uri(uri_string)
|
|
160
|
+
uri = begin
|
|
161
|
+
URI(uri_string)
|
|
112
162
|
rescue URI::Error
|
|
113
163
|
nil
|
|
114
164
|
end #: as URI::Source?
|
|
115
165
|
return unless uri
|
|
116
166
|
|
|
117
|
-
gem_version = resolve_version(uri)
|
|
167
|
+
gem_version = resolve_version(uri.gem_version, uri.gem_name)
|
|
118
168
|
return if gem_version.nil?
|
|
119
169
|
|
|
120
170
|
path = uri.path
|
|
121
171
|
return unless path
|
|
122
172
|
|
|
123
|
-
|
|
124
|
-
return unless gem_name
|
|
125
|
-
|
|
126
|
-
file_path = self.class.gem_paths.dig(gem_name, gem_version, CGI.unescape(path))
|
|
173
|
+
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(path))
|
|
127
174
|
return if file_path.nil?
|
|
128
175
|
|
|
129
|
-
|
|
130
|
-
range: range_from_location(comment.location),
|
|
131
|
-
target: "file://#{file_path}##{uri.line_number}",
|
|
132
|
-
tooltip: "Jump to #{file_path}##{uri.line_number}",
|
|
133
|
-
)
|
|
176
|
+
[file_path, uri.line_number || "0"]
|
|
134
177
|
end
|
|
135
178
|
|
|
136
179
|
# Try to figure out the gem version for a source:// link. The order of precedence is:
|
|
137
180
|
# 1. The version in the URI
|
|
138
181
|
# 2. The version in the RBI file name
|
|
139
182
|
# 3. The version from the gemspec
|
|
140
|
-
#: (
|
|
141
|
-
def resolve_version(
|
|
142
|
-
version = uri.gem_version
|
|
183
|
+
#: (String? version, String? gem_name) -> String?
|
|
184
|
+
def resolve_version(version, gem_name)
|
|
143
185
|
return version unless version.nil? || version.empty?
|
|
144
186
|
|
|
145
187
|
return @gem_version unless @gem_version.nil? || @gem_version.empty?
|
|
146
188
|
|
|
147
|
-
gem_name
|
|
148
|
-
return unless gem_name
|
|
149
|
-
|
|
150
|
-
GEM_TO_VERSION_MAP[gem_name]
|
|
189
|
+
GEM_TO_VERSION_MAP[gem_name.to_s]
|
|
151
190
|
end
|
|
152
191
|
end
|
|
153
192
|
end
|
|
@@ -283,10 +283,10 @@ module RubyLsp
|
|
|
283
283
|
content = KEYWORD_DOCS[keyword]
|
|
284
284
|
return unless content
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
doc_uri = URI::Generic.from_path(path: File.join(STATIC_DOCS_PATH, "#{keyword}.md"))
|
|
287
287
|
|
|
288
288
|
@response_builder.push("```ruby\n#{keyword}\n```", category: :title)
|
|
289
|
-
@response_builder.push("[Read more](#{
|
|
289
|
+
@response_builder.push("[Read more](#{doc_uri})", category: :links)
|
|
290
290
|
@response_builder.push(content, category: :documentation)
|
|
291
291
|
end
|
|
292
292
|
|
|
@@ -94,7 +94,7 @@ module RubyLsp
|
|
|
94
94
|
|
|
95
95
|
#: (Prism::MatchWriteNode node) -> void
|
|
96
96
|
def on_match_write_node_leave(node)
|
|
97
|
-
@inside_regex_capture =
|
|
97
|
+
@inside_regex_capture = false if node.call.message == "=~"
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
#: (Prism::DefNode node) -> void
|
|
@@ -162,7 +162,7 @@ module RubyLsp
|
|
|
162
162
|
|
|
163
163
|
#: (Prism::SelfNode node) -> void
|
|
164
164
|
def on_self_node_enter(node)
|
|
165
|
-
@response_builder.add_token(node.location, :variable, [:
|
|
165
|
+
@response_builder.add_token(node.location, :variable, [:defaultLibrary])
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
#: (Prism::LocalVariableWriteNode node) -> void
|
|
@@ -51,20 +51,42 @@ module RubyLsp
|
|
|
51
51
|
#: -> (Interface::CodeAction)
|
|
52
52
|
def switch_block_style
|
|
53
53
|
source_range = @code_action.dig(:data, :range)
|
|
54
|
-
|
|
54
|
+
if source_range[:start] == source_range[:end]
|
|
55
|
+
block_context = @document.locate_node(
|
|
56
|
+
source_range[:start],
|
|
57
|
+
node_types: [Prism::BlockNode],
|
|
58
|
+
)
|
|
59
|
+
node = block_context.node
|
|
60
|
+
unless node.is_a?(Prism::BlockNode)
|
|
61
|
+
raise InvalidTargetRangeError, "Cursor is not inside a block"
|
|
62
|
+
end
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
# Find the call node at the block node's start position.
|
|
65
|
+
# This should be the call node whose block the cursor is inside of.
|
|
66
|
+
call_context = RubyDocument.locate(
|
|
67
|
+
@document.ast,
|
|
68
|
+
node.location.cached_start_code_units_offset(@document.code_units_cache),
|
|
69
|
+
node_types: [Prism::CallNode],
|
|
70
|
+
code_units_cache: @document.code_units_cache,
|
|
71
|
+
)
|
|
72
|
+
target = call_context.node
|
|
73
|
+
unless target.is_a?(Prism::CallNode) && target.block == node
|
|
74
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
target = @document.locate_first_within_range(
|
|
78
|
+
@code_action.dig(:data, :range),
|
|
79
|
+
node_types: [Prism::CallNode],
|
|
80
|
+
)
|
|
60
81
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
82
|
+
unless target.is_a?(Prism::CallNode)
|
|
83
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
84
|
+
end
|
|
64
85
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
86
|
+
node = target.block
|
|
87
|
+
unless node.is_a?(Prism::BlockNode)
|
|
88
|
+
raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
|
|
89
|
+
end
|
|
68
90
|
end
|
|
69
91
|
|
|
70
92
|
indentation = " " * target.location.start_column unless node.opening_loc.slice == "do"
|
|
@@ -63,12 +63,8 @@ module RubyLsp
|
|
|
63
63
|
kind: Constant::CodeActionKind::REFACTOR_EXTRACT,
|
|
64
64
|
data: { range: @range, uri: @uri.to_s },
|
|
65
65
|
)
|
|
66
|
-
code_actions << Interface::CodeAction.new(
|
|
67
|
-
title: TOGGLE_BLOCK_STYLE_TITLE,
|
|
68
|
-
kind: Constant::CodeActionKind::REFACTOR_REWRITE,
|
|
69
|
-
data: { range: @range, uri: @uri.to_s },
|
|
70
|
-
)
|
|
71
66
|
end
|
|
67
|
+
code_actions.concat(toggle_block_style_action)
|
|
72
68
|
code_actions.concat(attribute_actions)
|
|
73
69
|
|
|
74
70
|
code_actions
|
|
@@ -113,6 +109,25 @@ module RubyLsp
|
|
|
113
109
|
),
|
|
114
110
|
]
|
|
115
111
|
end
|
|
112
|
+
|
|
113
|
+
#: -> Array[Interface::CodeAction]
|
|
114
|
+
def toggle_block_style_action
|
|
115
|
+
if @range[:start] == @range[:end]
|
|
116
|
+
block_context = @document.locate_node(
|
|
117
|
+
@range[:start],
|
|
118
|
+
node_types: [Prism::BlockNode],
|
|
119
|
+
)
|
|
120
|
+
return [] unless block_context.node.is_a?(Prism::BlockNode)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
[
|
|
124
|
+
Interface::CodeAction.new(
|
|
125
|
+
title: TOGGLE_BLOCK_STYLE_TITLE,
|
|
126
|
+
kind: Constant::CodeActionKind::REFACTOR_REWRITE,
|
|
127
|
+
data: { range: @range, uri: @uri.to_s },
|
|
128
|
+
),
|
|
129
|
+
]
|
|
130
|
+
end
|
|
116
131
|
end
|
|
117
132
|
end
|
|
118
133
|
end
|
|
@@ -68,10 +68,12 @@ module RubyLsp
|
|
|
68
68
|
"[Learn more about guessed types](#{GUESSED_TYPES_URL})"
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
@item[:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
unless @item[:kind] == Constant::CompletionItemKind::FILE
|
|
72
|
+
@item[:documentation] = Interface::MarkupContent.new(
|
|
73
|
+
kind: "markdown",
|
|
74
|
+
value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES, extra_links: extra_links),
|
|
75
|
+
)
|
|
76
|
+
end
|
|
75
77
|
|
|
76
78
|
@item
|
|
77
79
|
end
|
|
@@ -84,7 +86,7 @@ module RubyLsp
|
|
|
84
86
|
content = KEYWORD_DOCS[keyword]
|
|
85
87
|
|
|
86
88
|
if content
|
|
87
|
-
|
|
89
|
+
doc_uri = URI::Generic.from_path(path: File.join(STATIC_DOCS_PATH, "#{keyword}.md"))
|
|
88
90
|
|
|
89
91
|
@item[:documentation] = Interface::MarkupContent.new(
|
|
90
92
|
kind: "markdown",
|
|
@@ -93,7 +95,7 @@ module RubyLsp
|
|
|
93
95
|
#{keyword}
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
[Read more](#{
|
|
98
|
+
[Read more](#{doc_uri})
|
|
97
99
|
|
|
98
100
|
#{content}
|
|
99
101
|
MARKDOWN
|