summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite0 <rabite@posteo.de>2020-03-01 22:57:09 +0100
committerrabite0 <rabite@posteo.de>2020-03-01 22:57:09 +0100
commite4850e38b603c4adc99fbdcedc80de7c2dfbe775 (patch)
treeb526d3bfd1a45bd007c0280b74d4f8d3929bc76a
parentb163a9f4b400b4b14d96beacce912be47fa00f8a (diff)
improved navigation/turbo-cd
-rw-r--r--src/fail.rs22
-rw-r--r--src/file_browser.rs177
-rw-r--r--src/listview.rs104
-rw-r--r--src/minibuffer.rs31
-rw-r--r--src/quick_actions.rs13
-rw-r--r--src/widget.rs21
6 files changed, 308 insertions, 60 deletions
diff --git a/src/fail.rs b/src/fail.rs
index 4e793ac..519de19 100644
--- a/src/fail.rs
+++ b/src/fail.rs
@@ -70,10 +70,6 @@ pub enum HError {
INotifyError(String),
#[fail(display = "Tags not loaded yet")]
TagsNotLoadedYetError,
- #[fail(display = "Input cancelled!")]
- MiniBufferCancelledInput,
- #[fail(display = "Empty input!")]
- MiniBufferEmptyInput,
#[fail(display = "Undefined key: {:?}", key)]
WidgetUndefinedKeyError{key: Key},
#[fail(display = "Terminal has been resized!")]
@@ -107,7 +103,11 @@ pub enum HError {
#[fail(display = "{}", _0)]
FileError(crate::files::FileError),
#[fail(display = "{}", _0)]
- Nix(#[cause] nix::Error)
+ Nix(#[cause] nix::Error),
+ #[fail(display = "Refresh parent widget!")]
+ RefreshParent,
+ #[fail(display = "Refresh parent widget!")]
+ MiniBufferEvent(crate::minibuffer::MiniBufferEvent),
}
impl HError {
@@ -134,12 +134,6 @@ impl HError {
pub fn tags_not_loaded<T>() -> HResult<T> {
Err(HError::TagsNotLoadedYetError)
}
- pub fn minibuffer_cancel<T>() -> HResult<T> {
- Err(HError::MiniBufferCancelledInput)
- }
- pub fn minibuffer_empty<T>() -> HResult<T> {
- Err(HError::MiniBufferEmptyInput)
- }
pub fn undefined_key<T>(key: Key) -> HResult<T> {
Err(HError::WidgetUndefinedKeyError { key: key })
}
@@ -417,6 +411,12 @@ impl From<KeyBindError> for HError {
}
}
+impl From<crate::minibuffer::MiniBufferEvent> for HError {
+ fn from(e: crate::minibuffer::MiniBufferEvent) -> Self {
+ HError::MiniBufferEvent(e)
+ }
+}
+
#[derive(Fail, Debug, Clone)]
pub enum KeyBindError {
diff --git a/src/file_browser.rs b/src/file_browser.rs
index 3a84ee1..fcc0c6c 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -631,6 +631,13 @@ impl FileBrowser {
self.draw().log();
continue;
}
+
+ if let Err(HError::RefreshParent) = bookmark {
+ self.refresh().log();
+ self.draw().log();
+ continue;
+ }
+
return bookmark;
}
}
@@ -876,11 +883,146 @@ impl FileBrowser {
}
pub fn turbo_cd(&mut self) -> HResult<()> {
- let dir = self.core.minibuffer("cd")?;
+ use crate::minibuffer::MiniBufferEvent::*;
+
+ // Return and reset on cancel
+ let orig_dir = self.cwd()?.clone();
+ let orig_dir_selected_file = self.selected_file()?;
+ let mut orig_dir_filter = self.main_widget()?
+ .content
+ .get_filter();
+
+ // For current dir
+ let mut selected_file = Some(orig_dir_selected_file.clone());
+ let mut filter = Some(orig_dir_filter.clone());
+
+ // Helper function to restore any previous filter/selection
+ let dir_restore =
+ |s: &mut FileBrowser, filter: Option<Option<String>>, file: Option<File>| {
+ s.main_widget_mut()
+ .map(|mw| {
+ filter.map(|f| mw.set_filter(f));
+ file.map(|f| mw.select_file(&f));
+ }).log();
+ };
- let path = std::path::PathBuf::from(&dir);
- let dir = File::new_from_path(&path.canonicalize()?)?;
- self.main_widget_goto(&dir)?;
+ loop {
+ let input = self.core.minibuffer_continuous("nav");
+ // dbg!(&input);
+ // self.refresh().log();
+ // self.draw().log();
+
+ match input {
+ // While minibuffer runs it steals all events, thus explicit refresh/redraw
+ Err(HError::RefreshParent) => {
+ self.refresh().log();
+ self.draw().log();
+ continue;
+ }
+ Err(HError::MiniBufferEvent(event)) => {
+ match event {
+ // Done here, restore filter, but leave selection as is
+ Done(_) | Empty => {
+ dir_restore(self, filter.take(), None);
+ self.core.minibuffer_clear().log();
+ break;
+ }
+ NewInput(input) => {
+ // Don't filter anything until a letter appears
+ if input.as_str() == "." || input.as_str() == ".." {
+ continue;
+ }
+
+ if input.ends_with('/') {
+ match input.as_str() {
+ "../" => {
+ dir_restore(self,
+ filter.take(),
+ selected_file.take());
+ self.go_back().log();
+ self.core.minibuffer_clear().log();
+ }
+ _ => {
+ let sel = self.selected_file()?;
+
+ if sel.is_dir() {
+ dir_restore(self,
+ filter.take(),
+ selected_file.take());
+ self.main_widget_goto(&sel)?;
+ self.core.minibuffer_clear().log();
+ }
+ }
+ }
+ continue;
+ }
+
+ // Save current filter, if existing, before overwriting it
+ // Type is Option<Option<_>>, because filter itself is Option<_>
+ if filter.is_none() {
+ let dir_filter = self.main_widget()?
+ .content
+ .get_filter();
+ filter = Some(dir_filter);
+ }
+
+ // To restore on leave/cancel
+ if selected_file.is_none() {
+ selected_file = Some(self.selected_file()?);
+ }
+
+ self.main_widget_mut()?
+ .set_filter(Some(input));
+ }
+ // Restore original directory and filter/selection
+ Cancelled => {
+ self.main_widget_goto(&orig_dir)?;
+ // Special case, because all others fail if directory isn't ready anyway
+ self.main_async_widget_mut()?
+ .widget
+ .on_ready(move |mw,_| {
+ let mw = mw?;
+ mw.set_filter(orig_dir_filter.take());
+ mw.select_file(&orig_dir_selected_file);
+ Ok(())
+ })?;
+ break;
+ }
+ CycleNext => {
+ // Because of filtering the selected file isn't just at n+1
+ let oldpos = self.main_widget()?.get_selection();
+
+ let mw = self.main_widget_mut()?;
+ mw.move_down();
+ mw.update_selected_file(oldpos);
+
+ // Refresh preview and draw header, too
+ self.refresh().log();
+ self.draw().log();
+
+ // Explicitly selected
+ selected_file = Some(self.selected_file()?);
+ }
+ CyclePrev => {
+ // Because of filtering the selected file isn't just at n-1
+ let oldpos = self.main_widget()?.get_selection();
+
+ let mw = self.main_widget_mut()?;
+ mw.move_up();
+ mw.update_selected_file(oldpos);
+
+ // Refresh preview and draw header, too
+ self.refresh().log();
+ self.draw().log();
+
+ // Explicitly selected
+ selected_file = Some(self.selected_file()?);
+ }
+ }
+ },
+ _ => { }
+ }
+ }
Ok(())
}
@@ -1121,13 +1263,36 @@ impl FileBrowser {
pub fn show_procview(&mut self) -> HResult<()> {
self.preview_widget().map(|preview| preview.cancel_animation()).log();
- self.proc_view.lock()?.popup()?;
+ let procview = self.proc_view.clone();
+ loop {
+ match procview.lock()?.popup() {
+ // Ignore refresh
+ Err(HError::RefreshParent) => continue,
+ Err(HError::TerminalResizedError) |
+ Err(HError::WidgetResizedError) => self.resize().log(),
+ _ => break
+ }
+ }
Ok(())
}
pub fn show_log(&mut self) -> HResult<()> {
self.preview_widget().map(|preview| preview.cancel_animation()).log();
- self.log_view.lock()?.popup()?;
+ loop {
+ let res = self.log_view.lock()?.popup();
+
+ if let Err(HError::RefreshParent) = res {
+ continue
+ }
+
+ if let Err(HError::TerminalResizedError) = res {
+ self.resize().log();
+ continue;
+ }
+
+ break
+ }
+
Ok(())
}
diff --git a/src/listview.rs b/src/listview.rs
index b260b6a..d728cf1 100644
--- a/src/listview.rs
+++ b/src/listview.rs
@@ -602,20 +602,38 @@ impl ListView<Files>
// Only set this, search is on-the-fly
self.searching = Some(input);
}
- Err(HError::MiniBufferInputUpdated(input)) => {
- let file = self.content
- .find_file_with_name(&input)
- .cloned();
-
- file.map(|f| self.select_file(&f));
-
- self.draw().log();
-
+ Err(HError::RefreshParent) => {
+ self.refresh().log();
continue;
- },
- Err(HError::MiniBufferEmptyInput) |
- Err(HError::MiniBufferCancelledInput) => {
- self.select_file(&selected_file);
+ }
+ Err(HError::MiniBufferEvent(ev)) => {
+ use crate::minibuffer::MiniBufferEvent::*;
+
+ match ev {
+ Done(_) => {}
+ NewInput(input) => {
+ let file = self.content
+ .find_file_with_name(&input)
+ .cloned();
+
+ file.map(|f| self.select_file(&f));
+
+ self.draw().log();
+
+ self.searching = Some(input);
+
+ continue;
+ }
+ Empty | Cancelled => {
+ self.select_file(&selected_file);
+ }
+ CycleNext => {
+ self.search_next().log();
+ }
+ CyclePrev => {
+ self.search_prev().log();
+ }
+ }
}
_ => { }
}
@@ -688,35 +706,61 @@ impl ListView<Files>
Ok(())
}
+ pub fn set_filter(&mut self, filter: Option<String>) {
+ let prev_len = self.len();
+ let selected_file = self.clone_selected_file();
+
+ self.content.set_filter(filter);
+
+ // Only do something if filter changed something
+ if self.len() != prev_len {
+ self.refresh().ok();
+ self.select_file(&selected_file);
+ // Clear away that wouldn't get drawn over
+ if self.len() < prev_len {
+ self.core.clear().ok();
+ }
+ self.draw().ok();
+ }
+ }
+
fn filter(&mut self) -> HResult<()> {
+ use crate::minibuffer::MiniBufferEvent::*;
+
let selected_file = self.selected_file().clone();
+ let mut prev_filter = self.content.get_filter();
loop {
let filter = self.core.minibuffer_continuous("filter");
match filter {
- Err(HError::MiniBufferInputUpdated(input)) => {
- self.content.set_filter(Some(input));
- self.refresh().ok();
-
- self.select_file(&selected_file);
- self.draw().ok();
-
- continue;
- }
- Err(HError::MiniBufferEmptyInput) |
- Err(HError::MiniBufferCancelledInput) => {
- self.content.set_filter(None);
- self.refresh().ok();
- self.select_file(&selected_file);
+ Err(HError::MiniBufferEvent(event)) => {
+ match event {
+ Done(filter) => {
+ self.core.show_status(&format!("Filtering with: \"{}\"",
+ &filter)).log();
+
+ self.set_filter(Some(filter));
+ }
+ NewInput(input) => {
+ self.set_filter(Some(input.clone()));
+ continue;
+ }
+ Empty => {
+ self.set_filter(None);
+ }
+ Cancelled => {
+ self.set_filter(prev_filter.take());
+ self.select_file(&selected_file);
+ }
+ _ => {}
+ }
}
_ => {}
}
- let msgstr = filter.clone().unwrap_or(String::from(""));
- self.core.show_status(&format!("Filtering with: \"{}\"", msgstr)).log();
-
break;
+
}
Ok(())
diff --git a/src/minibuffer.rs b/src/minibuffer.rs
index 12cd900..de0ca91 100644
--- a/src/minibuffer.rs
+++ b/src/minibuffer.rs
@@ -128,6 +128,16 @@ impl History {
}
}
+#[derive(Clone, Debug)]
+pub enum MiniBufferEvent {
+ Done(String),
+ NewInput(String),
+ Empty,
+ Cancelled,
+ CycleNext,
+ CyclePrev
+}
+
#[derive(Debug)]
pub struct MiniBuffer {
core: WidgetCore,
@@ -171,8 +181,8 @@ impl MiniBuffer {
self.core.screen()?.cursor_hide().log();
match self.popup() {
- Err(HError::MiniBufferCancelledInput) => self.input_cancelled()?,
- err @ Err(HError::MiniBufferInputUpdated(_)) => err?,
+ event @ Err(HError::MiniBufferEvent(_)) => event?,
+ err @ Err(HError::RefreshParent) => err?,
_ => {}
};
@@ -186,7 +196,6 @@ impl MiniBuffer {
pub fn clear(&mut self) {
self.input.clear();
self.position = 0;
- self.history.reset();
self.completions.clear();
self.last_completion = None;
}
@@ -255,6 +264,11 @@ impl MiniBuffer {
}
pub fn history_up(&mut self) -> HResult<()> {
+ if self.query.as_str() == "nav" {
+ return Err(MiniBufferEvent::CyclePrev)?;
+ }
+
+
if let Ok(historic) = self.history.get_prev(&self.query) {
self.position = historic.len();
self.input = historic;
@@ -263,6 +277,11 @@ impl MiniBuffer {
}
pub fn history_down(&mut self) -> HResult<()> {
+ if self.query.as_str() == "nav" {
+ return Err(MiniBufferEvent::CycleNext)?;
+ }
+
+
if let Ok(historic) = self.history.get_next(&self.query) {
self.position = historic.len();
self.input = historic;
@@ -340,16 +359,16 @@ impl MiniBuffer {
pub fn input_cancelled(&self) -> HResult<()> {
self.core.show_status("Input cancelled").log();
- return HError::minibuffer_cancel()
+ return Err(MiniBufferEvent::Cancelled)?;
}
pub fn input_updated(&self) -> HResult<()> {
- return HError::input_updated(self.input.clone())
+ return Err(MiniBufferEvent::NewInput(self.input.clone()))?;
}
pub fn input_empty(&self) -> HResult<()> {
self.core.show_status("Empty!").log();
- return HError::minibuffer_empty()
+ return Err(MiniBufferEvent::Empty)?;
}
}
diff --git a/src/quick_actions.rs b/src/quick_actions.rs
index 5a08e0e..74ff9db 100644
--- a/src/quick_actions.rs
+++ b/src/quick_actions.rs
@@ -277,7 +277,15 @@ pub fn open(files: Vec<File>,
act_base.map(|act| action_view.content.push(act)).ok();
act_sub.map(|act| action_view.content.push(act)).ok();
- action_view.popup()
+ loop {
+ // TODO: Handle this properly
+ match action_view.popup() {
+ Err(HError::RefreshParent) => continue,
+ Err(HError::WidgetResizedError) => continue,
+ Err(HError::TerminalResizedError) => continue,
+ r @ _ => break r
+ }
+ }
}
@@ -355,6 +363,7 @@ impl QuickAction {
files: Vec<File>,
core: &WidgetCore,
proc_view: Arc<Mutex<ProcView>>) -> HResult<()> {
+ use crate::minibuffer::MiniBufferEvent::*;;
let answers = self.queries
.iter()
@@ -364,7 +373,7 @@ impl QuickAction {
if acc.is_err() { return acc; }
match core.minibuffer(query) {
- Err(HError::MiniBufferEmptyInput) => {
+ Err(HError::MiniBufferEvent(Empty)) => {
acc.as_mut()
.map(|acc| acc.push((OsString::from(query),
OsString::from(""))))
diff --git a/src/widget.rs b/src/widget.rs
index c5c7511..e4dd677 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -139,6 +139,15 @@ impl WidgetCore {
Ok(())
}
+ pub fn minibuffer_clear(&self) -> HResult<()> {
+ self.minibuffer
+ .lock()?
+ .as_mut()?
+ .clear();
+
+ Ok(())
+ }
+
pub fn minibuffer(&self, query: &str) -> HResult<String> {
let answer = self.minibuffer
.lock()?
@@ -338,7 +347,11 @@ pub trait Widget {
print!("\x1b_Ga=d,d=y,y={}\x1b\\", ypos+1);
}
let result = self.run_widget();
- self.get_core()?.clear().log();
+ match result {
+ Err(HError::RefreshParent) => {},
+ _ => self.get_core()?.clear().log()
+ }
+
self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?;
result
}
@@ -364,17 +377,15 @@ pub trait Widget {
match self.on_event(input) {
err @ Err(HError::PopupFinnished) |
err @ Err(HError::Quit) |
- err @ Err(HError::MiniBufferCancelledInput) => err?,
- err @ Err(HError::MiniBufferInputUpdated(_)) => err?,
err @ Err(HError::WidgetResizedError) => err?,
+ event @ Err(HError::MiniBufferEvent(_)) => event?,
err @ Err(_) => err.log(),
Ok(_) => {}
}
self.get_core()?.get_sender().send(Events::RequestInput)?;
}
Events::WidgetReady => {
- self.refresh().log();
- self.draw().log();
+ return Err(HError::RefreshParent);
}
Events::Status(status) => {
self.get_core()?.show_status(&status).log();