mirror of
https://github.com/qwjyh/xdbm
synced 2025-01-19 02:33:14 +09:00
119 lines
3.2 KiB
Rust
119 lines
3.2 KiB
Rust
use inquire::{
|
|
autocompletion::{Autocomplete, Replacement},
|
|
CustomUserError,
|
|
};
|
|
use std::io::ErrorKind;
|
|
|
|
#[derive(Clone, Default)]
|
|
pub struct FilePathCompleter {
|
|
input: String,
|
|
paths: Vec<String>,
|
|
lcp: String,
|
|
}
|
|
|
|
impl FilePathCompleter {
|
|
fn update_input(&mut self, input: &str) -> Result<(), CustomUserError> {
|
|
if input == self.input {
|
|
return Ok(());
|
|
}
|
|
|
|
input.clone_into(&mut self.input);
|
|
self.paths.clear();
|
|
|
|
let input_path = std::path::PathBuf::from(input);
|
|
|
|
let fallback_parent = input_path
|
|
.parent()
|
|
.map(|p| {
|
|
if p.to_string_lossy() == "" {
|
|
std::path::PathBuf::from(".")
|
|
} else {
|
|
p.to_owned()
|
|
}
|
|
})
|
|
.unwrap_or_else(|| std::path::PathBuf::from("."));
|
|
|
|
let scan_dir = if input.ends_with('/') {
|
|
input_path
|
|
} else {
|
|
fallback_parent.clone()
|
|
};
|
|
|
|
let entries = match std::fs::read_dir(scan_dir) {
|
|
Ok(read_dir) => Ok(read_dir),
|
|
Err(err) if err.kind() == ErrorKind::NotFound => std::fs::read_dir(fallback_parent),
|
|
Err(err) => Err(err),
|
|
}?
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
let mut idx = 0;
|
|
let limit = 15;
|
|
|
|
while idx < entries.len() && self.paths.len() < limit {
|
|
let entry = entries.get(idx).unwrap();
|
|
|
|
let path = entry.path();
|
|
let path_str = if path.is_dir() {
|
|
format!("{}/", path.to_string_lossy())
|
|
} else {
|
|
path.to_string_lossy().to_string()
|
|
};
|
|
|
|
if path_str.starts_with(&self.input) && path_str.len() != self.input.len() {
|
|
self.paths.push(path_str);
|
|
}
|
|
|
|
idx = idx.saturating_add(1);
|
|
}
|
|
|
|
self.lcp = self.longest_common_prefix();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn longest_common_prefix(&self) -> String {
|
|
let mut ret: String = String::new();
|
|
|
|
let mut sorted = self.paths.clone();
|
|
sorted.sort();
|
|
if sorted.is_empty() {
|
|
return ret;
|
|
}
|
|
|
|
let mut first_word = sorted.first().unwrap().chars();
|
|
let mut last_word = sorted.last().unwrap().chars();
|
|
|
|
loop {
|
|
match (first_word.next(), last_word.next()) {
|
|
(Some(c1), Some(c2)) if c1 == c2 => {
|
|
ret.push(c1);
|
|
}
|
|
_ => return ret,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Autocomplete for FilePathCompleter {
|
|
fn get_suggestions(&mut self, input: &str) -> Result<Vec<String>, CustomUserError> {
|
|
self.update_input(input)?;
|
|
|
|
Ok(self.paths.clone())
|
|
}
|
|
|
|
fn get_completion(
|
|
&mut self,
|
|
input: &str,
|
|
highlighted_suggestion: Option<String>,
|
|
) -> Result<Replacement, CustomUserError> {
|
|
self.update_input(input)?;
|
|
|
|
Ok(match highlighted_suggestion {
|
|
Some(suggestion) => Replacement::Some(suggestion),
|
|
None => match self.lcp.is_empty() {
|
|
true => Replacement::None,
|
|
false => Replacement::Some(self.lcp.clone()),
|
|
},
|
|
})
|
|
}
|
|
}
|