strop_engine/editor/trace/
seed.rs1use std::io;
7use std::path::PathBuf;
8
9use serde::{Deserialize, Serialize};
10use strop_core::id::{Arena, ArenaSeed, DocumentId};
11use strop_core::worker::{Load, WorkerId, WorkerIds};
12use strop_core::{Buffer, BufferSeed};
13
14use crate::editor::document::DocumentSource;
15use crate::editor::{Document, Editor, LayoutDir, Pane};
16
17#[derive(Serialize, Deserialize)]
20struct DocSeed {
21 buffer: BufferSeed,
22 file: bool,
23}
24
25#[derive(Serialize, Deserialize)]
26pub struct Seed {
27 documents: ArenaSeed<DocSeed>,
28 panes: Vec<Pane>,
29 mru: Vec<DocumentId>,
30 active: usize,
31 layout: LayoutDir,
32 #[serde(with = "strop_core::path_serde")]
33 cwd: PathBuf,
34 #[serde(with = "strop_core::path_serde::option")]
35 state_dir: Option<PathBuf>,
36 session_policy: crate::session::SessionPolicy,
37 config: crate::config::Config,
38 git: Option<strop_git::GitContext>,
39 git_view: WorkerId,
40 git_discovery: Load<crate::editor::git_memory::ContextKey>,
41 worker_ids: WorkerIds,
42 focus_epoch: u64,
43 generation: u64,
44 message: String,
45}
46
47impl Seed {
48 pub fn capture(editor: &Editor) -> io::Result<Self> {
52 if editor.picker.is_some()
53 || !editor.lsp_servers.is_empty()
54 || matches!(editor.git_discovery, Load::Running(_))
55 || editor.docs.iter().any(|(_, document)| {
56 !matches!(
57 document.source,
58 DocumentSource::File | DocumentSource::Scratch
59 )
60 })
61 {
62 return Err(io::Error::other("seed must precede service startup"));
63 }
64 Ok(Self {
65 documents: editor.docs.seed_with(|document| DocSeed {
66 buffer: document.buf.seed(),
67 file: matches!(document.source, DocumentSource::File),
68 }),
69 panes: editor.panes.clone(),
70 mru: editor.mru.clone(),
71 active: editor.active_pane,
72 layout: editor.layout,
73 cwd: editor.cwd.clone(),
74 state_dir: editor.state_dir.clone(),
75 session_policy: editor.session_policy,
76 config: editor.config.clone(),
77 git: editor.git.clone(),
78 git_view: editor.git_view,
79 git_discovery: editor.git_discovery.clone(),
80 worker_ids: editor.worker_ids.clone(),
81 focus_epoch: editor.focus_epoch,
82 generation: editor.generation,
83 message: editor.message.clone(),
84 })
85 }
86
87 pub fn input_text(&self) -> io::Result<&str> {
90 let pane = self
91 .panes
92 .get(self.active)
93 .ok_or_else(|| io::Error::other("seed has no active pane"))?;
94 let document = self
95 .documents
96 .slots
97 .get(pane.doc.index())
98 .filter(|(generation, _)| *generation == pane.doc.generation())
99 .and_then(|(_, document)| document.as_ref())
100 .ok_or_else(|| io::Error::other("seed has no active document"))?;
101 Ok(&document.buffer.text)
102 }
103
104 pub fn into_editor(self, tape: std::rc::Rc<strop_trace::replay::Tape>) -> io::Result<Editor> {
107 let mut slots = Vec::with_capacity(self.documents.slots.len());
108 for (generation, value) in self.documents.slots {
109 let document = value
110 .map(|seed| {
111 let buffer = seed.buffer.into_buffer().map_err(io::Error::other)?;
112 Ok::<_, io::Error>(if seed.file {
113 Document::new(buffer)
114 } else {
115 Document::scratch(buffer)
116 })
117 })
118 .transpose()?;
119 slots.push((generation, document));
120 }
121 let docs = Arena::from_seed(ArenaSeed {
122 slots,
123 free: self.documents.free,
124 })
125 .map_err(io::Error::other)?;
126 if docs.is_empty()
127 || self.active >= self.panes.len()
128 || self.panes.iter().any(|pane| docs.get(pane.doc).is_none())
129 || self.mru.iter().any(|id| docs.get(*id).is_none())
130 {
131 return Err(io::Error::other("invalid initial document references"));
132 }
133 for pane in &self.panes {
134 let buffer = &docs
135 .get(pane.doc)
136 .ok_or_else(|| io::Error::other("invalid initial document reference"))?
137 .buf;
138 for selection in
139 std::iter::once(pane.sels.primary()).chain(pane.sels.extra_heads().iter().copied())
140 {
141 if selection.anchor > buffer.len_bytes()
142 || selection.head > buffer.len_bytes()
143 || !buffer.is_boundary(selection.anchor)
144 || !buffer.is_boundary(selection.head)
145 {
146 return Err(io::Error::other("invalid initial selection"));
147 }
148 }
149 }
150 let mut editor = Editor::new_in(Buffer::from_text(""), self.cwd);
151 editor.docs = docs;
152 editor.panes = self.panes;
153 editor.mru = self.mru;
154 editor.active_pane = self.active;
155 editor.layout = self.layout;
156 editor.state_dir = self.state_dir;
157 editor.session_policy = self.session_policy;
158 editor.config = self.config;
159 editor.git = self.git;
160 editor.git_view = self.git_view;
161 editor.git_discovery = self.git_discovery;
162 editor.worker_ids = self.worker_ids;
163 editor.focus_epoch = self.focus_epoch;
164 editor.generation = self.generation;
165 editor.message = self.message;
166 editor.tape = tape;
167 Ok(editor)
168 }
169}