Skip to content

Commit

Permalink
update path cache if PATH changes
Browse files Browse the repository at this point in the history
  • Loading branch information
IanButterworth committed Jan 10, 2024
1 parent f77fac3 commit 50e57fb
Showing 1 changed file with 32 additions and 22 deletions.
54 changes: 32 additions & 22 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,14 @@ end

const path_cache_lock = Base.ReentrantLock()
const path_cache = Dict{String,Vector{String}}()
cached_path_string::Union{String,Nothing} = nothing

function cache_path()
pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":")
pathdirs = @lock path_cache_lock begin
global cached_path_string = get(ENV, "PATH", nothing)
cached_path_string isa String || return
split(cached_path_string, @static Sys.iswindows() ? ";" : ":")
end

for pathdir in pathdirs
actualpath = try
Expand All @@ -291,7 +296,7 @@ function cache_path()
end

try
@lock path_cache_lock get!(path_cache, pathdir, readdir(pathdir))
@lock path_cache_lock path_cache[pathdir] = readdir(pathdir)
catch e
# Bash allows dirs in PATH that can't be read, so we should as well.
if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
Expand Down Expand Up @@ -343,26 +348,31 @@ function complete_path(path::AbstractString;
end
end

if use_envpath && isempty(dir) && trylock(path_cache_lock)
# Look for files in PATH as well.
# these are cached in `cache_path` in a separate task at first shell mode switch.
# If we cannot get lock because its still caching just pass over this.
for (pathdir, filesinpath) in path_cache
for file in filesinpath
# In a perfect world, we would filter on whether the file is executable
# here, or even on whether the current user can execute the file in question.
try
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
push!(matches, file)
end
catch e
# `isfile()` can throw in rare cases such as when probing a
# symlink that points to a file within a directory we do not
# have read access to.
if isa(e, Base.IOError)
continue
else
rethrow()
if use_envpath && isempty(dir)
# if the PATH string has changed re-cache
global cached_path_string
get(ENV, "PATH", nothing) === @lock(path_cache_lock, cached_path_string) || cache_path()
if trylock(path_cache_lock)
# Look for files in PATH as well.
# these are cached in `cache_path` in a separate task at first shell mode switch.
# If we cannot get lock because its still caching just pass over this.
for (pathdir, filesinpath) in path_cache
for file in filesinpath
# In a perfect world, we would filter on whether the file is executable
# here, or even on whether the current user can execute the file in question.
try
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
push!(matches, file)
end
catch e
# `isfile()` can throw in rare cases such as when probing a
# symlink that points to a file within a directory we do not
# have read access to.
if isa(e, Base.IOError)
continue
else
rethrow()
end
end
end
end
Expand Down

0 comments on commit 50e57fb

Please sign in to comment.