chore: refactor into classic
All checks were successful
continuous-integration/drone/push Build is passing

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-05-10 12:38:58 +02:00
parent 46cf74da3a
commit b3d6b77752
Signed by: kjuulh
GPG Key ID: 9AA7BC13CE474394
4 changed files with 386 additions and 383 deletions

View File

@ -1,249 +1,3 @@
pub(crate) mod graph_explorer;
pub(crate) mod movement_graph; pub(crate) mod movement_graph;
pub(crate) mod render_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<usize>,
graph: Option<GraphItem>,
}
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::<Vec<_>>())
.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<MovementGraph> {
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<String> {
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<MovementGraphItem> {
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<Option<Msg>> {
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);
}
}
}
}

View File

@ -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<usize>,
graph: Option<GraphItem>,
}
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::<Vec<_>>())
.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<MovementGraph> {
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<String> {
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<MovementGraphItem> {
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<Option<Msg>> {
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);
}
}
}

View File

@ -1,143 +1,8 @@
use ratatui::prelude::*; use ratatui::prelude::*;
use super::movement_graph::{GraphItemType, MovementGraph};
pub trait RenderGraph { pub trait RenderGraph {
fn render_graph(&self, items: &[usize]) -> Vec<Line>; fn render_graph(&self, items: &[usize]) -> Vec<Line>;
fn render_graph_spans(&self, items: &[usize]) -> Vec<Vec<Span>>; fn render_graph_spans(&self, items: &[usize]) -> Vec<Vec<Span>>;
} }
impl RenderGraph for MovementGraph { pub mod classic;
/// 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<Line> {
// 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<Vec<Span>> {
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
}
}

View File

@ -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<Line> {
// 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<Vec<Span>> {
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
}
}