// pomprt, a line editor prompt library // Copyright (c) 2023 rini // // SPDX-License-Identifier: Apache-2.0 use std::process::Command; struct MiniShell; impl pomprt::Editor for MiniShell { fn complete(&self, buffer: &str, cursor: usize) -> Option { let mut start = buffer[..cursor].rfind(' ').map_or(0, |c| c + 1); let end = buffer[cursor..] .find(' ') .map_or(buffer.len(), |c| cursor + c); let word = &buffer[start..end]; let results = match buffer[..start].find(|c| c != ' ') { Some(_) => match word.rsplit_once('/') { Some((dir, name)) => { start += dir.len() + 1; complete_file(if dir.is_empty() { "/" } else { dir }, name) } None => complete_file(".", word), }, None => complete_file("/usr/bin", word), }; Some(pomprt::Completion(start..end, results)) } } fn complete_file(dir: &str, prefix: &str) -> Vec { let mut entries = std::fs::read_dir(dir).map_or(Vec::new(), |dir| { dir.filter_map(|entry| { let entry = entry.ok()?; let name = entry.file_name().into_string().ok()?; let suffix = if entry.path().is_dir() { "/" } else { " " }; name.starts_with(prefix).then(|| name + suffix) }) .collect() }); entries.sort_unstable(); entries } fn main() -> Result<(), Box> { let mut sh = pomprt::with(MiniShell, "% "); loop { let ok = match sh.read() { Ok(input) => { let mut args = input.split_ascii_whitespace(); if let Some(cmd) = args.next() { Command::new(cmd).args(args).spawn()?.wait()?.success() } else { true } } Err(pomprt::Interrupt) => false, Err(pomprt::Eof) => return Ok(()), Err(e) => Err(e)?, }; sh.set_prompt(if ok { "% " } else { "! " }); } }