From 97641c7bf6d1094f3b3bcfafd7a07cd9f0f74e66 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Thu, 24 Apr 2025 17:02:09 +0200 Subject: [PATCH] Set transient command line in custom completions (Rust port regression) Commit df3b0bd89fa (Fix commandline state for custom completions with variable overrides, 2022-01-26) made us push a transient command line for custom completions based on a tautological null-pointer check ("var_assignments"). Commit 77aeb6a2a88 (Port execution, 2023-10-08) turned the null pointer into a reference and replaced the check with "!ad.var_assignments.is_empty()". This broke scenarios that relied on the transient commandline. In particular the attached test cases rely on the transient commandline implicitly placing the cursor at the end, irrespective of the cursor in the actual commandline. I'm not sure if there is an easy way to identify these scenarios. Let's restore historical behavior by always pushing the transient command line. Fixes #11423 --- src/complete.rs | 11 ++++------- tests/pexpects/complete.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/complete.rs b/src/complete.rs index 8975b2de6..4a679d72a 100644 --- a/src/complete.rs +++ b/src/complete.rs @@ -1953,19 +1953,16 @@ fn complete_custom(&mut self, cmd: &wstr, cmdline: &wstr, ad: &mut CustomArgData // Perhaps set a transient commandline so that custom completions // builtin_commandline will refer to the wrapped command. But not if // we're doing autosuggestions. - let mut _remove_transient = None; - let wants_transient = - (ad.wrap_depth > 0 || !ad.var_assignments.is_empty()) && !is_autosuggest; - if wants_transient { + let _remove_transient = (!is_autosuggest).then(|| { let parser = self.ctx.parser(); let saved_transient = parser .libdata_mut() .transient_commandline .replace(cmdline.to_owned()); - _remove_transient = Some(ScopeGuard::new((), move |_| { + ScopeGuard::new((), move |_| { parser.libdata_mut().transient_commandline = saved_transient; - })); - } + }) + }); // Maybe apply variable assignments. let _restore_vars = self.apply_var_assignments(ad.var_assignments); diff --git a/tests/pexpects/complete.py b/tests/pexpects/complete.py index 843d4d192..ce432f40b 100644 --- a/tests/pexpects/complete.py +++ b/tests/pexpects/complete.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from pexpect_helper import SpawnedProc +from pexpect_helper import SpawnedProc, control sp = SpawnedProc() send, sendline, sleep, expect_prompt, expect_re, expect_str = ( @@ -78,3 +78,15 @@ sendline("echo bar") expect_re("\n.*bar") sendline("echo fo\t") expect_re("foooo") + +# Custom completions that access the command line. +sendline("complete -e :; complete : -a '(echo (commandline -ct)-completed)'") +send(": abcd" + control("b") * 2 + "\t") +expect_str(": abcd-completed") +send(control("u")) +# Another one. +sendline("mkdir -p foo/bar; touch foo/bar/baz.fish") +send("source foo/b/baz.fish") +send(control("b") * 9 + "\t") +expect_str("source foo/bar/baz.fish") +send(control("u"))