mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 21:03:12 +00:00
Insert some completions with quotes instead of backslashes
File names that have lots of spaces look quite ugly when inserted as completions because every space will have a backslash. Add an initial heuristic to decide when to use quotes instead of backslash escapes. Quote when 1. it's not an autosuggestion 2. we replace the token or insert a fresh one 3. we will add a space at the end In future we could relax some of these requirements. Requirement 2 means we don't quote when appending to an existing token. Need to find a natural behavior here. Re 3, if the completion adds no space, users will probably want to add more characters, which looks a bit weird if the token has a trailing quote. We could relax this requirement for directory completions, so «ls so» completes to «ls 'some dir with spaces'/». Closes #5433
This commit is contained in:
parent
cacfcf8089
commit
29dc307111
7 changed files with 17 additions and 58 deletions
|
@ -95,6 +95,7 @@ Interactive improvements
|
||||||
------------------------
|
------------------------
|
||||||
- Command-specific tab completions may now offer results whose first character is a period. For example, it is now possible to tab-complete ``git add`` for files with leading periods. The default file completions hide these files, unless the token itself has a leading period (:issue:`3707`).
|
- Command-specific tab completions may now offer results whose first character is a period. For example, it is now possible to tab-complete ``git add`` for files with leading periods. The default file completions hide these files, unless the token itself has a leading period (:issue:`3707`).
|
||||||
- Option completion now uses fuzzy subsequence filtering, as non-option completion does. This means that ``--fb`` may be completed to ``--foobar`` if there is no better match.
|
- Option completion now uses fuzzy subsequence filtering, as non-option completion does. This means that ``--fb`` may be completed to ``--foobar`` if there is no better match.
|
||||||
|
- Completions that insert an entire token now use quotes instead of backslashes to escape special characters (:issue:`5433`).
|
||||||
- Autosuggestions were sometimes not shown after recalling a line from history, which has been fixed (:issue:`10287`).
|
- Autosuggestions were sometimes not shown after recalling a line from history, which has been fixed (:issue:`10287`).
|
||||||
- Nonprintable ASCII control characters are now rendered using symbols from Unicode's Control Pictures block (:issue:`5274`).
|
- Nonprintable ASCII control characters are now rendered using symbols from Unicode's Control Pictures block (:issue:`5274`).
|
||||||
- When a command like ``fg %2`` fails to find the given job, it no longer behaves as if no job spec was given (:issue:`9835`).
|
- When a command like ``fg %2`` fails to find the given job, it no longer behaves as if no job spec was given (:issue:`9835`).
|
||||||
|
|
|
@ -674,48 +674,6 @@ fn error_for_character(c: char) -> WString {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates information on the parameter at the specified index.
|
|
||||||
///
|
|
||||||
/// \param cmd The command to be analyzed
|
|
||||||
/// \param pos An index in the string which is inside the parameter
|
|
||||||
/// \return the type of quote used by the parameter: either ' or " or \0.
|
|
||||||
pub fn parse_util_get_quote_type(cmd: &wstr, pos: usize) -> Option<char> {
|
|
||||||
let mut tok = Tokenizer::new(cmd, TOK_ACCEPT_UNFINISHED);
|
|
||||||
while let Some(token) = tok.next() {
|
|
||||||
if token.type_ == TokenType::string && token.location_in_or_at_end_of_source_range(pos) {
|
|
||||||
return get_quote(tok.text_of(&token), pos - token.offset());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_quote(cmd_str: &wstr, len: usize) -> Option<char> {
|
|
||||||
let cmd = cmd_str.as_char_slice();
|
|
||||||
let mut i = 0;
|
|
||||||
while i < cmd.len() {
|
|
||||||
if cmd[i] == '\\' {
|
|
||||||
i += 1;
|
|
||||||
if i == cmd_str.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
} else if cmd[i] == '\'' || cmd[i] == '"' {
|
|
||||||
match quote_end(cmd_str, i, cmd[i]) {
|
|
||||||
Some(end) => {
|
|
||||||
if end > len {
|
|
||||||
return Some(cmd[i]);
|
|
||||||
}
|
|
||||||
i = end + 1;
|
|
||||||
}
|
|
||||||
None => return Some(cmd[i]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to escape the string 'cmd' using the given quote type, as determined by the quote
|
/// Attempts to escape the string 'cmd' using the given quote type, as determined by the quote
|
||||||
/// character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and
|
/// character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and
|
||||||
/// thus escaping should be with backslashes). Optionally do not escape tildes.
|
/// thus escaping should be with backslashes). Optionally do not escape tildes.
|
||||||
|
|
|
@ -5119,7 +5119,10 @@ pub fn completion_apply_to_command_line(
|
||||||
return replace_line_at_cursor(command_line, inout_cursor_pos, val_str);
|
return replace_line_at_cursor(command_line, inout_cursor_pos, val_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut escape_flags = EscapeFlags::NO_QUOTED;
|
let mut escape_flags = EscapeFlags::empty();
|
||||||
|
if append_only || !add_space {
|
||||||
|
escape_flags.insert(EscapeFlags::NO_QUOTED);
|
||||||
|
}
|
||||||
if no_tilde {
|
if no_tilde {
|
||||||
escape_flags.insert(EscapeFlags::NO_TILDE);
|
escape_flags.insert(EscapeFlags::NO_TILDE);
|
||||||
}
|
}
|
||||||
|
@ -5132,17 +5135,7 @@ pub fn completion_apply_to_command_line(
|
||||||
let mut sb = command_line[..range.start].to_owned();
|
let mut sb = command_line[..range.start].to_owned();
|
||||||
|
|
||||||
if do_escape {
|
if do_escape {
|
||||||
let escaped = escape_string(
|
let escaped = escape_string(val_str, EscapeStringStyle::Script(escape_flags));
|
||||||
val_str,
|
|
||||||
EscapeStringStyle::Script(
|
|
||||||
EscapeFlags::NO_QUOTED
|
|
||||||
| if no_tilde {
|
|
||||||
EscapeFlags::NO_TILDE
|
|
||||||
} else {
|
|
||||||
EscapeFlags::empty()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
sb.push_utfstr(&escaped);
|
sb.push_utfstr(&escaped);
|
||||||
move_cursor = escaped.len();
|
move_cursor = escaped.len();
|
||||||
} else {
|
} else {
|
||||||
|
@ -5168,8 +5161,10 @@ pub fn completion_apply_to_command_line(
|
||||||
let mut tok = 0..0;
|
let mut tok = 0..0;
|
||||||
parse_util_token_extent(command_line, cursor_pos, &mut tok, None);
|
parse_util_token_extent(command_line, cursor_pos, &mut tok, None);
|
||||||
// Find the last quote in the token to complete.
|
// Find the last quote in the token to complete.
|
||||||
|
let mut have_token = false;
|
||||||
if tok.contains(&cursor_pos) || cursor_pos == tok.end {
|
if tok.contains(&cursor_pos) || cursor_pos == tok.end {
|
||||||
quote = get_quote(&command_line[tok.clone()], cursor_pos - tok.start);
|
quote = get_quote(&command_line[tok.clone()], cursor_pos - tok.start);
|
||||||
|
have_token = !tok.is_empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the token is reported as unquoted, but ends with a (unescaped) quote, and we can
|
// If the token is reported as unquoted, but ends with a (unescaped) quote, and we can
|
||||||
|
@ -5185,6 +5180,10 @@ pub fn completion_apply_to_command_line(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if have_token {
|
||||||
|
escape_flags.insert(EscapeFlags::NO_QUOTED);
|
||||||
|
}
|
||||||
|
|
||||||
parse_util_escape_string_with_quote(val_str, quote, escape_flags)
|
parse_util_escape_string_with_quote(val_str, quote, escape_flags)
|
||||||
} else {
|
} else {
|
||||||
val_str.to_owned()
|
val_str.to_owned()
|
||||||
|
|
|
@ -159,7 +159,7 @@ fn test_complete() {
|
||||||
&mut cursor,
|
&mut cursor,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
assert_eq!(newcmdline, L!("touch test/complete_test/bracket\\[abc\\] "));
|
assert_eq!(newcmdline, L!("touch 'test/complete_test/bracket[abc]' "));
|
||||||
|
|
||||||
// #8820
|
// #8820
|
||||||
let mut cursor_pos = 11;
|
let mut cursor_pos = 11;
|
||||||
|
@ -191,7 +191,7 @@ fn test_complete() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
newcmdline,
|
newcmdline,
|
||||||
L!(r"touch test/complete_test/gnarlybracket\\\[abc\] ")
|
L!(r"touch 'test/complete_test/gnarlybracket\\[abc]' ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub enum TokenizerError {
|
||||||
expected_bclose_found_pclose,
|
expected_bclose_found_pclose,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Tok {
|
pub struct Tok {
|
||||||
// Offset of the token.
|
// Offset of the token.
|
||||||
pub offset: u32,
|
pub offset: u32,
|
||||||
|
|
|
@ -10,7 +10,7 @@ complete -c complete_test_alpha3 --no-files -w 'complete_test_alpha2 extra2'
|
||||||
complete -C'complete_test_alpha1 arg1 '
|
complete -C'complete_test_alpha1 arg1 '
|
||||||
# CHECK: complete_test_alpha1 arg1
|
# CHECK: complete_test_alpha1 arg1
|
||||||
complete --escape -C'complete_test_alpha1 arg1 '
|
complete --escape -C'complete_test_alpha1 arg1 '
|
||||||
# CHECK: complete_test_alpha1\ arg1\
|
# CHECK: 'complete_test_alpha1 arg1 '
|
||||||
complete -C'complete_test_alpha2 arg2 '
|
complete -C'complete_test_alpha2 arg2 '
|
||||||
# CHECK: complete_test_alpha1 extra1 arg2
|
# CHECK: complete_test_alpha1 extra1 arg2
|
||||||
complete -C'complete_test_alpha3 arg3 '
|
complete -C'complete_test_alpha3 arg3 '
|
||||||
|
|
|
@ -53,7 +53,7 @@ expect_prompt("foo")
|
||||||
sendline("complete -c foo -xa '(commandline)'")
|
sendline("complete -c foo -xa '(commandline)'")
|
||||||
expect_prompt()
|
expect_prompt()
|
||||||
send("foo bar \t")
|
send("foo bar \t")
|
||||||
expect_str("foo bar foo\ bar\ ")
|
expect_str("foo bar 'foo bar '")
|
||||||
send("\b" * 64)
|
send("\b" * 64)
|
||||||
|
|
||||||
# Commandline works when run on its own (#8807).
|
# Commandline works when run on its own (#8807).
|
||||||
|
|
Loading…
Reference in a new issue