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.rs138
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(..);