1
0
mirror of https://github.com/bobwen-dev/hunter synced 2025-04-12 00:55:41 +02:00

improved navigation/turbo-cd

This commit is contained in:
rabite0 2020-03-01 22:57:09 +01:00
parent b163a9f4b4
commit e4850e38b6
6 changed files with 307 additions and 59 deletions

View File

@ -70,10 +70,6 @@ pub enum HError {
INotifyError(String), INotifyError(String),
#[fail(display = "Tags not loaded yet")] #[fail(display = "Tags not loaded yet")]
TagsNotLoadedYetError, TagsNotLoadedYetError,
#[fail(display = "Input cancelled!")]
MiniBufferCancelledInput,
#[fail(display = "Empty input!")]
MiniBufferEmptyInput,
#[fail(display = "Undefined key: {:?}", key)] #[fail(display = "Undefined key: {:?}", key)]
WidgetUndefinedKeyError{key: Key}, WidgetUndefinedKeyError{key: Key},
#[fail(display = "Terminal has been resized!")] #[fail(display = "Terminal has been resized!")]
@ -107,7 +103,11 @@ pub enum HError {
#[fail(display = "{}", _0)] #[fail(display = "{}", _0)]
FileError(crate::files::FileError), FileError(crate::files::FileError),
#[fail(display = "{}", _0)] #[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 { impl HError {
@ -134,12 +134,6 @@ impl HError {
pub fn tags_not_loaded<T>() -> HResult<T> { pub fn tags_not_loaded<T>() -> HResult<T> {
Err(HError::TagsNotLoadedYetError) 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> { pub fn undefined_key<T>(key: Key) -> HResult<T> {
Err(HError::WidgetUndefinedKeyError { key: key }) 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)] #[derive(Fail, Debug, Clone)]
pub enum KeyBindError { pub enum KeyBindError {

View File

@ -631,6 +631,13 @@ impl FileBrowser {
self.draw().log(); self.draw().log();
continue; continue;
} }
if let Err(HError::RefreshParent) = bookmark {
self.refresh().log();
self.draw().log();
continue;
}
return bookmark; return bookmark;
} }
} }
@ -876,11 +883,146 @@ impl FileBrowser {
} }
pub fn turbo_cd(&mut self) -> HResult<()> { pub fn turbo_cd(&mut self) -> HResult<()> {
let dir = self.core.minibuffer("cd")?; use crate::minibuffer::MiniBufferEvent::*;
let path = std::path::PathBuf::from(&dir); // Return and reset on cancel
let dir = File::new_from_path(&path.canonicalize()?)?; let orig_dir = self.cwd()?.clone();
self.main_widget_goto(&dir)?; 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();
};
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(()) Ok(())
} }
@ -1121,13 +1263,36 @@ impl FileBrowser {
pub fn show_procview(&mut self) -> HResult<()> { pub fn show_procview(&mut self) -> HResult<()> {
self.preview_widget().map(|preview| preview.cancel_animation()).log(); 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(()) Ok(())
} }
pub fn show_log(&mut self) -> HResult<()> { pub fn show_log(&mut self) -> HResult<()> {
self.preview_widget().map(|preview| preview.cancel_animation()).log(); 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(()) Ok(())
} }

View File

@ -602,20 +602,38 @@ impl ListView<Files>
// Only set this, search is on-the-fly // Only set this, search is on-the-fly
self.searching = Some(input); self.searching = Some(input);
} }
Err(HError::MiniBufferInputUpdated(input)) => { Err(HError::RefreshParent) => {
let file = self.content self.refresh().log();
.find_file_with_name(&input)
.cloned();
file.map(|f| self.select_file(&f));
self.draw().log();
continue; continue;
}, }
Err(HError::MiniBufferEmptyInput) | Err(HError::MiniBufferEvent(ev)) => {
Err(HError::MiniBufferCancelledInput) => { use crate::minibuffer::MiniBufferEvent::*;
self.select_file(&selected_file);
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(()) 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<()> { fn filter(&mut self) -> HResult<()> {
use crate::minibuffer::MiniBufferEvent::*;
let selected_file = self.selected_file().clone(); let selected_file = self.selected_file().clone();
let mut prev_filter = self.content.get_filter();
loop { loop {
let filter = self.core.minibuffer_continuous("filter"); let filter = self.core.minibuffer_continuous("filter");
match filter { match filter {
Err(HError::MiniBufferInputUpdated(input)) => { Err(HError::MiniBufferEvent(event)) => {
self.content.set_filter(Some(input)); match event {
self.refresh().ok(); Done(filter) => {
self.core.show_status(&format!("Filtering with: \"{}\"",
&filter)).log();
self.select_file(&selected_file); self.set_filter(Some(filter));
self.draw().ok(); }
NewInput(input) => {
continue; self.set_filter(Some(input.clone()));
} continue;
Err(HError::MiniBufferEmptyInput) | }
Err(HError::MiniBufferCancelledInput) => { Empty => {
self.content.set_filter(None); self.set_filter(None);
self.refresh().ok(); }
self.select_file(&selected_file); 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; break;
} }
Ok(()) Ok(())

View File

@ -128,6 +128,16 @@ impl History {
} }
} }
#[derive(Clone, Debug)]
pub enum MiniBufferEvent {
Done(String),
NewInput(String),
Empty,
Cancelled,
CycleNext,
CyclePrev
}
#[derive(Debug)] #[derive(Debug)]
pub struct MiniBuffer { pub struct MiniBuffer {
core: WidgetCore, core: WidgetCore,
@ -171,8 +181,8 @@ impl MiniBuffer {
self.core.screen()?.cursor_hide().log(); self.core.screen()?.cursor_hide().log();
match self.popup() { match self.popup() {
Err(HError::MiniBufferCancelledInput) => self.input_cancelled()?, event @ Err(HError::MiniBufferEvent(_)) => event?,
err @ Err(HError::MiniBufferInputUpdated(_)) => err?, err @ Err(HError::RefreshParent) => err?,
_ => {} _ => {}
}; };
@ -186,7 +196,6 @@ impl MiniBuffer {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.input.clear(); self.input.clear();
self.position = 0; self.position = 0;
self.history.reset();
self.completions.clear(); self.completions.clear();
self.last_completion = None; self.last_completion = None;
} }
@ -255,6 +264,11 @@ impl MiniBuffer {
} }
pub fn history_up(&mut self) -> HResult<()> { 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) { if let Ok(historic) = self.history.get_prev(&self.query) {
self.position = historic.len(); self.position = historic.len();
self.input = historic; self.input = historic;
@ -263,6 +277,11 @@ impl MiniBuffer {
} }
pub fn history_down(&mut self) -> HResult<()> { 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) { if let Ok(historic) = self.history.get_next(&self.query) {
self.position = historic.len(); self.position = historic.len();
self.input = historic; self.input = historic;
@ -340,16 +359,16 @@ impl MiniBuffer {
pub fn input_cancelled(&self) -> HResult<()> { pub fn input_cancelled(&self) -> HResult<()> {
self.core.show_status("Input cancelled").log(); self.core.show_status("Input cancelled").log();
return HError::minibuffer_cancel() return Err(MiniBufferEvent::Cancelled)?;
} }
pub fn input_updated(&self) -> HResult<()> { 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<()> { pub fn input_empty(&self) -> HResult<()> {
self.core.show_status("Empty!").log(); self.core.show_status("Empty!").log();
return HError::minibuffer_empty() return Err(MiniBufferEvent::Empty)?;
} }
} }

View File

@ -277,7 +277,15 @@ pub fn open(files: Vec<File>,
act_base.map(|act| action_view.content.push(act)).ok(); act_base.map(|act| action_view.content.push(act)).ok();
act_sub.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>, files: Vec<File>,
core: &WidgetCore, core: &WidgetCore,
proc_view: Arc<Mutex<ProcView>>) -> HResult<()> { proc_view: Arc<Mutex<ProcView>>) -> HResult<()> {
use crate::minibuffer::MiniBufferEvent::*;;
let answers = self.queries let answers = self.queries
.iter() .iter()
@ -364,7 +373,7 @@ impl QuickAction {
if acc.is_err() { return acc; } if acc.is_err() { return acc; }
match core.minibuffer(query) { match core.minibuffer(query) {
Err(HError::MiniBufferEmptyInput) => { Err(HError::MiniBufferEvent(Empty)) => {
acc.as_mut() acc.as_mut()
.map(|acc| acc.push((OsString::from(query), .map(|acc| acc.push((OsString::from(query),
OsString::from("")))) OsString::from(""))))

View File

@ -139,6 +139,15 @@ impl WidgetCore {
Ok(()) Ok(())
} }
pub fn minibuffer_clear(&self) -> HResult<()> {
self.minibuffer
.lock()?
.as_mut()?
.clear();
Ok(())
}
pub fn minibuffer(&self, query: &str) -> HResult<String> { pub fn minibuffer(&self, query: &str) -> HResult<String> {
let answer = self.minibuffer let answer = self.minibuffer
.lock()? .lock()?
@ -338,7 +347,11 @@ pub trait Widget {
print!("\x1b_Ga=d,d=y,y={}\x1b\\", ypos+1); print!("\x1b_Ga=d,d=y,y={}\x1b\\", ypos+1);
} }
let result = self.run_widget(); 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))?; self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?;
result result
} }
@ -364,17 +377,15 @@ pub trait Widget {
match self.on_event(input) { match self.on_event(input) {
err @ Err(HError::PopupFinnished) | err @ Err(HError::PopupFinnished) |
err @ Err(HError::Quit) | err @ Err(HError::Quit) |
err @ Err(HError::MiniBufferCancelledInput) => err?,
err @ Err(HError::MiniBufferInputUpdated(_)) => err?,
err @ Err(HError::WidgetResizedError) => err?, err @ Err(HError::WidgetResizedError) => err?,
event @ Err(HError::MiniBufferEvent(_)) => event?,
err @ Err(_) => err.log(), err @ Err(_) => err.log(),
Ok(_) => {} Ok(_) => {}
} }
self.get_core()?.get_sender().send(Events::RequestInput)?; self.get_core()?.get_sender().send(Events::RequestInput)?;
} }
Events::WidgetReady => { Events::WidgetReady => {
self.refresh().log(); return Err(HError::RefreshParent);
self.draw().log();
} }
Events::Status(status) => { Events::Status(status) => {
self.get_core()?.show_status(&status).log(); self.get_core()?.show_status(&status).log();