refactor: remove unused completer and status modules with rustyline/terminal_size deps
This commit is contained in:
@@ -14,10 +14,8 @@ name = "aishell"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustyline = "14.0"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
terminal_size = "0.4"
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
notify = { version = "7", features = ["macos_fsevent"] }
|
notify = { version = "7", features = ["macos_fsevent"] }
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
|
|||||||
170
src/completer.rs
170
src/completer.rs
@@ -1,170 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use rustyline::completion::{Completer, Pair};
|
|
||||||
use rustyline::highlight::Highlighter;
|
|
||||||
use rustyline::hint::{Hinter, HistoryHinter};
|
|
||||||
use rustyline::validate::Validator;
|
|
||||||
use rustyline::Helper;
|
|
||||||
use rustyline::Context;
|
|
||||||
|
|
||||||
pub struct ShellHelper {
|
|
||||||
hinter: HistoryHinter,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShellHelper {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
hinter: HistoryHinter::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Helper for ShellHelper {}
|
|
||||||
impl Validator for ShellHelper {}
|
|
||||||
|
|
||||||
impl Highlighter for ShellHelper {
|
|
||||||
fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> {
|
|
||||||
// Gray color for hints
|
|
||||||
std::borrow::Cow::Owned(format!("\x1b[90m{}\x1b[0m", hint))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hinter for ShellHelper {
|
|
||||||
type Hint = String;
|
|
||||||
|
|
||||||
fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
|
|
||||||
self.hinter.hint(line, pos, ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Completer for ShellHelper {
|
|
||||||
type Candidate = Pair;
|
|
||||||
|
|
||||||
fn complete(
|
|
||||||
&self,
|
|
||||||
line: &str,
|
|
||||||
pos: usize,
|
|
||||||
_ctx: &Context<'_>,
|
|
||||||
) -> rustyline::Result<(usize, Vec<Pair>)> {
|
|
||||||
let (start, word) = extract_word(line, pos);
|
|
||||||
|
|
||||||
// If first word → command completion
|
|
||||||
// Otherwise → file path completion
|
|
||||||
let is_first_word = line[..start].trim().is_empty();
|
|
||||||
|
|
||||||
let candidates = if is_first_word {
|
|
||||||
let mut results = complete_commands(word);
|
|
||||||
// Also include files in current dir for ./script style
|
|
||||||
results.extend(complete_path(word));
|
|
||||||
results
|
|
||||||
} else {
|
|
||||||
complete_path(word)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((start, candidates))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the current word being completed
|
|
||||||
fn extract_word(line: &str, pos: usize) -> (usize, &str) {
|
|
||||||
let bytes = &line.as_bytes()[..pos];
|
|
||||||
let start = bytes.iter().rposition(|&b| b == b' ').map(|i| i + 1).unwrap_or(0);
|
|
||||||
(start, &line[start..pos])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Complete commands from PATH
|
|
||||||
fn complete_commands(prefix: &str) -> Vec<Pair> {
|
|
||||||
if prefix.is_empty() {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let path_var = match env::var("PATH") {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => return Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut seen = std::collections::HashSet::new();
|
|
||||||
let mut results = Vec::new();
|
|
||||||
|
|
||||||
for dir in path_var.split(':') {
|
|
||||||
let entries = match fs::read_dir(dir) {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
for entry in entries.flatten() {
|
|
||||||
let name = entry.file_name();
|
|
||||||
let name = name.to_string_lossy();
|
|
||||||
if name.starts_with(prefix) && !seen.contains(name.as_ref()) {
|
|
||||||
seen.insert(name.to_string());
|
|
||||||
results.push(Pair {
|
|
||||||
display: name.to_string(),
|
|
||||||
replacement: name.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results.sort_by(|a, b| a.display.cmp(&b.display));
|
|
||||||
results
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Complete file/directory paths
|
|
||||||
fn complete_path(prefix: &str) -> Vec<Pair> {
|
|
||||||
let (dir, file_prefix) = if prefix.contains('/') {
|
|
||||||
let p = Path::new(prefix);
|
|
||||||
let dir = p.parent().unwrap_or(Path::new("."));
|
|
||||||
let file = p.file_name().map(|f| f.to_string_lossy().to_string()).unwrap_or_default();
|
|
||||||
// Expand ~
|
|
||||||
let dir_str = dir.to_string_lossy();
|
|
||||||
let expanded = if dir_str.starts_with('~') {
|
|
||||||
if let Ok(home) = env::var("HOME") {
|
|
||||||
Path::new(&dir_str.replacen('~', &home, 1)).to_path_buf()
|
|
||||||
} else {
|
|
||||||
dir.to_path_buf()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dir.to_path_buf()
|
|
||||||
};
|
|
||||||
(expanded, file)
|
|
||||||
} else {
|
|
||||||
(std::env::current_dir().unwrap_or_default(), prefix.to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
let entries = match fs::read_dir(&dir) {
|
|
||||||
Ok(e) => e,
|
|
||||||
Err(_) => return Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut results = Vec::new();
|
|
||||||
for entry in entries.flatten() {
|
|
||||||
let name = entry.file_name();
|
|
||||||
let name = name.to_string_lossy().to_string();
|
|
||||||
if name.starts_with(&file_prefix) {
|
|
||||||
let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
|
|
||||||
let replacement = if prefix.contains('/') {
|
|
||||||
let parent = Path::new(prefix).parent().unwrap_or(Path::new(""));
|
|
||||||
let mut r = format!("{}/{}", parent.display(), name);
|
|
||||||
if is_dir {
|
|
||||||
r.push('/');
|
|
||||||
}
|
|
||||||
r
|
|
||||||
} else {
|
|
||||||
let mut r = name.clone();
|
|
||||||
if is_dir {
|
|
||||||
r.push('/');
|
|
||||||
}
|
|
||||||
r
|
|
||||||
};
|
|
||||||
let display = if is_dir {
|
|
||||||
format!("{}/", name)
|
|
||||||
} else {
|
|
||||||
name
|
|
||||||
};
|
|
||||||
results.push(Pair { display, replacement });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results.sort_by(|a, b| a.display.cmp(&b.display));
|
|
||||||
results
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,6 @@ pub mod config;
|
|||||||
pub mod judge;
|
pub mod judge;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub mod ai;
|
pub mod ai;
|
||||||
pub mod status;
|
|
||||||
pub mod completer;
|
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
pub mod tui;
|
pub mod tui;
|
||||||
pub mod headless;
|
pub mod headless;
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
use std::io::{Write, stdout};
|
|
||||||
use terminal_size::{terminal_size, Width, Height};
|
|
||||||
|
|
||||||
pub struct StatusBar {
|
|
||||||
message: String,
|
|
||||||
cleaned: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StatusBar {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let sb = Self {
|
|
||||||
message: "idle".to_string(),
|
|
||||||
cleaned: false,
|
|
||||||
};
|
|
||||||
sb.setup();
|
|
||||||
sb
|
|
||||||
}
|
|
||||||
|
|
||||||
fn term_size(&self) -> (u16, u16) {
|
|
||||||
terminal_size()
|
|
||||||
.map(|(Width(w), Height(h))| (w, h))
|
|
||||||
.unwrap_or((80, 24))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup(&self) {
|
|
||||||
let (_, rows) = self.term_size();
|
|
||||||
let mut out = stdout();
|
|
||||||
write!(out, "\x1b[1;{}r", rows - 1).ok();
|
|
||||||
write!(out, "\x1b[1;1H").ok();
|
|
||||||
out.flush().ok();
|
|
||||||
self.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&mut self, msg: &str) {
|
|
||||||
self.message = msg.to_string();
|
|
||||||
self.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&self) {
|
|
||||||
let (cols, rows) = self.term_size();
|
|
||||||
let status = format!(" ● {}", self.message);
|
|
||||||
let padded = format!("{:<width$}", status, width = cols as usize);
|
|
||||||
let mut out = stdout();
|
|
||||||
write!(out, "\x1b7\x1b[{};1H\x1b[7m{}\x1b[0m\x1b8", rows, padded).ok();
|
|
||||||
out.flush().ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cleanup(&mut self) {
|
|
||||||
if self.cleaned {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.cleaned = true;
|
|
||||||
let (_, rows) = self.term_size();
|
|
||||||
let mut out = stdout();
|
|
||||||
write!(out, "\x1b[r").ok();
|
|
||||||
write!(out, "\x1b[{};1H\x1b[2K", rows).ok();
|
|
||||||
out.flush().ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for StatusBar {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user