ruby-lsp 0.23.11 → 0.23.13

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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-launcher +12 -11
  5. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
  6. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +81 -115
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +117 -166
  9. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -7
  10. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +88 -200
  11. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +56 -192
  12. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  13. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +14 -16
  14. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +22 -45
  15. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +42 -60
  16. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +9 -16
  17. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
  18. data/lib/ruby_indexer/test/configuration_test.rb +42 -3
  19. data/lib/ruby_indexer/test/method_test.rb +28 -2
  20. data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
  21. data/lib/ruby_lsp/addon.rb +44 -71
  22. data/lib/ruby_lsp/base_server.rb +29 -32
  23. data/lib/ruby_lsp/client_capabilities.rb +10 -12
  24. data/lib/ruby_lsp/document.rb +34 -45
  25. data/lib/ruby_lsp/erb_document.rb +24 -36
  26. data/lib/ruby_lsp/global_state.rb +51 -56
  27. data/lib/ruby_lsp/internal.rb +2 -0
  28. data/lib/ruby_lsp/listeners/code_lens.rb +81 -88
  29. data/lib/ruby_lsp/listeners/completion.rb +36 -55
  30. data/lib/ruby_lsp/listeners/definition.rb +37 -51
  31. data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
  32. data/lib/ruby_lsp/listeners/document_link.rb +43 -62
  33. data/lib/ruby_lsp/listeners/document_symbol.rb +35 -49
  34. data/lib/ruby_lsp/listeners/folding_ranges.rb +32 -39
  35. data/lib/ruby_lsp/listeners/hover.rb +81 -100
  36. data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
  37. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +42 -51
  38. data/lib/ruby_lsp/listeners/signature_help.rb +6 -25
  39. data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
  40. data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
  41. data/lib/ruby_lsp/listeners/test_style.rb +160 -88
  42. data/lib/ruby_lsp/node_context.rb +12 -39
  43. data/lib/ruby_lsp/rbs_document.rb +8 -6
  44. data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
  45. data/lib/ruby_lsp/requests/code_actions.rb +14 -26
  46. data/lib/ruby_lsp/requests/code_lens.rb +6 -17
  47. data/lib/ruby_lsp/requests/completion.rb +7 -20
  48. data/lib/ruby_lsp/requests/completion_resolve.rb +5 -5
  49. data/lib/ruby_lsp/requests/definition.rb +8 -17
  50. data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
  51. data/lib/ruby_lsp/requests/discover_tests.rb +18 -5
  52. data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
  53. data/lib/ruby_lsp/requests/document_link.rb +6 -17
  54. data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
  55. data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
  56. data/lib/ruby_lsp/requests/formatting.rb +6 -9
  57. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
  58. data/lib/ruby_lsp/requests/hover.rb +8 -18
  59. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
  60. data/lib/ruby_lsp/requests/on_type_formatting.rb +28 -38
  61. data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
  62. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
  63. data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
  64. data/lib/ruby_lsp/requests/references.rb +6 -36
  65. data/lib/ruby_lsp/requests/rename.rb +11 -37
  66. data/lib/ruby_lsp/requests/request.rb +7 -19
  67. data/lib/ruby_lsp/requests/selection_ranges.rb +5 -5
  68. data/lib/ruby_lsp/requests/semantic_highlighting.rb +12 -31
  69. data/lib/ruby_lsp/requests/show_syntax_tree.rb +5 -6
  70. data/lib/ruby_lsp/requests/signature_help.rb +8 -26
  71. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  72. data/lib/ruby_lsp/requests/support/common.rb +13 -50
  73. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
  74. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +9 -12
  75. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
  76. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  77. data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
  78. data/lib/ruby_lsp/requests/support/source_uri.rb +16 -30
  79. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
  80. data/lib/ruby_lsp/requests/support/test_item.rb +10 -14
  81. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
  82. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  83. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
  84. data/lib/ruby_lsp/response_builders/document_symbol.rb +10 -16
  85. data/lib/ruby_lsp/response_builders/hover.rb +10 -13
  86. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  87. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +59 -87
  88. data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
  89. data/lib/ruby_lsp/response_builders/test_collection.rb +6 -10
  90. data/lib/ruby_lsp/ruby_document.rb +22 -60
  91. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +109 -0
  92. data/lib/ruby_lsp/scope.rb +7 -11
  93. data/lib/ruby_lsp/server.rb +133 -74
  94. data/lib/ruby_lsp/setup_bundler.rb +58 -57
  95. data/lib/ruby_lsp/static_docs.rb +4 -7
  96. data/lib/ruby_lsp/store.rb +21 -40
  97. data/lib/ruby_lsp/test_helper.rb +3 -12
  98. data/lib/ruby_lsp/test_reporter.rb +207 -0
  99. data/lib/ruby_lsp/test_unit_test_runner.rb +98 -0
  100. data/lib/ruby_lsp/type_inferrer.rb +9 -13
  101. data/lib/ruby_lsp/utils.rb +37 -81
  102. metadata +9 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b93894c0d2c2687def87d9e173c7a9e45e810088613cb902c2a4b1a9d93a8404
4
- data.tar.gz: 9e92f360c8367cde864ac1a5dd2305170a975fa3164e6fc2c1d6f492396fbbc5
3
+ metadata.gz: e224f23084eccd6f7b441b6cf6ae2793fb9c73a0045bf36f8c1bbdf2b1f00445
4
+ data.tar.gz: 581749c8bd2ab362075e49940d364af6a237068e6107d340a2535043d7fdec37
5
5
  SHA512:
6
- metadata.gz: fc2172fba1c50192cb86d2dfc619c633cf756f3ebb574caaaeabb81fe23d283b7b896f48fe23981d29c472fce98248137171d119b33c75d1a7f6fafc73c7a775
7
- data.tar.gz: 03b0b5987fc8dcc6c989ce6c5df55e16fff906836ef74aa993ddc445b50c5d3458775fee1a31b9e228710df44f80b8338390ef1fdf75d464deac4e38fe822644
6
+ metadata.gz: a5d7cb45c56ad5e9e92cba7a0012be7a1626044da4fbc936c413b7a8e5cb3cdd4463bf56dd35ef65feed10f0de363edd04702a4496572248e6e74ea9f720a373
7
+ data.tar.gz: 53639a355c1641e8b11d4e756a417ea9ee96421cb5a87090ffc94cd8f905d41ca3fa5111e4171a81fa62b452c23088895deca0e7eb52fd2e8fed82e1232d2d06
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![Build Status](https://github.com/Shopify/ruby-lsp/workflows/CI/badge.svg)](https://github.com/Shopify/ruby-lsp/actions/workflows/ci.yml)
6
6
  [![Ruby LSP extension](https://img.shields.io/badge/VS%20Code-Ruby%20LSP-success?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
7
- [![Ruby DX Slack](https://img.shields.io/badge/Slack-Ruby%20DX-success?logo=slack)](https://join.slack.com/t/ruby-dx/shared_invite/zt-2yd77ayis-yAiVc1TX_kH0mHMBbi89dA)
7
+ [![Ruby DX Slack](https://img.shields.io/badge/Slack-Ruby%20DX-success?logo=slack)](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://join.slack.com/t/ruby-dx/shared_invite/zt-2yd77ayis-yAiVc1TX_kH0mHMBbi89dA).
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.23.11
1
+ 0.23.13
@@ -83,19 +83,20 @@ begin
83
83
  Bundler.setup
84
84
  $stderr.puts("Composed Bundle set up successfully")
85
85
  end
86
- rescue StandardError => e
87
- # If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most
88
- # features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be
89
- # missing, but we respect the LSP life cycle.
90
- #
91
- # If an install error occurred and one of the gems is not installed, Bundler.setup is guaranteed to fail with
92
- # `Bundler::GemNotFound`. We don't set `setup_error` here because that would essentially report the same problem twice
93
- # to telemetry, which is not useful
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}")
86
+ rescue Bundler::GemNotFound, Bundler::GitError
87
+ # Sometimes, we successfully set up the bundle, but users either change their Gemfile or uninstall gems from an
88
+ # external process. If there's no install error, but the gem is still not found, then we need to attempt to start from
89
+ # scratch
90
+ unless install_error || ARGV.include?("--retry")
91
+ $stderr.puts("Initial bundle compose succeeded, but Bundler.setup failed. Trying to restart from scratch...")
92
+ exec(Gem.ruby, File.expand_path("ruby-lsp-launcher", __dir__), *ARGV, "--retry")
97
93
  end
98
94
 
95
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
96
+ rescue StandardError => e
97
+ setup_error = e
98
+ $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
99
+
99
100
  # If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
100
101
  # in degraded mode
101
102
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
@@ -29,7 +29,7 @@ module RuboCop
29
29
  class UseLanguageServerAliases < RuboCop::Cop::Base
30
30
  extend RuboCop::Cop::AutoCorrector
31
31
 
32
- ALIASED_CONSTANTS = T.let([:Interface, :Transport, :Constant].freeze, T::Array[Symbol])
32
+ ALIASED_CONSTANTS = [:Interface, :Transport, :Constant].freeze #: Array[Symbol]
33
33
 
34
34
  MSG = "Use constant alias `%{constant}`."
35
35
 
@@ -63,8 +63,6 @@ module RuboCop
63
63
  # end
64
64
  # end
65
65
  class UseRegisterWithHandlerMethod < RuboCop::Cop::Base
66
- extend T::Sig
67
-
68
66
  MSG_MISSING_HANDLER = "Registered to `%{listener}` without a handler defined."
69
67
  MSG_MISSING_LISTENER = "Created a handler without registering the associated `%{listener}` event."
70
68
 
@@ -93,12 +91,12 @@ module RuboCop
93
91
 
94
92
  private
95
93
 
96
- sig { params(event_name: Symbol).returns(T::Boolean) }
94
+ #: (Symbol event_name) -> bool
97
95
  def valid_event_name?(event_name)
98
96
  /^on_.*(node_enter|node_leave)$/.match?(event_name)
99
97
  end
100
98
 
101
- sig { params(listeners: T::Array[RuboCop::AST::SymbolNode], handlers: T::Array[RuboCop::AST::DefNode]).void }
99
+ #: (Array[RuboCop::AST::SymbolNode] listeners, Array[RuboCop::AST::DefNode] handlers) -> void
102
100
  def add_offense_to_listeners_without_handler(listeners, handlers)
103
101
  return if listeners.none?
104
102
 
@@ -107,7 +105,7 @@ module RuboCop
107
105
  .each { |node| add_offense(node, message: format(MSG_MISSING_HANDLER, listener: node.value)) }
108
106
  end
109
107
 
110
- sig { params(listeners: T::Array[RuboCop::AST::SymbolNode], handlers: T::Array[RuboCop::AST::DefNode]).void }
108
+ #: (Array[RuboCop::AST::SymbolNode] listeners, Array[RuboCop::AST::DefNode] handlers) -> void
111
109
  def add_offense_handlers_without_listener(listeners, handlers)
112
110
  return if handlers.none?
113
111
 
@@ -3,85 +3,59 @@
3
3
 
4
4
  module RubyIndexer
5
5
  class Configuration
6
- extend T::Sig
7
-
8
- CONFIGURATION_SCHEMA = T.let(
9
- {
10
- "excluded_gems" => Array,
11
- "included_gems" => Array,
12
- "excluded_patterns" => Array,
13
- "included_patterns" => Array,
14
- "excluded_magic_comments" => Array,
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
- sig { returns(Encoding) }
17
+ #: Encoding
23
18
  attr_accessor :encoding
24
19
 
25
- sig { void }
20
+ #: -> void
26
21
  def initialize
27
- @workspace_path = T.let(Dir.pwd, String)
28
- @encoding = T.let(Encoding::UTF_8, Encoding)
29
- @excluded_gems = T.let(initial_excluded_gems, T::Array[String])
30
- @included_gems = T.let([], T::Array[String])
31
-
32
- @excluded_patterns = T.let(
33
- [
34
- File.join("**", "*_test.rb"),
35
- File.join("node_modules", "**", "*"),
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
- @excluded_patterns << File.join(glob, "**", "*.rb")
36
+ glob.delete_suffix!("/")
37
+ @excluded_patterns << "#{glob}/**/*.rb"
48
38
  end
49
39
 
50
- @included_patterns = T.let([File.join("**", "*.rb")], T::Array[String])
51
- @excluded_magic_comments = T.let(
52
- [
53
- "frozen_string_literal:",
54
- "typed:",
55
- "compiled:",
56
- "encoding:",
57
- "shareable_constant_value:",
58
- "warn_indent:",
59
- "rubocop:",
60
- "nodoc:",
61
- "doc:",
62
- "coding:",
63
- "warn_past_scope:",
64
- ],
65
- T::Array[String],
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
- sig { returns(T::Array[URI::Generic]) }
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
- # In order to speed up indexing, only traverse into top-level directories that are not entirely excluded.
95
- # For example, if "tmp/**/*" is excluded, we don't need to traverse into "tmp" at all. However, if
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|
68
+ uris = @included_patterns.flat_map do |pattern|
120
69
  load_path_entry = T.let(nil, T.nilable(String))
121
70
 
122
- included_paths.each do |included_path, relative_path|
123
- relative_pattern = pattern.delete_prefix(File.join(relative_path, "/"))
124
-
125
- next unless pattern.start_with?("**") || pattern.start_with?(relative_path)
126
-
127
- Dir.glob(File.join(included_path, relative_pattern), flags).each do |path|
128
- path = File.expand_path(path)
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
- excluded_patterns.any? do |pattern|
155
- File.fnmatch?(pattern, T.must(indexable.full_path), File::FNM_PATHNAME | File::FNM_EXTGLOB)
156
- end
97
+ path = T.must(indexable.full_path)
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
- sig { returns(Regexp) }
169
+ #: -> Regexp
226
170
  def magic_comment_regex
227
- @magic_comment_regex ||= T.let(/^#\s*#{@excluded_magic_comments.join("|")}/, T.nilable(Regexp))
171
+ @magic_comment_regex ||= /^#\s*#{@excluded_magic_comments.join("|")}/ #: Regexp?
228
172
  end
229
173
 
230
- sig { params(config: T::Hash[String, T.untyped]).void }
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
- sig { params(config: T::Hash[String, T.untyped]).void }
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
- sig { returns(T::Array[String]) }
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