ruby-lsp 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
# ![Definition demo](../../definition.gif)
|
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
|
+
# ![Show syntax tree demo](../../show_syntax_tree.gif)
|
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
|