feat: allow async function in command
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(fn_traits)]
|
||||
|
||||
use std::{io::Stdout, time::Duration};
|
||||
use std::io::Stdout;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use app::{render_app, App};
|
||||
use commands::IntoCommand;
|
||||
use commands::{Dispatch, IntoCommand, Receiver};
|
||||
use components::graph_explorer::GraphExplorer;
|
||||
use core_state::State;
|
||||
use crossterm::event::{self, Event, KeyCode};
|
||||
use crossterm::event::{Event, KeyCode, KeyEventKind};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use models::{EditMsg, Msg};
|
||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
@@ -43,12 +44,12 @@ pub async fn execute(state: State) -> Result<()> {
|
||||
let state = SharedState::from(state);
|
||||
|
||||
let mut terminal = TerminalInstance::new()?;
|
||||
run(&mut terminal, state).context("app loop failed")?;
|
||||
run(&mut terminal, state).await.context("app loop failed")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) -> Result<()> {
|
||||
async fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) -> Result<()> {
|
||||
let root = match state.querier.get_available_roots() {
|
||||
// TODO: maybe present choose root screen
|
||||
Some(roots) => roots.first().cloned().unwrap(),
|
||||
@@ -62,11 +63,22 @@ fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) ->
|
||||
graph_explorer.update_graph()?;
|
||||
|
||||
let mut app = App::new(&root, state.clone(), graph_explorer);
|
||||
let (dispatch, mut receiver) = commands::create_dispatch();
|
||||
let mut event_stream = crossterm::event::EventStream::new();
|
||||
|
||||
loop {
|
||||
terminal.draw(|f| render_app(f, &mut app))?;
|
||||
|
||||
if update(terminal, &mut app)?.should_quit() {
|
||||
if update(
|
||||
terminal,
|
||||
&mut app,
|
||||
&dispatch,
|
||||
&mut receiver,
|
||||
&mut event_stream,
|
||||
)
|
||||
.await?
|
||||
.should_quit()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -85,53 +97,104 @@ impl UpdateConclusion {
|
||||
}
|
||||
}
|
||||
|
||||
fn update(
|
||||
async fn update<'a>(
|
||||
_terminal: &mut Terminal<CrosstermBackend<Stdout>>,
|
||||
app: &mut App,
|
||||
app: &mut App<'a>,
|
||||
dispatch: &Dispatch,
|
||||
receiver: &mut Receiver,
|
||||
event_stream: &mut crossterm::event::EventStream,
|
||||
) -> Result<UpdateConclusion> {
|
||||
if event::poll(Duration::from_millis(250)).context("event poll failed")? {
|
||||
if let Event::Key(key) = event::read().context("event read failed")? {
|
||||
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)?
|
||||
}
|
||||
KeyCode::Char('i') => app.update(Msg::EnterInsertMode)?,
|
||||
KeyCode::Char(':') => app.update(Msg::EnterCommandMode)?,
|
||||
_ => return Ok(UpdateConclusion(false)),
|
||||
},
|
||||
let cross_event = event_stream.next().fuse();
|
||||
|
||||
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)),
|
||||
},
|
||||
};
|
||||
let mut handle_key_event = |maybe_event| -> anyhow::Result<UpdateConclusion> {
|
||||
match maybe_event {
|
||||
Some(Ok(e)) => {
|
||||
if let Event::Key(key) = e {
|
||||
if 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)?
|
||||
}
|
||||
KeyCode::Char('i') => app.update(Msg::EnterInsertMode)?,
|
||||
KeyCode::Char(':') => app.update(Msg::EnterCommandMode)?,
|
||||
_ => return Ok(UpdateConclusion(false)),
|
||||
},
|
||||
|
||||
loop {
|
||||
let msg = cmd.into_command().execute();
|
||||
match msg {
|
||||
Some(msg) => {
|
||||
if let Msg::QuitApp = msg {
|
||||
return Ok(UpdateConclusion(true));
|
||||
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));
|
||||
}
|
||||
|
||||
cmd = app.update(msg)?;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
cmd = app.update(msg)?;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
tracing::warn!("failed to send event: {}", e);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
Ok(UpdateConclusion(false))
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user