1 use {
2 super::*,
3 crate::{
4 browser::BrowserState,
5 command::{Command, Sequence},
6 conf::Conf,
7 display::{Areas, Screen, W},
8 errors::ProgramError,
9 file_sum, git,
10 launchable::Launchable,
11 path::closest_dir,
12 skin::*,
13 stage::Stage,
14 task_sync::{Dam, Either},
15 verb::Internal,
16 },
17 crossbeam::channel::{
18 Receiver,
19 Sender,
20 unbounded,
21 },
22 crossterm::event::Event,
23 std::{
24 io::Write,
25 path::PathBuf,
26 sync::{Arc, Mutex},
27 },
28 strict::NonEmptyVec,
29 termimad::EventSource,
30 };
31
32
33 /// The GUI
34 pub struct App {
35 /// dimensions of the screen
36 screen: Screen,
37
38 /// the panels of the application, at least one
39 panels: NonEmptyVec<Panel>,
40
41 /// index of the currently focused panel
42 active_panel_idx: usize,
43
44 /// whether the app is in the (uncancellable) process of quitting
45 quitting: bool,
46
47 /// what must be done after having closed the TUI
48 launch_at_end: Option<Launchable>,
49
50 /// a count of all panels created
51 created_panels_count: usize,
52
53 /// the panel dedicated to preview, if any
54 preview_panel: Option<PanelId>,
55
56 stage_panel: Option<PanelId>,
57
58 /// an optional copy of the root for the --server
59 shared_root: Option<Arc<Mutex<PathBuf>>>,
60
61 /// sender to the sequence channel
62 tx_seqs: Sender<Sequence>,
63
64 /// receiver to listen to the sequence channel
65 rx_seqs: Receiver<Sequence>,
66 }
67
68 impl App {
69
new( con: &AppContext, ) -> Result<App, ProgramError>70 pub fn new(
71 con: &AppContext,
72 ) -> Result<App, ProgramError> {
73 let screen = Screen::new(con)?;
74 let panel = Panel::new(
75 PanelId::from(0),
76 Box::new(
77 BrowserState::new(
78 con.launch_args.root.clone(),
79 con.launch_args.tree_options.clone(),
80 screen,
81 con,
82 &Dam::unlimited(),
83 )?
84 .expect("Failed to create BrowserState"),
85 ),
86 Areas::create(&mut Vec::new(), 0, screen, false)?,
87 con,
88 );
89 let (tx_seqs, rx_seqs) = unbounded::<Sequence>();
90 Ok(App {
91 screen,
92 active_panel_idx: 0,
93 panels: panel.into(),
94 quitting: false,
95 launch_at_end: None,
96 created_panels_count: 1,
97 preview_panel: None,
98 stage_panel: None,
99 shared_root: None,
100 tx_seqs,
101 rx_seqs,
102 })
103 }
104
panel_ref_to_idx(&self, panel_ref: PanelReference) -> Option<usize>105 fn panel_ref_to_idx(&self, panel_ref: PanelReference) -> Option<usize> {
106 match panel_ref {
107 PanelReference::Active => Some(self.active_panel_idx),
108 PanelReference::Leftest => Some(0),
109 PanelReference::Rightest => Some(self.panels.len().get() - 1),
110 PanelReference::Id(id) => self.panel_id_to_idx(id),
111 PanelReference::Preview => self.preview_panel.and_then(|id| self.panel_id_to_idx(id)),
112 }
113 }
114
115 /// return the current index of the panel whith given id
panel_id_to_idx(&self, id: PanelId) -> Option<usize>116 fn panel_id_to_idx(&self, id: PanelId) -> Option<usize> {
117 self.panels.iter().position(|panel| panel.id == id)
118 }
119
state(&self) -> &dyn PanelState120 fn state(&self) -> &dyn PanelState {
121 self.panels[self.active_panel_idx].state()
122 }
mut_state(&mut self) -> &mut dyn PanelState123 fn mut_state(&mut self) -> &mut dyn PanelState {
124 self.panels[self.active_panel_idx].mut_state()
125 }
panel(&self) -> &Panel126 fn panel(&self) -> &Panel {
127 &self.panels[self.active_panel_idx]
128 }
mut_panel(&mut self) -> &mut Panel129 fn mut_panel(&mut self) -> &mut Panel {
130 unsafe {
131 self.panels
132 .as_mut_slice()
133 .get_unchecked_mut(self.active_panel_idx)
134 }
135 }
136
137 /// close the panel if it's not the last one
138 ///
139 /// Return true when the panel has been removed (ie it wasn't the last one)
close_panel(&mut self, panel_idx: usize) -> bool140 fn close_panel(&mut self, panel_idx: usize) -> bool {
141 let active_panel_id = self.panels[self.active_panel_idx].id;
142 if let Some(preview_id) = self.preview_panel {
143 if self.panels.has_len(2) && self.panels[panel_idx].id != preview_id {
144 // we don't want to stay with just the preview
145 return false;
146 }
147 }
148 if let Some(stage_id) = self.stage_panel {
149 if self.panels.has_len(2) && self.panels[panel_idx].id != stage_id {
150 // we don't want to stay with just the stage
151 return false;
152 }
153 }
154 if let Ok(removed_panel) = self.panels.remove(panel_idx) {
155 if self.preview_panel == Some(removed_panel.id) {
156 self.preview_panel = None;
157 }
158 if self.stage_panel == Some(removed_panel.id) {
159 self.stage_panel = None;
160 }
161 Areas::resize_all(
162 self.panels.as_mut_slice(),
163 self.screen,
164 self.preview_panel.is_some(),
165 )
166 .expect("removing a panel should be easy");
167 self.active_panel_idx = self
168 .panels
169 .iter()
170 .position(|p| p.id == active_panel_id)
171 .unwrap_or(self.panels.len().get() - 1);
172 true
173 } else {
174 false // there's no other panel to go to
175 }
176 }
177
178 /// remove the top state of the current panel
179 ///
180 /// Close the panel too if that was its only state.
181 /// Close nothing and return false if there's not
182 /// at least two states in the app.
remove_state(&mut self) -> bool183 fn remove_state(&mut self) -> bool {
184 self.panels[self.active_panel_idx].remove_state()
185 || self.close_panel(self.active_panel_idx)
186 }
187
188 /// redraw the whole screen. All drawing
189 /// are supposed to happen here, and only here.
display_panels( &mut self, w: &mut W, skin: &AppSkin, app_state: &AppState, con: &AppContext, ) -> Result<(), ProgramError>190 fn display_panels(
191 &mut self,
192 w: &mut W,
193 skin: &AppSkin,
194 app_state: &AppState,
195 con: &AppContext,
196 ) -> Result<(), ProgramError> {
197 // if some images are displayed by kitty, we'll erase it,
198 // but only after having displayed the new ones (if any)
199 // to prevent some flickerings
200 #[cfg(unix)]
201 let previous_images = crate::kitty::image_renderer()
202 .as_ref()
203 .and_then(|renderer| {
204 let mut renderer = renderer.lock().unwrap();
205 renderer.take_current_images()
206 });
207 for (idx, panel) in self.panels.as_mut_slice().iter_mut().enumerate() {
208 let active = idx == self.active_panel_idx;
209 let panel_skin = if active { &skin.focused } else { &skin.unfocused };
210 let disc = DisplayContext {
211 active,
212 screen: self.screen,
213 panel_skin,
214 state_area: panel.areas.state.clone(),
215 app_state,
216 con,
217 };
218 time!(
219 "display panel",
220 panel.display(w, &disc)?,
221 );
222 }
223 #[cfg(unix)]
224 if let Some(previous_images) = previous_images {
225 if let Some(renderer) = crate::kitty::image_renderer().as_ref() {
226 let mut renderer = renderer.lock().unwrap();
227 renderer.erase(w, previous_images)?;
228 }
229 }
230 w.flush()?;
231 Ok(())
232 }
233
234 /// if there are exactly two non preview panels, return the selection
235 /// in the non focused panel
get_other_panel_path(&self) -> Option<PathBuf>236 fn get_other_panel_path(&self) -> Option<PathBuf> {
237 let len = self.panels.len().get();
238 if len == 3 {
239 if let Some(preview_id) = self.preview_panel {
240 for (idx, panel) in self.panels.iter().enumerate() {
241 if self.active_panel_idx != idx && panel.id != preview_id {
242 return panel.state().selected_path()
243 .map(|p| p.to_path_buf());
244 }
245 }
246 }
247 None
248 } else if self.panels.len().get() == 2 && self.preview_panel.is_none() {
249 let non_focused_panel_idx = if self.active_panel_idx == 0 { 1 } else { 0 };
250 self.panels[non_focused_panel_idx].state().selected_path()
251 .map(|p| p.to_path_buf())
252 } else {
253 None
254 }
255 }
256
257 /// apply a command. Change the states but don't redraw on screen.
apply_command( &mut self, w: &mut W, cmd: Command, panel_skin: &PanelSkin, app_state: &mut AppState, con: &AppContext, ) -> Result<(), ProgramError>258 fn apply_command(
259 &mut self,
260 w: &mut W,
261 cmd: Command,
262 panel_skin: &PanelSkin,
263 app_state: &mut AppState,
264 con: &AppContext,
265 ) -> Result<(), ProgramError> {
266 use CmdResult::*;
267 let mut error: Option<String> = None;
268 let is_input_invocation = cmd.is_verb_invocated_from_input();
269 let app_cmd_context = AppCmdContext {
270 panel_skin,
271 preview_panel: self.preview_panel,
272 stage_panel: self.stage_panel,
273 screen: self.screen, // it can't change in this function
274 con,
275 };
276 match self.mut_panel().apply_command(w, &cmd, app_state, &app_cmd_context)? {
277 ApplyOnPanel { id } => {
278 if let Some(idx) = self.panel_id_to_idx(id) {
279 if let DisplayError(txt) = self.panels[idx].apply_command(
280 w, &cmd, app_state, &app_cmd_context
281 )? {
282 // we should probably handle other results
283 // which implies the possibility of a recursion
284 error = Some(txt);
285 } else if is_input_invocation {
286 self.mut_panel().clear_input();
287 }
288 } else {
289 warn!("no panel found for ApplyOnPanel");
290 }
291 }
292 ClosePanel { validate_purpose, panel_ref } => {
293 if is_input_invocation {
294 self.mut_panel().clear_input_invocation(con);
295 }
296 let close_idx = self.panel_ref_to_idx(panel_ref)
297 .unwrap_or_else(||
298 // when there's a preview panel, we close it rather than the app
299 if self.panels.len().get()==2 && self.preview_panel.is_some() {
300 1
301 } else {
302 self.active_panel_idx
303 }
304 );
305 let mut new_arg = None;
306 if validate_purpose {
307 let purpose = &self.panels[close_idx].purpose;
308 if let PanelPurpose::ArgEdition { .. } = purpose {
309 new_arg = self.panels[close_idx]
310 .state()
311 .selected_path()
312 .map(|p| p.to_string_lossy().to_string());
313 }
314 }
315 if self.close_panel(close_idx) {
316 let screen = self.screen;
317 self.mut_state().refresh(screen, con);
318 if let Some(new_arg) = new_arg {
319 self.mut_panel().set_input_arg(new_arg);
320 let new_input = self.panel().get_input_content();
321 let cmd = Command::from_raw(new_input, false);
322 let app_cmd_context = AppCmdContext {
323 panel_skin,
324 preview_panel: self.preview_panel,
325 stage_panel: self.stage_panel,
326 screen,
327 con,
328 };
329 self.mut_panel().apply_command(w, &cmd, app_state, &app_cmd_context)?;
330 }
331 } else {
332 self.quitting = true;
333 }
334 }
335 DisplayError(txt) => {
336 error = Some(txt);
337 }
338 ExecuteSequence { sequence } => {
339 self.tx_seqs.send(sequence).unwrap();
340 }
341 HandleInApp(internal) => {
342 match internal {
343 Internal::panel_left | Internal::panel_right => {
344 let new_active_panel_idx = if internal == Internal::panel_left {
345 // we're not here to create a panel, this is handled in the state
346 // we're here because the state wants us to either move to the panel
347 // to the left, or close the rightest one
348 if self.active_panel_idx == 0 {
349 self.close_panel(self.panels.len().get()-1);
350 None
351 } else {
352 Some(self.active_panel_idx - 1)
353 }
354 } else { // panel_right
355 // we're not here to create panels (it's done in the state).
356 // So we either move to the right or close the leftes panel
357 if self.active_panel_idx + 1 == self.panels.len().get() {
358 self.close_panel(0);
359 None
360 } else {
361 Some(self.active_panel_idx + 1)
362 }
363 };
364 if let Some(idx) = new_active_panel_idx {
365 if is_input_invocation {
366 self.mut_panel().clear_input();
367 }
368 self.active_panel_idx = idx;
369 let app_cmd_context = AppCmdContext {
370 panel_skin,
371 preview_panel: self.preview_panel,
372 stage_panel: self.stage_panel,
373 screen: self.screen,
374 con,
375 };
376 self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
377 }
378 }
379 Internal::toggle_second_tree => {
380 let panels_count = self.panels.len().get();
381 let trees_count = self.panels.iter()
382 .filter(|p| p.state().get_type() == PanelStateType::Tree)
383 .count();
384 if trees_count < 2 {
385 // we open a tree, closing a (non tree) panel if necessary
386 if panels_count >= con.max_panels_count {
387 for i in (0..panels_count).rev() {
388 if self.panels[i].state().get_type() != PanelStateType::Tree {
389 self.close_panel(i);
390 break;
391 }
392 }
393 }
394 if let Some(selected_path) = self.state().selected_path() {
395 let dir = closest_dir(selected_path);
396 if let Ok(Some(new_state)) = BrowserState::new(
397 dir,
398 self.state().tree_options().without_pattern(),
399 self.screen,
400 con,
401 &Dam::unlimited(),
402 ) {
403 if let Err(s) = self.new_panel(
404 Box::new(new_state),
405 PanelPurpose::None,
406 HDir::Right,
407 is_input_invocation,
408 con,
409 ) {
410 error = Some(s);
411 }
412 }
413 }
414 } else {
415 // we close the rightest inactive tree
416 for i in (0..panels_count).rev() {
417 if self.panels[i].state().get_type() == PanelStateType::Tree {
418 if i == self.active_panel_idx {
419 continue;
420 }
421 self.close_panel(i);
422 break;
423 }
424 }
425 }
426 }
427 _ => {
428 info!("unhandled propagated internal. cmd={:?}", &cmd);
429 }
430 }
431 }
432 Keep => {
433 if is_input_invocation {
434 self.mut_panel().clear_input_invocation(con);
435 }
436 }
437 Launch(launchable) => {
438 self.launch_at_end = Some(*launchable);
439 self.quitting = true;
440 }
441 NewPanel {
442 state,
443 purpose,
444 direction,
445 } => {
446 if let Err(s) = self.new_panel(state, purpose, direction, is_input_invocation, con) {
447 error = Some(s);
448 }
449 }
450 NewState(state) => {
451 self.mut_panel().clear_input();
452 self.mut_panel().push_state(state);
453 self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
454 }
455 PopState => {
456 if is_input_invocation {
457 self.mut_panel().clear_input();
458 }
459 if self.remove_state() {
460 self.mut_state().refresh(app_cmd_context.screen, con);
461 self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
462 } else if con.quit_on_last_cancel {
463 self.quitting = true;
464 }
465 }
466 PopStateAndReapply => {
467 if is_input_invocation {
468 self.mut_panel().clear_input();
469 }
470 if self.remove_state() {
471 let app_cmd_context = AppCmdContext {
472 panel_skin,
473 preview_panel: self.preview_panel,
474 stage_panel: self.stage_panel,
475 screen: self.screen,
476 con,
477 };
478 self.mut_panel().apply_command(w, &cmd, app_state, &app_cmd_context)?;
479 } else if con.quit_on_last_cancel {
480 self.quitting = true;
481 }
482 }
483 Quit => {
484 self.quitting = true;
485 }
486 RefreshState { clear_cache } => {
487 if is_input_invocation {
488 self.mut_panel().clear_input_invocation(con);
489 }
490 if clear_cache {
491 clear_caches();
492 }
493 app_state.stage.refresh();
494 for i in 0..self.panels.len().get() {
495 self.panels[i].mut_state().refresh(self.screen, con);
496 }
497 }
498 }
499 if let Some(text) = error {
500 self.mut_panel().set_error(text);
501 }
502
503 app_state.other_panel_path = self.get_other_panel_path();
504 if let Some(path) = self.state().tree_root() {
505 app_state.root = path.to_path_buf();
506 }
507
508
509 if let Some(shared_root) = &mut self.shared_root {
510 if let Ok(mut root) = shared_root.lock() {
511 *root = app_state.root.clone();
512 }
513 }
514
515 self.update_preview(con);
516
517 Ok(())
518 }
519
520 /// update the state of the preview, if there's some
update_preview(&mut self, con: &AppContext)521 fn update_preview(&mut self, con: &AppContext) {
522 let preview_idx = self.preview_panel.and_then(|id| self.panel_id_to_idx(id));
523 if let Some(preview_idx) = preview_idx {
524 if let Some(path) = self.state().selected_path() {
525 let old_path = self.panels[preview_idx].state().selected_path();
526 if Some(path) != old_path && path.is_file() {
527 let path = path.to_path_buf();
528 self.panels[preview_idx].mut_state().set_selected_path(path, con);
529 }
530 }
531 }
532 }
533
534 /// get the index of the panel at x
clicked_panel_index(&self, x: u16, _y: u16) -> usize535 fn clicked_panel_index(&self, x: u16, _y: u16) -> usize {
536 let len = self.panels.len().get();
537 (len * x as usize) / (self.screen.width as usize + 1)
538 }
539
540 /// handle CmdResult::NewPanel
new_panel( &mut self, state: Box<dyn PanelState>, purpose: PanelPurpose, direction: HDir, is_input_invocation: bool, con: &AppContext, ) -> Result<(), String>541 fn new_panel(
542 &mut self,
543 state: Box<dyn PanelState>,
544 purpose: PanelPurpose,
545 direction: HDir,
546 is_input_invocation: bool, // if true we clean the input
547 con: &AppContext,
548 ) -> Result<(), String> {
549 match state.get_type() {
550 PanelStateType::Preview if self.preview_panel.is_some() => {
551 return Err("There can be only one preview panel".to_owned());
552 // todo replace instead ?
553 }
554 PanelStateType::Stage if self.stage_panel.is_some() => {
555 return Err("There can be only one stage panel".to_owned());
556 // todo replace instead ?
557 }
558 _ => {}
559 }
560 if is_input_invocation {
561 self.mut_panel().clear_input_invocation(con);
562 }
563 let insertion_idx = if purpose.is_preview() {
564 self.panels.len().get()
565 } else if direction == HDir::Right {
566 self.active_panel_idx + 1
567 } else {
568 self.active_panel_idx
569 };
570 let with_preview = purpose.is_preview() || self.preview_panel.is_some();
571 match Areas::create(
572 self.panels.as_mut_slice(),
573 insertion_idx,
574 self.screen,
575 with_preview,
576 ) {
577 Ok(areas) => {
578 let panel_id = self.created_panels_count.into();
579 match state.get_type() {
580 PanelStateType::Preview => {
581 self.preview_panel = Some(panel_id);
582 }
583 PanelStateType::Stage => {
584 self.stage_panel = Some(panel_id);
585 }
586 _ => {
587 self.active_panel_idx = insertion_idx;
588 }
589 }
590 let mut panel = Panel::new(panel_id, state, areas, con);
591 panel.purpose = purpose;
592 self.created_panels_count += 1;
593 self.panels.insert(insertion_idx, panel);
594 Ok(())
595 }
596 Err(e) => Err(e.to_string())
597 }
598 }
599
600 /// do the pending tasks, if any, and refresh the screen accordingly
do_pending_tasks( &mut self, w: &mut W, skin: &AppSkin, dam: &mut Dam, app_state: &mut AppState, con: &AppContext, ) -> Result<(), ProgramError>601 fn do_pending_tasks(
602 &mut self,
603 w: &mut W,
604 skin: &AppSkin,
605 dam: &mut Dam,
606 app_state: &mut AppState,
607 con: &AppContext,
608 ) -> Result<(), ProgramError> {
609 while self.has_pending_task() && !dam.has_event() {
610 if self.do_pending_task(app_state, con, dam) {
611 self.update_preview(con); // the selection may have changed
612 let app_cmd_context = AppCmdContext {
613 panel_skin: &skin.focused,
614 preview_panel: self.preview_panel,
615 stage_panel: self.stage_panel,
616 screen: self.screen,
617 con,
618 };
619 self.mut_panel().refresh_input_status(app_state, &app_cmd_context);
620 self.display_panels(w, skin, app_state, con)?;
621 } else {
622 warn!("unexpected lack of update on do_pending_task");
623 return Ok(());
624 }
625 }
626 Ok(())
627 }
628
629 /// do the next pending task
do_pending_task( &mut self, app_state: &AppState, con: &AppContext, dam: &mut Dam, ) -> bool630 fn do_pending_task(
631 &mut self,
632 app_state: &AppState,
633 con: &AppContext,
634 dam: &mut Dam,
635 ) -> bool {
636 let screen = self.screen;
637 // we start with the focused panel
638 if self.panel().has_pending_task() {
639 self.mut_panel().do_pending_task(&app_state.stage, screen, con, dam);
640 return true;
641 }
642 // then the other ones
643 for idx in 0..self.panels.len().get() {
644 if idx != self.active_panel_idx {
645 let panel = &mut self.panels[idx];
646 if panel.has_pending_task() {
647 panel.do_pending_task(&app_state.stage, screen, con, dam);
648 return true;
649 }
650 }
651 }
652 false
653 }
654
has_pending_task(&mut self) -> bool655 fn has_pending_task(&mut self) -> bool {
656 self.panels.iter().any(|p| p.has_pending_task())
657 }
658
659 /// This is the main loop of the application
run( mut self, w: &mut W, con: &AppContext, conf: &Conf, ) -> Result<Option<Launchable>, ProgramError>660 pub fn run(
661 mut self,
662 w: &mut W,
663 con: &AppContext,
664 conf: &Conf,
665 ) -> Result<Option<Launchable>, ProgramError> {
666 #[cfg(feature = "clipboard")]
667 {
668 // different systems have different clipboard capabilities
669 // and it may be useful to know which one we have
670 debug!("Clipboard backend: {:?}", terminal_clipboard::get_type());
671 }
672
673 // we listen for events in a separate thread so that we can go on listening
674 // when a long search is running, and interrupt it if needed
675 let event_source = EventSource::new()?;
676 let rx_events = event_source.receiver();
677 let mut dam = Dam::from(rx_events);
678 let skin = AppSkin::new(conf, con.launch_args.color == Some(false));
679 let mut app_state = AppState {
680 stage: Stage::default(),
681 root: con.launch_args.root.clone(),
682 other_panel_path: None,
683 };
684
685 self.screen.clear_bottom_right_char(w, &skin.focused)?;
686
687 if let Some(raw_sequence) = &con.launch_args.commands {
688 self.tx_seqs
689 .send(Sequence::new_local(raw_sequence.to_string()))
690 .unwrap();
691 }
692
693 #[cfg(unix)]
694 let _server = con.launch_args.listen.as_ref()
695 .map(|server_name| {
696 let shared_root = Arc::new(Mutex::new(app_state.root.clone()));
697 let server = crate::net::Server::new(
698 server_name,
699 self.tx_seqs.clone(),
700 Arc::clone(&shared_root),
701 );
702 self.shared_root = Some(shared_root);
703 server
704 })
705 .transpose()?;
706
707 loop {
708 if !self.quitting {
709 self.display_panels(w, &skin, &app_state, con)?;
710 time!(
711 Info,
712 "pending_tasks",
713 self.do_pending_tasks(w, &skin, &mut dam, &mut app_state, con)?,
714 );
715 }
716 match dam.next(&self.rx_seqs) {
717 Either::First(Some(event)) => {
718 info!("event: {:?}", &event);
719 let mut handled = false;
720
721 // app level handling
722 if let Some((x, y)) = event.as_click() {
723 if self.clicked_panel_index(x, y) != self.active_panel_idx {
724 // panel activation click
725 self.active_panel_idx = self.clicked_panel_index(x, y);
726 handled = true;
727 }
728 } else if let Event::Resize(w, h) = event.event {
729 self.screen.set_terminal_size(w, h, con);
730 Areas::resize_all(
731 self.panels.as_mut_slice(),
732 self.screen,
733 self.preview_panel.is_some(),
734 )?;
735 for panel in &mut self.panels {
736 panel.mut_state().refresh(self.screen, con);
737 }
738 handled = true;
739 }
740
741 // event handled by the panel
742 if !handled {
743 let cmd = self.mut_panel().add_event(w, event, &app_state, con)?;
744 debug!("command after add_event: {:?}", &cmd);
745 self.apply_command(w, cmd, &skin.focused, &mut app_state, con)?;
746
747 }
748
749 event_source.unblock(self.quitting);
750 }
751 Either::First(None) => {
752 // this is how we quit the application,
753 // when the input thread is properly closed
754 break;
755 }
756 Either::Second(Some(raw_sequence)) => {
757 debug!("got command sequence: {:?}", &raw_sequence);
758 for (input, arg_cmd) in raw_sequence.parse(con)? {
759 self.mut_panel().set_input_content(&input);
760 self.apply_command(w, arg_cmd, &skin.focused, &mut app_state, con)?;
761 if self.quitting {
762 // is that a 100% safe way of quitting ?
763 return Ok(self.launch_at_end.take());
764 } else {
765 self.display_panels(w, &skin, &app_state, con)?;
766 time!(
767 "sequence pending tasks",
768 self.do_pending_tasks(w, &skin, &mut dam, &mut app_state, con)?,
769 );
770 }
771 }
772 }
773 Either::Second(None) => {
774 warn!("I didn't expect a None to occur here");
775 }
776 }
777 }
778
779 Ok(self.launch_at_end.take())
780 }
781 }
782
783 /// clear the file sizes and git stats cache.
784 /// This should be done on Refresh actions and after any external
785 /// command.
clear_caches()786 fn clear_caches() {
787 file_sum::clear_cache();
788 git::clear_status_computer_cache();
789 #[cfg(unix)]
790 crate::filesystems::clear_cache();
791 }
792