Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-core/src/position.rs')
| -rw-r--r-- | helix-core/src/position.rs | 138 |
1 files changed, 105 insertions, 33 deletions
diff --git a/helix-core/src/position.rs b/helix-core/src/position.rs index ee764bc6..1b378911 100644 --- a/helix-core/src/position.rs +++ b/helix-core/src/position.rs @@ -1,4 +1,8 @@ -use std::{borrow::Cow, cmp::Ordering}; +use std::{ + borrow::Cow, + cmp::Ordering, + ops::{Add, AddAssign, Sub, SubAssign}, +}; use crate::{ chars::char_is_line_ending, @@ -16,6 +20,38 @@ pub struct Position { pub col: usize, } +impl AddAssign for Position { + fn add_assign(&mut self, rhs: Self) { + self.row += rhs.row; + self.col += rhs.col; + } +} + +impl SubAssign for Position { + fn sub_assign(&mut self, rhs: Self) { + self.row -= rhs.row; + self.col -= rhs.col; + } +} + +impl Sub for Position { + type Output = Position; + + fn sub(mut self, rhs: Self) -> Self::Output { + self -= rhs; + self + } +} + +impl Add for Position { + type Output = Position; + + fn add(mut self, rhs: Self) -> Self::Output { + self += rhs; + self + } +} + impl Position { pub const fn new(row: usize, col: usize) -> Self { Self { row, col } @@ -121,22 +157,31 @@ pub fn visual_offset_from_block( annotations: &TextAnnotations, ) -> (Position, usize) { let mut last_pos = Position::default(); - let (formatter, block_start) = + let mut formatter = DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, annotations, anchor); - let mut char_pos = block_start; + let block_start = formatter.next_char_pos(); - for (grapheme, vpos) in formatter { - last_pos = vpos; - char_pos += grapheme.doc_chars(); - - if char_pos > pos { - return (last_pos, block_start); + while let Some(grapheme) = formatter.next() { + last_pos = grapheme.visual_pos; + if formatter.next_char_pos() > pos { + return (grapheme.visual_pos, block_start); } } (last_pos, block_start) } +/// Returns the height of the given text when softwrapping +pub fn softwrapped_dimensions(text: RopeSlice, text_fmt: &TextFormat) -> (usize, u16) { + let last_pos = + visual_offset_from_block(text, 0, usize::MAX, text_fmt, &TextAnnotations::default()).0; + if last_pos.row == 0 { + (1, last_pos.col as u16) + } else { + (last_pos.row + 1, text_fmt.viewport_width) + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum VisualOffsetError { PosBeforeAnchorRow, @@ -153,22 +198,21 @@ pub fn visual_offset_from_anchor( annotations: &TextAnnotations, max_rows: usize, ) -> Result<(Position, usize), VisualOffsetError> { - let (formatter, block_start) = + let mut formatter = DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, annotations, anchor); - let mut char_pos = block_start; let mut anchor_line = None; let mut found_pos = None; let mut last_pos = Position::default(); + let block_start = formatter.next_char_pos(); if pos < block_start { return Err(VisualOffsetError::PosBeforeAnchorRow); } - for (grapheme, vpos) in formatter { - last_pos = vpos; - char_pos += grapheme.doc_chars(); + while let Some(grapheme) = formatter.next() { + last_pos = grapheme.visual_pos; - if char_pos > pos { + if formatter.next_char_pos() > pos { if let Some(anchor_line) = anchor_line { last_pos.row -= anchor_line; return Ok((last_pos, block_start)); @@ -176,7 +220,7 @@ pub fn visual_offset_from_anchor( found_pos = Some(last_pos); } } - if char_pos > anchor && anchor_line.is_none() { + if formatter.next_char_pos() > anchor && anchor_line.is_none() { if let Some(mut found_pos) = found_pos { return if found_pos.row == last_pos.row { found_pos.row = 0; @@ -190,7 +234,7 @@ pub fn visual_offset_from_anchor( } if let Some(anchor_line) = anchor_line { - if vpos.row >= anchor_line + max_rows { + if grapheme.visual_pos.row >= anchor_line + max_rows { return Err(VisualOffsetError::PosAfterMaxRow); } } @@ -368,39 +412,43 @@ pub fn char_idx_at_visual_block_offset( text_fmt: &TextFormat, annotations: &TextAnnotations, ) -> (usize, usize) { - let (formatter, mut char_idx) = + let mut formatter = DocumentFormatter::new_at_prev_checkpoint(text, text_fmt, annotations, anchor); - let mut last_char_idx = char_idx; - let mut last_char_idx_on_line = None; + let mut last_char_idx = formatter.next_char_pos(); + let mut found_non_virtual_on_row = false; let mut last_row = 0; - for (grapheme, grapheme_pos) in formatter { - match grapheme_pos.row.cmp(&row) { + for grapheme in &mut formatter { + match grapheme.visual_pos.row.cmp(&row) { Ordering::Equal => { - if grapheme_pos.col + grapheme.width() > column { + if grapheme.visual_pos.col + grapheme.width() > column { if !grapheme.is_virtual() { - return (char_idx, 0); - } else if let Some(char_idx) = last_char_idx_on_line { - return (char_idx, 0); + return (grapheme.char_idx, 0); + } else if found_non_virtual_on_row { + return (last_char_idx, 0); } } else if !grapheme.is_virtual() { - last_char_idx_on_line = Some(char_idx) + found_non_virtual_on_row = true; + last_char_idx = grapheme.char_idx; } } + Ordering::Greater if found_non_virtual_on_row => return (last_char_idx, 0), Ordering::Greater => return (last_char_idx, row - last_row), - _ => (), + Ordering::Less => { + if !grapheme.is_virtual() { + last_row = grapheme.visual_pos.row; + last_char_idx = grapheme.char_idx; + } + } } - - last_char_idx = char_idx; - last_row = grapheme_pos.row; - char_idx += grapheme.doc_chars(); } - (char_idx, 0) + (formatter.next_char_pos(), 0) } #[cfg(test)] mod test { use super::*; + use crate::text_annotations::InlineAnnotation; use crate::Rope; #[test] @@ -762,6 +810,30 @@ mod test { } #[test] + fn test_char_idx_at_visual_row_offset_inline_annotation() { + let text = Rope::from("foo\nbar"); + let slice = text.slice(..); + let mut text_fmt = TextFormat::default(); + let annotations = [InlineAnnotation { + text: "x".repeat(100).into(), + char_idx: 3, + }]; + text_fmt.soft_wrap = true; + + assert_eq!( + char_idx_at_visual_offset( + slice, + 0, + 1, + 0, + &text_fmt, + TextAnnotations::default().add_inline_annotations(&annotations, None) + ), + (2, 1) + ); + } + + #[test] fn test_char_idx_at_visual_row_offset() { let text = Rope::from("ḧëḷḷö\nẅöṛḷḋ\nfoo"); let slice = text.slice(..); |