@@ -232,7 +232,10 @@ function complete_keyword(s::Union{String,SubString{String}})
232232 Completion[KeywordCompletion (kw) for kw in sorted_keywords[r]]
233233end
234234
235- function complete_path (path:: AbstractString , pos:: Int ; use_envpath= false , shell_escape= false )
235+ function complete_path (path:: AbstractString , pos:: Int ;
236+ use_envpath= false , shell_escape= false ,
237+ string_escape= false )
238+ @assert ! (shell_escape && string_escape)
236239 if Base. Sys. isunix () && occursin (r" ^~(?:/|$)" , path)
237240 # if the path is just "~", don't consider the expanded username as a prefix
238241 if path == " ~"
@@ -259,9 +262,9 @@ function complete_path(path::AbstractString, pos::Int; use_envpath=false, shell_
259262 matches = Set {String} ()
260263 for file in files
261264 if startswith (file, prefix)
262- id = try isdir ( joinpath (dir, file)) catch ; false end
263- # joinpath is not used because windows needs to complete with double-backslash
264- push! (matches, id ? file * ( @static Sys . iswindows () ? " \\\\ " : " / " ) : file)
265+ p = joinpath (dir, file)
266+ is_dir = try isdir (p) catch ; false end
267+ push! (matches, is_dir ? joinpath ( file, " " ) : file)
265268 end
266269 end
267270
@@ -307,8 +310,14 @@ function complete_path(path::AbstractString, pos::Int; use_envpath=false, shell_
307310 end
308311 end
309312
310- matchList = Completion[PathCompletion (shell_escape ? replace (s, r" \s " => s "\\\0 " ) : s) for s in matches]
311- startpos = pos - lastindex (prefix) + 1 - count (isequal (' ' ), prefix)
313+ function do_escape (s)
314+ return shell_escape ? replace (s, r" (\s |\\ )" => s "\\\0 " ) :
315+ string_escape ? escape_string (s, (' \" ' ,' $' )) :
316+ s
317+ end
318+
319+ matchList = Completion[PathCompletion (do_escape (s)) for s in matches]
320+ startpos = pos - lastindex (do_escape (prefix)) + 1
312321 # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`,
313322 # hence we need to add one to get the first index. This is also correct when considering
314323 # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`.
767776function close_path_completion (str, startpos, r, paths, pos)
768777 length (paths) == 1 || return false # Only close if there's a single choice...
769778 _path = str[startpos: prevind (str, first (r))] * (paths[1 ]:: PathCompletion ). path
770- path = expanduser (replace (_path, r "\\ " => " " ))
779+ path = expanduser (unescape_string ( replace (_path, " \\\$ " => " \$ " , " \\\" " => " \" " ) ))
771780 # ...except if it's a directory...
772781 try
773782 isdir (path)
@@ -1039,23 +1048,44 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
10391048 dotpos = something (findprev (isequal (' .' ), string, first (varrange)- 1 ), 0 )
10401049 return complete_identifiers! (Completion[], ffunc, context_module, string,
10411050 string[startpos: pos], pos, dotpos, startpos)
1042- # otherwise...
1043- elseif inc_tag in [:cmd , :string ]
1051+ elseif inc_tag === :cmd
10441052 m = match (r" [\t\n\r\" `><=*?|]| (?!\\ )" , reverse (partial))
10451053 startpos = nextind (partial, reverseind (partial, m. offset))
10461054 r = startpos: pos
10471055
1056+ # This expansion with "\\ "=>' ' replacement and shell_escape=true
1057+ # assumes the path isn't further quoted within the cmd backticks.
10481058 expanded = complete_expanduser (replace (string[r], r" \\ " => " " ), r)
10491059 expanded[3 ] && return expanded # If user expansion available, return it
10501060
1051- paths, r, success = complete_path (replace (string[r], r" \\ " => " " ), pos)
1061+ paths, r, success = complete_path (replace (string[r], r" \\ " => " " ), pos,
1062+ shell_escape= true )
1063+
1064+ return sort! (paths, by= p-> p. path), r, success
1065+ elseif inc_tag === :string
1066+ # Find first non-escaped quote
1067+ m = match (r" \" (?!\\ )" , reverse (partial))
1068+ startpos = nextind (partial, reverseind (partial, m. offset))
1069+ r = startpos: pos
1070+
1071+ expanded = complete_expanduser (string[r], r)
1072+ expanded[3 ] && return expanded # If user expansion available, return it
10521073
1053- if inc_tag === :string && close_path_completion (string, startpos, r, paths, pos)
1054- paths[1 ] = PathCompletion ((paths[1 ]:: PathCompletion ). path * " \" " )
1074+ path_prefix = try
1075+ unescape_string (replace (string[r], " \\\$ " => " \$ " , " \\\" " => " \" " ))
1076+ catch
1077+ nothing
10551078 end
1079+ if ! isnothing (path_prefix)
1080+ paths, r, success = complete_path (path_prefix, pos, string_escape= true )
10561081
1057- # Latex symbols can be completed for strings
1058- (success || inc_tag === :cmd ) && return sort! (paths, by= p-> p. path), r, success
1082+ if close_path_completion (string, startpos, r, paths, pos)
1083+ paths[1 ] = PathCompletion ((paths[1 ]:: PathCompletion ). path * " \" " )
1084+ end
1085+
1086+ # Fallthrough allowed so that Latex symbols can be completed in strings
1087+ success && return sort! (paths, by= p-> p. path), r, success
1088+ end
10591089 end
10601090
10611091 ok, ret = bslash_completions (string, pos)
0 commit comments