mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Support quoted escaping also when ' or \ is present
Also, if there are more single quotes than double quotes and dollars, use double quotes for quoting.
This commit is contained in:
parent
88d6801720
commit
8d88b4d358
7 changed files with 94 additions and 40 deletions
|
@ -9,6 +9,7 @@ use crate::future_feature_flags::{feature_test, FeatureFlag};
|
|||
use crate::global_safety::AtomicRef;
|
||||
use crate::global_safety::RelaxedAtomicBool;
|
||||
use crate::libc::MB_CUR_MAX;
|
||||
use crate::parse_util::parse_util_escape_string_with_quote;
|
||||
use crate::termsize::Termsize;
|
||||
use crate::wchar::{decode_byte_from_char, encode_byte_to_char, prelude::*};
|
||||
use crate::wcstringutil::wcs2string_callback;
|
||||
|
@ -190,6 +191,9 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
|
|||
|
||||
let mut need_escape = false;
|
||||
let mut need_complex_escape = false;
|
||||
let mut double_quotes = 0;
|
||||
let mut single_quotes = 0;
|
||||
let mut dollars = 0;
|
||||
|
||||
if !no_quoted && input.is_empty() {
|
||||
return L!("''").to_owned();
|
||||
|
@ -267,7 +271,9 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
|
|||
}
|
||||
'\\' | '\'' => {
|
||||
need_escape = true;
|
||||
need_complex_escape = true;
|
||||
if c == '\'' {
|
||||
single_quotes += 1;
|
||||
}
|
||||
if escape_printables || (c == '\\' && !symbolic) {
|
||||
out.push('\\');
|
||||
}
|
||||
|
@ -286,6 +292,12 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
|
|||
|
||||
'&' | '$' | ' ' | '#' | '<' | '>' | '(' | ')' | '[' | ']' | '{' | '}' | '?' | '*'
|
||||
| '|' | ';' | '"' | '%' | '~' => {
|
||||
if c == '"' {
|
||||
double_quotes += 1;
|
||||
}
|
||||
if c == '$' {
|
||||
dollars += 1;
|
||||
}
|
||||
let char_is_normal = (c == '~' && no_tilde) || (c == '?' && no_qmark);
|
||||
if !char_is_normal {
|
||||
need_escape = true;
|
||||
|
@ -327,12 +339,20 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
|
|||
|
||||
// Use quoted escaping if possible, since most people find it easier to read.
|
||||
if !no_quoted && need_escape && !need_complex_escape && escape_printables {
|
||||
let single_quote = '\'';
|
||||
let quote = if single_quotes > double_quotes + dollars {
|
||||
'"'
|
||||
} else {
|
||||
'\''
|
||||
};
|
||||
out.clear();
|
||||
out.reserve(2 + input.len());
|
||||
out.push(single_quote);
|
||||
out.push_utfstr(input);
|
||||
out.push(single_quote);
|
||||
out.push(quote);
|
||||
out.push_utfstr(&parse_util_escape_string_with_quote(
|
||||
input,
|
||||
Some(quote),
|
||||
EscapeFlags::empty(),
|
||||
));
|
||||
out.push(quote);
|
||||
}
|
||||
|
||||
out
|
||||
|
|
|
@ -722,14 +722,10 @@ fn get_quote(cmd_str: &wstr, len: usize) -> Option<char> {
|
|||
pub fn parse_util_escape_string_with_quote(
|
||||
cmd: &wstr,
|
||||
quote: Option<char>,
|
||||
no_tilde: bool,
|
||||
escape_flags: EscapeFlags,
|
||||
) -> WString {
|
||||
let Some(quote) = quote else {
|
||||
let mut flags = EscapeFlags::NO_QUOTED;
|
||||
if no_tilde {
|
||||
flags |= EscapeFlags::NO_TILDE;
|
||||
}
|
||||
return escape_string(cmd, EscapeStringStyle::Script(flags));
|
||||
return escape_string(cmd, EscapeStringStyle::Script(escape_flags));
|
||||
};
|
||||
// Here we are going to escape a string with quotes.
|
||||
// A few characters cannot be represented inside quotes, e.g. newlines. In that case,
|
||||
|
@ -1863,40 +1859,70 @@ fn test_escape_quotes() {
|
|||
macro_rules! validate {
|
||||
($cmd:expr, $quote:expr, $no_tilde:expr, $expected:expr) => {
|
||||
assert_eq!(
|
||||
parse_util_escape_string_with_quote(L!($cmd), $quote, $no_tilde),
|
||||
parse_util_escape_string_with_quote(
|
||||
L!($cmd),
|
||||
$quote,
|
||||
if $no_tilde {
|
||||
EscapeFlags::NO_TILDE
|
||||
} else {
|
||||
EscapeFlags::empty()
|
||||
}
|
||||
),
|
||||
L!($expected)
|
||||
);
|
||||
};
|
||||
}
|
||||
macro_rules! validate_no_quoted {
|
||||
($cmd:expr, $quote:expr, $no_tilde:expr, $expected:expr) => {
|
||||
assert_eq!(
|
||||
parse_util_escape_string_with_quote(
|
||||
L!($cmd),
|
||||
$quote,
|
||||
EscapeFlags::NO_QUOTED
|
||||
| if $no_tilde {
|
||||
EscapeFlags::NO_TILDE
|
||||
} else {
|
||||
EscapeFlags::empty()
|
||||
}
|
||||
),
|
||||
L!($expected)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// These are "raw string literals"
|
||||
validate!("abc", None, false, "abc");
|
||||
validate!("abc~def", None, false, "abc\\~def");
|
||||
validate!("abc~def", None, false, "'abc~def'");
|
||||
validate!("abc~def", None, true, "abc~def");
|
||||
validate!("abc\\~def", None, false, "abc\\\\\\~def");
|
||||
validate!("abc\\~def", None, true, "abc\\\\~def");
|
||||
validate!("~abc", None, false, "\\~abc");
|
||||
validate!("~abc", None, false, "'~abc'");
|
||||
validate!("~abc", None, true, "~abc");
|
||||
validate!("~abc|def", None, false, "\\~abc\\|def");
|
||||
validate!("|abc~def", None, false, "\\|abc\\~def");
|
||||
validate!("|abc~def", None, true, "\\|abc~def");
|
||||
validate!("foo\nbar", None, false, "foo\\nbar");
|
||||
|
||||
// These are "raw string literals"
|
||||
validate_no_quoted!("abc", None, false, "abc");
|
||||
validate_no_quoted!("abc~def", None, false, "abc\\~def");
|
||||
validate_no_quoted!("abc~def", None, true, "abc~def");
|
||||
validate_no_quoted!("abc\\~def", None, false, "abc\\\\\\~def");
|
||||
validate_no_quoted!("abc\\~def", None, true, "abc\\\\~def");
|
||||
validate_no_quoted!("~abc", None, false, "\\~abc");
|
||||
validate_no_quoted!("~abc", None, true, "~abc");
|
||||
validate_no_quoted!("~abc|def", None, false, "\\~abc\\|def");
|
||||
validate_no_quoted!("|abc~def", None, false, "\\|abc\\~def");
|
||||
validate_no_quoted!("|abc~def", None, true, "\\|abc~def");
|
||||
validate_no_quoted!("foo\nbar", None, false, "foo\\nbar");
|
||||
|
||||
// Note tildes are not expanded inside quotes, so no_tilde is ignored with a quote.
|
||||
validate!("abc", Some('\''), false, "abc");
|
||||
validate!("abc\\def", Some('\''), false, "abc\\\\def");
|
||||
validate!("abc'def", Some('\''), false, "abc\\'def");
|
||||
validate!("~abc'def", Some('\''), false, "~abc\\'def");
|
||||
validate!("~abc'def", Some('\''), true, "~abc\\'def");
|
||||
validate!("foo\nba'r", Some('\''), false, "foo'\\n'ba\\'r");
|
||||
validate!("foo\\\\bar", Some('\''), false, "foo\\\\\\\\bar");
|
||||
validate_no_quoted!("abc", Some('\''), false, "abc");
|
||||
validate_no_quoted!("abc\\def", Some('\''), false, "abc\\\\def");
|
||||
validate_no_quoted!("abc'def", Some('\''), false, "abc\\'def");
|
||||
validate_no_quoted!("~abc'def", Some('\''), false, "~abc\\'def");
|
||||
validate_no_quoted!("~abc'def", Some('\''), true, "~abc\\'def");
|
||||
validate_no_quoted!("foo\nba'r", Some('\''), false, "foo'\\n'ba\\'r");
|
||||
validate_no_quoted!("foo\\\\bar", Some('\''), false, "foo\\\\\\\\bar");
|
||||
|
||||
validate!("abc", Some('"'), false, "abc");
|
||||
validate!("abc\\def", Some('"'), false, "abc\\\\def");
|
||||
validate!("~abc'def", Some('"'), false, "~abc'def");
|
||||
validate!("~abc'def", Some('"'), true, "~abc'def");
|
||||
validate!("foo\nba'r", Some('"'), false, "foo\"\\n\"ba'r");
|
||||
validate!("foo\\\\bar", Some('"'), false, "foo\\\\\\\\bar");
|
||||
validate_no_quoted!("abc", Some('"'), false, "abc");
|
||||
validate_no_quoted!("abc\\def", Some('"'), false, "abc\\\\def");
|
||||
validate_no_quoted!("~abc'def", Some('"'), false, "~abc'def");
|
||||
validate_no_quoted!("~abc'def", Some('"'), true, "~abc'def");
|
||||
validate_no_quoted!("foo\nba'r", Some('"'), false, "foo\"\\n\"ba'r");
|
||||
validate_no_quoted!("foo\\\\bar", Some('"'), false, "foo\\\\\\\\bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5091,6 +5091,11 @@ pub fn completion_apply_to_command_line(
|
|||
return replace_line_at_cursor(command_line, inout_cursor_pos, val_str);
|
||||
}
|
||||
|
||||
let mut escape_flags = EscapeFlags::NO_QUOTED;
|
||||
if no_tilde {
|
||||
escape_flags.insert(EscapeFlags::NO_TILDE);
|
||||
}
|
||||
|
||||
if do_replace_token {
|
||||
let mut move_cursor;
|
||||
let mut range = 0..0;
|
||||
|
@ -5157,7 +5162,7 @@ pub fn completion_apply_to_command_line(
|
|||
}
|
||||
}
|
||||
|
||||
parse_util_escape_string_with_quote(val_str, quote, no_tilde)
|
||||
parse_util_escape_string_with_quote(val_str, quote, escape_flags)
|
||||
} else {
|
||||
val_str.to_owned()
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ alias foo '"a b" c d e'
|
|||
# framework and we can't predict the definition.
|
||||
alias | grep -Ev '^alias (fish_indent|fish_key_reader) '
|
||||
# CHECK: alias a-2 'echo "hello there"'
|
||||
# CHECK: alias a-3 echo\\\ hello\\\\\\\ there
|
||||
# CHECK: alias a-3 'echo hello\\\\ there'
|
||||
# CHECK: alias foo '"a b" c d e'
|
||||
# CHECK: alias my_alias 'foo; and echo foo ran'
|
||||
|
||||
|
@ -28,7 +28,7 @@ alias l. "ls -d .*"
|
|||
alias d "'/mnt/c/Program Files (x86)/devenv.exe' /Edit"
|
||||
functions d
|
||||
# CHECK: # Defined via `source`
|
||||
# CHECK: function d --wraps=\'/mnt/c/Program\ Files\ \(x86\)/devenv.exe\'\ /Edit --description alias\ d\ \'/mnt/c/Program\ Files\ \(x86\)/devenv.exe\'\ /Edit
|
||||
# CHECK: function d --wraps="'/mnt/c/Program Files (x86)/devenv.exe' /Edit" --description "alias d '/mnt/c/Program Files (x86)/devenv.exe' /Edit"
|
||||
# CHECK: '/mnt/c/Program Files (x86)/devenv.exe' /Edit $argv
|
||||
# CHECK: end
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ complete
|
|||
# CHECK: complete --force-files t -l fileoption
|
||||
# CHECK: complete --no-files t -a '(t)'
|
||||
# CHECK: complete -p '/complete test/beta1' -s Z -d 'desc, desc'
|
||||
# CHECK: complete --require-parameter 'complete test beta2' -d desc\ \'\ desc2\ \[ -a 'foo bar'
|
||||
# CHECK: complete --require-parameter 'complete test beta2' -d "desc ' desc2 [" -a 'foo bar'
|
||||
# CHECK: complete --exclusive complete_test_beta2 -o test -n false
|
||||
# CHECK: complete {{.*}}
|
||||
# CHECK: complete {{.*}}
|
||||
|
|
|
@ -9,4 +9,4 @@ echo foo\x00bar | string escape
|
|||
# CHECK: foo
|
||||
# This one is just escaped
|
||||
echo foo\\x00bar | string escape
|
||||
# CHECK: foo\\x00bar
|
||||
# CHECK: 'foo\\x00bar'
|
||||
|
|
|
@ -260,6 +260,9 @@ echo \x07 | string escape
|
|||
# CHECK: \cg
|
||||
|
||||
string escape --style=script 'a b#c"\'d'
|
||||
# CHECK: 'a b#c"\'d'
|
||||
|
||||
string escape --no-quoted --style=script 'a b#c"\'d'
|
||||
# CHECK: a\ b\#c\"\'d
|
||||
|
||||
string escape --style=url 'a b#c"\'d'
|
||||
|
|
Loading…
Reference in a new issue