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 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 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 #[must_use]
177 pub fn exit_code(&self) -> Option<ExitCode> {
178 self.exit_code
179 }
180
181 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 Seek(seek::Action),
325}