pepper/
application.rs

1use std::{env, fs, io, panic, path::Path, time::Duration};
2
3use crate::{
4    client::ClientManager,
5    command::CommandManager,
6    editor::{Editor, EditorContext, EditorFlow},
7    editor_utils::{LogKind, REGISTER_READLINE_INPUT},
8    events::{ClientEvent, ClientEventReceiver, ServerEvent, TargetClient},
9    platform::{Key, Platform, PlatformEvent, PlatformRequest, ProcessTag},
10    plugin::{PluginCollection, PluginDefinition},
11    serialization::{DeserializeError, Serialize},
12    ui, Args, ResourceFile,
13};
14
15#[derive(Default, Clone, Copy)]
16pub struct OnPanicConfig {
17    pub write_info_to_file: Option<&'static Path>,
18    pub try_attaching_debugger: bool,
19}
20
21pub struct ApplicationConfig {
22    pub args: Args,
23    pub plugin_definitions: Vec<PluginDefinition>,
24    pub static_configs: Vec<ResourceFile>,
25    pub on_panic_config: OnPanicConfig,
26}
27impl Default for ApplicationConfig {
28    fn default() -> Self {
29        Self {
30            args: Args::parse(),
31            static_configs: vec![crate::DEFAULT_CONFIGS, crate::DEFAULT_SYNTAXES],
32            plugin_definitions: Vec::new(),
33            on_panic_config: OnPanicConfig::default(),
34        }
35    }
36}
37
38pub const SERVER_CONNECTION_BUFFER_LEN: usize = 4 * 1024;
39pub const SERVER_IDLE_DURATION: Duration = Duration::from_secs(1);
40
41pub struct ServerApplication {
42    pub ctx: EditorContext,
43    client_event_receiver: ClientEventReceiver,
44}
45impl ServerApplication {
46    pub fn new(config: ApplicationConfig) -> Option<Self> {
47        let current_dir = env::current_dir().unwrap_or_default();
48
49        let mut ctx = EditorContext {
50            editor: Editor::new(current_dir, config.args.session_name),
51            platform: Platform::default(),
52            clients: ClientManager::default(),
53            plugins: PluginCollection::default(),
54        };
55
56        for definition in config.plugin_definitions {
57            PluginCollection::add(&mut ctx, definition);
58        }
59
60        for config in &config.static_configs {
61            let result = CommandManager::eval(&mut ctx, None, config.name, config.content);
62            let flow = CommandManager::unwrap_eval_result(&mut ctx, result);
63            if !matches!(flow, EditorFlow::Continue) {
64                return None;
65            }
66        }
67
68        for config in config.args.configs {
69            let path = Path::new(&config.path);
70            if config.suppress_file_not_found && !path.exists() {
71                continue;
72            }
73            match fs::read_to_string(path) {
74                Ok(source) => {
75                    let path = path.to_str().unwrap_or("");
76                    let result = CommandManager::eval(&mut ctx, None, path, &source);
77                    let flow = CommandManager::unwrap_eval_result(&mut ctx, result);
78                    if !matches!(flow, EditorFlow::Continue) {
79                        return None;
80                    }
81                }
82                Err(_) => ctx
83                    .editor
84                    .logger
85                    .write(LogKind::Error)
86                    .fmt(format_args!("could not load config '{}'", config.path)),
87            }
88        }
89
90        Some(Self {
91            ctx,
92            client_event_receiver: ClientEventReceiver::default(),
93        })
94    }
95
96    pub fn update<I>(&mut self, events: I)
97    where
98        I: Iterator<Item = PlatformEvent>,
99    {
100        for event in events {
101            match event {
102                PlatformEvent::Idle => {
103                    self.ctx.editor.on_idle();
104                    self.ctx.trigger_event_handlers();
105                }
106                PlatformEvent::ConnectionOpen { handle } => {
107                    self.ctx.clients.on_client_joined(handle)
108                }
109                PlatformEvent::ConnectionClose { handle } => {
110                    self.ctx
111                        .editor
112                        .buffer_views
113                        .remove_buffer_views_with_client(handle);
114                    self.ctx.clients.on_client_left(handle);
115                    if self.ctx.clients.iter().next().is_none() {
116                        self.ctx.platform.requests.enqueue(PlatformRequest::Quit);
117                    }
118                }
119                PlatformEvent::ConnectionOutput { handle, buf } => {
120                    let mut events = self
121                        .client_event_receiver
122                        .receive_events(handle, buf.as_bytes());
123                    self.ctx.platform.buf_pool.release(buf);
124
125                    while let Some(event) = events.next(&self.client_event_receiver) {
126                        match Editor::on_client_event(&mut self.ctx, handle, event) {
127                            EditorFlow::Continue => (),
128                            EditorFlow::Suspend => {
129                                let mut buf = self.ctx.platform.buf_pool.acquire();
130                                ServerEvent::Suspend.serialize(buf.write());
131                                self.ctx
132                                    .platform
133                                    .requests
134                                    .enqueue(PlatformRequest::WriteToClient { handle, buf });
135                            }
136                            EditorFlow::Quit => self
137                                .ctx
138                                .platform
139                                .requests
140                                .enqueue(PlatformRequest::CloseClient { handle }),
141                            EditorFlow::QuitAll => {
142                                self.ctx.platform.requests.enqueue(PlatformRequest::Quit)
143                            }
144                        }
145                    }
146                    events.finish(&mut self.client_event_receiver);
147                }
148                PlatformEvent::ProcessSpawned { tag, handle } => {
149                    match tag {
150                        ProcessTag::Ignored => (),
151                        ProcessTag::Buffer(index) => self.ctx.editor.buffers.on_process_spawned(
152                            &mut self.ctx.platform,
153                            index,
154                            handle,
155                        ),
156                        ProcessTag::PickerEntries => self
157                            .ctx
158                            .editor
159                            .picker_entries_process_buf
160                            .on_process_spawned(),
161                        ProcessTag::Plugin { plugin_handle, id } => {
162                            PluginCollection::on_process_spawned(
163                                &mut self.ctx,
164                                plugin_handle,
165                                id,
166                                handle,
167                            )
168                        }
169                    }
170                    self.ctx.trigger_event_handlers();
171                }
172                PlatformEvent::ProcessOutput { tag, buf } => {
173                    let bytes = buf.as_bytes();
174                    match tag {
175                        ProcessTag::Ignored => (),
176                        ProcessTag::Buffer(index) => self.ctx.editor.buffers.on_process_output(
177                            &mut self.ctx.editor.word_database,
178                            index,
179                            bytes,
180                            self.ctx.editor.events.writer(),
181                        ),
182                        ProcessTag::PickerEntries => self
183                            .ctx
184                            .editor
185                            .picker_entries_process_buf
186                            .on_process_output(
187                                &mut self.ctx.editor.picker,
188                                self.ctx.editor.registers.get(REGISTER_READLINE_INPUT),
189                                bytes,
190                            ),
191                        ProcessTag::Plugin { plugin_handle, id } => {
192                            PluginCollection::on_process_output(
193                                &mut self.ctx,
194                                plugin_handle,
195                                id,
196                                bytes,
197                            )
198                        }
199                    }
200                    self.ctx.trigger_event_handlers();
201                    self.ctx.platform.buf_pool.release(buf);
202                }
203                PlatformEvent::ProcessExit { tag } => {
204                    match tag {
205                        ProcessTag::Ignored => (),
206                        ProcessTag::Buffer(index) => self.ctx.editor.buffers.on_process_exit(
207                            &mut self.ctx.editor.word_database,
208                            index,
209                            self.ctx.editor.events.writer(),
210                        ),
211                        ProcessTag::PickerEntries => {
212                            self.ctx.editor.picker_entries_process_buf.on_process_exit(
213                                &mut self.ctx.editor.picker,
214                                self.ctx.editor.registers.get(REGISTER_READLINE_INPUT),
215                            )
216                        }
217                        ProcessTag::Plugin { plugin_handle, id } => {
218                            PluginCollection::on_process_exit(&mut self.ctx, plugin_handle, id)
219                        }
220                    }
221                    self.ctx.trigger_event_handlers();
222                }
223                PlatformEvent::IpcConnected { tag, handle } => {
224                    PluginCollection::on_ipc_connected(
225                        &mut self.ctx,
226                        tag.plugin_handle,
227                        tag.id,
228                        handle,
229                    );
230                    self.ctx.trigger_event_handlers();
231                }
232                PlatformEvent::IpcOutput { tag, buf } => {
233                    PluginCollection::on_ipc_output(
234                        &mut self.ctx,
235                        tag.plugin_handle,
236                        tag.id,
237                        buf.as_bytes(),
238                    );
239                    self.ctx.trigger_event_handlers();
240                    self.ctx.platform.buf_pool.release(buf);
241                }
242                PlatformEvent::IpcClose { tag } => {
243                    PluginCollection::on_ipc_close(&mut self.ctx, tag.plugin_handle, tag.id);
244                    self.ctx.trigger_event_handlers();
245                }
246            }
247        }
248
249        self.ctx.editor.events.assert_empty();
250        self.ctx.render();
251    }
252}
253
254pub const CLIENT_STDIN_BUFFER_LEN: usize = 4 * 1024;
255pub const CLIENT_CONNECTION_BUFFER_LEN: usize = 4 * 1024;
256
257pub struct ClientApplication<O>
258where
259    O: io::Write,
260{
261    target_client: TargetClient,
262    server_read_buf: Vec<u8>,
263    server_write_buf: Vec<u8>,
264    pub output: Option<O>,
265    stdout_buf: Vec<u8>,
266}
267impl<O> ClientApplication<O>
268where
269    O: io::Write,
270{
271    pub fn new() -> Self {
272        Self {
273            target_client: TargetClient::Sender,
274            server_read_buf: Vec::new(),
275            server_write_buf: Vec::new(),
276            output: None,
277            stdout_buf: Vec::new(),
278        }
279    }
280
281    pub fn init(&mut self, args: Args) -> &[u8] {
282        if args.as_focused_client {
283            self.target_client = TargetClient::Focused;
284        }
285
286        self.server_write_buf.clear();
287
288        self.reinit_screen();
289        if !args.quit && !args.as_focused_client {
290            ClientEvent::Key(self.target_client, Key::default())
291                .serialize(&mut self.server_write_buf);
292        }
293
294        let mut commands = String::new();
295        for path in &args.files {
296            commands.clear();
297            commands.push_str("open \"");
298            for c in path.chars() {
299                match c {
300                    '\\' => commands.push_str("\\\\"),
301                    '"' => commands.push_str("\\\""),
302                    c => commands.push(c),
303                }
304            }
305            commands.push('"');
306            ClientEvent::Commands(self.target_client, &commands)
307                .serialize(&mut self.server_write_buf);
308        }
309
310        if args.quit {
311            ClientEvent::Commands(TargetClient::Sender, "quit")
312                .serialize(&mut self.server_write_buf);
313        }
314
315        self.server_write_buf.as_slice()
316    }
317
318    pub fn reinit_screen(&mut self) {
319        if let Some(output) = &mut self.output {
320            let _ = output.write_all(ui::ENTER_ALTERNATE_BUFFER_CODE);
321            let _ = output.write_all(ui::HIDE_CURSOR_CODE);
322            let _ = output.write_all(ui::MODE_256_COLORS_CODE);
323            let _ = output.flush();
324        }
325    }
326
327    pub fn restore_screen(&mut self) {
328        if let Some(output) = &mut self.output {
329            let _ = output.write_all(ui::EXIT_ALTERNATE_BUFFER_CODE);
330            let _ = output.write_all(ui::SHOW_CURSOR_CODE);
331            let _ = output.write_all(ui::RESET_STYLE_CODE);
332            let _ = output.flush();
333        }
334    }
335
336    pub fn update(
337        &mut self,
338        resize: Option<(u16, u16)>,
339        keys: &[Key],
340        stdin_bytes: Option<&[u8]>,
341        server_bytes: &[u8],
342    ) -> (bool, &'_ [u8]) {
343        self.server_write_buf.clear();
344
345        if let Some((width, height)) = resize {
346            ClientEvent::Resize(width, height).serialize(&mut self.server_write_buf);
347        }
348
349        for key in keys {
350            ClientEvent::Key(self.target_client, *key).serialize(&mut self.server_write_buf);
351        }
352
353        if let Some(bytes) = stdin_bytes {
354            ClientEvent::StdinInput(self.target_client, bytes)
355                .serialize(&mut self.server_write_buf);
356        }
357
358        let mut suspend = false;
359        if !server_bytes.is_empty() {
360            self.server_read_buf.extend_from_slice(server_bytes);
361            let mut read_slice = &self.server_read_buf[..];
362
363            loop {
364                let previous_slice = read_slice;
365                match ServerEvent::deserialize(&mut read_slice) {
366                    Ok(ServerEvent::Display(display)) => {
367                        if let Some(output) = &mut self.output {
368                            output.write_all(display).unwrap();
369                        }
370                    }
371                    Ok(ServerEvent::Suspend) => suspend = true,
372                    Ok(ServerEvent::StdoutOutput(bytes)) => {
373                        self.stdout_buf.clear();
374                        self.stdout_buf.extend_from_slice(bytes);
375                    }
376                    Err(DeserializeError::InsufficientData) => {
377                        let read_len = self.server_read_buf.len() - previous_slice.len();
378                        self.server_read_buf.drain(..read_len);
379                        break;
380                    }
381                    Err(DeserializeError::InvalidData) => {
382                        panic!("client received invalid data from server")
383                    }
384                }
385            }
386
387            if let Some(output) = &mut self.output {
388                output.flush().unwrap();
389            }
390        }
391
392        (suspend, self.server_write_buf.as_slice())
393    }
394
395    pub fn get_stdout_bytes(&self) -> &[u8] {
396        &self.stdout_buf
397    }
398}
399impl<O> Drop for ClientApplication<O>
400where
401    O: io::Write,
402{
403    fn drop(&mut self) {
404        self.restore_screen();
405    }
406}