ruby-lsp 0.1.0 → 0.2.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 +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
|
+
# 
|
|
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
|
+
# 
|
|
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
|
+
# 
|
|
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
|
+
# 
|
|
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
|