use crate::*; use std::path::{Path, PathBuf}; use std::{env, fs}; pub struct Suggestion { pub display: BString, pub delta: BString, } fn _path_completion(mut prefix: BString) -> io::Result> { let mut partial_entry = BString::new(); while let Some(c) = prefix.last().cloned() { if c == b'/' { break; } partial_entry.push(c); prefix.pop(); } partial_entry.reverse(); let mut sugs = Vec::new(); if prefix.is_empty() { prefix.push(b'.'); } for entry in fs::read_dir(OsStr::from_bytes(&prefix))? { let entry = entry?; 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 is_dir = entry.metadata().map(|m| m.is_dir()).unwrap_or(false); if is_dir { delta.push(b'/'); } else { delta.push(b' '); } sugs.push(Suggestion { display: name, delta, }); } } Ok(sugs) } pub fn path_completion(prefix: BString) -> Vec { match _path_completion(prefix) { Ok(suggestions) => suggestions, Err(err) => { println!("path completion failed: {err:?}\r"); Vec::new() } } } pub fn variable_completion(session: Arc>, prefix: BString) -> Vec { let se = session.lock().unwrap(); let mut out = Vec::new(); for var in se.vars.keys() { if var.starts_with(&prefix) { out.push(Suggestion { display: var.to_vec(), delta: var[prefix.len()..].to_vec(), }); } } drop(se); for var in env::vars_os() { let var = var.0.as_bytes(); if var.starts_with(&prefix) { out.push(Suggestion { display: var.to_vec(), delta: var[prefix.len()..].to_vec(), }); } } out } #[derive(Default)] pub struct PathCache { binaries: HashMap, } fn is_executable(path: &Path) -> bool { use std::os::unix::fs::PermissionsExt; fs::metadata(path) .map(|m| m.permissions().mode() & 0o111 != 0) .unwrap_or(false) } pub fn populate_path_cache(session: Arc>) { let path_var = env::var_os("PATH").unwrap(); let mut binaries = HashMap::new(); for dir in env::split_paths(&path_var) { if let Ok(entries) = fs::read_dir(&dir) { for entry in entries.flatten() { let path = entry.path(); if path.is_file() && is_executable(&path) { binaries.insert(path.file_name().unwrap().as_bytes().to_vec(), path); } } } } session.lock().unwrap().path_cache = PathCache { binaries }; } pub fn command_completion(session: Arc>, prefix: BString) -> Vec { let se = session.lock().unwrap(); let mut out = Vec::new(); for fun in se.funs.keys().chain(se.builtins.keys()).chain(se.path_cache.binaries.keys()) { if fun.starts_with(&prefix) { out.push(Suggestion { display: fun.to_vec(), delta: fun[prefix.len()..].to_vec(), }) } } for s in out.iter_mut() { s.delta.push(b' '); } out }