ruby-lsp 0.4.4 → 0.4.5
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 +1 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -1
- data/lib/ruby_lsp/event_emitter.rb +3 -1
- data/lib/ruby_lsp/executor.rb +34 -11
- data/lib/ruby_lsp/extension.rb +104 -0
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listener.rb +10 -0
- data/lib/ruby_lsp/requests/hover.rb +15 -0
- data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -0
- data/lib/ruby_lsp/server.rb +14 -5
- data/lib/ruby_lsp/utils.rb +12 -6
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 437c2048862bed450fd70057489636117c99943dd7bb68df35c4e161a29f8385
|
4
|
+
data.tar.gz: 30d26e8455f9086bc934ffe23b5e37816f18f5904e3422495c316a980a860cfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 366dd4b756ded58762b598545e398059fe0eae7578b13c92cc0a90dcc9aba2d58106dbf2dc0e9867a9db7de5007e6114ebbbf7cf191bcdebb843b077de945ba9
|
7
|
+
data.tar.gz: 1f798c1462825cccafd48bff23baecb8ba8ef36f9b5733502d79c5d8e6e52550c7ab722a99678eec04ce2a8b42c4d84aab912acb2100ff7d26e422b02f2c185b
|
data/README.md
CHANGED
@@ -123,7 +123,7 @@ To add a new expectations test runner for a new request handler:
|
|
123
123
|
3. [`vscode-ruby-lsp`] Select `Run Extension` and click the green triangle (or press F5).
|
124
124
|
4. [`vscode-ruby-lsp`] Now VS Code will:
|
125
125
|
- Open another workspace as the `Extension Development Host`.
|
126
|
-
- Run `vscode-ruby-lsp` extension in debug mode, which will start a new `ruby-lsp` process with the `--debug` flag.
|
126
|
+
- Run `vscode-ruby-lsp` extension in debug mode, which will start a new `ruby-lsp` process with the `--debug` flag. Note that debugging is not available on Windows.
|
127
127
|
5. Open `ruby-lsp` in VS Code.
|
128
128
|
6. [`ruby-lsp`] Run `bin/rdbg -A` to connect to the running `ruby-lsp` process.
|
129
129
|
7. [`ruby-lsp`] Use commands like `b <file>:<line>` or `b Class#method` to set breakpoints and type `c` to continue the process.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.5
|
data/exe/ruby-lsp
CHANGED
@@ -21,6 +21,11 @@ end
|
|
21
21
|
require_relative "../lib/ruby_lsp/internal"
|
22
22
|
|
23
23
|
if ARGV.include?("--debug")
|
24
|
+
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
25
|
+
puts "Debugging is not supported on Windows"
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
24
29
|
sockets_dir = "/tmp/ruby-lsp-debug-sockets"
|
25
30
|
Dir.mkdir(sockets_dir) unless Dir.exist?(sockets_dir)
|
26
31
|
# ruby-debug-ENV["USER"] is an implicit naming pattern in ruby/debug
|
@@ -28,7 +33,11 @@ if ARGV.include?("--debug")
|
|
28
33
|
socket_identifier = "ruby-debug-#{ENV["USER"]}-#{File.basename(Dir.pwd)}.sock"
|
29
34
|
ENV["RUBY_DEBUG_SOCK_PATH"] = "#{sockets_dir}/#{socket_identifier}"
|
30
35
|
|
31
|
-
|
36
|
+
begin
|
37
|
+
require "debug/open_nonstop"
|
38
|
+
rescue LoadError
|
39
|
+
warn("You need to install the debug gem to use the --debug flag")
|
40
|
+
end
|
32
41
|
end
|
33
42
|
|
34
43
|
RubyLsp::Server.new.start
|
@@ -9,7 +9,7 @@ module RubyLsp
|
|
9
9
|
# - For nonpositional requests, use `visit` to go through the AST, which will fire events for each listener as nodes
|
10
10
|
# are found
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# # Example
|
13
13
|
#
|
14
14
|
# ```ruby
|
15
15
|
# target_node = document.locate_node(position)
|
@@ -46,6 +46,8 @@ module RubyLsp
|
|
46
46
|
@event_to_listener_map[:on_call]&.each { |listener| T.unsafe(listener).on_call(node) }
|
47
47
|
when SyntaxTree::ConstPathRef
|
48
48
|
@event_to_listener_map[:on_const_path_ref]&.each { |listener| T.unsafe(listener).on_const_path_ref(node) }
|
49
|
+
when SyntaxTree::Const
|
50
|
+
@event_to_listener_map[:on_const]&.each { |listener| T.unsafe(listener).on_const(node) }
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
data/lib/ruby_lsp/executor.rb
CHANGED
@@ -11,7 +11,7 @@ module RubyLsp
|
|
11
11
|
# Requests that mutate the store must be run sequentially! Parallel requests only receive a temporary copy of the
|
12
12
|
# store
|
13
13
|
@store = store
|
14
|
-
@
|
14
|
+
@messages = T.let([], T::Array[Message])
|
15
15
|
end
|
16
16
|
|
17
17
|
sig { params(request: T::Hash[Symbol, T.untyped]).returns(Result) }
|
@@ -25,7 +25,7 @@ module RubyLsp
|
|
25
25
|
error = e
|
26
26
|
end
|
27
27
|
|
28
|
-
Result.new(response: response, error: error, request_time: request_time,
|
28
|
+
Result.new(response: response, error: error, request_time: request_time, messages: @messages)
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
@@ -38,6 +38,22 @@ module RubyLsp
|
|
38
38
|
when "initialize"
|
39
39
|
initialize_request(request.dig(:params))
|
40
40
|
when "initialized"
|
41
|
+
Extension.load_extensions
|
42
|
+
|
43
|
+
errored_extensions = Extension.extensions.select(&:error?)
|
44
|
+
|
45
|
+
if errored_extensions.any?
|
46
|
+
@messages << Notification.new(
|
47
|
+
message: "window/showMessage",
|
48
|
+
params: Interface::ShowMessageParams.new(
|
49
|
+
type: Constant::MessageType::WARNING,
|
50
|
+
message: "Error loading extensions:\n\n#{errored_extensions.map(&:formatted_errors).join("\n\n")}",
|
51
|
+
),
|
52
|
+
)
|
53
|
+
|
54
|
+
warn(errored_extensions.map(&:backtraces).join("\n\n"))
|
55
|
+
end
|
56
|
+
|
41
57
|
warn("Ruby LSP is ready")
|
42
58
|
VOID
|
43
59
|
when "textDocument/didOpen"
|
@@ -47,7 +63,7 @@ module RubyLsp
|
|
47
63
|
request.dig(:params, :textDocument, :version),
|
48
64
|
)
|
49
65
|
when "textDocument/didClose"
|
50
|
-
@
|
66
|
+
@messages << Notification.new(
|
51
67
|
message: "textDocument/publishDiagnostics",
|
52
68
|
params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: []),
|
53
69
|
)
|
@@ -75,7 +91,7 @@ module RubyLsp
|
|
75
91
|
begin
|
76
92
|
formatting(uri)
|
77
93
|
rescue Requests::Formatting::InvalidFormatter => error
|
78
|
-
@
|
94
|
+
@messages << Notification.new(
|
79
95
|
message: "window/showMessage",
|
80
96
|
params: Interface::ShowMessageParams.new(
|
81
97
|
type: Constant::MessageType::ERROR,
|
@@ -85,7 +101,7 @@ module RubyLsp
|
|
85
101
|
|
86
102
|
nil
|
87
103
|
rescue StandardError => error
|
88
|
-
@
|
104
|
+
@messages << Notification.new(
|
89
105
|
message: "window/showMessage",
|
90
106
|
params: Interface::ShowMessageParams.new(
|
91
107
|
type: Constant::MessageType::ERROR,
|
@@ -111,7 +127,7 @@ module RubyLsp
|
|
111
127
|
begin
|
112
128
|
diagnostic(uri)
|
113
129
|
rescue StandardError => error
|
114
|
-
@
|
130
|
+
@messages << Notification.new(
|
115
131
|
message: "window/showMessage",
|
116
132
|
params: Interface::ShowMessageParams.new(
|
117
133
|
type: Constant::MessageType::ERROR,
|
@@ -160,9 +176,16 @@ module RubyLsp
|
|
160
176
|
target = parent
|
161
177
|
end
|
162
178
|
|
163
|
-
|
164
|
-
|
165
|
-
|
179
|
+
# Instantiate all listeners
|
180
|
+
base_listener = Requests::Hover.new
|
181
|
+
listeners = Requests::Hover.listeners.map(&:new)
|
182
|
+
|
183
|
+
# Emit events for all listeners
|
184
|
+
T.unsafe(EventEmitter).new(base_listener, *listeners).emit_for_target(target)
|
185
|
+
|
186
|
+
# Merge all responses into a single hover
|
187
|
+
listeners.each { |ext| base_listener.merge_response!(ext) }
|
188
|
+
base_listener.response
|
166
189
|
end
|
167
190
|
|
168
191
|
sig { params(uri: String).returns(T::Array[Interface::DocumentLink]) }
|
@@ -291,7 +314,7 @@ module RubyLsp
|
|
291
314
|
|
292
315
|
case result
|
293
316
|
when Requests::CodeActionResolve::Error::EmptySelection
|
294
|
-
@
|
317
|
+
@messages << Notification.new(
|
295
318
|
message: "window/showMessage",
|
296
319
|
params: Interface::ShowMessageParams.new(
|
297
320
|
type: Constant::MessageType::ERROR,
|
@@ -300,7 +323,7 @@ module RubyLsp
|
|
300
323
|
)
|
301
324
|
raise Requests::CodeActionResolve::CodeActionError
|
302
325
|
when Requests::CodeActionResolve::Error::InvalidTargetRange
|
303
|
-
@
|
326
|
+
@messages << Notification.new(
|
304
327
|
message: "window/showMessage",
|
305
328
|
params: Interface::ShowMessageParams.new(
|
306
329
|
type: Constant::MessageType::ERROR,
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# To register an extension, inherit from this class and implement both `name` and `activate`
|
6
|
+
#
|
7
|
+
# # Example
|
8
|
+
#
|
9
|
+
# ```ruby
|
10
|
+
# module MyGem
|
11
|
+
# class MyExtension < Extension
|
12
|
+
# def activate
|
13
|
+
# # Perform any relevant initialization
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# def name
|
17
|
+
# "My extension name"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# ```
|
22
|
+
class Extension
|
23
|
+
extend T::Sig
|
24
|
+
extend T::Helpers
|
25
|
+
|
26
|
+
abstract!
|
27
|
+
|
28
|
+
class << self
|
29
|
+
extend T::Sig
|
30
|
+
|
31
|
+
# Automatically track and instantiate extension classes
|
32
|
+
sig { params(child_class: T.class_of(Extension)).void }
|
33
|
+
def inherited(child_class)
|
34
|
+
extensions << child_class.new
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { returns(T::Array[Extension]) }
|
39
|
+
def extensions
|
40
|
+
@extensions ||= T.let([], T.nilable(T::Array[Extension]))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Discovers and loads all extensions. Returns the list of activated extensions
|
44
|
+
sig { returns(T::Array[Extension]) }
|
45
|
+
def load_extensions
|
46
|
+
# Require all extensions entry points, which should be placed under
|
47
|
+
# `some_gem/lib/ruby_lsp/your_gem_name/extension.rb`
|
48
|
+
Gem.find_files("ruby_lsp/**/extension.rb").each do |extension|
|
49
|
+
require File.expand_path(extension)
|
50
|
+
rescue => e
|
51
|
+
warn(e.message)
|
52
|
+
warn(e.backtrace.to_s) # rubocop:disable Lint/RedundantStringCoercion
|
53
|
+
end
|
54
|
+
|
55
|
+
# Activate each one of the discovered extensions. If any problems occur in the extensions, we don't want to
|
56
|
+
# fail to boot the server
|
57
|
+
extensions.each do |extension|
|
58
|
+
extension.activate
|
59
|
+
nil
|
60
|
+
rescue => e
|
61
|
+
extension.add_error(e)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
sig { void }
|
67
|
+
def initialize
|
68
|
+
@errors = T.let([], T::Array[StandardError])
|
69
|
+
end
|
70
|
+
|
71
|
+
sig { params(error: StandardError).returns(T.self_type) }
|
72
|
+
def add_error(error)
|
73
|
+
@errors << error
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
sig { returns(T::Boolean) }
|
78
|
+
def error?
|
79
|
+
@errors.any?
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { returns(String) }
|
83
|
+
def formatted_errors
|
84
|
+
<<~ERRORS
|
85
|
+
#{name}:
|
86
|
+
#{@errors.map(&:message).join("\n")}
|
87
|
+
ERRORS
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { returns(String) }
|
91
|
+
def backtraces
|
92
|
+
@errors.filter_map(&:backtrace).join("\n\n")
|
93
|
+
end
|
94
|
+
|
95
|
+
# Each extension should implement `MyExtension#activate` and use to perform any sort of initialization, such as
|
96
|
+
# reading information into memory or even spawning a separate process
|
97
|
+
sig { abstract.void }
|
98
|
+
def activate; end
|
99
|
+
|
100
|
+
# Extensions should override the `name` method to return the extension name
|
101
|
+
sig { abstract.returns(String) }
|
102
|
+
def name; end
|
103
|
+
end
|
104
|
+
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
data/lib/ruby_lsp/listener.rb
CHANGED
@@ -20,6 +20,16 @@ module RubyLsp
|
|
20
20
|
sig { returns(T.nilable(T::Array[Symbol])) }
|
21
21
|
attr_reader :events
|
22
22
|
|
23
|
+
sig { returns(T::Array[T.class_of(Listener)]) }
|
24
|
+
def listeners
|
25
|
+
@listeners ||= T.let([], T.nilable(T::Array[T.class_of(Listener)]))
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { params(listener: T.class_of(Listener)).void }
|
29
|
+
def add_listener(listener)
|
30
|
+
listeners << listener
|
31
|
+
end
|
32
|
+
|
23
33
|
# All listener events must be defined inside of a `listener_events` block. This is to ensure we know which events
|
24
34
|
# have been registered. Defining an event outside of this block will simply not register it and it'll never be
|
25
35
|
# invoked
|
@@ -41,6 +41,21 @@ module RubyLsp
|
|
41
41
|
super()
|
42
42
|
end
|
43
43
|
|
44
|
+
# Merges responses from other hover listeners
|
45
|
+
sig { params(other: Listener[ResponseType]).returns(T.self_type) }
|
46
|
+
def merge_response!(other)
|
47
|
+
other_response = other.response
|
48
|
+
return self unless other_response
|
49
|
+
|
50
|
+
if @response.nil?
|
51
|
+
@response = other.response
|
52
|
+
else
|
53
|
+
@response.contents.value << other_response.contents.value << "\n\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
44
59
|
listener_events do
|
45
60
|
sig { params(node: SyntaxTree::Command).void }
|
46
61
|
def on_command(node)
|
@@ -80,6 +80,10 @@ module RubyLsp
|
|
80
80
|
|
81
81
|
sig { void }
|
82
82
|
def handle_statement_end
|
83
|
+
# If a keyword occurs in a line which appears be a comment or a string, we will not try to format it, since
|
84
|
+
# it could be a coincidental match. This approach is not perfect, but it should cover most cases.
|
85
|
+
return if @previous_line.start_with?(/["'#]/)
|
86
|
+
|
83
87
|
return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
|
84
88
|
|
85
89
|
indents = " " * @indentation
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -23,6 +23,7 @@ module RubyLsp
|
|
23
23
|
@jobs = T.let({}, T::Hash[T.any(String, Integer), Job])
|
24
24
|
@mutex = T.let(Mutex.new, Mutex)
|
25
25
|
@worker = T.let(new_worker, Thread)
|
26
|
+
@current_request_id = T.let(1, Integer)
|
26
27
|
|
27
28
|
Thread.main.priority = 1
|
28
29
|
end
|
@@ -54,7 +55,7 @@ module RubyLsp
|
|
54
55
|
@worker.join
|
55
56
|
@store.clear
|
56
57
|
|
57
|
-
finalize_request(Result.new(response: nil,
|
58
|
+
finalize_request(Result.new(response: nil, messages: []), request)
|
58
59
|
when "exit"
|
59
60
|
# We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
|
60
61
|
# https://microsoft.github.io/language-server-protocol/specification/#exit
|
@@ -88,7 +89,7 @@ module RubyLsp
|
|
88
89
|
|
89
90
|
result = if job.cancelled
|
90
91
|
# We need to return nil to the client even if the request was cancelled
|
91
|
-
Result.new(response: nil,
|
92
|
+
Result.new(response: nil, messages: [])
|
92
93
|
else
|
93
94
|
Executor.new(@store).execute(request)
|
94
95
|
end
|
@@ -105,9 +106,6 @@ module RubyLsp
|
|
105
106
|
error = result.error
|
106
107
|
response = result.response
|
107
108
|
|
108
|
-
# If the response include any notifications, go through them and publish each one
|
109
|
-
result.notifications.each { |n| @writer.write(method: n.message, params: n.params) }
|
110
|
-
|
111
109
|
if error
|
112
110
|
@writer.write(
|
113
111
|
id: request[:id],
|
@@ -121,6 +119,17 @@ module RubyLsp
|
|
121
119
|
@writer.write(id: request[:id], result: response)
|
122
120
|
end
|
123
121
|
|
122
|
+
# If the response include any messages, go through them and publish each one
|
123
|
+
result.messages.each do |n|
|
124
|
+
case n
|
125
|
+
when Notification
|
126
|
+
@writer.write(method: n.message, params: n.params)
|
127
|
+
when Request
|
128
|
+
@writer.write(id: @current_request_id, method: n.message, params: n.params)
|
129
|
+
@current_request_id += 1
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
124
133
|
request_time = result.request_time
|
125
134
|
if request_time
|
126
135
|
@writer.write(method: "telemetry/event", params: telemetry_params(request, request_time, error))
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -9,8 +9,11 @@ module RubyLsp
|
|
9
9
|
WORKSPACE_URI = T.let("file://#{Dir.pwd}".freeze, String) # rubocop:disable Style/RedundantFreeze
|
10
10
|
|
11
11
|
# A notification to be sent to the client
|
12
|
-
class
|
12
|
+
class Message
|
13
13
|
extend T::Sig
|
14
|
+
extend T::Helpers
|
15
|
+
|
16
|
+
abstract!
|
14
17
|
|
15
18
|
sig { returns(String) }
|
16
19
|
attr_reader :message
|
@@ -25,6 +28,9 @@ module RubyLsp
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
31
|
+
class Notification < Message; end
|
32
|
+
class Request < Message; end
|
33
|
+
|
28
34
|
# The final result of running a request before its IO is finalized
|
29
35
|
class Result
|
30
36
|
extend T::Sig
|
@@ -32,8 +38,8 @@ module RubyLsp
|
|
32
38
|
sig { returns(T.untyped) }
|
33
39
|
attr_reader :response
|
34
40
|
|
35
|
-
sig { returns(T::Array[
|
36
|
-
attr_reader :
|
41
|
+
sig { returns(T::Array[Message]) }
|
42
|
+
attr_reader :messages
|
37
43
|
|
38
44
|
sig { returns(T.nilable(Exception)) }
|
39
45
|
attr_reader :error
|
@@ -44,14 +50,14 @@ module RubyLsp
|
|
44
50
|
sig do
|
45
51
|
params(
|
46
52
|
response: T.untyped,
|
47
|
-
|
53
|
+
messages: T::Array[Message],
|
48
54
|
error: T.nilable(Exception),
|
49
55
|
request_time: T.nilable(Float),
|
50
56
|
).void
|
51
57
|
end
|
52
|
-
def initialize(response:,
|
58
|
+
def initialize(response:, messages:, error: nil, request_time: nil)
|
53
59
|
@response = response
|
54
|
-
@
|
60
|
+
@messages = messages
|
55
61
|
@error = error
|
56
62
|
@request_time = request_time
|
57
63
|
end
|
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.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- lib/ruby_lsp/document.rb
|
76
76
|
- lib/ruby_lsp/event_emitter.rb
|
77
77
|
- lib/ruby_lsp/executor.rb
|
78
|
+
- lib/ruby_lsp/extension.rb
|
78
79
|
- lib/ruby_lsp/internal.rb
|
79
80
|
- lib/ruby_lsp/listener.rb
|
80
81
|
- lib/ruby_lsp/requests.rb
|
@@ -131,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
132
|
- !ruby/object:Gem::Version
|
132
133
|
version: '0'
|
133
134
|
requirements: []
|
134
|
-
rubygems_version: 3.4.
|
135
|
+
rubygems_version: 3.4.12
|
135
136
|
signing_key:
|
136
137
|
specification_version: 4
|
137
138
|
summary: An opinionated language server for Ruby
|