aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJonas Maier <>2026-03-17 15:32:30 +0100
committerJonas Maier <>2026-03-17 15:32:30 +0100
commit89e027f61eda0d918ee2c2622f6ad370a76c5754 (patch)
treebca51022f83e6bf477312fdb03976da8b9c86cd0 /src
parent965196972fa27237b6cb97209c915381a95a5bef (diff)
downloadpish-89e027f61eda0d918ee2c2622f6ad370a76c5754.tar.gz
completion when using path in place of command
Diffstat (limited to 'src')
-rw-r--r--src/completion.rs22
-rw-r--r--src/parse/mod.rs10
2 files changed, 27 insertions, 5 deletions
diff --git a/src/completion.rs b/src/completion.rs
index c7a183f..d3e4704 100644
--- a/src/completion.rs
+++ b/src/completion.rs
@@ -2,6 +2,7 @@ use crate::parse::{self, CompletionContext};
use crate::{BString, Session};
use std::collections::HashMap;
use std::ffi::OsStr;
+use std::fs::DirEntry;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
@@ -12,7 +13,10 @@ pub struct Suggestion {
pub delta: BString,
}
-fn _path_completion(mut prefix: BString) -> std::io::Result<Vec<Suggestion>> {
+fn _path_completion(
+ mut prefix: BString,
+ filter: &dyn Fn(&DirEntry) -> bool,
+) -> std::io::Result<Vec<Suggestion>> {
let mut partial_entry = BString::new();
while let Some(c) = prefix.last().cloned() {
if c == b'/' {
@@ -31,6 +35,9 @@ fn _path_completion(mut prefix: BString) -> std::io::Result<Vec<Suggestion>> {
for entry in fs::read_dir(OsStr::from_bytes(&prefix))? {
let entry = entry?;
+ if !filter(&entry) {
+ continue;
+ }
let name = entry.file_name().as_bytes().to_vec();
if name.starts_with(&partial_entry) {
let mut delta = name[partial_entry.len()..].to_vec();
@@ -53,7 +60,17 @@ fn _path_completion(mut prefix: BString) -> std::io::Result<Vec<Suggestion>> {
}
pub fn path_completion(prefix: BString) -> Vec<Suggestion> {
- match _path_completion(prefix) {
+ match _path_completion(prefix, &|_| true) {
+ Ok(suggestions) => suggestions,
+ Err(err) => {
+ println!("path completion failed: {err:?}\r");
+ Vec::new()
+ }
+ }
+}
+
+pub fn path_exe_completion(prefix: BString) -> Vec<Suggestion> {
+ match _path_completion(prefix, &|d| is_executable(&d.path())) {
Ok(suggestions) => suggestions,
Err(err) => {
println!("path completion failed: {err:?}\r");
@@ -165,6 +182,7 @@ pub fn completion(session: Arc<Mutex<Session>>, cmd: &[u8]) -> CompletionResult
let mut suggestions = match comp.kind {
parse::CompletionKind::Command => command_completion(session.clone(), comp.partial),
+ parse::CompletionKind::PathCommand => path_exe_completion(comp.partial),
parse::CompletionKind::Argument => path_completion(comp.partial),
parse::CompletionKind::Variable => variable_completion(session.clone(), comp.partial),
parse::CompletionKind::None => return CompletionResult::empty(),
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index e085f34..006fce2 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -1,4 +1,4 @@
-use crate::BString;
+use crate::{BString, run::CommandKind};
#[cfg(test)]
mod test;
@@ -797,9 +797,10 @@ pub fn do_parse(x: &[u8]) -> Res<Ast<PreExpansion>, (ParseError, &[u8])> {
}
}
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
pub enum CompletionKind {
Command,
+ PathCommand,
Argument,
Variable,
None,
@@ -840,7 +841,7 @@ impl Ast<PreExpansion> {
}
impl ExpString {
- fn completion<E: Expander>(&self, e: &mut E, kind: CompletionKind) -> CompletionContext {
+ fn completion<E: Expander>(&self, e: &mut E, mut kind: CompletionKind) -> CompletionContext {
if let Some(StringPart::Var(var)) = self.parts.last()
&& !var.already_complete
{
@@ -853,6 +854,9 @@ impl ExpString {
{
cmd.cmd.completion(e)
} else if let Ok(s) = self.clone().expand(e) {
+ if s.contains(&b'/') && kind == CompletionKind::Command {
+ kind = CompletionKind::PathCommand;
+ }
CompletionContext { kind, partial: s }
} else {
CompletionContext::none()