ruby-lsp 0.0.3 → 0.2.0
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/.github/workflows/ci.yml +6 -0
- data/.rubocop.yml +25 -0
- data/CHANGELOG.md +35 -0
- data/Gemfile +10 -6
- data/Gemfile.lock +63 -16
- data/README.md +41 -0
- data/Rakefile +8 -1
- data/VERSION +1 -1
- data/bin/console +19 -0
- data/bin/tapioca +29 -0
- data/dev.yml +3 -0
- data/exe/ruby-lsp +19 -4
- data/lib/ruby-lsp.rb +2 -1
- data/lib/ruby_lsp/cli.rb +13 -5
- data/lib/ruby_lsp/document.rb +43 -14
- data/lib/ruby_lsp/handler.rb +107 -37
- data/lib/ruby_lsp/internal.rb +7 -0
- data/lib/ruby_lsp/requests/base_request.rb +18 -5
- data/lib/ruby_lsp/requests/code_actions.rb +20 -8
- data/lib/ruby_lsp/requests/diagnostics.rb +25 -7
- data/lib/ruby_lsp/requests/document_highlight.rb +113 -0
- data/lib/ruby_lsp/requests/document_symbol.rb +56 -16
- data/lib/ruby_lsp/requests/folding_ranges.rb +70 -34
- data/lib/ruby_lsp/requests/formatting.rb +24 -14
- data/lib/ruby_lsp/requests/selection_ranges.rb +18 -4
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +187 -34
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +16 -3
- 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 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +24 -3
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +6 -0
- data/lib/ruby_lsp/requests.rb +13 -1
- data/lib/ruby_lsp/store.rb +20 -3
- data/rakelib/check_docs.rake +34 -6
- data/ruby-lsp.gemspec +7 -5
- data/sorbet/config +4 -0
- data/sorbet/rbi/.rubocop.yml +8 -0
- data/sorbet/rbi/gems/ansi@1.5.0.rbi +338 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +522 -0
- data/sorbet/rbi/gems/builder@3.2.4.rbi +418 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/debug@1.5.0.rbi +1273 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +867 -0
- data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
- data/sorbet/rbi/gems/irb@1.4.1.rbi +376 -0
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +7325 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +612 -0
- data/sorbet/rbi/gems/minitest@5.15.0.rbi +994 -0
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +163 -0
- data/sorbet/rbi/gems/parser@3.1.2.0.rbi +3968 -0
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +734 -0
- data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +227 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +1853 -0
- data/sorbet/rbi/gems/rbi@0.0.14.rbi +2337 -0
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +1854 -0
- data/sorbet/rbi/gems/reline@0.3.1.rbi +1274 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +3852 -0
- data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +4180 -0
- data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +1369 -0
- data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +246 -0
- data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +652 -0
- data/sorbet/rbi/gems/rubocop@1.30.0.rbi +36729 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +732 -0
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +1600 -0
- data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +6777 -0
- data/sorbet/rbi/gems/tapioca@0.8.1.rbi +1972 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +2921 -0
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +27 -0
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +2789 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +1779 -0
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +289 -0
- data/sorbet/rbi/gems/yard@0.9.27.rbi +13048 -0
- data/sorbet/rbi/shims/fiddle.rbi +4 -0
- data/sorbet/rbi/shims/hash.rbi +6 -0
- data/sorbet/rbi/shims/rdoc.rbi +4 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +7 -0
- metadata +62 -13
- data/lib/ruby_lsp/requests/rubocop_request.rb +0 -49
- data/shipit.production.yml +0 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
4
5
|
module Requests
|
|
6
|
+
# 
|
|
7
|
+
#
|
|
5
8
|
# The [semantic
|
|
6
9
|
# highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
|
|
7
10
|
# request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
|
|
@@ -16,22 +19,77 @@ module RubyLsp
|
|
|
16
19
|
# end
|
|
17
20
|
# ```
|
|
18
21
|
class SemanticHighlighting < BaseRequest
|
|
19
|
-
|
|
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({
|
|
51
|
+
declaration: 0,
|
|
52
|
+
definition: 1,
|
|
53
|
+
readonly: 2,
|
|
54
|
+
static: 3,
|
|
55
|
+
deprecated: 4,
|
|
56
|
+
abstract: 5,
|
|
57
|
+
async: 6,
|
|
58
|
+
modification: 7,
|
|
59
|
+
documentation: 8,
|
|
60
|
+
default_library: 9,
|
|
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
|
|
26
74
|
|
|
75
|
+
sig { params(document: Document, encoder: T.nilable(Support::SemanticTokenEncoder)).void }
|
|
27
76
|
def initialize(document, encoder: nil)
|
|
28
77
|
super(document)
|
|
29
78
|
|
|
30
79
|
@encoder = encoder
|
|
31
|
-
@tokens = []
|
|
32
|
-
@tree = document.tree
|
|
80
|
+
@tokens = T.let([], T::Array[SemanticToken])
|
|
81
|
+
@tree = T.let(document.tree, SyntaxTree::Node)
|
|
82
|
+
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
|
33
83
|
end
|
|
34
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
|
|
35
93
|
def run
|
|
36
94
|
visit(@tree)
|
|
37
95
|
return @tokens unless @encoder
|
|
@@ -39,59 +97,154 @@ module RubyLsp
|
|
|
39
97
|
@encoder.encode(@tokens)
|
|
40
98
|
end
|
|
41
99
|
|
|
42
|
-
|
|
43
|
-
node.target.parts.each do |var_ref|
|
|
44
|
-
add_token(var_ref.value.location, :variable)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def visit_var_field(node)
|
|
49
|
-
case node.value
|
|
50
|
-
when SyntaxTree::Ident
|
|
51
|
-
add_token(node.value.location, :variable)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def visit_var_ref(node)
|
|
56
|
-
case node.value
|
|
57
|
-
when SyntaxTree::Ident
|
|
58
|
-
add_token(node.value.location, :variable)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
100
|
+
sig { params(node: SyntaxTree::ARefField).void }
|
|
62
101
|
def visit_a_ref_field(node)
|
|
63
102
|
add_token(node.collection.value.location, :variable)
|
|
64
103
|
end
|
|
65
104
|
|
|
105
|
+
sig { params(node: SyntaxTree::Call).void }
|
|
66
106
|
def visit_call(node)
|
|
67
107
|
visit(node.receiver)
|
|
68
108
|
add_token(node.message.location, :method)
|
|
69
109
|
visit(node.arguments)
|
|
70
110
|
end
|
|
71
111
|
|
|
112
|
+
sig { params(node: SyntaxTree::Command).void }
|
|
72
113
|
def visit_command(node)
|
|
73
|
-
add_token(node.message.location, :method)
|
|
114
|
+
add_token(node.message.location, :method) unless special_method?(node.message.value)
|
|
74
115
|
visit(node.arguments)
|
|
75
116
|
end
|
|
76
117
|
|
|
118
|
+
sig { params(node: SyntaxTree::CommandCall).void }
|
|
77
119
|
def visit_command_call(node)
|
|
78
120
|
visit(node.receiver)
|
|
79
121
|
add_token(node.message.location, :method)
|
|
80
122
|
visit(node.arguments)
|
|
81
123
|
end
|
|
82
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 }
|
|
83
155
|
def visit_fcall(node)
|
|
84
|
-
add_token(node.value.location, :method)
|
|
156
|
+
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
|
85
157
|
visit(node.arguments)
|
|
86
158
|
end
|
|
87
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
|
+
add_token(node.keyword_rest.name.location, :variable) if node.keyword_rest
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
sig { params(node: SyntaxTree::VarField).void }
|
|
186
|
+
def visit_var_field(node)
|
|
187
|
+
case node.value
|
|
188
|
+
when SyntaxTree::Ident
|
|
189
|
+
add_token(node.value.location, :variable)
|
|
190
|
+
else
|
|
191
|
+
visit(node.value)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
sig { params(node: SyntaxTree::VarRef).void }
|
|
196
|
+
def visit_var_ref(node)
|
|
197
|
+
case node.value
|
|
198
|
+
when SyntaxTree::Ident
|
|
199
|
+
add_token(node.value.location, :variable)
|
|
200
|
+
else
|
|
201
|
+
visit(node.value)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
sig { params(node: SyntaxTree::VCall).void }
|
|
88
206
|
def visit_vcall(node)
|
|
89
|
-
add_token(node.value.location, :method)
|
|
207
|
+
add_token(node.value.location, :method) unless special_method?(node.value.value)
|
|
90
208
|
end
|
|
91
209
|
|
|
92
|
-
|
|
210
|
+
sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
|
|
211
|
+
def add_token(location, type, modifiers = [])
|
|
93
212
|
length = location.end_char - location.start_char
|
|
94
|
-
|
|
213
|
+
modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
|
|
214
|
+
@tokens.push(
|
|
215
|
+
SemanticToken.new(
|
|
216
|
+
location: location,
|
|
217
|
+
length: length,
|
|
218
|
+
type: T.must(TOKEN_TYPES[type]),
|
|
219
|
+
modifier: modifiers_indices
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
private
|
|
225
|
+
|
|
226
|
+
# Exclude the ":" symbol at the end of a location
|
|
227
|
+
# We use it on keyword parameters to be consistent
|
|
228
|
+
# with the rest of the parameters
|
|
229
|
+
sig { params(location: T.untyped).returns(SyntaxTree::Location) }
|
|
230
|
+
def location_without_colon(location)
|
|
231
|
+
SyntaxTree::Location.new(
|
|
232
|
+
start_line: location.start_line,
|
|
233
|
+
start_column: location.start_column,
|
|
234
|
+
start_char: location.start_char,
|
|
235
|
+
end_char: location.end_char - 1,
|
|
236
|
+
end_column: location.end_column - 1,
|
|
237
|
+
end_line: location.end_line,
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Textmate provides highlighting for a subset
|
|
242
|
+
# of these special Ruby-specific methods.
|
|
243
|
+
# We want to utilize that highlighting, so we
|
|
244
|
+
# avoid making a semantic token for it.
|
|
245
|
+
sig { params(method_name: String).returns(T::Boolean) }
|
|
246
|
+
def special_method?(method_name)
|
|
247
|
+
SPECIAL_RUBY_METHODS.include?(method_name)
|
|
95
248
|
end
|
|
96
249
|
end
|
|
97
250
|
end
|
|
@@ -1,34 +1,45 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
4
5
|
module Requests
|
|
5
6
|
module Support
|
|
6
7
|
class RuboCopDiagnostic
|
|
7
|
-
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
RUBOCOP_TO_LSP_SEVERITY = T.let({
|
|
8
11
|
convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
|
9
12
|
info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
|
10
13
|
refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
|
|
11
14
|
warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
|
|
12
15
|
error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
|
13
16
|
fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
|
|
14
|
-
}.freeze
|
|
17
|
+
}.freeze, T::Hash[Symbol, Integer])
|
|
15
18
|
|
|
19
|
+
sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
|
|
16
20
|
attr_reader :replacements
|
|
17
21
|
|
|
22
|
+
sig { params(offense: RuboCop::Cop::Offense, uri: String).void }
|
|
18
23
|
def initialize(offense, uri)
|
|
19
24
|
@offense = offense
|
|
20
25
|
@uri = uri
|
|
21
|
-
@replacements =
|
|
26
|
+
@replacements = T.let(
|
|
27
|
+
offense.correctable? ? offense_replacements : [],
|
|
28
|
+
T::Array[LanguageServer::Protocol::Interface::TextEdit]
|
|
29
|
+
)
|
|
22
30
|
end
|
|
23
31
|
|
|
32
|
+
sig { returns(T::Boolean) }
|
|
24
33
|
def correctable?
|
|
25
34
|
@offense.correctable?
|
|
26
35
|
end
|
|
27
36
|
|
|
37
|
+
sig { params(range: T::Range[Integer]).returns(T::Boolean) }
|
|
28
38
|
def in_range?(range)
|
|
29
39
|
range.cover?(@offense.line - 1)
|
|
30
40
|
end
|
|
31
41
|
|
|
42
|
+
sig { returns(LanguageServer::Protocol::Interface::CodeAction) }
|
|
32
43
|
def to_lsp_code_action
|
|
33
44
|
LanguageServer::Protocol::Interface::CodeAction.new(
|
|
34
45
|
title: "Autocorrect #{@offense.cop_name}",
|
|
@@ -48,6 +59,7 @@ module RubyLsp
|
|
|
48
59
|
)
|
|
49
60
|
end
|
|
50
61
|
|
|
62
|
+
sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
|
|
51
63
|
def to_lsp_diagnostic
|
|
52
64
|
LanguageServer::Protocol::Interface::Diagnostic.new(
|
|
53
65
|
message: @offense.message,
|
|
@@ -69,6 +81,7 @@ module RubyLsp
|
|
|
69
81
|
|
|
70
82
|
private
|
|
71
83
|
|
|
84
|
+
sig { returns(T::Array[LanguageServer::Protocol::Interface::TextEdit]) }
|
|
72
85
|
def offense_replacements
|
|
73
86
|
@offense.corrector.as_replacements.map do |range, replacement|
|
|
74
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,9 +1,13 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
4
5
|
module Requests
|
|
5
6
|
module Support
|
|
6
7
|
class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { params(position: Document::PositionShape).returns(T::Boolean) }
|
|
7
11
|
def cover?(position)
|
|
8
12
|
line_range = (range.start.line..range.end.line)
|
|
9
13
|
character_range = (range.start.character..range.end.character)
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
4
5
|
module Requests
|
|
5
6
|
module Support
|
|
6
7
|
class SemanticTokenEncoder
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { void }
|
|
7
11
|
def initialize
|
|
8
|
-
@current_row = 0
|
|
9
|
-
@current_column = 0
|
|
12
|
+
@current_row = T.let(0, Integer)
|
|
13
|
+
@current_column = T.let(0, Integer)
|
|
10
14
|
end
|
|
11
15
|
|
|
16
|
+
sig do
|
|
17
|
+
params(
|
|
18
|
+
tokens: T::Array[SemanticHighlighting::SemanticToken]
|
|
19
|
+
).returns(LanguageServer::Protocol::Interface::SemanticTokens)
|
|
20
|
+
end
|
|
12
21
|
def encode(tokens)
|
|
13
22
|
delta = tokens
|
|
14
23
|
.sort_by do |token|
|
|
@@ -30,6 +39,7 @@ module RubyLsp
|
|
|
30
39
|
|
|
31
40
|
# For more information on how each number is calculated, read:
|
|
32
41
|
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
|
|
42
|
+
sig { params(token: SemanticHighlighting::SemanticToken).returns(T::Array[Integer]) }
|
|
33
43
|
def compute_delta(token)
|
|
34
44
|
row = token.location.start_line - 1
|
|
35
45
|
column = token.location.start_column
|
|
@@ -38,11 +48,22 @@ module RubyLsp
|
|
|
38
48
|
delta_column = column
|
|
39
49
|
delta_column -= @current_column if delta_line == 0
|
|
40
50
|
|
|
41
|
-
[delta_line, delta_column, token.length, token.type, token.modifier]
|
|
51
|
+
[delta_line, delta_column, token.length, token.type, encode_modifiers(token.modifier)]
|
|
42
52
|
ensure
|
|
43
53
|
@current_row = row
|
|
44
54
|
@current_column = column
|
|
45
55
|
end
|
|
56
|
+
|
|
57
|
+
# Encode an array of modifiers to positions onto a bit flag
|
|
58
|
+
# For example, [:default_library] will be encoded as
|
|
59
|
+
# 0b1000000000, as :default_library is the 10th bit according
|
|
60
|
+
# to the token modifiers index map.
|
|
61
|
+
sig { params(modifiers: T::Array[Integer]).returns(Integer) }
|
|
62
|
+
def encode_modifiers(modifiers)
|
|
63
|
+
modifiers.inject(0) do |encoded_modifiers, modifier|
|
|
64
|
+
encoded_modifiers | (1 << modifier)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
46
67
|
end
|
|
47
68
|
end
|
|
48
69
|
end
|
|
@@ -1,17 +1,23 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
4
5
|
module Requests
|
|
5
6
|
module Support
|
|
6
7
|
class SyntaxErrorDiagnostic
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
sig { params(edit: Document::EditShape).void }
|
|
7
11
|
def initialize(edit)
|
|
8
12
|
@edit = edit
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
sig { returns(FalseClass) }
|
|
11
16
|
def correctable?
|
|
12
17
|
false
|
|
13
18
|
end
|
|
14
19
|
|
|
20
|
+
sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
|
|
15
21
|
def to_lsp_diagnostic
|
|
16
22
|
LanguageServer::Protocol::Interface::Diagnostic.new(
|
|
17
23
|
message: "Syntax error",
|
data/lib/ruby_lsp/requests.rb
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
5
|
+
# Supported features
|
|
6
|
+
#
|
|
7
|
+
# - {RubyLsp::Requests::DocumentSymbol}
|
|
8
|
+
# - {RubyLsp::Requests::FoldingRanges}
|
|
9
|
+
# - {RubyLsp::Requests::SelectionRanges}
|
|
10
|
+
# - {RubyLsp::Requests::SemanticHighlighting}
|
|
11
|
+
# - {RubyLsp::Requests::Formatting}
|
|
12
|
+
# - {RubyLsp::Requests::Diagnostics}
|
|
13
|
+
# - {RubyLsp::Requests::CodeActions}
|
|
14
|
+
# - {RubyLsp::Requests::DocumentHighlight}
|
|
4
15
|
module Requests
|
|
5
16
|
autoload :BaseRequest, "ruby_lsp/requests/base_request"
|
|
6
17
|
autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
|
|
7
18
|
autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
|
|
8
19
|
autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
|
|
9
20
|
autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
|
|
10
|
-
autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
|
|
11
21
|
autoload :Formatting, "ruby_lsp/requests/formatting"
|
|
12
22
|
autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
|
|
13
23
|
autoload :CodeActions, "ruby_lsp/requests/code_actions"
|
|
24
|
+
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
|
14
25
|
|
|
26
|
+
# :nodoc:
|
|
15
27
|
module Support
|
|
16
28
|
autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
|
|
17
29
|
autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
|
data/lib/ruby_lsp/store.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require "cgi"
|
|
@@ -6,36 +7,52 @@ require "ruby_lsp/document"
|
|
|
6
7
|
|
|
7
8
|
module RubyLsp
|
|
8
9
|
class Store
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig { void }
|
|
9
13
|
def initialize
|
|
10
|
-
@state = {}
|
|
14
|
+
@state = T.let({}, T::Hash[String, Document])
|
|
11
15
|
end
|
|
12
16
|
|
|
17
|
+
sig { params(uri: String).returns(Document) }
|
|
13
18
|
def get(uri)
|
|
14
19
|
document = @state[uri]
|
|
15
20
|
return document unless document.nil?
|
|
16
21
|
|
|
17
22
|
set(uri, File.binread(CGI.unescape(URI.parse(uri).path)))
|
|
18
|
-
@state[uri]
|
|
23
|
+
T.must(@state[uri])
|
|
19
24
|
end
|
|
20
25
|
|
|
26
|
+
sig { params(uri: String, content: String).void }
|
|
21
27
|
def set(uri, content)
|
|
22
28
|
@state[uri] = Document.new(content)
|
|
23
29
|
rescue SyntaxTree::Parser::ParseError
|
|
24
30
|
# Do not update the store if there are syntax errors
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
sig { params(uri: String, edits: T::Array[Document::EditShape]).void }
|
|
27
34
|
def push_edits(uri, edits)
|
|
28
|
-
@state[uri].push_edits(edits)
|
|
35
|
+
T.must(@state[uri]).push_edits(edits)
|
|
29
36
|
end
|
|
30
37
|
|
|
38
|
+
sig { void }
|
|
31
39
|
def clear
|
|
32
40
|
@state.clear
|
|
33
41
|
end
|
|
34
42
|
|
|
43
|
+
sig { params(uri: String).void }
|
|
35
44
|
def delete(uri)
|
|
36
45
|
@state.delete(uri)
|
|
37
46
|
end
|
|
38
47
|
|
|
48
|
+
sig do
|
|
49
|
+
type_parameters(:T)
|
|
50
|
+
.params(
|
|
51
|
+
uri: String,
|
|
52
|
+
request_name: Symbol,
|
|
53
|
+
block: T.proc.params(document: Document).returns(T.type_parameter(:T))
|
|
54
|
+
).returns(T.type_parameter(:T))
|
|
55
|
+
end
|
|
39
56
|
def cache_fetch(uri, request_name, &block)
|
|
40
57
|
get(uri).cache_fetch(request_name, &block)
|
|
41
58
|
end
|