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 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.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}