From 7b5a112d000bb21fd81569617353daa0c911d084 Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Tue, 17 Mar 2026 15:17:23 +0100 Subject: refactor completion logic --- src/completion.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 62 ++++++++----------------------------------------------- 2 files changed, 71 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/completion.rs b/src/completion.rs index 3e2565b..c7a183f 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -1,3 +1,4 @@ +use crate::parse::{self, CompletionContext}; use crate::{BString, Session}; use std::collections::HashMap; use std::ffi::OsStr; @@ -139,3 +140,64 @@ pub fn command_completion(session: Arc>, prefix: BString) -> Vec< out } + +pub struct CompletionResult { + pub kind: parse::CompletionKind, + pub suggestions: Vec, + pub shared_prefix: BString, +} + +impl CompletionResult { + pub fn empty() -> Self { + CompletionResult { + kind: parse::CompletionKind::None, + suggestions: Vec::new(), + shared_prefix: BString::new(), + } + } +} + +pub fn completion(session: Arc>, cmd: &[u8]) -> CompletionResult { + let comp = parse::completion_context( + &cmd, + &mut crate::run::Executor::new_for_completion(session.clone()), + ); + + let mut suggestions = match comp.kind { + parse::CompletionKind::Command => command_completion(session.clone(), comp.partial), + parse::CompletionKind::Argument => path_completion(comp.partial), + parse::CompletionKind::Variable => variable_completion(session.clone(), comp.partial), + parse::CompletionKind::None => return CompletionResult::empty(), + }; + + suggestions.sort_by(|x, y| x.delta.cmp(&y.delta)); + suggestions.dedup_by(|x, y| x.delta == y.delta); + + if suggestions.is_empty() { + return CompletionResult { + kind: comp.kind, + ..CompletionResult::empty() + }; + } + + // find longest shared prefix + let mut shared_prefix = &suggestions[0].delta[..]; + for s in suggestions.iter() { + let mut new = &shared_prefix[..0]; + for i in 0..shared_prefix.len().min(s.delta.len()) { + if shared_prefix[i] != s.delta[i] { + break; + } else { + new = &s.delta[..=i]; + } + } + shared_prefix = new; + } + let shared_prefix = shared_prefix.to_vec(); + + CompletionResult { + kind: comp.kind, + suggestions, + shared_prefix, + } +} diff --git a/src/main.rs b/src/main.rs index b7c3dc5..a9a0ff0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ pub mod wait; use linebuf::LineBuf; use raw::*; -use crate::completion::PathCache; +use crate::completion::{PathCache, completion}; use crate::ctrlc::CtrlC; use crate::cursor::{Direction, move_cursor}; use crate::history::HistoryEntry; @@ -368,65 +368,21 @@ fn event_loop() { b'\t' => { let cmd = se.line.pre().to_vec(); drop(se); - let comp = parse::completion_context( - &cmd, - &mut Executor::new_for_completion(session.clone()), - ); - - let mut suggestions = match comp.kind { - parse::CompletionKind::Command => { - completion::command_completion(session.clone(), comp.partial) - } - parse::CompletionKind::Argument => completion::path_completion(comp.partial), - parse::CompletionKind::Variable => { - completion::variable_completion(session.clone(), comp.partial) - } - parse::CompletionKind::None => continue, - }; - suggestions.sort_by(|x, y| x.delta.cmp(&y.delta)); - suggestions.dedup_by(|x, y| x.delta == y.delta); - - if suggestions.is_empty() { - continue; - } + let comp = completion(session.clone(), &cmd); let mut se = session.lock().unwrap(); - if suggestions.len() == 1 { - // apply suggestion - se.type_bytes(&suggestions[0].delta); - continue; - } - - // find longest shared prefix and type it - { - let mut longest = &suggestions[0].delta[..]; - for s in suggestions.iter() { - let mut new = &longest[..0]; - for i in 0..longest.len().min(s.delta.len()) { - if longest[i] != s.delta[i] { - break; - } else { - new = &s.delta[..=i]; - } - } - longest = new; - } + se.type_bytes(&comp.shared_prefix); - if !longest.is_empty() { - se.type_bytes(longest); + if comp.suggestions.len() > 1 { + print!("\r\n"); + for s in comp.suggestions { + io::stdout().lock().write_all(&s.display).unwrap(); + println!(); } + se.reprint_prompt(); } - - // one line below - print!("\r\n"); - for s in suggestions { - io::stdout().lock().write_all(&s.display).unwrap(); - println!(); - } - - se.reprint_prompt(); } // Escape sequence -- cgit v1.2.3