diff options
Diffstat (limited to 'crates/dinggerd/src/hooks.rs')
| -rw-r--r-- | crates/dinggerd/src/hooks.rs | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/crates/dinggerd/src/hooks.rs b/crates/dinggerd/src/hooks.rs index 0074544..072735f 100644 --- a/crates/dinggerd/src/hooks.rs +++ b/crates/dinggerd/src/hooks.rs @@ -27,6 +27,9 @@ //! `{"method":"agents.reload_outdated"}`, and the watcher reloads each agent //! on an old extension once it is idle and off screen. //! +//! It also carries the `draft` in the agent's input box ("" for none), for +//! marking a draft you left behind; never logged. +//! //! Each line is answered with `{"ok":true}`. //! //! `{"method":"poke"}` only wakes the watcher; `dingger focus` and tmux's @@ -69,6 +72,8 @@ struct PaneReports { /// The version of the extension that reported, and its control socket. extension: Option<String>, control: Option<String>, + /// The draft in the agent's input box, as one line ("" for none). + draft: Option<String>, /// When the last report arrived. at: Option<Instant>, } @@ -165,6 +170,8 @@ struct HookParams { extension: Option<String>, #[serde(default)] control: Option<String>, + #[serde(default)] + draft: Option<String>, } fn handle_stream(stream: UnixStream) { @@ -231,6 +238,7 @@ fn handle_stream(stream: UnixStream) { importance, extension: params.extension, control: params.control, + draft: params.draft, }; record(params.pane_id, state, reported); wake(); @@ -244,6 +252,22 @@ struct Reported { importance: Option<Importance>, extension: Option<String>, control: Option<String>, + draft: Option<String>, +} + +/// The longest draft kept: enough to find an agent by it. +const DRAFT_CHARS: usize = 200; + +/// A draft as one line, at most [`DRAFT_CHARS`] long; "" for none. +fn draft_line(draft: &str) -> String { + draft + .split(|c: char| c.is_whitespace() || c.is_control()) + .filter(|word| !word.is_empty()) + .collect::<Vec<_>>() + .join(" ") + .chars() + .take(DRAFT_CHARS) + .collect() } /// A value fit for a tmux option: not empty, no control characters. @@ -271,6 +295,9 @@ fn record(pane_id: String, state: Option<AgentState>, reported: Reported) { if let Some(control) = option_value(reported.control) { pane.control = Some(control); } + if let Some(draft) = reported.draft { + pane.draft = Some(draft_line(&draft)); + } } /// Start listening; reports accumulate for the watcher to consume. @@ -328,6 +355,12 @@ pub fn extension(pane_id: &str) -> (Option<String>, Option<String>) { reports().get(pane_id).map(|pane| (pane.extension.clone(), pane.control.clone())).unwrap_or_default() } +/// The draft in the pane's input box ("" for none), if its extension +/// reported one. +pub fn draft(pane_id: &str) -> Option<String> { + reports().get(pane_id).and_then(|pane| pane.draft.clone()) +} + /// Ask the extension behind `control` to reload; its answer: `Ok(())` when /// it does, else why not (busy, a draft, or no answer). pub fn request_reload(control: &str) -> Result<(), String> { @@ -408,6 +441,21 @@ mod tests { } #[test] + fn a_draft_is_kept_as_one_short_line_until_reported_again() { + let draft = |text: &str| Reported { draft: Some(text.into()), ..Default::default() }; + record("%t5".into(), Some(AgentState::Idle), Reported::default()); + assert_eq!(super::draft("%t5"), None, "an extension that does not report drafts"); + record("%t5".into(), None, draft(" fix the\n\tsidecar;\x1b[1m ")); + assert_eq!(super::draft("%t5").as_deref(), Some("fix the sidecar; [1m")); + record("%t5".into(), Some(AgentState::Working), Reported::default()); + assert_eq!(super::draft("%t5").as_deref(), Some("fix the sidecar; [1m"), "kept by a report without one"); + record("%t5".into(), None, draft(&"x".repeat(500))); + assert_eq!(super::draft("%t5").map(|d| d.chars().count()), Some(DRAFT_CHARS)); + record("%t5".into(), None, draft("")); + assert_eq!(super::draft("%t5").as_deref(), Some(""), "cleared"); + } + + #[test] fn a_reload_answer_says_whether_it_reloads() { let dir = std::env::temp_dir().join(format!("dingger-reload-{}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); |
