generated from eblume/project-template
`o` on a task suspends the TUI, opens its canonical-context doc in the owner's
nvim via heph.nvim's live buffer surface (+lua require('heph.node').open), then
restores the alternate screen and reloads to pick up edits. The child nvim is
pointed at the same daemon via $HEPH_SOCKET, so it works under a custom
--socket too. This is the KB↔task fusion — edit the description/checklist in
the real editor and return straight to triage.
handle_key now returns an Action the event loop performs (the suspend/spawn is
terminal-owning, kept out of App). nvim arg builder unit-tested; the actual
suspend/spawn is interactive so it's exercised manually.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
53 lines
1.8 KiB
Rust
53 lines
1.8 KiB
Rust
//! The nvim context handoff (tech-spec §8.1): suspend the TUI, open the task's
|
|
//! canonical-context doc in the owner's nvim (live, via heph.nvim's buffer
|
|
//! surface), then resume. The KB↔task fusion — edit the description/checklist
|
|
//! in the real editor and come straight back to triage.
|
|
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
use anyhow::{Context, Result};
|
|
use ratatui::crossterm::{
|
|
execute,
|
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
|
};
|
|
use ratatui::DefaultTerminal;
|
|
|
|
/// The nvim arguments that open a heph node buffer through heph.nvim. Node ids
|
|
/// are ULIDs (no quote/escaping hazard), so a plain `'<id>'` literal is safe.
|
|
pub fn nvim_args(node_id: &str) -> Vec<String> {
|
|
vec![format!("+lua require('heph.node').open('{node_id}')")]
|
|
}
|
|
|
|
/// Drop out of the alternate screen, run `nvim` on `node_id` (pointing its
|
|
/// heph.nvim at the same daemon via `$HEPH_SOCKET`), then restore the TUI.
|
|
pub fn edit_in_nvim(terminal: &mut DefaultTerminal, node_id: &str, socket: &Path) -> Result<()> {
|
|
disable_raw_mode()?;
|
|
execute!(std::io::stdout(), LeaveAlternateScreen)?;
|
|
|
|
let status = Command::new("nvim")
|
|
.args(nvim_args(node_id))
|
|
.env("HEPH_SOCKET", socket)
|
|
.status();
|
|
|
|
// Restore the TUI regardless of how nvim exited, then surface any error.
|
|
enable_raw_mode()?;
|
|
execute!(std::io::stdout(), EnterAlternateScreen)?;
|
|
terminal.clear()?;
|
|
|
|
status.context("launching nvim (is it on PATH?)")?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn args_open_the_node_via_heph_nvim() {
|
|
let args = nvim_args("01ABCXYZ");
|
|
assert_eq!(args.len(), 1);
|
|
assert!(args[0].starts_with("+lua"));
|
|
assert!(args[0].contains("require('heph.node').open('01ABCXYZ')"));
|
|
}
|
|
}
|