From b3d6b77752cab81037bbed3c621b63e9748d834b Mon Sep 17 00:00:00 2001 From: kjuulh Date: Fri, 10 May 2024 12:38:58 +0200 Subject: [PATCH] chore: refactor into classic Signed-off-by: kjuulh --- crates/hyperlog-tui/src/components.rs | 248 +----------------- .../src/components/graph_explorer.rs | 244 +++++++++++++++++ .../src/components/render_graph.rs | 137 +--------- .../src/components/render_graph/classic.rs | 140 ++++++++++ 4 files changed, 386 insertions(+), 383 deletions(-) create mode 100644 crates/hyperlog-tui/src/components/graph_explorer.rs create mode 100644 crates/hyperlog-tui/src/components/render_graph/classic.rs diff --git a/crates/hyperlog-tui/src/components.rs b/crates/hyperlog-tui/src/components.rs index 9b987c2..ae156f0 100644 --- a/crates/hyperlog-tui/src/components.rs +++ b/crates/hyperlog-tui/src/components.rs @@ -1,249 +1,3 @@ +pub(crate) mod graph_explorer; pub(crate) mod movement_graph; pub(crate) mod render_graph; - -pub(crate) mod graph_explorer { - use anyhow::Result; - use hyperlog_core::log::GraphItem; - use ratatui::{prelude::*, widgets::*}; - - use crate::{ - command_parser::Commands, components::movement_graph::GraphItemType, models::Msg, - state::SharedState, - }; - - use super::movement_graph::{MovementGraph, MovementGraphItem}; - use super::render_graph::RenderGraph; - - pub struct GraphExplorer<'a> { - state: SharedState, - - pub inner: GraphExplorerState<'a>, - } - - pub struct GraphExplorerState<'a> { - root: String, - - current_path: Option<&'a str>, - current_position: Vec, - - graph: Option, - } - - impl<'a> GraphExplorer<'a> { - pub fn new(root: String, state: SharedState) -> Self { - Self { - state, - inner: GraphExplorerState::<'a> { - root, - current_path: None, - current_position: Vec::new(), - graph: None, - }, - } - } - - pub fn update_graph(&mut self) -> Result<&mut Self> { - let now = std::time::SystemTime::now(); - - let graph = self - .state - .querier - .get( - &self.inner.root, - self.inner - .current_path - .map(|p| p.split('.').collect::>()) - .unwrap_or_default(), - ) - .ok_or(anyhow::anyhow!("graph should've had an item"))?; - - self.inner.graph = Some(graph); - - let elapsed = now.elapsed()?; - tracing::trace!("Graph.update_graph took: {}nanos", elapsed.as_nanos()); - - Ok(self) - } - - fn linearize_graph(&self) -> Option { - self.inner.graph.clone().map(|g| g.into()) - } - - /// Will only incrmeent to the next level - /// - /// Current: 0.1.0 - /// Available: 0.1.0.[0,1,2] - /// Choses: 0.1.0.0 else nothing - pub(crate) fn move_right(&mut self) -> Result<()> { - if let Some(graph) = self.linearize_graph() { - tracing::debug!("graph: {:?}", graph); - let position_items = &self.inner.current_position; - - if let Some(next_item) = graph.next_right(position_items) { - self.inner.current_position.push(next_item.index); - tracing::trace!("found next item: {:?}", self.inner.current_position); - } - } - - Ok(()) - } - - /// Will only incrmeent to the next level - /// - /// Current: 0.1.0 - /// Available: 0.[0,1,2].0 - /// Choses: 0.1 else nothing - pub(crate) fn move_left(&mut self) -> Result<()> { - if let Some(last) = self.inner.current_position.pop() { - tracing::trace!( - "found last item: {:?}, popped: {}", - self.inner.current_position, - last - ); - } - - Ok(()) - } - - /// Will move up if a sibling exists, or up to the most common sibling between sections - /// - /// Current: 0.1.1 - /// Available: 0.[0.[0,1],1.[0,1]] - /// Chose: 0.1.0 again 0.0 We don't choose a subitem in the next three instead we just find the most common sibling - pub(crate) fn move_up(&mut self) -> Result<()> { - if let Some(graph) = self.linearize_graph() { - let position_items = &self.inner.current_position; - - if let Some(next_item) = graph.next_up(position_items) { - self.inner.current_position = next_item; - tracing::trace!("found next up: {:?}", self.inner.current_position) - } - } - - Ok(()) - } - - /// Will move down if a sibling exists, or down to the most common sibling between sections - /// - /// Current: 0.0.0 - /// Available: 0.[0.[0,1],1.[0,1]] - /// Chose: 0.0.1 again 0.1 - pub(crate) fn move_down(&mut self) -> Result<()> { - if let Some(graph) = self.linearize_graph() { - let position_items = &self.inner.current_position; - - if let Some(next_item) = graph.next_down(position_items) { - self.inner.current_position = next_item; - tracing::trace!("found next down: {:?}", self.inner.current_position) - } - } - - Ok(()) - } - - pub(crate) fn get_current_path(&self) -> Vec { - let graph = self.linearize_graph(); - let position_items = &self.inner.current_position; - - if let Some(graph) = graph { - graph.to_current_path(position_items) - } else { - Vec::new() - } - } - - fn get_current_item(&self) -> Option { - let graph = self.linearize_graph(); - - if let Some(graph) = graph { - graph.get_graph_item(&self.inner.current_position).cloned() - } else { - None - } - } - - pub fn execute_command(&mut self, command: &Commands) -> anyhow::Result> { - match command { - Commands::Archive => { - if !self.get_current_path().is_empty() { - tracing::debug!("archiving path: {:?}", self.get_current_path()) - } - } - Commands::CreateSection { name } => { - if !name.is_empty() { - let mut path = self.get_current_path(); - path.push(name.replace(" ", "-").replace(".", "-")); - - self.state.commander.execute( - hyperlog_core::commander::Command::CreateSection { - root: self.inner.root.clone(), - path, - }, - )?; - } - } - Commands::Edit => { - if let Some(item) = self.get_current_item() { - let path = self.get_current_path(); - - tracing::debug!( - "found item to edit: path: {}, item: {}", - path.join("."), - item.name - ); - match item.item_type { - GraphItemType::Section => { - todo!("cannot edit section at the moment") - } - GraphItemType::Item { .. } => { - if let Some(item) = self.state.querier.get(&self.inner.root, path) { - if let GraphItem::Item { .. } = item { - return Ok(Some(Msg::OpenEditItemDialog { item })); - } - } - } - } - } - } - _ => (), - } - - self.update_graph()?; - - Ok(None) - } - - pub(crate) fn interact(&mut self) -> anyhow::Result<()> { - if !self.get_current_path().is_empty() { - tracing::info!("toggling state of items"); - - self.state - .commander - .execute(hyperlog_core::commander::Command::ToggleItem { - root: self.inner.root.to_string(), - path: self.get_current_path(), - })?; - } - - self.update_graph()?; - - Ok(()) - } - } - - impl<'a> StatefulWidget for GraphExplorer<'a> { - type State = GraphExplorerState<'a>; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let Rect { height, .. } = area; - let _height = height as usize; - - if let Some(graph) = &state.graph { - let movement_graph: MovementGraph = graph.clone().into(); - let lines = movement_graph.render_graph(&state.current_position); - let para = Paragraph::new(lines); - para.render(area, buf); - } - } - } -} diff --git a/crates/hyperlog-tui/src/components/graph_explorer.rs b/crates/hyperlog-tui/src/components/graph_explorer.rs new file mode 100644 index 0000000..e9538f8 --- /dev/null +++ b/crates/hyperlog-tui/src/components/graph_explorer.rs @@ -0,0 +1,244 @@ +use anyhow::Result; +use hyperlog_core::log::GraphItem; +use ratatui::{prelude::*, widgets::*}; + +use crate::{ + command_parser::Commands, components::movement_graph::GraphItemType, models::Msg, + state::SharedState, +}; + +use super::movement_graph::{MovementGraph, MovementGraphItem}; +use super::render_graph::RenderGraph; + +pub struct GraphExplorer<'a> { + state: SharedState, + + pub inner: GraphExplorerState<'a>, +} + +pub struct GraphExplorerState<'a> { + root: String, + + current_path: Option<&'a str>, + current_position: Vec, + + graph: Option, +} + +impl<'a> GraphExplorer<'a> { + pub fn new(root: String, state: SharedState) -> Self { + Self { + state, + inner: GraphExplorerState::<'a> { + root, + current_path: None, + current_position: Vec::new(), + graph: None, + }, + } + } + + pub fn update_graph(&mut self) -> Result<&mut Self> { + let now = std::time::SystemTime::now(); + + let graph = self + .state + .querier + .get( + &self.inner.root, + self.inner + .current_path + .map(|p| p.split('.').collect::>()) + .unwrap_or_default(), + ) + .ok_or(anyhow::anyhow!("graph should've had an item"))?; + + self.inner.graph = Some(graph); + + let elapsed = now.elapsed()?; + tracing::trace!("Graph.update_graph took: {}nanos", elapsed.as_nanos()); + + Ok(self) + } + + fn linearize_graph(&self) -> Option { + self.inner.graph.clone().map(|g| g.into()) + } + + /// Will only incrmeent to the next level + /// + /// Current: 0.1.0 + /// Available: 0.1.0.[0,1,2] + /// Choses: 0.1.0.0 else nothing + pub(crate) fn move_right(&mut self) -> Result<()> { + if let Some(graph) = self.linearize_graph() { + tracing::debug!("graph: {:?}", graph); + let position_items = &self.inner.current_position; + + if let Some(next_item) = graph.next_right(position_items) { + self.inner.current_position.push(next_item.index); + tracing::trace!("found next item: {:?}", self.inner.current_position); + } + } + + Ok(()) + } + + /// Will only incrmeent to the next level + /// + /// Current: 0.1.0 + /// Available: 0.[0,1,2].0 + /// Choses: 0.1 else nothing + pub(crate) fn move_left(&mut self) -> Result<()> { + if let Some(last) = self.inner.current_position.pop() { + tracing::trace!( + "found last item: {:?}, popped: {}", + self.inner.current_position, + last + ); + } + + Ok(()) + } + + /// Will move up if a sibling exists, or up to the most common sibling between sections + /// + /// Current: 0.1.1 + /// Available: 0.[0.[0,1],1.[0,1]] + /// Chose: 0.1.0 again 0.0 We don't choose a subitem in the next three instead we just find the most common sibling + pub(crate) fn move_up(&mut self) -> Result<()> { + if let Some(graph) = self.linearize_graph() { + let position_items = &self.inner.current_position; + + if let Some(next_item) = graph.next_up(position_items) { + self.inner.current_position = next_item; + tracing::trace!("found next up: {:?}", self.inner.current_position) + } + } + + Ok(()) + } + + /// Will move down if a sibling exists, or down to the most common sibling between sections + /// + /// Current: 0.0.0 + /// Available: 0.[0.[0,1],1.[0,1]] + /// Chose: 0.0.1 again 0.1 + pub(crate) fn move_down(&mut self) -> Result<()> { + if let Some(graph) = self.linearize_graph() { + let position_items = &self.inner.current_position; + + if let Some(next_item) = graph.next_down(position_items) { + self.inner.current_position = next_item; + tracing::trace!("found next down: {:?}", self.inner.current_position) + } + } + + Ok(()) + } + + pub(crate) fn get_current_path(&self) -> Vec { + let graph = self.linearize_graph(); + let position_items = &self.inner.current_position; + + if let Some(graph) = graph { + graph.to_current_path(position_items) + } else { + Vec::new() + } + } + + fn get_current_item(&self) -> Option { + let graph = self.linearize_graph(); + + if let Some(graph) = graph { + graph.get_graph_item(&self.inner.current_position).cloned() + } else { + None + } + } + + pub fn execute_command(&mut self, command: &Commands) -> anyhow::Result> { + match command { + Commands::Archive => { + if !self.get_current_path().is_empty() { + tracing::debug!("archiving path: {:?}", self.get_current_path()) + } + } + Commands::CreateSection { name } => { + if !name.is_empty() { + let mut path = self.get_current_path(); + path.push(name.replace(" ", "-").replace(".", "-")); + + self.state.commander.execute( + hyperlog_core::commander::Command::CreateSection { + root: self.inner.root.clone(), + path, + }, + )?; + } + } + Commands::Edit => { + if let Some(item) = self.get_current_item() { + let path = self.get_current_path(); + + tracing::debug!( + "found item to edit: path: {}, item: {}", + path.join("."), + item.name + ); + match item.item_type { + GraphItemType::Section => { + todo!("cannot edit section at the moment") + } + GraphItemType::Item { .. } => { + if let Some(item) = self.state.querier.get(&self.inner.root, path) { + if let GraphItem::Item { .. } = item { + return Ok(Some(Msg::OpenEditItemDialog { item })); + } + } + } + } + } + } + _ => (), + } + + self.update_graph()?; + + Ok(None) + } + + pub(crate) fn interact(&mut self) -> anyhow::Result<()> { + if !self.get_current_path().is_empty() { + tracing::info!("toggling state of items"); + + self.state + .commander + .execute(hyperlog_core::commander::Command::ToggleItem { + root: self.inner.root.to_string(), + path: self.get_current_path(), + })?; + } + + self.update_graph()?; + + Ok(()) + } +} + +impl<'a> StatefulWidget for GraphExplorer<'a> { + type State = GraphExplorerState<'a>; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + let Rect { height, .. } = area; + let _height = height as usize; + + if let Some(graph) = &state.graph { + let movement_graph: MovementGraph = graph.clone().into(); + let lines = movement_graph.render_graph(&state.current_position); + let para = Paragraph::new(lines); + para.render(area, buf); + } + } +} diff --git a/crates/hyperlog-tui/src/components/render_graph.rs b/crates/hyperlog-tui/src/components/render_graph.rs index f55793e..1ba8ffb 100644 --- a/crates/hyperlog-tui/src/components/render_graph.rs +++ b/crates/hyperlog-tui/src/components/render_graph.rs @@ -1,143 +1,8 @@ use ratatui::prelude::*; -use super::movement_graph::{GraphItemType, MovementGraph}; - pub trait RenderGraph { fn render_graph(&self, items: &[usize]) -> Vec; fn render_graph_spans(&self, items: &[usize]) -> Vec>; } -impl RenderGraph for MovementGraph { - /// render_graph takes each level of items, renders them, and finally renders a strongly set selector for the current item the user is on - /// This is done from buttom up, and composed via. string padding - fn render_graph(&self, items: &[usize]) -> Vec { - // Gets the inner content of the strings - - let mut lines = Vec::new(); - - for item in &self.items { - let prefix = match item.item_type { - GraphItemType::Section => "- ", - GraphItemType::Item { done } => { - if done { - "- [x]" - } else { - "- [ ]" - } - } - }; - - match items.split_first().map(|(first, rest)| { - if item.index == *first { - (true, rest) - } else { - (false, rest) - } - }) { - Some((true, rest)) => { - if rest.is_empty() { - lines.push( - Line::raw(format!("{} {}", prefix, item.name)) - .style(Style::new().bold().white()), - ); - } else { - lines.push( - Line::raw(format!("{} {}", prefix, item.name)) - .patch_style(Style::new().dark_gray()), - ); - } - - lines.push("".into()); - - let embedded_sections = item.values.render_graph_spans(rest); - for section in &embedded_sections { - let mut line = vec![Span::raw(" ".repeat(4))]; - line.extend_from_slice(section); - lines.push(Line::from(line)); - } - } - _ => { - lines.push( - Line::raw(format!("{} {}", prefix, item.name)) - .patch_style(Style::new().dark_gray()), - ); - - lines.push("".into()); - - let embedded_sections = item.values.render_graph_spans(&[]); - for section in &embedded_sections { - let mut line = vec![Span::raw(" ".repeat(4))]; - line.extend_from_slice(section); - lines.push(Line::from(line)); - } - } - } - } - - lines - } - - fn render_graph_spans(&self, items: &[usize]) -> Vec> { - let mut lines = Vec::new(); - - for item in &self.items { - let prefix = match item.item_type { - GraphItemType::Section => "-", - GraphItemType::Item { done } => { - if done { - "- [x]" - } else { - "- [ ]" - } - } - }; - match items.split_first().map(|(first, rest)| { - if item.index == *first { - (true, rest) - } else { - (false, rest) - } - }) { - Some((true, rest)) => { - let mut line = Vec::new(); - if rest.is_empty() { - line.push( - Span::raw(format!("{} {}", prefix, item.name)) - .style(Style::new().bold().white()), - ); - } else { - line.push( - Span::raw(format!("{} {}", prefix, item.name)) - .patch_style(Style::new().dark_gray()), - ); - } - - lines.push(line); - lines.push(vec!["".into()]); - - let embedded_sections = item.values.render_graph_spans(rest); - for section in &embedded_sections { - let mut line = vec![Span::raw(" ".repeat(4))]; - line.extend_from_slice(section); - lines.push(line); - } - } - _ => { - lines.push(vec![Span::raw(format!("{prefix} {}", item.name)) - .patch_style(Style::new().dark_gray())]); - - lines.push(vec!["".into()]); - - let embedded_sections = item.values.render_graph_spans(&[]); - for section in &embedded_sections { - let mut line = vec![Span::raw(" ".repeat(4))]; - line.extend_from_slice(section); - lines.push(line); - } - } - } - } - - lines - } -} +pub mod classic; diff --git a/crates/hyperlog-tui/src/components/render_graph/classic.rs b/crates/hyperlog-tui/src/components/render_graph/classic.rs new file mode 100644 index 0000000..1832327 --- /dev/null +++ b/crates/hyperlog-tui/src/components/render_graph/classic.rs @@ -0,0 +1,140 @@ +use ratatui::prelude::*; + +use crate::components::movement_graph::{GraphItemType, MovementGraph}; + +use super::RenderGraph; + +impl RenderGraph for MovementGraph { + /// render_graph takes each level of items, renders them, and finally renders a strongly set selector for the current item the user is on + /// This is done from buttom up, and composed via. string padding + fn render_graph(&self, items: &[usize]) -> Vec { + // Gets the inner content of the strings + + let mut lines = Vec::new(); + + for item in &self.items { + let prefix = match item.item_type { + GraphItemType::Section => "- ", + GraphItemType::Item { done } => { + if done { + "- [x]" + } else { + "- [ ]" + } + } + }; + + match items.split_first().map(|(first, rest)| { + if item.index == *first { + (true, rest) + } else { + (false, rest) + } + }) { + Some((true, rest)) => { + if rest.is_empty() { + lines.push( + Line::raw(format!("{} {}", prefix, item.name)) + .style(Style::new().bold().white()), + ); + } else { + lines.push( + Line::raw(format!("{} {}", prefix, item.name)) + .patch_style(Style::new().dark_gray()), + ); + } + + lines.push("".into()); + + let embedded_sections = item.values.render_graph_spans(rest); + for section in &embedded_sections { + let mut line = vec![Span::raw(" ".repeat(4))]; + line.extend_from_slice(section); + lines.push(Line::from(line)); + } + } + _ => { + lines.push( + Line::raw(format!("{} {}", prefix, item.name)) + .patch_style(Style::new().dark_gray()), + ); + + lines.push("".into()); + + let embedded_sections = item.values.render_graph_spans(&[]); + for section in &embedded_sections { + let mut line = vec![Span::raw(" ".repeat(4))]; + line.extend_from_slice(section); + lines.push(Line::from(line)); + } + } + } + } + + lines + } + + fn render_graph_spans(&self, items: &[usize]) -> Vec> { + let mut lines = Vec::new(); + + for item in &self.items { + let prefix = match item.item_type { + GraphItemType::Section => "-", + GraphItemType::Item { done } => { + if done { + "- [x]" + } else { + "- [ ]" + } + } + }; + match items.split_first().map(|(first, rest)| { + if item.index == *first { + (true, rest) + } else { + (false, rest) + } + }) { + Some((true, rest)) => { + let mut line = Vec::new(); + if rest.is_empty() { + line.push( + Span::raw(format!("{} {}", prefix, item.name)) + .style(Style::new().bold().white()), + ); + } else { + line.push( + Span::raw(format!("{} {}", prefix, item.name)) + .patch_style(Style::new().dark_gray()), + ); + } + + lines.push(line); + lines.push(vec!["".into()]); + + let embedded_sections = item.values.render_graph_spans(rest); + for section in &embedded_sections { + let mut line = vec![Span::raw(" ".repeat(4))]; + line.extend_from_slice(section); + lines.push(line); + } + } + _ => { + lines.push(vec![Span::raw(format!("{prefix} {}", item.name)) + .patch_style(Style::new().dark_gray())]); + + lines.push(vec!["".into()]); + + let embedded_sections = item.values.render_graph_spans(&[]); + for section in &embedded_sections { + let mut line = vec![Span::raw(" ".repeat(4))]; + line.extend_from_slice(section); + lines.push(line); + } + } + } + } + + lines + } +}