revi_core/
revi.rs

1use crate::buffer::Buffer;
2use crate::line_number::LineNumbers;
3use crate::mode::Mode;
4use crate::position::Position;
5use crate::revi_command::ReViCommand::{
6    self, Backspace, ChangeMode, CursorDown, CursorLeft, CursorRight, CursorUp, DeleteChar,
7    DeleteLine, End, EnterCommandMode, ExcuteCommandLine, ExitCommandMode, FirstCharInLine, Home,
8    InsertChar, JumpToFirstLineBuffer, JumpToLastLineBuffer, MoveBackwardByWord, MoveForwardByWord,
9    NewLine, NextWindow, Quit, Save, ScrollDown, ScrollUp, StartUp,
10};
11use crate::window::Window;
12use revi_ui::screen_size;
13use std::{cell::RefCell, rc::Rc};
14
15#[derive(Debug)]
16pub struct ReVi {
17    pub is_running: bool,
18    size: Position,
19    windows: Vec<Window>,
20    queue: Vec<usize>,
21    buffers: Vec<Rc<RefCell<Buffer>>>,
22    focused: usize,
23    last_focused: usize,
24    clipboard: String,
25}
26
27impl ReVi {
28    #[must_use]
29    pub fn new(files: &[String]) -> Rc<RefCell<Self>> {
30        let mut buffers: Vec<Rc<RefCell<Buffer>>> = files
31            .iter()
32            .map(|f| Rc::new(RefCell::new(Buffer::from_path(f.as_str()))))
33            .collect();
34        if buffers.is_empty() {
35            buffers.push(Rc::new(RefCell::new(Buffer::new())));
36        }
37
38        let cbuffer = Rc::new(RefCell::new(Buffer::new()));
39        buffers.insert(0, Clone::clone(&cbuffer));
40
41        let (w, h) = screen_size();
42
43        // We subtract 1 from the height here to count for the command bar.
44        let h = h.saturating_sub(1);
45
46        let main_window = Window::new(w, h, Clone::clone(&buffers[1]))
47            .with_status_bar(true)
48            .with_line_numbers(LineNumbers::RelativeNumber);
49
50        let command_bar = Window::new(w, 1, cbuffer).with_position((0, h + 2).into());
51
52        let windows = vec![command_bar, main_window];
53        let queue = windows
54            .iter()
55            .enumerate()
56            .map(|(i, _)| i)
57            .collect::<Vec<usize>>();
58
59        let revi = Self {
60            size: Position::new_u16(w, h),
61            is_running: true,
62            windows,
63            queue,
64            buffers,
65            focused: 1,
66            last_focused: 1,
67            clipboard: String::new(),
68        };
69        Rc::new(RefCell::new(revi))
70    }
71
72    #[must_use]
73    pub fn cursor_position_u16(&self) -> (u16, u16) {
74        self.windows[self.focused].cursor_screen().as_u16()
75    }
76
77    pub fn set_cursor_position(&mut self, x: u16, y: u16) {
78        self.windows[self.focused].set_cursor(Position::new_u16(x, y));
79    }
80
81    #[must_use]
82    pub fn mode(&self) -> &Mode {
83        &self.focused_window().mode
84    }
85
86    #[must_use]
87    pub fn mode_mut(&mut self) -> &mut Mode {
88        &mut self.focused_window_mut().mode
89    }
90
91    #[must_use]
92    pub fn last_focused_window(&self) -> &Window {
93        &self.windows[self.last_focused]
94    }
95
96    #[must_use]
97    pub fn last_focused_window_mut(&mut self) -> &mut Window {
98        &mut self.windows[self.last_focused]
99    }
100
101    #[must_use]
102    pub fn focused_window(&self) -> &Window {
103        &self.windows[self.focused]
104    }
105
106    #[must_use]
107    pub fn focused_window_mut(&mut self) -> &mut Window {
108        &mut self.windows[self.focused]
109    }
110
111    #[must_use]
112    pub fn queued(&self) -> &[usize] {
113        &self.queue
114    }
115
116    pub fn exit(&mut self) {
117        self.is_running = false;
118    }
119
120    pub fn next_window(&mut self) {
121        self.focused = if self.focused < self.windows.len().saturating_sub(1) {
122            self.focused + 1
123        } else {
124            1
125        }
126    }
127
128    pub fn change_modes(&mut self, mode: Mode) {
129        *self.mode_mut() = mode;
130        self.focused_window_mut().adjust_cursor_x();
131    }
132
133    pub fn enter_command_mode(&mut self) {
134        *self.mode_mut() = Mode::Command;
135        self.last_focused = self.focused.max(1);
136        self.focused = 0;
137        *self.mode_mut() = Mode::Insert;
138    }
139
140    pub fn exit_command_mode(&mut self) {
141        self.focused = self.last_focused;
142        *self.mode_mut() = Mode::Normal;
143    }
144
145    pub fn execute_command_line(&mut self) {
146        let string = self.focused_window().buffer().contents();
147        let new_buffer = Rc::new(RefCell::new(Buffer::new()));
148        self.buffers.remove(0);
149        self.buffers.insert(0, Clone::clone(&new_buffer));
150        self.focused_window_mut().set_buffer(new_buffer);
151        self.run_command_line(&string);
152    }
153
154    pub fn execute(&mut self, count: usize, commands: &[ReViCommand]) {
155        for command in commands {
156            match command {
157                StartUp => {}
158                CursorUp => self.focused_window_mut().move_cursor_up(count),
159                CursorDown => self.focused_window_mut().move_cursor_down(count),
160                ScrollUp => self.focused_window_mut().scroll_up(count),
161                ScrollDown => self.focused_window_mut().scroll_down(count),
162                CursorLeft => self.focused_window_mut().move_cursor_left(count),
163                CursorRight => self.focused_window_mut().move_cursor_right(count),
164                Home => self.focused_window_mut().home(),
165                End => self.focused_window_mut().end(),
166                FirstCharInLine => self.focused_window_mut().first_char_in_line(),
167                JumpToFirstLineBuffer => self.focused_window_mut().jump_to_first_line_buffer(),
168                JumpToLastLineBuffer => self.focused_window_mut().jump_to_last_line_buffer(),
169                DeleteChar => self.focused_window_mut().delete(),
170                DeleteLine => self.focused_window_mut().delete_line(),
171                NewLine if self.focused != 0 => self.focused_window_mut().insert_newline(),
172                Backspace => self.focused_window_mut().backspace(),
173                InsertChar(c) => self.focused_window_mut().insert_char(*c),
174                EnterCommandMode => self.enter_command_mode(),
175                ExitCommandMode if self.focused == 0 => self.exit_command_mode(),
176                ExcuteCommandLine if self.focused == 0 => self.execute_command_line(),
177                NextWindow => self.next_window(),
178                ChangeMode(m) => self.change_modes(*m),
179                MoveForwardByWord => self.focused_window_mut().move_forward_by_word(),
180                MoveBackwardByWord => self.focused_window_mut().move_backward_by_word(),
181                Save => self.focused_window().save(),
182                Quit => self.exit(),
183                _ => {}
184            }
185        }
186    }
187
188    fn run_command_line(&mut self, command: &str) {
189        let mut items: Vec<&str> = command.split(' ').collect();
190        match items.remove(0) {
191            "q" => self.exit(),
192            "b" if !items.is_empty() => {
193                if let Some(i) = items.get(0).and_then(|i| i.parse::<usize>().ok()) {
194                    let buffer = self.buffers.get(i).map(|rc| Clone::clone(rc));
195                    if let Some(b) = buffer {
196                        // self.focused = self.last_focused;
197                        self.last_focused_window_mut().set_buffer(b);
198                    }
199                }
200            }
201            "set" if !items.is_empty() => match items.get(0).copied().unwrap_or_default() {
202                "number" => self.windows[self.last_focused].set_number(LineNumbers::AbsoluteNumber),
203                "relativenumber" => {
204                    self.windows[self.last_focused].set_number(LineNumbers::RelativeNumber)
205                }
206                "nonumber" | "norelativenumber" => {
207                    self.windows[self.last_focused].set_number(LineNumbers::None)
208                }
209                _ => {}
210            },
211            _ => {}
212        }
213    }
214}
215
216impl revi_ui::Display for ReVi {
217    fn render(&self, mut func: impl FnMut(u16, u16, String)) {
218        for id in self.queued() {
219            let window = &self.windows[*id];
220            if let Some(((x, y), text)) = window.get_text_feild() {
221                func(x, y, text);
222            }
223            if let Some(((x, y), text)) = window.get_line_number() {
224                func(x, y, text);
225            }
226            if let Some(((x, y), text)) = window.get_status_bar() {
227                func(x, y, text);
228            }
229        }
230    }
231
232    fn cursor(&self, mut func: impl FnMut(u16, u16, Option<revi_ui::CursorShape>)) {
233        let window = self.focused_window();
234        let (x, y) = window.cursor_screen().as_u16();
235        func(x, y, Some(window.mode.shape()));
236    }
237}