mirror of https://github.com/bobwen-dev/hunter
minibuffer history and more keybinds
This commit is contained in:
parent
32ab32fc5d
commit
e2acef5ddf
|
@ -37,6 +37,10 @@ pub enum HError {
|
||||||
PopupFinnished,
|
PopupFinnished,
|
||||||
#[fail(display = "Input finnished")]
|
#[fail(display = "Input finnished")]
|
||||||
InputFinnished,
|
InputFinnished,
|
||||||
|
#[fail(display = "No completions found")]
|
||||||
|
NoCompletionsError,
|
||||||
|
#[fail(display = "No more history")]
|
||||||
|
NoHistoryError
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for HError {
|
impl From<std::io::Error> for HError {
|
||||||
|
|
|
@ -10,9 +10,11 @@ pub struct MiniBuffer {
|
||||||
coordinates: Coordinates,
|
coordinates: Coordinates,
|
||||||
query: String,
|
query: String,
|
||||||
input: String,
|
input: String,
|
||||||
done: bool,
|
|
||||||
position: usize,
|
position: usize,
|
||||||
history: Vec<String>
|
history: Vec<String>,
|
||||||
|
history_pos: Option<usize>,
|
||||||
|
completions: Vec<String>,
|
||||||
|
last_completion: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MiniBuffer {
|
impl MiniBuffer {
|
||||||
|
@ -24,96 +26,260 @@ impl MiniBuffer {
|
||||||
coordinates: coordinates,
|
coordinates: coordinates,
|
||||||
query: String::new(),
|
query: String::new(),
|
||||||
input: String::new(),
|
input: String::new(),
|
||||||
done: false,
|
|
||||||
position: 0,
|
position: 0,
|
||||||
history: vec![]
|
history: vec![],
|
||||||
|
history_pos: None,
|
||||||
|
completions: vec![],
|
||||||
|
last_completion: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query(&mut self, query: &str) -> HResult<String> {
|
pub fn query(&mut self, query: &str) -> HResult<String> {
|
||||||
self.query = query.to_string();
|
self.query = query.to_string();
|
||||||
self.input.clear();
|
self.input.clear();
|
||||||
self.done = false;
|
|
||||||
self.position = 0;
|
self.position = 0;
|
||||||
|
self.history_pos = None;
|
||||||
|
self.completions.clear();
|
||||||
|
self.last_completion = None;
|
||||||
|
|
||||||
write!(stdout(), "{}", termion::cursor::Show)?;
|
write!(stdout(), "{}", termion::cursor::Show)?;
|
||||||
|
|
||||||
self.popup()?;
|
self.popup()?;
|
||||||
|
|
||||||
|
|
||||||
// for event in stdin().events() {
|
|
||||||
// let event = event?;
|
|
||||||
// self.on_event(event);
|
|
||||||
// if self.done {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// self.draw()?;
|
|
||||||
|
|
||||||
// write!(stdout(), "{}", termion::cursor::Restore)?;
|
|
||||||
// if self.position != 0 {
|
|
||||||
// write!(stdout(),
|
|
||||||
// "{}",
|
|
||||||
// termion::cursor::Right(self.position as u16))?;
|
|
||||||
// }
|
|
||||||
// stdout().flush()?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(self.input.clone())
|
Ok(self.input.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn complete(&mut self) -> HResult<()> {
|
||||||
|
if !self.input.ends_with(" ") {
|
||||||
|
if !self.completions.is_empty() {
|
||||||
|
self.cycle_completions()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let part = self.input
|
||||||
|
.rsplitn(2, " ")
|
||||||
|
.take(1)
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<String>();
|
||||||
|
let completions = find_files(part.clone());
|
||||||
|
|
||||||
|
if let Ok(mut completions) = completions {
|
||||||
|
let completion = completions.pop()?;
|
||||||
|
|
||||||
|
self.input
|
||||||
|
= self.input[..self.input.len() - part.len()].to_string();
|
||||||
|
self.input.push_str(&completion);
|
||||||
|
self.position += &completion.len() - part.len();
|
||||||
|
|
||||||
|
self.last_completion = Some(completion);
|
||||||
|
self.completions = completions;
|
||||||
|
} else {
|
||||||
|
let completions = find_bins(&part);
|
||||||
|
|
||||||
|
if let Ok(mut completions) = completions {
|
||||||
|
let completion = completions.pop()?;
|
||||||
|
|
||||||
|
self.input = self.input[..self.input.len()
|
||||||
|
- part.len()].to_string();
|
||||||
|
self.input.push_str(&completion);
|
||||||
|
self.position += &completion.len() - part.len();
|
||||||
|
|
||||||
|
self.last_completion = Some(completion);
|
||||||
|
self.completions = completions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.input += "$s";
|
||||||
|
self.position += 2
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle_completions(&mut self) -> HResult<()> {
|
||||||
|
let last_comp = self.last_completion.as_ref()?;
|
||||||
|
let last_len = last_comp.len();
|
||||||
|
|
||||||
|
self.input = self.input.trim_end_matches(last_comp).to_string();
|
||||||
|
self.position -= last_len;
|
||||||
|
|
||||||
|
let next_comp = self.completions.pop()?;
|
||||||
|
self.input.push_str(&next_comp);
|
||||||
|
self.position += next_comp.len();
|
||||||
|
self.last_completion = Some(next_comp);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn history_up(&mut self) -> HResult<()> {
|
||||||
|
if self.history_pos == Some(0) { self.history_pos = None; }
|
||||||
|
if self.history.len() == 0 { return Err(HError::NoHistoryError); }
|
||||||
|
|
||||||
|
if let Some(history_pos) = self.history_pos {
|
||||||
|
let historic = self.history[history_pos - 1].clone();
|
||||||
|
|
||||||
|
self.input = historic;
|
||||||
|
self.position = self.input.len();
|
||||||
|
self.history_pos = Some(history_pos - 1);
|
||||||
|
} else {
|
||||||
|
let historic = self.history[self.history.len() - 1].clone();
|
||||||
|
|
||||||
|
self.input = historic;
|
||||||
|
self.position = self.input.len();
|
||||||
|
self.history_pos = Some(self.history.len() - 1);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn history_down(&mut self) -> HResult<()> {
|
||||||
|
let hist_len = self.history.len();
|
||||||
|
|
||||||
|
if hist_len == 0 { return Err(HError::NoHistoryError); }
|
||||||
|
if self.history_pos == Some(hist_len) ||
|
||||||
|
self.history_pos == None
|
||||||
|
{ self.history_pos = Some(0); }
|
||||||
|
|
||||||
|
if let Some(history_pos) = self.history_pos {
|
||||||
|
let historic = self.history[history_pos].clone();
|
||||||
|
|
||||||
|
self.input = historic;
|
||||||
|
self.position = self.input.len();
|
||||||
|
self.history_pos = Some(history_pos + 1);
|
||||||
|
} else {
|
||||||
|
let historic = self.history[0].clone();
|
||||||
|
|
||||||
|
self.input = historic;
|
||||||
|
self.position = self.input.len();
|
||||||
|
self.history_pos = Some(1);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_line(&mut self) -> HResult<()> {
|
||||||
|
self.input.clear();
|
||||||
|
self.position = 0;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_word(&mut self) -> HResult<()> {
|
||||||
|
let old_input_len = self.input.len();
|
||||||
|
let (before_cursor, after_cursor) = self.input.split_at(self.position);
|
||||||
|
|
||||||
|
let no_trim_len = before_cursor.len();
|
||||||
|
let before_cursor = before_cursor.trim_end();
|
||||||
|
|
||||||
|
if no_trim_len != before_cursor.len() {
|
||||||
|
self.position -= no_trim_len - before_cursor.len();
|
||||||
|
self.input = before_cursor.to_string() + after_cursor;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if before_cursor.ends_with("/") {
|
||||||
|
let mut new_input = before_cursor.to_string();
|
||||||
|
new_input.pop();
|
||||||
|
self.input = new_input + after_cursor;
|
||||||
|
self.position -= 1;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let dir_boundary = before_cursor.rfind("/");
|
||||||
|
let word_boundary = before_cursor.rfind(" ");
|
||||||
|
let boundaries = (dir_boundary, word_boundary);
|
||||||
|
|
||||||
|
let new_input = match boundaries {
|
||||||
|
(Some(dir_boundary), Some(word_boundary)) => {
|
||||||
|
if dir_boundary > word_boundary {
|
||||||
|
before_cursor
|
||||||
|
.split_at(dir_boundary).0
|
||||||
|
.to_string() + "/"
|
||||||
|
} else {
|
||||||
|
before_cursor
|
||||||
|
.split_at(word_boundary).0
|
||||||
|
.to_string() + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(dir_boundary), None) => {
|
||||||
|
before_cursor
|
||||||
|
.split_at(dir_boundary).0
|
||||||
|
.to_string() + "/"
|
||||||
|
}
|
||||||
|
(None, Some(word_boundary)) => {
|
||||||
|
before_cursor
|
||||||
|
.split_at(word_boundary).0
|
||||||
|
.to_string() + " "
|
||||||
|
}
|
||||||
|
(None, None) => "".to_string()
|
||||||
|
} + after_cursor;
|
||||||
|
|
||||||
|
let len_difference = old_input_len - new_input.len();
|
||||||
|
self.position -= len_difference;
|
||||||
|
|
||||||
|
self.input = new_input;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn input_finnished(&mut self) -> HResult<()> {
|
pub fn input_finnished(&mut self) -> HResult<()> {
|
||||||
return Err(HError::PopupFinnished)
|
return Err(HError::PopupFinnished)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_bins(comp_name: &str) -> Vec<String> {
|
pub fn find_bins(comp_name: &str) -> HResult<Vec<String>> {
|
||||||
let paths = std::env::var_os("PATH").unwrap()
|
let paths = std::env::var_os("PATH")?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.split(":")
|
.split(":")
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
paths.iter().map(|path| {
|
let completions = paths.iter().map(|path| {
|
||||||
std::fs::read_dir(path).unwrap().flat_map(|file| {
|
if let Ok(read_dir) = std::fs::read_dir(path) {
|
||||||
let file = file.unwrap();
|
read_dir.map(|file| {
|
||||||
let name = file.file_name().into_string().unwrap();
|
let file = file.unwrap();
|
||||||
if name.starts_with(comp_name) {
|
let name = file.file_name().into_string().unwrap();
|
||||||
Some(name)
|
if name.starts_with(comp_name) {
|
||||||
} else {
|
Ok(name)
|
||||||
None
|
} else {
|
||||||
}
|
Err(HError::NoCompletionsError)
|
||||||
}).collect::<Vec<String>>()
|
}
|
||||||
}).flatten().collect::<Vec<String>>()
|
}).collect::<Vec<HResult<String>>>()
|
||||||
|
} else { vec![Err(HError::NoCompletionsError)] }
|
||||||
|
}).flatten()
|
||||||
|
.filter(|result| result.is_ok())
|
||||||
|
.map(|result| result.unwrap())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
if completions.is_empty() { return Err(HError::NoCompletionsError); }
|
||||||
|
Ok(completions)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_files(mut comp_name: String) -> Vec<String> {
|
pub fn find_files(comp_name: String) -> HResult<Vec<String>> {
|
||||||
let mut path = std::path::PathBuf::from(&comp_name);
|
let mut path = std::env::current_dir().unwrap();
|
||||||
|
let comp_path = std::path::PathBuf::from(&comp_name);
|
||||||
|
path.push(&comp_path);
|
||||||
|
|
||||||
let dir = if comp_name.starts_with("/") {
|
let filename_part = path.file_name()?.to_string_lossy().to_string();
|
||||||
comp_name = path.file_name().unwrap().to_string_lossy().to_string();
|
|
||||||
path.pop();
|
|
||||||
path.to_string_lossy().to_string()
|
|
||||||
} else {
|
|
||||||
std::env::current_dir().unwrap().to_string_lossy().to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let reader = std::fs::read_dir(dir.clone());
|
let dir = if path.is_dir() { &path } else { path.parent().unwrap() };
|
||||||
if reader.is_err() { return vec![] }
|
let dir = std::path::PathBuf::from(dir);
|
||||||
let reader = reader.unwrap();
|
|
||||||
|
|
||||||
reader.flat_map(|file| {
|
let prefix = comp_name.trim_end_matches(&filename_part);
|
||||||
let file = file.unwrap();
|
|
||||||
|
let reader = std::fs::read_dir(&dir)?;
|
||||||
|
|
||||||
|
let completions = reader.map(|file| {
|
||||||
|
let file = file?;
|
||||||
let name = file.file_name().into_string().unwrap();
|
let name = file.file_name().into_string().unwrap();
|
||||||
if name.starts_with(&comp_name) {
|
if name.starts_with(&filename_part) {
|
||||||
if file.file_type().unwrap().is_dir() {
|
if file.file_type().unwrap().is_dir() {
|
||||||
Some(format!("{}/{}/", &dir, name))
|
Ok(format!("{}{}/", prefix, name))
|
||||||
} else {
|
} else {
|
||||||
Some(format!("/{}/", name))
|
Ok(format!("{}{}", prefix, name))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(HError::NoCompletionsError)
|
||||||
}
|
}
|
||||||
}).collect::<Vec<String>>()
|
}).filter(|res| res.is_ok() )
|
||||||
|
.map(|res| res.unwrap() )
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
if completions.is_empty() { return Err(HError::NoCompletionsError); }
|
||||||
|
Ok(completions)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for MiniBuffer {
|
impl Widget for MiniBuffer {
|
||||||
|
@ -149,28 +315,7 @@ impl Widget for MiniBuffer {
|
||||||
self.input_finnished()?;
|
self.input_finnished()?;
|
||||||
}
|
}
|
||||||
Key::Char('\t') => {
|
Key::Char('\t') => {
|
||||||
if !self.input.ends_with(" ") {
|
self.complete()?;
|
||||||
let part = self.input.rsplitn(2, " ").take(1)
|
|
||||||
.map(|s| s.to_string()).collect::<String>();
|
|
||||||
let completions = find_files(part.clone());
|
|
||||||
if !completions.is_empty() {
|
|
||||||
self.input
|
|
||||||
= self.input[..self.input.len() - part.len()].to_string();
|
|
||||||
self.input.push_str(&completions[0]);
|
|
||||||
self.position += &completions[0].len() - part.len();
|
|
||||||
} else {
|
|
||||||
let completions = find_bins(&part);
|
|
||||||
if !completions.is_empty() {
|
|
||||||
self.input = self.input[..self.input.len()
|
|
||||||
- part.len()].to_string();
|
|
||||||
self.input.push_str(&completions[0]);
|
|
||||||
self.position += &completions[0].len() - part.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.input += "$s";
|
|
||||||
self.position += 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Key::Backspace => {
|
Key::Backspace => {
|
||||||
if self.position != 0 {
|
if self.position != 0 {
|
||||||
|
@ -193,6 +338,14 @@ impl Widget for MiniBuffer {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Key::Up | Key::Ctrl('p') | Key::Alt('p') => {
|
||||||
|
self.history_up()?;
|
||||||
|
}
|
||||||
|
Key::Down | Key::Ctrl('n') | Key::Alt('n') => {
|
||||||
|
self.history_down()?;
|
||||||
|
}
|
||||||
|
Key::Ctrl('u') => { self.clear_line()?; },
|
||||||
|
Key::Ctrl('h') => { self.delete_word()?; },
|
||||||
Key::Ctrl('a') => { self.position = 0 },
|
Key::Ctrl('a') => { self.position = 0 },
|
||||||
Key::Ctrl('e') => { self.position = self.input.len(); },
|
Key::Ctrl('e') => { self.position = self.input.len(); },
|
||||||
Key::Char(key) => {
|
Key::Char(key) => {
|
||||||
|
|
Loading…
Reference in New Issue