ruby-lsp 0.23.11 → 0.23.16
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/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp-launcher +20 -11
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +123 -169
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -7
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +92 -202
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +116 -222
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +18 -19
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +22 -45
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +47 -61
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +48 -7
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +139 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
- data/lib/ruby_indexer/test/method_test.rb +143 -117
- data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +65 -71
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +44 -71
- data/lib/ruby_lsp/base_server.rb +29 -32
- data/lib/ruby_lsp/client_capabilities.rb +10 -12
- data/lib/ruby_lsp/document.rb +39 -45
- data/lib/ruby_lsp/erb_document.rb +36 -40
- data/lib/ruby_lsp/global_state.rb +52 -57
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +60 -66
- data/lib/ruby_lsp/listeners/definition.rb +38 -52
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +46 -63
- data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
- data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
- data/lib/ruby_lsp/listeners/hover.rb +83 -102
- data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +11 -26
- data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
- data/lib/ruby_lsp/listeners/test_style.rb +160 -88
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +8 -6
- data/lib/ruby_lsp/requests/code_action_resolve.rb +24 -25
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +6 -17
- data/lib/ruby_lsp/requests/completion.rb +7 -20
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +8 -17
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +18 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
- data/lib/ruby_lsp/requests/document_link.rb +6 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
- data/lib/ruby_lsp/requests/formatting.rb +6 -9
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
- data/lib/ruby_lsp/requests/hover.rb +10 -20
- data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +8 -37
- data/lib/ruby_lsp/requests/rename.rb +19 -42
- data/lib/ruby_lsp/requests/request.rb +7 -19
- data/lib/ruby_lsp/requests/selection_ranges.rb +6 -6
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
- data/lib/ruby_lsp/requests/signature_help.rb +8 -26
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +16 -51
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +11 -14
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +10 -14
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
- data/lib/ruby_lsp/response_builders/document_symbol.rb +13 -18
- data/lib/ruby_lsp/response_builders/hover.rb +11 -14
- data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +60 -88
- data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
- data/lib/ruby_lsp/response_builders/test_collection.rb +6 -10
- data/lib/ruby_lsp/ruby_document.rb +24 -62
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +147 -79
- data/lib/ruby_lsp/setup_bundler.rb +65 -60
- data/lib/ruby_lsp/static_docs.rb +11 -7
- data/lib/ruby_lsp/store.rb +24 -42
- data/lib/ruby_lsp/test_helper.rb +2 -12
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +164 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +49 -83
- metadata +9 -3
@@ -3,93 +3,180 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
module Listeners
|
6
|
-
class TestStyle
|
7
|
-
|
6
|
+
class TestStyle < TestDiscovery
|
7
|
+
class << self
|
8
|
+
# Resolves the minimal set of commands required to execute the requested tests
|
9
|
+
#: (Array[Hash[Symbol, untyped]]) -> Array[String]
|
10
|
+
def resolve_test_commands(items)
|
11
|
+
# A nested hash of file_path => test_group => { tags: [], examples: [test_example] } to ensure we build the
|
12
|
+
# minimum amount of commands needed to execute the requested tests. This is only used for specific examples
|
13
|
+
# where we will need more complex regexes to execute it all at the same time
|
14
|
+
aggregated_tests = Hash.new do |hash, key|
|
15
|
+
hash[key] = Hash.new do |inner_h, inner_k|
|
16
|
+
inner_h[inner_k] = { tags: Set.new, examples: [] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Full files are paths that should be executed as a whole e.g.: an entire test file or directory
|
21
|
+
full_files = []
|
22
|
+
queue = items.dup
|
23
|
+
|
24
|
+
until queue.empty?
|
25
|
+
item = queue.shift #: as !nil
|
26
|
+
tags = Set.new(item[:tags])
|
27
|
+
next unless tags.include?("framework:minitest") || tags.include?("framework:test_unit")
|
28
|
+
|
29
|
+
children = item[:children]
|
30
|
+
uri = URI(item[:uri])
|
31
|
+
path = uri.full_path
|
32
|
+
next unless path
|
33
|
+
|
34
|
+
if tags.include?("test_dir")
|
35
|
+
if children.empty?
|
36
|
+
full_files.concat(Dir.glob(
|
37
|
+
"#{path}/**/{*_test,test_*}.rb",
|
38
|
+
File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME,
|
39
|
+
))
|
40
|
+
end
|
41
|
+
elsif tags.include?("test_file")
|
42
|
+
full_files << path if children.empty?
|
43
|
+
elsif tags.include?("test_group")
|
44
|
+
# If all of the children of the current test group are other groups, then there's no need to add it to the
|
45
|
+
# aggregated examples
|
46
|
+
unless children.any? && children.all? { |child| child[:tags].include?("test_group") }
|
47
|
+
aggregated_tests[path][item[:label]] = { tags: tags, examples: [] }
|
48
|
+
end
|
49
|
+
else
|
50
|
+
class_name, method_name = item[:id].split("#")
|
51
|
+
aggregated_tests[path][class_name][:examples] << method_name
|
52
|
+
aggregated_tests[path][class_name][:tags].merge(tags)
|
53
|
+
end
|
54
|
+
|
55
|
+
queue.concat(children) unless children.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
commands = []
|
59
|
+
|
60
|
+
aggregated_tests.each do |file_path, groups_and_examples|
|
61
|
+
# Separate groups into Minitest and Test Unit. You can have both frameworks in the same file, but you cannot
|
62
|
+
# have a group belongs to both at the same time
|
63
|
+
minitest_groups, test_unit_groups = groups_and_examples.partition do |_, info|
|
64
|
+
info[:tags].include?("framework:minitest")
|
65
|
+
end
|
66
|
+
|
67
|
+
if minitest_groups.any?
|
68
|
+
commands << handle_minitest_groups(file_path, minitest_groups)
|
69
|
+
end
|
70
|
+
|
71
|
+
if test_unit_groups.any?
|
72
|
+
commands.concat(handle_test_unit_groups(file_path, test_unit_groups))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
unless full_files.empty?
|
77
|
+
commands << "#{BASE_COMMAND} -Itest -e \"ARGV.each { |f| require f }\" #{full_files.join(" ")}"
|
78
|
+
end
|
79
|
+
|
80
|
+
commands
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
#: (String, Hash[String, Hash[Symbol, untyped]]) -> String
|
86
|
+
def handle_minitest_groups(file_path, groups_and_examples)
|
87
|
+
regexes = groups_and_examples.flat_map do |group, info|
|
88
|
+
examples = info[:examples]
|
89
|
+
group_regex = Shellwords.escape(group).gsub(
|
90
|
+
Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
|
91
|
+
".*",
|
92
|
+
)
|
93
|
+
if examples.empty?
|
94
|
+
"^#{group_regex}(#|::)"
|
95
|
+
elsif examples.length == 1
|
96
|
+
"^#{group_regex}##{examples[0]}\\$"
|
97
|
+
else
|
98
|
+
"^#{group_regex}#(#{examples.join("|")})\\$"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
regex = if regexes.length == 1
|
103
|
+
regexes[0]
|
104
|
+
else
|
105
|
+
"(#{regexes.join("|")})"
|
106
|
+
end
|
107
|
+
|
108
|
+
"#{BASE_COMMAND} -Itest #{file_path} --name \"/#{regex}/\""
|
109
|
+
end
|
110
|
+
|
111
|
+
#: (String, Hash[String, Hash[Symbol, untyped]]) -> Array[String]
|
112
|
+
def handle_test_unit_groups(file_path, groups_and_examples)
|
113
|
+
groups_and_examples.map do |group, info|
|
114
|
+
examples = info[:examples]
|
115
|
+
group_regex = Shellwords.escape(group).gsub(
|
116
|
+
Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
|
117
|
+
".*",
|
118
|
+
)
|
119
|
+
command = +"#{BASE_COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex}\\$/\""
|
120
|
+
|
121
|
+
unless examples.empty?
|
122
|
+
command << if examples.length == 1
|
123
|
+
" --name \"/#{examples[0]}\\$/\""
|
124
|
+
else
|
125
|
+
" --name \"/(#{examples.join("|")})\\$/\""
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
command
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
8
134
|
include Requests::Support::Common
|
9
135
|
|
136
|
+
MINITEST_REPORTER_PATH = File.expand_path("../test_reporters/minitest_reporter.rb", __dir__) #: String
|
137
|
+
TEST_UNIT_REPORTER_PATH = File.expand_path("../test_reporters/test_unit_reporter.rb", __dir__) #: String
|
10
138
|
ACCESS_MODIFIERS = [:public, :private, :protected].freeze
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
).void
|
20
|
-
end
|
139
|
+
BASE_COMMAND = begin
|
140
|
+
Bundler.with_original_env { Bundler.default_lockfile }
|
141
|
+
"bundle exec ruby"
|
142
|
+
rescue Bundler::GemfileNotFound
|
143
|
+
"ruby"
|
144
|
+
end #: String
|
145
|
+
|
146
|
+
#: (ResponseBuilders::TestCollection response_builder, GlobalState global_state, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
|
21
147
|
def initialize(response_builder, global_state, dispatcher, uri)
|
22
|
-
|
23
|
-
|
24
|
-
@
|
25
|
-
@visibility_stack = T.let([:public], T::Array[Symbol])
|
26
|
-
@nesting = T.let([], T::Array[String])
|
148
|
+
super
|
149
|
+
|
150
|
+
@framework = :minitest #: Symbol
|
27
151
|
|
28
152
|
dispatcher.register(
|
29
153
|
self,
|
154
|
+
# Common handlers registered in parent class
|
30
155
|
:on_class_node_enter,
|
31
|
-
:on_class_node_leave,
|
32
|
-
:on_module_node_enter,
|
33
|
-
:on_module_node_leave,
|
34
156
|
:on_def_node_enter,
|
35
157
|
:on_call_node_enter,
|
36
158
|
:on_call_node_leave,
|
37
159
|
)
|
38
160
|
end
|
39
161
|
|
40
|
-
|
162
|
+
#: (Prism::ClassNode node) -> void
|
41
163
|
def on_class_node_enter(node)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
if attached_ancestors.include?("Test::Unit::TestCase") ||
|
57
|
-
non_declarative_minitest?(attached_ancestors, fully_qualified_name)
|
58
|
-
|
59
|
-
@response_builder.add(Requests::Support::TestItem.new(
|
60
|
-
fully_qualified_name,
|
61
|
-
fully_qualified_name,
|
62
|
-
@uri,
|
63
|
-
range_from_node(node),
|
64
|
-
))
|
164
|
+
with_test_ancestor_tracking(node) do |name, ancestors|
|
165
|
+
@framework = :test_unit if ancestors.include?("Test::Unit::TestCase")
|
166
|
+
|
167
|
+
if @framework == :test_unit || non_declarative_minitest?(ancestors, name)
|
168
|
+
@response_builder.add(Requests::Support::TestItem.new(
|
169
|
+
name,
|
170
|
+
name,
|
171
|
+
@uri,
|
172
|
+
range_from_node(node),
|
173
|
+
framework: @framework,
|
174
|
+
))
|
175
|
+
end
|
65
176
|
end
|
66
|
-
|
67
|
-
@nesting << name
|
68
177
|
end
|
69
178
|
|
70
|
-
|
71
|
-
def on_module_node_enter(node)
|
72
|
-
@visibility_stack << :public
|
73
|
-
|
74
|
-
name = constant_name(node.constant_path)
|
75
|
-
name ||= name_with_dynamic_reference(node.constant_path)
|
76
|
-
|
77
|
-
@nesting << name
|
78
|
-
end
|
79
|
-
|
80
|
-
sig { params(node: Prism::ModuleNode).void }
|
81
|
-
def on_module_node_leave(node)
|
82
|
-
@visibility_stack.pop
|
83
|
-
@nesting.pop
|
84
|
-
end
|
85
|
-
|
86
|
-
sig { params(node: Prism::ClassNode).void }
|
87
|
-
def on_class_node_leave(node)
|
88
|
-
@visibility_stack.pop
|
89
|
-
@nesting.pop
|
90
|
-
end
|
91
|
-
|
92
|
-
sig { params(node: Prism::DefNode).void }
|
179
|
+
#: (Prism::DefNode node) -> void
|
93
180
|
def on_def_node_enter(node)
|
94
181
|
return if @visibility_stack.last != :public
|
95
182
|
|
@@ -109,10 +196,11 @@ module RubyLsp
|
|
109
196
|
name,
|
110
197
|
@uri,
|
111
198
|
range_from_node(node),
|
199
|
+
framework: @framework,
|
112
200
|
))
|
113
201
|
end
|
114
202
|
|
115
|
-
|
203
|
+
#: (Prism::CallNode node) -> void
|
116
204
|
def on_call_node_enter(node)
|
117
205
|
name = node.name
|
118
206
|
return unless ACCESS_MODIFIERS.include?(name)
|
@@ -120,7 +208,7 @@ module RubyLsp
|
|
120
208
|
@visibility_stack << name
|
121
209
|
end
|
122
210
|
|
123
|
-
|
211
|
+
#: (Prism::CallNode node) -> void
|
124
212
|
def on_call_node_leave(node)
|
125
213
|
name = node.name
|
126
214
|
return unless ACCESS_MODIFIERS.include?(name)
|
@@ -131,7 +219,7 @@ module RubyLsp
|
|
131
219
|
|
132
220
|
private
|
133
221
|
|
134
|
-
|
222
|
+
#: (Array[String] attached_ancestors, String fully_qualified_name) -> bool
|
135
223
|
def non_declarative_minitest?(attached_ancestors, fully_qualified_name)
|
136
224
|
return false unless attached_ancestors.include?("Minitest::Test")
|
137
225
|
|
@@ -143,22 +231,6 @@ module RubyLsp
|
|
143
231
|
rescue RubyIndexer::Index::NonExistingNamespaceError
|
144
232
|
true
|
145
233
|
end
|
146
|
-
|
147
|
-
sig do
|
148
|
-
params(
|
149
|
-
node: T.any(
|
150
|
-
Prism::ConstantPathNode,
|
151
|
-
Prism::ConstantReadNode,
|
152
|
-
Prism::ConstantPathTargetNode,
|
153
|
-
Prism::CallNode,
|
154
|
-
Prism::MissingNode,
|
155
|
-
),
|
156
|
-
).returns(String)
|
157
|
-
end
|
158
|
-
def name_with_dynamic_reference(node)
|
159
|
-
slice = node.slice
|
160
|
-
slice.gsub(/((?<=::)|^)[a-z]\w*/, DYNAMIC_REFERENCE_MARKER)
|
161
|
-
end
|
162
234
|
end
|
163
235
|
end
|
164
236
|
end
|
@@ -5,36 +5,19 @@ module RubyLsp
|
|
5
5
|
# This class allows listeners to access contextual information about a node in the AST, such as its parent,
|
6
6
|
# its namespace nesting, and the surrounding CallNode (e.g. a method call).
|
7
7
|
class NodeContext
|
8
|
-
|
9
|
-
|
10
|
-
sig { returns(T.nilable(Prism::Node)) }
|
8
|
+
#: Prism::Node?
|
11
9
|
attr_reader :node, :parent
|
12
10
|
|
13
|
-
|
11
|
+
#: Array[String]
|
14
12
|
attr_reader :nesting
|
15
13
|
|
16
|
-
|
14
|
+
#: Prism::CallNode?
|
17
15
|
attr_reader :call_node
|
18
16
|
|
19
|
-
|
17
|
+
#: String?
|
20
18
|
attr_reader :surrounding_method
|
21
19
|
|
22
|
-
|
23
|
-
params(
|
24
|
-
node: T.nilable(Prism::Node),
|
25
|
-
parent: T.nilable(Prism::Node),
|
26
|
-
nesting_nodes: T::Array[T.any(
|
27
|
-
Prism::ClassNode,
|
28
|
-
Prism::ModuleNode,
|
29
|
-
Prism::SingletonClassNode,
|
30
|
-
Prism::DefNode,
|
31
|
-
Prism::BlockNode,
|
32
|
-
Prism::LambdaNode,
|
33
|
-
Prism::ProgramNode,
|
34
|
-
)],
|
35
|
-
call_node: T.nilable(Prism::CallNode),
|
36
|
-
).void
|
37
|
-
end
|
20
|
+
#: (Prism::Node? node, Prism::Node? parent, Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)] nesting_nodes, Prism::CallNode? call_node) -> void
|
38
21
|
def initialize(node, parent, nesting_nodes, call_node)
|
39
22
|
@node = node
|
40
23
|
@parent = parent
|
@@ -42,16 +25,16 @@ module RubyLsp
|
|
42
25
|
@call_node = call_node
|
43
26
|
|
44
27
|
nesting, surrounding_method = handle_nesting_nodes(nesting_nodes)
|
45
|
-
@nesting =
|
46
|
-
@surrounding_method =
|
28
|
+
@nesting = nesting #: Array[String]
|
29
|
+
@surrounding_method = surrounding_method #: String?
|
47
30
|
end
|
48
31
|
|
49
|
-
|
32
|
+
#: -> String
|
50
33
|
def fully_qualified_name
|
51
|
-
@fully_qualified_name ||=
|
34
|
+
@fully_qualified_name ||= @nesting.join("::") #: String?
|
52
35
|
end
|
53
36
|
|
54
|
-
|
37
|
+
#: -> Array[Symbol]
|
55
38
|
def locals_for_scope
|
56
39
|
locals = []
|
57
40
|
|
@@ -69,20 +52,10 @@ module RubyLsp
|
|
69
52
|
|
70
53
|
private
|
71
54
|
|
72
|
-
|
73
|
-
params(nodes: T::Array[T.any(
|
74
|
-
Prism::ClassNode,
|
75
|
-
Prism::ModuleNode,
|
76
|
-
Prism::SingletonClassNode,
|
77
|
-
Prism::DefNode,
|
78
|
-
Prism::BlockNode,
|
79
|
-
Prism::LambdaNode,
|
80
|
-
Prism::ProgramNode,
|
81
|
-
)]).returns([T::Array[String], T.nilable(String)])
|
82
|
-
end
|
55
|
+
#: (Array[(Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode | Prism::DefNode | Prism::BlockNode | Prism::LambdaNode | Prism::ProgramNode)] nodes) -> [Array[String], String?]
|
83
56
|
def handle_nesting_nodes(nodes)
|
84
57
|
nesting = []
|
85
|
-
surrounding_method =
|
58
|
+
surrounding_method = nil #: String?
|
86
59
|
|
87
60
|
@nesting_nodes.each do |node|
|
88
61
|
case node
|
@@ -3,18 +3,18 @@
|
|
3
3
|
|
4
4
|
module RubyLsp
|
5
5
|
class RBSDocument < Document
|
6
|
-
extend T::Sig
|
7
6
|
extend T::Generic
|
8
7
|
|
9
8
|
ParseResultType = type_member { { fixed: T::Array[RBS::AST::Declarations::Base] } }
|
10
9
|
|
11
|
-
|
10
|
+
#: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
|
12
11
|
def initialize(source:, version:, uri:, global_state:)
|
13
|
-
@syntax_error =
|
12
|
+
@syntax_error = false #: bool
|
14
13
|
super
|
15
14
|
end
|
16
15
|
|
17
|
-
|
16
|
+
# @override
|
17
|
+
#: -> bool
|
18
18
|
def parse!
|
19
19
|
return false unless @needs_parsing
|
20
20
|
|
@@ -29,12 +29,14 @@ module RubyLsp
|
|
29
29
|
true
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
# @override
|
33
|
+
#: -> bool
|
33
34
|
def syntax_error?
|
34
35
|
@syntax_error
|
35
36
|
end
|
36
37
|
|
37
|
-
|
38
|
+
# @override
|
39
|
+
#: -> LanguageId
|
38
40
|
def language_id
|
39
41
|
LanguageId::RBS
|
40
42
|
end
|
@@ -7,7 +7,6 @@ module RubyLsp
|
|
7
7
|
# request is used to to resolve the edit field for a given code action, if it is not already provided in the
|
8
8
|
# textDocument/codeAction response. We can use it for scenarios that require more computation such as refactoring.
|
9
9
|
class CodeActionResolve < Request
|
10
|
-
extend T::Sig
|
11
10
|
include Support::Common
|
12
11
|
|
13
12
|
NEW_VARIABLE_NAME = "new_variable"
|
@@ -23,7 +22,7 @@ module RubyLsp
|
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
|
25
|
+
#: (RubyDocument document, GlobalState global_state, Hash[Symbol, untyped] code_action) -> void
|
27
26
|
def initialize(document, global_state, code_action)
|
28
27
|
super()
|
29
28
|
@document = document
|
@@ -31,7 +30,8 @@ module RubyLsp
|
|
31
30
|
@code_action = code_action
|
32
31
|
end
|
33
32
|
|
34
|
-
|
33
|
+
# @override
|
34
|
+
#: -> (Interface::CodeAction | Error)
|
35
35
|
def perform
|
36
36
|
return Error::EmptySelection if @document.source.empty?
|
37
37
|
|
@@ -53,7 +53,7 @@ module RubyLsp
|
|
53
53
|
|
54
54
|
private
|
55
55
|
|
56
|
-
|
56
|
+
#: -> (Interface::CodeAction | Error)
|
57
57
|
def switch_block_style
|
58
58
|
source_range = @code_action.dig(:data, :range)
|
59
59
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
@@ -91,13 +91,13 @@ module RubyLsp
|
|
91
91
|
)
|
92
92
|
end
|
93
93
|
|
94
|
-
|
94
|
+
#: -> (Interface::CodeAction | Error)
|
95
95
|
def refactor_variable
|
96
96
|
source_range = @code_action.dig(:data, :range)
|
97
97
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
98
98
|
|
99
99
|
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
100
|
-
extracted_source =
|
100
|
+
extracted_source = @document.source[start_index...end_index] #: as !nil
|
101
101
|
|
102
102
|
# Find the closest statements node, so that we place the refactor in a valid position
|
103
103
|
node_context = RubyDocument
|
@@ -115,10 +115,10 @@ module RubyLsp
|
|
115
115
|
|
116
116
|
# Find the node with the end line closest to the requested position, so that we can place the refactor
|
117
117
|
# immediately after that closest node
|
118
|
-
closest_node =
|
118
|
+
closest_node = closest_statements.child_nodes.compact.min_by do |node|
|
119
119
|
distance = source_range.dig(:start, :line) - (node.location.end_line - 1)
|
120
120
|
distance <= 0 ? Float::INFINITY : distance
|
121
|
-
end
|
121
|
+
end #: as !nil
|
122
122
|
|
123
123
|
return Error::InvalidTargetRange if closest_node.is_a?(Prism::MissingNode)
|
124
124
|
|
@@ -153,7 +153,8 @@ module RubyLsp
|
|
153
153
|
indentation_line = lines[indentation_line_number]
|
154
154
|
return Error::InvalidTargetRange unless indentation_line
|
155
155
|
|
156
|
-
indentation =
|
156
|
+
indentation = indentation_line[/\A */] #: as !nil
|
157
|
+
.size
|
157
158
|
|
158
159
|
target_range = {
|
159
160
|
start: { line: target_line, character: indentation },
|
@@ -189,13 +190,13 @@ module RubyLsp
|
|
189
190
|
)
|
190
191
|
end
|
191
192
|
|
192
|
-
|
193
|
+
#: -> (Interface::CodeAction | Error)
|
193
194
|
def refactor_method
|
194
195
|
source_range = @code_action.dig(:data, :range)
|
195
196
|
return Error::EmptySelection if source_range[:start] == source_range[:end]
|
196
197
|
|
197
198
|
start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
|
198
|
-
extracted_source =
|
199
|
+
extracted_source = @document.source[start_index...end_index] #: as !nil
|
199
200
|
|
200
201
|
# Find the closest method declaration node, so that we place the refactor in a valid position
|
201
202
|
node_context = RubyDocument.locate(
|
@@ -261,7 +262,7 @@ module RubyLsp
|
|
261
262
|
)
|
262
263
|
end
|
263
264
|
|
264
|
-
|
265
|
+
#: (Hash[Symbol, untyped] range, String new_text) -> Interface::TextEdit
|
265
266
|
def create_text_edit(range, new_text)
|
266
267
|
Interface::TextEdit.new(
|
267
268
|
range: Interface::Range.new(
|
@@ -272,7 +273,7 @@ module RubyLsp
|
|
272
273
|
)
|
273
274
|
end
|
274
275
|
|
275
|
-
|
276
|
+
#: (Prism::BlockNode node, String? indentation) -> String
|
276
277
|
def recursively_switch_nested_block_styles(node, indentation)
|
277
278
|
parameters = node.parameters
|
278
279
|
body = node.body
|
@@ -301,7 +302,7 @@ module RubyLsp
|
|
301
302
|
source
|
302
303
|
end
|
303
304
|
|
304
|
-
|
305
|
+
#: (Prism::Node body, String? indentation) -> String
|
305
306
|
def switch_block_body(body, indentation)
|
306
307
|
# Check if there are any nested blocks inside of the current block
|
307
308
|
body_loc = body.location
|
@@ -330,7 +331,7 @@ module RubyLsp
|
|
330
331
|
indentation ? body_content.gsub(";", "\n") : "#{body_content.gsub("\n", ";")} "
|
331
332
|
end
|
332
333
|
|
333
|
-
|
334
|
+
#: -> (Interface::CodeAction | Error)
|
334
335
|
def create_attribute_accessor
|
335
336
|
source_range = @code_action.dig(:data, :range)
|
336
337
|
|
@@ -379,16 +380,14 @@ module RubyLsp
|
|
379
380
|
|
380
381
|
attribute_name = node.name[1..]
|
381
382
|
indentation = " " * (closest_node.location.start_column + 2)
|
382
|
-
attribute_accessor_source =
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
end,
|
391
|
-
)
|
383
|
+
attribute_accessor_source = case @code_action[:title]
|
384
|
+
when CodeActions::CREATE_ATTRIBUTE_READER
|
385
|
+
"#{indentation}attr_reader :#{attribute_name}\n\n"
|
386
|
+
when CodeActions::CREATE_ATTRIBUTE_WRITER
|
387
|
+
"#{indentation}attr_writer :#{attribute_name}\n\n"
|
388
|
+
when CodeActions::CREATE_ATTRIBUTE_ACCESSOR
|
389
|
+
"#{indentation}attr_accessor :#{attribute_name}\n\n"
|
390
|
+
end #: as !nil
|
392
391
|
|
393
392
|
target_start_line = closest_node.location.start_line
|
394
393
|
target_range = {
|
@@ -7,8 +7,6 @@ module RubyLsp
|
|
7
7
|
# request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
|
8
8
|
# specific diagnostic.
|
9
9
|
class CodeActions < Request
|
10
|
-
extend T::Sig
|
11
|
-
|
12
10
|
EXTRACT_TO_VARIABLE_TITLE = "Refactor: Extract Variable"
|
13
11
|
EXTRACT_TO_METHOD_TITLE = "Refactor: Extract Method"
|
14
12
|
TOGGLE_BLOCK_STYLE_TITLE = "Refactor: Toggle block style"
|
@@ -16,22 +14,17 @@ module RubyLsp
|
|
16
14
|
CREATE_ATTRIBUTE_WRITER = "Create Attribute Writer"
|
17
15
|
CREATE_ATTRIBUTE_ACCESSOR = "Create Attribute Accessor"
|
18
16
|
|
19
|
-
INSTANCE_VARIABLE_NODES =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
],
|
28
|
-
T::Array[T.class_of(Prism::Node)],
|
29
|
-
)
|
17
|
+
INSTANCE_VARIABLE_NODES = [
|
18
|
+
Prism::InstanceVariableAndWriteNode,
|
19
|
+
Prism::InstanceVariableOperatorWriteNode,
|
20
|
+
Prism::InstanceVariableOrWriteNode,
|
21
|
+
Prism::InstanceVariableReadNode,
|
22
|
+
Prism::InstanceVariableTargetNode,
|
23
|
+
Prism::InstanceVariableWriteNode,
|
24
|
+
] #: Array[singleton(Prism::Node)]
|
30
25
|
|
31
26
|
class << self
|
32
|
-
|
33
|
-
|
34
|
-
sig { returns(Interface::CodeActionRegistrationOptions) }
|
27
|
+
#: -> Interface::CodeActionRegistrationOptions
|
35
28
|
def provider
|
36
29
|
Interface::CodeActionRegistrationOptions.new(
|
37
30
|
document_selector: nil,
|
@@ -40,22 +33,17 @@ module RubyLsp
|
|
40
33
|
end
|
41
34
|
end
|
42
35
|
|
43
|
-
|
44
|
-
params(
|
45
|
-
document: T.any(RubyDocument, ERBDocument),
|
46
|
-
range: T::Hash[Symbol, T.untyped],
|
47
|
-
context: T::Hash[Symbol, T.untyped],
|
48
|
-
).void
|
49
|
-
end
|
36
|
+
#: ((RubyDocument | ERBDocument) document, Hash[Symbol, untyped] range, Hash[Symbol, untyped] context) -> void
|
50
37
|
def initialize(document, range, context)
|
51
38
|
super()
|
52
39
|
@document = document
|
53
|
-
@uri =
|
40
|
+
@uri = document.uri #: URI::Generic
|
54
41
|
@range = range
|
55
42
|
@context = context
|
56
43
|
end
|
57
44
|
|
58
|
-
|
45
|
+
# @override
|
46
|
+
#: -> (Array[Interface::CodeAction] & Object)?
|
59
47
|
def perform
|
60
48
|
diagnostics = @context[:diagnostics]
|
61
49
|
|
@@ -88,7 +76,7 @@ module RubyLsp
|
|
88
76
|
|
89
77
|
private
|
90
78
|
|
91
|
-
|
79
|
+
#: -> Array[Interface::CodeAction]
|
92
80
|
def attribute_actions
|
93
81
|
return [] unless @document.is_a?(RubyDocument)
|
94
82
|
|