ruby-lsp 0.3.8 → 0.4.1
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 +39 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +2 -1
- data/lib/ruby_lsp/document.rb +5 -3
- data/lib/ruby_lsp/executor.rb +390 -0
- data/lib/ruby_lsp/internal.rb +6 -1
- data/lib/ruby_lsp/requests/base_request.rb +10 -3
- data/lib/ruby_lsp/requests/code_action_resolve.rb +100 -0
- data/lib/ruby_lsp/requests/code_actions.rb +38 -8
- data/lib/ruby_lsp/requests/diagnostics.rb +7 -11
- data/lib/ruby_lsp/requests/formatting.rb +10 -5
- data/lib/ruby_lsp/requests/on_type_formatting.rb +33 -14
- data/lib/ruby_lsp/requests/path_completion.rb +95 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +35 -11
- data/lib/ruby_lsp/requests/support/annotation.rb +46 -0
- data/lib/ruby_lsp/requests/support/highlight_target.rb +14 -3
- data/lib/ruby_lsp/requests/support/prefix_tree.rb +80 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +29 -44
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +27 -1
- data/lib/ruby_lsp/requests/support/sorbet.rb +120 -0
- data/lib/ruby_lsp/requests.rb +7 -1
- data/lib/ruby_lsp/server.rb +129 -221
- data/lib/ruby_lsp/utils.rb +78 -0
- metadata +13 -9
- data/lib/ruby_lsp/handler.rb +0 -118
- data/lib/ruby_lsp/queue.rb +0 -182
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +0 -32
@@ -16,6 +16,22 @@ end
|
|
16
16
|
module RubyLsp
|
17
17
|
module Requests
|
18
18
|
module Support
|
19
|
+
class InternalRuboCopError < StandardError
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
MESSAGE = <<~EOS
|
23
|
+
An internal error occurred for the %s cop.
|
24
|
+
Updating to a newer version of RuboCop may solve this.
|
25
|
+
For more details, run RuboCop on the command line.
|
26
|
+
EOS
|
27
|
+
|
28
|
+
sig { params(rubocop_error: RuboCop::ErrorWithAnalyzedFileLocation).void }
|
29
|
+
def initialize(rubocop_error)
|
30
|
+
message = format(MESSAGE, rubocop_error.cop.name)
|
31
|
+
super(message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
19
35
|
# :nodoc:
|
20
36
|
class RuboCopRunner < RuboCop::Runner
|
21
37
|
extend T::Sig
|
@@ -31,10 +47,18 @@ module RubyLsp
|
|
31
47
|
"--force-exclusion",
|
32
48
|
"--format",
|
33
49
|
"RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
|
34
|
-
]
|
50
|
+
],
|
35
51
|
T::Array[String],
|
36
52
|
)
|
37
53
|
|
54
|
+
begin
|
55
|
+
RuboCop::Options.new.parse(["--raise-cop-error"])
|
56
|
+
DEFAULT_ARGS << "--raise-cop-error"
|
57
|
+
rescue OptionParser::InvalidOption
|
58
|
+
# older versions of RuboCop don't support this flag
|
59
|
+
end
|
60
|
+
DEFAULT_ARGS.freeze
|
61
|
+
|
38
62
|
sig { params(args: String).void }
|
39
63
|
def initialize(*args)
|
40
64
|
@options = T.let({}, T::Hash[Symbol, T.untyped])
|
@@ -63,6 +87,8 @@ module RubyLsp
|
|
63
87
|
raise Formatting::Error, error.message
|
64
88
|
rescue RuboCop::ValidationError => error
|
65
89
|
raise ConfigurationError, error.message
|
90
|
+
rescue RuboCop::ErrorWithAnalyzedFileLocation => error
|
91
|
+
raise InternalRuboCopError, error
|
66
92
|
end
|
67
93
|
|
68
94
|
sig { returns(String) }
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
module Support
|
7
|
+
class Sorbet
|
8
|
+
class << self
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
ANNOTATIONS = T.let(
|
12
|
+
{
|
13
|
+
"abstract!" => Annotation.new(arity: 0),
|
14
|
+
"absurd" => Annotation.new(arity: 1, receiver: true),
|
15
|
+
"all" => Annotation.new(arity: (2..), receiver: true),
|
16
|
+
"any" => Annotation.new(arity: (2..), receiver: true),
|
17
|
+
"assert_type!" => Annotation.new(arity: 2, receiver: true),
|
18
|
+
"attached_class" => Annotation.new(arity: 0, receiver: true),
|
19
|
+
"bind" => Annotation.new(arity: 2, receiver: true),
|
20
|
+
"cast" => Annotation.new(arity: 2, receiver: true),
|
21
|
+
"class_of" => Annotation.new(arity: 1, receiver: true),
|
22
|
+
"enums" => Annotation.new(arity: 0),
|
23
|
+
"interface!" => Annotation.new(arity: 0),
|
24
|
+
"let" => Annotation.new(arity: 2, receiver: true),
|
25
|
+
"mixes_in_class_methods" => Annotation.new(arity: 1),
|
26
|
+
"must" => Annotation.new(arity: 1, receiver: true),
|
27
|
+
"must_because" => Annotation.new(arity: 1, receiver: true),
|
28
|
+
"nilable" => Annotation.new(arity: 1, receiver: true),
|
29
|
+
"noreturn" => Annotation.new(arity: 0, receiver: true),
|
30
|
+
"requires_ancestor" => Annotation.new(arity: 0),
|
31
|
+
"reveal_type" => Annotation.new(arity: 1, receiver: true),
|
32
|
+
"sealed!" => Annotation.new(arity: 0),
|
33
|
+
"self_type" => Annotation.new(arity: 0, receiver: true),
|
34
|
+
"sig" => Annotation.new(arity: 0),
|
35
|
+
"type_member" => Annotation.new(arity: (0..1)),
|
36
|
+
"type_template" => Annotation.new(arity: 0),
|
37
|
+
"unsafe" => Annotation.new(arity: 1),
|
38
|
+
"untyped" => Annotation.new(arity: 0, receiver: true),
|
39
|
+
},
|
40
|
+
T::Hash[String, Annotation],
|
41
|
+
)
|
42
|
+
|
43
|
+
sig do
|
44
|
+
params(
|
45
|
+
node: T.any(SyntaxTree::CallNode, SyntaxTree::VCall),
|
46
|
+
).returns(T::Boolean)
|
47
|
+
end
|
48
|
+
def annotation?(node)
|
49
|
+
annotation = annotation(node)
|
50
|
+
|
51
|
+
return false if annotation.nil?
|
52
|
+
|
53
|
+
return false unless annotation.supports_receiver?(receiver_name(node))
|
54
|
+
|
55
|
+
annotation.supports_arity?(node.arity)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
sig { params(node: T.any(SyntaxTree::CallNode, SyntaxTree::VCall)).returns(T.nilable(Annotation)) }
|
61
|
+
def annotation(node)
|
62
|
+
case node
|
63
|
+
when SyntaxTree::VCall
|
64
|
+
ANNOTATIONS[node.value.value]
|
65
|
+
when SyntaxTree::CallNode
|
66
|
+
ANNOTATIONS[node.message.value]
|
67
|
+
else
|
68
|
+
T.absurd(node)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
sig do
|
73
|
+
params(receiver: T.any(SyntaxTree::CallNode, SyntaxTree::VCall)).returns(T.nilable(String))
|
74
|
+
end
|
75
|
+
def receiver_name(receiver)
|
76
|
+
case receiver
|
77
|
+
when SyntaxTree::CallNode
|
78
|
+
node_name(receiver.receiver)
|
79
|
+
when SyntaxTree::VCall
|
80
|
+
nil
|
81
|
+
else
|
82
|
+
T.absurd(receiver)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
sig do
|
87
|
+
params(node: T.nilable(T.any(
|
88
|
+
SyntaxTree::VarRef,
|
89
|
+
SyntaxTree::CallNode,
|
90
|
+
SyntaxTree::VCall,
|
91
|
+
SyntaxTree::Ident,
|
92
|
+
SyntaxTree::Backtick,
|
93
|
+
SyntaxTree::Const,
|
94
|
+
SyntaxTree::Op,
|
95
|
+
Symbol,
|
96
|
+
))).returns(T.nilable(String))
|
97
|
+
end
|
98
|
+
def node_name(node)
|
99
|
+
case node
|
100
|
+
when NilClass
|
101
|
+
nil
|
102
|
+
when SyntaxTree::VarRef
|
103
|
+
node.value.value
|
104
|
+
when SyntaxTree::CallNode
|
105
|
+
node_name(node.receiver)
|
106
|
+
when SyntaxTree::VCall
|
107
|
+
node_name(node.value)
|
108
|
+
when SyntaxTree::Ident, SyntaxTree::Backtick, SyntaxTree::Const, SyntaxTree::Op
|
109
|
+
node.value
|
110
|
+
when Symbol
|
111
|
+
node.to_s
|
112
|
+
else
|
113
|
+
T.absurd(node)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -14,8 +14,10 @@ module RubyLsp
|
|
14
14
|
# - {RubyLsp::Requests::OnTypeFormatting}
|
15
15
|
# - {RubyLsp::Requests::Diagnostics}
|
16
16
|
# - {RubyLsp::Requests::CodeActions}
|
17
|
+
# - {RubyLsp::Requests::CodeActionResolve}
|
17
18
|
# - {RubyLsp::Requests::DocumentHighlight}
|
18
19
|
# - {RubyLsp::Requests::InlayHints}
|
20
|
+
# - {RubyLsp::Requests::PathCompletion}
|
19
21
|
|
20
22
|
module Requests
|
21
23
|
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
@@ -29,17 +31,21 @@ module RubyLsp
|
|
29
31
|
autoload :OnTypeFormatting, "ruby_lsp/requests/on_type_formatting"
|
30
32
|
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
31
33
|
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
34
|
+
autoload :CodeActionResolve, "ruby_lsp/requests/code_action_resolve"
|
32
35
|
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
33
36
|
autoload :InlayHints, "ruby_lsp/requests/inlay_hints"
|
37
|
+
autoload :PathCompletion, "ruby_lsp/requests/path_completion"
|
34
38
|
|
35
39
|
# :nodoc:
|
36
40
|
module Support
|
37
41
|
autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
|
38
42
|
autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
|
39
43
|
autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
|
40
|
-
autoload :
|
44
|
+
autoload :Annotation, "ruby_lsp/requests/support/annotation"
|
45
|
+
autoload :Sorbet, "ruby_lsp/requests/support/sorbet"
|
41
46
|
autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
|
42
47
|
autoload :RailsDocumentClient, "ruby_lsp/requests/support/rails_document_client"
|
48
|
+
autoload :PrefixTree, "ruby_lsp/requests/support/prefix_tree"
|
43
49
|
end
|
44
50
|
end
|
45
51
|
end
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -1,250 +1,158 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "ruby_lsp/internal"
|
5
|
-
|
6
4
|
module RubyLsp
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
5
|
+
Interface = LanguageServer::Protocol::Interface
|
6
|
+
Constant = LanguageServer::Protocol::Constant
|
7
|
+
Transport = LanguageServer::Protocol::Transport
|
8
|
+
|
9
|
+
class Server
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { void }
|
13
|
+
def initialize
|
14
|
+
@writer = T.let(Transport::Stdio::Writer.new, Transport::Stdio::Writer)
|
15
|
+
@reader = T.let(Transport::Stdio::Reader.new, Transport::Stdio::Reader)
|
16
|
+
@store = T.let(Store.new, Store)
|
17
|
+
|
18
|
+
# The job queue is the actual list of requests we have to process
|
19
|
+
@job_queue = T.let(Thread::Queue.new, Thread::Queue)
|
20
|
+
# The jobs hash is just a way of keeping a handle to jobs based on the request ID, so we can cancel them
|
21
|
+
@jobs = T.let({}, T::Hash[T.any(String, Integer), Job])
|
22
|
+
@mutex = T.let(Mutex.new, Mutex)
|
23
|
+
@worker = T.let(new_worker, Thread)
|
24
|
+
|
25
|
+
Thread.main.priority = 1
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { void }
|
29
|
+
def start
|
30
|
+
warn("Starting Ruby LSP...")
|
31
|
+
|
32
|
+
# Requests that have to be executed sequentially or in the main process are implemented here. All other requests
|
33
|
+
# fall under the else branch which just pushes requests to the queue
|
34
|
+
@reader.read do |request|
|
35
|
+
case request[:method]
|
36
|
+
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange"
|
37
|
+
result = Executor.new(@store).execute(request)
|
38
|
+
finalize_request(result, request)
|
39
|
+
when "$/cancelRequest"
|
40
|
+
# Cancel the job if it's still in the queue
|
41
|
+
@mutex.synchronize { @jobs[request[:params][:id]]&.cancel }
|
42
|
+
when "shutdown"
|
43
|
+
warn("Shutting down Ruby LSP...")
|
44
|
+
|
45
|
+
# Close the queue so that we can no longer receive items
|
46
|
+
@job_queue.close
|
47
|
+
# Clear any remaining jobs so that the thread can terminate
|
48
|
+
@job_queue.clear
|
49
|
+
@jobs.clear
|
50
|
+
# Wait until the thread is finished
|
51
|
+
@worker.join
|
52
|
+
@store.clear
|
53
|
+
|
54
|
+
finalize_request(Result.new(response: nil, notifications: []), request)
|
55
|
+
when "exit"
|
56
|
+
# We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
|
57
|
+
# https://microsoft.github.io/language-server-protocol/specification/#exit
|
58
|
+
status = @store.empty? ? 0 : 1
|
59
|
+
warn("Shutdown complete with status #{status}")
|
60
|
+
exit(status)
|
61
|
+
else
|
62
|
+
# Default case: push the request to the queue to be executed by the worker
|
63
|
+
job = Job.new(request: request, cancelled: false)
|
64
|
+
|
65
|
+
# Remember a handle to the job, so that we can cancel it
|
66
|
+
@mutex.synchronize { @jobs[request[:id]] = job }
|
67
|
+
@job_queue << job
|
68
|
+
end
|
64
69
|
end
|
65
|
-
|
66
|
-
Interface::InitializeResult.new(
|
67
|
-
capabilities: Interface::ServerCapabilities.new(
|
68
|
-
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
69
|
-
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
70
|
-
open_close: true,
|
71
|
-
),
|
72
|
-
selection_range_provider: enabled_features.include?("selectionRanges"),
|
73
|
-
hover_provider: hover_provider,
|
74
|
-
document_symbol_provider: document_symbol_provider,
|
75
|
-
document_link_provider: document_link_provider,
|
76
|
-
folding_range_provider: folding_ranges_provider,
|
77
|
-
semantic_tokens_provider: semantic_tokens_provider,
|
78
|
-
document_formatting_provider: enabled_features.include?("formatting"),
|
79
|
-
document_highlight_provider: enabled_features.include?("documentHighlights"),
|
80
|
-
code_action_provider: enabled_features.include?("codeActions"),
|
81
|
-
document_on_type_formatting_provider: on_type_formatting_provider,
|
82
|
-
diagnostic_provider: diagnostics_provider,
|
83
|
-
inlay_hint_provider: inlay_hint_provider,
|
84
|
-
),
|
85
|
-
)
|
86
70
|
end
|
87
71
|
|
88
|
-
|
89
|
-
uri = request.dig(:params, :textDocument, :uri)
|
90
|
-
store.push_edits(uri, request.dig(:params, :contentChanges))
|
91
|
-
|
92
|
-
Handler::VOID
|
93
|
-
end
|
94
|
-
|
95
|
-
on("textDocument/didOpen") do |request|
|
96
|
-
uri = request.dig(:params, :textDocument, :uri)
|
97
|
-
text = request.dig(:params, :textDocument, :text)
|
98
|
-
store.set(uri, text)
|
99
|
-
|
100
|
-
Handler::VOID
|
101
|
-
end
|
102
|
-
|
103
|
-
on("textDocument/didClose") do |request|
|
104
|
-
uri = request.dig(:params, :textDocument, :uri)
|
105
|
-
store.delete(uri)
|
106
|
-
clear_diagnostics(uri)
|
72
|
+
private
|
107
73
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
on("textDocument/documentLink", parallel: true) do |request|
|
118
|
-
uri = request.dig(:params, :textDocument, :uri)
|
119
|
-
store.cache_fetch(uri, :document_link) do |document|
|
120
|
-
RubyLsp::Requests::DocumentLink.new(uri, document).run
|
121
|
-
end
|
122
|
-
end
|
74
|
+
sig { returns(Thread) }
|
75
|
+
def new_worker
|
76
|
+
Thread.new do
|
77
|
+
# Thread::Queue#pop is thread safe and will wait until an item is available
|
78
|
+
loop do
|
79
|
+
job = T.let(@job_queue.pop, T.nilable(Job))
|
80
|
+
# The only time when the job is nil is when the queue is closed and we can then terminate the thread
|
81
|
+
break if job.nil?
|
123
82
|
|
124
|
-
|
125
|
-
|
126
|
-
document = store.get(request.dig(:params, :textDocument, :uri))
|
83
|
+
request = job.request
|
84
|
+
@mutex.synchronize { @jobs.delete(request[:id]) }
|
127
85
|
|
128
|
-
|
129
|
-
|
86
|
+
result = if job.cancelled
|
87
|
+
# We need to return nil to the client even if the request was cancelled
|
88
|
+
Result.new(response: nil, notifications: [])
|
89
|
+
else
|
90
|
+
Executor.new(@store).execute(request)
|
91
|
+
end
|
130
92
|
|
131
|
-
|
132
|
-
|
133
|
-
Requests::FoldingRanges.new(document).run
|
93
|
+
finalize_request(result, request)
|
94
|
+
end
|
134
95
|
end
|
135
96
|
end
|
136
97
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
98
|
+
# Finalize a Queue::Result. All IO operations should happen here to avoid any issues with cancelling requests
|
99
|
+
sig { params(result: Result, request: T::Hash[Symbol, T.untyped]).void }
|
100
|
+
def finalize_request(result, request)
|
101
|
+
@mutex.synchronize do
|
102
|
+
error = result.error
|
103
|
+
response = result.response
|
104
|
+
|
105
|
+
# If the response include any notifications, go through them and publish each one
|
106
|
+
result.notifications.each { |n| @writer.write(method: n.message, params: n.params) }
|
107
|
+
|
108
|
+
if error
|
109
|
+
@writer.write(
|
110
|
+
id: request[:id],
|
111
|
+
error: {
|
112
|
+
code: Constant::ErrorCodes::INTERNAL_ERROR,
|
113
|
+
message: error.inspect,
|
114
|
+
data: request.to_json,
|
115
|
+
},
|
116
|
+
)
|
117
|
+
elsif response != VOID
|
118
|
+
@writer.write(id: request[:id], result: response)
|
155
119
|
end
|
156
|
-
end
|
157
|
-
end
|
158
120
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
document,
|
164
|
-
encoder: Requests::Support::SemanticTokenEncoder.new,
|
165
|
-
).run,
|
166
|
-
LanguageServer::Protocol::Interface::SemanticTokens,
|
167
|
-
)
|
121
|
+
request_time = result.request_time
|
122
|
+
if request_time
|
123
|
+
@writer.write(method: "telemetry/event", params: telemetry_params(request, request_time, error))
|
124
|
+
end
|
168
125
|
end
|
169
126
|
end
|
170
127
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
Requests::SemanticHighlighting.new(
|
178
|
-
document,
|
179
|
-
range: start_line..end_line,
|
180
|
-
encoder: Requests::Support::SemanticTokenEncoder.new,
|
181
|
-
).run
|
128
|
+
sig do
|
129
|
+
params(
|
130
|
+
request: T::Hash[Symbol, T.untyped],
|
131
|
+
request_time: Float,
|
132
|
+
error: T.nilable(Exception),
|
133
|
+
).returns(T::Hash[Symbol, T.any(String, Float)])
|
182
134
|
end
|
183
|
-
|
184
|
-
on("textDocument/formatting", parallel: true) do |request|
|
135
|
+
def telemetry_params(request, request_time, error)
|
185
136
|
uri = request.dig(:params, :textDocument, :uri)
|
137
|
+
params = {
|
138
|
+
request: request[:method],
|
139
|
+
lspVersion: RubyLsp::VERSION,
|
140
|
+
requestTime: request_time,
|
141
|
+
}
|
186
142
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
end
|
191
|
-
|
192
|
-
on("textDocument/onTypeFormatting", parallel: true) do |request|
|
193
|
-
uri = request.dig(:params, :textDocument, :uri)
|
194
|
-
position = request.dig(:params, :position)
|
195
|
-
character = request.dig(:params, :ch)
|
143
|
+
if error
|
144
|
+
params[:errorClass] = error.class.name
|
145
|
+
params[:errorMessage] = error.message
|
196
146
|
|
197
|
-
|
198
|
-
|
147
|
+
log_params = request[:params]
|
148
|
+
params[:params] = log_params.reject { |k, _| k == :textDocument }.to_json if log_params
|
199
149
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
Requests::DocumentHighlight.new(document, request.dig(:params, :position)).run
|
204
|
-
end
|
205
|
-
|
206
|
-
on("textDocument/codeAction", parallel: true) do |request|
|
207
|
-
uri = request.dig(:params, :textDocument, :uri)
|
208
|
-
document = store.get(uri)
|
209
|
-
range = request.dig(:params, :range)
|
210
|
-
start_line = range.dig(:start, :line)
|
211
|
-
end_line = range.dig(:end, :line)
|
212
|
-
|
213
|
-
Requests::CodeActions.new(uri, document, start_line..end_line).run
|
214
|
-
end
|
215
|
-
|
216
|
-
on("textDocument/inlayHint", parallel: true) do |request|
|
217
|
-
document = store.get(request.dig(:params, :textDocument, :uri))
|
218
|
-
range = request.dig(:params, :range)
|
219
|
-
start_line = range.dig(:start, :line)
|
220
|
-
end_line = range.dig(:end, :line)
|
221
|
-
|
222
|
-
Requests::InlayHints.new(document, start_line..end_line).run
|
223
|
-
end
|
224
|
-
|
225
|
-
on("$/cancelRequest") do |request|
|
226
|
-
cancel_request(request[:params][:id])
|
227
|
-
Handler::VOID
|
228
|
-
end
|
229
|
-
|
230
|
-
on("textDocument/diagnostic", parallel: true) do |request|
|
231
|
-
uri = request.dig(:params, :textDocument, :uri)
|
232
|
-
response = store.cache_fetch(uri, :diagnostics) do |document|
|
233
|
-
Requests::Diagnostics.new(uri, document).run
|
150
|
+
backtrace = error.backtrace
|
151
|
+
params[:backtrace] = backtrace.map { |bt| bt.sub(/^#{Dir.home}/, "~") }.join("\n") if backtrace
|
234
152
|
end
|
235
153
|
|
236
|
-
|
237
|
-
|
238
|
-
show_message(Constant::MessageType::ERROR, "Error running diagnostics: #{error.message}")
|
239
|
-
end
|
240
|
-
|
241
|
-
on("shutdown") { shutdown }
|
242
|
-
|
243
|
-
on("exit") do
|
244
|
-
# We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
|
245
|
-
# https://microsoft.github.io/language-server-protocol/specification/#exit
|
246
|
-
status = store.empty? ? 0 : 1
|
247
|
-
exit(status)
|
154
|
+
params[:uri] = uri.sub(%r{.*://#{Dir.home}}, "~") if uri
|
155
|
+
params
|
248
156
|
end
|
249
157
|
end
|
250
158
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# Used to indicate that a request shouldn't return a response
|
6
|
+
VOID = T.let(Object.new.freeze, Object)
|
7
|
+
|
8
|
+
# A notification to be sent to the client
|
9
|
+
class Notification
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { returns(String) }
|
13
|
+
attr_reader :message
|
14
|
+
|
15
|
+
sig { returns(Object) }
|
16
|
+
attr_reader :params
|
17
|
+
|
18
|
+
sig { params(message: String, params: Object).void }
|
19
|
+
def initialize(message:, params:)
|
20
|
+
@message = message
|
21
|
+
@params = params
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# The final result of running a request before its IO is finalized
|
26
|
+
class Result
|
27
|
+
extend T::Sig
|
28
|
+
|
29
|
+
sig { returns(T.untyped) }
|
30
|
+
attr_reader :response
|
31
|
+
|
32
|
+
sig { returns(T::Array[Notification]) }
|
33
|
+
attr_reader :notifications
|
34
|
+
|
35
|
+
sig { returns(T.nilable(Exception)) }
|
36
|
+
attr_reader :error
|
37
|
+
|
38
|
+
sig { returns(T.nilable(Float)) }
|
39
|
+
attr_reader :request_time
|
40
|
+
|
41
|
+
sig do
|
42
|
+
params(
|
43
|
+
response: T.untyped,
|
44
|
+
notifications: T::Array[Notification],
|
45
|
+
error: T.nilable(Exception),
|
46
|
+
request_time: T.nilable(Float),
|
47
|
+
).void
|
48
|
+
end
|
49
|
+
def initialize(response:, notifications:, error: nil, request_time: nil)
|
50
|
+
@response = response
|
51
|
+
@notifications = notifications
|
52
|
+
@error = error
|
53
|
+
@request_time = request_time
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# A request that will sit in the queue until it's executed
|
58
|
+
class Job
|
59
|
+
extend T::Sig
|
60
|
+
|
61
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
62
|
+
attr_reader :request
|
63
|
+
|
64
|
+
sig { returns(T::Boolean) }
|
65
|
+
attr_reader :cancelled
|
66
|
+
|
67
|
+
sig { params(request: T::Hash[Symbol, T.untyped], cancelled: T::Boolean).void }
|
68
|
+
def initialize(request:, cancelled:)
|
69
|
+
@request = request
|
70
|
+
@cancelled = cancelled
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { void }
|
74
|
+
def cancel
|
75
|
+
@cancelled = true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|