floem_editor_core/
cursor.rs

1use lapce_xi_rope::{RopeDelta, Transformer};
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    buffer::{rope_text::RopeText, Buffer},
7    mode::{Mode, MotionMode, VisualMode},
8    register::RegisterData,
9    selection::{InsertDrift, SelRegion, Selection},
10};
11
12#[derive(Clone, Copy, PartialEq, Debug)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub enum ColPosition {
15    FirstNonBlank,
16    Start,
17    End,
18    Col(f64),
19}
20
21#[derive(Clone, Debug, PartialEq)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub struct Cursor {
24    pub mode: CursorMode,
25    pub horiz: Option<ColPosition>,
26    pub motion_mode: Option<MotionMode>,
27    pub history_selections: Vec<Selection>,
28    pub affinity: CursorAffinity,
29}
30
31#[derive(Clone, Debug, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub enum CursorMode {
34    Normal(usize),
35    Visual {
36        start: usize,
37        end: usize,
38        mode: VisualMode,
39    },
40    Insert(Selection),
41}
42
43struct RegionsIter<'c> {
44    cursor_mode: &'c CursorMode,
45    idx: usize,
46}
47
48impl Iterator for RegionsIter<'_> {
49    type Item = (usize, usize);
50
51    fn next(&mut self) -> Option<Self::Item> {
52        match self.cursor_mode {
53            &CursorMode::Normal(offset) => (self.idx == 0).then(|| {
54                self.idx = 1;
55                (offset, offset)
56            }),
57            &CursorMode::Visual { start, end, .. } => (self.idx == 0).then(|| {
58                self.idx = 1;
59                (start, end)
60            }),
61            CursorMode::Insert(selection) => {
62                let next = selection
63                    .regions()
64                    .get(self.idx)
65                    .map(|&SelRegion { start, end, .. }| (start, end));
66
67                if next.is_some() {
68                    self.idx += 1;
69                }
70
71                next
72            }
73        }
74    }
75
76    fn size_hint(&self) -> (usize, Option<usize>) {
77        let total_len = match self.cursor_mode {
78            CursorMode::Normal(_) | CursorMode::Visual { .. } => 1,
79            CursorMode::Insert(selection) => selection.len(),
80        };
81        let len = total_len - self.idx;
82
83        (len, Some(len))
84    }
85}
86
87impl ExactSizeIterator for RegionsIter<'_> {}
88
89impl CursorMode {
90    pub fn offset(&self) -> usize {
91        match &self {
92            CursorMode::Normal(offset) => *offset,
93            CursorMode::Visual { end, .. } => *end,
94            CursorMode::Insert(selection) => selection.get_cursor_offset(),
95        }
96    }
97
98    pub fn start_offset(&self) -> usize {
99        match &self {
100            CursorMode::Normal(offset) => *offset,
101            CursorMode::Visual { start, .. } => *start,
102            CursorMode::Insert(selection) => selection.first().map(|s| s.start).unwrap_or(0),
103        }
104    }
105
106    pub fn regions_iter(&self) -> impl ExactSizeIterator<Item = (usize, usize)> + '_ {
107        RegionsIter {
108            cursor_mode: self,
109            idx: 0,
110        }
111    }
112}
113
114/// Decides how the cursor should be placed around special areas of text.  
115/// Ex:
116/// ```rust,ignore
117/// let j =            // soft linewrap
118/// 1 + 2 + 3;
119/// ```
120/// where `let j = ` has the issue that there's two positions you might want your cursor to be:  
121/// `let j = |` or `|1 + 2 + 3;`  
122/// These are the same offset in the text, but it feels more natural to have it move in a certain
123/// way.  
124/// If you're at `let j =| ` and you press the right-arrow key, then it uses your backwards
125/// affinity to keep you on the line at `let j = |`.  
126/// If you're at `1| + 2 + 3;` and you press the left-arrow key, then it uses your forwards affinity
127/// to keep you on the line at `|1 + 2 + 3;`.  
128///
129/// For other special text, like inlay hints, this can also apply.  
130/// ```rust,ignore
131/// let j<: String> = ...
132/// ```
133/// where `<: String>` is our inlay hint, then  
134/// `let |j<: String> =` and you press the right-arrow key, then it uses your backwards affinity to
135/// keep you on the same side of the hint, `let j|<: String>`.  
136/// `let j<: String> |=` and you press the right-arrow key, then it uses your forwards affinity to
137/// keep you on the same side of the hint, `let j<: String>| =`.
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140pub enum CursorAffinity {
141    /// `<: String>|`
142    Forward,
143    /// `|<: String>`
144    Backward,
145}
146impl CursorAffinity {
147    pub fn invert(&self) -> Self {
148        match self {
149            CursorAffinity::Forward => CursorAffinity::Backward,
150            CursorAffinity::Backward => CursorAffinity::Forward,
151        }
152    }
153}
154
155impl Cursor {
156    pub fn new(
157        mode: CursorMode,
158        horiz: Option<ColPosition>,
159        motion_mode: Option<MotionMode>,
160    ) -> Self {
161        Self {
162            mode,
163            horiz,
164            motion_mode,
165            history_selections: Vec::new(),
166            // It should appear before any inlay hints at the very first position
167            affinity: CursorAffinity::Backward,
168        }
169    }
170
171    pub fn origin(modal: bool) -> Self {
172        Self::new(
173            if modal {
174                CursorMode::Normal(0)
175            } else {
176                CursorMode::Insert(Selection::caret(0))
177            },
178            None,
179            None,
180        )
181    }
182
183    pub fn offset(&self) -> usize {
184        self.mode.offset()
185    }
186
187    pub fn start_offset(&self) -> usize {
188        self.mode.start_offset()
189    }
190
191    pub fn regions_iter(&self) -> impl ExactSizeIterator<Item = (usize, usize)> + '_ {
192        self.mode.regions_iter()
193    }
194
195    pub fn is_normal(&self) -> bool {
196        matches!(&self.mode, CursorMode::Normal(_))
197    }
198
199    pub fn is_insert(&self) -> bool {
200        matches!(&self.mode, CursorMode::Insert(_))
201    }
202
203    pub fn is_visual(&self) -> bool {
204        matches!(&self.mode, CursorMode::Visual { .. })
205    }
206
207    pub fn get_mode(&self) -> Mode {
208        match &self.mode {
209            CursorMode::Normal(_) => Mode::Normal,
210            CursorMode::Visual { mode, .. } => Mode::Visual(*mode),
211            CursorMode::Insert(_) => Mode::Insert,
212        }
213    }
214
215    pub fn set_mode(&mut self, mode: CursorMode) {
216        if let CursorMode::Insert(selection) = &self.mode {
217            self.history_selections.push(selection.clone());
218        }
219        self.mode = mode;
220    }
221
222    pub fn set_insert(&mut self, selection: Selection) {
223        self.set_mode(CursorMode::Insert(selection));
224    }
225
226    pub fn update_selection(&mut self, buffer: &Buffer, selection: Selection) {
227        match self.mode {
228            CursorMode::Normal(_) | CursorMode::Visual { .. } => {
229                let offset = selection.min_offset();
230                let offset = buffer.offset_line_end(offset, false).min(offset);
231                self.mode = CursorMode::Normal(offset);
232            }
233            CursorMode::Insert(_) => {
234                self.mode = CursorMode::Insert(selection);
235            }
236        }
237    }
238
239    pub fn edit_selection(&self, text: &impl RopeText) -> Selection {
240        match &self.mode {
241            CursorMode::Insert(selection) => selection.clone(),
242            CursorMode::Normal(offset) => {
243                Selection::region(*offset, text.next_grapheme_offset(*offset, 1, text.len()))
244            }
245            CursorMode::Visual { start, end, mode } => match mode {
246                VisualMode::Normal => Selection::region(
247                    *start.min(end),
248                    text.next_grapheme_offset(*start.max(end), 1, text.len()),
249                ),
250                VisualMode::Linewise => {
251                    let start_offset = text.offset_of_line(text.line_of_offset(*start.min(end)));
252                    let end_offset = text.offset_of_line(text.line_of_offset(*start.max(end)) + 1);
253                    Selection::region(start_offset, end_offset)
254                }
255                VisualMode::Blockwise => {
256                    let mut selection = Selection::new();
257                    let (start_line, start_col) = text.offset_to_line_col(*start.min(end));
258                    let (end_line, end_col) = text.offset_to_line_col(*start.max(end));
259                    let left = start_col.min(end_col);
260                    let right = start_col.max(end_col) + 1;
261                    for line in start_line..end_line + 1 {
262                        let max_col = text.line_end_col(line, true);
263                        if left > max_col {
264                            continue;
265                        }
266                        let right = match &self.horiz {
267                            Some(ColPosition::End) => max_col,
268                            _ => {
269                                if right > max_col {
270                                    max_col
271                                } else {
272                                    right
273                                }
274                            }
275                        };
276                        let left = text.offset_of_line_col(line, left);
277                        let right = text.offset_of_line_col(line, right);
278                        selection.add_region(SelRegion::new(left, right, None));
279                    }
280                    selection
281                }
282            },
283        }
284    }
285
286    pub fn apply_delta(&mut self, delta: &RopeDelta) {
287        match &self.mode {
288            CursorMode::Normal(offset) => {
289                let mut transformer = Transformer::new(delta);
290                let new_offset = transformer.transform(*offset, true);
291                self.mode = CursorMode::Normal(new_offset);
292            }
293            CursorMode::Visual { start, end, mode } => {
294                let mut transformer = Transformer::new(delta);
295                let start = transformer.transform(*start, false);
296                let end = transformer.transform(*end, true);
297                self.mode = CursorMode::Visual {
298                    start,
299                    end,
300                    mode: *mode,
301                };
302            }
303            CursorMode::Insert(selection) => {
304                let selection = selection.apply_delta(delta, true, InsertDrift::Default);
305                self.mode = CursorMode::Insert(selection);
306            }
307        }
308        self.horiz = None;
309    }
310
311    pub fn yank(&self, text: &impl RopeText) -> RegisterData {
312        let (content, mode) = match &self.mode {
313            CursorMode::Insert(selection) => {
314                let mut mode = VisualMode::Normal;
315                let mut content = "".to_string();
316                for region in selection.regions() {
317                    let region_content = if region.is_caret() {
318                        mode = VisualMode::Linewise;
319                        let line = text.line_of_offset(region.start);
320                        text.line_content(line)
321                    } else {
322                        text.slice_to_cow(region.min()..region.max())
323                    };
324                    if content.is_empty() {
325                        content = region_content.to_string();
326                    } else if content.ends_with('\n') {
327                        content += &region_content;
328                    } else {
329                        content += "\n";
330                        content += &region_content;
331                    }
332                }
333                (content, mode)
334            }
335            CursorMode::Normal(offset) => {
336                let new_offset = text.next_grapheme_offset(*offset, 1, text.len());
337                (
338                    text.slice_to_cow(*offset..new_offset).to_string(),
339                    VisualMode::Normal,
340                )
341            }
342            CursorMode::Visual { start, end, mode } => match mode {
343                VisualMode::Normal => (
344                    text.slice_to_cow(
345                        *start.min(end)..text.next_grapheme_offset(*start.max(end), 1, text.len()),
346                    )
347                    .to_string(),
348                    VisualMode::Normal,
349                ),
350                VisualMode::Linewise => {
351                    let start_offset = text.offset_of_line(text.line_of_offset(*start.min(end)));
352                    let end_offset = text.offset_of_line(text.line_of_offset(*start.max(end)) + 1);
353                    (
354                        text.slice_to_cow(start_offset..end_offset).to_string(),
355                        VisualMode::Linewise,
356                    )
357                }
358                VisualMode::Blockwise => {
359                    let mut lines = Vec::new();
360                    let (start_line, start_col) = text.offset_to_line_col(*start.min(end));
361                    let (end_line, end_col) = text.offset_to_line_col(*start.max(end));
362                    let left = start_col.min(end_col);
363                    let right = start_col.max(end_col) + 1;
364                    for line in start_line..end_line + 1 {
365                        let max_col = text.line_end_col(line, true);
366                        if left > max_col {
367                            lines.push("".to_string());
368                        } else {
369                            let right = match &self.horiz {
370                                Some(ColPosition::End) => max_col,
371                                _ => {
372                                    if right > max_col {
373                                        max_col
374                                    } else {
375                                        right
376                                    }
377                                }
378                            };
379                            let left = text.offset_of_line_col(line, left);
380                            let right = text.offset_of_line_col(line, right);
381                            lines.push(text.slice_to_cow(left..right).to_string());
382                        }
383                    }
384                    (lines.join("\n") + "\n", VisualMode::Blockwise)
385                }
386            },
387        };
388        RegisterData { content, mode }
389    }
390
391    /// Return the current selection start and end position for a
392    /// Single cursor selection
393    pub fn get_selection(&self) -> Option<(usize, usize)> {
394        match &self.mode {
395            CursorMode::Visual {
396                start,
397                end,
398                mode: _,
399            } => Some((*start, *end)),
400            CursorMode::Insert(selection) => selection
401                .regions()
402                .first()
403                .map(|region| (region.start, region.end)),
404            _ => None,
405        }
406    }
407
408    pub fn get_line_col_char(&self, buffer: &Buffer) -> Option<(usize, usize, usize)> {
409        match &self.mode {
410            CursorMode::Normal(offset) => {
411                let ln_col = buffer.offset_to_line_col(*offset);
412                Some((ln_col.0, ln_col.1, *offset))
413            }
414            CursorMode::Visual {
415                start,
416                end,
417                mode: _,
418            } => {
419                let v = buffer.offset_to_line_col(*start.min(end));
420                Some((v.0, v.1, *start))
421            }
422            CursorMode::Insert(selection) => {
423                if selection.regions().len() > 1 {
424                    return None;
425                }
426
427                let x = selection.regions().first().unwrap();
428                let v = buffer.offset_to_line_col(x.start);
429
430                Some((v.0, v.1, x.start))
431            }
432        }
433    }
434
435    pub fn get_selection_count(&self) -> usize {
436        match &self.mode {
437            CursorMode::Insert(selection) => selection.regions().len(),
438            _ => 0,
439        }
440    }
441
442    pub fn set_offset(&mut self, offset: usize, modify: bool, new_cursor: bool) {
443        match &self.mode {
444            CursorMode::Normal(old_offset) => {
445                if modify && *old_offset != offset {
446                    self.mode = CursorMode::Visual {
447                        start: *old_offset,
448                        end: offset,
449                        mode: VisualMode::Normal,
450                    };
451                } else {
452                    self.mode = CursorMode::Normal(offset);
453                }
454            }
455            CursorMode::Visual {
456                start,
457                end: _,
458                mode: _,
459            } => {
460                if modify {
461                    self.mode = CursorMode::Visual {
462                        start: *start,
463                        end: offset,
464                        mode: VisualMode::Normal,
465                    };
466                } else {
467                    self.mode = CursorMode::Normal(offset);
468                }
469            }
470            CursorMode::Insert(selection) => {
471                if new_cursor {
472                    let mut new_selection = selection.clone();
473                    if modify {
474                        if let Some(region) = new_selection.last_inserted_mut() {
475                            region.end = offset;
476                        } else {
477                            new_selection.add_region(SelRegion::caret(offset));
478                        }
479                        self.set_insert(new_selection);
480                    } else {
481                        let mut new_selection = selection.clone();
482                        new_selection.add_region(SelRegion::caret(offset));
483                        self.set_insert(new_selection);
484                    }
485                } else if modify {
486                    let mut new_selection = Selection::new();
487                    if let Some(region) = selection.first() {
488                        let new_region = SelRegion::new(region.start, offset, None);
489                        new_selection.add_region(new_region);
490                    } else {
491                        new_selection.add_region(SelRegion::new(offset, offset, None));
492                    }
493                    self.set_insert(new_selection);
494                } else {
495                    self.set_insert(Selection::caret(offset));
496                }
497            }
498        }
499    }
500
501    pub fn add_region(&mut self, start: usize, end: usize, modify: bool, new_cursor: bool) {
502        match &self.mode {
503            CursorMode::Normal(_offset) => {
504                self.mode = CursorMode::Visual {
505                    start,
506                    end: end - 1,
507                    mode: VisualMode::Normal,
508                };
509            }
510            CursorMode::Visual {
511                start: old_start,
512                end: old_end,
513                mode: _,
514            } => {
515                let forward = old_end >= old_start;
516                let new_start = (*old_start).min(*old_end).min(start).min(end - 1);
517                let new_end = (*old_start).max(*old_end).max(start).max(end - 1);
518                let (new_start, new_end) = if forward {
519                    (new_start, new_end)
520                } else {
521                    (new_end, new_start)
522                };
523                self.mode = CursorMode::Visual {
524                    start: new_start,
525                    end: new_end,
526                    mode: VisualMode::Normal,
527                };
528            }
529            CursorMode::Insert(selection) => {
530                let new_selection = if new_cursor {
531                    let mut new_selection = selection.clone();
532                    if modify {
533                        let new_region = if let Some(last_inserted) = selection.last_inserted() {
534                            last_inserted.merge_with(SelRegion::new(start, end, None))
535                        } else {
536                            SelRegion::new(start, end, None)
537                        };
538                        new_selection.replace_last_inserted_region(new_region);
539                    } else {
540                        new_selection.add_region(SelRegion::new(start, end, None));
541                    }
542                    new_selection
543                } else if modify {
544                    let mut new_selection = selection.clone();
545                    new_selection.add_region(SelRegion::new(start, end, None));
546                    new_selection
547                } else {
548                    Selection::region(start, end)
549                };
550                self.mode = CursorMode::Insert(new_selection);
551            }
552        }
553    }
554}
555
556pub fn get_first_selection_after(
557    cursor: &Cursor,
558    buffer: &Buffer,
559    delta: &RopeDelta,
560) -> Option<Cursor> {
561    let mut transformer = Transformer::new(delta);
562
563    let offset = cursor.offset();
564    let offset = transformer.transform(offset, false);
565    let (ins, del) = delta.clone().factor();
566    let ins = ins.transform_shrink(&del);
567    for el in ins.els.iter() {
568        match el {
569            lapce_xi_rope::DeltaElement::Copy(b, e) => {
570                // if b == e, ins.inserted_subset() will panic
571                if b == e {
572                    return None;
573                }
574            }
575            lapce_xi_rope::DeltaElement::Insert(_) => {}
576        }
577    }
578
579    // TODO it's silly to store the whole thing in memory, we only need the first element.
580    let mut positions = ins
581        .inserted_subset()
582        .complement_iter()
583        .map(|s| s.1)
584        .collect::<Vec<usize>>();
585    positions.append(
586        &mut del
587            .complement_iter()
588            .map(|s| transformer.transform(s.1, false))
589            .collect::<Vec<usize>>(),
590    );
591    positions.sort_by_key(|p| {
592        let p = *p as i32 - offset as i32;
593        if p > 0 {
594            p as usize
595        } else {
596            -p as usize
597        }
598    });
599
600    positions
601        .first()
602        .cloned()
603        .map(Selection::caret)
604        .map(|selection| {
605            let cursor_mode = match cursor.mode {
606                CursorMode::Normal(_) | CursorMode::Visual { .. } => {
607                    let offset = selection.min_offset();
608                    let offset = buffer.offset_line_end(offset, false).min(offset);
609                    CursorMode::Normal(offset)
610                }
611                CursorMode::Insert(_) => CursorMode::Insert(selection),
612            };
613
614            Cursor::new(cursor_mode, None, None)
615        })
616}