Do not add a space after completing inside brace expansion

Another everyday annoyance, has been for many years.
This commit is contained in:
Johannes Altmanninger 2024-10-19 17:07:10 +02:00
parent c41dbe4551
commit 2e4f98b51c
5 changed files with 32 additions and 18 deletions

View file

@ -739,6 +739,7 @@ impl<'ctx> Completer<'ctx> {
&current_token[pos + 1..], &current_token[pos + 1..],
/*do_file=*/ true, /*do_file=*/ true,
/*handle_as_special_cd=*/ false, /*handle_as_special_cd=*/ false,
cur_tok.is_unterminated_brace,
); );
return; return;
} }
@ -837,7 +838,13 @@ impl<'ctx> Completer<'ctx> {
} }
// This function wants the unescaped string. // This function wants the unescaped string.
self.complete_param_expand(L!(""), current_argument, do_file, handle_as_special_cd); self.complete_param_expand(
L!(""),
current_argument,
do_file,
handle_as_special_cd,
cur_tok.is_unterminated_brace,
);
// Lastly mark any completions that appear to already be present in arguments. // Lastly mark any completions that appear to already be present in arguments.
self.mark_completions_duplicating_arguments(&cmdline, current_token, tokens); self.mark_completions_duplicating_arguments(&cmdline, current_token, tokens);
@ -1511,6 +1518,7 @@ impl<'ctx> Completer<'ctx> {
s: &wstr, s: &wstr,
do_file: bool, do_file: bool,
handle_as_special_cd: bool, handle_as_special_cd: bool,
is_unterminated_brace: bool,
) { ) {
if self.ctx.check_cancel() { if self.ctx.check_cancel() {
return; return;
@ -1522,6 +1530,9 @@ impl<'ctx> Completer<'ctx> {
if !do_file { if !do_file {
flags |= ExpandFlags::SKIP_WILDCARDS; flags |= ExpandFlags::SKIP_WILDCARDS;
} }
if is_unterminated_brace {
flags |= ExpandFlags::NO_SPACE_FOR_UNCLOSED_BRACE;
}
if handle_as_special_cd && do_file { if handle_as_special_cd && do_file {
if self.flags.autosuggestion { if self.flags.autosuggestion {

View file

@ -72,6 +72,8 @@ bitflags! {
/// Do expansions specifically to support external command completions. This means using PATH as /// Do expansions specifically to support external command completions. This means using PATH as
/// a list of potential working directories. /// a list of potential working directories.
const SPECIAL_FOR_COMMAND = 1 << 13; const SPECIAL_FOR_COMMAND = 1 << 13;
/// The token has an unclosed brace, so don't add a space.
const NO_SPACE_FOR_UNCLOSED_BRACE = 1 << 14;
} }
} }

View file

@ -159,6 +159,12 @@ fn test_complete() {
}; };
} }
unique_completion_applies_as!(
"touch test/complete_test/{testfi",
r"le",
"touch test/complete_test/{testfile",
);
// Brackets - see #5831 // Brackets - see #5831
unique_completion_applies_as!( unique_completion_applies_as!(
"touch test/complete_test/bracket[", "touch test/complete_test/bracket[",

View file

@ -67,6 +67,8 @@ pub struct Tok {
// If an error, this is the error code. // If an error, this is the error code.
pub error: TokenizerError, pub error: TokenizerError,
pub is_unterminated_brace: bool,
// The type of the token. // The type of the token.
pub type_: TokenType, pub type_: TokenType,
} }
@ -206,6 +208,7 @@ impl Tok {
error_offset_within_token: SOURCE_OFFSET_INVALID.try_into().unwrap(), error_offset_within_token: SOURCE_OFFSET_INVALID.try_into().unwrap(),
error_length: 0, error_length: 0,
error: TokenizerError::none, error: TokenizerError::none,
is_unterminated_brace: false,
type_: r#type, type_: r#type,
} }
} }
@ -550,6 +553,7 @@ impl<'c> Tokenizer<'c> {
error_offset_within_token: (error_loc - token_start) as u32, error_offset_within_token: (error_loc - token_start) as u32,
error_length: error_len as u32, error_length: error_len as u32,
error: error_type, error: error_type,
is_unterminated_brace: false,
type_: TokenType::error, type_: TokenType::error,
} }
} }
@ -785,6 +789,7 @@ impl<'c> Tokenizer<'c> {
let mut result = Tok::new(TokenType::string); let mut result = Tok::new(TokenType::string);
result.set_offset(buff_start); result.set_offset(buff_start);
result.set_length(self.token_cursor - buff_start); result.set_length(self.token_cursor - buff_start);
result.is_unterminated_brace = mode & TOK_MODE_CURLY_BRACES;
result result
} }
} }

View file

@ -341,6 +341,10 @@ fn wildcard_test_flags_then_complete(
) -> bool { ) -> bool {
let executables_only = expand_flags.contains(ExpandFlags::EXECUTABLES_ONLY); let executables_only = expand_flags.contains(ExpandFlags::EXECUTABLES_ONLY);
let need_directory = expand_flags.contains(ExpandFlags::DIRECTORIES_ONLY); let need_directory = expand_flags.contains(ExpandFlags::DIRECTORIES_ONLY);
let mut flags = CompleteFlags::default();
if expand_flags.contains(ExpandFlags::NO_SPACE_FOR_UNCLOSED_BRACE) {
flags |= CompleteFlags::NO_SPACE;
}
// Fast path: If we need directories, and we already know it is one, // Fast path: If we need directories, and we already know it is one,
// and we don't need to do anything else, just return it. // and we don't need to do anything else, just return it.
// This is a common case for cd completions, and removes the `stat` entirely in case the system // This is a common case for cd completions, and removes the `stat` entirely in case the system
@ -357,15 +361,7 @@ fn wildcard_test_flags_then_complete(
) == WildcardResult::Match; ) == WildcardResult::Match;
} }
// Check if it will match before stat(). // Check if it will match before stat().
if wildcard_complete( if wildcard_complete(filename, wc, None, None, expand_flags, flags) != WildcardResult::Match {
filename,
wc,
None,
None,
expand_flags,
CompleteFlags::default(),
) != WildcardResult::Match
{
return false; return false;
} }
@ -432,14 +428,8 @@ fn wildcard_test_flags_then_complete(
) == WildcardResult::Match; ) == WildcardResult::Match;
} }
wildcard_complete( wildcard_complete(filename, wc, desc_func, Some(out), expand_flags, flags)
filename, == WildcardResult::Match
wc,
desc_func,
Some(out),
expand_flags,
CompleteFlags::empty(),
) == WildcardResult::Match
} }
use expander::WildCardExpander; use expander::WildCardExpander;