minibuffer history and more keybinds

This commit is contained in:
rabite 2019-03-01 13:56:57 +01:00
parent 32ab32fc5d
commit e2acef5ddf
2 changed files with 234 additions and 77 deletions

View File

@ -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 {

View File

@ -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) => {