aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--helix-core/src/lib.rs2
-rw-r--r--helix-core/src/position.rs40
-rw-r--r--helix-view/src/view.rs76
3 files changed, 70 insertions, 48 deletions
diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs
index e3f862a6..4d50e48b 100644
--- a/helix-core/src/lib.rs
+++ b/helix-core/src/lib.rs
@@ -98,7 +98,7 @@ pub use {regex, tree_sitter};
pub use graphemes::RopeGraphemes;
pub use position::{
char_idx_at_visual_offset, coords_at_pos, pos_at_coords, visual_offset_from_anchor,
- visual_offset_from_block, Position,
+ visual_offset_from_block, Position, VisualOffsetError,
};
#[allow(deprecated)]
pub use position::{pos_at_visual_coords, visual_coords_at_pos};
diff --git a/helix-core/src/position.rs b/helix-core/src/position.rs
index 7b8dc326..c3233a34 100644
--- a/helix-core/src/position.rs
+++ b/helix-core/src/position.rs
@@ -137,6 +137,12 @@ pub fn visual_offset_from_block(
(last_pos, block_start)
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum VisualOffsetError {
+ PosBeforeAnchorRow,
+ PosAfterMaxRow,
+}
+
/// Returns the visual offset from the start of the visual line
/// that contains anchor.
pub fn visual_offset_from_anchor(
@@ -146,28 +152,46 @@ pub fn visual_offset_from_anchor(
text_fmt: &TextFormat,
annotations: &TextAnnotations,
max_rows: usize,
-) -> Option<(Position, usize)> {
+) -> Result<(Position, usize), VisualOffsetError> {
let (formatter, block_start) =
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();
+ if pos < block_start {
+ return Err(VisualOffsetError::PosBeforeAnchorRow);
+ }
+
for (grapheme, vpos) in formatter {
last_pos = vpos;
char_pos += grapheme.doc_chars();
- if char_pos > anchor && anchor_line.is_none() {
- anchor_line = Some(last_pos.row);
- }
if char_pos > pos {
- last_pos.row -= anchor_line.unwrap();
- return Some((last_pos, block_start));
+ if let Some(anchor_line) = anchor_line {
+ last_pos.row -= anchor_line;
+ return Ok((last_pos, block_start));
+ } else {
+ found_pos = Some(last_pos);
+ }
+ }
+ if 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;
+ Ok((found_pos, block_start))
+ } else {
+ Err(VisualOffsetError::PosBeforeAnchorRow)
+ };
+ } else {
+ anchor_line = Some(last_pos.row);
+ }
}
if let Some(anchor_line) = anchor_line {
if vpos.row >= anchor_line + max_rows {
- return None;
+ return Err(VisualOffsetError::PosAfterMaxRow);
}
}
}
@@ -175,7 +199,7 @@ pub fn visual_offset_from_anchor(
let anchor_line = anchor_line.unwrap_or(last_pos.row);
last_pos.row -= anchor_line;
- Some((last_pos, block_start))
+ Ok((last_pos, block_start))
}
/// Convert (line, column) coordinates to a character index.
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index 0ac7ca3b..ee6fc127 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -7,9 +7,13 @@ use crate::{
};
use helix_core::{
- char_idx_at_visual_offset, doc_formatter::TextFormat, syntax::Highlight,
- text_annotations::TextAnnotations, visual_offset_from_anchor, visual_offset_from_block,
- Position, RopeSlice, Selection, Transaction,
+ char_idx_at_visual_offset,
+ doc_formatter::TextFormat,
+ syntax::Highlight,
+ text_annotations::TextAnnotations,
+ visual_offset_from_anchor, visual_offset_from_block, Position, RopeSlice, Selection,
+ Transaction,
+ VisualOffsetError::{PosAfterMaxRow, PosBeforeAnchorRow},
};
use std::{
@@ -213,46 +217,38 @@ impl View {
// - 1 so we have at least one gap in the middle.
// a height of 6 with padding of 3 on each side will keep shifting the view back and forth
// as we type
- let scrolloff = scrolloff.min(viewport.height.saturating_sub(1) as usize / 2);
+ let scrolloff = if CENTERING {
+ 0
+ } else {
+ scrolloff.min(viewport.height.saturating_sub(1) as usize / 2)
+ };
let cursor = doc.selection(self.id).primary().cursor(doc_text);
let mut offset = self.offset;
+ let off = visual_offset_from_anchor(
+ doc_text,
+ offset.anchor,
+ cursor,
+ &text_fmt,
+ &annotations,
+ vertical_viewport_end,
+ );
- let (visual_off, mut at_top) = if cursor >= offset.anchor {
- let off = visual_offset_from_anchor(
- doc_text,
- offset.anchor,
- cursor,
- &text_fmt,
- &annotations,
- vertical_viewport_end,
- );
- (off, false)
- } else if CENTERING {
- // cursor out of view
- return None;
- } else {
- (None, true)
- };
-
- let new_anchor = match visual_off {
- Some((visual_pos, _)) if visual_pos.row < scrolloff + offset.vertical_offset => {
- if CENTERING && visual_pos.row < offset.vertical_offset {
+ let (new_anchor, at_top) = match off {
+ Ok((visual_pos, _)) if visual_pos.row < scrolloff + offset.vertical_offset => {
+ if CENTERING {
// cursor out of view
return None;
}
- at_top = true;
- true
+ (true, true)
}
- Some((visual_pos, _)) if visual_pos.row + scrolloff + 1 >= vertical_viewport_end => {
- if CENTERING && visual_pos.row >= vertical_viewport_end {
- // cursor out of view
- return None;
- }
- true
+ Ok((visual_pos, _)) if visual_pos.row + scrolloff >= vertical_viewport_end => {
+ (true, false)
}
- Some(_) => false,
- None => true,
+ Ok((_, _)) => (false, false),
+ Err(_) if CENTERING => return None,
+ Err(PosBeforeAnchorRow) => (true, true),
+ Err(PosAfterMaxRow) => (true, false),
};
if new_anchor {
@@ -269,8 +265,8 @@ impl View {
offset.horizontal_offset = 0;
} else {
// determine the current visual column of the text
- let col = visual_off
- .unwrap_or_else(|| {
+ let col = off
+ .unwrap_or_else(|_| {
visual_offset_from_block(
doc_text,
offset.anchor,
@@ -360,8 +356,9 @@ impl View {
);
match pos {
- Some((Position { row, .. }, _)) => row.saturating_sub(self.offset.vertical_offset),
- None => visual_height.saturating_sub(1),
+ Ok((Position { row, .. }, _)) => row.saturating_sub(self.offset.vertical_offset),
+ Err(PosAfterMaxRow) => visual_height.saturating_sub(1),
+ Err(PosBeforeAnchorRow) => 0,
}
}
@@ -390,7 +387,8 @@ impl View {
&text_fmt,
&annotations,
viewport.height as usize,
- )?
+ )
+ .ok()?
.0;
if pos.row < self.offset.vertical_offset {
return None;