ruby-lsp 0.0.4 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -1
- data/CHANGELOG.md +35 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +26 -24
- data/README.md +3 -2
- data/Rakefile +8 -1
- data/VERSION +1 -1
- data/bin/console +19 -0
- data/exe/ruby-lsp +1 -3
- data/lib/ruby_lsp/document.rb +17 -4
- data/lib/ruby_lsp/handler.rb +54 -141
- data/lib/{internal.rb → ruby_lsp/internal.rb} +4 -2
- data/lib/ruby_lsp/requests/base_request.rb +9 -7
- data/lib/ruby_lsp/requests/code_actions.rb +20 -9
- data/lib/ruby_lsp/requests/diagnostics.rb +25 -8
- data/lib/ruby_lsp/requests/document_highlight.rb +32 -32
- data/lib/ruby_lsp/requests/document_symbol.rb +59 -10
- data/lib/ruby_lsp/requests/folding_ranges.rb +73 -34
- data/lib/ruby_lsp/requests/formatting.rb +25 -15
- data/lib/ruby_lsp/requests/selection_ranges.rb +18 -5
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +179 -36
- data/lib/ruby_lsp/requests/support/highlight_target.rb +87 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +16 -4
- 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/support/selection_range.rb +4 -1
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +13 -3
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +6 -1
- data/lib/ruby_lsp/requests.rb +13 -2
- data/lib/ruby_lsp/server.rb +160 -0
- data/lib/ruby_lsp/store.rb +17 -9
- data/rakelib/check_docs.rake +30 -5
- data/ruby-lsp.gemspec +6 -5
- data/sorbet/tapioca/require.rb +1 -1
- metadata +14 -26
- data/lib/ruby_lsp/cli.rb +0 -88
- data/lib/ruby_lsp/requests/rubocop_request.rb +0 -50
- data/shipit.production.yml +0 -1
@@ -1,8 +1,10 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
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).
|
@@ -17,7 +19,9 @@ module RubyLsp
|
|
17
19
|
# end
|
18
20
|
# ```
|
19
21
|
class SelectionRanges < BaseRequest
|
20
|
-
|
22
|
+
extend T::Sig
|
23
|
+
|
24
|
+
NODES_THAT_CAN_BE_PARENTS = T.let([
|
21
25
|
SyntaxTree::Assign,
|
22
26
|
SyntaxTree::ArrayLiteral,
|
23
27
|
SyntaxTree::Begin,
|
@@ -54,15 +58,17 @@ module RubyLsp
|
|
54
58
|
SyntaxTree::VCall,
|
55
59
|
SyntaxTree::When,
|
56
60
|
SyntaxTree::While,
|
57
|
-
].freeze
|
61
|
+
].freeze, T::Array[T.class_of(SyntaxTree::Node)])
|
58
62
|
|
63
|
+
sig { params(document: Document).void }
|
59
64
|
def initialize(document)
|
60
65
|
super(document)
|
61
66
|
|
62
|
-
@ranges = []
|
63
|
-
@stack = []
|
67
|
+
@ranges = T.let([], T::Array[Support::SelectionRange])
|
68
|
+
@stack = T.let([], T::Array[Support::SelectionRange])
|
64
69
|
end
|
65
70
|
|
71
|
+
sig { override.returns(T.all(T::Array[Support::SelectionRange], Object)) }
|
66
72
|
def run
|
67
73
|
visit(@document.tree)
|
68
74
|
@ranges.reverse!
|
@@ -70,6 +76,7 @@ module RubyLsp
|
|
70
76
|
|
71
77
|
private
|
72
78
|
|
79
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
73
80
|
def visit(node)
|
74
81
|
return if node.nil?
|
75
82
|
|
@@ -83,6 +90,12 @@ module RubyLsp
|
|
83
90
|
@stack.pop if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
|
84
91
|
end
|
85
92
|
|
93
|
+
sig do
|
94
|
+
params(
|
95
|
+
location: SyntaxTree::Location,
|
96
|
+
parent: T.nilable(Support::SelectionRange)
|
97
|
+
).returns(Support::SelectionRange)
|
98
|
+
end
|
86
99
|
def create_selection_range(location, parent = nil)
|
87
100
|
RubyLsp::Requests::Support::SelectionRange.new(
|
88
101
|
range: LanguageServer::Protocol::Interface::Range.new(
|
@@ -1,8 +1,10 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
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.
|
@@ -17,12 +19,35 @@ module RubyLsp
|
|
17
19
|
# end
|
18
20
|
# ```
|
19
21
|
class SemanticHighlighting < BaseRequest
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
extend T::Sig
|
23
|
+
|
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])
|
49
|
+
|
50
|
+
TOKEN_MODIFIERS = T.let({
|
26
51
|
declaration: 0,
|
27
52
|
definition: 1,
|
28
53
|
readonly: 2,
|
@@ -33,18 +58,38 @@ module RubyLsp
|
|
33
58
|
modification: 7,
|
34
59
|
documentation: 8,
|
35
60
|
default_library: 9,
|
36
|
-
}.freeze
|
37
|
-
|
38
|
-
|
61
|
+
}.freeze, T::Hash[Symbol, Integer])
|
62
|
+
|
63
|
+
SPECIAL_RUBY_METHODS = T.let((Module.instance_methods(false) +
|
64
|
+
Kernel.methods(false) + Bundler::Dsl.instance_methods(false) +
|
65
|
+
Module.private_instance_methods(false))
|
66
|
+
.map(&:to_s), T::Array[String])
|
67
|
+
|
68
|
+
class SemanticToken < T::Struct
|
69
|
+
const :location, SyntaxTree::Location
|
70
|
+
const :length, Integer
|
71
|
+
const :type, Integer
|
72
|
+
const :modifier, T::Array[Integer]
|
73
|
+
end
|
39
74
|
|
75
|
+
sig { params(document: Document, encoder: T.nilable(Support::SemanticTokenEncoder)).void }
|
40
76
|
def initialize(document, encoder: nil)
|
41
77
|
super(document)
|
42
78
|
|
43
79
|
@encoder = encoder
|
44
|
-
@tokens = []
|
45
|
-
@tree = document.tree
|
80
|
+
@tokens = T.let([], T::Array[SemanticToken])
|
81
|
+
@tree = T.let(T.must(document.tree), SyntaxTree::Node)
|
82
|
+
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
46
83
|
end
|
47
84
|
|
85
|
+
sig do
|
86
|
+
override.returns(
|
87
|
+
T.any(
|
88
|
+
LanguageServer::Protocol::Interface::SemanticTokens,
|
89
|
+
T.all(T::Array[SemanticToken], Object),
|
90
|
+
)
|
91
|
+
)
|
92
|
+
end
|
48
93
|
def run
|
49
94
|
visit(@tree)
|
50
95
|
return @tokens unless @encoder
|
@@ -52,60 +97,158 @@ module RubyLsp
|
|
52
97
|
@encoder.encode(@tokens)
|
53
98
|
end
|
54
99
|
|
55
|
-
|
56
|
-
node.target.parts.each do |var_ref|
|
57
|
-
add_token(var_ref.value.location, :variable)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def visit_var_field(node)
|
62
|
-
case node.value
|
63
|
-
when SyntaxTree::Ident
|
64
|
-
add_token(node.value.location, :variable)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def visit_var_ref(node)
|
69
|
-
case node.value
|
70
|
-
when SyntaxTree::Ident
|
71
|
-
add_token(node.value.location, :variable)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
100
|
+
sig { params(node: SyntaxTree::ARefField).void }
|
75
101
|
def visit_a_ref_field(node)
|
76
102
|
add_token(node.collection.value.location, :variable)
|
77
103
|
end
|
78
104
|
|
105
|
+
sig { params(node: SyntaxTree::Call).void }
|
79
106
|
def visit_call(node)
|
80
107
|
visit(node.receiver)
|
81
108
|
add_token(node.message.location, :method)
|
82
109
|
visit(node.arguments)
|
83
110
|
end
|
84
111
|
|
112
|
+
sig { params(node: SyntaxTree::Command).void }
|
85
113
|
def visit_command(node)
|
86
|
-
add_token(node.message.location, :method)
|
114
|
+
add_token(node.message.location, :method) unless special_method?(node.message.value)
|
87
115
|
visit(node.arguments)
|
88
116
|
end
|
89
117
|
|
118
|
+
sig { params(node: SyntaxTree::CommandCall).void }
|
90
119
|
def visit_command_call(node)
|
91
120
|
visit(node.receiver)
|
92
121
|
add_token(node.message.location, :method)
|
93
122
|
visit(node.arguments)
|
94
123
|
end
|
95
124
|
|
125
|
+
sig { params(node: SyntaxTree::Const).void }
|
126
|
+
def visit_const(node)
|
127
|
+
add_token(node.location, :namespace)
|
128
|
+
end
|
129
|
+
|
130
|
+
sig { params(node: SyntaxTree::Def).void }
|
131
|
+
def visit_def(node)
|
132
|
+
add_token(node.name.location, :method, [:declaration])
|
133
|
+
visit(node.params)
|
134
|
+
visit(node.bodystmt)
|
135
|
+
end
|
136
|
+
|
137
|
+
sig { params(node: SyntaxTree::DefEndless).void }
|
138
|
+
def visit_def_endless(node)
|
139
|
+
add_token(node.name.location, :method, [:declaration])
|
140
|
+
visit(node.paren)
|
141
|
+
visit(node.operator)
|
142
|
+
visit(node.statement)
|
143
|
+
end
|
144
|
+
|
145
|
+
sig { params(node: SyntaxTree::Defs).void }
|
146
|
+
def visit_defs(node)
|
147
|
+
visit(node.target)
|
148
|
+
visit(node.operator)
|
149
|
+
add_token(node.name.location, :method, [:declaration])
|
150
|
+
visit(node.params)
|
151
|
+
visit(node.bodystmt)
|
152
|
+
end
|
153
|
+
|
154
|
+
sig { params(node: SyntaxTree::FCall).void }
|
96
155
|
def visit_fcall(node)
|
97
|
-
add_token(node.value.location, :method)
|
156
|
+
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
98
157
|
visit(node.arguments)
|
99
158
|
end
|
100
159
|
|
160
|
+
sig { params(node: SyntaxTree::Kw).void }
|
161
|
+
def visit_kw(node)
|
162
|
+
case node.value
|
163
|
+
when "self"
|
164
|
+
add_token(node.location, :variable, [:default_library])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
sig { params(node: SyntaxTree::MAssign).void }
|
169
|
+
def visit_m_assign(node)
|
170
|
+
node.target.parts.each do |var_ref|
|
171
|
+
add_token(var_ref.value.location, :variable)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
sig { params(node: SyntaxTree::Params).void }
|
176
|
+
def visit_params(node)
|
177
|
+
node.keywords.each do |keyword,|
|
178
|
+
location = keyword.location
|
179
|
+
add_token(location_without_colon(location), :variable)
|
180
|
+
end
|
181
|
+
|
182
|
+
rest = node.keyword_rest
|
183
|
+
return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
|
184
|
+
|
185
|
+
name = rest.name
|
186
|
+
add_token(name.location, :variable) if name
|
187
|
+
end
|
188
|
+
|
189
|
+
sig { params(node: SyntaxTree::VarField).void }
|
190
|
+
def visit_var_field(node)
|
191
|
+
case node.value
|
192
|
+
when SyntaxTree::Ident
|
193
|
+
add_token(node.value.location, :variable)
|
194
|
+
else
|
195
|
+
visit(node.value)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
sig { params(node: SyntaxTree::VarRef).void }
|
200
|
+
def visit_var_ref(node)
|
201
|
+
case node.value
|
202
|
+
when SyntaxTree::Ident
|
203
|
+
add_token(node.value.location, :variable)
|
204
|
+
else
|
205
|
+
visit(node.value)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
sig { params(node: SyntaxTree::VCall).void }
|
101
210
|
def visit_vcall(node)
|
102
|
-
add_token(node.value.location, :method)
|
211
|
+
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
103
212
|
end
|
104
213
|
|
214
|
+
sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
|
105
215
|
def add_token(location, type, modifiers = [])
|
106
216
|
length = location.end_char - location.start_char
|
107
217
|
modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
|
108
|
-
@tokens.push(
|
218
|
+
@tokens.push(
|
219
|
+
SemanticToken.new(
|
220
|
+
location: location,
|
221
|
+
length: length,
|
222
|
+
type: T.must(TOKEN_TYPES[type]),
|
223
|
+
modifier: modifiers_indices
|
224
|
+
)
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
private
|
229
|
+
|
230
|
+
# Exclude the ":" symbol at the end of a location
|
231
|
+
# We use it on keyword parameters to be consistent
|
232
|
+
# with the rest of the parameters
|
233
|
+
sig { params(location: T.untyped).returns(SyntaxTree::Location) }
|
234
|
+
def location_without_colon(location)
|
235
|
+
SyntaxTree::Location.new(
|
236
|
+
start_line: location.start_line,
|
237
|
+
start_column: location.start_column,
|
238
|
+
start_char: location.start_char,
|
239
|
+
end_char: location.end_char - 1,
|
240
|
+
end_column: location.end_column - 1,
|
241
|
+
end_line: location.end_line,
|
242
|
+
)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Textmate provides highlighting for a subset
|
246
|
+
# of these special Ruby-specific methods.
|
247
|
+
# We want to utilize that highlighting, so we
|
248
|
+
# avoid making a semantic token for it.
|
249
|
+
sig { params(method_name: String).returns(T::Boolean) }
|
250
|
+
def special_method?(method_name)
|
251
|
+
SPECIAL_RUBY_METHODS.include?(method_name)
|
109
252
|
end
|
110
253
|
end
|
111
254
|
end
|
@@ -0,0 +1,87 @@
|
|
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
|
+
node.message.value
|
80
|
+
when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
|
81
|
+
node.constant.constant.value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -1,35 +1,45 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
6
|
module Support
|
7
7
|
class RuboCopDiagnostic
|
8
|
-
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
RUBOCOP_TO_LSP_SEVERITY = T.let({
|
9
11
|
convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
10
12
|
info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
11
13
|
refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
12
14
|
warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
|
13
15
|
error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
14
16
|
fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
15
|
-
}.freeze
|
17
|
+
}.freeze, T::Hash[Symbol, Integer])
|
16
18
|
|
19
|
+
sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
|
17
20
|
attr_reader :replacements
|
18
21
|
|
22
|
+
sig { params(offense: RuboCop::Cop::Offense, uri: String).void }
|
19
23
|
def initialize(offense, uri)
|
20
24
|
@offense = offense
|
21
25
|
@uri = uri
|
22
|
-
@replacements =
|
26
|
+
@replacements = T.let(
|
27
|
+
offense.correctable? ? offense_replacements : [],
|
28
|
+
T::Array[LanguageServer::Protocol::Interface::TextEdit]
|
29
|
+
)
|
23
30
|
end
|
24
31
|
|
32
|
+
sig { returns(T::Boolean) }
|
25
33
|
def correctable?
|
26
34
|
@offense.correctable?
|
27
35
|
end
|
28
36
|
|
37
|
+
sig { params(range: T::Range[Integer]).returns(T::Boolean) }
|
29
38
|
def in_range?(range)
|
30
39
|
range.cover?(@offense.line - 1)
|
31
40
|
end
|
32
41
|
|
42
|
+
sig { returns(LanguageServer::Protocol::Interface::CodeAction) }
|
33
43
|
def to_lsp_code_action
|
34
44
|
LanguageServer::Protocol::Interface::CodeAction.new(
|
35
45
|
title: "Autocorrect #{@offense.cop_name}",
|
@@ -49,6 +59,7 @@ module RubyLsp
|
|
49
59
|
)
|
50
60
|
end
|
51
61
|
|
62
|
+
sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
|
52
63
|
def to_lsp_diagnostic
|
53
64
|
LanguageServer::Protocol::Interface::Diagnostic.new(
|
54
65
|
message: @offense.message,
|
@@ -70,6 +81,7 @@ module RubyLsp
|
|
70
81
|
|
71
82
|
private
|
72
83
|
|
84
|
+
sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
|
73
85
|
def offense_replacements
|
74
86
|
@offense.corrector.as_replacements.map do |range, replacement|
|
75
87
|
LanguageServer::Protocol::Interface::TextEdit.new(
|
@@ -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
|
@@ -1,10 +1,13 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
6
|
module Support
|
7
7
|
class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(position: Document::PositionShape).returns(T::Boolean) }
|
8
11
|
def cover?(position)
|
9
12
|
line_range = (range.start.line..range.end.line)
|
10
13
|
character_range = (range.start.character..range.end.character)
|
@@ -1,15 +1,23 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
6
|
module Support
|
7
7
|
class SemanticTokenEncoder
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { void }
|
8
11
|
def initialize
|
9
|
-
@current_row = 0
|
10
|
-
@current_column = 0
|
12
|
+
@current_row = T.let(0, Integer)
|
13
|
+
@current_column = T.let(0, Integer)
|
11
14
|
end
|
12
15
|
|
16
|
+
sig do
|
17
|
+
params(
|
18
|
+
tokens: T::Array[SemanticHighlighting::SemanticToken]
|
19
|
+
).returns(LanguageServer::Protocol::Interface::SemanticTokens)
|
20
|
+
end
|
13
21
|
def encode(tokens)
|
14
22
|
delta = tokens
|
15
23
|
.sort_by do |token|
|
@@ -31,6 +39,7 @@ module RubyLsp
|
|
31
39
|
|
32
40
|
# For more information on how each number is calculated, read:
|
33
41
|
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
|
42
|
+
sig { params(token: SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
|
34
43
|
def compute_delta(token)
|
35
44
|
row = token.location.start_line - 1
|
36
45
|
column = token.location.start_column
|
@@ -49,6 +58,7 @@ module RubyLsp
|
|
49
58
|
# For example, [:default_library] will be encoded as
|
50
59
|
# 0b1000000000, as :default_library is the 10th bit according
|
51
60
|
# to the token modifiers index map.
|
61
|
+
sig { params(modifiers: T::Array[Integer]).returns(Integer) }
|
52
62
|
def encode_modifiers(modifiers)
|
53
63
|
modifiers.inject(0) do |encoded_modifiers, modifier|
|
54
64
|
encoded_modifiers | (1 << modifier)
|
@@ -1,18 +1,23 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
6
|
module Support
|
7
7
|
class SyntaxErrorDiagnostic
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(edit: Document::EditShape).void }
|
8
11
|
def initialize(edit)
|
9
12
|
@edit = edit
|
10
13
|
end
|
11
14
|
|
15
|
+
sig { returns(FalseClass) }
|
12
16
|
def correctable?
|
13
17
|
false
|
14
18
|
end
|
15
19
|
|
20
|
+
sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
|
16
21
|
def to_lsp_diagnostic
|
17
22
|
LanguageServer::Protocol::Interface::Diagnostic.new(
|
18
23
|
message: "Syntax error",
|