ruby-lsp 0.23.11 → 0.26.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/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +45 -22
- data/exe/ruby-lsp-test-exec +6 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -2
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -6
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +140 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +10 -14
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +107 -236
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +166 -281
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +23 -27
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +25 -57
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +58 -68
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
- 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 +49 -9
- 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 +185 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +61 -37
- data/lib/ruby_indexer/test/method_test.rb +166 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +21 -21
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +70 -75
- data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
- data/lib/ruby_indexer/test/test_case.rb +9 -3
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +88 -86
- data/lib/ruby_lsp/base_server.rb +59 -54
- data/lib/ruby_lsp/client_capabilities.rb +16 -13
- data/lib/ruby_lsp/document.rb +205 -104
- data/lib/ruby_lsp/erb_document.rb +45 -47
- data/lib/ruby_lsp/global_state.rb +73 -57
- data/lib/ruby_lsp/internal.rb +8 -3
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +81 -76
- data/lib/ruby_lsp/listeners/definition.rb +44 -58
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +50 -70
- 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 +107 -115
- data/lib/ruby_lsp/listeners/inlay_hints.rb +8 -13
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
- data/lib/ruby_lsp/listeners/spec_style.rb +214 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +92 -0
- data/lib/ruby_lsp/listeners/test_style.rb +205 -95
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +10 -11
- data/lib/ruby_lsp/requests/code_action_resolve.rb +65 -61
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +31 -21
- data/lib/ruby_lsp/requests/completion.rb +8 -21
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +8 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +20 -7
- data/lib/ruby_lsp/requests/document_highlight.rb +6 -16
- 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 +85 -0
- data/lib/ruby_lsp/requests/hover.rb +12 -25
- data/lib/ruby_lsp/requests/inlay_hints.rb +8 -19
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +5 -10
- 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 +17 -57
- data/lib/ruby_lsp/requests/rename.rb +27 -51
- data/lib/ruby_lsp/requests/request.rb +13 -25
- data/lib/ruby_lsp/requests/selection_ranges.rb +7 -7
- 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 +9 -27
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +16 -58
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
- 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 +16 -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 +6 -9
- data/lib/ruby_lsp/response_builders/document_symbol.rb +15 -21
- data/lib/ruby_lsp/response_builders/hover.rb +12 -18
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +62 -91
- data/lib/ruby_lsp/response_builders/signature_help.rb +6 -8
- data/lib/ruby_lsp/response_builders/test_collection.rb +35 -13
- data/lib/ruby_lsp/ruby_document.rb +32 -98
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +303 -196
- data/lib/ruby_lsp/setup_bundler.rb +121 -82
- data/lib/ruby_lsp/static_docs.rb +12 -7
- data/lib/ruby_lsp/store.rb +21 -49
- data/lib/ruby_lsp/test_helper.rb +3 -16
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +233 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +145 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +92 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +138 -93
- data/static_docs/break.md +103 -0
- metadata +14 -20
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c80f549675508ffbb28d649de04506adabec01eac9aa4c6eee057ec848adf858
|
4
|
+
data.tar.gz: 71ea1a4d628444b98bc1173748f5aecf0d71bdc8d3dc80f33b2779c9c78d9de0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7261bf15c095154ff36152492aa252cbd84b84f6e83eb458623c7bb9790a57957885a855c2ee10683b7035267177e323c013988befc02e96f3fdf0274d2312ca
|
7
|
+
data.tar.gz: 74bcea4844e876230713400776bf7def1db44492bcf913526195b5d01919efd24ac69b517594ce2499e332184bf7ef4881e17e7694574bf4d35afabeda25b8c4
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
[](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
|
6
6
|
[](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
|
7
|
-
[](https://
|
7
|
+
[](https://shopify.github.io/ruby-lsp/invite)
|
8
8
|
|
9
9
|
# Ruby LSP
|
10
10
|
|
@@ -13,7 +13,7 @@ for Ruby, used to improve rich features in editors. It is a part of a wider goal
|
|
13
13
|
experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.
|
14
14
|
|
15
15
|
Want to discuss Ruby developer experience? Consider joining the public
|
16
|
-
[Ruby DX Slack workspace](https://
|
16
|
+
[Ruby DX Slack workspace](https://shopify.github.io/ruby-lsp/invite).
|
17
17
|
|
18
18
|
## Getting Started
|
19
19
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.26.1
|
data/exe/ruby-lsp
CHANGED
@@ -88,13 +88,17 @@ if ENV["BUNDLE_GEMFILE"].nil?
|
|
88
88
|
exit exec(env, "#{base_command} exec ruby-lsp #{original_args.join(" ")}".strip)
|
89
89
|
end
|
90
90
|
|
91
|
+
$stdin.sync = true
|
92
|
+
$stdout.sync = true
|
93
|
+
$stderr.sync = true
|
94
|
+
$stdin.binmode
|
95
|
+
$stdout.binmode
|
96
|
+
$stderr.binmode
|
97
|
+
|
91
98
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
92
99
|
|
93
|
-
require "ruby_lsp/load_sorbet"
|
94
100
|
require "ruby_lsp/internal"
|
95
101
|
|
96
|
-
T::Utils.run_all_sig_blocks
|
97
|
-
|
98
102
|
if options[:debug]
|
99
103
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
100
104
|
$stderr.puts "Debugging is not supported on Windows"
|
@@ -147,8 +151,10 @@ if options[:doctor]
|
|
147
151
|
return
|
148
152
|
end
|
149
153
|
|
154
|
+
server = RubyLsp::Server.new
|
155
|
+
|
150
156
|
# Ensure all output goes out stderr by default to allow puts/p/pp to work
|
151
157
|
# without specifying output device.
|
152
158
|
$> = $stderr
|
153
159
|
|
154
|
-
|
160
|
+
server.start
|
data/exe/ruby-lsp-check
CHANGED
@@ -3,13 +3,9 @@
|
|
3
3
|
|
4
4
|
# This executable checks if all automatic LSP requests run successfully on every Ruby file under the current directory
|
5
5
|
|
6
|
-
require "ruby_lsp/load_sorbet"
|
7
|
-
|
8
6
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
9
7
|
require "ruby_lsp/internal"
|
10
8
|
|
11
|
-
T::Utils.run_all_sig_blocks
|
12
|
-
|
13
9
|
files = Dir.glob("#{Dir.pwd}/**/*.rb")
|
14
10
|
|
15
11
|
puts "Verifying that all automatic LSP requests execute successfully. This may take a while..."
|
data/exe/ruby-lsp-launcher
CHANGED
@@ -6,22 +6,35 @@
|
|
6
6
|
# composed bundle
|
7
7
|
# !!!!!!!
|
8
8
|
|
9
|
+
$stdin.sync = true
|
10
|
+
$stdout.sync = true
|
11
|
+
$stderr.sync = true
|
12
|
+
$stdin.binmode
|
13
|
+
$stdout.binmode
|
14
|
+
$stderr.binmode
|
15
|
+
|
9
16
|
setup_error = nil
|
10
17
|
install_error = nil
|
11
18
|
reboot = false
|
12
19
|
|
13
20
|
workspace_uri = ARGV.first
|
21
|
+
raw_initialize_path = File.join(".ruby-lsp", "raw_initialize")
|
14
22
|
|
15
23
|
raw_initialize = if workspace_uri && !workspace_uri.start_with?("--")
|
16
24
|
# If there's an argument without `--`, then it's the server asking to compose the bundle and passing to this
|
17
25
|
# executable the workspace URI. We can't require gems at this point, so we built a fake initialize request manually
|
18
26
|
reboot = true
|
19
27
|
"{\"params\":{\"workspaceFolders\":[{\"uri\":\"#{workspace_uri}\"}]}}"
|
28
|
+
elsif ARGV.include?("--retry")
|
29
|
+
# If we're trying to re-boot automatically, we can't try to read the same initialize request again from the pipe. We
|
30
|
+
# need to ensure that the retry mechanism always writes the request to a file, so that we can reuse it
|
31
|
+
content = File.read(raw_initialize_path)
|
32
|
+
File.delete(raw_initialize_path)
|
33
|
+
content
|
20
34
|
else
|
21
35
|
# Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
|
22
36
|
# Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
|
23
37
|
# to ensure that we're setting up the bundle in the right place
|
24
|
-
$stdin.binmode
|
25
38
|
headers = $stdin.gets("\r\n\r\n")
|
26
39
|
content_length = headers[/Content-Length: (\d+)/i, 1].to_i
|
27
40
|
$stdin.read(content_length)
|
@@ -76,26 +89,33 @@ begin
|
|
76
89
|
# This Marshal load can only happen after requiring Bundler because it will load a custom error class from Bundler
|
77
90
|
# itself. If we try to load before requiring, the class will not be defined and loading will fail
|
78
91
|
error_path = File.join(".ruby-lsp", "install_error")
|
79
|
-
install_error =
|
80
|
-
Marshal.load(File.read(error_path))
|
92
|
+
install_error = begin
|
93
|
+
Marshal.load(File.read(error_path)) if File.exist?(error_path)
|
94
|
+
rescue ArgumentError
|
95
|
+
# The class we tried to load is not defined. This might happen when the user upgrades Bundler and new error
|
96
|
+
# classes are introduced or removed
|
97
|
+
File.delete(error_path)
|
98
|
+
nil
|
81
99
|
end
|
82
100
|
|
83
101
|
Bundler.setup
|
84
102
|
$stderr.puts("Composed Bundle set up successfully")
|
85
103
|
end
|
86
|
-
rescue
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
unless install_error && e.is_a?(Bundler::GemNotFound)
|
95
|
-
setup_error = e
|
96
|
-
$stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
|
104
|
+
rescue Bundler::GemNotFound, Bundler::GitError
|
105
|
+
# Sometimes, we successfully set up the bundle, but users either change their Gemfile or uninstall gems from an
|
106
|
+
# external process. If there's no install error, but the gem is still not found, then we need to attempt to start from
|
107
|
+
# scratch
|
108
|
+
unless install_error || ARGV.include?("--retry")
|
109
|
+
$stderr.puts("Initial bundle compose succeeded, but Bundler.setup failed. Trying to restart from scratch...")
|
110
|
+
File.write(raw_initialize_path, raw_initialize)
|
111
|
+
exec(Gem.ruby, __FILE__, *ARGV, "--retry")
|
97
112
|
end
|
98
113
|
|
114
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
115
|
+
rescue StandardError => e
|
116
|
+
setup_error = e
|
117
|
+
$stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
|
118
|
+
|
99
119
|
# If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
|
100
120
|
# in degraded mode
|
101
121
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
@@ -111,11 +131,8 @@ end
|
|
111
131
|
# Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
|
112
132
|
# configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
|
113
133
|
# paths into the load path manually or we may end up requiring the wrong version of the gem
|
114
|
-
require "ruby_lsp/load_sorbet"
|
115
134
|
require "ruby_lsp/internal"
|
116
135
|
|
117
|
-
T::Utils.run_all_sig_blocks
|
118
|
-
|
119
136
|
if ARGV.include?("--debug")
|
120
137
|
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
|
121
138
|
$stderr.puts "Debugging is not supported on Windows"
|
@@ -129,22 +146,28 @@ if ARGV.include?("--debug")
|
|
129
146
|
end
|
130
147
|
end
|
131
148
|
|
132
|
-
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
|
133
|
-
$> = $stderr
|
134
|
-
|
135
149
|
initialize_request = JSON.parse(raw_initialize, symbolize_names: true) if raw_initialize
|
136
150
|
|
137
151
|
begin
|
138
|
-
RubyLsp::Server.new(
|
152
|
+
server = RubyLsp::Server.new(
|
139
153
|
install_error: install_error,
|
140
154
|
setup_error: setup_error,
|
141
155
|
initialize_request: initialize_request,
|
142
|
-
)
|
156
|
+
)
|
157
|
+
|
158
|
+
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
|
159
|
+
$> = $stderr
|
160
|
+
|
161
|
+
server.start
|
143
162
|
rescue ArgumentError
|
144
163
|
# If the launcher is booting an outdated version of the server, then the initializer doesn't accept a keyword splat
|
145
164
|
# and we already read the initialize request from the stdin pipe. In this case, we need to process the initialize
|
146
165
|
# request manually and then start the main loop
|
147
166
|
server = RubyLsp::Server.new
|
167
|
+
|
168
|
+
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
|
169
|
+
$> = $stderr
|
170
|
+
|
148
171
|
server.process_message(initialize_request)
|
149
172
|
server.start
|
150
173
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "rubocop"
|
5
|
-
require "sorbet-runtime"
|
6
5
|
|
7
6
|
module RuboCop
|
8
7
|
module Cop
|
@@ -29,7 +28,7 @@ module RuboCop
|
|
29
28
|
class UseLanguageServerAliases < RuboCop::Cop::Base
|
30
29
|
extend RuboCop::Cop::AutoCorrector
|
31
30
|
|
32
|
-
ALIASED_CONSTANTS =
|
31
|
+
ALIASED_CONSTANTS = [:Interface, :Transport, :Constant].freeze #: Array[Symbol]
|
33
32
|
|
34
33
|
MSG = "Use constant alias `%{constant}`."
|
35
34
|
|
@@ -2,7 +2,6 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "rubocop"
|
5
|
-
require "sorbet-runtime"
|
6
5
|
|
7
6
|
module RuboCop
|
8
7
|
module Cop
|
@@ -63,8 +62,6 @@ module RuboCop
|
|
63
62
|
# end
|
64
63
|
# end
|
65
64
|
class UseRegisterWithHandlerMethod < RuboCop::Cop::Base
|
66
|
-
extend T::Sig
|
67
|
-
|
68
65
|
MSG_MISSING_HANDLER = "Registered to `%{listener}` without a handler defined."
|
69
66
|
MSG_MISSING_LISTENER = "Created a handler without registering the associated `%{listener}` event."
|
70
67
|
|
@@ -93,12 +90,12 @@ module RuboCop
|
|
93
90
|
|
94
91
|
private
|
95
92
|
|
96
|
-
|
93
|
+
#: (Symbol event_name) -> bool
|
97
94
|
def valid_event_name?(event_name)
|
98
95
|
/^on_.*(node_enter|node_leave)$/.match?(event_name)
|
99
96
|
end
|
100
97
|
|
101
|
-
|
98
|
+
#: (Array[RuboCop::AST::SymbolNode] listeners, Array[RuboCop::AST::DefNode] handlers) -> void
|
102
99
|
def add_offense_to_listeners_without_handler(listeners, handlers)
|
103
100
|
return if listeners.none?
|
104
101
|
|
@@ -107,7 +104,7 @@ module RuboCop
|
|
107
104
|
.each { |node| add_offense(node, message: format(MSG_MISSING_HANDLER, listener: node.value)) }
|
108
105
|
end
|
109
106
|
|
110
|
-
|
107
|
+
#: (Array[RuboCop::AST::SymbolNode] listeners, Array[RuboCop::AST::DefNode] handlers) -> void
|
111
108
|
def add_offense_handlers_without_listener(listeners, handlers)
|
112
109
|
return if handlers.none?
|
113
110
|
|
@@ -3,85 +3,59 @@
|
|
3
3
|
|
4
4
|
module RubyIndexer
|
5
5
|
class Configuration
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
}.freeze,
|
16
|
-
T::Hash[String, T.untyped],
|
17
|
-
)
|
18
|
-
|
19
|
-
sig { params(workspace_path: String).void }
|
6
|
+
CONFIGURATION_SCHEMA = {
|
7
|
+
"excluded_gems" => Array,
|
8
|
+
"included_gems" => Array,
|
9
|
+
"excluded_patterns" => Array,
|
10
|
+
"included_patterns" => Array,
|
11
|
+
"excluded_magic_comments" => Array,
|
12
|
+
}.freeze #: Hash[String, untyped]
|
13
|
+
|
14
|
+
#: String
|
20
15
|
attr_writer :workspace_path
|
21
16
|
|
22
|
-
|
17
|
+
#: Encoding
|
23
18
|
attr_accessor :encoding
|
24
19
|
|
25
|
-
|
20
|
+
#: -> void
|
26
21
|
def initialize
|
27
|
-
@workspace_path =
|
28
|
-
@encoding =
|
29
|
-
@excluded_gems =
|
30
|
-
@included_gems =
|
31
|
-
|
32
|
-
@excluded_patterns =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
File.join("spec", "**", "*"),
|
37
|
-
File.join("test", "**", "*"),
|
38
|
-
File.join("tmp", "**", "*"),
|
39
|
-
],
|
40
|
-
T::Array[String],
|
41
|
-
)
|
22
|
+
@workspace_path = Dir.pwd #: String
|
23
|
+
@encoding = Encoding::UTF_8 #: Encoding
|
24
|
+
@excluded_gems = initial_excluded_gems #: Array[String]
|
25
|
+
@included_gems = [] #: Array[String]
|
26
|
+
|
27
|
+
@excluded_patterns = [
|
28
|
+
"**/{test,spec}/**/{*_test.rb,test_*.rb,*_spec.rb}",
|
29
|
+
"**/fixtures/**/*",
|
30
|
+
] #: Array[String]
|
42
31
|
|
43
32
|
path = Bundler.settings["path"]
|
44
33
|
if path
|
45
34
|
# Substitute Windows backslashes into forward slashes, which are used in glob patterns
|
46
35
|
glob = path.gsub(/[\\]+/, "/")
|
47
|
-
|
36
|
+
glob.delete_suffix!("/")
|
37
|
+
@excluded_patterns << "#{glob}/**/*.rb"
|
48
38
|
end
|
49
39
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
)
|
67
|
-
end
|
68
|
-
|
69
|
-
sig { returns(String) }
|
70
|
-
def merged_excluded_file_pattern
|
71
|
-
# This regex looks for @excluded_patterns that follow the format of "something/**/*", where
|
72
|
-
# "something" is one or more non-"/"
|
73
|
-
#
|
74
|
-
# Returns "/path/to/workspace/{tmp,node_modules}/**/*"
|
75
|
-
@excluded_patterns
|
76
|
-
.filter_map do |pattern|
|
77
|
-
next if File.absolute_path?(pattern)
|
78
|
-
|
79
|
-
pattern.match(%r{\A([^/]+)/\*\*/\*\z})&.captures&.first
|
80
|
-
end
|
81
|
-
.then { |dirs| File.join(@workspace_path, "{#{dirs.join(",")}}/**/*") }
|
40
|
+
# We start the included patterns with only the non excluded directories so that we can avoid paying the price of
|
41
|
+
# traversing large directories that don't include Ruby files like `node_modules`
|
42
|
+
@included_patterns = ["{#{top_level_directories.join(",")}}/**/*.rb", "*.rb"] #: Array[String]
|
43
|
+
@excluded_magic_comments = [
|
44
|
+
"frozen_string_literal:",
|
45
|
+
"typed:",
|
46
|
+
"compiled:",
|
47
|
+
"encoding:",
|
48
|
+
"shareable_constant_value:",
|
49
|
+
"warn_indent:",
|
50
|
+
"rubocop:",
|
51
|
+
"nodoc:",
|
52
|
+
"doc:",
|
53
|
+
"coding:",
|
54
|
+
"warn_past_scope:",
|
55
|
+
] #: Array[String]
|
82
56
|
end
|
83
57
|
|
84
|
-
|
58
|
+
#: -> Array[URI::Generic]
|
85
59
|
def indexable_uris
|
86
60
|
excluded_gems = @excluded_gems - @included_gems
|
87
61
|
locked_gems = Bundler.locked_gems&.specs
|
@@ -91,51 +65,19 @@ module RubyIndexer
|
|
91
65
|
|
92
66
|
flags = File::FNM_PATHNAME | File::FNM_EXTGLOB
|
93
67
|
|
94
|
-
|
95
|
-
|
96
|
-
# "vendor/bundle/**/*" is excluded, we will traverse all of "vendor" and `reject!` out all "vendor/bundle" entries
|
97
|
-
# later.
|
98
|
-
excluded_pattern = merged_excluded_file_pattern
|
99
|
-
included_paths = Dir.glob(File.join(@workspace_path, "*/"), flags)
|
100
|
-
.filter_map do |included_path|
|
101
|
-
next if File.fnmatch?(excluded_pattern, included_path, flags)
|
102
|
-
|
103
|
-
relative_path = included_path
|
104
|
-
.delete_prefix(@workspace_path)
|
105
|
-
.tap { |path| path.delete_prefix!("/") }
|
106
|
-
|
107
|
-
[included_path, relative_path]
|
108
|
-
end
|
109
|
-
|
110
|
-
uris = T.let([], T::Array[URI::Generic])
|
111
|
-
|
112
|
-
# Handle top level files separately. The path below is an optimization to prevent descending down directories that
|
113
|
-
# are going to be excluded anyway, so we need to handle top level scripts separately
|
114
|
-
Dir.glob(File.join(@workspace_path, "*.rb"), flags).each do |path|
|
115
|
-
uris << URI::Generic.from_path(path: path)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Add user specified patterns
|
119
|
-
@included_patterns.each do |pattern|
|
120
|
-
load_path_entry = T.let(nil, T.nilable(String))
|
68
|
+
uris = @included_patterns.flat_map do |pattern|
|
69
|
+
load_path_entry = nil #: String?
|
121
70
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
|
130
|
-
# entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This
|
131
|
-
# happens on repositories that define multiple gems, like Rails. All frameworks are defined inside the
|
132
|
-
# current workspace directory, but each one of them belongs to a different $LOAD_PATH entry
|
133
|
-
if load_path_entry.nil? || !path.start_with?(load_path_entry)
|
134
|
-
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
|
135
|
-
end
|
136
|
-
|
137
|
-
uris << URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
|
71
|
+
Dir.glob(File.join(@workspace_path, pattern), flags).map! do |path|
|
72
|
+
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
|
73
|
+
# entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
|
74
|
+
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the current
|
75
|
+
# workspace directory, but each one of them belongs to a different $LOAD_PATH entry
|
76
|
+
if load_path_entry.nil? || !path.start_with?(load_path_entry)
|
77
|
+
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
|
138
78
|
end
|
79
|
+
|
80
|
+
URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
|
139
81
|
end
|
140
82
|
end
|
141
83
|
|
@@ -150,10 +92,12 @@ module RubyIndexer
|
|
150
92
|
end
|
151
93
|
|
152
94
|
# Remove user specified patterns
|
95
|
+
bundle_path = Bundler.settings["path"]&.gsub(/[\\]+/, "/")
|
153
96
|
uris.reject! do |indexable|
|
154
|
-
|
155
|
-
|
156
|
-
|
97
|
+
path = indexable.full_path #: as !nil
|
98
|
+
next false if test_files_ignored_from_exclusion?(path, bundle_path)
|
99
|
+
|
100
|
+
excluded_patterns.any? { |pattern| File.fnmatch?(pattern, path, flags) }
|
157
101
|
end
|
158
102
|
|
159
103
|
# Add default gems to the list of files to be indexed
|
@@ -222,12 +166,12 @@ module RubyIndexer
|
|
222
166
|
uris
|
223
167
|
end
|
224
168
|
|
225
|
-
|
169
|
+
#: -> Regexp
|
226
170
|
def magic_comment_regex
|
227
|
-
@magic_comment_regex ||=
|
171
|
+
@magic_comment_regex ||= /^#\s*#{@excluded_magic_comments.join("|")}/ #: Regexp?
|
228
172
|
end
|
229
173
|
|
230
|
-
|
174
|
+
#: (Hash[String, untyped] config) -> void
|
231
175
|
def apply_config(config)
|
232
176
|
validate_config!(config)
|
233
177
|
|
@@ -240,7 +184,7 @@ module RubyIndexer
|
|
240
184
|
|
241
185
|
private
|
242
186
|
|
243
|
-
|
187
|
+
#: (Hash[String, untyped] config) -> void
|
244
188
|
def validate_config!(config)
|
245
189
|
errors = config.filter_map do |key, value|
|
246
190
|
type = CONFIGURATION_SCHEMA[key]
|
@@ -255,7 +199,7 @@ module RubyIndexer
|
|
255
199
|
raise ArgumentError, errors.join("\n") if errors.any?
|
256
200
|
end
|
257
201
|
|
258
|
-
|
202
|
+
#: -> Array[String]
|
259
203
|
def initial_excluded_gems
|
260
204
|
excluded, others = Bundler.definition.dependencies.partition do |dependency|
|
261
205
|
dependency.groups == [:development]
|
@@ -305,5 +249,27 @@ module RubyIndexer
|
|
305
249
|
rescue Bundler::GemfileNotFound
|
306
250
|
[]
|
307
251
|
end
|
252
|
+
|
253
|
+
# Checks if the test file is never supposed to be ignored from indexing despite matching exclusion patterns, like
|
254
|
+
# `test_helper.rb` or `test_case.rb`. Also takes into consideration the possibility of finding these files under
|
255
|
+
# fixtures or inside gem source code if the bundle path points to a directory inside the workspace
|
256
|
+
#: (String path, String? bundle_path) -> bool
|
257
|
+
def test_files_ignored_from_exclusion?(path, bundle_path)
|
258
|
+
["test_case.rb", "test_helper.rb"].include?(File.basename(path)) &&
|
259
|
+
!File.fnmatch?("**/fixtures/**/*", path, File::FNM_PATHNAME | File::FNM_EXTGLOB) &&
|
260
|
+
(!bundle_path || !path.start_with?(bundle_path))
|
261
|
+
end
|
262
|
+
|
263
|
+
#: -> Array[String]
|
264
|
+
def top_level_directories
|
265
|
+
excluded_directories = ["tmp", "node_modules", "sorbet"]
|
266
|
+
|
267
|
+
Dir.glob("#{Dir.pwd}/*").filter_map do |path|
|
268
|
+
dir_name = File.basename(path)
|
269
|
+
next unless File.directory?(path) && !excluded_directories.include?(dir_name)
|
270
|
+
|
271
|
+
dir_name
|
272
|
+
end
|
273
|
+
end
|
308
274
|
end
|
309
275
|
end
|