strop_engine/editor/io/
save.rs1use super::*;
3
4impl Editor {
5 pub fn request_save(&mut self, target: Option<PathBuf>, force: bool, close: bool) {
6 if self.config.auto_format && target.is_none() && self.lsp_format_available() {
10 let before = self.lsp_state.navigation;
11 self.lsp_format();
12 if let Some(request) = self
13 .lsp_state
14 .navigation
15 .filter(|request| Some(*request) != before)
16 {
17 self.lsp_state.after_format = Some(crate::editor::lsp::state::AfterFormat::Save {
18 document: self.current(),
19 close,
20 force,
21 request,
22 });
23 return;
24 }
25 }
26 self.request_save_document(self.current(), target, force, close);
27 }
28
29 pub(crate) fn request_save_document(
30 &mut self,
31 document: DocumentId,
32 target: Option<PathBuf>,
33 force: bool,
34 close: bool,
35 ) -> bool {
36 if self.docs.get(document).is_some_and(|doc| {
37 matches!(
38 doc.source,
39 crate::editor::document::DocumentSource::Remote(_)
40 | crate::editor::document::DocumentSource::RemoteDirectory(_)
41 )
42 }) {
43 return self.request_remote_save(document, target, force, close);
44 }
45 if target
46 .as_ref()
47 .and_then(|path| path.to_str())
48 .is_some_and(|path| path.starts_with("ssh://"))
49 {
50 self.message = "remote save-as is unsupported; no local fallback".into();
51 return false;
52 }
53 if self.io.saves.contains_key(&document) {
54 self.message = "write already in progress".into();
55 return false;
56 }
57 let Some(buffer) = self.docs.get(document).map(|doc| &doc.buf) else {
58 self.message = "write refused: source buffer closed".into();
59 return false;
60 };
61 let revision = buffer.revision();
62 let target = target.map(|path| self.cwd.join(path));
63 let work = match buffer.prepare_save(target.clone(), force) {
64 Ok(work) => work,
65 Err(error) => {
66 self.message = format!("write failed: {error}");
67 return false;
68 }
69 };
70 let request = match self.worker_ids.allocate() {
71 Ok(request) => request,
72 Err(error) => {
73 self.message = error.message;
74 return false;
75 }
76 };
77 let ticket = Ticket {
78 request,
79 key: SaveKey {
80 document,
81 revision,
82 focus: self.focus_epoch,
83 close,
84 target,
85 force,
86 },
87 };
88 self.io.saves.insert(document, ticket.clone());
89 self.message = "saving".into();
90 match self.tape.request("io.save", &ticket) {
91 Ok(false) => return true,
92 Ok(true) => {}
93 Err(error) => {
94 self.handle_io(IoEvent::Save(Box::new(Completion {
95 ticket,
96 outcome: Outcome::failed(FailureKind::Protocol, error.to_string()),
97 })));
98 return false;
99 }
100 }
101 let tx = self.io.tx.clone();
102 let handle = worker::spawn(
103 "strop-save",
104 move |outcome| {
105 let _ = tx.send(IoEvent::Save(Box::new(Completion { ticket, outcome })));
106 },
107 move |_| match work.execute() {
108 Ok(receipt) => Outcome::Success(receipt),
109 Err(error) => Outcome::failed(FailureKind::Io, error.to_string()),
110 },
111 );
112 self.worker_handles.insert(request, handle);
113 true
114 }
115
116 pub(crate) fn finish_save_feedback(&mut self, document: DocumentId) {
118 if let Some(warning) = self.io.format_warnings.remove(&document) {
119 self.message
120 .push_str(&format!(" — format warning: {warning}"));
121 }
122 self.finish_change_save(document);
123 }
124}