ruby-lsp 0.0.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|
+
# 
|
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
|
+
# 
|
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",
|