ruby-lsp 0.12.5 → 0.13.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/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
|