ruby-lsp 0.2.4 → 0.3.2
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/CHANGELOG.md +23 -26
- data/VERSION +1 -1
- data/lib/ruby_lsp/handler.rb +54 -66
- data/lib/ruby_lsp/queue.rb +176 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -3
- data/lib/ruby_lsp/requests/formatting.rb +2 -0
- data/lib/ruby_lsp/requests/on_type_formatting.rb +149 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +17 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +9 -34
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +9 -23
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +73 -0
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +46 -25
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e09a85000549c0b7b7dd2886b49d44ca2e32b830616a4af7181a2d7952f948f
|
4
|
+
data.tar.gz: f19b017ebffce3a090d9fe9c39c94cb0dad75a7a74856c099443839524d513f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0b75fa95c43a4a15a46e3cd663b5dd637c9288497116b356eb69dc3f831cfdbb3e167c1c578b68feee715e85c72d26c7d63236e3250552ddbf0f2db334e8f46
|
7
|
+
data.tar.gz: c80518dba77375f07f9944f0401f6836ca69bba60ba6723677e33e366104dd69b70832e8ce76c05312b47c3a032a5308711505e88be2ce69f3f837bec2db3c21
|
data/CHANGELOG.md
CHANGED
@@ -6,32 +6,29 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
-
## [0.2
|
10
|
-
|
11
|
-
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
-
|
19
|
-
-
|
20
|
-
-
|
21
|
-
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
-
|
26
|
-
|
27
|
-
|
28
|
-
-
|
29
|
-
-
|
30
|
-
-
|
31
|
-
-
|
32
|
-
- Fix handling of argument fowarding in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/228)
|
33
|
-
- Recover from initial syntax errors when opening documents (https://github.com/Shopify/ruby-lsp/pull/224)
|
34
|
-
- Highlight occurrences and definitions in document highlight (https://github.com/Shopify/ruby-lsp/pull/187)
|
9
|
+
## [0.3.2]
|
10
|
+
|
11
|
+
- Make the diagnostic request parallel (https://github.com/Shopify/ruby-lsp/pull/293)
|
12
|
+
- Improve worker stability (https://github.com/Shopify/ruby-lsp/pull/295)
|
13
|
+
|
14
|
+
## [0.3.1]
|
15
|
+
|
16
|
+
- Resolve TODO for LSP v3.17 (https://github.com/Shopify/ruby-lsp/pull/268)
|
17
|
+
- Add dependency constraint for LSP v3.17 (https://github.com/Shopify/ruby-lsp/pull/269)
|
18
|
+
- Handle class/module declarations as a class token with declaration modifier (https://github.com/Shopify/ruby-lsp/pull/260)
|
19
|
+
- Handle required parameters in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/271)
|
20
|
+
- Add comment continuation via on type on_type_formatting (https://github.com/Shopify/ruby-lsp/pull/274)
|
21
|
+
- Make RuboCop runner use composition instead of inheritance (https://github.com/Shopify/ruby-lsp/pull/278)
|
22
|
+
- Protect worker against cancellation during popping (https://github.com/Shopify/ruby-lsp/pull/280)
|
23
|
+
- Handle formatting errors in on_error block (https://github.com/Shopify/ruby-lsp/pull/279)
|
24
|
+
- Fix on type formatting pipe completion for regular or expressions (https://github.com/Shopify/ruby-lsp/pull/282)
|
25
|
+
- Do not fail on LoadError (https://github.com/Shopify/ruby-lsp/pull/292)
|
26
|
+
|
27
|
+
## [0.3.0]
|
28
|
+
- Add on type formatting completions (https://github.com/Shopify/ruby-lsp/pull/253)
|
29
|
+
- Upgrade syntax_tree requirement to >= 3.4 (https://github.com/Shopify/ruby-lsp/pull/254)
|
30
|
+
- Show error message when there's a InfiniteCorrectionLoop exception (https://github.com/Shopify/ruby-lsp/pull/252)
|
31
|
+
- Add request cancellation (https://github.com/Shopify/ruby-lsp/pull/243)
|
35
32
|
|
36
33
|
## [0.2.0]
|
37
34
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2
|
1
|
+
0.3.2
|
data/lib/ruby_lsp/handler.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
require "ruby_lsp/requests"
|
5
5
|
require "ruby_lsp/store"
|
6
|
-
require "
|
6
|
+
require "ruby_lsp/queue"
|
7
7
|
|
8
8
|
module RubyLsp
|
9
9
|
Interface = LanguageServer::Protocol::Interface
|
@@ -14,11 +14,35 @@ module RubyLsp
|
|
14
14
|
extend T::Sig
|
15
15
|
VOID = T.let(Object.new.freeze, Object)
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
class RequestHandler < T::Struct
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
const :action, T.proc.params(request: T::Hash[Symbol, T.untyped]).returns(T.untyped)
|
21
|
+
const :parallel, T::Boolean
|
22
|
+
prop :error_handler,
|
23
|
+
T.nilable(T.proc.params(error: Exception, request: T::Hash[Symbol, T.untyped]).void)
|
24
|
+
|
25
|
+
# A proc that runs in case a request has errored. Receives the error and the original request as arguments. Useful
|
26
|
+
# for displaying window messages on errors
|
27
|
+
sig do
|
28
|
+
params(
|
29
|
+
block: T.proc.bind(Handler).params(error: Exception, request: T::Hash[Symbol, T.untyped]).void
|
30
|
+
).void
|
31
|
+
end
|
32
|
+
def on_error(&block)
|
33
|
+
self.error_handler = block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
extend T::Sig
|
39
|
+
|
40
|
+
sig { params(blk: T.proc.bind(Handler).params(arg0: T.untyped).void).void }
|
41
|
+
def start(&blk)
|
42
|
+
handler = new
|
43
|
+
handler.instance_exec(&blk)
|
44
|
+
handler.start
|
45
|
+
end
|
22
46
|
end
|
23
47
|
|
24
48
|
sig { returns(Store) }
|
@@ -28,61 +52,50 @@ module RubyLsp
|
|
28
52
|
def initialize
|
29
53
|
@writer = T.let(Transport::Stdio::Writer.new, Transport::Stdio::Writer)
|
30
54
|
@reader = T.let(Transport::Stdio::Reader.new, Transport::Stdio::Reader)
|
31
|
-
@handlers = T.let({}, T::Hash[String,
|
55
|
+
@handlers = T.let({}, T::Hash[String, RequestHandler])
|
32
56
|
@store = T.let(Store.new, Store)
|
57
|
+
@queue = T.let(Queue.new(@writer, @handlers), Queue)
|
33
58
|
end
|
34
59
|
|
35
60
|
sig { void }
|
36
61
|
def start
|
37
62
|
$stderr.puts "Starting Ruby LSP..."
|
38
|
-
|
63
|
+
|
64
|
+
@reader.read do |request|
|
65
|
+
handler = @handlers[request[:method]]
|
66
|
+
next if handler.nil?
|
67
|
+
|
68
|
+
if handler.parallel
|
69
|
+
@queue.push(request)
|
70
|
+
else
|
71
|
+
result = @queue.execute(request)
|
72
|
+
@queue.finalize_request(result, request)
|
73
|
+
end
|
74
|
+
end
|
39
75
|
end
|
40
76
|
|
41
77
|
private
|
42
78
|
|
79
|
+
sig { params(id: T.any(String, Integer)).void }
|
80
|
+
def cancel_request(id)
|
81
|
+
@queue.cancel(id)
|
82
|
+
end
|
83
|
+
|
43
84
|
sig do
|
44
85
|
params(
|
45
86
|
msg: String,
|
87
|
+
parallel: T::Boolean,
|
46
88
|
blk: T.proc.bind(Handler).params(request: T::Hash[Symbol, T.untyped]).returns(T.untyped)
|
47
|
-
).
|
89
|
+
).returns(RequestHandler)
|
48
90
|
end
|
49
|
-
def on(msg, &blk)
|
50
|
-
@handlers[msg] = blk
|
51
|
-
end
|
52
|
-
|
53
|
-
sig { params(request: T::Hash[Symbol, T.untyped]).void }
|
54
|
-
def handle(request)
|
55
|
-
result = T.let(nil, T.untyped)
|
56
|
-
error = T.let(nil, T.nilable(StandardError))
|
57
|
-
handler = @handlers[request[:method]]
|
58
|
-
|
59
|
-
request_time = Benchmark.realtime do
|
60
|
-
if handler
|
61
|
-
begin
|
62
|
-
result = handler.call(request)
|
63
|
-
rescue StandardError => e
|
64
|
-
error = e
|
65
|
-
end
|
66
|
-
|
67
|
-
if error
|
68
|
-
@writer.write(
|
69
|
-
{
|
70
|
-
id: request[:id],
|
71
|
-
error: { code: Constant::ErrorCodes::INTERNAL_ERROR, message: error.inspect, data: request.to_json },
|
72
|
-
}
|
73
|
-
)
|
74
|
-
elsif result != VOID
|
75
|
-
@writer.write(id: request[:id], result: result)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
@writer.write(method: "telemetry/event", params: telemetry_params(request, request_time, error))
|
91
|
+
def on(msg, parallel: false, &blk)
|
92
|
+
@handlers[msg] = RequestHandler.new(action: blk, parallel: parallel)
|
81
93
|
end
|
82
94
|
|
83
95
|
sig { void }
|
84
96
|
def shutdown
|
85
97
|
$stderr.puts "Shutting down Ruby LSP..."
|
98
|
+
@queue.shutdown
|
86
99
|
store.clear
|
87
100
|
end
|
88
101
|
|
@@ -101,30 +114,5 @@ module RubyLsp
|
|
101
114
|
params: Interface::ShowMessageParams.new(type: type, message: message)
|
102
115
|
)
|
103
116
|
end
|
104
|
-
|
105
|
-
sig do
|
106
|
-
params(
|
107
|
-
request: T::Hash[Symbol, T.untyped],
|
108
|
-
request_time: Float,
|
109
|
-
error: T.nilable(StandardError)
|
110
|
-
).returns(T::Hash[Symbol, T.any(String, Float)])
|
111
|
-
end
|
112
|
-
def telemetry_params(request, request_time, error)
|
113
|
-
uri = request.dig(:params, :textDocument, :uri)
|
114
|
-
|
115
|
-
params = {
|
116
|
-
request: request[:method],
|
117
|
-
lspVersion: RubyLsp::VERSION,
|
118
|
-
requestTime: request_time,
|
119
|
-
}
|
120
|
-
|
121
|
-
if error
|
122
|
-
params[:errorClass] = error.class.name
|
123
|
-
params[:errorMessage] = error.message
|
124
|
-
end
|
125
|
-
|
126
|
-
params[:uri] = uri.sub(%r{.*://#{Dir.home}}, "~") if uri
|
127
|
-
params
|
128
|
-
end
|
129
117
|
end
|
130
118
|
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "benchmark"
|
5
|
+
|
6
|
+
module RubyLsp
|
7
|
+
class Queue
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
class Result < T::Struct
|
11
|
+
const :response, T.untyped # rubocop:disable Sorbet/ForbidUntypedStructProps
|
12
|
+
const :error, T.nilable(Exception)
|
13
|
+
const :request_time, T.nilable(Float)
|
14
|
+
end
|
15
|
+
|
16
|
+
class Job < T::Struct
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
const :request, T::Hash[Symbol, T.untyped]
|
20
|
+
prop :cancelled, T::Boolean
|
21
|
+
|
22
|
+
sig { void }
|
23
|
+
def cancel
|
24
|
+
self.cancelled = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
sig do
|
29
|
+
params(
|
30
|
+
writer: LanguageServer::Protocol::Transport::Stdio::Writer,
|
31
|
+
handlers: T::Hash[String, Handler::RequestHandler]
|
32
|
+
).void
|
33
|
+
end
|
34
|
+
def initialize(writer, handlers)
|
35
|
+
@writer = writer
|
36
|
+
@handlers = handlers
|
37
|
+
# The job queue is the actual list of requests we have to process
|
38
|
+
@job_queue = T.let(Thread::Queue.new, Thread::Queue)
|
39
|
+
# The jobs hash is just a way of keeping a handle to jobs based on the request ID, so we can cancel them
|
40
|
+
@jobs = T.let({}, T::Hash[T.any(String, Integer), Job])
|
41
|
+
@mutex = T.let(Mutex.new, Mutex)
|
42
|
+
@worker = T.let(new_worker, Thread)
|
43
|
+
|
44
|
+
Thread.main.priority = 1
|
45
|
+
end
|
46
|
+
|
47
|
+
sig { params(request: T::Hash[Symbol, T.untyped]).void }
|
48
|
+
def push(request)
|
49
|
+
job = Job.new(request: request, cancelled: false)
|
50
|
+
|
51
|
+
# Remember a handle to the job, so that we can cancel it
|
52
|
+
@mutex.synchronize do
|
53
|
+
@jobs[request[:id]] = job
|
54
|
+
end
|
55
|
+
|
56
|
+
@job_queue << job
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { params(id: T.any(String, Integer)).void }
|
60
|
+
def cancel(id)
|
61
|
+
@mutex.synchronize do
|
62
|
+
# Cancel the job if it's still in the queue
|
63
|
+
@jobs[id]&.cancel
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { void }
|
68
|
+
def shutdown
|
69
|
+
# Close the queue so that we can no longer receive items
|
70
|
+
@job_queue.close
|
71
|
+
# Clear any remaining jobs so that the thread can terminate
|
72
|
+
@job_queue.clear
|
73
|
+
# Wait until the thread is finished
|
74
|
+
@worker.join
|
75
|
+
end
|
76
|
+
|
77
|
+
# Executes a request and returns a Queue::Result. No IO should happen in this method, because it can be cancelled in
|
78
|
+
# the middle with a raise
|
79
|
+
sig { params(request: T::Hash[Symbol, T.untyped]).returns(Queue::Result) }
|
80
|
+
def execute(request)
|
81
|
+
response = T.let(nil, T.untyped)
|
82
|
+
error = T.let(nil, T.nilable(Exception))
|
83
|
+
|
84
|
+
request_time = Benchmark.realtime do
|
85
|
+
response = T.must(@handlers[request[:method]]).action.call(request)
|
86
|
+
rescue StandardError, LoadError => e
|
87
|
+
error = e
|
88
|
+
end
|
89
|
+
|
90
|
+
Queue::Result.new(response: response, error: error, request_time: request_time)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Finalize a Queue::Result. All IO operations should happen here to avoid any issues with cancelling requests
|
94
|
+
sig do
|
95
|
+
params(
|
96
|
+
result: Result,
|
97
|
+
request: T::Hash[Symbol, T.untyped]
|
98
|
+
).void
|
99
|
+
end
|
100
|
+
def finalize_request(result, request)
|
101
|
+
@mutex.synchronize do
|
102
|
+
error = result.error
|
103
|
+
if error
|
104
|
+
T.must(@handlers[request[:method]]).error_handler&.call(error, request)
|
105
|
+
|
106
|
+
@writer.write(
|
107
|
+
id: request[:id],
|
108
|
+
error: {
|
109
|
+
code: LanguageServer::Protocol::Constant::ErrorCodes::INTERNAL_ERROR,
|
110
|
+
message: result.error.inspect,
|
111
|
+
data: request.to_json,
|
112
|
+
},
|
113
|
+
)
|
114
|
+
elsif result.response != Handler::VOID
|
115
|
+
@writer.write(id: request[:id], result: result.response)
|
116
|
+
end
|
117
|
+
|
118
|
+
request_time = result.request_time
|
119
|
+
if request_time
|
120
|
+
@writer.write(method: "telemetry/event", params: telemetry_params(request, request_time, result.error))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
sig { returns(Thread) }
|
128
|
+
def new_worker
|
129
|
+
Thread.new do
|
130
|
+
# Thread::Queue#pop is thread safe and will wait until an item is available
|
131
|
+
loop do
|
132
|
+
job = T.let(@job_queue.pop, T.nilable(Job))
|
133
|
+
# The only time when the job is nil is when the queue is closed and we can then terminate the thread
|
134
|
+
break if job.nil?
|
135
|
+
|
136
|
+
request = job.request
|
137
|
+
@mutex.synchronize { @jobs.delete(request[:id]) }
|
138
|
+
|
139
|
+
result = if job.cancelled
|
140
|
+
# We need to return nil to the client even if the request was cancelled
|
141
|
+
Queue::Result.new(response: nil, error: nil, request_time: nil)
|
142
|
+
else
|
143
|
+
execute(request)
|
144
|
+
end
|
145
|
+
|
146
|
+
finalize_request(result, request)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
sig do
|
152
|
+
params(
|
153
|
+
request: T::Hash[Symbol, T.untyped],
|
154
|
+
request_time: Float,
|
155
|
+
error: T.nilable(Exception)
|
156
|
+
).returns(T::Hash[Symbol, T.any(String, Float)])
|
157
|
+
end
|
158
|
+
def telemetry_params(request, request_time, error)
|
159
|
+
uri = request.dig(:params, :textDocument, :uri)
|
160
|
+
|
161
|
+
params = {
|
162
|
+
request: request[:method],
|
163
|
+
lspVersion: RubyLsp::VERSION,
|
164
|
+
requestTime: request_time,
|
165
|
+
}
|
166
|
+
|
167
|
+
if error
|
168
|
+
params[:errorClass] = error.class.name
|
169
|
+
params[:errorMessage] = error.message
|
170
|
+
end
|
171
|
+
|
172
|
+
params[:uri] = uri.sub(%r{.*://#{Dir.home}}, "~") if uri
|
173
|
+
params
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -105,9 +105,13 @@ module RubyLsp
|
|
105
105
|
sig { returns(Integer) }
|
106
106
|
attr_reader :end_line
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
class << self
|
109
|
+
extend T::Sig
|
110
|
+
|
111
|
+
sig { params(node: SyntaxTree::Node, kind: String).returns(PartialRange) }
|
112
|
+
def from(node, kind)
|
113
|
+
new(node.location.start_line - 1, node.location.end_line - 1, kind)
|
114
|
+
end
|
111
115
|
end
|
112
116
|
|
113
117
|
sig { params(start_line: Integer, end_line: Integer, kind: String).void }
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# 
|
7
|
+
#
|
8
|
+
# The [on type formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_onTypeFormatting)
|
9
|
+
# request formats code as the user is typing. For example, automatically adding `end` to class definitions.
|
10
|
+
#
|
11
|
+
# # Example
|
12
|
+
#
|
13
|
+
# ```ruby
|
14
|
+
# class Foo # <-- upon adding a line break, on type formatting is triggered
|
15
|
+
# # <-- cursor ends up here
|
16
|
+
# end # <-- end is automatically added
|
17
|
+
# ```
|
18
|
+
class OnTypeFormatting < BaseRequest
|
19
|
+
extend T::Sig
|
20
|
+
|
21
|
+
END_REGEXES = T.let([
|
22
|
+
/(if|unless|for|while|class|module|until|def|case).*/,
|
23
|
+
/.*\sdo/,
|
24
|
+
], T::Array[Regexp])
|
25
|
+
|
26
|
+
sig { params(document: Document, position: Document::PositionShape, trigger_character: String).void }
|
27
|
+
def initialize(document, position, trigger_character)
|
28
|
+
super(document)
|
29
|
+
|
30
|
+
scanner = Document::Scanner.new(document.source)
|
31
|
+
line_begin = position[:line] == 0 ? 0 : scanner.find_position({ line: position[:line] - 1, character: 0 })
|
32
|
+
line_end = scanner.find_position(position)
|
33
|
+
line = T.must(@document.source[line_begin..line_end])
|
34
|
+
|
35
|
+
@indentation = T.let(find_indentation(line), Integer)
|
36
|
+
@previous_line = T.let(line.strip.chomp, String)
|
37
|
+
@position = position
|
38
|
+
@edits = T.let([], T::Array[Interface::TextEdit])
|
39
|
+
@trigger_character = trigger_character
|
40
|
+
end
|
41
|
+
|
42
|
+
sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
|
43
|
+
def run
|
44
|
+
handle_comment_line
|
45
|
+
|
46
|
+
return @edits unless @document.syntax_errors?
|
47
|
+
|
48
|
+
case @trigger_character
|
49
|
+
when "{"
|
50
|
+
handle_curly_brace
|
51
|
+
when "|"
|
52
|
+
handle_pipe
|
53
|
+
when "\n"
|
54
|
+
handle_statement_end
|
55
|
+
end
|
56
|
+
|
57
|
+
@edits
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
sig { void }
|
63
|
+
def handle_pipe
|
64
|
+
return unless /((?<=do)|(?<={))\s+\|/.match?(@previous_line)
|
65
|
+
|
66
|
+
add_edit_with_text("|")
|
67
|
+
move_cursor_to(@position[:line], @position[:character])
|
68
|
+
end
|
69
|
+
|
70
|
+
sig { void }
|
71
|
+
def handle_curly_brace
|
72
|
+
return unless /".*#\{/.match?(@previous_line)
|
73
|
+
|
74
|
+
add_edit_with_text("}")
|
75
|
+
move_cursor_to(@position[:line], @position[:character])
|
76
|
+
end
|
77
|
+
|
78
|
+
sig { void }
|
79
|
+
def handle_statement_end
|
80
|
+
return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
|
81
|
+
|
82
|
+
indents = " " * @indentation
|
83
|
+
|
84
|
+
add_edit_with_text(" \n#{indents}end")
|
85
|
+
move_cursor_to(@position[:line], @indentation + 2)
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { void }
|
89
|
+
def handle_comment_line
|
90
|
+
return unless @trigger_character == "\n"
|
91
|
+
|
92
|
+
is_comment_match = @previous_line.match(/^#(\s*)/)
|
93
|
+
return unless is_comment_match
|
94
|
+
|
95
|
+
spaces = T.must(is_comment_match[1])
|
96
|
+
add_edit_with_text("##{spaces}")
|
97
|
+
move_cursor_to(@position[:line], @indentation + spaces.size + 1)
|
98
|
+
end
|
99
|
+
|
100
|
+
sig { params(text: String).void }
|
101
|
+
def add_edit_with_text(text)
|
102
|
+
position = Interface::Position.new(
|
103
|
+
line: @position[:line],
|
104
|
+
character: @position[:character]
|
105
|
+
)
|
106
|
+
|
107
|
+
@edits << Interface::TextEdit.new(
|
108
|
+
range: Interface::Range.new(
|
109
|
+
start: position,
|
110
|
+
end: position
|
111
|
+
),
|
112
|
+
new_text: text
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
sig { params(line: Integer, character: Integer).void }
|
117
|
+
def move_cursor_to(line, character)
|
118
|
+
position = Interface::Position.new(
|
119
|
+
line: line,
|
120
|
+
character: character
|
121
|
+
)
|
122
|
+
|
123
|
+
# The $0 is a special snippet anchor that moves the cursor to that given position. See the snippets
|
124
|
+
# documentation for more information:
|
125
|
+
# https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets
|
126
|
+
@edits << Interface::TextEdit.new(
|
127
|
+
range: Interface::Range.new(
|
128
|
+
start: position,
|
129
|
+
end: position
|
130
|
+
),
|
131
|
+
new_text: "$0"
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
sig { params(line: String).returns(Integer) }
|
136
|
+
def find_indentation(line)
|
137
|
+
count = 0
|
138
|
+
|
139
|
+
line.chars.each do |c|
|
140
|
+
break unless c == " "
|
141
|
+
|
142
|
+
count += 1
|
143
|
+
end
|
144
|
+
|
145
|
+
count
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -185,6 +185,10 @@ module RubyLsp
|
|
185
185
|
add_token(location_without_colon(location), :variable)
|
186
186
|
end
|
187
187
|
|
188
|
+
node.requireds.each do |required|
|
189
|
+
add_token(required.location, :variable)
|
190
|
+
end
|
191
|
+
|
188
192
|
rest = node.keyword_rest
|
189
193
|
return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
|
190
194
|
|
@@ -217,6 +221,19 @@ module RubyLsp
|
|
217
221
|
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
218
222
|
end
|
219
223
|
|
224
|
+
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
225
|
+
def visit_class(node)
|
226
|
+
add_token(node.constant.location, :class, [:declaration])
|
227
|
+
add_token(node.superclass.location, :class) if node.superclass
|
228
|
+
visit(node.bodystmt)
|
229
|
+
end
|
230
|
+
|
231
|
+
sig { params(node: SyntaxTree::ModuleDeclaration).void }
|
232
|
+
def visit_module(node)
|
233
|
+
add_token(node.constant.location, :class, [:declaration])
|
234
|
+
visit(node.bodystmt)
|
235
|
+
end
|
236
|
+
|
220
237
|
sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
|
221
238
|
def add_token(location, type, modifiers = [])
|
222
239
|
length = location.end_char - location.start_char
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
rescue LoadError
|
7
|
-
return
|
8
|
-
end
|
4
|
+
require "ruby_lsp/requests/support/rubocop_runner"
|
5
|
+
return unless defined?(::RubyLsp::Requests::Support::RuboCopRunner)
|
9
6
|
|
10
7
|
require "cgi"
|
11
8
|
require "singleton"
|
@@ -14,46 +11,24 @@ module RubyLsp
|
|
14
11
|
module Requests
|
15
12
|
module Support
|
16
13
|
# :nodoc:
|
17
|
-
class RuboCopDiagnosticsRunner
|
14
|
+
class RuboCopDiagnosticsRunner
|
18
15
|
extend T::Sig
|
19
16
|
include Singleton
|
20
17
|
|
21
18
|
sig { void }
|
22
19
|
def initialize
|
23
|
-
@
|
24
|
-
@uri = T.let(nil, T.nilable(String))
|
25
|
-
@diagnostics = T.let([], T::Array[Support::RuboCopDiagnostic])
|
26
|
-
|
27
|
-
super(
|
28
|
-
::RuboCop::Options.new.parse([
|
29
|
-
"--stderr", # Print any output to stderr so that our stdout does not get polluted
|
30
|
-
"--force-exclusion",
|
31
|
-
"--format",
|
32
|
-
"RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
|
33
|
-
]).first,
|
34
|
-
::RuboCop::ConfigStore.new
|
35
|
-
)
|
20
|
+
@runner = T.let(RuboCopRunner.new, RuboCopRunner)
|
36
21
|
end
|
37
22
|
|
38
23
|
sig { params(uri: String, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) }
|
39
24
|
def run(uri, document)
|
40
|
-
|
41
|
-
@uri = uri
|
42
|
-
|
43
|
-
file = CGI.unescape(URI.parse(uri).path)
|
44
|
-
# We communicate with Rubocop via stdin
|
45
|
-
@options[:stdin] = document.source
|
46
|
-
|
25
|
+
filename = CGI.unescape(URI.parse(uri).path)
|
47
26
|
# Invoke RuboCop with just this file in `paths`
|
48
|
-
|
49
|
-
@diagnostics
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
27
|
+
@runner.run(filename, document.source)
|
53
28
|
|
54
|
-
|
55
|
-
|
56
|
-
|
29
|
+
@runner.offenses.map do |offense|
|
30
|
+
Support::RuboCopDiagnostic.new(offense, uri)
|
31
|
+
end
|
57
32
|
end
|
58
33
|
end
|
59
34
|
end
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
rescue LoadError
|
7
|
-
return
|
8
|
-
end
|
4
|
+
require "ruby_lsp/requests/support/rubocop_runner"
|
5
|
+
return unless defined?(::RubyLsp::Requests::Support::RuboCopRunner)
|
9
6
|
|
10
7
|
require "cgi"
|
11
8
|
require "singleton"
|
@@ -14,35 +11,24 @@ module RubyLsp
|
|
14
11
|
module Requests
|
15
12
|
module Support
|
16
13
|
# :nodoc:
|
17
|
-
class RuboCopFormattingRunner
|
14
|
+
class RuboCopFormattingRunner
|
18
15
|
extend T::Sig
|
19
16
|
include Singleton
|
20
17
|
|
21
18
|
sig { void }
|
22
19
|
def initialize
|
23
|
-
|
24
|
-
|
25
|
-
super(
|
26
|
-
::RuboCop::Options.new.parse([
|
27
|
-
"--stderr", # Print any output to stderr so that our stdout does not get polluted
|
28
|
-
"--force-exclusion",
|
29
|
-
"--format",
|
30
|
-
"RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
|
31
|
-
"-a", # --auto-correct
|
32
|
-
]).first,
|
33
|
-
::RuboCop::ConfigStore.new
|
34
|
-
)
|
20
|
+
# -a is for "--auto-correct" (or "--autocorrect" on newer versions of RuboCop)
|
21
|
+
@runner = T.let(RuboCopRunner.new("-a"), RuboCopRunner)
|
35
22
|
end
|
36
23
|
|
37
24
|
sig { params(uri: String, document: Document).returns(T.nilable(String)) }
|
38
25
|
def run(uri, document)
|
39
|
-
|
40
|
-
# We communicate with Rubocop via stdin
|
41
|
-
@options[:stdin] = document.source
|
26
|
+
filename = CGI.unescape(URI.parse(uri).path)
|
42
27
|
|
43
28
|
# Invoke RuboCop with just this file in `paths`
|
44
|
-
|
45
|
-
|
29
|
+
@runner.run(filename, document.source)
|
30
|
+
|
31
|
+
@runner.formatted_source
|
46
32
|
end
|
47
33
|
end
|
48
34
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rubocop"
|
6
|
+
rescue LoadError
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
module RubyLsp
|
11
|
+
module Requests
|
12
|
+
module Support
|
13
|
+
# :nodoc:
|
14
|
+
class RuboCopRunner < RuboCop::Runner
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
class ConfigurationError < StandardError; end
|
18
|
+
|
19
|
+
sig { returns(T::Array[RuboCop::Cop::Offense]) }
|
20
|
+
attr_reader :offenses
|
21
|
+
|
22
|
+
DEFAULT_ARGS = T.let([
|
23
|
+
"--stderr", # Print any output to stderr so that our stdout does not get polluted
|
24
|
+
"--force-exclusion",
|
25
|
+
"--format",
|
26
|
+
"RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
|
27
|
+
].freeze, T::Array[String])
|
28
|
+
|
29
|
+
sig { params(args: String).void }
|
30
|
+
def initialize(*args)
|
31
|
+
@options = T.let({}, T::Hash[Symbol, T.untyped])
|
32
|
+
@offenses = T.let([], T::Array[RuboCop::Cop::Offense])
|
33
|
+
@errors = T.let([], T::Array[String])
|
34
|
+
@warnings = T.let([], T::Array[String])
|
35
|
+
|
36
|
+
args += DEFAULT_ARGS
|
37
|
+
rubocop_options = ::RuboCop::Options.new.parse(args).first
|
38
|
+
config_store = ::RuboCop::ConfigStore.new
|
39
|
+
|
40
|
+
super(rubocop_options, config_store)
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(path: String, contents: String).void }
|
44
|
+
def run(path, contents)
|
45
|
+
# Clear Runner state between runs since we get a single instance of this class
|
46
|
+
# on every use site.
|
47
|
+
@errors = []
|
48
|
+
@warnings = []
|
49
|
+
@offenses = []
|
50
|
+
@options[:stdin] = contents
|
51
|
+
|
52
|
+
super([path])
|
53
|
+
rescue RuboCop::Runner::InfiniteCorrectionLoop => error
|
54
|
+
raise Formatting::Error, error.message
|
55
|
+
rescue RuboCop::ValidationError => error
|
56
|
+
raise ConfigurationError, error.message
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { returns(String) }
|
60
|
+
def formatted_source
|
61
|
+
@options[:stdin]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
|
67
|
+
def file_finished(_file, offenses)
|
68
|
+
@offenses = offenses
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -10,6 +10,7 @@ module RubyLsp
|
|
10
10
|
# - {RubyLsp::Requests::SelectionRanges}
|
11
11
|
# - {RubyLsp::Requests::SemanticHighlighting}
|
12
12
|
# - {RubyLsp::Requests::Formatting}
|
13
|
+
# - {RubyLsp::Requests::OnTypeFormatting}
|
13
14
|
# - {RubyLsp::Requests::Diagnostics}
|
14
15
|
# - {RubyLsp::Requests::CodeActions}
|
15
16
|
# - {RubyLsp::Requests::DocumentHighlight}
|
@@ -21,6 +22,7 @@ module RubyLsp
|
|
21
22
|
autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
|
22
23
|
autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
|
23
24
|
autoload :Formatting, "ruby_lsp/requests/formatting"
|
25
|
+
autoload :OnTypeFormatting, "ruby_lsp/requests/on_type_formatting"
|
24
26
|
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
25
27
|
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
26
28
|
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -48,23 +48,30 @@ module RubyLsp
|
|
48
48
|
}
|
49
49
|
end
|
50
50
|
|
51
|
-
|
51
|
+
on_type_formatting_provider = if enabled_features.include?("onTypeFormatting")
|
52
|
+
Interface::DocumentOnTypeFormattingOptions.new(
|
53
|
+
first_trigger_character: "{",
|
54
|
+
more_trigger_character: ["\n", "|"]
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
52
58
|
Interface::InitializeResult.new(
|
53
|
-
capabilities:
|
54
|
-
|
59
|
+
capabilities: Interface::ServerCapabilities.new(
|
60
|
+
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
55
61
|
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
56
62
|
open_close: true,
|
57
63
|
),
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
selection_range_provider: enabled_features.include?("selectionRanges"),
|
65
|
+
document_symbol_provider: document_symbol_provider,
|
66
|
+
document_link_provider: document_link_provider,
|
67
|
+
folding_range_provider: folding_ranges_provider,
|
68
|
+
semantic_tokens_provider: semantic_tokens_provider,
|
69
|
+
document_formatting_provider: enabled_features.include?("formatting"),
|
70
|
+
document_highlight_provider: enabled_features.include?("documentHighlights"),
|
71
|
+
code_action_provider: enabled_features.include?("codeActions"),
|
72
|
+
document_on_type_formatting_provider: on_type_formatting_provider,
|
73
|
+
diagnostic_provider: diagnostics_provider,
|
74
|
+
)
|
68
75
|
)
|
69
76
|
end
|
70
77
|
|
@@ -91,26 +98,26 @@ module RubyLsp
|
|
91
98
|
Handler::VOID
|
92
99
|
end
|
93
100
|
|
94
|
-
on("textDocument/documentSymbol") do |request|
|
101
|
+
on("textDocument/documentSymbol", parallel: true) do |request|
|
95
102
|
store.cache_fetch(request.dig(:params, :textDocument, :uri), :document_symbol) do |document|
|
96
103
|
Requests::DocumentSymbol.new(document).run
|
97
104
|
end
|
98
105
|
end
|
99
106
|
|
100
|
-
on("textDocument/documentLink") do |request|
|
107
|
+
on("textDocument/documentLink", parallel: true) do |request|
|
101
108
|
uri = request.dig(:params, :textDocument, :uri)
|
102
109
|
store.cache_fetch(uri, :document_link) do |document|
|
103
110
|
RubyLsp::Requests::DocumentLink.new(uri, document).run
|
104
111
|
end
|
105
112
|
end
|
106
113
|
|
107
|
-
on("textDocument/foldingRange") do |request|
|
114
|
+
on("textDocument/foldingRange", parallel: true) do |request|
|
108
115
|
store.cache_fetch(request.dig(:params, :textDocument, :uri), :folding_ranges) do |document|
|
109
116
|
Requests::FoldingRanges.new(document).run
|
110
117
|
end
|
111
118
|
end
|
112
119
|
|
113
|
-
on("textDocument/selectionRange") do |request|
|
120
|
+
on("textDocument/selectionRange", parallel: true) do |request|
|
114
121
|
uri = request.dig(:params, :textDocument, :uri)
|
115
122
|
positions = request.dig(:params, :positions)
|
116
123
|
|
@@ -132,7 +139,7 @@ module RubyLsp
|
|
132
139
|
end
|
133
140
|
end
|
134
141
|
|
135
|
-
on("textDocument/semanticTokens/full") do |request|
|
142
|
+
on("textDocument/semanticTokens/full", parallel: true) do |request|
|
136
143
|
store.cache_fetch(request.dig(:params, :textDocument, :uri), :semantic_highlighting) do |document|
|
137
144
|
T.cast(
|
138
145
|
Requests::SemanticHighlighting.new(
|
@@ -144,13 +151,23 @@ module RubyLsp
|
|
144
151
|
end
|
145
152
|
end
|
146
153
|
|
147
|
-
on("textDocument/formatting") do |request|
|
154
|
+
on("textDocument/formatting", parallel: true) do |request|
|
148
155
|
uri = request.dig(:params, :textDocument, :uri)
|
149
156
|
|
150
157
|
Requests::Formatting.new(uri, store.get(uri)).run
|
158
|
+
end.on_error do |error|
|
159
|
+
show_message(Constant::MessageType::ERROR, "Formatting error: #{error.message}")
|
151
160
|
end
|
152
161
|
|
153
|
-
on("textDocument/
|
162
|
+
on("textDocument/onTypeFormatting", parallel: true) do |request|
|
163
|
+
uri = request.dig(:params, :textDocument, :uri)
|
164
|
+
position = request.dig(:params, :position)
|
165
|
+
character = request.dig(:params, :ch)
|
166
|
+
|
167
|
+
Requests::OnTypeFormatting.new(store.get(uri), position, character).run
|
168
|
+
end
|
169
|
+
|
170
|
+
on("textDocument/documentHighlight", parallel: true) do |request|
|
154
171
|
document = store.get(request.dig(:params, :textDocument, :uri))
|
155
172
|
|
156
173
|
if document.parsed?
|
@@ -158,7 +175,7 @@ module RubyLsp
|
|
158
175
|
end
|
159
176
|
end
|
160
177
|
|
161
|
-
on("textDocument/codeAction") do |request|
|
178
|
+
on("textDocument/codeAction", parallel: true) do |request|
|
162
179
|
uri = request.dig(:params, :textDocument, :uri)
|
163
180
|
range = request.dig(:params, :range)
|
164
181
|
start_line = range.dig(:start, :line)
|
@@ -169,16 +186,20 @@ module RubyLsp
|
|
169
186
|
end
|
170
187
|
end
|
171
188
|
|
172
|
-
on("
|
189
|
+
on("$/cancelRequest") do |request|
|
190
|
+
cancel_request(request[:params][:id])
|
191
|
+
Handler::VOID
|
192
|
+
end
|
193
|
+
|
194
|
+
on("textDocument/diagnostic", parallel: true) do |request|
|
173
195
|
uri = request.dig(:params, :textDocument, :uri)
|
174
196
|
response = store.cache_fetch(uri, :diagnostics) do |document|
|
175
197
|
Requests::Diagnostics.new(uri, document).run
|
176
198
|
end
|
177
199
|
|
178
200
|
{ kind: "full", items: response.map(&:to_lsp_diagnostic) } if response
|
179
|
-
|
180
|
-
show_message(Constant::MessageType::ERROR, "Error
|
181
|
-
nil
|
201
|
+
end.on_error do |error|
|
202
|
+
show_message(Constant::MessageType::ERROR, "Error running diagnostics: #{error.message}")
|
182
203
|
end
|
183
204
|
|
184
205
|
on("shutdown") { shutdown }
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.17.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.17.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: sorbet-runtime
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3.4'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.4'
|
55
55
|
description: An opinionated language server for Ruby
|
56
56
|
email:
|
57
57
|
- ruby@shopify.com
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/ruby_lsp/document.rb
|
70
70
|
- lib/ruby_lsp/handler.rb
|
71
71
|
- lib/ruby_lsp/internal.rb
|
72
|
+
- lib/ruby_lsp/queue.rb
|
72
73
|
- lib/ruby_lsp/requests.rb
|
73
74
|
- lib/ruby_lsp/requests/base_request.rb
|
74
75
|
- lib/ruby_lsp/requests/code_actions.rb
|
@@ -78,12 +79,14 @@ files:
|
|
78
79
|
- lib/ruby_lsp/requests/document_symbol.rb
|
79
80
|
- lib/ruby_lsp/requests/folding_ranges.rb
|
80
81
|
- lib/ruby_lsp/requests/formatting.rb
|
82
|
+
- lib/ruby_lsp/requests/on_type_formatting.rb
|
81
83
|
- lib/ruby_lsp/requests/selection_ranges.rb
|
82
84
|
- lib/ruby_lsp/requests/semantic_highlighting.rb
|
83
85
|
- lib/ruby_lsp/requests/support/highlight_target.rb
|
84
86
|
- lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
|
85
87
|
- lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb
|
86
88
|
- lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
|
89
|
+
- lib/ruby_lsp/requests/support/rubocop_runner.rb
|
87
90
|
- lib/ruby_lsp/requests/support/selection_range.rb
|
88
91
|
- lib/ruby_lsp/requests/support/semantic_token_encoder.rb
|
89
92
|
- lib/ruby_lsp/requests/support/source_uri.rb
|