From 2e4f98b51caca9dfe642b88a34d7d72e50f873e4 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 19 Oct 2024 17:07:10 +0200 Subject: [PATCH] Do not add a space after completing inside brace expansion Another everyday annoyance, has been for many years. --- src/complete.rs | 13 ++++++++++++- src/expand.rs | 2 ++ src/tests/complete.rs | 6 ++++++ src/tokenizer.rs | 5 +++++ src/wildcard.rs | 24 +++++++----------------- 5 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/complete.rs b/src/complete.rs index 057a52387..84a770f86 100644 --- a/src/complete.rs +++ b/src/complete.rs @@ -739,6 +739,7 @@ impl<'ctx> Completer<'ctx> { ¤t_token[pos + 1..], /*do_file=*/ true, /*handle_as_special_cd=*/ false, + cur_tok.is_unterminated_brace, ); return; } @@ -837,7 +838,13 @@ impl<'ctx> Completer<'ctx> { } // 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. self.mark_completions_duplicating_arguments(&cmdline, current_token, tokens); @@ -1511,6 +1518,7 @@ impl<'ctx> Completer<'ctx> { s: &wstr, do_file: bool, handle_as_special_cd: bool, + is_unterminated_brace: bool, ) { if self.ctx.check_cancel() { return; @@ -1522,6 +1530,9 @@ impl<'ctx> Completer<'ctx> { if !do_file { flags |= ExpandFlags::SKIP_WILDCARDS; } + if is_unterminated_brace { + flags |= ExpandFlags::NO_SPACE_FOR_UNCLOSED_BRACE; + } if handle_as_special_cd && do_file { if self.flags.autosuggestion { diff --git a/src/expand.rs b/src/expand.rs index d8f694ff1..99b418b97 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -72,6 +72,8 @@ bitflags! { /// Do expansions specifically to support external command completions. This means using PATH as /// a list of potential working directories. 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; } } diff --git a/src/tests/complete.rs b/src/tests/complete.rs index 1835390bc..891e41c08 100644 --- a/src/tests/complete.rs +++ b/src/tests/complete.rs @@ -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 unique_completion_applies_as!( "touch test/complete_test/bracket[", diff --git a/src/tokenizer.rs b/src/tokenizer.rs index ec3a52ba9..722f52654 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -67,6 +67,8 @@ pub struct Tok { // If an error, this is the error code. pub error: TokenizerError, + pub is_unterminated_brace: bool, + // The type of the token. pub type_: TokenType, } @@ -206,6 +208,7 @@ impl Tok { error_offset_within_token: SOURCE_OFFSET_INVALID.try_into().unwrap(), error_length: 0, error: TokenizerError::none, + is_unterminated_brace: false, type_: r#type, } } @@ -550,6 +553,7 @@ impl<'c> Tokenizer<'c> { error_offset_within_token: (error_loc - token_start) as u32, error_length: error_len as u32, error: error_type, + is_unterminated_brace: false, type_: TokenType::error, } } @@ -785,6 +789,7 @@ impl<'c> Tokenizer<'c> { let mut result = Tok::new(TokenType::string); result.set_offset(buff_start); result.set_length(self.token_cursor - buff_start); + result.is_unterminated_brace = mode & TOK_MODE_CURLY_BRACES; result } } diff --git a/src/wildcard.rs b/src/wildcard.rs index aa4f30c86..eeb840cf8 100644 --- a/src/wildcard.rs +++ b/src/wildcard.rs @@ -341,6 +341,10 @@ fn wildcard_test_flags_then_complete( ) -> bool { let executables_only = expand_flags.contains(ExpandFlags::EXECUTABLES_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, // 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 @@ -357,15 +361,7 @@ fn wildcard_test_flags_then_complete( ) == WildcardResult::Match; } // Check if it will match before stat(). - if wildcard_complete( - filename, - wc, - None, - None, - expand_flags, - CompleteFlags::default(), - ) != WildcardResult::Match - { + if wildcard_complete(filename, wc, None, None, expand_flags, flags) != WildcardResult::Match { return false; } @@ -432,14 +428,8 @@ fn wildcard_test_flags_then_complete( ) == WildcardResult::Match; } - wildcard_complete( - filename, - wc, - desc_func, - Some(out), - expand_flags, - CompleteFlags::empty(), - ) == WildcardResult::Match + wildcard_complete(filename, wc, desc_func, Some(out), expand_flags, flags) + == WildcardResult::Match } use expander::WildCardExpander;