ruby-lsp 0.26.1 → 0.26.2
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/lib/ruby_indexer/lib/ruby_indexer/index.rb +17 -3
- data/lib/ruby_indexer/test/index_test.rb +40 -0
- data/lib/ruby_lsp/base_server.rb +1 -1
- data/lib/ruby_lsp/listeners/document_highlight.rb +26 -1
- data/lib/ruby_lsp/listeners/spec_style.rb +32 -15
- data/lib/ruby_lsp/listeners/test_discovery.rb +20 -5
- data/lib/ruby_lsp/listeners/test_style.rb +10 -8
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +64 -10
- data/lib/ruby_lsp/requests/support/common.rb +8 -4
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +10 -6
- data/lib/ruby_lsp/server.rb +2 -2
- data/lib/ruby_lsp/setup_bundler.rb +7 -6
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a3c596f1104438061fe9ee2b890b980a7048e2137c10d34783d55defa0f17354
|
|
4
|
+
data.tar.gz: b9f328146d02d36436a48385f1156a3a94ce9f321123220a2316a794ab253695
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 21c6b94baa433cf5c944a96f2ef8829f36ec81b0a093fb1e6fb5c53537698df6adc936f71ff2856bc68411d9ccefbd143cb75ee703d8d2b5a171e940faada191
|
|
7
|
+
data.tar.gz: 7e84f101f485088c24e2afccaaa779f8cfdae3bb91798ec24f30199402bc3bbd5e142ece40a86de7845cb342b31d68be5c953383b0a0b081bf388b0574b25ab9
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.26.
|
|
1
|
+
0.26.2
|
|
@@ -424,7 +424,10 @@ module RubyIndexer
|
|
|
424
424
|
(parts.length - 1).downto(0) do |i|
|
|
425
425
|
current_name = parts[0..i] #: as !nil
|
|
426
426
|
.join("::")
|
|
427
|
-
|
|
427
|
+
|
|
428
|
+
entry = unless seen_names.include?(current_name)
|
|
429
|
+
@entries[current_name]&.first
|
|
430
|
+
end
|
|
428
431
|
|
|
429
432
|
case entry
|
|
430
433
|
when Entry::ConstantAlias
|
|
@@ -904,6 +907,9 @@ module RubyIndexer
|
|
|
904
907
|
target = resolve(entry.target, entry.nesting, seen_names)
|
|
905
908
|
return entry unless target
|
|
906
909
|
|
|
910
|
+
# Self referential alias can be unresolved we should bail out from resolving
|
|
911
|
+
return entry if target.first == entry
|
|
912
|
+
|
|
907
913
|
target_name = target.first #: as !nil
|
|
908
914
|
.name
|
|
909
915
|
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
|
@@ -1023,11 +1029,19 @@ module RubyIndexer
|
|
|
1023
1029
|
name_parts.join("::")
|
|
1024
1030
|
end
|
|
1025
1031
|
|
|
1032
|
+
# Tries to return direct entry from index then non seen canonicalized alias or nil
|
|
1026
1033
|
#: (String full_name, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1027
1034
|
def direct_or_aliased_constant(full_name, seen_names)
|
|
1028
|
-
entries = @entries[full_name]
|
|
1035
|
+
if (entries = @entries[full_name])
|
|
1036
|
+
return entries.map do |e|
|
|
1037
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1038
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
aliased = follow_aliased_namespace(full_name, seen_names)
|
|
1042
|
+
return if full_name == aliased || seen_names.include?(aliased)
|
|
1029
1043
|
|
|
1030
|
-
entries&.map do |e|
|
|
1044
|
+
@entries[aliased]&.map do |e|
|
|
1031
1045
|
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1032
1046
|
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1033
1047
|
end
|
|
@@ -1152,6 +1152,46 @@ module RubyIndexer
|
|
|
1152
1152
|
assert_equal(2, foo_entry.location.start_line)
|
|
1153
1153
|
end
|
|
1154
1154
|
|
|
1155
|
+
def test_resolving_self_referential_constant_alias
|
|
1156
|
+
index(<<~RUBY)
|
|
1157
|
+
module A
|
|
1158
|
+
module B
|
|
1159
|
+
class C
|
|
1160
|
+
end
|
|
1161
|
+
end
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
module A
|
|
1165
|
+
module D
|
|
1166
|
+
B = B::C
|
|
1167
|
+
end
|
|
1168
|
+
end
|
|
1169
|
+
RUBY
|
|
1170
|
+
|
|
1171
|
+
entry = @index.resolve("A::D::B", [])&.first #: as Entry::ConstantAlias
|
|
1172
|
+
|
|
1173
|
+
assert_kind_of(RubyIndexer::Entry::ConstantAlias, entry)
|
|
1174
|
+
assert_equal(10, entry.location.start_line)
|
|
1175
|
+
assert_equal("A::B::C", entry.target)
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
def test_resolving_non_existing_self_referential_constant_alias
|
|
1179
|
+
index(<<~RUBY)
|
|
1180
|
+
module Foo
|
|
1181
|
+
SomeClass = ::SomeClass
|
|
1182
|
+
UNRESOLVED = SomeClass::CONSTANT
|
|
1183
|
+
end
|
|
1184
|
+
RUBY
|
|
1185
|
+
|
|
1186
|
+
entry = @index.resolve("Foo::UNRESOLVED", [])&.first #: as Entry::UnresolvedConstantAlias
|
|
1187
|
+
assert_kind_of(Entry::UnresolvedConstantAlias, entry)
|
|
1188
|
+
assert_equal(3, entry.location.start_line)
|
|
1189
|
+
assert_equal("SomeClass::CONSTANT", entry.target)
|
|
1190
|
+
|
|
1191
|
+
entry = @index.resolve("SomeClass::CONSTANT", ["Foo"])
|
|
1192
|
+
refute(entry)
|
|
1193
|
+
end
|
|
1194
|
+
|
|
1155
1195
|
def test_resolving_qualified_references
|
|
1156
1196
|
index(<<~RUBY)
|
|
1157
1197
|
module Namespace
|
data/lib/ruby_lsp/base_server.rb
CHANGED
|
@@ -103,7 +103,7 @@ module RubyLsp
|
|
|
103
103
|
# This method is only intended to be used in tests! Pops the latest response that would be sent to the client
|
|
104
104
|
#: -> untyped
|
|
105
105
|
def pop_response
|
|
106
|
-
@outgoing_queue.pop
|
|
106
|
+
@outgoing_queue.pop(timeout: 20) || raise("No message received from server")
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
# This method is only intended to be used in tests! Pushes a message to the incoming queue directly
|
|
@@ -92,7 +92,8 @@ module RubyLsp
|
|
|
92
92
|
Prism::RequiredParameterNode, Prism::RestParameterNode
|
|
93
93
|
[target, node_value(target)]
|
|
94
94
|
when Prism::ModuleNode, Prism::ClassNode, Prism::SingletonClassNode, Prism::DefNode, Prism::CaseNode,
|
|
95
|
-
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::IfNode, Prism::UnlessNode
|
|
95
|
+
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::IfNode, Prism::UnlessNode, Prism::BlockNode,
|
|
96
|
+
Prism::LambdaNode, Prism::BeginNode
|
|
96
97
|
[target, nil]
|
|
97
98
|
end
|
|
98
99
|
|
|
@@ -157,6 +158,9 @@ module RubyLsp
|
|
|
157
158
|
:on_for_node_enter,
|
|
158
159
|
:on_if_node_enter,
|
|
159
160
|
:on_unless_node_enter,
|
|
161
|
+
:on_block_node_enter,
|
|
162
|
+
:on_lambda_node_enter,
|
|
163
|
+
:on_begin_node_enter,
|
|
160
164
|
)
|
|
161
165
|
end
|
|
162
166
|
end
|
|
@@ -551,6 +555,27 @@ module RubyLsp
|
|
|
551
555
|
add_matching_end_highlights(node.keyword_loc, node.end_keyword_loc)
|
|
552
556
|
end
|
|
553
557
|
|
|
558
|
+
#: (Prism::BlockNode node) -> void
|
|
559
|
+
def on_block_node_enter(node)
|
|
560
|
+
return unless @target.is_a?(Prism::BlockNode)
|
|
561
|
+
|
|
562
|
+
add_matching_end_highlights(node.opening_loc, node.closing_loc)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
#: (Prism::LambdaNode node) -> void
|
|
566
|
+
def on_lambda_node_enter(node)
|
|
567
|
+
return unless @target.is_a?(Prism::LambdaNode)
|
|
568
|
+
|
|
569
|
+
add_matching_end_highlights(node.opening_loc, node.closing_loc)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
#: (Prism::BeginNode node) -> void
|
|
573
|
+
def on_begin_node_enter(node)
|
|
574
|
+
return unless @target.is_a?(Prism::BeginNode)
|
|
575
|
+
|
|
576
|
+
add_matching_end_highlights(node.begin_keyword_loc, node.end_keyword_loc)
|
|
577
|
+
end
|
|
578
|
+
|
|
554
579
|
private
|
|
555
580
|
|
|
556
581
|
#: (Prism::Node node, Array[singleton(Prism::Node)] classes) -> bool?
|
|
@@ -58,13 +58,11 @@ module RubyLsp
|
|
|
58
58
|
|
|
59
59
|
#: (Prism::CallNode) -> void
|
|
60
60
|
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
61
|
-
return unless in_spec_context?
|
|
62
|
-
|
|
63
61
|
case node.name
|
|
64
62
|
when :describe
|
|
65
63
|
handle_describe(node)
|
|
66
64
|
when :it, :specify
|
|
67
|
-
handle_example(node)
|
|
65
|
+
handle_example(node) if in_spec_context?
|
|
68
66
|
end
|
|
69
67
|
end
|
|
70
68
|
|
|
@@ -117,10 +115,7 @@ module RubyLsp
|
|
|
117
115
|
|
|
118
116
|
#: (Prism::CallNode) -> void
|
|
119
117
|
def handle_example(node)
|
|
120
|
-
|
|
121
|
-
# We are not guaranteed to discover examples in the exact order using static analysis, so we use the line number
|
|
122
|
-
# instead. Note that anonymous examples mixed with meta-programming will not be handled correctly
|
|
123
|
-
description = extract_description(node) || "anonymous"
|
|
118
|
+
description = extract_it_description(node)
|
|
124
119
|
line = node.location.start_line - 1
|
|
125
120
|
parent = latest_group
|
|
126
121
|
return unless parent.is_a?(Requests::Support::TestItem)
|
|
@@ -141,16 +136,37 @@ module RubyLsp
|
|
|
141
136
|
|
|
142
137
|
#: (Prism::CallNode) -> String?
|
|
143
138
|
def extract_description(node)
|
|
139
|
+
arguments = node.arguments&.arguments
|
|
140
|
+
return unless arguments
|
|
141
|
+
|
|
142
|
+
parts = arguments.map { |arg| extract_argument_content(arg) }
|
|
143
|
+
return if parts.empty?
|
|
144
|
+
|
|
145
|
+
parts.join("::")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
#: (Prism::CallNode) -> String
|
|
149
|
+
def extract_it_description(node)
|
|
150
|
+
# Minitest formats the descriptions into test method names by using the count of examples with the description
|
|
151
|
+
# We are not guaranteed to discover examples in the exact order using static analysis, so we use the line number
|
|
152
|
+
# instead. Note that anonymous examples mixed with meta-programming will not be handled correctly
|
|
144
153
|
first_argument = node.arguments&.arguments&.first
|
|
145
|
-
return unless first_argument
|
|
154
|
+
return "anonymous" unless first_argument
|
|
155
|
+
|
|
156
|
+
extract_argument_content(first_argument) || "anonymous"
|
|
157
|
+
end
|
|
146
158
|
|
|
147
|
-
|
|
159
|
+
#: (Prism::Node) -> String?
|
|
160
|
+
def extract_argument_content(arg)
|
|
161
|
+
case arg
|
|
148
162
|
when Prism::StringNode
|
|
149
|
-
|
|
163
|
+
arg.content
|
|
164
|
+
when Prism::SymbolNode
|
|
165
|
+
arg.value
|
|
150
166
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
151
|
-
constant_name(
|
|
167
|
+
constant_name(arg)
|
|
152
168
|
else
|
|
153
|
-
|
|
169
|
+
arg.slice
|
|
154
170
|
end
|
|
155
171
|
end
|
|
156
172
|
|
|
@@ -190,12 +206,13 @@ module RubyLsp
|
|
|
190
206
|
end
|
|
191
207
|
|
|
192
208
|
# Specs only using describes
|
|
193
|
-
|
|
194
|
-
return unless
|
|
209
|
+
first_group_index = @spec_group_id_stack.index { |i| i.is_a?(DescribeGroup) }
|
|
210
|
+
return unless first_group_index
|
|
195
211
|
|
|
212
|
+
first_group = @spec_group_id_stack[first_group_index] #: as !nil
|
|
196
213
|
item = @response_builder[first_group.id] #: as !nil
|
|
197
214
|
|
|
198
|
-
@spec_group_id_stack[1..] #: as !nil
|
|
215
|
+
@spec_group_id_stack[first_group_index + 1..] #: as !nil
|
|
199
216
|
.each do |group|
|
|
200
217
|
next unless group.is_a?(DescribeGroup)
|
|
201
218
|
|
|
@@ -61,11 +61,26 @@ module RubyLsp
|
|
|
61
61
|
|
|
62
62
|
#: (Prism::ClassNode node, String fully_qualified_name) -> Array[String]
|
|
63
63
|
def calc_attached_ancestors(node, fully_qualified_name)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
superclass = node.superclass
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
ancestors = @index.linearized_ancestors_of(fully_qualified_name)
|
|
68
|
+
# If the project has no bundle and a test class inherits from `Minitest::Test`, the linearized ancestors will
|
|
69
|
+
# not include the parent class because we never indexed it in the first place. Here we add the superclass
|
|
70
|
+
# directly, so that we can support running tests in projects without a bundle
|
|
71
|
+
return ancestors if ancestors.length > 1
|
|
72
|
+
|
|
73
|
+
# If all we found is the class itself, then manually include the parent class
|
|
74
|
+
if ancestors.first == fully_qualified_name && superclass
|
|
75
|
+
return [*ancestors, superclass.slice]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
ancestors
|
|
79
|
+
rescue RubyIndexer::Index::NonExistingNamespaceError
|
|
80
|
+
# When there are dynamic parts in the constant path, we will not have indexed the namespace. We can still
|
|
81
|
+
# provide test functionality if the class inherits directly from Test::Unit::TestCase or Minitest::Test
|
|
82
|
+
[superclass&.slice].compact
|
|
83
|
+
end
|
|
69
84
|
end
|
|
70
85
|
|
|
71
86
|
#: (Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode node) -> String
|
|
@@ -33,13 +33,15 @@ module RubyLsp
|
|
|
33
33
|
|
|
34
34
|
if tags.include?("test_dir")
|
|
35
35
|
if children.empty?
|
|
36
|
-
full_files.concat(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
full_files.concat(
|
|
37
|
+
Dir.glob(
|
|
38
|
+
"#{path}/**/{*_test,test_*,*_spec}.rb",
|
|
39
|
+
File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME,
|
|
40
|
+
).map! { |p| Shellwords.escape(p) },
|
|
41
|
+
)
|
|
40
42
|
end
|
|
41
43
|
elsif tags.include?("test_file")
|
|
42
|
-
full_files << path if children.empty?
|
|
44
|
+
full_files << Shellwords.escape(path) if children.empty?
|
|
43
45
|
elsif tags.include?("test_group")
|
|
44
46
|
# If all of the children of the current test group are other groups, then there's no need to add it to the
|
|
45
47
|
# aggregated examples
|
|
@@ -114,7 +116,7 @@ module RubyLsp
|
|
|
114
116
|
end
|
|
115
117
|
|
|
116
118
|
load_path = spec?(file_path) ? "-Ispec" : "-Itest"
|
|
117
|
-
"#{COMMAND} #{load_path} #{file_path} --name \"/#{regex}/\""
|
|
119
|
+
"#{COMMAND} #{load_path} #{Shellwords.escape(file_path)} --name \"/#{regex}/\""
|
|
118
120
|
end
|
|
119
121
|
|
|
120
122
|
#: (String, Hash[String, Hash[Symbol, untyped]]) -> Array[String]
|
|
@@ -125,7 +127,7 @@ module RubyLsp
|
|
|
125
127
|
Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
|
|
126
128
|
".*",
|
|
127
129
|
)
|
|
128
|
-
command = +"#{COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex}\\$/\""
|
|
130
|
+
command = +"#{COMMAND} -Itest #{Shellwords.escape(file_path)} --testcase \"/^#{group_regex}\\$/\""
|
|
129
131
|
|
|
130
132
|
unless examples.empty?
|
|
131
133
|
command << if examples.length == 1
|
|
@@ -145,7 +147,7 @@ module RubyLsp
|
|
|
145
147
|
MINITEST_REPORTER_PATH = File.expand_path("../test_reporters/minitest_reporter.rb", __dir__) #: String
|
|
146
148
|
TEST_UNIT_REPORTER_PATH = File.expand_path("../test_reporters/test_unit_reporter.rb", __dir__) #: String
|
|
147
149
|
BASE_COMMAND = begin
|
|
148
|
-
Bundler.
|
|
150
|
+
Bundler.with_unbundled_env { Bundler.default_lockfile }
|
|
149
151
|
"bundle exec ruby"
|
|
150
152
|
rescue Bundler::GemfileNotFound
|
|
151
153
|
"ruby"
|
|
@@ -35,24 +35,78 @@ module RubyLsp
|
|
|
35
35
|
|
|
36
36
|
#: -> Array[String]
|
|
37
37
|
def find_relevant_paths
|
|
38
|
-
|
|
38
|
+
patterns = relevant_filename_patterns
|
|
39
|
+
|
|
40
|
+
candidate_paths = patterns.flat_map do |pattern|
|
|
41
|
+
Dir.glob(File.join(search_root, "**", pattern))
|
|
42
|
+
end
|
|
43
|
+
|
|
39
44
|
return [] if candidate_paths.empty?
|
|
40
45
|
|
|
41
|
-
find_most_similar_with_jaccard(candidate_paths).map { File.
|
|
46
|
+
find_most_similar_with_jaccard(candidate_paths).map { |path| File.expand_path(path, @workspace_path) }
|
|
42
47
|
end
|
|
43
48
|
|
|
49
|
+
# Determine the search roots based on the closest test directories.
|
|
50
|
+
# This scopes the search to reduce the number of files that need to be checked.
|
|
44
51
|
#: -> String
|
|
45
|
-
def
|
|
46
|
-
|
|
52
|
+
def search_root
|
|
53
|
+
current_path = File.join(".", @path)
|
|
54
|
+
current_dir = File.dirname(current_path)
|
|
55
|
+
while current_dir != "."
|
|
56
|
+
dir_basename = File.basename(current_dir)
|
|
57
|
+
|
|
58
|
+
# If current directory is a test directory, return its parent as search root
|
|
59
|
+
if TEST_KEYWORDS.include?(dir_basename)
|
|
60
|
+
return File.dirname(current_dir)
|
|
61
|
+
end
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
# Search the test directories by walking up the directory tree
|
|
64
|
+
begin
|
|
65
|
+
contains_test_dir = Dir
|
|
66
|
+
.entries(current_dir)
|
|
67
|
+
.filter { |entry| TEST_KEYWORDS.include?(entry) }
|
|
68
|
+
.any? { |entry| File.directory?(File.join(current_dir, entry)) }
|
|
69
|
+
|
|
70
|
+
return current_dir if contains_test_dir
|
|
71
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
72
|
+
# Skip directories we can't read
|
|
53
73
|
end
|
|
54
74
|
|
|
55
|
-
|
|
75
|
+
# Move up one level
|
|
76
|
+
parent_dir = File.dirname(current_dir)
|
|
77
|
+
current_dir = parent_dir
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
"."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
#: -> Array[String]
|
|
84
|
+
def relevant_filename_patterns
|
|
85
|
+
extension = File.extname(@path)
|
|
86
|
+
input_basename = File.basename(@path, extension)
|
|
87
|
+
|
|
88
|
+
if input_basename.match?(TEST_PATTERN)
|
|
89
|
+
# Test file -> find implementation
|
|
90
|
+
base = input_basename.gsub(TEST_PATTERN, "")
|
|
91
|
+
parent_dir = File.basename(File.dirname(@path))
|
|
92
|
+
|
|
93
|
+
# If test file is in a directory matching the implementation name
|
|
94
|
+
# (e.g., go_to_relevant_file/test_go_to_relevant_file_a.rb)
|
|
95
|
+
# return patterns for both the base file name and the parent directory name
|
|
96
|
+
if base.include?(parent_dir) && base != parent_dir
|
|
97
|
+
["#{base}#{extension}", "#{parent_dir}#{extension}"]
|
|
98
|
+
else
|
|
99
|
+
["#{base}#{extension}"]
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
# Implementation file -> find tests (including in matching directory)
|
|
103
|
+
[
|
|
104
|
+
"{#{TEST_PREFIX_GLOB}}#{input_basename}#{extension}",
|
|
105
|
+
"#{input_basename}{#{TEST_SUFFIX_GLOB}}#{extension}",
|
|
106
|
+
"#{input_basename}/{#{TEST_PREFIX_GLOB}}*#{extension}",
|
|
107
|
+
"#{input_basename}/*{#{TEST_SUFFIX_GLOB}}#{extension}",
|
|
108
|
+
]
|
|
109
|
+
end
|
|
56
110
|
end
|
|
57
111
|
|
|
58
112
|
# Using the Jaccard algorithm to determine the similarity between the
|
|
@@ -140,21 +140,25 @@ module RubyLsp
|
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
#: (RubyIndexer::Entry entry) -> Integer
|
|
143
|
+
#: (RubyIndexer::Entry entry) -> Integer
|
|
144
144
|
def kind_for_entry(entry)
|
|
145
145
|
case entry
|
|
146
146
|
when RubyIndexer::Entry::Class
|
|
147
147
|
Constant::SymbolKind::CLASS
|
|
148
148
|
when RubyIndexer::Entry::Module
|
|
149
149
|
Constant::SymbolKind::NAMESPACE
|
|
150
|
-
when RubyIndexer::Entry::Constant
|
|
150
|
+
when RubyIndexer::Entry::Constant, RubyIndexer::Entry::UnresolvedConstantAlias, RubyIndexer::Entry::ConstantAlias
|
|
151
151
|
Constant::SymbolKind::CONSTANT
|
|
152
|
-
when RubyIndexer::Entry::Method
|
|
152
|
+
when RubyIndexer::Entry::Method, RubyIndexer::Entry::UnresolvedMethodAlias, RubyIndexer::Entry::MethodAlias
|
|
153
153
|
entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
|
|
154
154
|
when RubyIndexer::Entry::Accessor
|
|
155
155
|
Constant::SymbolKind::PROPERTY
|
|
156
|
-
when RubyIndexer::Entry::InstanceVariable
|
|
156
|
+
when RubyIndexer::Entry::InstanceVariable, RubyIndexer::Entry::ClassVariable
|
|
157
157
|
Constant::SymbolKind::FIELD
|
|
158
|
+
when RubyIndexer::Entry::GlobalVariable
|
|
159
|
+
Constant::SymbolKind::VARIABLE
|
|
160
|
+
else
|
|
161
|
+
Constant::SymbolKind::NULL
|
|
158
162
|
end
|
|
159
163
|
end
|
|
160
164
|
end
|
|
@@ -61,6 +61,14 @@ module RubyLsp
|
|
|
61
61
|
"RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
|
|
62
62
|
] #: Array[String]
|
|
63
63
|
|
|
64
|
+
# Functionality was introduced in 1.75.0 but had issues with autocorrect
|
|
65
|
+
REUSE_PRISM_RESULT = begin
|
|
66
|
+
gem("rubocop", ">= 1.80.1")
|
|
67
|
+
true
|
|
68
|
+
rescue LoadError
|
|
69
|
+
false
|
|
70
|
+
end #: bool
|
|
71
|
+
|
|
64
72
|
#: Array[::RuboCop::Cop::Offense]
|
|
65
73
|
attr_reader :offenses
|
|
66
74
|
|
|
@@ -81,7 +89,7 @@ module RubyLsp
|
|
|
81
89
|
@offenses = [] #: Array[::RuboCop::Cop::Offense]
|
|
82
90
|
@errors = [] #: Array[String]
|
|
83
91
|
@warnings = [] #: Array[String]
|
|
84
|
-
|
|
92
|
+
@prism_result = nil #: Prism::ParseLexResult?
|
|
85
93
|
|
|
86
94
|
args += DEFAULT_ARGS
|
|
87
95
|
rubocop_options = ::RuboCop::Options.new.parse(args).first
|
|
@@ -101,11 +109,7 @@ module RubyLsp
|
|
|
101
109
|
@warnings = []
|
|
102
110
|
@offenses = []
|
|
103
111
|
@options[:stdin] = contents
|
|
104
|
-
|
|
105
|
-
# Setting the Prism result before running the RuboCop runner makes it reuse the existing AST and avoids
|
|
106
|
-
# double-parsing. Unfortunately, this leads to a bunch of cops failing to execute properly under LSP mode.
|
|
107
|
-
# Uncomment this once reusing the Prism result is more stable
|
|
108
|
-
# @prism_result = prism_result
|
|
112
|
+
@prism_result = prism_result if REUSE_PRISM_RESULT
|
|
109
113
|
|
|
110
114
|
super([path])
|
|
111
115
|
|
data/lib/ruby_lsp/server.rb
CHANGED
|
@@ -303,7 +303,7 @@ module RubyLsp
|
|
|
303
303
|
@current_request_id,
|
|
304
304
|
Interface::RelativePattern.new(
|
|
305
305
|
base_uri: @global_state.workspace_uri.to_s,
|
|
306
|
-
pattern: "{.rubocop.yml,.rubocop}",
|
|
306
|
+
pattern: "{.rubocop.yml,.rubocop,.rubocop_todo.yml}",
|
|
307
307
|
),
|
|
308
308
|
registration_id: "rubocop-watcher",
|
|
309
309
|
))
|
|
@@ -1045,7 +1045,7 @@ module RubyLsp
|
|
|
1045
1045
|
|
|
1046
1046
|
file_name = File.basename(file_path)
|
|
1047
1047
|
|
|
1048
|
-
if file_name == ".rubocop.yml" || file_name == ".rubocop"
|
|
1048
|
+
if file_name == ".rubocop.yml" || file_name == ".rubocop" || file_name == ".rubocop_todo.yml"
|
|
1049
1049
|
handle_rubocop_config_change(uri)
|
|
1050
1050
|
end
|
|
1051
1051
|
end
|
|
@@ -234,13 +234,14 @@ module RubyLsp
|
|
|
234
234
|
# If no error occurred, then clear previous errors
|
|
235
235
|
@error_path.delete if @error_path.exist?
|
|
236
236
|
$stderr.puts("Ruby LSP> Composed bundle installation complete")
|
|
237
|
-
rescue Errno::EPIPE
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
# it does not represent an actual error.
|
|
237
|
+
rescue Errno::EPIPE, Bundler::HTTPError
|
|
238
|
+
# There are cases where we expect certain errors to happen occasionally, and we don't want to write them to
|
|
239
|
+
# a file, which would report to telemetry on the next launch.
|
|
241
240
|
#
|
|
242
|
-
#
|
|
243
|
-
#
|
|
241
|
+
# - The $stderr pipe might be closed by the client, for example when closing the editor during running bundle
|
|
242
|
+
# install. This situation may happen because, while running bundle install, the server is not yet ready to
|
|
243
|
+
# receive shutdown requests and we may continue doing work until the process is killed.
|
|
244
|
+
# - Bundler might also encounter a network error.
|
|
244
245
|
@error_path.delete if @error_path.exist?
|
|
245
246
|
rescue => e
|
|
246
247
|
# Write the error object to a file so that we can read it from the parent process
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-lsp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.26.
|
|
4
|
+
version: 0.26.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
@@ -216,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
216
216
|
- !ruby/object:Gem::Version
|
|
217
217
|
version: '0'
|
|
218
218
|
requirements: []
|
|
219
|
-
rubygems_version: 3.
|
|
219
|
+
rubygems_version: 3.7.2
|
|
220
220
|
specification_version: 4
|
|
221
221
|
summary: An opinionated language server for Ruby
|
|
222
222
|
test_files: []
|