From e56e2d1f9206102068c402a0cadbe62284816586 Mon Sep 17 00:00:00 2001 From: Jonas Maier Date: Mon, 4 May 2026 23:28:17 +0200 Subject: completion now should not introduce strings that do not parse as strings --- src/completion.rs | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'src/completion.rs') diff --git a/src/completion.rs b/src/completion.rs index c628bd2..0e24d6d 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -1,4 +1,4 @@ -use crate::parse; +use crate::parse::{self, CompletionContext}; use crate::{BString, Session}; use std::collections::HashMap; use std::ffi::OsStr; @@ -9,15 +9,21 @@ use std::sync::{Arc, Mutex}; use std::{env, fs}; pub struct Suggestion { + /// display string that is shown in the possibilities pub display: BString, + + /// *escaped* bytes that can be directly appended into terminal. pub delta: BString, } fn _path_completion( - mut prefix: BString, + cc: CompletionContext, filter: &dyn Fn(&DirEntry) -> bool, ) -> std::io::Result> { + let delim = cc.delim; + let mut prefix = cc.partial; let mut partial_entry = BString::new(); + while let Some(c) = prefix.last().cloned() { if c == b'/' { break; @@ -40,12 +46,14 @@ fn _path_completion( } let name = entry.file_name().as_bytes().to_vec(); if name.starts_with(&partial_entry) { - let mut delta = name[partial_entry.len()..].to_vec(); + let mut delta = BString::new(); + delim.escape(&name[partial_entry.len()..], &mut delta); let is_dir = entry.metadata().map(|m| m.is_dir()).unwrap_or(false); if is_dir { delta.push(b'/'); } else { + delim.write_closing_delimiter(&mut delta); delta.push(b' '); } @@ -59,8 +67,8 @@ fn _path_completion( Ok(sugs) } -pub fn path_completion(prefix: BString) -> Vec { - match _path_completion(prefix, &|_| true) { +pub fn path_completion(cc: CompletionContext) -> Vec { + match _path_completion(cc, &|_| true) { Ok(suggestions) => suggestions, Err(err) => { println!("path completion failed: {err:?}\r"); @@ -69,8 +77,8 @@ pub fn path_completion(prefix: BString) -> Vec { } } -pub fn path_exe_completion(prefix: BString) -> Vec { - match _path_completion(prefix, &|d| is_executable(&d.path())) { +pub fn path_exe_completion(cc: CompletionContext) -> Vec { + match _path_completion(cc, &|d| is_executable(&d.path())) { Ok(suggestions) => suggestions, Err(err) => { println!("path completion failed: {err:?}\r"); @@ -134,7 +142,7 @@ pub fn populate_path_cache(session: Arc>) { session.lock().unwrap().path_cache = PathCache { binaries }; } -pub fn command_completion(session: Arc>, prefix: BString) -> Vec { +pub fn command_completion(session: Arc>, cc: CompletionContext) -> Vec { let se = session.lock().unwrap(); let mut out = Vec::new(); for fun in se @@ -143,18 +151,19 @@ pub fn command_completion(session: Arc>, prefix: BString) -> Vec< .chain(se.builtins.keys()) .chain(se.path_cache.binaries.keys()) { - if fun.starts_with(&prefix) { + if fun.starts_with(&cc.partial) { + let mut delta = BString::new(); + cc.delim.escape(&fun[cc.partial.len()..], &mut delta); + cc.delim.write_closing_delimiter(&mut delta); + delta.push(b' '); + out.push(Suggestion { display: fun.to_vec(), - delta: fun[prefix.len()..].to_vec(), + delta, }) } } - for s in out.iter_mut() { - s.delta.push(b' '); - } - out } @@ -180,10 +189,12 @@ pub fn completion(session: Arc>, cmd: &[u8]) -> CompletionResult &mut crate::run::Executor::new_for_completion(session.clone()), ); + let kind = comp.kind.clone(); + let mut suggestions = match comp.kind { - parse::CompletionKind::Command => command_completion(session.clone(), comp.partial), - parse::CompletionKind::PathCommand => path_exe_completion(comp.partial), - parse::CompletionKind::Argument => path_completion(comp.partial), + parse::CompletionKind::Command => command_completion(session.clone(), comp), + parse::CompletionKind::PathCommand => path_exe_completion(comp), + parse::CompletionKind::Argument => path_completion(comp), parse::CompletionKind::Variable => variable_completion(session.clone(), comp.partial), parse::CompletionKind::None => return CompletionResult::empty(), }; @@ -193,7 +204,7 @@ pub fn completion(session: Arc>, cmd: &[u8]) -> CompletionResult if suggestions.is_empty() { return CompletionResult { - kind: comp.kind, + kind, ..CompletionResult::empty() }; } @@ -214,7 +225,7 @@ pub fn completion(session: Arc>, cmd: &[u8]) -> CompletionResult let shared_prefix = shared_prefix.to_vec(); CompletionResult { - kind: comp.kind, + kind, suggestions, shared_prefix, } -- cgit v1.2.3