diff options
Diffstat (limited to 'src/components/revision_files.rs')
-rw-r--r-- | src/components/revision_files.rs | 548 |
1 files changed, 274 insertions, 274 deletions
diff --git a/src/components/revision_files.rs b/src/components/revision_files.rs index 7a3ba043..b06538cc 100644 --- a/src/components/revision_files.rs +++ b/src/components/revision_files.rs @@ -1,30 +1,30 @@ use super::{ - utils::scroll_vertical::VerticalScroll, CommandBlocking, - CommandInfo, Component, DrawableComponent, EventState, - SyntaxTextComponent, + utils::scroll_vertical::VerticalScroll, CommandBlocking, + CommandInfo, Component, DrawableComponent, EventState, + SyntaxTextComponent, }; use crate::{ - keys::SharedKeyConfig, - queue::{InternalEvent, Queue}, - strings::{self, order}, - ui::{self, common_nav, style::SharedTheme}, - AsyncAppNotification, AsyncNotification, + keys::SharedKeyConfig, + queue::{InternalEvent, Queue}, + strings::{self, order}, + ui::{self, common_nav, style::SharedTheme}, + AsyncAppNotification, AsyncNotification, }; use anyhow::Result; use asyncgit::{ - sync::{self, CommitId, TreeFile}, - CWD, + sync::{self, CommitId, TreeFile}, + CWD, }; use crossbeam_channel::Sender; use crossterm::event::Event; use filetreelist::{FileTree, FileTreeItem}; use std::{collections::BTreeSet, convert::From, path::Path}; use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - text::Span, - widgets::{Block, Borders}, - Frame, + backend::Backend, + layout::{Constraint, Direction, Layout, Rect}, + text::Span, + widgets::{Block, Borders}, + Frame, }; const FOLDER_ICON_COLLAPSED: &str = "\u{25b8}"; //▸ @@ -32,306 +32,306 @@ const FOLDER_ICON_EXPANDED: &str = "\u{25be}"; //▾ const EMPTY_STR: &str = ""; enum Focus { - Tree, - File, + Tree, + File, } pub struct RevisionFilesComponent { - queue: Queue, - theme: SharedTheme, - //TODO: store TreeFiles in `tree` - files: Vec<TreeFile>, - current_file: SyntaxTextComponent, - tree: FileTree, - scroll: VerticalScroll, - revision: Option<CommitId>, - focus: Focus, - key_config: SharedKeyConfig, + queue: Queue, + theme: SharedTheme, + //TODO: store TreeFiles in `tree` + files: Vec<TreeFile>, + current_file: SyntaxTextComponent, + tree: FileTree, + scroll: VerticalScroll, + revision: Option<CommitId>, + focus: Focus, + key_config: SharedKeyConfig, } impl RevisionFilesComponent { - /// - pub fn new( - queue: &Queue, - sender: &Sender<AsyncAppNotification>, - theme: SharedTheme, - key_config: SharedKeyConfig, - ) -> Self { - Self { - queue: queue.clone(), - tree: FileTree::default(), - scroll: VerticalScroll::new(), - current_file: SyntaxTextComponent::new( - sender, - key_config.clone(), - theme.clone(), - ), - theme, - files: Vec::new(), - revision: None, - focus: Focus::Tree, - key_config, - } - } + /// + pub fn new( + queue: &Queue, + sender: &Sender<AsyncAppNotification>, + theme: SharedTheme, + key_config: SharedKeyConfig, + ) -> Self { + Self { + queue: queue.clone(), + tree: FileTree::default(), + scroll: VerticalScroll::new(), + current_file: SyntaxTextComponent::new( + sender, + key_config.clone(), + theme.clone(), + ), + theme, + files: Vec::new(), + revision: None, + focus: Focus::Tree, + key_config, + } + } - /// - pub fn set_commit(&mut self, commit: CommitId) -> Result<()> { - let same_id = - self.revision.map(|c| c == commit).unwrap_or_default(); - if !same_id { - self.files = sync::tree_files(CWD, commit)?; - let filenames: Vec<&Path> = - self.files.iter().map(|f| f.path.as_path()).collect(); - self.tree = FileTree::new(&filenames, &BTreeSet::new())?; - self.tree.collapse_but_root(); - self.revision = Some(commit); - } + /// + pub fn set_commit(&mut self, commit: CommitId) -> Result<()> { + let same_id = + self.revision.map(|c| c == commit).unwrap_or_default(); + if !same_id { + self.files = sync::tree_files(CWD, commit)?; + let filenames: Vec<&Path> = + self.files.iter().map(|f| f.path.as_path()).collect(); + self.tree = FileTree::new(&filenames, &BTreeSet::new())?; + self.tree.collapse_but_root(); + self.revision = Some(commit); + } - Ok(()) - } + Ok(()) + } - /// - pub fn update(&mut self, ev: AsyncNotification) { - self.current_file.update(ev); - } + /// + pub fn update(&mut self, ev: AsyncNotification) { + self.current_file.update(ev); + } - /// - pub fn any_work_pending(&self) -> bool { - self.current_file.any_work_pending() - } + /// + pub fn any_work_pending(&self) -> bool { + self.current_file.any_work_pending() + } - fn tree_item_to_span<'a>( - item: &'a FileTreeItem, - theme: &SharedTheme, - selected: bool, - ) -> Span<'a> { - let path = item.info().path_str(); - let indent = item.info().indent(); + fn tree_item_to_span<'a>( + item: &'a FileTreeItem, + theme: &SharedTheme, + selected: bool, + ) -> Span<'a> { + let path = item.info().path_str(); + let indent = item.info().indent(); - let indent_str = if indent == 0 { - String::from("") - } else { - format!("{:w$}", " ", w = (indent as usize) * 2) - }; + let indent_str = if indent == 0 { + String::from("") + } else { + format!("{:w$}", " ", w = (indent as usize) * 2) + }; - let is_path = item.kind().is_path(); - let path_arrow = if is_path { - if item.kind().is_path_collapsed() { - FOLDER_ICON_COLLAPSED - } else { - FOLDER_ICON_EXPANDED - } - } else { - EMPTY_STR - }; + let is_path = item.kind().is_path(); + let path_arrow = if is_path { + if item.kind().is_path_collapsed() { + FOLDER_ICON_COLLAPSED + } else { + FOLDER_ICON_EXPANDED + } + } else { + EMPTY_STR + }; - let path = format!("{}{}{}", indent_str, path_arrow, path); - Span::styled(path, theme.file_tree_item(is_path, selected)) - } + let path = format!("{}{}{}", indent_str, path_arrow, path); + Span::styled(path, theme.file_tree_item(is_path, selected)) + } - fn blame(&self) -> bool { - self.tree.selected_file().map_or(false, |file| { - self.queue.push(InternalEvent::BlameFile( - file.full_path_str() - .strip_prefix("./") - .unwrap_or_default() - .to_string(), - )); - true - }) - } + fn blame(&self) -> bool { + self.tree.selected_file().map_or(false, |file| { + self.queue.push(InternalEvent::BlameFile( + file.full_path_str() + .strip_prefix("./") + .unwrap_or_default() + .to_string(), + )); + true + }) + } - fn selection_changed(&mut self) { - //TODO: retrieve TreeFile from tree datastructure - if let Some(file) = self - .tree - .selected_file() - .map(|file| file.full_path_str().to_string()) - { - let path = Path::new(&file); - if let Some(item) = - self.files.iter().find(|f| f.path == path) - { - if let Ok(path) = path.strip_prefix("./") { - return self.current_file.load_file( - path.to_string_lossy().to_string(), - item, - ); - } - } - self.current_file.clear(); - } - } + fn selection_changed(&mut self) { + //TODO: retrieve TreeFile from tree datastructure + if let Some(file) = self + .tree + .selected_file() + .map(|file| file.full_path_str().to_string()) + { + let path = Path::new(&file); + if let Some(item) = + self.files.iter().find(|f| f.path == path) + { + if let Ok(path) = path.strip_prefix("./") { + return self.current_file.load_file( + path.to_string_lossy().to_string(), + item, + ); + } + } + self.current_file.clear(); + } + } - fn draw_tree<B: Backend>(&self, f: &mut Frame<B>, area: Rect) { - let tree_height = usize::from(area.height.saturating_sub(2)); + fn draw_tree<B: Backend>(&self, f: &mut Frame<B>, area: Rect) { + let tree_height = usize::from(area.height.saturating_sub(2)); - self.tree.visual_selection().map_or_else( - || { - self.scroll.reset(); - }, - |selection| { - self.scroll.update( - selection.index, - selection.count, - tree_height, - ); - }, - ); + self.tree.visual_selection().map_or_else( + || { + self.scroll.reset(); + }, + |selection| { + self.scroll.update( + selection.index, + selection.count, + tree_height, + ); + }, + ); - let items = self - .tree - .iterate(self.scroll.get_top(), tree_height) - .map(|(item, selected)| { - Self::tree_item_to_span(item, &self.theme, selected) - }); + let items = self + .tree + .iterate(self.scroll.get_top(), tree_height) + .map(|(item, selected)| { + Self::tree_item_to_span(item, &self.theme, selected) + }); - let is_tree_focused = matches!(self.focus, Focus::Tree); + let is_tree_focused = matches!(self.focus, Focus::Tree); - let title = format!( - "Files at [{}]", - self.revision - .map(|c| c.get_short_string()) - .unwrap_or_default() - ); - ui::draw_list_block( - f, - area, - Block::default() - .title(Span::styled( - title, - self.theme.title(is_tree_focused), - )) - .borders(Borders::ALL) - .border_style(self.theme.block(is_tree_focused)), - items, - ); + let title = format!( + "Files at [{}]", + self.revision + .map(|c| c.get_short_string()) + .unwrap_or_default() + ); + ui::draw_list_block( + f, + area, + Block::default() + .title(Span::styled( + title, + self.theme.title(is_tree_focused), + )) + .borders(Borders::ALL) + .border_style(self.theme.block(is_tree_focused)), + items, + ); - if is_tree_focused { - self.scroll.draw(f, area, &self.theme); - } - } + if is_tree_focused { + self.scroll.draw(f, area, &self.theme); + } + } } impl DrawableComponent for RevisionFilesComponent { - fn draw<B: Backend>( - &self, - f: &mut Frame<B>, - area: Rect, - ) -> Result<()> { - if self.is_visible() { - let chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints( - [ - Constraint::Percentage(40), - Constraint::Percentage(60), - ] - .as_ref(), - ) - .split(area); + fn draw<B: Backend>( + &self, + f: &mut Frame<B>, + area: Rect, + ) -> Result<()> { + if self.is_visible() { + let chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints( + [ + Constraint::Percentage(40), + Constraint::Percentage(60), + ] + .as_ref(), + ) + .split(area); - self.draw_tree(f, chunks[0]); + self.draw_tree(f, chunks[0]); - self.current_file.draw(f, chunks[1])?; - } - Ok(()) - } + self.current_file.draw(f, chunks[1])?; + } + Ok(()) + } } impl Component for RevisionFilesComponent { - fn commands( - &self, - out: &mut Vec<CommandInfo>, - force_all: bool, - ) -> CommandBlocking { - if matches!(self.focus, Focus::Tree) || force_all { - out.push( - CommandInfo::new( - strings::commands::blame_file(&self.key_config), - self.tree.selected_file().is_some(), - true, - ) - .order(order::NAV), - ); - tree_nav_cmds(&self.tree, &self.key_config, out); - } else { - self.current_file.commands(out, force_all); - } + fn commands( + &self, + out: &mut Vec<CommandInfo>, + force_all: bool, + ) -> CommandBlocking { + if matches!(self.focus, Focus::Tree) || force_all { + out.push( + CommandInfo::new( + strings::commands::blame_file(&self.key_config), + self.tree.selected_file().is_some(), + true, + ) + .order(order::NAV), + ); + tree_nav_cmds(&self.tree, &self.key_config, out); + } else { + self.current_file.commands(out, force_all); + } - CommandBlocking::PassingOn - } + CommandBlocking::PassingOn + } - fn event( - &mut self, - event: crossterm::event::Event, - ) -> Result<EventState> { - if let Event::Key(key) = event { - let is_tree_focused = matches!(self.focus, Focus::Tree); - if is_tree_focused - && tree_nav(&mut self.tree, &self.key_config, key) - { - self.selection_changed(); - return Ok(EventState::Consumed); - } else if key == self.key_config.blame { - if self.blame() { - self.hide(); - return Ok(EventState::Consumed); - } - } else if key == self.key_config.move_right { - if is_tree_focused { - self.focus = Focus::File; - self.current_file.focus(true); - self.focus(true); - return Ok(EventState::Consumed); - } - } else if key == self.key_config.move_left { - if !is_tree_focused { - self.focus = Focus::Tree; - self.current_file.focus(false); - self.focus(false); - return Ok(EventState::Consumed); - } - } else if !is_tree_focused { - return self.current_file.event(event); - } - } + fn event( + &mut self, + event: crossterm::event::Event, + ) -> Result<EventState> { + if let Event::Key(key) = event { + let is_tree_focused = matches!(self.focus, Focus::Tree); + if is_tree_focused + && tree_nav(&mut self.tree, &self.key_config, key) + { + self.selection_changed(); + return Ok(EventState::Consumed); + } else if key == self.key_config.blame { + if self.blame() { + self.hide(); + return Ok(EventState::Consumed); + } + } else if key == self.key_config.move_right { + if is_tree_focused { + self.focus = Focus::File; + self.current_file.focus(true); + self.focus(true); + return Ok(EventState::Consumed); + } + } else if key == self.key_config.move_left { + if !is_tree_focused { + self.focus = Focus::Tree; + self.current_file.focus(false); + self.focus(false); + return Ok(EventState::Consumed); + } + } else if !is_tree_focused { + return self.current_file.event(event); + } + } - Ok(EventState::NotConsumed) - } + Ok(EventState::NotConsumed) + } } //TODO: reuse for other tree usages fn tree_nav_cmds( - tree: &FileTree, - key_config: &SharedKeyConfig, - out: &mut Vec<CommandInfo>, + tree: &FileTree, + key_config: &SharedKeyConfig, + out: &mut Vec<CommandInfo>, ) { - out.push( - CommandInfo::new( - strings::commands::navigate_tree(key_config), - !tree.is_empty(), - true, - ) - .order(order::NAV), - ); + out.push( + CommandInfo::new( + strings::commands::navigate_tree(key_config), + !tree.is_empty(), + true, + ) + .order(order::NAV), + ); } //TODO: reuse for other tree usages fn tree_nav( - tree: &mut FileTree, - key_config: &SharedKeyConfig, - key: crossterm::event::KeyEvent, + tree: &mut FileTree, + key_config: &SharedKeyConfig, + key: crossterm::event::KeyEvent, ) -> bool { - if let Some(common_nav) = common_nav(key, key_config) { - tree.move_selection(common_nav) - } else if key == key_config.tree_collapse_recursive { - tree.collapse_recursive(); - true - } else if key == key_config.tree_expand_recursive { - tree.expand_recursive(); - true - } else { - false - } + if let Some(common_nav) = common_nav(key, key_config) { + tree.move_selection(common_nav) + } else if key == key_config.tree_collapse_recursive { + tree.collapse_recursive(); + true + } else if key == key_config.tree_expand_recursive { + tree.expand_recursive(); + true + } else { + false + } } |