2024-05-09 00:15:42 +02:00
|
|
|
#![feature(fn_traits)]
|
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
use std::{io::Stdout, time::Duration};
|
2024-05-01 22:17:39 +02:00
|
|
|
|
|
|
|
use anyhow::{Context, Result};
|
2024-05-02 23:47:47 +02:00
|
|
|
use app::{render_app, App};
|
2024-05-09 00:15:42 +02:00
|
|
|
use commands::IntoCommand;
|
2024-05-02 23:47:47 +02:00
|
|
|
use components::GraphExplorer;
|
|
|
|
use crossterm::event::{self, Event, KeyCode};
|
|
|
|
use hyperlog_core::state::State;
|
2024-05-07 23:21:13 +02:00
|
|
|
use models::{EditMsg, Msg};
|
2024-05-02 23:47:47 +02:00
|
|
|
use ratatui::{backend::CrosstermBackend, Terminal};
|
2024-05-01 22:17:39 +02:00
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
use crate::{state::SharedState, terminal::TerminalInstance};
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
pub mod models;
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
pub(crate) mod app;
|
2024-05-09 14:10:43 +02:00
|
|
|
pub(crate) mod command_parser;
|
2024-05-09 00:15:42 +02:00
|
|
|
pub(crate) mod commands;
|
2024-05-02 23:47:47 +02:00
|
|
|
pub(crate) mod components;
|
|
|
|
pub(crate) mod state;
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
mod logging;
|
|
|
|
mod terminal;
|
2024-05-01 23:26:25 +02:00
|
|
|
|
|
|
|
pub async fn execute(state: State) -> Result<()> {
|
2024-05-01 22:17:39 +02:00
|
|
|
tracing::debug!("starting hyperlog tui");
|
|
|
|
|
|
|
|
logging::initialize_panic_handler()?;
|
|
|
|
logging::initialize_logging()?;
|
|
|
|
|
2024-05-01 23:26:25 +02:00
|
|
|
let state = SharedState::from(state);
|
|
|
|
|
2024-05-01 22:17:39 +02:00
|
|
|
let mut terminal = TerminalInstance::new()?;
|
|
|
|
run(&mut terminal, state).context("app loop failed")?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-05-01 23:26:25 +02:00
|
|
|
fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) -> Result<()> {
|
|
|
|
let mut graph_explorer = GraphExplorer::new(state.clone());
|
|
|
|
graph_explorer.update_graph()?;
|
|
|
|
|
2024-05-09 14:10:43 +02:00
|
|
|
let mut app = App::new("kjuulh", state.clone(), graph_explorer);
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-05-01 22:17:39 +02:00
|
|
|
loop {
|
2024-05-02 23:47:47 +02:00
|
|
|
terminal.draw(|f| render_app(f, &mut app))?;
|
|
|
|
|
|
|
|
if update(terminal, &mut app)?.should_quit() {
|
2024-05-01 22:17:39 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
pub struct UpdateConclusion(bool);
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
impl UpdateConclusion {
|
|
|
|
pub fn new(should_quit: bool) -> Self {
|
|
|
|
Self(should_quit)
|
2024-05-01 23:26:25 +02:00
|
|
|
}
|
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
pub fn should_quit(self) -> bool {
|
|
|
|
self.0
|
2024-05-01 23:26:25 +02:00
|
|
|
}
|
2024-05-01 22:17:39 +02:00
|
|
|
}
|
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
fn update(
|
|
|
|
_terminal: &mut Terminal<CrosstermBackend<Stdout>>,
|
|
|
|
app: &mut App,
|
|
|
|
) -> Result<UpdateConclusion> {
|
2024-05-01 22:17:39 +02:00
|
|
|
if event::poll(Duration::from_millis(250)).context("event poll failed")? {
|
|
|
|
if let Event::Key(key) = event::read().context("event read failed")? {
|
2024-05-09 00:15:42 +02:00
|
|
|
let mut cmd = match &app.mode {
|
2024-05-07 23:21:13 +02:00
|
|
|
app::Mode::View => match key.code {
|
2024-05-09 15:26:58 +02:00
|
|
|
KeyCode::Enter => app.update(Msg::Interact)?,
|
2024-05-07 23:21:13 +02:00
|
|
|
KeyCode::Char('q') => return Ok(UpdateConclusion::new(true)),
|
2024-05-09 00:15:42 +02:00
|
|
|
KeyCode::Char('l') => app.update(Msg::MoveRight)?,
|
|
|
|
KeyCode::Char('h') => app.update(Msg::MoveLeft)?,
|
|
|
|
KeyCode::Char('j') => app.update(Msg::MoveDown)?,
|
|
|
|
KeyCode::Char('k') => app.update(Msg::MoveUp)?,
|
2024-05-07 23:21:13 +02:00
|
|
|
KeyCode::Char('a') => {
|
2024-05-09 00:15:42 +02:00
|
|
|
// TODO: batch commands
|
2024-05-07 23:21:13 +02:00
|
|
|
app.update(Msg::OpenCreateItemDialog)?;
|
2024-05-09 00:15:42 +02:00
|
|
|
app.update(Msg::EnterInsertMode)?
|
2024-05-07 23:21:13 +02:00
|
|
|
}
|
2024-05-09 00:15:42 +02:00
|
|
|
KeyCode::Char('i') => app.update(Msg::EnterInsertMode)?,
|
|
|
|
KeyCode::Char(':') => app.update(Msg::EnterCommandMode)?,
|
|
|
|
_ => return Ok(UpdateConclusion(false)),
|
2024-05-07 23:21:13 +02:00
|
|
|
},
|
|
|
|
|
2024-05-09 00:15:42 +02:00
|
|
|
app::Mode::Command | app::Mode::Insert => match key.code {
|
2024-05-07 23:21:13 +02:00
|
|
|
KeyCode::Backspace => app.update(Msg::Edit(EditMsg::Delete))?,
|
|
|
|
KeyCode::Enter => app.update(Msg::Edit(EditMsg::InsertNewLine))?,
|
|
|
|
KeyCode::Tab => app.update(Msg::Edit(EditMsg::InsertTab))?,
|
|
|
|
KeyCode::Delete => app.update(Msg::Edit(EditMsg::DeleteNext))?,
|
|
|
|
KeyCode::Char(c) => app.update(Msg::Edit(EditMsg::InsertChar(c)))?,
|
|
|
|
KeyCode::Left => app.update(Msg::Edit(EditMsg::MoveLeft))?,
|
|
|
|
KeyCode::Right => app.update(Msg::Edit(EditMsg::MoveRight))?,
|
2024-05-09 00:15:42 +02:00
|
|
|
KeyCode::Esc => app.update(Msg::EnterViewMode)?,
|
|
|
|
_ => return Ok(UpdateConclusion(false)),
|
2024-05-07 23:21:13 +02:00
|
|
|
},
|
2024-05-09 00:15:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let msg = cmd.into_command().execute();
|
|
|
|
match msg {
|
|
|
|
Some(msg) => {
|
|
|
|
cmd = app.update(msg)?;
|
|
|
|
}
|
|
|
|
None => break,
|
|
|
|
}
|
2024-05-02 23:47:47 +02:00
|
|
|
}
|
2024-05-01 22:17:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-02 23:47:47 +02:00
|
|
|
Ok(UpdateConclusion::new(false))
|
2024-05-01 22:17:39 +02:00
|
|
|
}
|