Make matching vim objects in helix mode fallible

This commit is contained in:
fantacell 2025-08-21 13:23:09 +02:00
parent 320b715dcb
commit c602172edb
2 changed files with 47 additions and 21 deletions

View file

@ -1,4 +1,8 @@
use std::ops::Range; use std::{
error::Error,
fmt::{self, Display},
ops::Range,
};
use editor::{DisplayPoint, display_map::DisplaySnapshot, movement}; use editor::{DisplayPoint, display_map::DisplaySnapshot, movement};
use text::Selection; use text::Selection;
@ -40,18 +44,12 @@ impl VimObject {
map: &DisplaySnapshot, map: &DisplaySnapshot,
selection: Selection<DisplayPoint>, selection: Selection<DisplayPoint>,
around: bool, around: bool,
) -> Option<Range<DisplayPoint>> { ) -> Result<Option<Range<DisplayPoint>>, VimToHelixError> {
let cursor = cursor_range(&selection, map); let cursor = cursor_range(&selection, map);
if let Some(helix_object) = self.to_helix_object() { if let Some(helix_object) = self.to_helix_object() {
helix_object.range(map, cursor, around) Ok(helix_object.range(map, cursor, around))
} else { } else {
let range = self.range(map, selection, around, None)?; Err(VimToHelixError)
if range.start > cursor.start {
None
} else {
Some(range)
}
} }
} }
/// Returns the range of the next object the cursor is not over. /// Returns the range of the next object the cursor is not over.
@ -61,10 +59,13 @@ impl VimObject {
map: &DisplaySnapshot, map: &DisplaySnapshot,
selection: Selection<DisplayPoint>, selection: Selection<DisplayPoint>,
around: bool, around: bool,
) -> Option<Range<DisplayPoint>> { ) -> Result<Option<Range<DisplayPoint>>, VimToHelixError> {
let cursor = cursor_range(&selection, map); let cursor = cursor_range(&selection, map);
let helix_object = self.to_helix_object()?; if let Some(helix_object) = self.to_helix_object() {
helix_object.next_range(map, cursor, around) Ok(helix_object.next_range(map, cursor, around))
} else {
Err(VimToHelixError)
}
} }
/// Returns the range of the previous object the cursor is not over. /// Returns the range of the previous object the cursor is not over.
/// Follows helix convention. /// Follows helix convention.
@ -73,13 +74,28 @@ impl VimObject {
map: &DisplaySnapshot, map: &DisplaySnapshot,
selection: Selection<DisplayPoint>, selection: Selection<DisplayPoint>,
around: bool, around: bool,
) -> Option<Range<DisplayPoint>> { ) -> Result<Option<Range<DisplayPoint>>, VimToHelixError> {
let cursor = cursor_range(&selection, map); let cursor = cursor_range(&selection, map);
let helix_object = self.to_helix_object()?; if let Some(helix_object) = self.to_helix_object() {
helix_object.previous_range(map, cursor, around) Ok(helix_object.previous_range(map, cursor, around))
} else {
Err(VimToHelixError)
}
} }
} }
#[derive(Debug)]
pub struct VimToHelixError;
impl Display for VimToHelixError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Not all vim text objects have an implemented helix equivalent"
)
}
}
impl Error for VimToHelixError {}
impl VimObject { impl VimObject {
fn to_helix_object(self) -> Option<Box<dyn HelixTextObject>> { fn to_helix_object(self) -> Option<Box<dyn HelixTextObject>> {
Some(match self { Some(match self {
@ -105,7 +121,10 @@ impl VimObject {
} }
/// Returns the start of the cursor of a selection, whether that is collapsed or not. /// Returns the start of the cursor of a selection, whether that is collapsed or not.
fn cursor_range(selection: &Selection<DisplayPoint>, map: &DisplaySnapshot) -> Range<DisplayPoint> { pub(crate) fn cursor_range(
selection: &Selection<DisplayPoint>,
map: &DisplaySnapshot,
) -> Range<DisplayPoint> {
if selection.is_empty() | selection.reversed { if selection.is_empty() | selection.reversed {
selection.head()..movement::right(map, selection.head()) selection.head()..movement::right(map, selection.head())
} else { } else {

View file

@ -1,7 +1,7 @@
use text::SelectionGoal; use text::SelectionGoal;
use ui::{Context, Window}; use ui::{Context, Window};
use crate::{Vim, object::Object}; use crate::{Vim, helix::object::cursor_range, object::Object};
impl Vim { impl Vim {
/// Selects the object each cursor is over. /// Selects the object each cursor is over.
@ -17,7 +17,13 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| { self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Default::default(), window, cx, |s| { editor.change_selections(Default::default(), window, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
let Some(range) = object.helix_range(map, selection.clone(), around) else { let Some(range) = object
.helix_range(map, selection.clone(), around)
.unwrap_or({
let vim_range = object.range(map, selection.clone(), around, None);
vim_range.filter(|r| r.start <= cursor_range(selection, map).start)
})
else {
return; return;
}; };
@ -40,7 +46,7 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| { self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Default::default(), window, cx, |s| { editor.change_selections(Default::default(), window, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
let Some(range) = object.helix_next_range(map, selection.clone(), around) let Ok(Some(range)) = object.helix_next_range(map, selection.clone(), around)
else { else {
return; return;
}; };
@ -64,7 +70,8 @@ impl Vim {
self.update_editor(cx, |_, editor, cx| { self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Default::default(), window, cx, |s| { editor.change_selections(Default::default(), window, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
let Some(range) = object.helix_previous_range(map, selection.clone(), around) let Ok(Some(range)) =
object.helix_previous_range(map, selection.clone(), around)
else { else {
return; return;
}; };