Skip to content

Commit 766621d

Browse files
committed
Always use workspace URI instead of Dir.pwd
1 parent 88b85b8 commit 766621d

File tree

7 files changed

+116
-67
lines changed

7 files changed

+116
-67
lines changed

lib/ruby_indexer/lib/ruby_indexer/configuration.rb

+23-9
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@ class Configuration
1616
T::Hash[String, T.untyped],
1717
)
1818

19+
sig { params(workspace_path: String).void }
20+
attr_writer :workspace_path
21+
1922
sig { void }
2023
def initialize
24+
@workspace_path = T.let(Dir.pwd, String)
2125
@excluded_gems = T.let(initial_excluded_gems, T::Array[String])
2226
@included_gems = T.let([], T::Array[String])
23-
@excluded_patterns = T.let([File.join("**", "*_test.rb"), File.join("**", "tmp", "**", "*")], T::Array[String])
27+
@excluded_patterns = T.let([File.join("**", "*_test.rb"), File.join("tmp", "**", "*")], T::Array[String])
2428
path = Bundler.settings["path"]
25-
@excluded_patterns << File.join(File.expand_path(path, Dir.pwd), "**", "*.rb") if path
29+
@excluded_patterns << File.join(path, "**", "*.rb") if path
2630

27-
@included_patterns = T.let([File.join(Dir.pwd, "**", "*.rb")], T::Array[String])
31+
@included_patterns = T.let([File.join("**", "*.rb")], T::Array[String])
2832
@excluded_magic_comments = T.let(
2933
[
3034
"frozen_string_literal:",
@@ -55,12 +59,12 @@ def indexables
5559
indexables = @included_patterns.flat_map do |pattern|
5660
load_path_entry = T.let(nil, T.nilable(String))
5761

58-
Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
62+
Dir.glob(File.join(@workspace_path, pattern), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
5963
path = File.expand_path(path)
6064
# All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
6165
# entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
62-
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the Dir.pwd, but
63-
# each one of them belongs to a different $LOAD_PATH entry
66+
# on repositories that define multiple gems, like Rails. All frameworks are defined inside the current
67+
# workspace directory, but each one of them belongs to a different $LOAD_PATH entry
6468
if load_path_entry.nil? || !path.start_with?(load_path_entry)
6569
load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
6670
end
@@ -69,9 +73,19 @@ def indexables
6973
end
7074
end
7175

76+
# If the patterns are relative, we make it relative to the workspace path. If they are absolute, then we shouldn't
77+
# concatenate anything
78+
excluded_patterns = @excluded_patterns.map do |pattern|
79+
if File.absolute_path?(pattern)
80+
pattern
81+
else
82+
File.join(@workspace_path, pattern)
83+
end
84+
end
85+
7286
# Remove user specified patterns
7387
indexables.reject! do |indexable|
74-
@excluded_patterns.any? do |pattern|
88+
excluded_patterns.any? do |pattern|
7589
File.fnmatch?(pattern, indexable.full_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
7690
end
7791
end
@@ -122,7 +136,7 @@ def indexables
122136
# When working on a gem, it will be included in the locked_gems list. Since these are the project's own files,
123137
# we have already included and handled exclude patterns for it and should not re-include or it'll lead to
124138
# duplicates or accidentally ignoring exclude patterns
125-
next if spec.full_gem_path == Dir.pwd
139+
next if spec.full_gem_path == @workspace_path
126140

127141
indexables.concat(
128142
spec.require_paths.flat_map do |require_path|
@@ -185,7 +199,7 @@ def initial_excluded_gems
185199
# If the dependency is prerelease, `to_spec` may return `nil` due to a bug in older version of Bundler/RubyGems:
186200
# https://github.com/Shopify/ruby-lsp/issues/1246
187201
this_gem = Bundler.definition.dependencies.find do |d|
188-
d.to_spec&.full_gem_path == Dir.pwd
202+
d.to_spec&.full_gem_path == @workspace_path
189203
rescue Gem::MissingSpecError
190204
false
191205
end

lib/ruby_indexer/test/configuration_test.rb

+41-7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ module RubyIndexer
77
class ConfigurationTest < Minitest::Test
88
def setup
99
@config = Configuration.new
10+
@workspace_path = File.expand_path(File.join("..", "..", ".."), __dir__)
11+
@config.workspace_path = @workspace_path
1012
end
1113

1214
def test_load_configuration_executes_configure_block
13-
@config.apply_config({ "excluded_patterns" => ["**/test/fixtures/**/*.rb"] })
15+
@config.apply_config({ "excluded_patterns" => ["**/fixtures/**/*.rb"] })
1416
indexables = @config.indexables
1517

1618
assert(indexables.none? { |indexable| indexable.full_path.include?("test/fixtures") })
@@ -25,7 +27,7 @@ def test_indexables_have_expanded_full_paths
2527
indexables = @config.indexables
2628

2729
# All paths should be expanded
28-
assert(indexables.none? { |indexable| indexable.full_path.start_with?("lib/") })
30+
assert(indexables.all? { |indexable| File.absolute_path?(indexable.full_path) })
2931
end
3032

3133
def test_indexables_only_includes_gem_require_paths
@@ -71,17 +73,20 @@ def test_indexables_avoids_duplicates_if_bundle_path_is_inside_project
7173
Bundler.settings.temporary(path: "vendor/bundle") do
7274
config = Configuration.new
7375

74-
assert_includes(config.instance_variable_get(:@excluded_patterns), "#{Dir.pwd}/vendor/bundle/**/*.rb")
76+
assert_includes(config.instance_variable_get(:@excluded_patterns), "vendor/bundle/**/*.rb")
7577
end
7678
end
7779

7880
def test_indexables_does_not_include_gems_own_installed_files
7981
indexables = @config.indexables
82+
indexables_inside_bundled_lsp = indexables.select do |indexable|
83+
indexable.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s)
84+
end
8085

81-
assert(
82-
indexables.none? do |indexable|
83-
indexable.full_path.start_with?(Bundler.bundle_path.join("gems", "ruby-lsp").to_s)
84-
end,
86+
assert_empty(
87+
indexables_inside_bundled_lsp,
88+
"Indexables should not include files from the gem currently being worked on. " \
89+
"Included: #{indexables_inside_bundled_lsp.map(&:full_path)}",
8590
)
8691
end
8792

@@ -126,5 +131,34 @@ def test_magic_comments_regex
126131
assert_match(regex, comment)
127132
end
128133
end
134+
135+
def test_indexables_respect_given_workspace_path
136+
Dir.mktmpdir do |dir|
137+
FileUtils.mkdir(File.join(dir, "ignore"))
138+
FileUtils.touch(File.join(dir, "ignore", "file0.rb"))
139+
FileUtils.touch(File.join(dir, "file1.rb"))
140+
FileUtils.touch(File.join(dir, "file2.rb"))
141+
142+
@config.apply_config({ "excluded_patterns" => ["ignore/**/*.rb"] })
143+
@config.workspace_path = dir
144+
indexables = @config.indexables
145+
146+
assert(indexables.none? { |indexable| indexable.full_path.start_with?(File.join(dir, "ignore")) })
147+
148+
# After switching the workspace path, all indexables will be found in one of these places:
149+
# - The new workspace path
150+
# - The Ruby LSP's own code (because Bundler is requiring the dependency from source)
151+
# - Bundled gems
152+
# - Default gems
153+
assert(
154+
indexables.all? do |i|
155+
i.full_path.start_with?(dir) ||
156+
i.full_path.start_with?(File.join(Dir.pwd, "lib")) ||
157+
i.full_path.start_with?(Bundler.bundle_path.to_s) ||
158+
i.full_path.start_with?(RbConfig::CONFIG["rubylibdir"])
159+
end,
160+
)
161+
end
162+
end
129163
end
130164
end

lib/ruby_lsp/listeners/definition.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def handle_require_definition(node, message)
250250
when :require_relative
251251
required_file = "#{node.content}.rb"
252252
path = @uri.to_standardized_path
253-
current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
253+
current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : @global_state.workspace_path
254254
candidate = File.expand_path(File.join(current_folder, required_file))
255255

256256
@response_builder << Interface::Location.new(

lib/ruby_lsp/server.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -1000,10 +1000,10 @@ def process_indexing_configuration(indexing_options)
10001000

10011001
return unless indexing_options
10021002

1003+
configuration = @global_state.index.configuration
1004+
configuration.workspace_path = @global_state.workspace_path
10031005
# The index expects snake case configurations, but VS Code standardizes on camel case settings
1004-
@global_state.index.configuration.apply_config(
1005-
indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase },
1006-
)
1006+
configuration.apply_config(indexing_options.transform_keys { |key| key.to_s.gsub(/([A-Z])/, "_\\1").downcase })
10071007
end
10081008
end
10091009
end

lib/ruby_lsp/setup_bundler.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def initialize(project_path, **options)
4343
@gemfile_name = T.let(@gemfile&.basename&.to_s || "Gemfile", String)
4444

4545
# Custom bundle paths
46-
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
46+
@custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(@project_path), Pathname)
4747
@custom_gemfile = T.let(@custom_dir + @gemfile_name, Pathname)
4848
@custom_lockfile = T.let(@custom_dir + (@lockfile&.basename || "Gemfile.lock"), Pathname)
4949
@lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
@@ -183,14 +183,14 @@ def run_bundle_install(bundle_gemfile = @gemfile)
183183
# `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
184184
# want to install it in the top level `vendor` and not `.ruby-lsp/vendor`
185185
path = Bundler.settings["path"]
186-
expanded_path = File.expand_path(path, Dir.pwd) if path
186+
expanded_path = File.expand_path(path, @project_path) if path
187187

188188
# Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
189189
env = {}
190190
env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
191191
env["BUNDLE_PATH"] = expanded_path if expanded_path
192192

193-
local_config_path = File.join(Dir.pwd, ".bundle")
193+
local_config_path = File.join(@project_path, ".bundle")
194194
env["BUNDLE_APP_CONFIG"] = local_config_path if Dir.exist?(local_config_path)
195195

196196
# If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try

sorbet/rbi/shims/file.rbi

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# typed: true
2+
3+
class File
4+
class << self
5+
sig { params(path: String).returns(T::Boolean) }
6+
def absolute_path?(path); end
7+
end
8+
end

0 commit comments

Comments
 (0)