summaryrefslogtreecommitdiffstats
path: root/src/minibuffer.rs
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-03-01 13:56:57 +0100
committerrabite <rabite@posteo.de>2019-03-01 13:56:57 +0100
commite2acef5ddfa5c7bf470aee5e24f429eabfb17951 (patch)
tree0659986536e060af3770a71bcf7924e5f0e8003d /src/minibuffer.rs
parent32ab32fc5dd0341b156e8d7666996ca6eaa191b1 (diff)
minibuffer history and more keybinds
Diffstat (limited to 'src/minibuffer.rs')
-rw-r--r--src/minibuffer.rs303
1 files changed, 228 insertions, 75 deletions
diff --git a/src/minibuffer.rs b/src/minibuffer.rs
index 2533014..985fbd5 100644
--- a/src/minibuffer.rs
+++ b/src/minibuffer.rs
@@ -10,9 +10,11 @@ pub struct MiniBuffer {
coordinates: Coordinates,
query: String,
input: String,
- done: bool,
position: usize,
- history: Vec<String>
+ history: Vec<String>,
+ history_pos: Option<usize>,
+ completions: Vec<String>,
+ last_completion: Option<String>
}
impl MiniBuffer {
@@ -24,41 +26,195 @@ impl MiniBuffer {
coordinates: coordinates,
query: String::new(),
input: String::new(),
- done: false,
position: 0,
- history: vec![]
+ history: vec![],
+ history_pos: None,
+ completions: vec![],
+ last_completion: None
}
}
pub fn query(&mut self, query: &str) -> HResult<String> {
self.query = query.to_string();
self.input.clear();
- self.done = false;
self.position = 0;
+ self.history_pos = None;
+ self.completions.clear();
+ self.last_completion = None;
write!(stdout(), "{}", termion::cursor::Show)?;
self.popup()?;
+ Ok(self.input.clone())
+ }
- // for event in stdin().events() {
- // let event = event?;
- // self.on_event(event);
- // if self.done {
- // break
- // }
- // self.draw()?;
+ pub fn complete(&mut self) -> HResult<()> {
+ if !self.input.ends_with(" ") {
+ if !self.completions.is_empty() {
+ self.cycle_completions()?;
+ return Ok(());
+ }
- // write!(stdout(), "{}", termion::cursor::Restore)?;
- // if self.position != 0 {
- // write!(stdout(),
- // "{}",
- // termion::cursor::Right(self.position as u16))?;
- // }
- // stdout().flush()?;
- // }
+ let part = self.input
+ .rsplitn(2, " ")
+ .take(1)
+ .map(|s| s.to_string())
+ .collect::<String>();
+ let completions = find_files(part.clone());
- Ok(self.input.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<()> {
@@ -66,54 +222,64 @@ impl MiniBuffer {
}
}
-pub fn find_bins(comp_name: &str) -> Vec<String> {
- let paths = std::env::var_os("PATH").unwrap()
+pub fn find_bins(comp_name: &str) -> HResult<Vec<String>> {
+ let paths = std::env::var_os("PATH")?
.to_string_lossy()
.split(":")
.map(|s| s.to_string())
.collect::<Vec<String>>();
- paths.iter().map(|path| {
- std::fs::read_dir(path).unwrap().flat_map(|file| {
- let file = file.unwrap();
- let name = file.file_name().into_string().unwrap();
- if name.starts_with(comp_name) {
- Some(name)
- } else {
- None
- }
- }).collect::<Vec<String>>()
- }).flatten().collect::<Vec<String>>()
+ let completions = paths.iter().map(|path| {
+ if let Ok(read_dir) = std::fs::read_dir(path) {
+ read_dir.map(|file| {
+ let file = file.unwrap();
+ let name = file.file_name().into_string().unwrap();
+ if name.starts_with(comp_name) {
+ Ok(name)
+ } else {
+ Err(HError::NoCompletionsError)
+ }
+ }).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> {
- let mut path = std::path::PathBuf::from(&comp_name);
+pub fn find_files(comp_name: String) -> HResult<Vec<String>> {
+ let mut path = std::env::current_dir().unwrap();
+ let comp_path = std::path::PathBuf::from(&comp_name);
+ path.push(&comp_path);
+
+ let filename_part = path.file_name()?.to_string_lossy().to_string();
+
+ let dir = if path.is_dir() { &path } else { path.parent().unwrap() };
+ let dir = std::path::PathBuf::from(dir);
- let dir = if comp_name.starts_with("/") {
- 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 prefix = comp_name.trim_end_matches(&filename_part);
- let reader = std::fs::read_dir(dir.clone());
- if reader.is_err() { return vec![] }
- let reader = reader.unwrap();
+ let reader = std::fs::read_dir(&dir)?;
- reader.flat_map(|file| {
- let file = file.unwrap();
+ let completions = reader.map(|file| {
+ let file = file?;
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() {
- Some(format!("{}/{}/", &dir, name))
+ Ok(format!("{}{}/", prefix, name))
} else {
- Some(format!("/{}/", name))
+ Ok(format!("{}{}", prefix, name))
}
} 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 {
@@ -149,28 +315,7 @@ impl Widget for MiniBuffer {
self.input_finnished()?;
}
Key::Char('\t') => {
- if !self.input.ends_with(" ") {
- 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
- }
+ self.complete()?;
}
Key::Backspace => {
if self.position != 0 {
@@ -193,6 +338,14 @@ impl Widget for MiniBuffer {
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('e') => { self.position = self.input.len(); },
Key::Char(key) => {