Skip to main content

indigo_core/
editor.rs

1use crate::{
2    buffer::{Buffer, BufferKey},
3    fs::{Fs, NoFs},
4    key::Key,
5    mode::{Mode, command, insert, normal, prompt, replace, seek},
6    window::{Window, WindowKey, WindowMut, WindowState},
7};
8use camino::Utf8PathBuf;
9use indigo_kernel::IndigoBuildHasher;
10use slotmap::SlotMap;
11use std::{collections::HashMap, num::NonZeroUsize, process::ExitCode, sync::Arc};
12use thiserror::Error;
13
14#[cfg(feature = "arbitrary")]
15use arbitrary::Arbitrary;
16
17#[derive(Debug, Error)]
18pub enum Error {
19    #[error("Focused window {key:?} does not exist")]
20    FocusedWindowMissing { key: WindowKey },
21
22    #[error("Window {window_key:?} refers to buffer {buffer_key:?}, which does not exist")]
23    WindowBufferMissing {
24        window_key: WindowKey,
25        buffer_key: BufferKey,
26    },
27
28    #[error("Error from buffer {key:?}")]
29    Buffer {
30        key: BufferKey,
31        #[source]
32        source: anyhow::Error,
33    },
34
35    #[error("Error from window {key:?}")]
36    Window {
37        key: WindowKey,
38        #[source]
39        source: anyhow::Error,
40    },
41
42    #[error("Error from prompt")]
43    Prompt(#[source] anyhow::Error),
44}
45
46#[derive(Clone)]
47pub struct Editor {
48    pub fs: Arc<dyn Fs + Send + Sync>,
49    buffers: SlotMap<BufferKey, Buffer>,
50    windows: SlotMap<WindowKey, WindowState>,
51    focused_window: WindowKey,
52    pub registers: HashMap<char, Vec<String>, IndigoBuildHasher>,
53    pub mode: Mode,
54    pub count: Option<NonZeroUsize>,
55    pub pending_keys: Vec<Key>,
56    pub pwd: Option<Utf8PathBuf>,
57    pub message: Option<Result<String, String>>,
58    exit_code: Option<ExitCode>,
59}
60
61impl Editor {
62    #[must_use]
63    pub fn new() -> Self {
64        Self::default()
65    }
66
67    // TODO: Figure out a safer/better way to add and remove buffers
68    pub(crate) fn insert_buffer(&mut self, buffer: Buffer) -> BufferKey {
69        self.buffers.insert(buffer)
70    }
71
72    #[must_use]
73    pub fn get_buffer(&self, buffer_key: BufferKey) -> Option<&Buffer> {
74        self.buffers.get(buffer_key)
75    }
76
77    #[must_use]
78    pub fn get_buffer_mut(&mut self, buffer_key: BufferKey) -> Option<&mut Buffer> {
79        self.buffers.get_mut(buffer_key)
80    }
81
82    #[must_use]
83    pub fn focused_buffer(&self) -> &Buffer {
84        let window = self
85            .windows
86            .get(self.focused_window)
87            .expect("Window state is always kept valid");
88        self.buffers
89            .get(window.buffer)
90            .expect("Window state is always kept valid")
91    }
92
93    #[must_use]
94    pub fn focused_buffer_mut(&mut self) -> &mut Buffer {
95        let window = self
96            .windows
97            .get_mut(self.focused_window)
98            .expect("Window state is always kept valid");
99        self.buffers
100            .get_mut(window.buffer)
101            .expect("Window state is always kept valid")
102    }
103
104    pub fn buffers(&self) -> impl Iterator<Item = (BufferKey, &Buffer)> {
105        self.buffers.iter()
106    }
107
108    pub fn buffers_mut(&mut self) -> impl Iterator<Item = (BufferKey, &mut Buffer)> {
109        self.buffers.iter_mut()
110    }
111
112    // TODO: Figure out a safer/better way to add and remove windows
113    pub(crate) fn insert_window(&mut self, window: WindowState) -> WindowKey {
114        assert!(
115            self.buffers.contains_key(window.buffer),
116            "Window has invalid buffer key"
117        );
118        self.windows.insert(window)
119    }
120
121    pub fn focus_window(&mut self, window_key: WindowKey) -> bool {
122        if self.windows.contains_key(window_key) {
123            self.focused_window = window_key;
124            true
125        } else {
126            false
127        }
128    }
129
130    #[must_use]
131    pub fn get_window(&self, window_key: WindowKey) -> Option<Window<'_>> {
132        let window = self.windows.get(window_key)?;
133        let buffer = self
134            .buffers
135            .get(window.buffer)
136            .expect("Window state is always kept valid");
137        Some(Window::new(buffer, window))
138    }
139
140    #[must_use]
141    pub fn get_window_mut(&mut self, window_key: WindowKey) -> Option<WindowMut<'_>> {
142        let window = self.windows.get_mut(window_key)?;
143        let buffer = self
144            .buffers
145            .get_mut(window.buffer)
146            .expect("Window state is always kept valid");
147        Some(WindowMut::new(buffer, window))
148    }
149
150    pub fn focused_window(&self) -> Window<'_> {
151        let window = self
152            .windows
153            .get(self.focused_window)
154            .expect("Window state is always kept valid");
155        let buffer = self
156            .buffers
157            .get(window.buffer)
158            .expect("Window state is always kept valid");
159        Window::new(buffer, window)
160    }
161
162    pub fn focused_window_mut(&mut self) -> WindowMut<'_> {
163        let window = self
164            .windows
165            .get_mut(self.focused_window)
166            .expect("Window state is always kept valid");
167        let buffer = self
168            .buffers
169            .get_mut(window.buffer)
170            .expect("Window state is always kept valid");
171        WindowMut::new(buffer, window)
172    }
173
174    // TODO: Add windows iterator
175
176    #[must_use]
177    pub fn exit_code(&self) -> Option<ExitCode> {
178        self.exit_code
179    }
180
181    /// Doesn't actually terminate the process, just records the desired exit code.
182    pub fn exit(&mut self, exit_code: u8) {
183        self.exit_code = Some(ExitCode::from(exit_code));
184    }
185
186    pub fn handle_event(&mut self, mut event: Event) -> anyhow::Result<bool> {
187        #[expect(irrefutable_let_patterns)]
188        if let Event::Key(KeyEvent { key, .. }) = &mut event {
189            key.normalize();
190            self.pending_keys.push(*key);
191        }
192
193        let handled = match event {
194            Event::Key(_) => match self.mode {
195                Mode::Normal => normal::handle_keys(self),
196                Mode::Seek(_) => seek::handle_keys(self),
197                Mode::Insert => insert::handle_keys(self),
198                Mode::Prompt(_) => prompt::handle_keys(self),
199                Mode::Replace => replace::handle_keys(self),
200            },
201        };
202
203        self.assert_invariants().unwrap();
204
205        Ok(handled)
206    }
207
208    pub fn handle_action(&mut self, action: &Action) {
209        match action {
210            Action::Normal(action) => normal::handle_action(self, action),
211            Action::Insert(action) => insert::handle_action(self, action),
212            Action::Prompt(action) => prompt::handle_action(self, action),
213            Action::Command(action) => command::handle_action(self, action),
214            Action::Seek(action) => seek::handle_action(self, action),
215        }
216        self.assert_invariants().unwrap();
217    }
218
219    pub fn assert_invariants(&self) -> anyhow::Result<()> {
220        if !self.windows.contains_key(self.focused_window) {
221            anyhow::bail!(Error::FocusedWindowMissing {
222                key: self.focused_window,
223            });
224        }
225        for (key, buffer) in &self.buffers {
226            buffer
227                .assert_invariants()
228                .map_err(|source| Error::Buffer { key, source })?;
229        }
230        for (window_key, window) in &self.windows {
231            let Some(buffer) = self.buffers.get(window.buffer) else {
232                anyhow::bail!(Error::WindowBufferMissing {
233                    window_key,
234                    buffer_key: window.buffer,
235                });
236            };
237            Window::new(buffer, window)
238                .assert_invariants()
239                .map_err(|source| Error::Window {
240                    key: window_key,
241                    source,
242                })?;
243        }
244        if let Mode::Prompt(state) = &self.mode {
245            state.assert_invariants().map_err(Error::Prompt)?;
246        }
247        Ok(())
248    }
249}
250
251impl Default for Editor {
252    fn default() -> Self {
253        let mut buffers = SlotMap::default();
254        let buffer = Buffer::default();
255        let buffer_key = buffers.insert(buffer);
256
257        let window = WindowState::new(buffer_key);
258        let mut windows = SlotMap::default();
259        let window_key = windows.insert(window);
260
261        Self {
262            fs: Arc::new(NoFs),
263            buffers,
264            windows,
265            focused_window: window_key,
266            registers: HashMap::default(),
267            mode: Mode::default(),
268            count: None,
269            pending_keys: Vec::new(),
270            pwd: None,
271            message: None,
272            exit_code: None,
273        }
274    }
275}
276
277impl From<Buffer> for Editor {
278    fn from(buffer: Buffer) -> Self {
279        let mut buffers = SlotMap::default();
280        let buffer_key = buffers.insert(buffer);
281
282        let window = WindowState::new(buffer_key);
283        let mut windows = SlotMap::default();
284        let window_key = windows.insert(window);
285
286        Self {
287            buffers,
288            windows,
289            focused_window: window_key,
290            ..Self::default()
291        }
292    }
293}
294
295#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
296#[derive(Debug)]
297pub enum Event {
298    Key(KeyEvent),
299}
300
301#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
302#[derive(Debug, PartialEq)]
303pub struct KeyEvent {
304    pub key: Key,
305    pub kind: KeyEventKind,
306}
307
308#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
309#[derive(Debug, PartialEq)]
310pub enum KeyEventKind {
311    Press,
312    Repeat,
313    Release,
314}
315
316#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
317#[derive(Debug)]
318pub enum Action {
319    Normal(normal::Action),
320    Insert(insert::Action),
321    Prompt(prompt::Action),
322    Command(command::Action),
323    // TODO: Replace this with multi-key mappings in normal mode
324    Seek(seek::Action),
325}