use std::sync::{Arc, Mutex, RwLock}; use std::boxed::FnBox; use rayon::ThreadPool; use crate::files::{File, Files, Kind}; use crate::fscache::FsCache; use crate::listview::ListView; use crate::textview::TextView; use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; use crate::fail::{HResult, HError, ErrorLog}; use crate::dirty::Dirtyable; pub type AsyncValueFn = Box HResult + Send + Sync>; pub type AsyncValue = Arc>>>; pub type AsyncReadyFn = Box HResult<()> + Send + Sync>; pub type AsyncWidgetFn = Box HResult + Send + Sync>; type WidgetO = Box; lazy_static! { static ref SUBPROC: Arc>> = { Arc::new(Mutex::new(None)) }; } fn kill_proc() -> HResult<()> { let mut pid = SUBPROC.lock()?; pid.map(|pid| unsafe { libc::kill(pid as i32, 15); } ); *pid = None; Ok(()) } #[derive(Clone, Debug)] pub struct Stale(Arc>); impl Stale { pub fn new() -> Stale { Stale(Arc::new(RwLock::new(false))) } pub fn is_stale(&self) -> HResult { Ok(*self.0.read()?) } pub fn set_stale(&self) -> HResult<()> { *self.0.write()? = true; Ok(()) } pub fn set_fresh(&self) -> HResult<()> { *self.0.write()? = false; Ok(()) } } pub fn is_stale(stale: &Stale) -> HResult { let stale = stale.is_stale()?; Ok(stale) } use std::fmt::{Debug, Formatter}; impl Debug for Async { fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> { write!(formatter, "{:?}, {:?} {:?}", self.value, self.async_value, self.stale) } } #[derive(Clone)] pub struct Async { pub value: HResult, async_value: AsyncValue, async_closure: Arc>>>, on_ready: Arc>>, started: bool, stale: Stale, } impl Async { pub fn new(closure: AsyncValueFn) -> Async { let async_value = Async { value: HError::async_not_ready(), async_value: Arc::new(Mutex::new(None)), async_closure: Arc::new(Mutex::new(Some(closure))), on_ready: Arc::new(Mutex::new(None)), started: false, stale: Stale::new() }; async_value } pub fn new_with_stale(closure: AsyncValueFn, stale: Stale) -> Async { let async_value = Async { value: HError::async_not_ready(), async_value: Arc::new(Mutex::new(None)), async_closure: Arc::new(Mutex::new(Some(closure))), on_ready: Arc::new(Mutex::new(None)), started: false, stale: stale }; async_value } pub fn new_with_value(val: T) -> Async { Async { value: Ok(val), async_value: Arc::new(Mutex::new(None)), async_closure: Arc::new(Mutex::new(None)), on_ready: Arc::new(Mutex::new(None)), started: false, stale: Stale::new() } } pub fn run_async(async_fn: Arc>>>, async_value: AsyncValue, on_ready_fn: Arc>>, stale: Stale) -> HResult<()> { let value_fn = async_fn.lock()?.take()?; let value = value_fn.call_box((stale.clone(),)); async_value.lock()?.replace(value); on_ready_fn.lock()? .take() .map(|on_ready| on_ready.call_box(()).log()); Ok(()) } pub fn run(&mut self) -> HResult<()> { if self.started { HError::async_started()? } let closure = self.async_closure.clone(); let async_value = self.async_value.clone(); let stale = self.stale.clone(); let on_ready_fn = self.on_ready.clone(); self.started = true; std::thread::spawn(move || { Async::run_async(closure, async_value, on_ready_fn, stale).log(); }); Ok(()) } pub fn run_pooled(&mut self, pool: &ThreadPool) -> HResult<()> { if self.started { HError::async_started()? } let closure = self.async_closure.clone(); let async_value = self.async_value.clone(); let stale = self.stale.clone(); let on_ready_fn = self.on_ready.clone(); self.started = true; pool.spawn(move || { Async::run_async(closure, async_value, on_ready_fn, stale).log(); }); Ok(()) } pub fn wait(self) -> HResult { Async::run_async(self.async_closure, self.async_value.clone(), self.on_ready, self.stale).log(); let value = self.async_value.lock()?.take()?; value } pub fn set_stale(&mut self) -> HResult<()> { self.stale.set_stale()?; Ok(()) } pub fn set_fresh(&self) -> HResult<()> { self.stale.set_fresh()?; Ok(()) } pub fn is_stale(&self) -> HResult { self.stale.is_stale() } pub fn get_stale(&self) -> Stale { self.stale.clone() } pub fn put_stale(&mut self, stale: Stale) { self.stale = stale; } pub fn is_started(&self) -> bool { self.started } pub fn set_unstarted(&mut self) { self.started = false; } pub fn take_async(&mut self) -> HResult<()> { if self.value.is_ok() { HError::async_taken()? } let mut async_value = self.async_value.lock()?; match async_value.as_ref() { Some(Ok(_)) => { let value = async_value.take()?; self.value = value; } Some(Err(HError::AsyncAlreadyTakenError)) => HError::async_taken()?, Some(Err(_)) => { let value = async_value.take()?; self.value = value; } None => HError::async_not_ready()?, } Ok(()) } pub fn get(&self) -> HResult<&T> { match self.value { Ok(ref value) => Ok(value), Err(ref err) => HError::async_error(err) } } pub fn get_mut(&mut self) -> HResult<&mut T> { self.take_async().ok(); match self.value { Ok(ref mut value) => Ok(value), Err(ref err) => HError::async_error(err) } } pub fn on_ready(&mut self, fun: AsyncReadyFn) { *self.on_ready.lock().unwrap() = Some(fun); } } impl PartialEq for AsyncWidget { fn eq(&self, other: &AsyncWidget) -> bool { if self.get_coordinates().unwrap() == other.get_coordinates().unwrap() { true } else { false } } } pub struct AsyncWidget { widget: Async, core: WidgetCore } impl AsyncWidget { pub fn new(core: &WidgetCore, closure: AsyncValueFn) -> AsyncWidget { let sender = Mutex::new(core.get_sender()); let mut widget = Async::new(Box::new(move |stale| closure.call_box((stale,)))); widget.on_ready(Box::new(move || { sender.lock()?.send(crate::widget::Events::WidgetReady)?; Ok(()) })); widget.run().log(); AsyncWidget { widget: widget, core: core.clone() } } pub fn change_to(&mut self, closure: AsyncWidgetFn) -> HResult<()> { self.set_stale().log(); let sender = Mutex::new(self.get_core()?.get_sender()); let core = self.get_core()?.clone(); let mut widget = Async::new(Box::new(move |stale| { closure.call_box((stale, core.clone(),)) })); widget.on_ready(Box::new(move || { sender.lock()?.send(crate::widget::Events::WidgetReady)?; Ok(()) })); widget.run().log(); self.widget = widget; Ok(()) } pub fn set_stale(&mut self) -> HResult<()> { self.widget.set_stale() } pub fn is_stale(&self) -> HResult { self.widget.is_stale() } pub fn get_stale(&self) -> Stale { self.widget.get_stale() } pub fn widget(&self) -> HResult<&W> { self.widget.get() } pub fn widget_mut(&mut self) -> HResult<&mut W> { self.widget.get_mut() } pub fn take_widget(self) -> HResult { Ok(self.widget.value?) } pub fn ready(&self) -> bool { self.widget().is_ok() } } impl Widget for AsyncWidget { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { Ok(&mut self.core) } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { self.core.coordinates = coordinates.clone(); if let Ok(widget) = self.widget_mut() { widget.set_coordinates(&coordinates)?; } Ok(()) } fn refresh(&mut self) -> HResult<()> { self.widget.take_async().ok(); let coords = self.get_coordinates()?.clone(); if let Ok(widget) = self.widget_mut() { if widget.get_coordinates()? != &coords { widget.set_coordinates(&coords)?; widget.refresh()?; } else { widget.refresh()?; } } Ok(()) } fn get_drawlist(&self) -> HResult { if self.widget().is_err() { let clear = self.get_clearlist()?; let (xpos, ypos) = self.get_coordinates()?.u16position(); let pos = crate::term::goto_xy(xpos, ypos); return Ok(clear + &pos + "...") } if self.is_stale()? { return self.get_clearlist() } self.widget()?.get_drawlist() } fn on_key(&mut self, key: termion::event::Key) -> HResult<()> { if self.widget().is_err() { return Ok(()) } self.widget_mut()?.on_key(key) } } impl PartialEq for Previewer { fn eq(&self, other: &Previewer) -> bool { if self.widget.get_coordinates().unwrap() == other.widget.get_coordinates().unwrap() { true } else { false } } } #[derive(PartialEq)] enum PreviewWidget { FileList(ListView), TextView(TextView) } pub struct Previewer { widget: AsyncWidget, core: WidgetCore, file: Option, pub cache: FsCache, animator: Stale } impl Previewer { pub fn new(core: &WidgetCore, cache: FsCache) -> Previewer { let core_ = core.clone(); let widget = AsyncWidget::new(&core, Box::new(move |_| { let blank = TextView::new_blank(&core_); let blank = PreviewWidget::TextView(blank); Ok(blank) })); Previewer { widget: widget, core: core.clone(), file: None, cache: cache, animator: Stale::new()} } fn become_preview(&mut self, widget: HResult>) -> HResult<()> { let coordinates = self.get_coordinates()?.clone(); self.widget = widget?; self.widget.set_coordinates(&coordinates)?; Ok(()) } pub fn set_stale(&mut self) -> HResult<()> { self.widget.set_stale() } pub fn get_file(&self) -> Option<&File> { self.file.as_ref() } pub fn cancel_animation(&self) -> HResult<()> { self.animator.set_stale() } pub fn take_files(&mut self) -> HResult { let core = self.core.clone(); let mut widget = AsyncWidget::new(&core.clone(), Box::new(move |_| { let widget = TextView::new_blank(&core); let widget = PreviewWidget::TextView(widget); Ok(widget) })); std::mem::swap(&mut self.widget, &mut widget); match widget.take_widget() { Ok(PreviewWidget::FileList(file_list)) => { let files = file_list.content; Ok(files) } _ => HError::no_files()? } } pub fn replace_file(&mut self, dir: &File, old: Option<&File>, new: Option<&File>) -> HResult<()> { if self.file.as_ref() != Some(dir) { return Ok(()) } self.widget.widget_mut().map(|widget| { match widget { PreviewWidget::FileList(filelist) => { filelist.content.replace_file(old, new.cloned()).map(|_| { filelist.refresh().ok(); }).ok(); } _ => {} } }) } pub fn put_preview_files(&mut self, files: Files) { let core = self.core.clone(); let dir = files.directory.clone(); let cache = self.cache.clone(); self.file = Some(dir); self.widget = AsyncWidget::new(&self.core, Box::new(move |_| { let selected_file = cache.get_selection(&files.directory); let mut filelist = ListView::new(&core, files); selected_file.map(|file| filelist.select_file(&file)).log(); Ok(PreviewWidget::FileList(filelist)) })); } pub fn set_file(&mut self, file: &File) -> HResult<()> { if Some(file) == self.file.as_ref() && !self.widget.is_stale()? { return Ok(()) } self.file = Some(file.clone()); let coordinates = self.get_coordinates().unwrap().clone(); let file = file.clone(); let core = self.core.clone(); let cache = self.cache.clone(); let animator = self.animator.clone(); self.widget.set_stale().ok(); self.animator.set_fresh().log(); self.become_preview(Ok(AsyncWidget::new(&self.core, Box::new(move |stale: Stale| { kill_proc().unwrap(); if file.kind == Kind::Directory { let preview = Previewer::preview_dir(&file, cache, &core, stale, animator); return preview; } if file.is_text() { return Previewer::preview_text(&file, &core, stale, animator); } let preview = Previewer::preview_external(&file, &core, stale, animator.clone()); if preview.is_ok() { return preview; } else { let mut blank = TextView::new_blank(&core); blank.set_coordinates(&coordinates).log(); blank.refresh().log(); blank.animate_slide_up(Some(animator)).log(); return Ok(PreviewWidget::TextView(blank)) } })))) } pub fn reload(&mut self) { if let Some(file) = self.file.clone() { self.file = None; self.set_file(&file).log(); } } fn preview_failed(file: &File) -> HResult { HError::preview_failed(file) } fn preview_dir(file: &File, cache: FsCache, core: &WidgetCore, stale: Stale, animator: Stale) -> HResult { let (selection, cached_files) = cache.get_files(&file, stale.clone())?; let files = cached_files.wait()?; let len = files.len(); if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) } let mut file_list = ListView::new(&core, files); if let Some(selection) = selection { file_list.select_file(&selection); } file_list.set_coordinates(&core.coordinates)?; file_list.refresh()?; if is_stale(&stale)? { return Previewer::preview_failed(&file) } file_list.animate_slide_up(Some(animator))?; Ok(PreviewWidget::FileList(file_list)) } fn preview_text(file: &File, core: &WidgetCore, stale: Stale, animator: Stale) -> HResult { let lines = core.coordinates.ysize() as usize; let mut textview = TextView::new_from_file_limit_lines(&core, &file, lines)?; if is_stale(&stale)? { return Previewer::preview_failed(&file) } textview.set_coordinates(&core.coordinates)?; textview.refresh()?; if is_stale(&stale)? { return Previewer::preview_failed(&file) } textview.animate_slide_up(Some(animator))?; Ok(PreviewWidget::TextView(textview)) } fn preview_external(file: &File, core: &WidgetCore, stale: Stale, animator: Stale) -> HResult { let process = std::process::Command::new("scope.sh") .arg(&file.path) .arg("10".to_string()) .arg("10".to_string()) .arg("".to_string()) .arg("false".to_string()) .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::null()) .spawn()?; let pid = process.id(); { let mut pid_ = SUBPROC.lock()?; *pid_ = Some(pid); } if is_stale(&stale)? { return Previewer::preview_failed(&file) } let output = process.wait_with_output()?; if is_stale(&stale)? { return Previewer::preview_failed(&file) } { let mut pid_ = SUBPROC.lock()?; *pid_ = None; } //let status = output.status.code()?; if !is_stale(&stale)? { let output = std::str::from_utf8(&output.stdout) .unwrap() .to_string(); let mut textview = TextView { lines: output.lines().map(|s| s.to_string()).collect(), core: core.clone(), follow: false, offset: 0}; textview.set_coordinates(&core.coordinates).log(); textview.refresh().log(); textview.animate_slide_up(Some(animator)).log(); return Ok(PreviewWidget::TextView(textview)) } HError::preview_failed(file) } } impl Widget for Previewer { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { Ok(&mut self.core) } fn config_loaded(&mut self) -> HResult<()> { let show_hidden = self.config().show_hidden(); if let PreviewWidget::FileList(filelist) = self.widget.widget_mut()? { filelist.content.show_hidden = show_hidden; filelist.content.dirty_meta.set_dirty(); } Ok(()) } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { self.core.coordinates = coordinates.clone(); self.widget.set_coordinates(&coordinates) } fn refresh(&mut self) -> HResult<()> { self.widget.refresh() } fn get_drawlist(&self) -> HResult { self.widget.get_drawlist() } } impl Widget for PreviewWidget { fn get_core(&self) -> HResult<&WidgetCore> { match self { PreviewWidget::FileList(widget) => widget.get_core(), PreviewWidget::TextView(widget) => widget.get_core() } } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { match self { PreviewWidget::FileList(widget) => widget.get_core_mut(), PreviewWidget::TextView(widget) => widget.get_core_mut() } } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { match self { PreviewWidget::FileList(widget) => widget.set_coordinates(coordinates), PreviewWidget::TextView(widget) => widget.set_coordinates(coordinates), } } fn refresh(&mut self) -> HResult<()> { match self { PreviewWidget::FileList(widget) => widget.refresh(), PreviewWidget::TextView(widget) => widget.refresh() } } fn get_drawlist(&self) -> HResult { match self { PreviewWidget::FileList(widget) => widget.get_drawlist(), PreviewWidget::TextView(widget) => widget.get_drawlist() } } } impl Widget for Box where T: Widget + ?Sized { fn get_core(&self) -> HResult<&WidgetCore> { Ok((**self).get_core()?) } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { Ok((**self).get_core_mut()?) } fn render_header(&self) -> HResult { (**self).render_header() } fn refresh(&mut self) -> HResult<()> { (**self).refresh() } fn get_drawlist(&self) -> HResult { (**self).get_drawlist() } }