pepper/
client.rs

1use std::{fmt, path::Path};
2
3use crate::{
4    buffer::{BufferCollection, BufferHandle, BufferProperties, CharDisplayDistances},
5    buffer_position::BufferPositionIndex,
6    buffer_view::{BufferView, BufferViewCollection, BufferViewHandle},
7    editor::Editor,
8    editor_utils::ResidualStrBytes,
9    navigation_history::{NavigationHistory, NavigationMovement},
10    serialization::{DeserializeError, Deserializer, Serialize, Serializer},
11};
12
13#[derive(Clone, Copy, Eq, PartialEq)]
14pub struct ClientHandle(pub u8);
15
16impl<'de> Serialize<'de> for ClientHandle {
17    fn serialize(&self, serializer: &mut dyn Serializer) {
18        self.0.serialize(serializer);
19    }
20
21    fn deserialize(deserializer: &mut dyn Deserializer<'de>) -> Result<Self, DeserializeError> {
22        Ok(Self(u8::deserialize(deserializer)?))
23    }
24}
25
26pub enum ViewAnchor {
27    Top,
28    Center,
29    Bottom,
30}
31
32pub struct Client {
33    active: bool,
34    handle: ClientHandle,
35
36    pub viewport_size: (u16, u16),
37
38    pub(crate) navigation_history: NavigationHistory,
39
40    buffer_view_handle: Option<BufferViewHandle>,
41    stdin_buffer_handle: Option<BufferHandle>,
42    stdin_residual_bytes: ResidualStrBytes,
43}
44
45impl Client {
46    pub(crate) fn new() -> Self {
47        Self {
48            active: false,
49            handle: ClientHandle(0),
50
51            viewport_size: (0, 0),
52
53            navigation_history: NavigationHistory::default(),
54
55            buffer_view_handle: None,
56            stdin_buffer_handle: None,
57            stdin_residual_bytes: ResidualStrBytes::default(),
58        }
59    }
60
61    fn dispose(&mut self) {
62        self.active = false;
63
64        self.viewport_size = (0, 0);
65
66        self.navigation_history.clear();
67
68        self.buffer_view_handle = None;
69        self.stdin_buffer_handle = None;
70        self.stdin_residual_bytes = ResidualStrBytes::default();
71    }
72
73    pub fn handle(&self) -> ClientHandle {
74        self.handle
75    }
76
77    pub fn buffer_view_handle(&self) -> Option<BufferViewHandle> {
78        self.buffer_view_handle
79    }
80
81    pub fn stdin_buffer_handle(&self) -> Option<BufferHandle> {
82        self.stdin_buffer_handle
83    }
84
85    pub fn set_buffer_view_handle(
86        &mut self,
87        handle: Option<BufferViewHandle>,
88        buffer_views: &BufferViewCollection,
89    ) {
90        NavigationHistory::save_snapshot(self, buffer_views);
91        self.set_buffer_view_handle_no_history(handle);
92    }
93
94    pub(crate) fn set_buffer_view_handle_no_history(&mut self, handle: Option<BufferViewHandle>) {
95        self.buffer_view_handle = handle;
96    }
97
98    pub fn has_ui(&self) -> bool {
99        self.viewport_size.0 != 0 && self.viewport_size.1 != 0
100    }
101
102    pub fn set_view_anchor(&self, editor: &mut Editor, anchor: ViewAnchor) {
103        if !self.has_ui() {
104            return;
105        }
106
107        if let Some(buffer_view_handle) = self.buffer_view_handle {
108            let height = self.viewport_size.1.saturating_sub(1) as usize;
109            let height_offset = match anchor {
110                ViewAnchor::Top => 0,
111                ViewAnchor::Center => height / 2,
112                ViewAnchor::Bottom => height.saturating_sub(1),
113            };
114
115            let buffer_view = editor.buffer_views.get_mut(buffer_view_handle);
116            let main_cursor_padding_top = self.find_main_cursor_padding_top(
117                buffer_view,
118                &editor.buffers,
119                editor.config.tab_size,
120            );
121            buffer_view.scroll = main_cursor_padding_top.saturating_sub(height_offset) as _;
122        }
123    }
124
125    pub(crate) fn scroll_to_main_cursor(
126        &self,
127        buffer_views: &mut BufferViewCollection,
128        buffers: &BufferCollection,
129        tab_size: u8,
130        margin_bottom: usize,
131    ) -> BufferPositionIndex {
132        if !self.has_ui() {
133            return 0;
134        }
135
136        let height = self.viewport_size.1.saturating_sub(1) as usize;
137        let height = height.saturating_sub(margin_bottom);
138        let half_height = height / 2;
139
140        match self.buffer_view_handle {
141            Some(buffer_view_handle) => {
142                let buffer_view = buffer_views.get_mut(buffer_view_handle);
143                let main_cursor_padding_top =
144                    self.find_main_cursor_padding_top(buffer_view, buffers, tab_size);
145
146                let mut scroll = buffer_view.scroll as usize;
147                if main_cursor_padding_top < scroll.saturating_sub(half_height) {
148                    scroll = main_cursor_padding_top.saturating_sub(half_height) as _;
149                } else if main_cursor_padding_top < scroll {
150                    scroll = main_cursor_padding_top as _;
151                } else if main_cursor_padding_top >= scroll + height + half_height {
152                    scroll = (main_cursor_padding_top + 1 - half_height) as _;
153                } else if main_cursor_padding_top >= scroll + height {
154                    scroll = (main_cursor_padding_top + 1 - height) as _;
155                }
156                let scroll = scroll as _;
157                buffer_view.scroll = scroll;
158                scroll
159            }
160            None => 0,
161        }
162    }
163
164    pub(crate) fn on_stdin_input(&mut self, editor: &mut Editor, bytes: &[u8]) {
165        let mut buf = Default::default();
166        let texts = self.stdin_residual_bytes.receive_bytes(&mut buf, bytes);
167
168        let buffer_handle = match self.stdin_buffer_handle() {
169            Some(handle) => handle,
170            None => {
171                use fmt::Write;
172
173                let buffer = editor.buffers.add_new();
174
175                let mut path = editor.string_pool.acquire_with("pipe.");
176                let _ = write!(path, "{}", self.handle().0);
177                buffer.set_path(Path::new(&path));
178                editor.string_pool.release(path);
179
180                buffer.properties = BufferProperties::text();
181                buffer.properties.file_backed_enabled = false;
182
183                let buffer_view_handle =
184                    editor.buffer_views.add_new(self.handle(), buffer.handle());
185                self.set_buffer_view_handle(Some(buffer_view_handle), &editor.buffer_views);
186
187                self.stdin_buffer_handle = Some(buffer.handle());
188                buffer.handle()
189            }
190        };
191
192        let buffer = editor.buffers.get_mut(buffer_handle);
193        let mut events = editor
194            .events
195            .writer()
196            .buffer_text_inserts_mut_guard(buffer_handle);
197        for text in texts {
198            let position = buffer.content().end();
199            buffer.insert_text(&mut editor.word_database, position, text, &mut events);
200        }
201    }
202
203    pub(crate) fn on_buffer_close(&mut self, editor: &mut Editor, buffer_handle: BufferHandle) {
204        self.navigation_history
205            .remove_snapshots_with_buffer_handle(buffer_handle);
206
207        if let Some(handle) = self.buffer_view_handle {
208            let buffer_view = editor.buffer_views.get(handle);
209            if buffer_view.buffer_handle == buffer_handle {
210                self.buffer_view_handle = None;
211                NavigationHistory::move_in_history(self, editor, NavigationMovement::Backward);
212                NavigationHistory::move_in_history(self, editor, NavigationMovement::Forward);
213            }
214        }
215
216        if self.stdin_buffer_handle == Some(buffer_handle) {
217            self.stdin_buffer_handle = None;
218        }
219    }
220
221    fn find_main_cursor_padding_top(
222        &self,
223        buffer_view: &BufferView,
224        buffers: &BufferCollection,
225        tab_size: u8,
226    ) -> usize {
227        let width = self.viewport_size.0 as usize;
228
229        let buffer = buffers.get(buffer_view.buffer_handle).content();
230        let position = buffer_view.cursors.main_cursor().position;
231
232        let mut height = position.line_index as usize;
233        for display_len in &buffer.line_display_lens()[..position.line_index as usize] {
234            height += display_len.total_len(tab_size) / width;
235        }
236
237        let cursor_line = buffer.lines()[position.line_index as usize].as_str();
238        let cursor_line = &cursor_line[..position.column_byte_index as usize];
239        if let Some(d) = CharDisplayDistances::new(cursor_line, tab_size).last() {
240            height += d.distance as usize / width;
241        }
242
243        height
244    }
245}
246
247#[derive(Default)]
248pub struct ClientManager {
249    focused_client: Option<ClientHandle>,
250    previous_focused_client: Option<ClientHandle>,
251    clients: Vec<Client>,
252}
253
254impl ClientManager {
255    pub fn focused_client(&self) -> Option<ClientHandle> {
256        self.focused_client
257    }
258
259    pub fn previous_focused_client(&self) -> Option<ClientHandle> {
260        self.previous_focused_client
261    }
262
263    pub fn focus_client(&mut self, handle: ClientHandle) -> bool {
264        let client = self.get(handle);
265        if !client.has_ui() {
266            return false;
267        }
268
269        let changed = Some(handle) != self.focused_client;
270        if changed {
271            self.previous_focused_client = self.focused_client;
272        }
273        self.focused_client = Some(handle);
274        changed
275    }
276
277    pub fn get(&self, handle: ClientHandle) -> &Client {
278        &self.clients[handle.0 as usize]
279    }
280
281    pub fn get_mut(&mut self, handle: ClientHandle) -> &mut Client {
282        &mut self.clients[handle.0 as usize]
283    }
284
285    pub fn iter(&self) -> impl Clone + Iterator<Item = &Client> {
286        self.clients.iter().filter(|c| c.active)
287    }
288
289    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Client> {
290        self.clients.iter_mut().filter(|c| c.active)
291    }
292
293    pub(crate) fn on_client_joined(&mut self, handle: ClientHandle) {
294        let min_len = handle.0 as usize + 1;
295        if min_len > self.clients.len() {
296            self.clients.resize_with(min_len, Client::new);
297        }
298
299        let client = &mut self.clients[handle.0 as usize];
300        client.active = true;
301        client.handle = handle;
302    }
303
304    pub(crate) fn on_client_left(&mut self, handle: ClientHandle) {
305        self.clients[handle.0 as usize].dispose();
306        if self.focused_client == Some(handle) {
307            self.focused_client = None;
308        }
309    }
310}