Skip to content

Commit 28cd3c7

Browse files
committed
Merge pull request #12307 from mariushoch/shell-complete-path
Also complete commands (files in the env path) in REPL shell mode
2 parents 72db726 + 5b12348 commit 28cd3c7

File tree

2 files changed

+54
-3
lines changed

2 files changed

+54
-3
lines changed

base/REPLCompletions.jl

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ function complete_keyword(s::ByteString)
109109
sorted_keywords[r]
110110
end
111111

112-
function complete_path(path::AbstractString, pos)
112+
function complete_path(path::AbstractString, pos; use_envpath=false)
113113
if Base.is_unix(OS_NAME) && ismatch(r"^~(?:/|$)", path)
114114
# if the path is just "~", don't consider the expanded username as a prefix
115115
if path == "~"
@@ -141,6 +141,38 @@ function complete_path(path::AbstractString, pos)
141141
push!(matches, id ? file * (@windows? "\\\\" : "/") : file)
142142
end
143143
end
144+
145+
if use_envpath && length(dir) == 0
146+
# Look for files in PATH as well
147+
local pathdirs = split(ENV["PATH"], @unix? ":" : ";")
148+
149+
for pathdir in pathdirs
150+
local actualpath
151+
try
152+
actualpath = realpath(pathdir)
153+
catch
154+
# Bash doesn't expect every folder in PATH to exist, so neither shall we
155+
continue
156+
end
157+
158+
if actualpath != pathdir && in(actualpath,pathdirs)
159+
# Remove paths which (after resolving links) are in the env path twice.
160+
# Many distros eg. point /bin to /usr/bin but have both in the env path.
161+
continue
162+
end
163+
164+
local filesinpath = readdir(pathdir)
165+
166+
for file in filesinpath
167+
# In a perfect world, we would filter on whether the file is executable
168+
# here, or even on whether the current user can execute the file in question.
169+
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
170+
push!(matches, file)
171+
end
172+
end
173+
end
174+
end
175+
144176
matches = UTF8String[replace(s, r"\s", "\\ ") for s in matches]
145177
startpos = pos - endof(prefix) + 1 - length(matchall(r" ", prefix))
146178
# The pos - endof(prefix) + 1 is correct due to `endof(prefix)-endof(prefix)==0`,
@@ -440,8 +472,9 @@ function shell_completions(string, pos)
440472
isempty(args.args[end].args) && return UTF8String[], 0:-1, false
441473
arg = args.args[end].args[end]
442474
if all(s -> isa(s, AbstractString), args.args[end].args)
443-
# Treat this as a path (perhaps give a list of commands in the future as well?)
444-
return complete_path(join(args.args[end].args), pos)
475+
# Treat this as a path
476+
# Also try looking into the env path if the user wants to complete the first argument
477+
return complete_path(join(args.args[end].args), pos, use_envpath=length(args.args) < 2)
445478
elseif isexpr(arg, :escape) && (isexpr(arg.args[1], :incomplete) || isexpr(arg.args[1], :error))
446479
r = first(last_parse):prevind(last_parse, last(last_parse))
447480
partial = scs[r]

test/replcompletions.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,24 @@ c, r, res = test_scomplete(s)
446446
c,r = test_complete(s)
447447
rm(dir)
448448
end
449+
450+
# Tests detecting of files in the env path (in shell mode)
451+
let
452+
oldpath = ENV["PATH"]
453+
path = tempdir()
454+
ENV["PATH"] = path
455+
file = joinpath(path, "tmp-executable")
456+
touch(file)
457+
chmod(file, 0o755)
458+
s = "tmp-execu"
459+
c,r = test_scomplete(s)
460+
@test "tmp-executable" in c
461+
@test r == 1:9
462+
@test s[r] == "tmp-execu"
463+
rm(file)
464+
ENV["PATH"] = oldpath
465+
end
466+
449467
end
450468

451469
let #test that it can auto complete with spaces in file/path

0 commit comments

Comments
 (0)