ruby-lsp 0.6.1 → 0.7.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 +4 -4
- data/README.md +14 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +13 -0
- data/exe/ruby-lsp-check +62 -0
- data/lib/ruby_lsp/check_docs.rb +1 -1
- data/lib/ruby_lsp/document.rb +21 -7
- data/lib/ruby_lsp/event_emitter.rb +6 -0
- data/lib/ruby_lsp/executor.rb +38 -15
- data/lib/ruby_lsp/requests/code_lens.rb +31 -5
- data/lib/ruby_lsp/requests/definition.rb +95 -0
- data/lib/ruby_lsp/requests/document_highlight.rb +32 -37
- data/lib/ruby_lsp/requests/hover.rb +17 -0
- data/lib/ruby_lsp/requests/on_type_formatting.rb +37 -29
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +33 -0
- data/lib/ruby_lsp/requests/support/dependency_detector.rb +7 -3
- data/lib/ruby_lsp/requests/support/source_uri.rb +1 -1
- data/lib/ruby_lsp/requests.rb +4 -0
- data/lib/ruby_lsp/server.rb +2 -0
- data/lib/ruby_lsp/setup_bundler.rb +86 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 812bcf5c0bc5512ad4382440118051b99d79e79d37a2e91e1fbe4341458dd94c
|
4
|
+
data.tar.gz: 73a83f9e143bde544d72819e0c867182a0bc8b82ff2ef2dc98032aecdc28e825
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03b1252a63bc78186983a8962b91f2e89efa679e2ee3b4978d655a83e9a6cbdeb6a8171647e416dd4ebf43ae82fc36e04b805444acbc2a7eade386166873026e
|
7
|
+
data.tar.gz: 443e7b58bd600a27827043915897c1d848fe8f07c00d348677a9322dcff005bdb4427d2ceade5347f8256fbb6a68897f85215bdd563110d02b0a1887abb71224
|
data/README.md
CHANGED
@@ -27,7 +27,11 @@ The gem can be installed by doing
|
|
27
27
|
gem install ruby-lsp
|
28
28
|
```
|
29
29
|
|
30
|
-
|
30
|
+
**NOTE**: starting with v0.7.0, it is no longer recommended to add the `ruby-lsp` to the bundle. The gem will generate a
|
31
|
+
custom bundle in `.ruby-lsp/Gemfile` which is used to identify the versions of dependencies that should be used for the
|
32
|
+
application (e.g.: the correct RuboCop version).
|
33
|
+
|
34
|
+
For older versions, if you decide to add the gem to the bundle, it is not necessary to require it.
|
31
35
|
```ruby
|
32
36
|
group :development do
|
33
37
|
gem "ruby-lsp", require: false
|
@@ -42,6 +46,15 @@ See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth de
|
|
42
46
|
For creating rich themes for Ruby using the semantic highlighting information, see the [semantic highlighting
|
43
47
|
documentation](SEMANTIC_HIGHLIGHTING.md).
|
44
48
|
|
49
|
+
### Extensions
|
50
|
+
|
51
|
+
The Ruby LSP provides a server extension system that allows other gems to enhance the base functionality with more
|
52
|
+
editor features. This is the mechanism that powers extensions like
|
53
|
+
|
54
|
+
- [Ruby LSP Rails](https://github.com/Shopify/ruby-lsp-rails)
|
55
|
+
|
56
|
+
For instructions on how to create extensions, see the [server extensions documentation](SERVER_EXTENSIONS.md).
|
57
|
+
|
45
58
|
## Learn More
|
46
59
|
|
47
60
|
* [RubyConf 2022: Improving the development experience with language servers](https://www.youtube.com/watch?v=kEfXPTm1aCI) ([Vinicius Stock](https://github.com/vinistock))
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/exe/ruby-lsp
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
# When we're running without bundler, then we need to make sure the custom bundle is fully configured and re-execute
|
5
|
+
# using `BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp` so that we have access to the gems that are a part of
|
6
|
+
# the application's bundle
|
7
|
+
if ENV["BUNDLE_GEMFILE"].nil? && File.exist?("Gemfile.lock")
|
8
|
+
require_relative "../lib/ruby_lsp/setup_bundler"
|
9
|
+
|
10
|
+
# In some cases, like when the `ruby-lsp` is already a part of the bundle, we don't generate `.ruby-lsp/Gemfile`.
|
11
|
+
# However, we still want to run the server with `bundle exec`. We need to make sure we're pointing to the right
|
12
|
+
# `Gemfile`
|
13
|
+
bundle_gemfile = File.exist?(".ruby-lsp/Gemfile") ? ".ruby-lsp/Gemfile" : "Gemfile"
|
14
|
+
exit exec("BUNDLE_GEMFILE=#{bundle_gemfile} bundle exec ruby-lsp #{ARGV.join(" ")}")
|
15
|
+
end
|
16
|
+
|
4
17
|
require "sorbet-runtime"
|
5
18
|
|
6
19
|
begin
|
data/exe/ruby-lsp-check
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# This executable checks if all automatic LSP requests run successfully on every Ruby file under the current directory
|
5
|
+
|
6
|
+
require "sorbet-runtime"
|
7
|
+
|
8
|
+
begin
|
9
|
+
T::Configuration.default_checked_level = :never
|
10
|
+
T::Configuration.call_validation_error_handler = ->(*) {}
|
11
|
+
T::Configuration.inline_type_error_handler = ->(*) {}
|
12
|
+
T::Configuration.sig_validation_error_handler = ->(*) {}
|
13
|
+
rescue
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
require_relative "../lib/ruby_lsp/internal"
|
18
|
+
|
19
|
+
RubyLsp::Extension.load_extensions
|
20
|
+
|
21
|
+
T::Utils.run_all_sig_blocks
|
22
|
+
|
23
|
+
files = Dir.glob("#{Dir.pwd}/**/*.rb")
|
24
|
+
|
25
|
+
puts "Verifying that all automatic LSP requests execute successfully. This may take a while..."
|
26
|
+
|
27
|
+
errors = {}
|
28
|
+
store = RubyLsp::Store.new
|
29
|
+
message_queue = Thread::Queue.new
|
30
|
+
executor = RubyLsp::Executor.new(store, message_queue)
|
31
|
+
|
32
|
+
files.each_with_index do |file, index|
|
33
|
+
uri = "file://#{file}"
|
34
|
+
store.set(uri: uri, source: File.read(file), version: 1)
|
35
|
+
|
36
|
+
# Executing any of the automatic requests will execute all of them, so here we just pick one
|
37
|
+
result = executor.execute({
|
38
|
+
method: "textDocument/documentSymbol",
|
39
|
+
params: { textDocument: { uri: uri } },
|
40
|
+
})
|
41
|
+
|
42
|
+
error = result.error
|
43
|
+
errors[file] = error if error
|
44
|
+
ensure
|
45
|
+
store.delete(uri)
|
46
|
+
print("\033[M\033[0KCompleted #{index + 1}/#{files.length}") unless ENV["CI"]
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "\n"
|
50
|
+
message_queue.close
|
51
|
+
|
52
|
+
if errors.empty?
|
53
|
+
puts "All automatic LSP requests executed successfully"
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
|
57
|
+
puts <<~ERRORS
|
58
|
+
Errors while executing requests:
|
59
|
+
|
60
|
+
#{errors.map { |file, error| "#{file}: #{error.message}" }.join("\n")}
|
61
|
+
ERRORS
|
62
|
+
exit!
|
data/lib/ruby_lsp/check_docs.rb
CHANGED
@@ -52,7 +52,7 @@ module RubyLsp
|
|
52
52
|
# Find all classes that inherit from BaseRequest or Listener, which are the ones we want to make sure are
|
53
53
|
# documented
|
54
54
|
features = ObjectSpace.each_object(Class).filter_map do |k|
|
55
|
-
klass = T.
|
55
|
+
klass = T.unsafe(k)
|
56
56
|
klass if klass < RubyLsp::Requests::BaseRequest || klass < RubyLsp::Listener
|
57
57
|
end
|
58
58
|
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -114,12 +114,12 @@ module RubyLsp
|
|
114
114
|
params(
|
115
115
|
position: PositionShape,
|
116
116
|
node_types: T::Array[T.class_of(SyntaxTree::Node)],
|
117
|
-
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
|
117
|
+
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
|
118
118
|
end
|
119
119
|
def locate_node(position, node_types: [])
|
120
|
-
return [nil, nil] unless parsed?
|
120
|
+
return [nil, nil, []] unless parsed?
|
121
121
|
|
122
|
-
locate(T.must(@tree), create_scanner.find_char_position(position))
|
122
|
+
locate(T.must(@tree), create_scanner.find_char_position(position), node_types: node_types)
|
123
123
|
end
|
124
124
|
|
125
125
|
sig do
|
@@ -127,12 +127,13 @@ module RubyLsp
|
|
127
127
|
node: SyntaxTree::Node,
|
128
128
|
char_position: Integer,
|
129
129
|
node_types: T::Array[T.class_of(SyntaxTree::Node)],
|
130
|
-
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
|
130
|
+
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
|
131
131
|
end
|
132
132
|
def locate(node, char_position, node_types: [])
|
133
133
|
queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
|
134
134
|
closest = node
|
135
135
|
parent = T.let(nil, T.nilable(SyntaxTree::Node))
|
136
|
+
nesting = T.let([], T::Array[T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)])
|
136
137
|
|
137
138
|
until queue.empty?
|
138
139
|
candidate = queue.shift
|
@@ -140,8 +141,10 @@ module RubyLsp
|
|
140
141
|
# Skip nil child nodes
|
141
142
|
next if candidate.nil?
|
142
143
|
|
143
|
-
# Add the next child_nodes to the queue to be processed
|
144
|
-
|
144
|
+
# Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
|
145
|
+
# same order as the visiting mechanism, which means searching the child nodes before moving on to the next
|
146
|
+
# sibling
|
147
|
+
queue.unshift(*candidate.child_nodes)
|
145
148
|
|
146
149
|
# Skip if the current node doesn't cover the desired position
|
147
150
|
loc = candidate.location
|
@@ -151,6 +154,17 @@ module RubyLsp
|
|
151
154
|
# already
|
152
155
|
break if char_position < loc.start_char
|
153
156
|
|
157
|
+
# If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
|
158
|
+
# need to pop the stack
|
159
|
+
previous_level = nesting.last
|
160
|
+
nesting.pop if previous_level && candidate.start_char > previous_level.end_char
|
161
|
+
|
162
|
+
# Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
|
163
|
+
# target when it is a constant
|
164
|
+
if candidate.is_a?(SyntaxTree::ClassDeclaration) || candidate.is_a?(SyntaxTree::ModuleDeclaration)
|
165
|
+
nesting << candidate
|
166
|
+
end
|
167
|
+
|
154
168
|
# If there are node types to filter by, and the current node is not one of those types, then skip it
|
155
169
|
next if node_types.any? && node_types.none? { |type| candidate.class == type }
|
156
170
|
|
@@ -162,7 +176,7 @@ module RubyLsp
|
|
162
176
|
end
|
163
177
|
end
|
164
178
|
|
165
|
-
[closest, parent]
|
179
|
+
[closest, parent, nesting.map { |n| n.constant.constant.value }]
|
166
180
|
end
|
167
181
|
|
168
182
|
class Scanner
|
@@ -53,6 +53,12 @@ module RubyLsp
|
|
53
53
|
|
54
54
|
# Visit dispatchers are below. Notice that for nodes that create a new scope (e.g.: classes, modules, method defs)
|
55
55
|
# we need both an `on_*` and `after_*` event. This is because some requests must know when we exit the scope
|
56
|
+
sig { override.params(node: T.nilable(SyntaxTree::Node)).void }
|
57
|
+
def visit(node)
|
58
|
+
@listeners[:on_node]&.each { |l| T.unsafe(l).on_node(node) }
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
56
62
|
sig { override.params(node: SyntaxTree::ClassDeclaration).void }
|
57
63
|
def visit_class(node)
|
58
64
|
@listeners[:on_class]&.each { |l| T.unsafe(l).on_class(node) }
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -97,13 +97,11 @@ module RubyLsp
|
|
97
97
|
document_symbol = Requests::DocumentSymbol.new(emitter, @message_queue)
|
98
98
|
document_link = Requests::DocumentLink.new(uri, emitter, @message_queue)
|
99
99
|
code_lens = Requests::CodeLens.new(uri, emitter, @message_queue, @test_library)
|
100
|
-
|
101
|
-
T.unsafe(l).new(document.uri, emitter, @message_queue)
|
102
|
-
end
|
100
|
+
|
103
101
|
semantic_highlighting = Requests::SemanticHighlighting.new(emitter, @message_queue)
|
104
102
|
emitter.visit(document.tree) if document.parsed?
|
105
103
|
|
106
|
-
|
104
|
+
code_lens.merge_external_listeners_responses!
|
107
105
|
|
108
106
|
# Store all responses retrieve in this round of visits in the cache and then return the response for the request
|
109
107
|
# we actually received
|
@@ -130,7 +128,7 @@ module RubyLsp
|
|
130
128
|
)
|
131
129
|
|
132
130
|
nil
|
133
|
-
rescue StandardError => error
|
131
|
+
rescue StandardError, LoadError => error
|
134
132
|
@message_queue << Notification.new(
|
135
133
|
message: "window/showMessage",
|
136
134
|
params: Interface::ShowMessageParams.new(
|
@@ -156,7 +154,7 @@ module RubyLsp
|
|
156
154
|
when "textDocument/diagnostic"
|
157
155
|
begin
|
158
156
|
diagnostic(uri)
|
159
|
-
rescue StandardError => error
|
157
|
+
rescue StandardError, LoadError => error
|
160
158
|
@message_queue << Notification.new(
|
161
159
|
message: "window/showMessage",
|
162
160
|
params: Interface::ShowMessageParams.new(
|
@@ -169,9 +167,26 @@ module RubyLsp
|
|
169
167
|
end
|
170
168
|
when "textDocument/completion"
|
171
169
|
completion(uri, request.dig(:params, :position))
|
170
|
+
when "textDocument/definition"
|
171
|
+
definition(uri, request.dig(:params, :position))
|
172
|
+
when "rubyLsp/textDocument/showSyntaxTree"
|
173
|
+
{ ast: Requests::ShowSyntaxTree.new(@store.get(uri)).run }
|
172
174
|
end
|
173
175
|
end
|
174
176
|
|
177
|
+
sig { params(uri: String, position: Document::PositionShape).returns(T.nilable(Interface::Location)) }
|
178
|
+
def definition(uri, position)
|
179
|
+
document = @store.get(uri)
|
180
|
+
return if document.syntax_error?
|
181
|
+
|
182
|
+
target, _parent = document.locate_node(position, node_types: [SyntaxTree::Command])
|
183
|
+
|
184
|
+
emitter = EventEmitter.new
|
185
|
+
base_listener = Requests::Definition.new(uri, emitter, @message_queue)
|
186
|
+
emitter.emit_for_target(target)
|
187
|
+
base_listener.response
|
188
|
+
end
|
189
|
+
|
175
190
|
sig { params(uri: String).returns(T::Array[Interface::FoldingRange]) }
|
176
191
|
def folding_range(uri)
|
177
192
|
@store.cache_fetch(uri, "textDocument/foldingRange") do |document|
|
@@ -198,15 +213,13 @@ module RubyLsp
|
|
198
213
|
|
199
214
|
# Instantiate all listeners
|
200
215
|
emitter = EventEmitter.new
|
201
|
-
|
202
|
-
listeners = Requests::Hover.listeners.map { |l| l.new(emitter, @message_queue) }
|
216
|
+
hover = Requests::Hover.new(emitter, @message_queue)
|
203
217
|
|
204
218
|
# Emit events for all listeners
|
205
219
|
emitter.emit_for_target(target)
|
206
220
|
|
207
|
-
|
208
|
-
|
209
|
-
base_listener.response
|
221
|
+
hover.merge_external_listeners_responses!
|
222
|
+
hover.response
|
210
223
|
end
|
211
224
|
|
212
225
|
sig { params(uri: String, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object) }
|
@@ -275,10 +288,17 @@ module RubyLsp
|
|
275
288
|
params(
|
276
289
|
uri: String,
|
277
290
|
position: Document::PositionShape,
|
278
|
-
).returns(T::Array[Interface::DocumentHighlight])
|
291
|
+
).returns(T.nilable(T::Array[Interface::DocumentHighlight]))
|
279
292
|
end
|
280
293
|
def document_highlight(uri, position)
|
281
|
-
|
294
|
+
document = @store.get(uri)
|
295
|
+
return if document.syntax_error?
|
296
|
+
|
297
|
+
target, parent = document.locate_node(position)
|
298
|
+
emitter = EventEmitter.new
|
299
|
+
listener = Requests::DocumentHighlight.new(target, parent, emitter, @message_queue)
|
300
|
+
emitter.visit(document.tree)
|
301
|
+
listener.response
|
282
302
|
end
|
283
303
|
|
284
304
|
sig { params(uri: String, range: Document::RangeShape).returns(T.nilable(T::Array[Interface::InlayHint])) }
|
@@ -429,7 +449,9 @@ module RubyLsp
|
|
429
449
|
end
|
430
450
|
|
431
451
|
configured_features = options.dig(:initializationOptions, :enabledFeatures)
|
432
|
-
|
452
|
+
|
453
|
+
# Uncomment the line below and use the variable to gate features behind the experimental flag
|
454
|
+
# experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled)
|
433
455
|
|
434
456
|
enabled_features = case configured_features
|
435
457
|
when Array
|
@@ -458,7 +480,7 @@ module RubyLsp
|
|
458
480
|
Interface::DocumentLinkOptions.new(resolve_provider: false)
|
459
481
|
end
|
460
482
|
|
461
|
-
code_lens_provider = if
|
483
|
+
code_lens_provider = if enabled_features["codeLens"]
|
462
484
|
Interface::CodeLensOptions.new(resolve_provider: false)
|
463
485
|
end
|
464
486
|
|
@@ -532,6 +554,7 @@ module RubyLsp
|
|
532
554
|
inlay_hint_provider: inlay_hint_provider,
|
533
555
|
completion_provider: completion_provider,
|
534
556
|
code_lens_provider: code_lens_provider,
|
557
|
+
definition_provider: enabled_features["definition"],
|
535
558
|
),
|
536
559
|
)
|
537
560
|
end
|
@@ -29,6 +29,7 @@ module RubyLsp
|
|
29
29
|
|
30
30
|
BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
|
31
31
|
ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
|
32
|
+
SUPPORTED_TEST_LIBRARIES = T.let(["minitest", "test-unit"], T::Array[String])
|
32
33
|
|
33
34
|
sig { override.returns(ResponseType) }
|
34
35
|
attr_reader :response
|
@@ -37,6 +38,8 @@ module RubyLsp
|
|
37
38
|
def initialize(uri, emitter, message_queue, test_library)
|
38
39
|
super(emitter, message_queue)
|
39
40
|
|
41
|
+
@uri = T.let(uri, String)
|
42
|
+
@external_listeners = T.let([], T::Array[RubyLsp::Listener[ResponseType]])
|
40
43
|
@test_library = T.let(test_library, String)
|
41
44
|
@response = T.let([], ResponseType)
|
42
45
|
@path = T.let(T.must(URI(uri).path), String)
|
@@ -55,14 +58,31 @@ module RubyLsp
|
|
55
58
|
:after_call,
|
56
59
|
:on_vcall,
|
57
60
|
)
|
61
|
+
|
62
|
+
register_external_listeners!
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { void }
|
66
|
+
def register_external_listeners!
|
67
|
+
self.class.listeners.each do |l|
|
68
|
+
@external_listeners << T.unsafe(l).new(@uri, @emitter, @message_queue)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
sig { void }
|
73
|
+
def merge_external_listeners_responses!
|
74
|
+
@external_listeners.each do |l|
|
75
|
+
merge_response!(l)
|
76
|
+
end
|
58
77
|
end
|
59
78
|
|
60
79
|
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
61
80
|
def on_class(node)
|
62
81
|
@visibility_stack.push(["public", "public"])
|
63
82
|
class_name = node.constant.constant.value
|
83
|
+
@class_stack.push(class_name)
|
84
|
+
|
64
85
|
if class_name.end_with?("Test")
|
65
|
-
@class_stack.push(class_name)
|
66
86
|
add_test_code_lens(
|
67
87
|
node,
|
68
88
|
name: class_name,
|
@@ -81,7 +101,7 @@ module RubyLsp
|
|
81
101
|
sig { params(node: SyntaxTree::DefNode).void }
|
82
102
|
def on_def(node)
|
83
103
|
class_name = @class_stack.last
|
84
|
-
return unless class_name
|
104
|
+
return unless class_name&.end_with?("Test")
|
85
105
|
|
86
106
|
visibility, _ = @visibility_stack.last
|
87
107
|
if visibility == "public"
|
@@ -156,6 +176,9 @@ module RubyLsp
|
|
156
176
|
|
157
177
|
sig { params(node: SyntaxTree::Node, name: String, command: String, kind: Symbol).void }
|
158
178
|
def add_test_code_lens(node, name:, command:, kind:)
|
179
|
+
# don't add code lenses if the test library is not supported or unknown
|
180
|
+
return unless SUPPORTED_TEST_LIBRARIES.include?(@test_library)
|
181
|
+
|
159
182
|
arguments = [
|
160
183
|
@path,
|
161
184
|
name,
|
@@ -195,10 +218,13 @@ module RubyLsp
|
|
195
218
|
|
196
219
|
sig { params(node: SyntaxTree::Command).returns(T.nilable(String)) }
|
197
220
|
def resolve_gem_remote(node)
|
198
|
-
gem_statement = node.arguments.parts.
|
199
|
-
return unless gem_statement
|
221
|
+
gem_statement = node.arguments.parts.first
|
222
|
+
return unless gem_statement.is_a?(SyntaxTree::StringLiteral)
|
223
|
+
|
224
|
+
gem_name = gem_statement.parts.first
|
225
|
+
return unless gem_name.is_a?(SyntaxTree::TStringContent)
|
200
226
|
|
201
|
-
spec = Gem::Specification.stubs.find { |gem| gem.name ==
|
227
|
+
spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name.value }&.to_spec
|
202
228
|
return if spec.nil?
|
203
229
|
|
204
230
|
[spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# 
|
7
|
+
#
|
8
|
+
# The [definition
|
9
|
+
# request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
|
10
|
+
# definition of the symbol under the cursor.
|
11
|
+
#
|
12
|
+
# Currently, only jumping to required files is supported.
|
13
|
+
#
|
14
|
+
# # Example
|
15
|
+
#
|
16
|
+
# ```ruby
|
17
|
+
# require "some_gem/file" # <- Request go to definition on this string will take you to the file
|
18
|
+
# ```
|
19
|
+
class Definition < Listener
|
20
|
+
extend T::Sig
|
21
|
+
extend T::Generic
|
22
|
+
|
23
|
+
ResponseType = type_member { { fixed: T.nilable(Interface::Location) } }
|
24
|
+
|
25
|
+
sig { override.returns(ResponseType) }
|
26
|
+
attr_reader :response
|
27
|
+
|
28
|
+
sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
|
29
|
+
def initialize(uri, emitter, message_queue)
|
30
|
+
super(emitter, message_queue)
|
31
|
+
|
32
|
+
@uri = uri
|
33
|
+
@response = T.let(nil, ResponseType)
|
34
|
+
emitter.register(self, :on_command)
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { params(node: SyntaxTree::Command).void }
|
38
|
+
def on_command(node)
|
39
|
+
message = node.message.value
|
40
|
+
return unless message == "require" || message == "require_relative"
|
41
|
+
|
42
|
+
argument = node.arguments.parts.first
|
43
|
+
return unless argument.is_a?(SyntaxTree::StringLiteral)
|
44
|
+
|
45
|
+
string = argument.parts.first
|
46
|
+
return unless string.is_a?(SyntaxTree::TStringContent)
|
47
|
+
|
48
|
+
required_file = "#{string.value}.rb"
|
49
|
+
|
50
|
+
case message
|
51
|
+
when "require"
|
52
|
+
candidate = find_file_in_load_path(required_file)
|
53
|
+
|
54
|
+
if candidate
|
55
|
+
@response = Interface::Location.new(
|
56
|
+
uri: "file://#{candidate}",
|
57
|
+
range: Interface::Range.new(
|
58
|
+
start: Interface::Position.new(line: 0, character: 0),
|
59
|
+
end: Interface::Position.new(line: 0, character: 0),
|
60
|
+
),
|
61
|
+
)
|
62
|
+
end
|
63
|
+
when "require_relative"
|
64
|
+
current_file = T.must(URI.parse(@uri).path)
|
65
|
+
current_folder = Pathname.new(current_file).dirname
|
66
|
+
candidate = File.expand_path(File.join(current_folder, required_file))
|
67
|
+
|
68
|
+
if candidate
|
69
|
+
@response = Interface::Location.new(
|
70
|
+
uri: "file://#{candidate}",
|
71
|
+
range: Interface::Range.new(
|
72
|
+
start: Interface::Position.new(line: 0, character: 0),
|
73
|
+
end: Interface::Position.new(line: 0, character: 0),
|
74
|
+
),
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
sig { params(file: String).returns(T.nilable(String)) }
|
83
|
+
def find_file_in_load_path(file)
|
84
|
+
return unless file.include?("/")
|
85
|
+
|
86
|
+
$LOAD_PATH.each do |p|
|
87
|
+
found = Dir.glob("**/#{file}", base: p).first
|
88
|
+
return "#{p}/#{found}" if found
|
89
|
+
end
|
90
|
+
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -22,34 +22,49 @@ module RubyLsp
|
|
22
22
|
# FOO # should be highlighted as "read"
|
23
23
|
# end
|
24
24
|
# ```
|
25
|
-
class DocumentHighlight <
|
25
|
+
class DocumentHighlight < Listener
|
26
26
|
extend T::Sig
|
27
27
|
|
28
|
-
|
29
|
-
def initialize(document, position)
|
30
|
-
super(document)
|
28
|
+
ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } }
|
31
29
|
|
32
|
-
|
33
|
-
|
30
|
+
sig { override.returns(ResponseType) }
|
31
|
+
attr_reader :response
|
34
32
|
|
35
|
-
|
33
|
+
sig do
|
34
|
+
params(
|
35
|
+
target: T.nilable(SyntaxTree::Node),
|
36
|
+
parent: T.nilable(SyntaxTree::Node),
|
37
|
+
emitter: EventEmitter,
|
38
|
+
message_queue: Thread::Queue,
|
39
|
+
).void
|
36
40
|
end
|
41
|
+
def initialize(target, parent, emitter, message_queue)
|
42
|
+
super(emitter, message_queue)
|
43
|
+
|
44
|
+
@response = T.let([], T::Array[Interface::DocumentHighlight])
|
45
|
+
|
46
|
+
return unless target && parent
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
highlight_target =
|
49
|
+
case target
|
50
|
+
when *DIRECT_HIGHLIGHTS
|
51
|
+
Support::HighlightTarget.new(target)
|
52
|
+
when SyntaxTree::Ident
|
53
|
+
relevant_node = parent.is_a?(SyntaxTree::Params) ? target : parent
|
54
|
+
Support::HighlightTarget.new(relevant_node)
|
55
|
+
end
|
56
|
+
|
57
|
+
@target = T.let(highlight_target, T.nilable(Support::HighlightTarget))
|
58
|
+
|
59
|
+
emitter.register(self, :on_node) if @target
|
43
60
|
end
|
44
61
|
|
45
|
-
sig {
|
46
|
-
def
|
62
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
63
|
+
def on_node(node)
|
47
64
|
return if node.nil?
|
48
65
|
|
49
66
|
match = T.must(@target).highlight_type(node)
|
50
67
|
add_highlight(match) if match
|
51
|
-
|
52
|
-
super
|
53
68
|
end
|
54
69
|
|
55
70
|
private
|
@@ -65,30 +80,10 @@ module RubyLsp
|
|
65
80
|
T::Array[T.class_of(SyntaxTree::Node)],
|
66
81
|
)
|
67
82
|
|
68
|
-
sig do
|
69
|
-
params(
|
70
|
-
position: Document::PositionShape,
|
71
|
-
).returns(T.nilable(Support::HighlightTarget))
|
72
|
-
end
|
73
|
-
def find(position)
|
74
|
-
matched, parent = @document.locate_node(position)
|
75
|
-
|
76
|
-
return unless matched && parent
|
77
|
-
return unless matched.is_a?(SyntaxTree::Ident) || DIRECT_HIGHLIGHTS.include?(matched.class)
|
78
|
-
|
79
|
-
case matched
|
80
|
-
when *DIRECT_HIGHLIGHTS
|
81
|
-
Support::HighlightTarget.new(matched)
|
82
|
-
when SyntaxTree::Ident
|
83
|
-
relevant_node = parent.is_a?(SyntaxTree::Params) ? matched : parent
|
84
|
-
Support::HighlightTarget.new(relevant_node)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
83
|
sig { params(match: Support::HighlightTarget::HighlightMatch).void }
|
89
84
|
def add_highlight(match)
|
90
85
|
range = range_from_syntax_tree_node(match.node)
|
91
|
-
@
|
86
|
+
@response << Interface::DocumentHighlight.new(range: range, kind: match.type)
|
92
87
|
end
|
93
88
|
end
|
94
89
|
end
|
@@ -39,8 +39,25 @@ module RubyLsp
|
|
39
39
|
def initialize(emitter, message_queue)
|
40
40
|
super
|
41
41
|
|
42
|
+
@external_listeners = T.let([], T::Array[RubyLsp::Listener[ResponseType]])
|
42
43
|
@response = T.let(nil, ResponseType)
|
43
44
|
emitter.register(self, :on_command, :on_const_path_ref, :on_call)
|
45
|
+
|
46
|
+
register_external_listeners!
|
47
|
+
end
|
48
|
+
|
49
|
+
sig { void }
|
50
|
+
def register_external_listeners!
|
51
|
+
self.class.listeners.each do |l|
|
52
|
+
@external_listeners << T.unsafe(l).new(@emitter, @message_queue)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
sig { void }
|
57
|
+
def merge_external_listeners_responses!
|
58
|
+
@external_listeners.each do |l|
|
59
|
+
merge_response!(l)
|
60
|
+
end
|
44
61
|
end
|
45
62
|
|
46
63
|
# Merges responses from other hover listeners
|
@@ -30,13 +30,11 @@ module RubyLsp
|
|
30
30
|
def initialize(document, position, trigger_character)
|
31
31
|
super(document)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
@line_end = T.let(scanner.find_char_position(position), Integer)
|
36
|
-
line = T.must(@document.source[line_begin..@line_end])
|
33
|
+
@lines = T.let(@document.source.lines, T::Array[String])
|
34
|
+
line = @lines[[position[:line] - 1, 0].max]
|
37
35
|
|
38
|
-
@indentation = T.let(find_indentation(line), Integer)
|
39
|
-
@previous_line = T.let(line.strip.chomp, String)
|
36
|
+
@indentation = T.let(line ? find_indentation(line) : 0, Integer)
|
37
|
+
@previous_line = T.let(line ? line.strip.chomp : "", String)
|
40
38
|
@position = position
|
41
39
|
@edits = T.let([], T::Array[Interface::TextEdit])
|
42
40
|
@trigger_character = trigger_character
|
@@ -64,9 +62,34 @@ module RubyLsp
|
|
64
62
|
|
65
63
|
sig { void }
|
66
64
|
def handle_pipe
|
67
|
-
|
65
|
+
current_line = @lines[@position[:line]]
|
66
|
+
return unless /((?<=do)|(?<={))\s+\|/.match?(current_line)
|
67
|
+
|
68
|
+
line = T.must(current_line)
|
69
|
+
|
70
|
+
# If the current character is a pipe and both previous ones are pipes too, then we autocompleted a pipe and the
|
71
|
+
# user inserted a third one. In this case, we need to avoid adding a fourth and remove the previous one
|
72
|
+
if line[@position[:character] - 2] == "|" &&
|
73
|
+
line[@position[:character] - 1] == "|" &&
|
74
|
+
line[@position[:character]] == "|"
|
75
|
+
|
76
|
+
@edits << Interface::TextEdit.new(
|
77
|
+
range: Interface::Range.new(
|
78
|
+
start: Interface::Position.new(
|
79
|
+
line: @position[:line],
|
80
|
+
character: @position[:character],
|
81
|
+
),
|
82
|
+
end: Interface::Position.new(
|
83
|
+
line: @position[:line],
|
84
|
+
character: @position[:character] + 1,
|
85
|
+
),
|
86
|
+
),
|
87
|
+
new_text: "",
|
88
|
+
)
|
89
|
+
else
|
90
|
+
add_edit_with_text("|")
|
91
|
+
end
|
68
92
|
|
69
|
-
add_edit_with_text("|")
|
70
93
|
move_cursor_to(@position[:line], @position[:character])
|
71
94
|
end
|
72
95
|
|
@@ -87,30 +110,15 @@ module RubyLsp
|
|
87
110
|
return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
|
88
111
|
|
89
112
|
indents = " " * @indentation
|
113
|
+
current_line = @lines[@position[:line]]
|
114
|
+
next_line = @lines[@position[:line] + 1]
|
90
115
|
|
91
|
-
if
|
92
|
-
# If the previous line has a line break, then it means there's content after the line break that triggered
|
93
|
-
# this completion. For these cases, we want to add the `end` after the content and move the cursor back to the
|
94
|
-
# keyword that triggered the completion
|
95
|
-
|
96
|
-
line = @position[:line]
|
97
|
-
|
98
|
-
# If there are enough lines in the document, we want to add the `end` token on the line below the extra
|
99
|
-
# content. Otherwise, we want to insert and extra line break ourselves
|
100
|
-
correction = if T.must(@document.source[@line_end..-1]).count("\n") >= 2
|
101
|
-
line -= 1
|
102
|
-
"#{indents}end"
|
103
|
-
else
|
104
|
-
"#{indents}\nend"
|
105
|
-
end
|
106
|
-
|
107
|
-
add_edit_with_text(correction, { line: @position[:line] + 1, character: @position[:character] })
|
108
|
-
move_cursor_to(line, @indentation + 3)
|
109
|
-
else
|
110
|
-
# If there's nothing after the new line break that triggered the completion, then we want to add the `end` and
|
111
|
-
# move the cursor to the body of the statement
|
116
|
+
if current_line.nil? || current_line.strip.empty?
|
112
117
|
add_edit_with_text(" \n#{indents}end")
|
113
118
|
move_cursor_to(@position[:line], @indentation + 2)
|
119
|
+
elsif next_line.nil? || next_line.strip.empty?
|
120
|
+
add_edit_with_text("#{indents}end", { line: @position[:line] + 1, character: @position[:character] })
|
121
|
+
move_cursor_to(@position[:line], @indentation + 3)
|
114
122
|
end
|
115
123
|
end
|
116
124
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# 
|
7
|
+
#
|
8
|
+
# Show syntax tree is a custom [LSP
|
9
|
+
# request](https://microsoft.github.io/language-server-protocol/specification#requestMessage) that displays the AST
|
10
|
+
# for the current document in a new tab.
|
11
|
+
#
|
12
|
+
# # Example
|
13
|
+
#
|
14
|
+
# ```ruby
|
15
|
+
# # Executing the Ruby LSP: Show syntax tree command will display the AST for the document
|
16
|
+
# 1 + 1
|
17
|
+
# # (program (statements ((binary (int "1") + (int "1")))))
|
18
|
+
# ```
|
19
|
+
#
|
20
|
+
class ShowSyntaxTree < BaseRequest
|
21
|
+
extend T::Sig
|
22
|
+
|
23
|
+
sig { override.returns(String) }
|
24
|
+
def run
|
25
|
+
return "Document contains syntax error" if @document.syntax_error?
|
26
|
+
|
27
|
+
output_string = +""
|
28
|
+
PP.pp(@document.tree, output_string)
|
29
|
+
output_string
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -20,15 +20,19 @@ module RubyLsp
|
|
20
20
|
|
21
21
|
sig { returns(String) }
|
22
22
|
def detected_test_library
|
23
|
-
|
23
|
+
# A Rails app may have a dependency on minitest, but we would instead want to use the Rails test runner provided
|
24
|
+
# by ruby-lsp-rails.
|
25
|
+
if direct_dependency?(/^rails$/)
|
26
|
+
"rails"
|
27
|
+
# NOTE: Intentionally ends with $ to avoid mis-matching minitest-reporters, etc. in a Rails app.
|
28
|
+
elsif direct_dependency?(/^minitest$/)
|
24
29
|
"minitest"
|
25
30
|
elsif direct_dependency?(/^test-unit/)
|
26
31
|
"test-unit"
|
27
32
|
elsif direct_dependency?(/^rspec/)
|
28
33
|
"rspec"
|
29
34
|
else
|
30
|
-
|
31
|
-
"minitest"
|
35
|
+
"unknown"
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
@@ -78,7 +78,7 @@ module URI
|
|
78
78
|
if URI.respond_to?(:register_scheme)
|
79
79
|
URI.register_scheme("SOURCE", self)
|
80
80
|
else
|
81
|
-
@@schemes = T.let(@@schemes, T::Hash[String, T
|
81
|
+
@@schemes = T.let(@@schemes, T::Hash[String, T.untyped]) # rubocop:disable Style/ClassVars
|
82
82
|
@@schemes["SOURCE"] = self
|
83
83
|
end
|
84
84
|
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -19,6 +19,8 @@ module RubyLsp
|
|
19
19
|
# - [InlayHint](rdoc-ref:RubyLsp::Requests::InlayHints)
|
20
20
|
# - [PathCompletion](rdoc-ref:RubyLsp::Requests::PathCompletion)
|
21
21
|
# - [CodeLens](rdoc-ref:RubyLsp::Requests::CodeLens)
|
22
|
+
# - [Definition](rdoc-ref:RubyLsp::Requests::Definition)
|
23
|
+
# - [ShowSyntaxTree](rdoc-ref:RubyLsp::Requests::ShowSyntaxTree)
|
22
24
|
|
23
25
|
module Requests
|
24
26
|
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
@@ -37,6 +39,8 @@ module RubyLsp
|
|
37
39
|
autoload :InlayHints, "ruby_lsp/requests/inlay_hints"
|
38
40
|
autoload :PathCompletion, "ruby_lsp/requests/path_completion"
|
39
41
|
autoload :CodeLens, "ruby_lsp/requests/code_lens"
|
42
|
+
autoload :Definition, "ruby_lsp/requests/definition"
|
43
|
+
autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
|
40
44
|
|
41
45
|
# :nodoc:
|
42
46
|
module Support
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -0,0 +1,86 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler"
|
5
|
+
require "fileutils"
|
6
|
+
require "pathname"
|
7
|
+
|
8
|
+
# This file is a script that will configure a custom bundle for the Ruby LSP. The custom bundle allows developers to use
|
9
|
+
# the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
|
10
|
+
# exact locked versions of dependencies.
|
11
|
+
|
12
|
+
# Do not setup a custom bundle if we're working on the Ruby LSP, since it's already included by default
|
13
|
+
if Pathname.new(Dir.pwd).basename == "ruby-lsp"
|
14
|
+
warn("Ruby LSP> Skipping custom bundle setup since we're working on the Ruby LSP itself")
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
# We need to parse the Gemfile.lock manually here. If we try to do `bundler/setup` to use something more convenient, we
|
19
|
+
# may end up with issues when the globally installed `ruby-lsp` version mismatches the one included in the `Gemfile`
|
20
|
+
dependencies = Bundler::LockfileParser.new(Bundler.read_file("Gemfile.lock")).dependencies
|
21
|
+
|
22
|
+
# When working on a gem, the `ruby-lsp` might be listed as a dependency in the gemspec. We need to make sure we check
|
23
|
+
# those as well or else we may get version mismatch errors
|
24
|
+
gemspec_path = Dir.glob("*.gemspec").first
|
25
|
+
if gemspec_path
|
26
|
+
gemspec_dependencies = Bundler.load_gemspec(gemspec_path).dependencies.to_h { |dep| [dep.name, dep] }
|
27
|
+
dependencies.merge!(gemspec_dependencies)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Do not setup a custom bundle if both `ruby-lsp` and `debug` are already in the Gemfile
|
31
|
+
if dependencies["ruby-lsp"] && dependencies["debug"]
|
32
|
+
warn("Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in the Gemfile")
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
# Automatically create and ignore the .ruby-lsp folder for users
|
37
|
+
FileUtils.mkdir(".ruby-lsp") unless Dir.exist?(".ruby-lsp")
|
38
|
+
File.write(".ruby-lsp/.gitignore", "*") unless File.exist?(".ruby-lsp/.gitignore")
|
39
|
+
|
40
|
+
parts = [
|
41
|
+
"# This custom gemfile is automatically generated by the Ruby LSP.",
|
42
|
+
"# It should be automatically git ignored, but in any case: do not commit it to your repository.",
|
43
|
+
"",
|
44
|
+
"eval_gemfile(File.expand_path(\"../Gemfile\", __dir__))",
|
45
|
+
]
|
46
|
+
|
47
|
+
unless dependencies["ruby-lsp"]
|
48
|
+
parts << 'gem "ruby-lsp", require: false, group: :development, source: "https://rubygems.org"'
|
49
|
+
end
|
50
|
+
|
51
|
+
unless dependencies["debug"]
|
52
|
+
parts << 'gem "debug", require: false, group: :development, platforms: :mri, source: "https://rubygems.org"'
|
53
|
+
end
|
54
|
+
|
55
|
+
gemfile_content = parts.join("\n")
|
56
|
+
|
57
|
+
unless File.exist?(".ruby-lsp/Gemfile") && File.read(".ruby-lsp/Gemfile") == gemfile_content
|
58
|
+
File.write(".ruby-lsp/Gemfile", gemfile_content)
|
59
|
+
end
|
60
|
+
|
61
|
+
# If .ruby-lsp/Gemfile.lock already exists and the top level Gemfile.lock hasn't been modified since it was last
|
62
|
+
# updated, then we're ready to boot the server
|
63
|
+
if File.exist?(".ruby-lsp/Gemfile.lock") && File.stat(".ruby-lsp/Gemfile.lock").mtime > File.stat("Gemfile.lock").mtime
|
64
|
+
warn("Ruby LSP> Skipping custom bundle setup since .ruby-lsp/Gemfile.lock already exists and is up to date")
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
FileUtils.cp("Gemfile.lock", ".ruby-lsp/Gemfile.lock")
|
69
|
+
|
70
|
+
# If the user has a custom bundle path configured, we need to ensure that we will use the absolute and not relative
|
71
|
+
# version of it when running bundle install. This is necessary to avoid installing the gems under the `.ruby-lsp`
|
72
|
+
# folder, which is not the user's intention. For example, if path is configured as `vendor`, we want to install it in
|
73
|
+
# the top level `vendor` and not `.ruby-lsp/vendor`
|
74
|
+
path = Bundler.settings["path"]
|
75
|
+
|
76
|
+
command = +""
|
77
|
+
# Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
|
78
|
+
command << "BUNDLE_PATH=#{File.expand_path(path, Dir.pwd)} " if path
|
79
|
+
# Install gems using the custom bundle
|
80
|
+
command << "BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle install "
|
81
|
+
# Redirect stdout to stderr to prevent going into an infinite loop. The extension might confuse stdout output with
|
82
|
+
# responses
|
83
|
+
command << "1>&2"
|
84
|
+
|
85
|
+
warn("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
|
86
|
+
system(command)
|
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.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -63,6 +63,7 @@ email:
|
|
63
63
|
- ruby@shopify.com
|
64
64
|
executables:
|
65
65
|
- ruby-lsp
|
66
|
+
- ruby-lsp-check
|
66
67
|
extensions: []
|
67
68
|
extra_rdoc_files: []
|
68
69
|
files:
|
@@ -70,6 +71,7 @@ files:
|
|
70
71
|
- README.md
|
71
72
|
- VERSION
|
72
73
|
- exe/ruby-lsp
|
74
|
+
- exe/ruby-lsp-check
|
73
75
|
- lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
|
74
76
|
- lib/ruby-lsp.rb
|
75
77
|
- lib/ruby_lsp/check_docs.rb
|
@@ -84,6 +86,7 @@ files:
|
|
84
86
|
- lib/ruby_lsp/requests/code_action_resolve.rb
|
85
87
|
- lib/ruby_lsp/requests/code_actions.rb
|
86
88
|
- lib/ruby_lsp/requests/code_lens.rb
|
89
|
+
- lib/ruby_lsp/requests/definition.rb
|
87
90
|
- lib/ruby_lsp/requests/diagnostics.rb
|
88
91
|
- lib/ruby_lsp/requests/document_highlight.rb
|
89
92
|
- lib/ruby_lsp/requests/document_link.rb
|
@@ -96,6 +99,7 @@ files:
|
|
96
99
|
- lib/ruby_lsp/requests/path_completion.rb
|
97
100
|
- lib/ruby_lsp/requests/selection_ranges.rb
|
98
101
|
- lib/ruby_lsp/requests/semantic_highlighting.rb
|
102
|
+
- lib/ruby_lsp/requests/show_syntax_tree.rb
|
99
103
|
- lib/ruby_lsp/requests/support/annotation.rb
|
100
104
|
- lib/ruby_lsp/requests/support/common.rb
|
101
105
|
- lib/ruby_lsp/requests/support/dependency_detector.rb
|
@@ -113,6 +117,7 @@ files:
|
|
113
117
|
- lib/ruby_lsp/requests/support/source_uri.rb
|
114
118
|
- lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb
|
115
119
|
- lib/ruby_lsp/server.rb
|
120
|
+
- lib/ruby_lsp/setup_bundler.rb
|
116
121
|
- lib/ruby_lsp/store.rb
|
117
122
|
- lib/ruby_lsp/utils.rb
|
118
123
|
homepage: https://github.com/Shopify/ruby-lsp
|
@@ -135,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
140
|
- !ruby/object:Gem::Version
|
136
141
|
version: '0'
|
137
142
|
requirements: []
|
138
|
-
rubygems_version: 3.4.
|
143
|
+
rubygems_version: 3.4.16
|
139
144
|
signing_key:
|
140
145
|
specification_version: 4
|
141
146
|
summary: An opinionated language server for Ruby
|