ruby-lsp 0.12.5 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp-check +20 -4
- data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +36 -2
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +132 -13
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/index_test.rb +11 -0
- data/lib/ruby_indexer/test/method_test.rb +202 -0
- data/lib/ruby_lsp/addon.rb +9 -13
- data/lib/ruby_lsp/document.rb +3 -0
- data/lib/ruby_lsp/executor.rb +33 -28
- data/lib/ruby_lsp/listener.rb +4 -5
- data/lib/ruby_lsp/requests/code_actions.rb +2 -16
- data/lib/ruby_lsp/requests/code_lens.rb +29 -7
- data/lib/ruby_lsp/requests/completion.rb +11 -8
- data/lib/ruby_lsp/requests/definition.rb +3 -4
- data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +2 -3
- data/lib/ruby_lsp/requests/document_link.rb +2 -3
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
- data/lib/ruby_lsp/requests/formatting.rb +0 -5
- data/lib/ruby_lsp/requests/hover.rb +3 -4
- data/lib/ruby_lsp/requests/inlay_hints.rb +20 -3
- data/lib/ruby_lsp/requests/on_type_formatting.rb +31 -4
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +28 -11
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +92 -21
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +1 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
- data/lib/ruby_lsp/requests/workspace_symbol.rb +2 -0
- data/lib/ruby_lsp/store.rb +21 -0
- data/lib/ruby_lsp/utils.rb +18 -4
- metadata +7 -7
@@ -20,8 +20,8 @@ module RubyLsp
|
|
20
20
|
|
21
21
|
END_REGEXES = T.let(
|
22
22
|
[
|
23
|
-
|
24
|
-
/.*\
|
23
|
+
/\b(if|unless|for|while|class|module|until|def|case)\b.*/,
|
24
|
+
/.*\s\bdo\b/,
|
25
25
|
],
|
26
26
|
T::Array[Regexp],
|
27
27
|
)
|
@@ -60,6 +60,8 @@ module RubyLsp
|
|
60
60
|
handle_statement_end
|
61
61
|
end
|
62
62
|
end
|
63
|
+
when "d"
|
64
|
+
auto_indent_after_end_keyword
|
63
65
|
end
|
64
66
|
|
65
67
|
@edits
|
@@ -118,7 +120,7 @@ module RubyLsp
|
|
118
120
|
current_line = @lines[@position[:line]]
|
119
121
|
next_line = @lines[@position[:line] + 1]
|
120
122
|
|
121
|
-
if current_line.nil? || current_line.strip.empty?
|
123
|
+
if current_line.nil? || current_line.strip.empty? || current_line.include?(")") || current_line.include?("]")
|
122
124
|
add_edit_with_text("\n")
|
123
125
|
add_edit_with_text("#{indents}end")
|
124
126
|
move_cursor_to(@position[:line], @indentation + 2)
|
@@ -139,7 +141,6 @@ module RubyLsp
|
|
139
141
|
sig { params(spaces: String).void }
|
140
142
|
def handle_comment_line(spaces)
|
141
143
|
add_edit_with_text("##{spaces}")
|
142
|
-
move_cursor_to(@position[:line], @indentation + spaces.size + 1)
|
143
144
|
end
|
144
145
|
|
145
146
|
sig { params(text: String, position: Document::PositionShape).void }
|
@@ -186,6 +187,32 @@ module RubyLsp
|
|
186
187
|
|
187
188
|
count
|
188
189
|
end
|
190
|
+
|
191
|
+
sig { void }
|
192
|
+
def auto_indent_after_end_keyword
|
193
|
+
current_line = @lines[@position[:line]]
|
194
|
+
return unless current_line&.strip == "end"
|
195
|
+
|
196
|
+
target, _parent, _nesting = @document.locate_node({
|
197
|
+
line: @position[:line],
|
198
|
+
character: @position[:character] - 1,
|
199
|
+
})
|
200
|
+
|
201
|
+
statements = case target
|
202
|
+
when Prism::IfNode, Prism::UnlessNode, Prism::ForNode, Prism::WhileNode, Prism::UntilNode
|
203
|
+
target.statements
|
204
|
+
end
|
205
|
+
return unless statements
|
206
|
+
|
207
|
+
statements.body.each do |node|
|
208
|
+
loc = node.location
|
209
|
+
next unless loc.start_column == @indentation
|
210
|
+
|
211
|
+
add_edit_with_text(" ", { line: loc.start_line - 1, character: 0 })
|
212
|
+
end
|
213
|
+
|
214
|
+
move_cursor_to(@position[:line], @position[:character])
|
215
|
+
end
|
189
216
|
end
|
190
217
|
end
|
191
218
|
end
|
@@ -107,20 +107,15 @@ module RubyLsp
|
|
107
107
|
sig { override.returns(ResponseType) }
|
108
108
|
attr_reader :_response
|
109
109
|
|
110
|
-
sig
|
111
|
-
|
112
|
-
|
113
|
-
message_queue: Thread::Queue,
|
114
|
-
range: T.nilable(T::Range[Integer]),
|
115
|
-
).void
|
116
|
-
end
|
117
|
-
def initialize(dispatcher, message_queue, range: nil)
|
118
|
-
super(dispatcher, message_queue)
|
110
|
+
sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void }
|
111
|
+
def initialize(dispatcher, range: nil)
|
112
|
+
super(dispatcher)
|
119
113
|
|
120
114
|
@_response = T.let([], ResponseType)
|
121
115
|
@range = range
|
122
116
|
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
123
117
|
@current_scope = T.let(ParameterScope.new, ParameterScope)
|
118
|
+
@inside_regex_capture = T.let(false, T::Boolean)
|
124
119
|
|
125
120
|
dispatcher.register(
|
126
121
|
self,
|
@@ -152,6 +147,8 @@ module RubyLsp
|
|
152
147
|
:on_local_variable_or_write_node_enter,
|
153
148
|
:on_local_variable_target_node_enter,
|
154
149
|
:on_block_local_variable_node_enter,
|
150
|
+
:on_match_write_node_enter,
|
151
|
+
:on_match_write_node_leave,
|
155
152
|
)
|
156
153
|
end
|
157
154
|
|
@@ -165,14 +162,28 @@ module RubyLsp
|
|
165
162
|
# We can't push a semantic token for [] and []= because the argument inside the brackets is a part of
|
166
163
|
# the message_loc
|
167
164
|
return if message.start_with?("[") && (message.end_with?("]") || message.end_with?("]="))
|
168
|
-
|
169
|
-
return process_regexp_locals(node) if message == "=~"
|
165
|
+
return if message == "=~"
|
170
166
|
return if special_method?(message)
|
171
167
|
|
172
168
|
type = Support::Sorbet.annotation?(node) ? :type : :method
|
173
169
|
add_token(T.must(node.message_loc), type)
|
174
170
|
end
|
175
171
|
|
172
|
+
sig { params(node: Prism::MatchWriteNode).void }
|
173
|
+
def on_match_write_node_enter(node)
|
174
|
+
call = node.call
|
175
|
+
|
176
|
+
if call.message == "=~"
|
177
|
+
@inside_regex_capture = true
|
178
|
+
process_regexp_locals(call)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
sig { params(node: Prism::MatchWriteNode).void }
|
183
|
+
def on_match_write_node_leave(node)
|
184
|
+
@inside_regex_capture = true if node.call.message == "=~"
|
185
|
+
end
|
186
|
+
|
176
187
|
sig { params(node: Prism::ConstantReadNode).void }
|
177
188
|
def on_constant_read_node_enter(node)
|
178
189
|
return unless visible?(node, @range)
|
@@ -359,6 +370,12 @@ module RubyLsp
|
|
359
370
|
|
360
371
|
sig { params(node: Prism::LocalVariableTargetNode).void }
|
361
372
|
def on_local_variable_target_node_enter(node)
|
373
|
+
# If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
|
374
|
+
# Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
|
375
|
+
# the entire regex as a local variable. We process these captures in process_regexp_locals instead and then
|
376
|
+
# prevent pushing local variable target tokens. See https://github.com/ruby/prism/issues/1912
|
377
|
+
return if @inside_regex_capture
|
378
|
+
|
362
379
|
return unless visible?(node, @range)
|
363
380
|
|
364
381
|
add_token(node.location, @current_scope.type_for(node.name))
|
@@ -19,30 +19,23 @@ module RubyLsp
|
|
19
19
|
T::Hash[Symbol, Integer],
|
20
20
|
)
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
# TODO: avoid passing document once we have alternative ways to get at
|
23
|
+
# encoding and file source
|
24
|
+
sig { params(document: Document, offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
|
25
|
+
def initialize(document, offense, uri)
|
26
|
+
@document = document
|
24
27
|
@offense = offense
|
25
28
|
@uri = uri
|
26
29
|
end
|
27
30
|
|
28
|
-
sig { returns(Interface::CodeAction) }
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
|
37
|
-
uri: @uri.to_s,
|
38
|
-
version: nil,
|
39
|
-
),
|
40
|
-
edits: @offense.correctable? ? offense_replacements : [],
|
41
|
-
),
|
42
|
-
],
|
43
|
-
),
|
44
|
-
is_preferred: true,
|
45
|
-
)
|
31
|
+
sig { returns(T::Array[Interface::CodeAction]) }
|
32
|
+
def to_lsp_code_actions
|
33
|
+
code_actions = []
|
34
|
+
|
35
|
+
code_actions << autocorrect_action if @offense.correctable?
|
36
|
+
code_actions << disable_line_action
|
37
|
+
|
38
|
+
code_actions
|
46
39
|
end
|
47
40
|
|
48
41
|
sig { returns(Interface::Diagnostic) }
|
@@ -65,7 +58,7 @@ module RubyLsp
|
|
65
58
|
),
|
66
59
|
data: {
|
67
60
|
correctable: @offense.correctable?,
|
68
|
-
|
61
|
+
code_actions: to_lsp_code_actions,
|
69
62
|
},
|
70
63
|
)
|
71
64
|
end
|
@@ -90,6 +83,26 @@ module RubyLsp
|
|
90
83
|
Interface::CodeDescription.new(href: doc_url) if doc_url
|
91
84
|
end
|
92
85
|
|
86
|
+
sig { returns(Interface::CodeAction) }
|
87
|
+
def autocorrect_action
|
88
|
+
Interface::CodeAction.new(
|
89
|
+
title: "Autocorrect #{@offense.cop_name}",
|
90
|
+
kind: Constant::CodeActionKind::QUICK_FIX,
|
91
|
+
edit: Interface::WorkspaceEdit.new(
|
92
|
+
document_changes: [
|
93
|
+
Interface::TextDocumentEdit.new(
|
94
|
+
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
|
95
|
+
uri: @uri.to_s,
|
96
|
+
version: nil,
|
97
|
+
),
|
98
|
+
edits: @offense.correctable? ? offense_replacements : [],
|
99
|
+
),
|
100
|
+
],
|
101
|
+
),
|
102
|
+
is_preferred: true,
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
93
106
|
sig { returns(T::Array[Interface::TextEdit]) }
|
94
107
|
def offense_replacements
|
95
108
|
@offense.corrector.as_replacements.map do |range, replacement|
|
@@ -102,6 +115,64 @@ module RubyLsp
|
|
102
115
|
)
|
103
116
|
end
|
104
117
|
end
|
118
|
+
|
119
|
+
sig { returns(Interface::CodeAction) }
|
120
|
+
def disable_line_action
|
121
|
+
Interface::CodeAction.new(
|
122
|
+
title: "Disable #{@offense.cop_name} for this line",
|
123
|
+
kind: Constant::CodeActionKind::QUICK_FIX,
|
124
|
+
edit: Interface::WorkspaceEdit.new(
|
125
|
+
document_changes: [
|
126
|
+
Interface::TextDocumentEdit.new(
|
127
|
+
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(
|
128
|
+
uri: @uri.to_s,
|
129
|
+
version: nil,
|
130
|
+
),
|
131
|
+
edits: line_disable_comment,
|
132
|
+
),
|
133
|
+
],
|
134
|
+
),
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
sig { returns(T::Array[Interface::TextEdit]) }
|
139
|
+
def line_disable_comment
|
140
|
+
new_text = if @offense.source_line.include?(" # rubocop:disable ")
|
141
|
+
",#{@offense.cop_name}"
|
142
|
+
else
|
143
|
+
" # rubocop:disable #{@offense.cop_name}"
|
144
|
+
end
|
145
|
+
|
146
|
+
eol = Interface::Position.new(
|
147
|
+
line: @offense.line - 1,
|
148
|
+
character: length_of_line(@offense.source_line),
|
149
|
+
)
|
150
|
+
|
151
|
+
# TODO: fails for multiline strings - may be preferable to use block
|
152
|
+
# comments to disable some offenses
|
153
|
+
inline_comment = Interface::TextEdit.new(
|
154
|
+
range: Interface::Range.new(start: eol, end: eol),
|
155
|
+
new_text: new_text,
|
156
|
+
)
|
157
|
+
|
158
|
+
[inline_comment]
|
159
|
+
end
|
160
|
+
|
161
|
+
sig { params(line: String).returns(Integer) }
|
162
|
+
def length_of_line(line)
|
163
|
+
if @document.encoding == Constant::PositionEncodingKind::UTF16
|
164
|
+
line_length = 0
|
165
|
+
line.codepoints.each do |codepoint|
|
166
|
+
line_length += 1
|
167
|
+
if codepoint > RubyLsp::Document::Scanner::SURROGATE_PAIR_START
|
168
|
+
line_length += 1
|
169
|
+
end
|
170
|
+
end
|
171
|
+
line_length
|
172
|
+
else
|
173
|
+
line.length
|
174
|
+
end
|
175
|
+
end
|
105
176
|
end
|
106
177
|
end
|
107
178
|
end
|
@@ -34,15 +34,10 @@ module RubyLsp
|
|
34
34
|
|
35
35
|
sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
|
36
36
|
def run(uri, document)
|
37
|
-
|
38
|
-
|
39
|
-
return if @options.ignore_files.any? { |pattern| File.fnmatch(pattern, relative_path) }
|
37
|
+
path = uri.to_standardized_path
|
38
|
+
return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
|
40
39
|
|
41
|
-
SyntaxTree.format(
|
42
|
-
document.source,
|
43
|
-
@options.print_width,
|
44
|
-
options: @options.formatter_options,
|
45
|
-
)
|
40
|
+
SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
|
46
41
|
end
|
47
42
|
end
|
48
43
|
end
|
@@ -75,6 +75,8 @@ module RubyLsp
|
|
75
75
|
Constant::SymbolKind::CONSTANT
|
76
76
|
when RubyIndexer::Entry::Method
|
77
77
|
entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
78
|
+
when RubyIndexer::Entry::Accessor
|
79
|
+
Constant::SymbolKind::PROPERTY
|
78
80
|
end
|
79
81
|
end
|
80
82
|
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -17,6 +17,12 @@ module RubyLsp
|
|
17
17
|
sig { returns(T::Boolean) }
|
18
18
|
attr_accessor :experimental_features
|
19
19
|
|
20
|
+
sig { returns(URI::Generic) }
|
21
|
+
attr_accessor :workspace_uri
|
22
|
+
|
23
|
+
sig { returns(T::Hash[Symbol, RequestConfig]) }
|
24
|
+
attr_accessor :features_configuration
|
25
|
+
|
20
26
|
sig { void }
|
21
27
|
def initialize
|
22
28
|
@state = T.let({}, T::Hash[String, Document])
|
@@ -24,6 +30,21 @@ module RubyLsp
|
|
24
30
|
@formatter = T.let("auto", String)
|
25
31
|
@supports_progress = T.let(true, T::Boolean)
|
26
32
|
@experimental_features = T.let(false, T::Boolean)
|
33
|
+
@workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
34
|
+
@features_configuration = T.let(
|
35
|
+
{
|
36
|
+
codeLens: RequestConfig.new({
|
37
|
+
enableAll: false,
|
38
|
+
gemfileLinks: true,
|
39
|
+
}),
|
40
|
+
inlayHint: RequestConfig.new({
|
41
|
+
enableAll: false,
|
42
|
+
implicitRescue: false,
|
43
|
+
implicitHashValue: false,
|
44
|
+
}),
|
45
|
+
},
|
46
|
+
T::Hash[Symbol, RequestConfig],
|
47
|
+
)
|
27
48
|
end
|
28
49
|
|
29
50
|
sig { params(uri: URI::Generic).returns(Document) }
|
data/lib/ruby_lsp/utils.rb
CHANGED
@@ -4,10 +4,6 @@
|
|
4
4
|
module RubyLsp
|
5
5
|
# Used to indicate that a request shouldn't return a response
|
6
6
|
VOID = T.let(Object.new.freeze, Object)
|
7
|
-
|
8
|
-
# This freeze is not redundant since the interpolated string is mutable
|
9
|
-
WORKSPACE_URI = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
10
|
-
|
11
7
|
BUNDLE_PATH = T.let(
|
12
8
|
begin
|
13
9
|
Bundler.bundle_path.to_s
|
@@ -78,4 +74,22 @@ module RubyLsp
|
|
78
74
|
@cancelled = true
|
79
75
|
end
|
80
76
|
end
|
77
|
+
|
78
|
+
# A request configuration, to turn on/off features
|
79
|
+
class RequestConfig
|
80
|
+
extend T::Sig
|
81
|
+
|
82
|
+
sig { returns(T::Hash[Symbol, T::Boolean]) }
|
83
|
+
attr_accessor :configuration
|
84
|
+
|
85
|
+
sig { params(configuration: T::Hash[Symbol, T::Boolean]).void }
|
86
|
+
def initialize(configuration)
|
87
|
+
@configuration = configuration
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { params(feature: Symbol).returns(T.nilable(T::Boolean)) }
|
91
|
+
def enabled?(feature)
|
92
|
+
@configuration[:enableAll] || @configuration[feature]
|
93
|
+
end
|
94
|
+
end
|
81
95
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -30,20 +30,20 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.18.0
|
34
34
|
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: '0.
|
36
|
+
version: '0.19'
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.
|
43
|
+
version: 0.18.0
|
44
44
|
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '0.
|
46
|
+
version: '0.19'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: sorbet-runtime
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
157
|
- !ruby/object:Gem::Version
|
158
158
|
version: '0'
|
159
159
|
requirements: []
|
160
|
-
rubygems_version: 3.4.
|
160
|
+
rubygems_version: 3.4.22
|
161
161
|
signing_key:
|
162
162
|
specification_version: 4
|
163
163
|
summary: An opinionated language server for Ruby
|