Skip to main content

click/
complete.rs

1//! Built-in shell completion helpers for common patterns.
2
3use std::env;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7use crate::{CompletionItem, Context};
8
9/// Complete environment variable names using case-insensitive substring matching.
10pub fn env_vars(_ctx: &Context, incomplete: &str) -> Vec<CompletionItem> {
11    let needle = incomplete.to_lowercase();
12    env::vars()
13        .filter(|(key, _)| key.to_lowercase().contains(&needle))
14        .map(|(key, _)| CompletionItem::new(key))
15        .collect()
16}
17
18/// Complete directory paths.
19///
20/// Supports both bare names (`src`) and nested prefixes (`foo/bar`).
21pub fn directories(_ctx: &Context, incomplete: &str) -> Vec<CompletionItem> {
22    let (base_dir, prefix) = match incomplete.rsplit_once(std::path::MAIN_SEPARATOR) {
23        Some((parent, tail)) if !parent.is_empty() => (PathBuf::from(parent), tail),
24        _ => (PathBuf::from("."), incomplete),
25    };
26
27    fs::read_dir(&base_dir)
28        .into_iter()
29        .flatten()
30        .filter_map(|entry| entry.ok())
31        .filter_map(|entry| {
32            let path = entry.path();
33            if !path.is_dir() {
34                return None;
35            }
36
37            let name = entry.file_name().to_string_lossy().to_string();
38            if !name.starts_with(prefix) {
39                return None;
40            }
41
42            let value = if base_dir == Path::new(".") {
43                name
44            } else {
45                format!(
46                    "{}{}{}",
47                    base_dir.display(),
48                    std::path::MAIN_SEPARATOR,
49                    name
50                )
51            };
52            Some(CompletionItem::new(value))
53        })
54        .collect()
55}
56
57/// Build a completion callback from static values.
58pub fn items(
59    values: &'static [&'static str],
60) -> impl Fn(&Context, &str) -> Vec<CompletionItem> + Send + Sync + 'static {
61    move |_ctx: &Context, incomplete: &str| {
62        values
63            .iter()
64            .copied()
65            .filter(|value| value.contains(incomplete))
66            .map(CompletionItem::new)
67            .collect()
68    }
69}
70
71/// Build a completion callback from static `(value, help)` pairs.
72pub fn items_with_help(
73    values: &'static [(&'static str, &'static str)],
74) -> impl Fn(&Context, &str) -> Vec<CompletionItem> + Send + Sync + 'static {
75    move |_ctx: &Context, incomplete: &str| {
76        values
77            .iter()
78            .copied()
79            .filter(|(value, help)| value.contains(incomplete) || help.contains(incomplete))
80            .map(|(value, help)| CompletionItem::new(value).with_help(help))
81            .collect()
82    }
83}