summaryrefslogtreecommitdiffstats
path: root/src/minibuffer.rs
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-02-25 18:23:48 +0100
committerrabite <rabite@posteo.de>2019-02-25 19:06:26 +0100
commit7fc77f8605bf637331f2f9c283c4b4e284891af1 (patch)
tree62d2c03d60a2b2ea048e6f68fda93b39ed4a0ce2 /src/minibuffer.rs
parenta6c147442c508631df3b862a8d9b3d618ccfb339 (diff)
minibuffer Widgetified
Diffstat (limited to 'src/minibuffer.rs')
-rw-r--r--src/minibuffer.rs211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/minibuffer.rs b/src/minibuffer.rs
new file mode 100644
index 0000000..8c45bd0
--- /dev/null
+++ b/src/minibuffer.rs
@@ -0,0 +1,211 @@
+use termion::event::Key;
+use termion::input::TermRead;
+
+use std::io::{stdin, stdout, Write};
+
+use crate::coordinates::{Coordinates};
+use crate::widget::Widget;
+use crate::window::{send_event, Events};
+use crate::fail::HResult;
+
+pub struct MiniBuffer {
+ coordinates: Coordinates,
+ query: String,
+ input: String,
+ done: bool,
+ position: usize,
+ history: Vec<String>
+}
+
+impl MiniBuffer {
+ pub fn new() -> MiniBuffer {
+ let xsize = crate::term::xsize();
+ let ysize = crate::term::ysize();
+ let coordinates = Coordinates::new_at(xsize, 1, 1, ysize);
+ MiniBuffer {
+ coordinates: coordinates,
+ query: String::new(),
+ input: String::new(),
+ done: false,
+ position: 0,
+ history: vec![]
+ }
+ }
+
+ pub fn query(&mut self, query: &str) -> HResult<String> {
+ self.query = query.to_string();
+ self.input.clear();
+ self.done = false;
+ self.position = 0;
+
+ send_event(Events::ExclusiveInput(true))?;
+
+ self.draw()?;
+ write!(stdout(), "{}{}",
+ termion::cursor::Show,
+ termion::cursor::Save)?;
+ stdout().flush()?;
+
+
+ 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()?;
+ }
+
+ self.done = false;
+
+ send_event(Events::ExclusiveInput(false))?;
+
+ Ok(self.input.clone())
+ }
+}
+
+pub fn find_bins(comp_name: &str) -> Vec<String> {
+ let paths = std::env::var_os("PATH").unwrap()
+ .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>>()
+}
+
+pub fn find_files(mut comp_name: String) -> Vec<String> {
+ let mut path = std::path::PathBuf::from(&comp_name);
+
+ 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 reader = std::fs::read_dir(dir.clone());
+ if reader.is_err() { return vec![] }
+ let reader = reader.unwrap();
+
+ reader.flat_map(|file| {
+ let file = file.unwrap();
+ let name = file.file_name().into_string().unwrap();
+ if name.starts_with(&comp_name) {
+ if file.file_type().unwrap().is_dir() {
+ Some(format!("{}/{}/", &dir, name))
+ } else {
+ Some(format!("/{}/", name))
+ }
+ } else {
+ None
+ }
+ }).collect::<Vec<String>>()
+}
+
+impl Widget for MiniBuffer {
+ fn get_coordinates(&self) -> &Coordinates {
+ &self.coordinates
+ }
+ fn set_coordinates(&mut self, coordinates: &Coordinates) {
+ self.coordinates = coordinates.clone();
+ self.refresh();
+ }
+ fn render_header(&self) -> String {
+ "".to_string()
+ }
+ fn refresh(&mut self) {
+ }
+
+ fn get_drawlist(&self) -> String {
+ let (xpos, ypos) = self.get_coordinates().u16position();
+ format!("{}{}{}: {}",
+ crate::term::goto_xy(xpos, ypos),
+ termion::clear::CurrentLine,
+ self.query,
+ self.input)
+ }
+
+ fn on_key(&mut self, key: Key) {
+ match key {
+ Key::Esc | Key::Ctrl('c') => { self.input.clear(); self.done = true; },
+ Key::Char('\n') => {
+ if self.input != "" {
+ self.history.push(self.input.clone());
+ }
+ self.done = true;
+ }
+ 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
+ }
+ }
+ Key::Backspace => {
+ if self.position != 0 {
+ self.input.remove(self.position - 1);
+ self.position -= 1;
+ }
+ }
+ Key::Delete | Key::Ctrl('d') => {
+ if self.position != self.input.len() {
+ self.input.remove(self.position);
+ }
+ }
+ Key::Left | Key::Ctrl('b') => {
+ if self.position != 0 {
+ self.position -= 1;
+ }
+ }
+ Key::Right | Key::Ctrl('f') => {
+ if self.position != self.input.len() {
+ self.position += 1;
+ }
+ }
+ Key::Ctrl('a') => { self.position = 0 },
+ Key::Ctrl('e') => { self.position = self.input.len(); },
+ Key::Char(key) => {
+ self.input.insert(self.position, key);
+ self.position += 1;
+ }
+ _ => {}
+ }
+ }
+}