feat: tie dialog to graph

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-05-09 14:10:43 +02:00
parent 7cead58ed3
commit 547d34782c
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
5 changed files with 143 additions and 6 deletions

View File

@ -3,7 +3,10 @@ use ratatui::{
widgets::{Block, Borders, Padding, Paragraph}, widgets::{Block, Borders, Padding, Paragraph},
}; };
use crate::{commands::IntoCommand, components::GraphExplorer, state::SharedState, Msg}; use crate::{
command_parser::CommandParser, commands::IntoCommand, components::GraphExplorer,
state::SharedState, Msg,
};
use self::{ use self::{
command_bar::{CommandBar, CommandBarState}, command_bar::{CommandBar, CommandBarState},
@ -17,6 +20,14 @@ pub enum Dialog {
CreateItem { state: CreateItemState }, CreateItem { state: CreateItemState },
} }
impl Dialog {
pub fn get_command(&self) -> Option<hyperlog_core::commander::Command> {
match self {
Dialog::CreateItem { state } => state.get_command(),
}
}
}
pub enum Mode { pub enum Mode {
View, View,
Insert, Insert,
@ -24,6 +35,8 @@ pub enum Mode {
} }
pub struct App<'a> { pub struct App<'a> {
root: String,
state: SharedState, state: SharedState,
pub mode: Mode, pub mode: Mode,
@ -34,8 +47,13 @@ pub struct App<'a> {
} }
impl<'a> App<'a> { impl<'a> App<'a> {
pub fn new(state: SharedState, graph_explorer: GraphExplorer<'a>) -> Self { pub fn new(
root: impl Into<String>,
state: SharedState,
graph_explorer: GraphExplorer<'a>,
) -> Self {
Self { Self {
root: root.into(),
mode: Mode::View, mode: Mode::View,
dialog: None, dialog: None,
command: None, command: None,
@ -62,8 +80,22 @@ impl<'a> App<'a> {
Msg::SubmitCommand { command } => { Msg::SubmitCommand { command } => {
tracing::info!("submitting command"); tracing::info!("submitting command");
self.command = None; if let Some(command) = CommandParser::parse(&command) {
if command.is_write() {
if let Some(dialog) = &self.dialog {
if let Some(output) = dialog.get_command() {
self.state.commander.execute(output)?;
}
}
}
self.graph_explorer.update_graph()?;
if command.is_quit() {
self.dialog = None;
}
}
self.command = None;
return Ok(Msg::EnterViewMode.into_command()); return Ok(Msg::EnterViewMode.into_command());
} }
_ => {} _ => {}
@ -83,8 +115,11 @@ impl<'a> App<'a> {
fn open_dialog(&mut self) { fn open_dialog(&mut self) {
if self.dialog.is_none() { if self.dialog.is_none() {
let root = self.root.clone();
let path = self.graph_explorer.get_current_path();
self.dialog = Some(Dialog::CreateItem { self.dialog = Some(Dialog::CreateItem {
state: CreateItemState::default(), state: CreateItemState::new(root, path),
}); });
} }
} }

View File

@ -1,3 +1,4 @@
use itertools::Itertools;
use ratatui::{prelude::*, widgets::*}; use ratatui::{prelude::*, widgets::*};
use crate::models::{EditMsg, Msg}; use crate::models::{EditMsg, Msg};
@ -204,8 +205,10 @@ impl Default for CreateItemFocused {
} }
} }
#[derive(Default)]
pub struct CreateItemState { pub struct CreateItemState {
root: String,
path: Vec<String>,
title: InputBuffer, title: InputBuffer,
description: InputBuffer, description: InputBuffer,
@ -213,6 +216,20 @@ pub struct CreateItemState {
} }
impl CreateItemState { impl CreateItemState {
pub fn new(root: impl Into<String>, path: impl IntoIterator<Item = impl Into<String>>) -> Self {
let root = root.into();
let path = path.into_iter().map(|p| p.into()).collect_vec();
Self {
root,
path,
title: Default::default(),
description: Default::default(),
focused: Default::default(),
}
}
pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> { pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> {
match &msg { match &msg {
Msg::MoveDown | Msg::MoveUp => match self.focused { Msg::MoveDown | Msg::MoveUp => match self.focused {
@ -233,6 +250,26 @@ impl CreateItemState {
Ok(()) Ok(())
} }
pub fn get_command(&self) -> Option<hyperlog_core::commander::Command> {
let title = self.title.string();
let description = self.description.string();
if !title.is_empty() {
let mut path = self.path.clone();
path.push(title.replace(' ', "").replace('.', "-"));
Some(hyperlog_core::commander::Command::CreateItem {
root: self.root.clone(),
path,
title: title.trim().into(),
description: description.trim().into(),
state: hyperlog_core::log::ItemState::NotDone,
})
} else {
None
}
}
} }
#[derive(Default)] #[derive(Default)]

View File

@ -0,0 +1,37 @@
use itertools::Itertools;
pub enum Commands {
Write,
Quit,
WriteQuit,
}
impl Commands {
pub fn is_write(&self) -> bool {
matches!(self, Commands::Write | Commands::WriteQuit)
}
pub fn is_quit(&self) -> bool {
matches!(self, Commands::Quit | Commands::WriteQuit)
}
}
pub struct CommandParser {}
impl CommandParser {
pub fn parse(raw_command: &str) -> Option<Commands> {
let prepared = raw_command.trim();
// TODO: respect quotes
let parts = prepared.split_whitespace().collect_vec();
match parts.split_first() {
Some((command, _)) => match *command {
"w" | "write" => Some(Commands::Write),
"q" | "quit" => Some(Commands::Quit),
"wq" | "write-quit" => Some(Commands::WriteQuit),
_ => None,
},
None => None,
}
}
}

View File

@ -126,6 +126,17 @@ impl GraphExplorer<'_> {
Ok(()) 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()
}
}
} }
trait RenderGraph { trait RenderGraph {
@ -332,6 +343,22 @@ impl MovementGraph {
None => Some(self), None => Some(self),
} }
} }
fn to_current_path(&self, position_items: &[usize]) -> Vec<String> {
match position_items.split_first() {
Some((first, rest)) => match self.items.get(*first) {
Some(item) => {
let mut current = vec![item.name.clone()];
let mut next = item.values.to_current_path(rest);
current.append(&mut next);
current
}
None => Vec::new(),
},
None => Vec::new(),
}
}
} }
impl From<Box<GraphItem>> for MovementGraph { impl From<Box<GraphItem>> for MovementGraph {

View File

@ -16,6 +16,7 @@ use crate::{state::SharedState, terminal::TerminalInstance};
pub mod models; pub mod models;
pub(crate) mod app; pub(crate) mod app;
pub(crate) mod command_parser;
pub(crate) mod commands; pub(crate) mod commands;
pub(crate) mod components; pub(crate) mod components;
pub(crate) mod state; pub(crate) mod state;
@ -41,7 +42,7 @@ fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) ->
let mut graph_explorer = GraphExplorer::new(state.clone()); let mut graph_explorer = GraphExplorer::new(state.clone());
graph_explorer.update_graph()?; graph_explorer.update_graph()?;
let mut app = App::new(state.clone(), graph_explorer); let mut app = App::new("kjuulh", state.clone(), graph_explorer);
loop { loop {
terminal.draw(|f| render_app(f, &mut app))?; terminal.draw(|f| render_app(f, &mut app))?;