2024-05-11 23:23:00 +02:00
|
|
|
#![feature(map_try_insert)]
|
2024-05-09 00:15:42 +02:00
|
|
|
#![feature(fn_traits)]
|
2024-05-12 14:38:03 +02:00
|
|
|
#![feature(let_chains)]
|
2024-05-09 00:15:42 +02:00
|
|
|
|
2024-05-12 12:58:54 +02:00
|
|
|
use std::io::Stdout;
|
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-12 12:58:54 +02:00
|
|
|
use commands::{Dispatch, IntoCommand, Receiver};
|
2024-05-10 12:20:43 +02:00
|
|
|
use components::graph_explorer::GraphExplorer;
|
2024-05-11 23:23:00 +02:00
|
|
|
use core_state::State;
|
2024-05-12 12:58:54 +02:00
|
|
|
use crossterm::event::{Event, KeyCode, KeyEventKind};
|
|
|
|
use futures::{FutureExt, StreamExt};
|
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;
|
2024-05-11 23:23:00 +02:00
|
|
|
|
|
|
|
pub mod commander;
|
|
|
|
pub mod core_state;
|
|
|
|
pub mod shared_engine;
|
|
|
|
pub mod state;
|
|
|
|
|
|
|
|
mod engine;
|
|
|
|
mod events;
|
|
|
|
mod querier;
|
|
|
|
mod storage;
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-06-30 17:31:25 +02:00
|
|
|
mod editor;
|
2024-05-02 23:47:47 +02:00
|
|
|
mod logging;
|
2024-06-30 17:31:25 +02:00
|
|
|
mod project_dirs;
|
2024-05-02 23:47:47 +02:00
|
|
|
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()?;
|
2024-05-12 12:58:54 +02:00
|
|
|
run(&mut terminal, state).await.context("app loop failed")?;
|
2024-05-01 22:17:39 +02:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-05-12 12:58:54 +02:00
|
|
|
async fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) -> Result<()> {
|
2024-05-12 21:07:21 +02:00
|
|
|
let root = match state.querier.get_available_roots_async().await? {
|
2024-05-10 22:47:32 +02:00
|
|
|
// TODO: maybe present choose root screen
|
|
|
|
Some(roots) => roots.first().cloned().unwrap(),
|
|
|
|
None => {
|
|
|
|
// TODO: present create root screen
|
|
|
|
anyhow::bail!("no valid root available\nPlease run:\n\n$ hyperlog create-root --name <your-username>");
|
|
|
|
}
|
|
|
|
};
|
2024-05-09 17:02:06 +02:00
|
|
|
|
|
|
|
let mut graph_explorer = GraphExplorer::new(root.clone(), state.clone());
|
2024-05-12 14:29:14 +02:00
|
|
|
graph_explorer.update_graph().await?;
|
2024-05-01 23:26:25 +02:00
|
|
|
|
2024-05-09 17:02:06 +02:00
|
|
|
let mut app = App::new(&root, state.clone(), graph_explorer);
|
2024-05-12 12:58:54 +02:00
|
|
|
let (dispatch, mut receiver) = commands::create_dispatch();
|
|
|
|
let mut event_stream = crossterm::event::EventStream::new();
|
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))?;
|
|
|
|
|
2024-05-12 12:58:54 +02:00
|
|
|
if update(
|
|
|
|
terminal,
|
|
|
|
&mut app,
|
|
|
|
&dispatch,
|
|
|
|
&mut receiver,
|
|
|
|
&mut event_stream,
|
|
|
|
)
|
|
|
|
.await?
|
|
|
|
.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-12 12:58:54 +02:00
|
|
|
async fn update<'a>(
|
2024-05-02 23:47:47 +02:00
|
|
|
_terminal: &mut Terminal<CrosstermBackend<Stdout>>,
|
2024-05-12 12:58:54 +02:00
|
|
|
app: &mut App<'a>,
|
|
|
|
dispatch: &Dispatch,
|
|
|
|
receiver: &mut Receiver,
|
|
|
|
event_stream: &mut crossterm::event::EventStream,
|
2024-05-02 23:47:47 +02:00
|
|
|
) -> Result<UpdateConclusion> {
|
2024-05-12 12:58:54 +02:00
|
|
|
let cross_event = event_stream.next().fuse();
|
|
|
|
|
|
|
|
let mut handle_key_event = |maybe_event| -> anyhow::Result<UpdateConclusion> {
|
|
|
|
match maybe_event {
|
|
|
|
Some(Ok(e)) => {
|
2024-05-12 14:38:03 +02:00
|
|
|
if let Event::Key(key) = e
|
|
|
|
&& key.kind == KeyEventKind::Press
|
|
|
|
{
|
|
|
|
let mut cmd = match &app.mode {
|
|
|
|
app::Mode::View => match key.code {
|
|
|
|
KeyCode::Enter => app.update(Msg::Interact)?,
|
|
|
|
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)?,
|
|
|
|
KeyCode::Char('a') => {
|
|
|
|
// TODO: batch commands
|
|
|
|
app.update(Msg::OpenCreateItemDialog)?;
|
|
|
|
app.update(Msg::EnterInsertMode)?
|
|
|
|
}
|
2024-05-16 10:10:11 +02:00
|
|
|
KeyCode::Char('o') => {
|
|
|
|
// TODO: batch commands
|
|
|
|
app.update(Msg::OpenCreateItemDialogBelow)?;
|
|
|
|
app.update(Msg::EnterInsertMode)?
|
|
|
|
}
|
2024-05-12 14:38:03 +02:00
|
|
|
KeyCode::Char('i') => app.update(Msg::EnterInsertMode)?,
|
|
|
|
KeyCode::Char(':') => app.update(Msg::EnterCommandMode)?,
|
|
|
|
_ => return Ok(UpdateConclusion(false)),
|
|
|
|
},
|
|
|
|
|
|
|
|
app::Mode::Command | app::Mode::Insert => match key.code {
|
|
|
|
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))?,
|
|
|
|
KeyCode::Esc => app.update(Msg::EnterViewMode)?,
|
|
|
|
_ => return Ok(UpdateConclusion(false)),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let msg = cmd.into_command().execute(dispatch.clone());
|
|
|
|
match msg {
|
|
|
|
Some(msg) => {
|
|
|
|
if let Msg::QuitApp = msg {
|
|
|
|
return Ok(UpdateConclusion(true));
|
2024-05-12 12:58:54 +02:00
|
|
|
}
|
2024-05-12 14:38:03 +02:00
|
|
|
|
|
|
|
cmd = app.update(msg)?;
|
2024-05-12 12:58:54 +02:00
|
|
|
}
|
2024-05-12 14:38:03 +02:00
|
|
|
None => break,
|
2024-05-10 12:20:43 +02:00
|
|
|
}
|
2024-05-12 12:58:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(Err(e)) => {
|
|
|
|
tracing::warn!("failed to send event: {}", e);
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(UpdateConclusion(false))
|
|
|
|
};
|
2024-05-10 12:20:43 +02:00
|
|
|
|
2024-05-12 12:58:54 +02:00
|
|
|
tokio::select! {
|
|
|
|
maybe_event = cross_event => {
|
|
|
|
let conclusion = handle_key_event(maybe_event)?;
|
|
|
|
|
|
|
|
return Ok(conclusion)
|
|
|
|
},
|
|
|
|
|
|
|
|
msg = receiver.next() => {
|
|
|
|
if let Some(msg) = msg {
|
|
|
|
if let Msg::QuitApp = msg {
|
|
|
|
return Ok(UpdateConclusion(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut cmd = app.update(msg)?;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let msg = cmd.into_command().execute(dispatch.clone());
|
|
|
|
match msg {
|
|
|
|
Some(msg) => {
|
|
|
|
if let Msg::QuitApp = msg {
|
|
|
|
return Ok(UpdateConclusion(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = app.update(msg)?;
|
|
|
|
}
|
|
|
|
None => break,
|
2024-05-09 00:15:42 +02:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
}
|