use std::io;
use serde::{Deserialize, Serialize};
use crate::editor::events::AppEvent;
use crate::editor::Editor;
use strop_trace::replay::Tick;
#[derive(Serialize, Deserialize)]
pub struct StartupOpen {
pub target: crate::files::FileTarget,
pub intent: super::super::io::OpenIntent,
}
#[derive(Serialize, Deserialize)]
pub enum Action {
Start {
#[serde(default)]
open: Option<StartupOpen>,
},
Event(AppEvent),
Frame { columns: u16, rows: u16 },
Finish,
}
impl Editor {
pub fn recorded_action(&mut self, action: Action, tick: Tick) -> io::Result<()> {
if self.private_terminal_action(&action) {
self.tape.omit_content("terminal")?;
self.tape.action(tick, &action)?;
return strop_trace::without_content(|| self.apply_recorded(action));
}
self.tape.action(tick, &action)?;
self.apply_recorded(action)
}
pub(crate) fn apply_recorded(&mut self, action: Action) -> io::Result<()> {
let frame = matches!(action, Action::Frame { .. });
match action {
Action::Start { open } => {
self.resolution.enabled = true;
let startup_message = self.message.clone();
if let Some(open) = open {
self.lsp_start_services();
self.request_target(open.target, open.intent);
} else {
self.discover_git();
self.lsp_start_services();
}
if !startup_message.is_empty() {
self.message = startup_message;
}
}
Action::Event(event) => {
self.handle_app_event(event);
if !self.docs.is_empty() {
self.lsp_sync_changed();
}
if let Some(text) = self.osc52.take() {
if self.tape.request("terminal.osc52", &text)? {
self.terminal_output.push(text);
}
}
}
Action::Frame { columns, rows } => {
if u32::from(columns) * u32::from(rows) > 1_000_000 {
return Err(io::Error::other("invalid frame dimensions"));
}
if !self.should_quit && !self.docs.is_empty() {
let draw = self
.frame_draw
.ok_or_else(|| io::Error::other("frame draw hook not installed"))?;
draw(self, columns, rows, false)?;
}
}
Action::Finish => {
self.finish_background_work();
}
}
self.tape.healthy()?;
if !frame && self.tape.observes() {
self.tape.check(&self.observation())?;
}
Ok(())
}
fn observation(&self) -> serde_json::Value {
let documents: Vec<_> = self
.docs
.iter()
.map(
|(id, document)| serde_json::json!({"document": id, "buffer": document.buf.seed()}),
)
.collect();
serde_json::json!({
"documents": documents,
"panes": self.panes,
"mru": self.mru,
"active": self.active_pane,
"mode": self.mode.chip(),
"quit": self.should_quit,
"message": self.message,
"headless": crate::editor::state_json(self),
"hover": self.hover_card,
"hunks": self.hunks,
"staged_hunks": self.staged_hunks,
"picker_items": self.picker.as_ref().map(|glue| &glue.picker.items),
})
}
}
pub fn replay(
nodes: Vec<strop_trace::replay::Node>,
frame_draw: crate::editor::FrameDraw,
) -> io::Result<Editor> {
let tape = std::rc::Rc::new(strop_trace::replay::Tape::replay(nodes));
let seed: super::seed::Seed = tape.take_seed()?;
let mut editor = seed.into_editor(tape)?;
editor.frame_draw = Some(frame_draw);
while let Some(action) = editor.tape.next::<Action>()? {
editor.apply_recorded(action)?;
}
editor.tape.healthy()?;
Ok(editor)
}