diff --git a/src/complete.rs b/src/complete.rs index fc146f15e..63c9ac4cf 100644 --- a/src/complete.rs +++ b/src/complete.rs @@ -9,7 +9,11 @@ use std::{ time::{Duration, Instant}, }; -use crate::{common::charptr2wcstring, util::wcsfilecmp}; +use crate::{ + common::charptr2wcstring, + reader::{get_quote, is_backslashed}, + util::wcsfilecmp, +}; use bitflags::bitflags; use once_cell::sync::Lazy; use printf_compat::sprintf; @@ -1522,6 +1526,7 @@ impl<'ctx> Completer<'ctx> { flags -= ExpandFlags::GEN_DESCRIPTIONS; } + // Expand words separated by '=' separately, unless '=' is escaped or quoted. // We have the following cases: // // --foo=bar => expand just bar @@ -1529,13 +1534,25 @@ impl<'ctx> Completer<'ctx> { // foo=bar => expand the whole thing, and also just bar // // We also support colon separator (#2178). If there's more than one, prefer the last one. - let sep_index = s.chars().rposition(|c| c == '=' || c == ':'); + let sep_index = if get_quote(s, s.len()).is_some() { + None + } else { + let mut end = s.len(); + loop { + match s[..end].chars().rposition(|c| c == '=' || c == ':') { + Some(pos) => { + if !is_backslashed(s, pos) { + break Some(pos); + } + end = pos; + } + None => break None, + } + } + }; let complete_from_start = sep_index.is_none() || !string_prefixes_string(L!("-"), s); if let Some(sep_index) = sep_index { - // FIXME: This just cuts the token, - // so any quoting or braces gets lost. - // See #4954. let sep_string = s.slice_from(sep_index + 1); let mut local_completions = Vec::new(); if expand_string( diff --git a/src/reader.rs b/src/reader.rs index f35c55796..41830d630 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -5043,7 +5043,7 @@ fn try_expand_wildcard( /// Test if the specified character in the specified string is backslashed. pos may be at the end of /// the string, which indicates if there is a trailing backslash. -fn is_backslashed(s: &wstr, pos: usize) -> bool { +pub(crate) fn is_backslashed(s: &wstr, pos: usize) -> bool { // note pos == str.size() is OK. if pos > s.len() { return false; @@ -5093,7 +5093,7 @@ fn replace_line_at_cursor( text[..start].to_owned() + replacement + &text[end..] } -fn get_quote(cmd_str: &wstr, len: usize) -> Option { +pub(crate) fn get_quote(cmd_str: &wstr, len: usize) -> Option { let cmd = cmd_str.as_char_slice(); let mut i = 0; while i < cmd.len() {