aboutsummaryrefslogtreecommitdiffstats
path: root/src/completion.rs
diff options
context:
space:
mode:
authorJonas Maier <jonas@x77.dev>2026-05-04 23:28:17 +0200
committerJonas Maier <jonas@x77.dev>2026-05-04 23:28:17 +0200
commite56e2d1f9206102068c402a0cadbe62284816586 (patch)
tree350b4667b0f14648b10d70f30f682a7ecfbe603e /src/completion.rs
parent44dfa6dd66ed6734754262f7b27f6eff53c068bf (diff)
downloadpish-e56e2d1f9206102068c402a0cadbe62284816586.tar.gz
completion now should not introduce strings that do not parse as strings
Diffstat (limited to 'src/completion.rs')
-rw-r--r--src/completion.rs49
1 files changed, 30 insertions, 19 deletions
diff --git a/src/completion.rs b/src/completion.rs
index c628bd2..0e24d6d 100644
--- a/src/completion.rs
+++ b/src/completion.rs
@@ -1,4 +1,4 @@
-use crate::parse;
+use crate::parse::{self, CompletionContext};
use crate::{BString, Session};
use std::collections::HashMap;
use std::ffi::OsStr;
@@ -9,15 +9,21 @@ use std::sync::{Arc, Mutex};
use std::{env, fs};
pub struct Suggestion {
+ /// display string that is shown in the possibilities
pub display: BString,
+
+ /// *escaped* bytes that can be directly appended into terminal.
pub delta: BString,
}
fn _path_completion(
- mut prefix: BString,
+ cc: CompletionContext,
filter: &dyn Fn(&DirEntry) -> bool,
) -> std::io::Result<Vec<Suggestion>> {
+ let delim = cc.delim;
+ let mut prefix = cc.partial;
let mut partial_entry = BString::new();
+
while let Some(c) = prefix.last().cloned() {
if c == b'/' {
break;
@@ -40,12 +46,14 @@ fn _path_completion(
}
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 mut delta = BString::new();
+ delim.escape(&name[partial_entry.len()..], &mut delta);
let is_dir = entry.metadata().map(|m| m.is_dir()).unwrap_or(false);
if is_dir {
delta.push(b'/');
} else {
+ delim.write_closing_delimiter(&mut delta);
delta.push(b' ');
}
@@ -59,8 +67,8 @@ fn _path_completion(
Ok(sugs)
}
-pub fn path_completion(prefix: BString) -> Vec<Suggestion> {
- match _path_completion(prefix, &|_| true) {
+pub fn path_completion(cc: CompletionContext) -> Vec<Suggestion> {
+ match _path_completion(cc, &|_| true) {
Ok(suggestions) => suggestions,
Err(err) => {
println!("path completion failed: {err:?}\r");
@@ -69,8 +77,8 @@ pub fn path_completion(prefix: BString) -> Vec<Suggestion> {
}
}
-pub fn path_exe_completion(prefix: BString) -> Vec<Suggestion> {
- match _path_completion(prefix, &|d| is_executable(&d.path())) {
+pub fn path_exe_completion(cc: CompletionContext) -> Vec<Suggestion> {
+ match _path_completion(cc, &|d| is_executable(&d.path())) {
Ok(suggestions) => suggestions,
Err(err) => {
println!("path completion failed: {err:?}\r");
@@ -134,7 +142,7 @@ pub fn populate_path_cache(session: Arc<Mutex<Session>>) {
session.lock().unwrap().path_cache = PathCache { binaries };
}
-pub fn command_completion(session: Arc<Mutex<Session>>, prefix: BString) -> Vec<Suggestion> {
+pub fn command_completion(session: Arc<Mutex<Session>>, cc: CompletionContext) -> Vec<Suggestion> {
let se = session.lock().unwrap();
let mut out = Vec::new();
for fun in se
@@ -143,18 +151,19 @@ pub fn command_completion(session: Arc<Mutex<Session>>, prefix: BString) -> Vec<
.chain(se.builtins.keys())
.chain(se.path_cache.binaries.keys())
{
- if fun.starts_with(&prefix) {
+ if fun.starts_with(&cc.partial) {
+ let mut delta = BString::new();
+ cc.delim.escape(&fun[cc.partial.len()..], &mut delta);
+ cc.delim.write_closing_delimiter(&mut delta);
+ delta.push(b' ');
+
out.push(Suggestion {
display: fun.to_vec(),
- delta: fun[prefix.len()..].to_vec(),
+ delta,
})
}
}
- for s in out.iter_mut() {
- s.delta.push(b' ');
- }
-
out
}
@@ -180,10 +189,12 @@ pub fn completion(session: Arc<Mutex<Session>>, cmd: &[u8]) -> CompletionResult
&mut crate::run::Executor::new_for_completion(session.clone()),
);
+ let kind = comp.kind.clone();
+
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::Command => command_completion(session.clone(), comp),
+ parse::CompletionKind::PathCommand => path_exe_completion(comp),
+ parse::CompletionKind::Argument => path_completion(comp),
parse::CompletionKind::Variable => variable_completion(session.clone(), comp.partial),
parse::CompletionKind::None => return CompletionResult::empty(),
};
@@ -193,7 +204,7 @@ pub fn completion(session: Arc<Mutex<Session>>, cmd: &[u8]) -> CompletionResult
if suggestions.is_empty() {
return CompletionResult {
- kind: comp.kind,
+ kind,
..CompletionResult::empty()
};
}
@@ -214,7 +225,7 @@ pub fn completion(session: Arc<Mutex<Session>>, cmd: &[u8]) -> CompletionResult
let shared_prefix = shared_prefix.to_vec();
CompletionResult {
- kind: comp.kind,
+ kind,
suggestions,
shared_prefix,
}