ruby-lsp 0.1.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +2 -1
- data/VERSION +1 -1
- data/exe/ruby-lsp +1 -3
- data/lib/ruby-lsp.rb +2 -2
- data/lib/ruby_lsp/document.rb +10 -3
- data/lib/ruby_lsp/handler.rb +30 -134
- data/lib/ruby_lsp/internal.rb +3 -1
- data/lib/ruby_lsp/requests/code_actions.rb +2 -0
- data/lib/ruby_lsp/requests/diagnostics.rb +14 -9
- data/lib/ruby_lsp/requests/document_highlight.rb +27 -42
- data/lib/ruby_lsp/requests/document_link.rb +59 -0
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +26 -21
- data/lib/ruby_lsp/requests/formatting.rb +21 -16
- data/lib/ruby_lsp/requests/selection_ranges.rb +2 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +85 -11
- data/lib/ruby_lsp/requests/support/highlight_target.rb +88 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +9 -2
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +61 -0
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +50 -0
- data/lib/ruby_lsp/requests.rb +14 -1
- data/lib/ruby_lsp/server.rb +192 -0
- data/lib/ruby_lsp/store.rb +12 -5
- metadata +10 -87
- data/.github/dependabot.yml +0 -11
- data/.github/probots.yml +0 -2
- data/.github/pull_request_template.md +0 -15
- data/.github/workflows/ci.yml +0 -31
- data/.github/workflows/publish_docs.yml +0 -32
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -39
- data/.vscode/extensions.json +0 -5
- data/.vscode/settings.json +0 -5
- data/.vscode/tasks.json +0 -25
- data/CODE_OF_CONDUCT.md +0 -78
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -124
- data/Rakefile +0 -21
- data/bin/rubocop +0 -29
- data/bin/tapioca +0 -29
- data/bin/test +0 -9
- data/dev.yml +0 -20
- data/lib/ruby_lsp/cli.rb +0 -88
- data/lib/ruby_lsp/requests/rubocop_request.rb +0 -60
- data/rakelib/check_docs.rake +0 -57
- data/ruby-lsp.gemspec +0 -26
- data/service.yml +0 -2
- data/sorbet/config +0 -4
- data/sorbet/rbi/.rubocop.yml +0 -8
- data/sorbet/rbi/gems/ansi@1.5.0.rbi +0 -338
- data/sorbet/rbi/gems/ast@2.4.2.rbi +0 -522
- data/sorbet/rbi/gems/builder@3.2.4.rbi +0 -418
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +0 -8
- data/sorbet/rbi/gems/debug@1.5.0.rbi +0 -1273
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +0 -867
- data/sorbet/rbi/gems/io-console@0.5.11.rbi +0 -8
- data/sorbet/rbi/gems/irb@1.4.1.rbi +0 -376
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +0 -7325
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +0 -8
- data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +0 -612
- data/sorbet/rbi/gems/minitest@5.15.0.rbi +0 -994
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +0 -163
- data/sorbet/rbi/gems/parser@3.1.2.0.rbi +0 -3968
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +0 -734
- data/sorbet/rbi/gems/pry@0.14.1.rbi +0 -8
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +0 -227
- data/sorbet/rbi/gems/rake@13.0.6.rbi +0 -1853
- data/sorbet/rbi/gems/rbi@0.0.14.rbi +0 -2337
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +0 -1854
- data/sorbet/rbi/gems/reline@0.3.1.rbi +0 -1274
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +0 -3852
- data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +0 -4180
- data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +0 -1369
- data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +0 -246
- data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +0 -8
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +0 -652
- data/sorbet/rbi/gems/rubocop@1.30.0.rbi +0 -36729
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +0 -732
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +0 -1600
- data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +0 -6777
- data/sorbet/rbi/gems/tapioca@0.8.1.rbi +0 -1972
- data/sorbet/rbi/gems/thor@1.2.1.rbi +0 -2921
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +0 -27
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +0 -2789
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +0 -1779
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +0 -289
- data/sorbet/rbi/gems/yard@0.9.27.rbi +0 -13048
- data/sorbet/rbi/shims/fiddle.rbi +0 -4
- data/sorbet/rbi/shims/hash.rbi +0 -6
- data/sorbet/rbi/shims/rdoc.rbi +0 -4
- data/sorbet/tapioca/config.yml +0 -13
- data/sorbet/tapioca/require.rb +0 -7
@@ -3,10 +3,13 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
+
# ![Folding ranges demo](../../misc/folding_ranges.gif)
|
7
|
+
#
|
6
8
|
# The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
|
7
|
-
# request informs the editor of the ranges where code can be folded.
|
9
|
+
# request informs the editor of the ranges where and how code can be folded.
|
8
10
|
#
|
9
11
|
# # Example
|
12
|
+
#
|
10
13
|
# ```ruby
|
11
14
|
# def say_hello # <-- folding range start
|
12
15
|
# puts "Hello"
|
@@ -32,12 +35,13 @@ module RubyLsp
|
|
32
35
|
SyntaxTree::Unless,
|
33
36
|
SyntaxTree::Until,
|
34
37
|
SyntaxTree::While,
|
38
|
+
SyntaxTree::Else,
|
39
|
+
SyntaxTree::Ensure,
|
40
|
+
SyntaxTree::Begin,
|
35
41
|
].freeze, T::Array[T.class_of(SyntaxTree::Node)])
|
36
42
|
|
37
43
|
NODES_WITH_STATEMENTS = T.let([
|
38
|
-
SyntaxTree::Else,
|
39
44
|
SyntaxTree::Elsif,
|
40
|
-
SyntaxTree::Ensure,
|
41
45
|
SyntaxTree::In,
|
42
46
|
SyntaxTree::Rescue,
|
43
47
|
SyntaxTree::When,
|
@@ -45,9 +49,7 @@ module RubyLsp
|
|
45
49
|
|
46
50
|
StatementNode = T.type_alias do
|
47
51
|
T.any(
|
48
|
-
SyntaxTree::Else,
|
49
52
|
SyntaxTree::Elsif,
|
50
|
-
SyntaxTree::Ensure,
|
51
53
|
SyntaxTree::In,
|
52
54
|
SyntaxTree::Rescue,
|
53
55
|
SyntaxTree::When,
|
@@ -77,11 +79,10 @@ module RubyLsp
|
|
77
79
|
|
78
80
|
case node
|
79
81
|
when *SIMPLE_FOLDABLES
|
80
|
-
|
82
|
+
location = T.must(node).location
|
83
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
81
84
|
when *NODES_WITH_STATEMENTS
|
82
85
|
add_statements_range(T.must(node), T.cast(node, StatementNode).statements)
|
83
|
-
when SyntaxTree::Begin
|
84
|
-
add_statements_range(node, node.bodystmt.statements)
|
85
86
|
when SyntaxTree::Call, SyntaxTree::CommandCall
|
86
87
|
add_call_range(node)
|
87
88
|
return
|
@@ -135,6 +136,11 @@ module RubyLsp
|
|
135
136
|
kind: @kind
|
136
137
|
)
|
137
138
|
end
|
139
|
+
|
140
|
+
sig { returns(T::Boolean) }
|
141
|
+
def multiline?
|
142
|
+
@end_line > @start_line
|
143
|
+
end
|
138
144
|
end
|
139
145
|
|
140
146
|
sig { params(node: T.nilable(SyntaxTree::Node)).returns(T::Boolean) }
|
@@ -175,7 +181,7 @@ module RubyLsp
|
|
175
181
|
def emit_partial_range
|
176
182
|
return if @partial_range.nil?
|
177
183
|
|
178
|
-
@ranges << @partial_range.to_range
|
184
|
+
@ranges << @partial_range.to_range if @partial_range.multiline?
|
179
185
|
@partial_range = nil
|
180
186
|
end
|
181
187
|
|
@@ -189,13 +195,17 @@ module RubyLsp
|
|
189
195
|
receiver = receiver.receiver
|
190
196
|
when SyntaxTree::MethodAddBlock
|
191
197
|
visit(receiver.block)
|
192
|
-
receiver = receiver.call
|
198
|
+
receiver = receiver.call
|
199
|
+
|
200
|
+
if receiver.is_a?(SyntaxTree::Call) || receiver.is_a?(SyntaxTree::CommandCall)
|
201
|
+
receiver = receiver.receiver
|
202
|
+
end
|
193
203
|
else
|
194
204
|
break
|
195
205
|
end
|
196
206
|
end
|
197
207
|
|
198
|
-
add_lines_range(receiver.location.start_line, node.location.end_line)
|
208
|
+
add_lines_range(receiver.location.start_line, node.location.end_line - 1)
|
199
209
|
|
200
210
|
visit(node.arguments)
|
201
211
|
end
|
@@ -205,9 +215,10 @@ module RubyLsp
|
|
205
215
|
params_location = node.params.location
|
206
216
|
|
207
217
|
if params_location.start_line < params_location.end_line
|
208
|
-
add_lines_range(params_location.end_line, node.location.end_line)
|
218
|
+
add_lines_range(params_location.end_line, node.location.end_line - 1)
|
209
219
|
else
|
210
|
-
|
220
|
+
location = node.location
|
221
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
211
222
|
end
|
212
223
|
|
213
224
|
visit(node.bodystmt.statements)
|
@@ -215,7 +226,7 @@ module RubyLsp
|
|
215
226
|
|
216
227
|
sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
|
217
228
|
def add_statements_range(node, statements)
|
218
|
-
add_lines_range(node.location.start_line, statements.location.end_line) unless statements.empty?
|
229
|
+
add_lines_range(node.location.start_line, statements.body.last.location.end_line) unless statements.empty?
|
219
230
|
end
|
220
231
|
|
221
232
|
sig { params(node: SyntaxTree::StringConcat).void }
|
@@ -223,13 +234,7 @@ module RubyLsp
|
|
223
234
|
left = T.let(node.left, SyntaxTree::Node)
|
224
235
|
left = left.left while left.is_a?(SyntaxTree::StringConcat)
|
225
236
|
|
226
|
-
add_lines_range(left.location.start_line, node.right.location.end_line)
|
227
|
-
end
|
228
|
-
|
229
|
-
sig { params(node: SyntaxTree::Node).void }
|
230
|
-
def add_node_range(node)
|
231
|
-
location = node.location
|
232
|
-
add_lines_range(location.start_line, location.end_line)
|
237
|
+
add_lines_range(left.location.start_line, node.right.location.end_line - 1)
|
233
238
|
end
|
234
239
|
|
235
240
|
sig { params(start_line: Integer, end_line: Integer).void }
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/requests/support/rubocop_formatting_runner"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
8
|
+
# ![Formatting symbol demo](../../misc/formatting.gif)
|
9
|
+
#
|
6
10
|
# The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
|
7
11
|
# request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
|
8
12
|
# registering the ruby-lsp as the Ruby formatter.
|
@@ -14,43 +18,44 @@ module RubyLsp
|
|
14
18
|
# puts "Hello" # --> formatting: fixes the indentation on save
|
15
19
|
# end
|
16
20
|
# ```
|
17
|
-
class Formatting <
|
21
|
+
class Formatting < BaseRequest
|
18
22
|
extend T::Sig
|
19
23
|
|
20
|
-
RUBOCOP_FLAGS = T.let((COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze, T::Array[String])
|
21
|
-
|
22
24
|
sig { params(uri: String, document: Document).void }
|
23
25
|
def initialize(uri, document)
|
24
|
-
super
|
25
|
-
|
26
|
+
super(document)
|
27
|
+
|
28
|
+
@uri = uri
|
26
29
|
end
|
27
30
|
|
28
31
|
sig { override.returns(T.nilable(T.all(T::Array[LanguageServer::Protocol::Interface::TextEdit], Object))) }
|
29
32
|
def run
|
30
|
-
|
33
|
+
formatted_text = formatted_file
|
34
|
+
return unless formatted_text
|
31
35
|
|
32
|
-
|
33
|
-
return
|
36
|
+
size = @document.source.size
|
37
|
+
return if formatted_text.size == size && formatted_text == @document.source
|
34
38
|
|
35
39
|
[
|
36
40
|
LanguageServer::Protocol::Interface::TextEdit.new(
|
37
41
|
range: LanguageServer::Protocol::Interface::Range.new(
|
38
42
|
start: LanguageServer::Protocol::Interface::Position.new(line: 0, character: 0),
|
39
|
-
end: LanguageServer::Protocol::Interface::Position.new(
|
40
|
-
line: text.size,
|
41
|
-
character: text.size
|
42
|
-
)
|
43
|
+
end: LanguageServer::Protocol::Interface::Position.new(line: size, character: size)
|
43
44
|
),
|
44
|
-
new_text:
|
45
|
+
new_text: formatted_text
|
45
46
|
),
|
46
47
|
]
|
47
48
|
end
|
48
49
|
|
49
50
|
private
|
50
51
|
|
51
|
-
sig { returns(T
|
52
|
-
def
|
53
|
-
|
52
|
+
sig { returns(T.nilable(String)) }
|
53
|
+
def formatted_file
|
54
|
+
if defined?(Support::RuboCopFormattingRunner)
|
55
|
+
Support::RuboCopFormattingRunner.instance.run(@uri, @document)
|
56
|
+
else
|
57
|
+
SyntaxTree.format(@document.source)
|
58
|
+
end
|
54
59
|
end
|
55
60
|
end
|
56
61
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
+
# ![Selection ranges demo](../../misc/selection_ranges.gif)
|
7
|
+
#
|
6
8
|
# The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
|
7
9
|
# request informs the editor of ranges that the user may want to select based on the location(s)
|
8
10
|
# of their cursor(s).
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
+
# ![Semantic highlighting demo](../../misc/semantic_highlighting.gif)
|
7
|
+
#
|
6
8
|
# The [semantic
|
7
9
|
# highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
|
8
10
|
# request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
|
@@ -19,11 +21,31 @@ module RubyLsp
|
|
19
21
|
class SemanticHighlighting < BaseRequest
|
20
22
|
extend T::Sig
|
21
23
|
|
22
|
-
TOKEN_TYPES = T.let(
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
|
24
|
+
TOKEN_TYPES = T.let({
|
25
|
+
namespace: 0,
|
26
|
+
type: 1,
|
27
|
+
class: 2,
|
28
|
+
enum: 3,
|
29
|
+
interface: 4,
|
30
|
+
struct: 5,
|
31
|
+
typeParameter: 6,
|
32
|
+
parameter: 7,
|
33
|
+
variable: 8,
|
34
|
+
property: 9,
|
35
|
+
enumMember: 10,
|
36
|
+
event: 11,
|
37
|
+
function: 12,
|
38
|
+
method: 13,
|
39
|
+
macro: 14,
|
40
|
+
keyword: 15,
|
41
|
+
modifier: 16,
|
42
|
+
comment: 17,
|
43
|
+
string: 18,
|
44
|
+
number: 19,
|
45
|
+
regexp: 20,
|
46
|
+
operator: 21,
|
47
|
+
decorator: 22,
|
48
|
+
}.freeze, T::Hash[Symbol, Integer])
|
27
49
|
|
28
50
|
TOKEN_MODIFIERS = T.let({
|
29
51
|
declaration: 0,
|
@@ -38,6 +60,14 @@ module RubyLsp
|
|
38
60
|
default_library: 9,
|
39
61
|
}.freeze, T::Hash[Symbol, Integer])
|
40
62
|
|
63
|
+
SPECIAL_RUBY_METHODS = T.let([
|
64
|
+
Module.instance_methods(false),
|
65
|
+
Kernel.instance_methods(false),
|
66
|
+
Kernel.methods(false),
|
67
|
+
Bundler::Dsl.instance_methods(false),
|
68
|
+
Module.private_instance_methods(false),
|
69
|
+
].flatten.map(&:to_s), T::Array[String])
|
70
|
+
|
41
71
|
class SemanticToken < T::Struct
|
42
72
|
const :location, SyntaxTree::Location
|
43
73
|
const :length, Integer
|
@@ -51,7 +81,8 @@ module RubyLsp
|
|
51
81
|
|
52
82
|
@encoder = encoder
|
53
83
|
@tokens = T.let([], T::Array[SemanticToken])
|
54
|
-
@tree = T.let(document.tree, SyntaxTree::Node)
|
84
|
+
@tree = T.let(T.must(document.tree), SyntaxTree::Node)
|
85
|
+
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
55
86
|
end
|
56
87
|
|
57
88
|
sig do
|
@@ -77,13 +108,16 @@ module RubyLsp
|
|
77
108
|
sig { params(node: SyntaxTree::Call).void }
|
78
109
|
def visit_call(node)
|
79
110
|
visit(node.receiver)
|
80
|
-
|
111
|
+
|
112
|
+
message = node.message
|
113
|
+
add_token(message.location, :method) if message != :call
|
114
|
+
|
81
115
|
visit(node.arguments)
|
82
116
|
end
|
83
117
|
|
84
118
|
sig { params(node: SyntaxTree::Command).void }
|
85
119
|
def visit_command(node)
|
86
|
-
add_token(node.message.location, :method)
|
120
|
+
add_token(node.message.location, :method) unless special_method?(node.message.value)
|
87
121
|
visit(node.arguments)
|
88
122
|
end
|
89
123
|
|
@@ -125,7 +159,7 @@ module RubyLsp
|
|
125
159
|
|
126
160
|
sig { params(node: SyntaxTree::FCall).void }
|
127
161
|
def visit_fcall(node)
|
128
|
-
add_token(node.value.location, :method)
|
162
|
+
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
129
163
|
visit(node.arguments)
|
130
164
|
end
|
131
165
|
|
@@ -144,6 +178,20 @@ module RubyLsp
|
|
144
178
|
end
|
145
179
|
end
|
146
180
|
|
181
|
+
sig { params(node: SyntaxTree::Params).void }
|
182
|
+
def visit_params(node)
|
183
|
+
node.keywords.each do |keyword,|
|
184
|
+
location = keyword.location
|
185
|
+
add_token(location_without_colon(location), :variable)
|
186
|
+
end
|
187
|
+
|
188
|
+
rest = node.keyword_rest
|
189
|
+
return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
|
190
|
+
|
191
|
+
name = rest.name
|
192
|
+
add_token(name.location, :variable) if name
|
193
|
+
end
|
194
|
+
|
147
195
|
sig { params(node: SyntaxTree::VarField).void }
|
148
196
|
def visit_var_field(node)
|
149
197
|
case node.value
|
@@ -166,7 +214,7 @@ module RubyLsp
|
|
166
214
|
|
167
215
|
sig { params(node: SyntaxTree::VCall).void }
|
168
216
|
def visit_vcall(node)
|
169
|
-
add_token(node.value.location, :method)
|
217
|
+
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
170
218
|
end
|
171
219
|
|
172
220
|
sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
|
@@ -177,11 +225,37 @@ module RubyLsp
|
|
177
225
|
SemanticToken.new(
|
178
226
|
location: location,
|
179
227
|
length: length,
|
180
|
-
type: T.must(TOKEN_TYPES
|
228
|
+
type: T.must(TOKEN_TYPES[type]),
|
181
229
|
modifier: modifiers_indices
|
182
230
|
)
|
183
231
|
)
|
184
232
|
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
# Exclude the ":" symbol at the end of a location
|
237
|
+
# We use it on keyword parameters to be consistent
|
238
|
+
# with the rest of the parameters
|
239
|
+
sig { params(location: T.untyped).returns(SyntaxTree::Location) }
|
240
|
+
def location_without_colon(location)
|
241
|
+
SyntaxTree::Location.new(
|
242
|
+
start_line: location.start_line,
|
243
|
+
start_column: location.start_column,
|
244
|
+
start_char: location.start_char,
|
245
|
+
end_char: location.end_char - 1,
|
246
|
+
end_column: location.end_column - 1,
|
247
|
+
end_line: location.end_line,
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Textmate provides highlighting for a subset
|
252
|
+
# of these special Ruby-specific methods.
|
253
|
+
# We want to utilize that highlighting, so we
|
254
|
+
# avoid making a semantic token for it.
|
255
|
+
sig { params(method_name: String).returns(T::Boolean) }
|
256
|
+
def special_method?(method_name)
|
257
|
+
SPECIAL_RUBY_METHODS.include?(method_name)
|
258
|
+
end
|
185
259
|
end
|
186
260
|
end
|
187
261
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
module Support
|
7
|
+
class HighlightTarget
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
READ = LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
|
11
|
+
WRITE = LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
|
12
|
+
|
13
|
+
class HighlightMatch < T::Struct
|
14
|
+
const :type, Integer
|
15
|
+
const :node, SyntaxTree::Node
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { params(node: SyntaxTree::Node).void }
|
19
|
+
def initialize(node)
|
20
|
+
@node = node
|
21
|
+
@value = T.let(value(node), T.nilable(String))
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
|
25
|
+
def highlight_type(other)
|
26
|
+
matched_highlight(other) if other.is_a?(SyntaxTree::Params) || (@value && @value == value(other))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Match the target type (where the cursor is positioned) with the `other` type (the node we're currently
|
32
|
+
# visiting)
|
33
|
+
sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
|
34
|
+
def matched_highlight(other)
|
35
|
+
case @node
|
36
|
+
# Method definitions and invocations
|
37
|
+
when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command,
|
38
|
+
SyntaxTree::CommandCall, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless
|
39
|
+
case other
|
40
|
+
when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
|
41
|
+
HighlightMatch.new(type: READ, node: other)
|
42
|
+
when SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::Defs
|
43
|
+
HighlightMatch.new(type: WRITE, node: other.name)
|
44
|
+
end
|
45
|
+
# Variables, parameters and constants
|
46
|
+
when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::VarField,
|
47
|
+
SyntaxTree::VarRef, SyntaxTree::Ident
|
48
|
+
case other
|
49
|
+
when SyntaxTree::VarField
|
50
|
+
HighlightMatch.new(type: WRITE, node: other)
|
51
|
+
when SyntaxTree::VarRef
|
52
|
+
HighlightMatch.new(type: READ, node: other)
|
53
|
+
when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
|
54
|
+
HighlightMatch.new(type: WRITE, node: other.constant)
|
55
|
+
when SyntaxTree::ConstPathRef
|
56
|
+
HighlightMatch.new(type: READ, node: other.constant)
|
57
|
+
when SyntaxTree::Params
|
58
|
+
params = other.child_nodes.compact
|
59
|
+
match = params.find { |param| value(param) == @value }
|
60
|
+
HighlightMatch.new(type: WRITE, node: match) if match
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { params(node: SyntaxTree::Node).returns(T.nilable(String)) }
|
66
|
+
def value(node)
|
67
|
+
case node
|
68
|
+
when SyntaxTree::ConstPathRef, SyntaxTree::ConstPathField, SyntaxTree::TopConstField
|
69
|
+
node.constant.value
|
70
|
+
when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::Ident,
|
71
|
+
SyntaxTree::ArgsForward
|
72
|
+
node.value
|
73
|
+
when SyntaxTree::Field, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless, SyntaxTree::RestParam,
|
74
|
+
SyntaxTree::KwRestParam, SyntaxTree::BlockArg
|
75
|
+
node.name&.value
|
76
|
+
when SyntaxTree::VarField, SyntaxTree::VarRef, SyntaxTree::VCall, SyntaxTree::FCall
|
77
|
+
node.value&.value
|
78
|
+
when SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
|
79
|
+
message = node.message
|
80
|
+
message != :call ? message.value : nil
|
81
|
+
when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
|
82
|
+
node.constant.constant.value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -61,11 +61,18 @@ module RubyLsp
|
|
61
61
|
|
62
62
|
sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
|
63
63
|
def to_lsp_diagnostic
|
64
|
+
if @offense.correctable?
|
65
|
+
severity = RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
|
66
|
+
message = @offense.message
|
67
|
+
else
|
68
|
+
severity = LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING
|
69
|
+
message = "#{@offense.message}\n\nThis offense is not auto-correctable.\n"
|
70
|
+
end
|
64
71
|
LanguageServer::Protocol::Interface::Diagnostic.new(
|
65
|
-
message:
|
72
|
+
message: message,
|
66
73
|
source: "RuboCop",
|
67
74
|
code: @offense.cop_name,
|
68
|
-
severity:
|
75
|
+
severity: severity,
|
69
76
|
range: LanguageServer::Protocol::Interface::Range.new(
|
70
77
|
start: LanguageServer::Protocol::Interface::Position.new(
|
71
78
|
line: @offense.line - 1,
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rubocop"
|
6
|
+
rescue LoadError
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
require "cgi"
|
11
|
+
require "singleton"
|
12
|
+
|
13
|
+
module RubyLsp
|
14
|
+
module Requests
|
15
|
+
module Support
|
16
|
+
# :nodoc:
|
17
|
+
class RuboCopDiagnosticsRunner < RuboCop::Runner
|
18
|
+
extend T::Sig
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
sig { void }
|
22
|
+
def initialize
|
23
|
+
@options = T.let({}, T::Hash[Symbol, T.untyped])
|
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
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { params(uri: String, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) }
|
39
|
+
def run(uri, document)
|
40
|
+
@diagnostics.clear
|
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
|
+
|
47
|
+
# Invoke RuboCop with just this file in `paths`
|
48
|
+
super([file])
|
49
|
+
@diagnostics
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
|
55
|
+
def file_finished(_file, offenses)
|
56
|
+
@diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, T.must(@uri)) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rubocop"
|
6
|
+
rescue LoadError
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
require "cgi"
|
11
|
+
require "singleton"
|
12
|
+
|
13
|
+
module RubyLsp
|
14
|
+
module Requests
|
15
|
+
module Support
|
16
|
+
# :nodoc:
|
17
|
+
class RuboCopFormattingRunner < RuboCop::Runner
|
18
|
+
extend T::Sig
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
sig { void }
|
22
|
+
def initialize
|
23
|
+
@options = T.let({}, T::Hash[Symbol, T.untyped])
|
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
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { params(uri: String, document: Document).returns(T.nilable(String)) }
|
38
|
+
def run(uri, document)
|
39
|
+
file = CGI.unescape(URI.parse(uri).path)
|
40
|
+
# We communicate with Rubocop via stdin
|
41
|
+
@options[:stdin] = document.source
|
42
|
+
|
43
|
+
# Invoke RuboCop with just this file in `paths`
|
44
|
+
super([file])
|
45
|
+
@options[:stdin]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -2,23 +2,36 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
|
+
# Supported features
|
6
|
+
#
|
7
|
+
# - {RubyLsp::Requests::DocumentSymbol}
|
8
|
+
# - {RubyLsp::Requests::DocumentLink}
|
9
|
+
# - {RubyLsp::Requests::FoldingRanges}
|
10
|
+
# - {RubyLsp::Requests::SelectionRanges}
|
11
|
+
# - {RubyLsp::Requests::SemanticHighlighting}
|
12
|
+
# - {RubyLsp::Requests::Formatting}
|
13
|
+
# - {RubyLsp::Requests::Diagnostics}
|
14
|
+
# - {RubyLsp::Requests::CodeActions}
|
15
|
+
# - {RubyLsp::Requests::DocumentHighlight}
|
5
16
|
module Requests
|
6
17
|
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
7
18
|
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
|
19
|
+
autoload :DocumentLink, "ruby_lsp/requests/document_link"
|
8
20
|
autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
|
9
21
|
autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
|
10
22
|
autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
|
11
|
-
autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
|
12
23
|
autoload :Formatting, "ruby_lsp/requests/formatting"
|
13
24
|
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
14
25
|
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
15
26
|
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
16
27
|
|
28
|
+
# :nodoc:
|
17
29
|
module Support
|
18
30
|
autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
|
19
31
|
autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
|
20
32
|
autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
|
21
33
|
autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
|
34
|
+
autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
|
22
35
|
end
|
23
36
|
end
|
24
37
|
end
|