completion/
completion.rs

1// pomprt, a line editor prompt library
2// Copyright (c) 2023 rini
3//
4// SPDX-License-Identifier: Apache-2.0
5
6use std::process::Command;
7
8struct MiniShell;
9
10impl pomprt::Editor for MiniShell {
11    fn complete(&self, buffer: &str, cursor: usize) -> Option<pomprt::Completion> {
12        let mut start = buffer[..cursor].rfind(' ').map_or(0, |c| c + 1);
13        let end = buffer[cursor..]
14            .find(' ')
15            .map_or(buffer.len(), |c| cursor + c);
16        let word = &buffer[start..end];
17
18        let results = match buffer[..start].find(|c| c != ' ') {
19            Some(_) => match word.rsplit_once('/') {
20                Some((dir, name)) => {
21                    start += dir.len() + 1;
22                    complete_file(if dir.is_empty() { "/" } else { dir }, name)
23                }
24                None => complete_file(".", word),
25            },
26            None => complete_file("/usr/bin", word),
27        };
28
29        Some(pomprt::Completion(start..end, results))
30    }
31}
32
33fn complete_file(dir: &str, prefix: &str) -> Vec<String> {
34    let mut entries = std::fs::read_dir(dir).map_or(Vec::new(), |dir| {
35        dir.filter_map(|entry| {
36            let entry = entry.ok()?;
37            let name = entry.file_name().into_string().ok()?;
38            let suffix = if entry.path().is_dir() { "/" } else { " " };
39            name.starts_with(prefix).then(|| name + suffix)
40        })
41        .collect()
42    });
43    entries.sort_unstable();
44    entries
45}
46
47fn main() -> Result<(), Box<dyn std::error::Error>> {
48    let mut sh = pomprt::with(MiniShell, "% ");
49
50    loop {
51        let ok = match sh.read() {
52            Ok(input) => {
53                let mut args = input.split_ascii_whitespace();
54                if let Some(cmd) = args.next() {
55                    Command::new(cmd).args(args).spawn()?.wait()?.success()
56                } else {
57                    true
58                }
59            }
60            Err(pomprt::Interrupt) => false,
61            Err(pomprt::Eof) => return Ok(()),
62            Err(e) => Err(e)?,
63        };
64
65        sh.set_prompt(if ok { "% " } else { "! " });
66    }
67}