Skip to content

Commit dc2c4f1

Browse files
IanButterworthKristofferC
authored andcommitted
[REPL] fix computation of startpos for path completions (#52009)
Fixes #51985 Ensure that the REPL completions escape and unescape text correctly, using the correct functions, and accounting for exactly what the user has currently typed. The old broken method is left around for Pkg, since it has an over-reliance on it returning incorrect answers. Once Pkg is fixed, we can delete that code. Co-authored-by: Jameson Nash <[email protected]> (cherry picked from commit 5edcdc5)
1 parent 0a1e83e commit dc2c4f1

File tree

4 files changed

+246
-118
lines changed

4 files changed

+246
-118
lines changed

base/shell.jl

+15-9
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ end
1818

1919
function shell_parse(str::AbstractString, interpolate::Bool=true;
2020
special::AbstractString="", filename="none")
21-
s = SubString(str, firstindex(str))
21+
last_arg = firstindex(str) # N.B.: This is used by REPLCompletions
22+
s = SubString(str, last_arg)
2223
s = rstrip_shell(lstrip(s))
2324

24-
# N.B.: This is used by REPLCompletions
25-
last_parse = 0:-1
26-
isempty(s) && return interpolate ? (Expr(:tuple,:()),last_parse) : ([],last_parse)
25+
isempty(s) && return interpolate ? (Expr(:tuple,:()), last_arg) : ([], last_arg)
2726

2827
in_single_quotes = false
2928
in_double_quotes = false
@@ -32,6 +31,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
3231
arg = []
3332
i = firstindex(s)
3433
st = Iterators.Stateful(pairs(s))
34+
update_last_arg = false # true after spaces or interpolate
3535

3636
function push_nonempty!(list, x)
3737
if !isa(x,AbstractString) || !isempty(x)
@@ -54,6 +54,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
5454
for (j, c) in st
5555
j, c = j::Int, c::C
5656
if !in_single_quotes && !in_double_quotes && isspace(c)
57+
update_last_arg = true
5758
i = consume_upto!(arg, s, i, j)
5859
append_2to1!(args, arg)
5960
while !isempty(st)
@@ -77,12 +78,17 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
7778
# use parseatom instead of parse to respect filename (#28188)
7879
ex, j = Meta.parseatom(s, stpos, filename=filename)
7980
end
80-
last_parse = (stpos:prevind(s, j)) .+ s.offset
81-
push_nonempty!(arg, ex)
81+
last_arg = stpos + s.offset
82+
update_last_arg = true
83+
push!(arg, ex)
8284
s = SubString(s, j)
8385
Iterators.reset!(st, pairs(s))
8486
i = firstindex(s)
8587
else
88+
if update_last_arg
89+
last_arg = i + s.offset
90+
update_last_arg = false
91+
end
8692
if !in_double_quotes && c == '\''
8793
in_single_quotes = !in_single_quotes
8894
i = consume_upto!(arg, s, i, j)
@@ -124,14 +130,14 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
124130
push_nonempty!(arg, s[i:end])
125131
append_2to1!(args, arg)
126132

127-
interpolate || return args, last_parse
133+
interpolate || return args, last_arg
128134

129135
# construct an expression
130136
ex = Expr(:tuple)
131137
for arg in args
132138
push!(ex.args, Expr(:tuple, arg...))
133139
end
134-
return ex, last_parse
140+
return ex, last_arg
135141
end
136142

137143
function shell_split(s::AbstractString)
@@ -216,7 +222,7 @@ function print_shell_escaped_posixly(io::IO, args::AbstractString...)
216222
function isword(c::AbstractChar)
217223
if '0' <= c <= '9' || 'a' <= c <= 'z' || 'A' <= c <= 'Z'
218224
# word characters
219-
elseif c == '_' || c == '/' || c == '+' || c == '-'
225+
elseif c == '_' || c == '/' || c == '+' || c == '-' || c == '.'
220226
# other common characters
221227
elseif c == '\''
222228
have_single = true

0 commit comments

Comments
 (0)