From 740cb4699df8368e86c02aef4fab5d5ef5f8516f Mon Sep 17 00:00:00 2001 From: aawsome <37850842+aawsome@users.noreply.github.com> Date: Sun, 8 Sep 2024 19:32:44 +0200 Subject: [PATCH] feat(interactive): Allow to view text files (#1216) Adds the possibility to view text files up to 1MiB in the interactive snapshot tree view. Note this is a quick-and-dirty implementation which could be improved better non-text-files and which could be optimized to read files of all sizes --- src/commands/tui/ls.rs | 45 ++++++++++++++++++-- src/commands/tui/widgets.rs | 13 +++++- src/commands/tui/widgets/text_input.rs | 59 ++++++++++++++++++-------- 3 files changed, 96 insertions(+), 21 deletions(-) diff --git a/src/commands/tui/ls.rs b/src/commands/tui/ls.rs index 6123461..2956fa1 100644 --- a/src/commands/tui/ls.rs +++ b/src/commands/tui/ls.rs @@ -5,6 +5,7 @@ use crossterm::event::{Event, KeyCode, KeyEventKind}; use ratatui::{prelude::*, widgets::*}; use rustic_core::{ repofile::{Node, SnapshotFile, Tree}, + vfs::OpenFile, IndexedFull, ProgressBars, Repository, }; use style::palette::tailwind; @@ -14,22 +15,25 @@ use crate::commands::{ tui::{ restore::Restore, widgets::{ - popup_prompt, popup_text, Draw, PopUpPrompt, PopUpText, ProcessEvent, PromptResult, - SelectTable, WithBlock, + popup_prompt, popup_scrollable_text, popup_text, Draw, PopUpPrompt, PopUpText, + ProcessEvent, PromptResult, SelectTable, TextInputResult, WithBlock, }, }, }; +use super::widgets::PopUpInput; + // the states this screen can be in enum CurrentScreen<'a, P, S> { Snapshot, ShowHelp(PopUpText), Restore(Restore<'a, P, S>), PromptExit(PopUpPrompt), + ShowFile(PopUpInput), } const INFO_TEXT: &str = - "(Esc) quit | (Enter) enter dir | (Backspace) return to parent | (r) restore | (?) show all commands"; + "(Esc) quit | (Enter) enter dir | (Backspace) return to parent | (v) view | (r) restore | (?) show all commands"; const HELP_TEXT: &str = r#" General Commands: @@ -37,6 +41,7 @@ General Commands: q,Esc : exit Enter : enter dir Backspace : return to parent dir + v : view file contents (text files only, up to 1MiB) r : restore selected item n : toggle numeric IDs ? : show this help page @@ -204,6 +209,33 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> { CurrentScreen::ShowHelp(popup_text("help", HELP_TEXT.into())); } Char('n') => self.toggle_numeric(), + Char('v') => { + // viewing is not supported on cold repositories + if self.repo.config().is_hot != Some(true) { + if let Some(node) = self.selected_node() { + if node.is_file() { + if let Ok(data) = OpenFile::from_node(self.repo, node).read_at( + self.repo, + 0, + node.meta.size.min(1_000_000).try_into().unwrap(), + ) { + // viewing is only supported for text files + if let Ok(content) = String::from_utf8(data.to_vec()) { + let lines = content.lines().count(); + let path = self.path.join(node.name()); + let path = path.display(); + self.current_screen = + CurrentScreen::ShowFile(popup_scrollable_text( + format!("{}:/{path}", self.snapshot.id), + &content, + (lines + 1).min(40).try_into().unwrap(), + )); + } + } + } + } + } + } Char('r') => { if let Some(node) = self.selected_node() { let is_absolute = self @@ -231,6 +263,12 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> { }, _ => {} }, + CurrentScreen::ShowFile(prompt) => match prompt.input(event) { + TextInputResult::Cancel | TextInputResult::Input(_) => { + self.current_screen = CurrentScreen::Snapshot; + } + TextInputResult::None => {} + }, CurrentScreen::ShowHelp(_) => match event { Event::Key(key) if key.kind == KeyEventKind::Press => { if matches!(key.code, Char('q') | Esc | Enter | Char(' ') | Char('?')) { @@ -276,6 +314,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> { CurrentScreen::Snapshot | CurrentScreen::Restore(_) => {} CurrentScreen::ShowHelp(popup) => popup.draw(area, f), CurrentScreen::PromptExit(popup) => popup.draw(area, f), + CurrentScreen::ShowFile(popup) => popup.draw(area, f), } } } diff --git a/src/commands/tui/widgets.rs b/src/commands/tui/widgets.rs index f15e43b..e9fde21 100644 --- a/src/commands/tui/widgets.rs +++ b/src/commands/tui/widgets.rs @@ -49,7 +49,18 @@ pub fn popup_input( lines: u16, ) -> PopUpInput { PopUp(WithBlock::new( - TextInput::new(text, initial, lines), + TextInput::new(Some(text), initial, lines, true), + Block::bordered().title(title), + )) +} + +pub fn popup_scrollable_text( + title: impl Into