-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[vim] Replace s:fzf_shellescape and s:shellesc with fzf#shellescape #916
Conversation
|
@junegunn Any issues with using |
Yeah, looks like we don't need both |
The following examples mainly focus on the escape characters:
Metacharacters ( I haven't escaped variables (wrapped in :: Following comments before each command are the resulting prompt
:: If fzf doesn't run, output the error below the command
:: hello>
> fzf "--prompt" "hello>" "--multi" "--query" "world" > foobar.txt
:: he\^l^^l\\o>
> fzf "--prompt" "he\^l^^l\\o>" "--multi" "--query" "world" > foobar.txt
> fzf "--prompt" "he"llo>" "--multi" "--query" "world" > foobar.txt
The filename, directory name, or volume label syntax is incorrect
:: he"llo> --multi --query world
> fzf "--prompt" "he""llo>" "--multi" "--query" "world" > foobar.txt
> fzf "--prompt" "he\"llo>" "--multi" "--query" "world" > foobar.txt
The filename, directory name, or volume label syntax is incorrect
> fzf "--prompt" "he^"llo>" "--multi" "--query" "world" > foobar.txt
The filename, directory name, or volume label syntax is incorrect
> fzf "--prompt" "he\^"llo>" "--multi" "--query" "world" > foobar.txt
The filename, directory name, or volume label syntax is incorrect
> fzf "--prompt" ^"hello>" "--multi" "--query" "world" > foobar.txt
The filename, directory name, or volume label syntax is incorrect
:: hello>^
> fzf "--prompt" "hello>^" "--multi" "--query" "world" > foobar.txt
:: hello
> fzf "--prompt" ^"hello>^" "--multi" "--query" "world" > foobar.txt
:: hello>
> fzf "--prompt" ^"hello^>^" "--multi" "--query" "world" > foobar.txt
:: hello^>^ --multi --query world
> fzf "--prompt" ^"hel"lo^>^" "--multi" "--query" "world" > foobar.txt
:: hello> --multi --query world
> fzf "--prompt" ^"hel^"lo^>^" "--multi" "--query" "world" > foobar.txt
:: hel"lo>
> fzf "--prompt" ^"hel\^"lo^>^" "--multi" "--query" "world" > foobar.txt
:: hel"lo>" --multi --query world
> fzf "--prompt" ^"hel\^"lo^>\^" "--multi" "--query" "world" > foobar.txt
:: hel"lo>\
> fzf "--prompt" ^"hel\^"lo^>\\^" "--multi" "--query" "world" > foobar.txt
:: dir
> fzf "--prompt" ^"dir && ag fzf %USERPROFILE%\vimfiles ^" "--multi" "--query" "world" > foobar.txt
ERR: Error opening directory --multi : No such file or directory
:: dir && ag fzf C:\Users\janlazo\vimfiles
> fzf "--prompt" ^"dir ^&^& ag fzf %USERPROFILE%\vimfiles ^" "--multi" "--query" "world" > foobar.txt |
Thank you very much for the detailed explanation, do you think this is ready for merge? |
I'm testing this PR with junegunn/fzf.vim#372 so I want to make sure that both versions of I recommend that we keep both PRs open until you want to make a new release to minimize upgrade issues for the users. |
Sample prompt escaping via s:shellesc: \\\\^\^\\"\ becomes ^"\\\^^\^^\\\^"\\^"
Because of Do you have time to setup appveyor so we can convert the test cases in #916 (comment) to vader tests? |
Well I have no prior experience with appveyor and honestly I'm not sure if I can find time (and motivation) to look into that any time soon. |
Anyway, I like the idea of setting up Vader test cases for the function. And I can live with having the function visible for testing, i.e. |
How about using function! s:shellesc_sh(arg)
return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
endfunction
function! s:shellesc_cmd(arg)
let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g')
let escaped = substitute(escaped, '"', '\\^&', 'g')
let escaped = substitute(escaped, '\\\+\(\\^\)', '\\\\\1', 'g')
return '^"'.substitute(escaped, '[^\\]\zs\\$', '\\\\', '').'^"'
endfunction
function! fzf#shellesc(arg, ...)
let shell = exists('a:1') ? a:1 : &shell
if shell =~# 'cmd.exe$'
return s:shellesc_cmd(a:arg)
endif
return s:shellesc_sh(a:arg)
endfunction |
I like that. |
fzf#shellesc(string : {arg}, [string : {shell}]) Escape {arg} based on the shell. If {shell} is omitted, &shell is used. If {shell} contains cmd.exe, then escape {arg} for Window's cmd.exe Else, escape {arg} for sh.
LGTM, ready for merge? |
test/fzf.vader
Outdated
AssertEqual '""', fzf#shellesc('', 'sh') | ||
AssertEqual '"\"\""', fzf#shellesc('""', 'sh') | ||
AssertEqual '"foobar>"', fzf#shellesc('foobar>', 'sh') | ||
AssertEqual '"\\""', fzf#shellesc('\"', 'sh') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inner double quote is unescaped but this case is handled in s:shellesc_cmd
.
Need to handle backslashes preceding the escaped double quote.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember why I added s:shellesc
when there's builtin shellescape
. What do you think about removing s:shellesc_sh
and just using shellescape
instead?
EDIT: Maybe I was trying to allow $ENV
vars.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you remember the issue with shellescape
for $ENV
vars and tmux integration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I was not referring to tmux integration as it's no longer the default (thanks to --height
).
Maybe I was confused, I thought something like :call fzf#run({'source': 'echo $PATH'})
wouldn't work if we use builtin shellescape
, but it is unaffected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shellescape
in cygwin vim uses single quotes so it doesn't expand env vars.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Say that the user wants to use $VIMRUNTIME
as their prompt. What should appear, $VIMRUNTIME
or the resolved path after variable expansion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, actually this was not properly discussed when we added support for list typed options. Currently call fzf#run({'options': ['--prompt', '$USER']})
gives literal $USER
due to the use of builtin shellescape
, and I think it makes sense as one can manually expand the var when needed: call fzf#run({'options': ['--prompt', expand('$USER')]})
.
I briefly looked at the other places where s:shellesc
is used and it appeared that not expanding env vars anymore will not break anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR becomes Use fzf#shellescape for escaping shell arguments to deprecate s:shellesc
and the Window's version of s:fzf_shellescape
. To be consistent with the built-in shellescape
, I assume Windows-style env vars should be escaped.
plugin/fzf.vim
Outdated
endif | ||
|
||
function! s:shellesc_sh(arg) | ||
return '"'.substitute(a:arg, '"', '\\"', 'g').'"' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return '"'.escape(a:arg, '\"').'"'
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't test in a linux box atm but I had no issues with this change in cygwin (using ConEmu, not mintty). It didn't collide with s:escape
. Tested with this branch, https://github.com/janlazo/fzf/tree/vim/shellesc_cygwin_sh
If there's no issues on your end (including fzf.vim), I'll commit your suggestion (not the cygwin changes) and update the test cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works well. Please update as above. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, wait, I guess we should probably just use builtin shellescape
if it's okay.
The first escape in s:execute to handle %#! breaks FZF because fzf is run in the batchfile. |
It's a different issue, right? Escaping of let escaped = (a:use_height || s:is_win) ? a:command : escape(substitute(a:command, '\n', '\\n', 'g'), '%#!') |
- renamed from fzf#shellesc - replaces s:fzf_shellescape and s:shellesc - if shell argument is omitted, use &shell - if &shell is cmd.exe, use s:shellesc_cmd for batchfile escaping Else, use built-in shellescape. - returns a string so &shell treats it as literal string
@junegunn It's done with the test cases updated. |
Thank you 👍 |
Continuation of #896.
Previous PR cannot escape double quotes properly in
s:evaluate_opts
because of escaping assumptions made by cmd.exe. Instead on relyingshellescape
which relies onshellslash
, uses:shellesc
to escape with\
in Unix and\^
in Windows. For now, only"
is escaped this way. The last backslash is escaped the same way as ins:fzf_shellescape
.The outer double quotes are escaped via
^
so cmd.exe knows where the escaping starts and ends. Without it, shell redirection with>
fails even if the double quote character is escaped.