ruby-lsp 0.23.11 → 0.23.12

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 (98) 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_register_with_handler_method.rb +3 -5
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +52 -77
  7. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +61 -144
  8. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +8 -6
  9. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +73 -182
  10. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +48 -181
  11. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  12. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +12 -14
  13. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +21 -44
  14. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +40 -58
  15. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +9 -16
  16. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
  17. data/lib/ruby_indexer/test/configuration_test.rb +32 -2
  18. data/lib/ruby_indexer/test/method_test.rb +2 -2
  19. data/lib/ruby_lsp/addon.rb +32 -67
  20. data/lib/ruby_lsp/base_server.rb +10 -10
  21. data/lib/ruby_lsp/client_capabilities.rb +4 -6
  22. data/lib/ruby_lsp/document.rb +21 -32
  23. data/lib/ruby_lsp/erb_document.rb +17 -27
  24. data/lib/ruby_lsp/global_state.rb +30 -32
  25. data/lib/ruby_lsp/internal.rb +2 -0
  26. data/lib/ruby_lsp/listeners/code_lens.rb +21 -39
  27. data/lib/ruby_lsp/listeners/completion.rb +34 -53
  28. data/lib/ruby_lsp/listeners/definition.rb +35 -49
  29. data/lib/ruby_lsp/listeners/document_highlight.rb +60 -69
  30. data/lib/ruby_lsp/listeners/document_link.rb +9 -19
  31. data/lib/ruby_lsp/listeners/document_symbol.rb +34 -48
  32. data/lib/ruby_lsp/listeners/folding_ranges.rb +31 -38
  33. data/lib/ruby_lsp/listeners/hover.rb +37 -47
  34. data/lib/ruby_lsp/listeners/inlay_hints.rb +3 -10
  35. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +29 -35
  36. data/lib/ruby_lsp/listeners/signature_help.rb +4 -23
  37. data/lib/ruby_lsp/listeners/spec_style.rb +199 -0
  38. data/lib/ruby_lsp/listeners/test_style.rb +136 -30
  39. data/lib/ruby_lsp/node_context.rb +8 -35
  40. data/lib/ruby_lsp/rbs_document.rb +7 -5
  41. data/lib/ruby_lsp/requests/code_action_resolve.rb +10 -10
  42. data/lib/ruby_lsp/requests/code_actions.rb +5 -14
  43. data/lib/ruby_lsp/requests/code_lens.rb +4 -13
  44. data/lib/ruby_lsp/requests/completion.rb +4 -15
  45. data/lib/ruby_lsp/requests/completion_resolve.rb +4 -4
  46. data/lib/ruby_lsp/requests/definition.rb +4 -12
  47. data/lib/ruby_lsp/requests/diagnostics.rb +6 -9
  48. data/lib/ruby_lsp/requests/discover_tests.rb +15 -3
  49. data/lib/ruby_lsp/requests/document_highlight.rb +3 -11
  50. data/lib/ruby_lsp/requests/document_link.rb +4 -13
  51. data/lib/ruby_lsp/requests/document_symbol.rb +4 -7
  52. data/lib/ruby_lsp/requests/folding_ranges.rb +4 -7
  53. data/lib/ruby_lsp/requests/formatting.rb +4 -7
  54. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
  55. data/lib/ruby_lsp/requests/hover.rb +6 -16
  56. data/lib/ruby_lsp/requests/inlay_hints.rb +4 -13
  57. data/lib/ruby_lsp/requests/on_type_formatting.rb +17 -24
  58. data/lib/ruby_lsp/requests/prepare_rename.rb +3 -8
  59. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +4 -13
  60. data/lib/ruby_lsp/requests/range_formatting.rb +3 -4
  61. data/lib/ruby_lsp/requests/references.rb +5 -35
  62. data/lib/ruby_lsp/requests/rename.rb +9 -35
  63. data/lib/ruby_lsp/requests/request.rb +5 -17
  64. data/lib/ruby_lsp/requests/selection_ranges.rb +3 -3
  65. data/lib/ruby_lsp/requests/semantic_highlighting.rb +6 -23
  66. data/lib/ruby_lsp/requests/show_syntax_tree.rb +4 -5
  67. data/lib/ruby_lsp/requests/signature_help.rb +6 -24
  68. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  69. data/lib/ruby_lsp/requests/support/common.rb +12 -49
  70. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -14
  71. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +7 -10
  72. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -15
  73. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  74. data/lib/ruby_lsp/requests/support/sorbet.rb +1 -7
  75. data/lib/ruby_lsp/requests/support/source_uri.rb +5 -16
  76. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +7 -10
  77. data/lib/ruby_lsp/requests/support/test_item.rb +14 -13
  78. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +4 -5
  79. data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -3
  80. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +4 -4
  81. data/lib/ruby_lsp/response_builders/document_symbol.rb +8 -11
  82. data/lib/ruby_lsp/response_builders/hover.rb +5 -5
  83. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  84. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +18 -40
  85. data/lib/ruby_lsp/response_builders/signature_help.rb +4 -5
  86. data/lib/ruby_lsp/response_builders/test_collection.rb +5 -9
  87. data/lib/ruby_lsp/ruby_document.rb +15 -40
  88. data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +106 -0
  89. data/lib/ruby_lsp/scope.rb +6 -10
  90. data/lib/ruby_lsp/server.rb +125 -74
  91. data/lib/ruby_lsp/setup_bundler.rb +22 -15
  92. data/lib/ruby_lsp/store.rb +12 -28
  93. data/lib/ruby_lsp/test_helper.rb +3 -12
  94. data/lib/ruby_lsp/test_reporter.rb +71 -0
  95. data/lib/ruby_lsp/test_unit_test_runner.rb +96 -0
  96. data/lib/ruby_lsp/type_inferrer.rb +9 -13
  97. data/lib/ruby_lsp/utils.rb +27 -65
  98. metadata +8 -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: 1b902a160bf7f96c2b65716065887523f5e4d1788e9ba7211a5500513f951d2f
4
+ data.tar.gz: d2500874ca70ea7aa4df5d1ce2106d16e67143fb5cff87208bdfab36a4b49414
5
5
  SHA512:
6
- metadata.gz: fc2172fba1c50192cb86d2dfc619c633cf756f3ebb574caaaeabb81fe23d283b7b896f48fe23981d29c472fce98248137171d119b33c75d1a7f6fafc73c7a775
7
- data.tar.gz: 03b0b5987fc8dcc6c989ce6c5df55e16fff906836ef74aa993ddc445b50c5d3458775fee1a31b9e228710df44f80b8338390ef1fdf75d464deac4e38fe822644
6
+ metadata.gz: 5d7d14c58e08d99a9c4f19d2ccde6171da415093c7ebab744181af7ebc8617eae4ba09a992123542d5a5ba44177457cd23cd8bd0ece1886b17237c7bad73fc2b
7
+ data.tar.gz: eea9a42eeabe3e8c5ce86210770f76e210b313bd94bc457dcfe99fc7a0913e24443b67e35c4c64e46420e95a47cd2cb0eaa7004fbbb8e9544932a90c63686219
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.12
@@ -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__))
@@ -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,8 +3,6 @@
3
3
 
4
4
  module RubyIndexer
5
5
  class Configuration
6
- extend T::Sig
7
-
8
6
  CONFIGURATION_SCHEMA = T.let(
9
7
  {
10
8
  "excluded_gems" => Array,
@@ -16,13 +14,13 @@ module RubyIndexer
16
14
  T::Hash[String, T.untyped],
17
15
  )
18
16
 
19
- sig { params(workspace_path: String).void }
17
+ #: String
20
18
  attr_writer :workspace_path
21
19
 
22
- sig { returns(Encoding) }
20
+ #: Encoding
23
21
  attr_accessor :encoding
24
22
 
25
- sig { void }
23
+ #: -> void
26
24
  def initialize
27
25
  @workspace_path = T.let(Dir.pwd, String)
28
26
  @encoding = T.let(Encoding::UTF_8, Encoding)
@@ -31,11 +29,8 @@ module RubyIndexer
31
29
 
32
30
  @excluded_patterns = T.let(
33
31
  [
34
- File.join("**", "*_test.rb"),
35
- File.join("node_modules", "**", "*"),
36
- File.join("spec", "**", "*"),
37
- File.join("test", "**", "*"),
38
- File.join("tmp", "**", "*"),
32
+ "**/{test,spec}/**/{*_test.rb,test_*.rb,*_spec.rb}",
33
+ "**/fixtures/**/*",
39
34
  ],
40
35
  T::Array[String],
41
36
  )
@@ -44,10 +39,13 @@ module RubyIndexer
44
39
  if path
45
40
  # Substitute Windows backslashes into forward slashes, which are used in glob patterns
46
41
  glob = path.gsub(/[\\]+/, "/")
47
- @excluded_patterns << File.join(glob, "**", "*.rb")
42
+ glob.delete_suffix!("/")
43
+ @excluded_patterns << "#{glob}/**/*.rb"
48
44
  end
49
45
 
50
- @included_patterns = T.let([File.join("**", "*.rb")], T::Array[String])
46
+ # We start the included patterns with only the non excluded directories so that we can avoid paying the price of
47
+ # traversing large directories that don't include Ruby files like `node_modules`
48
+ @included_patterns = T.let(["{#{top_level_directories.join(",")}}/**/*.rb", "*.rb"], T::Array[String])
51
49
  @excluded_magic_comments = T.let(
52
50
  [
53
51
  "frozen_string_literal:",
@@ -66,22 +64,7 @@ module RubyIndexer
66
64
  )
67
65
  end
68
66
 
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(",")}}/**/*") }
82
- end
83
-
84
- sig { returns(T::Array[URI::Generic]) }
67
+ #: -> Array[URI::Generic]
85
68
  def indexable_uris
86
69
  excluded_gems = @excluded_gems - @included_gems
87
70
  locked_gems = Bundler.locked_gems&.specs
@@ -91,51 +74,19 @@ module RubyIndexer
91
74
 
92
75
  flags = File::FNM_PATHNAME | File::FNM_EXTGLOB
93
76
 
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|
77
+ uris = @included_patterns.flat_map do |pattern|
120
78
  load_path_entry = T.let(nil, T.nilable(String))
121
79
 
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)
80
+ Dir.glob(File.join(@workspace_path, pattern), flags).map! do |path|
81
+ # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
82
+ # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
83
+ # on repositories that define multiple gems, like Rails. All frameworks are defined inside the current
84
+ # workspace directory, but each one of them belongs to a different $LOAD_PATH entry
85
+ if load_path_entry.nil? || !path.start_with?(load_path_entry)
86
+ load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
138
87
  end
88
+
89
+ URI::Generic.from_path(path: path, load_path_entry: load_path_entry)
139
90
  end
140
91
  end
141
92
 
@@ -150,10 +101,12 @@ module RubyIndexer
150
101
  end
151
102
 
152
103
  # Remove user specified patterns
104
+ bundle_path = Bundler.settings["path"]&.gsub(/[\\]+/, "/")
153
105
  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
106
+ path = T.must(indexable.full_path)
107
+ next false if test_files_ignored_from_exclusion?(path, bundle_path)
108
+
109
+ excluded_patterns.any? { |pattern| File.fnmatch?(pattern, path, flags) }
157
110
  end
158
111
 
159
112
  # Add default gems to the list of files to be indexed
@@ -222,12 +175,12 @@ module RubyIndexer
222
175
  uris
223
176
  end
224
177
 
225
- sig { returns(Regexp) }
178
+ #: -> Regexp
226
179
  def magic_comment_regex
227
180
  @magic_comment_regex ||= T.let(/^#\s*#{@excluded_magic_comments.join("|")}/, T.nilable(Regexp))
228
181
  end
229
182
 
230
- sig { params(config: T::Hash[String, T.untyped]).void }
183
+ #: (Hash[String, untyped] config) -> void
231
184
  def apply_config(config)
232
185
  validate_config!(config)
233
186
 
@@ -240,7 +193,7 @@ module RubyIndexer
240
193
 
241
194
  private
242
195
 
243
- sig { params(config: T::Hash[String, T.untyped]).void }
196
+ #: (Hash[String, untyped] config) -> void
244
197
  def validate_config!(config)
245
198
  errors = config.filter_map do |key, value|
246
199
  type = CONFIGURATION_SCHEMA[key]
@@ -255,7 +208,7 @@ module RubyIndexer
255
208
  raise ArgumentError, errors.join("\n") if errors.any?
256
209
  end
257
210
 
258
- sig { returns(T::Array[String]) }
211
+ #: -> Array[String]
259
212
  def initial_excluded_gems
260
213
  excluded, others = Bundler.definition.dependencies.partition do |dependency|
261
214
  dependency.groups == [:development]
@@ -305,5 +258,27 @@ module RubyIndexer
305
258
  rescue Bundler::GemfileNotFound
306
259
  []
307
260
  end
261
+
262
+ # Checks if the test file is never supposed to be ignored from indexing despite matching exclusion patterns, like
263
+ # `test_helper.rb` or `test_case.rb`. Also takes into consideration the possibility of finding these files under
264
+ # fixtures or inside gem source code if the bundle path points to a directory inside the workspace
265
+ #: (String path, String? bundle_path) -> bool
266
+ def test_files_ignored_from_exclusion?(path, bundle_path)
267
+ ["test_case.rb", "test_helper.rb"].include?(File.basename(path)) &&
268
+ !File.fnmatch?("**/fixtures/**/*", path, File::FNM_PATHNAME | File::FNM_EXTGLOB) &&
269
+ (!bundle_path || !path.start_with?(bundle_path))
270
+ end
271
+
272
+ #: -> Array[String]
273
+ def top_level_directories
274
+ excluded_directories = ["tmp", "node_modules", "sorbet"]
275
+
276
+ Dir.glob("#{Dir.pwd}/*").filter_map do |path|
277
+ dir_name = File.basename(path)
278
+ next unless File.directory?(path) && !excluded_directories.include?(dir_name)
279
+
280
+ dir_name
281
+ end
282
+ end
308
283
  end
309
284
  end