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,12 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/requests/support/rubocop_diagnostics_runner"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
8
|
+
# ![Diagnostics demo](../../misc/diagnostics.gif)
|
9
|
+
#
|
6
10
|
# The
|
7
11
|
# [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
|
8
12
|
# request informs the editor of RuboCop offenses for a given file.
|
@@ -14,21 +18,34 @@ module RubyLsp
|
|
14
18
|
# puts "Hello" # --> diagnostics: incorrect indentantion
|
15
19
|
# end
|
16
20
|
# ```
|
17
|
-
class Diagnostics <
|
18
|
-
|
19
|
-
return syntax_error_diagnostics if @document.syntax_errors?
|
21
|
+
class Diagnostics < BaseRequest
|
22
|
+
extend T::Sig
|
20
23
|
|
21
|
-
|
24
|
+
sig { params(uri: String, document: Document).void }
|
25
|
+
def initialize(uri, document)
|
26
|
+
super(document)
|
22
27
|
|
23
|
-
@
|
28
|
+
@uri = uri
|
24
29
|
end
|
25
30
|
|
26
|
-
|
27
|
-
|
31
|
+
sig do
|
32
|
+
override.returns(
|
33
|
+
T.any(
|
34
|
+
T.all(T::Array[Support::RuboCopDiagnostic], Object),
|
35
|
+
T.all(T::Array[Support::SyntaxErrorDiagnostic], Object),
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
def run
|
40
|
+
return syntax_error_diagnostics if @document.syntax_errors?
|
41
|
+
return [] unless defined?(Support::RuboCopDiagnosticsRunner)
|
42
|
+
|
43
|
+
Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
|
28
44
|
end
|
29
45
|
|
30
46
|
private
|
31
47
|
|
48
|
+
sig { returns(T::Array[Support::SyntaxErrorDiagnostic]) }
|
32
49
|
def syntax_error_diagnostics
|
33
50
|
@document.syntax_error_edits.map { |e| Support::SyntaxErrorDiagnostic.new(e) }
|
34
51
|
end
|
@@ -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
|
+
# ![Document highlight demo](../../misc/document_highlight.gif)
|
7
|
+
#
|
6
8
|
# The [document highlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
|
7
9
|
# informs the editor all relevant elements of the currently pointed item for highlighting. For example, when
|
8
10
|
# the cursor is on the `F` of the constant `FOO`, the editor should identify other occurences of `FOO`
|
@@ -21,18 +23,18 @@ module RubyLsp
|
|
21
23
|
# end
|
22
24
|
# ```
|
23
25
|
class DocumentHighlight < BaseRequest
|
24
|
-
|
25
|
-
new(document, position).run
|
26
|
-
end
|
26
|
+
extend T::Sig
|
27
27
|
|
28
|
+
sig { params(document: Document, position: Document::PositionShape).void }
|
28
29
|
def initialize(document, position)
|
29
|
-
@highlights = []
|
30
|
+
@highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
|
30
31
|
position = Document::Scanner.new(document.source).find_position(position)
|
31
|
-
@target = find(document.tree, position)
|
32
|
+
@target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
|
32
33
|
|
33
34
|
super(document)
|
34
35
|
end
|
35
36
|
|
37
|
+
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight], Object)) }
|
36
38
|
def run
|
37
39
|
# no @target means the target is not highlightable
|
38
40
|
return [] unless @target
|
@@ -41,30 +43,24 @@ module RubyLsp
|
|
41
43
|
@highlights
|
42
44
|
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
node.value,
|
48
|
-
LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
|
49
|
-
)
|
50
|
-
end
|
51
|
-
|
52
|
-
super
|
53
|
-
end
|
46
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
47
|
+
def visit(node)
|
48
|
+
return if node.nil?
|
54
49
|
|
55
|
-
|
56
|
-
if
|
57
|
-
add_highlight(
|
58
|
-
node.value,
|
59
|
-
LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
|
60
|
-
)
|
61
|
-
end
|
50
|
+
match = T.must(@target).highlight_type(node)
|
51
|
+
add_highlight(match) if match
|
62
52
|
|
63
53
|
super
|
64
54
|
end
|
65
55
|
|
66
56
|
private
|
67
57
|
|
58
|
+
sig do
|
59
|
+
params(
|
60
|
+
node: SyntaxTree::Node,
|
61
|
+
position: Integer,
|
62
|
+
).returns(T.nilable(Support::HighlightTarget))
|
63
|
+
end
|
68
64
|
def find(node, position)
|
69
65
|
matched =
|
70
66
|
node.child_nodes.compact.bsearch do |child|
|
@@ -76,20 +72,24 @@ module RubyLsp
|
|
76
72
|
end
|
77
73
|
|
78
74
|
case matched
|
79
|
-
when SyntaxTree::GVar,
|
80
|
-
|
75
|
+
when SyntaxTree::GVar,
|
76
|
+
SyntaxTree::IVar,
|
77
|
+
SyntaxTree::Const,
|
78
|
+
SyntaxTree::CVar,
|
79
|
+
SyntaxTree::VarField
|
80
|
+
Support::HighlightTarget.new(matched)
|
81
|
+
when SyntaxTree::Ident
|
82
|
+
relevant_node = node.is_a?(SyntaxTree::Params) ? matched : node
|
83
|
+
Support::HighlightTarget.new(relevant_node)
|
81
84
|
when SyntaxTree::Node
|
82
85
|
find(matched, position)
|
83
86
|
end
|
84
87
|
end
|
85
88
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def add_highlight(node, kind)
|
91
|
-
range = range_from_syntax_tree_node(node)
|
92
|
-
@highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: kind)
|
89
|
+
sig { params(match: Support::HighlightTarget::HighlightMatch).void }
|
90
|
+
def add_highlight(match)
|
91
|
+
range = range_from_syntax_tree_node(match.node)
|
92
|
+
@highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: match.type)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
@@ -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
|
+
# ![Document symbol demo](../../misc/document_symbol.gif)
|
7
|
+
#
|
6
8
|
# The [document
|
7
9
|
# symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
|
8
10
|
# informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
|
@@ -25,7 +27,9 @@ module RubyLsp
|
|
25
27
|
# end
|
26
28
|
# ```
|
27
29
|
class DocumentSymbol < BaseRequest
|
28
|
-
|
30
|
+
extend T::Sig
|
31
|
+
|
32
|
+
SYMBOL_KIND = T.let({
|
29
33
|
file: 1,
|
30
34
|
module: 2,
|
31
35
|
namespace: 3,
|
@@ -52,33 +56,43 @@ module RubyLsp
|
|
52
56
|
event: 24,
|
53
57
|
operator: 25,
|
54
58
|
typeparameter: 26,
|
55
|
-
}.freeze
|
59
|
+
}.freeze, T::Hash[Symbol, Integer])
|
56
60
|
|
57
|
-
ATTR_ACCESSORS = ["attr_reader", "attr_writer", "attr_accessor"].freeze
|
61
|
+
ATTR_ACCESSORS = T.let(["attr_reader", "attr_writer", "attr_accessor"].freeze, T::Array[String])
|
58
62
|
|
59
63
|
class SymbolHierarchyRoot
|
64
|
+
extend T::Sig
|
65
|
+
|
66
|
+
sig { returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
|
60
67
|
attr_reader :children
|
61
68
|
|
69
|
+
sig { void }
|
62
70
|
def initialize
|
63
|
-
@children = []
|
71
|
+
@children = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentSymbol])
|
64
72
|
end
|
65
73
|
end
|
66
74
|
|
75
|
+
sig { params(document: Document).void }
|
67
76
|
def initialize(document)
|
68
77
|
super
|
69
78
|
|
70
|
-
@root = SymbolHierarchyRoot.new
|
71
|
-
@stack =
|
79
|
+
@root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
|
80
|
+
@stack = T.let(
|
81
|
+
[@root],
|
82
|
+
T::Array[T.any(SymbolHierarchyRoot, LanguageServer::Protocol::Interface::DocumentSymbol)]
|
83
|
+
)
|
72
84
|
end
|
73
85
|
|
86
|
+
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol], Object)) }
|
74
87
|
def run
|
75
88
|
visit(@document.tree)
|
76
89
|
@root.children
|
77
90
|
end
|
78
91
|
|
92
|
+
sig { params(node: SyntaxTree::ClassDeclaration).void }
|
79
93
|
def visit_class(node)
|
80
94
|
symbol = create_document_symbol(
|
81
|
-
name: node
|
95
|
+
name: fully_qualified_name(node),
|
82
96
|
kind: :class,
|
83
97
|
range_node: node,
|
84
98
|
selection_range_node: node.constant
|
@@ -89,6 +103,7 @@ module RubyLsp
|
|
89
103
|
@stack.pop
|
90
104
|
end
|
91
105
|
|
106
|
+
sig { params(node: SyntaxTree::Command).void }
|
92
107
|
def visit_command(node)
|
93
108
|
return unless ATTR_ACCESSORS.include?(node.message.value)
|
94
109
|
|
@@ -104,6 +119,7 @@ module RubyLsp
|
|
104
119
|
end
|
105
120
|
end
|
106
121
|
|
122
|
+
sig { params(node: SyntaxTree::ConstPathField).void }
|
107
123
|
def visit_const_path_field(node)
|
108
124
|
create_document_symbol(
|
109
125
|
name: node.constant.value,
|
@@ -113,6 +129,7 @@ module RubyLsp
|
|
113
129
|
)
|
114
130
|
end
|
115
131
|
|
132
|
+
sig { params(node: SyntaxTree::Def).void }
|
116
133
|
def visit_def(node)
|
117
134
|
name = node.name.value
|
118
135
|
|
@@ -128,6 +145,7 @@ module RubyLsp
|
|
128
145
|
@stack.pop
|
129
146
|
end
|
130
147
|
|
148
|
+
sig { params(node: SyntaxTree::DefEndless).void }
|
131
149
|
def visit_def_endless(node)
|
132
150
|
name = node.name.value
|
133
151
|
|
@@ -143,6 +161,7 @@ module RubyLsp
|
|
143
161
|
@stack.pop
|
144
162
|
end
|
145
163
|
|
164
|
+
sig { params(node: SyntaxTree::Defs).void }
|
146
165
|
def visit_defs(node)
|
147
166
|
symbol = create_document_symbol(
|
148
167
|
name: "self.#{node.name.value}",
|
@@ -156,9 +175,10 @@ module RubyLsp
|
|
156
175
|
@stack.pop
|
157
176
|
end
|
158
177
|
|
178
|
+
sig { params(node: SyntaxTree::ModuleDeclaration).void }
|
159
179
|
def visit_module(node)
|
160
180
|
symbol = create_document_symbol(
|
161
|
-
name: node
|
181
|
+
name: fully_qualified_name(node),
|
162
182
|
kind: :module,
|
163
183
|
range_node: node,
|
164
184
|
selection_range_node: node.constant
|
@@ -169,6 +189,7 @@ module RubyLsp
|
|
169
189
|
@stack.pop
|
170
190
|
end
|
171
191
|
|
192
|
+
sig { params(node: SyntaxTree::TopConstField).void }
|
172
193
|
def visit_top_const_field(node)
|
173
194
|
create_document_symbol(
|
174
195
|
name: node.constant.value,
|
@@ -178,6 +199,7 @@ module RubyLsp
|
|
178
199
|
)
|
179
200
|
end
|
180
201
|
|
202
|
+
sig { params(node: SyntaxTree::VarField).void }
|
181
203
|
def visit_var_field(node)
|
182
204
|
kind = case node.value
|
183
205
|
when SyntaxTree::Const
|
@@ -198,6 +220,14 @@ module RubyLsp
|
|
198
220
|
|
199
221
|
private
|
200
222
|
|
223
|
+
sig do
|
224
|
+
params(
|
225
|
+
name: String,
|
226
|
+
kind: Symbol,
|
227
|
+
range_node: SyntaxTree::Node,
|
228
|
+
selection_range_node: SyntaxTree::Node
|
229
|
+
).returns(LanguageServer::Protocol::Interface::DocumentSymbol)
|
230
|
+
end
|
201
231
|
def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
|
202
232
|
symbol = LanguageServer::Protocol::Interface::DocumentSymbol.new(
|
203
233
|
name: name,
|
@@ -207,10 +237,29 @@ module RubyLsp
|
|
207
237
|
children: [],
|
208
238
|
)
|
209
239
|
|
210
|
-
@stack.last.children << symbol
|
240
|
+
T.must(@stack.last).children << symbol
|
211
241
|
|
212
242
|
symbol
|
213
243
|
end
|
244
|
+
|
245
|
+
sig { params(node: T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)).returns(String) }
|
246
|
+
def fully_qualified_name(node)
|
247
|
+
constant = T.let(node.constant, SyntaxTree::Node)
|
248
|
+
name = +node.constant.constant.value
|
249
|
+
|
250
|
+
while constant.is_a?(SyntaxTree::ConstPathRef)
|
251
|
+
constant = constant.parent
|
252
|
+
|
253
|
+
case constant
|
254
|
+
when SyntaxTree::ConstPathRef
|
255
|
+
name.prepend("#{constant.constant.value}::")
|
256
|
+
when SyntaxTree::VarRef
|
257
|
+
name.prepend("#{constant.value.value}::")
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
name
|
262
|
+
end
|
214
263
|
end
|
215
264
|
end
|
216
265
|
end
|
@@ -1,19 +1,24 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Requests
|
6
|
+
# ![Folding ranges demo](../../misc/folding_ranges.gif)
|
7
|
+
#
|
6
8
|
# The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
|
7
|
-
# request informs the editor of the ranges where code can be folded.
|
9
|
+
# request informs the editor of the ranges where and how code can be folded.
|
8
10
|
#
|
9
11
|
# # Example
|
12
|
+
#
|
10
13
|
# ```ruby
|
11
14
|
# def say_hello # <-- folding range start
|
12
15
|
# puts "Hello"
|
13
16
|
# end # <-- folding range end
|
14
17
|
# ```
|
15
18
|
class FoldingRanges < BaseRequest
|
16
|
-
|
19
|
+
extend T::Sig
|
20
|
+
|
21
|
+
SIMPLE_FOLDABLES = T.let([
|
17
22
|
SyntaxTree::ArrayLiteral,
|
18
23
|
SyntaxTree::BraceBlock,
|
19
24
|
SyntaxTree::Case,
|
@@ -30,24 +35,36 @@ module RubyLsp
|
|
30
35
|
SyntaxTree::Unless,
|
31
36
|
SyntaxTree::Until,
|
32
37
|
SyntaxTree::While,
|
33
|
-
].freeze
|
34
|
-
|
35
|
-
NODES_WITH_STATEMENTS = [
|
36
38
|
SyntaxTree::Else,
|
37
|
-
SyntaxTree::Elsif,
|
38
39
|
SyntaxTree::Ensure,
|
40
|
+
SyntaxTree::Begin,
|
41
|
+
].freeze, T::Array[T.class_of(SyntaxTree::Node)])
|
42
|
+
|
43
|
+
NODES_WITH_STATEMENTS = T.let([
|
44
|
+
SyntaxTree::Elsif,
|
39
45
|
SyntaxTree::In,
|
40
46
|
SyntaxTree::Rescue,
|
41
47
|
SyntaxTree::When,
|
42
|
-
].freeze
|
48
|
+
].freeze, T::Array[T.class_of(SyntaxTree::Node)])
|
49
|
+
|
50
|
+
StatementNode = T.type_alias do
|
51
|
+
T.any(
|
52
|
+
SyntaxTree::Elsif,
|
53
|
+
SyntaxTree::In,
|
54
|
+
SyntaxTree::Rescue,
|
55
|
+
SyntaxTree::When,
|
56
|
+
)
|
57
|
+
end
|
43
58
|
|
59
|
+
sig { params(document: Document).void }
|
44
60
|
def initialize(document)
|
45
61
|
super
|
46
62
|
|
47
|
-
@ranges = []
|
48
|
-
@partial_range = nil
|
63
|
+
@ranges = T.let([], T::Array[LanguageServer::Protocol::Interface::FoldingRange])
|
64
|
+
@partial_range = T.let(nil, T.nilable(PartialRange))
|
49
65
|
end
|
50
66
|
|
67
|
+
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::FoldingRange], Object)) }
|
51
68
|
def run
|
52
69
|
visit(@document.tree)
|
53
70
|
emit_partial_range
|
@@ -56,16 +73,16 @@ module RubyLsp
|
|
56
73
|
|
57
74
|
private
|
58
75
|
|
76
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
59
77
|
def visit(node)
|
60
78
|
return unless handle_partial_range(node)
|
61
79
|
|
62
80
|
case node
|
63
81
|
when *SIMPLE_FOLDABLES
|
64
|
-
|
82
|
+
location = T.must(node).location
|
83
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
65
84
|
when *NODES_WITH_STATEMENTS
|
66
|
-
add_statements_range(node, node.statements)
|
67
|
-
when SyntaxTree::Begin
|
68
|
-
add_statements_range(node, node.bodystmt.statements)
|
85
|
+
add_statements_range(T.must(node), T.cast(node, StatementNode).statements)
|
69
86
|
when SyntaxTree::Call, SyntaxTree::CommandCall
|
70
87
|
add_call_range(node)
|
71
88
|
return
|
@@ -80,27 +97,38 @@ module RubyLsp
|
|
80
97
|
end
|
81
98
|
|
82
99
|
class PartialRange
|
83
|
-
|
100
|
+
extend T::Sig
|
84
101
|
|
102
|
+
sig { returns(String) }
|
103
|
+
attr_reader :kind
|
104
|
+
|
105
|
+
sig { returns(Integer) }
|
106
|
+
attr_reader :end_line
|
107
|
+
|
108
|
+
sig { params(node: SyntaxTree::Node, kind: String).returns(PartialRange) }
|
85
109
|
def self.from(node, kind)
|
86
110
|
new(node.location.start_line - 1, node.location.end_line - 1, kind)
|
87
111
|
end
|
88
112
|
|
113
|
+
sig { params(start_line: Integer, end_line: Integer, kind: String).void }
|
89
114
|
def initialize(start_line, end_line, kind)
|
90
115
|
@start_line = start_line
|
91
116
|
@end_line = end_line
|
92
117
|
@kind = kind
|
93
118
|
end
|
94
119
|
|
120
|
+
sig { params(node: SyntaxTree::Node).returns(PartialRange) }
|
95
121
|
def extend_to(node)
|
96
122
|
@end_line = node.location.end_line - 1
|
97
123
|
self
|
98
124
|
end
|
99
125
|
|
126
|
+
sig { params(node: SyntaxTree::Node).returns(T::Boolean) }
|
100
127
|
def new_section?(node)
|
101
128
|
node.is_a?(SyntaxTree::Comment) && @end_line + 1 != node.location.start_line - 1
|
102
129
|
end
|
103
130
|
|
131
|
+
sig { returns(LanguageServer::Protocol::Interface::FoldingRange) }
|
104
132
|
def to_range
|
105
133
|
LanguageServer::Protocol::Interface::FoldingRange.new(
|
106
134
|
start_line: @start_line,
|
@@ -108,8 +136,14 @@ module RubyLsp
|
|
108
136
|
kind: @kind
|
109
137
|
)
|
110
138
|
end
|
139
|
+
|
140
|
+
sig { returns(T::Boolean) }
|
141
|
+
def multiline?
|
142
|
+
@end_line > @start_line
|
143
|
+
end
|
111
144
|
end
|
112
145
|
|
146
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).returns(T::Boolean) }
|
113
147
|
def handle_partial_range(node)
|
114
148
|
kind = partial_range_kind(node)
|
115
149
|
|
@@ -118,18 +152,20 @@ module RubyLsp
|
|
118
152
|
return true
|
119
153
|
end
|
120
154
|
|
155
|
+
target_node = T.must(node)
|
121
156
|
@partial_range = if @partial_range.nil?
|
122
|
-
PartialRange.from(
|
123
|
-
elsif @partial_range.kind != kind || @partial_range.new_section?(
|
157
|
+
PartialRange.from(target_node, kind)
|
158
|
+
elsif @partial_range.kind != kind || @partial_range.new_section?(target_node)
|
124
159
|
emit_partial_range
|
125
|
-
PartialRange.from(
|
160
|
+
PartialRange.from(target_node, kind)
|
126
161
|
else
|
127
|
-
@partial_range.extend_to(
|
162
|
+
@partial_range.extend_to(target_node)
|
128
163
|
end
|
129
164
|
|
130
165
|
false
|
131
166
|
end
|
132
167
|
|
168
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).returns(T.nilable(String)) }
|
133
169
|
def partial_range_kind(node)
|
134
170
|
case node
|
135
171
|
when SyntaxTree::Comment
|
@@ -141,13 +177,15 @@ module RubyLsp
|
|
141
177
|
end
|
142
178
|
end
|
143
179
|
|
180
|
+
sig { void }
|
144
181
|
def emit_partial_range
|
145
182
|
return if @partial_range.nil?
|
146
183
|
|
147
|
-
@ranges << @partial_range.to_range
|
184
|
+
@ranges << @partial_range.to_range if @partial_range.multiline?
|
148
185
|
@partial_range = nil
|
149
186
|
end
|
150
187
|
|
188
|
+
sig { params(node: T.any(SyntaxTree::Call, SyntaxTree::CommandCall)).void }
|
151
189
|
def add_call_range(node)
|
152
190
|
receiver = T.let(node.receiver, SyntaxTree::Node)
|
153
191
|
loop do
|
@@ -157,48 +195,49 @@ module RubyLsp
|
|
157
195
|
receiver = receiver.receiver
|
158
196
|
when SyntaxTree::MethodAddBlock
|
159
197
|
visit(receiver.block)
|
160
|
-
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
|
161
203
|
else
|
162
204
|
break
|
163
205
|
end
|
164
206
|
end
|
165
207
|
|
166
|
-
add_lines_range(receiver.location.start_line, node.location.end_line)
|
208
|
+
add_lines_range(receiver.location.start_line, node.location.end_line - 1)
|
167
209
|
|
168
210
|
visit(node.arguments)
|
169
211
|
end
|
170
212
|
|
213
|
+
sig { params(node: T.any(SyntaxTree::Def, SyntaxTree::Defs)).void }
|
171
214
|
def add_def_range(node)
|
172
215
|
params_location = node.params.location
|
173
216
|
|
174
217
|
if params_location.start_line < params_location.end_line
|
175
|
-
add_lines_range(params_location.end_line, node.location.end_line)
|
218
|
+
add_lines_range(params_location.end_line, node.location.end_line - 1)
|
176
219
|
else
|
177
|
-
|
220
|
+
location = node.location
|
221
|
+
add_lines_range(location.start_line, location.end_line - 1)
|
178
222
|
end
|
179
223
|
|
180
224
|
visit(node.bodystmt.statements)
|
181
225
|
end
|
182
226
|
|
227
|
+
sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
|
183
228
|
def add_statements_range(node, statements)
|
184
|
-
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?
|
185
230
|
end
|
186
231
|
|
232
|
+
sig { params(node: SyntaxTree::StringConcat).void }
|
187
233
|
def add_string_concat(node)
|
188
234
|
left = T.let(node.left, SyntaxTree::Node)
|
189
235
|
left = left.left while left.is_a?(SyntaxTree::StringConcat)
|
190
236
|
|
191
|
-
add_lines_range(left.location.start_line, node.right.location.end_line)
|
192
|
-
end
|
193
|
-
|
194
|
-
def add_node_range(node)
|
195
|
-
add_location_range(node.location)
|
196
|
-
end
|
197
|
-
|
198
|
-
def add_location_range(location)
|
199
|
-
add_lines_range(location.start_line, location.end_line)
|
237
|
+
add_lines_range(left.location.start_line, node.right.location.end_line - 1)
|
200
238
|
end
|
201
239
|
|
240
|
+
sig { params(start_line: Integer, end_line: Integer).void }
|
202
241
|
def add_lines_range(start_line, end_line)
|
203
242
|
return if start_line >= end_line
|
204
243
|
|
@@ -1,8 +1,12 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "ruby_lsp/requests/support/rubocop_formatting_runner"
|
5
|
+
|
4
6
|
module RubyLsp
|
5
7
|
module Requests
|
8
|
+
# ![Formatting symbol demo](../../misc/formatting.gif)
|
9
|
+
#
|
6
10
|
# The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
|
7
11
|
# request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
|
8
12
|
# registering the ruby-lsp as the Ruby formatter.
|
@@ -14,38 +18,44 @@ module RubyLsp
|
|
14
18
|
# puts "Hello" # --> formatting: fixes the indentation on save
|
15
19
|
# end
|
16
20
|
# ```
|
17
|
-
class Formatting <
|
18
|
-
|
21
|
+
class Formatting < BaseRequest
|
22
|
+
extend T::Sig
|
19
23
|
|
24
|
+
sig { params(uri: String, document: Document).void }
|
20
25
|
def initialize(uri, document)
|
21
|
-
super
|
22
|
-
|
26
|
+
super(document)
|
27
|
+
|
28
|
+
@uri = uri
|
23
29
|
end
|
24
30
|
|
31
|
+
sig { override.returns(T.nilable(T.all(T::Array[LanguageServer::Protocol::Interface::TextEdit], Object))) }
|
25
32
|
def run
|
26
|
-
|
33
|
+
formatted_text = formatted_file
|
34
|
+
return unless formatted_text
|
27
35
|
|
28
|
-
|
29
|
-
return
|
36
|
+
size = @document.source.size
|
37
|
+
return if formatted_text.size == size && formatted_text == @document.source
|
30
38
|
|
31
39
|
[
|
32
40
|
LanguageServer::Protocol::Interface::TextEdit.new(
|
33
41
|
range: LanguageServer::Protocol::Interface::Range.new(
|
34
42
|
start: LanguageServer::Protocol::Interface::Position.new(line: 0, character: 0),
|
35
|
-
end: LanguageServer::Protocol::Interface::Position.new(
|
36
|
-
line: text.size,
|
37
|
-
character: text.size
|
38
|
-
)
|
43
|
+
end: LanguageServer::Protocol::Interface::Position.new(line: size, character: size)
|
39
44
|
),
|
40
|
-
new_text:
|
45
|
+
new_text: formatted_text
|
41
46
|
),
|
42
47
|
]
|
43
48
|
end
|
44
49
|
|
45
50
|
private
|
46
51
|
|
47
|
-
|
48
|
-
|
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
|
49
59
|
end
|
50
60
|
end
|
51
61
|
end
|