Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:40:00.104Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

object.rs

183 lines · 5.4 KB · rust
1use std::{
2    error::Error,
3    fmt::{self, Display},
4    ops::Range,
5};
6
7use editor::{DisplayPoint, display_map::DisplaySnapshot, movement};
8use text::Selection;
9
10use crate::{
11    helix::boundary::{FuzzyBoundary, ImmediateBoundary},
12    object::Object as VimObject,
13};
14
15/// A text object from helix or an extra one
16pub trait HelixTextObject {
17    fn range(
18        &self,
19        map: &DisplaySnapshot,
20        relative_to: Range<DisplayPoint>,
21        around: bool,
22    ) -> Option<Range<DisplayPoint>>;
23
24    fn next_range(
25        &self,
26        map: &DisplaySnapshot,
27        relative_to: Range<DisplayPoint>,
28        around: bool,
29    ) -> Option<Range<DisplayPoint>>;
30
31    fn previous_range(
32        &self,
33        map: &DisplaySnapshot,
34        relative_to: Range<DisplayPoint>,
35        around: bool,
36    ) -> Option<Range<DisplayPoint>>;
37}
38
39impl VimObject {
40    /// Returns the range of the object the cursor is over.
41    /// Follows helix convention.
42    pub fn helix_range(
43        self,
44        map: &DisplaySnapshot,
45        selection: Selection<DisplayPoint>,
46        around: bool,
47    ) -> Result<Option<Range<DisplayPoint>>, VimToHelixError> {
48        let cursor = cursor_range(&selection, map);
49        if let Some(helix_object) = self.to_helix_object() {
50            Ok(helix_object.range(map, cursor, around))
51        } else {
52            Err(VimToHelixError)
53        }
54    }
55    /// Returns the range of the next object the cursor is not over.
56    /// Follows helix convention.
57    pub fn helix_next_range(
58        self,
59        map: &DisplaySnapshot,
60        selection: Selection<DisplayPoint>,
61        around: bool,
62    ) -> Result<Option<Range<DisplayPoint>>, VimToHelixError> {
63        let cursor = cursor_range(&selection, map);
64        if let Some(helix_object) = self.to_helix_object() {
65            Ok(helix_object.next_range(map, cursor, around))
66        } else {
67            Err(VimToHelixError)
68        }
69    }
70    /// Returns the range of the previous object the cursor is not over.
71    /// Follows helix convention.
72    pub fn helix_previous_range(
73        self,
74        map: &DisplaySnapshot,
75        selection: Selection<DisplayPoint>,
76        around: bool,
77    ) -> Result<Option<Range<DisplayPoint>>, VimToHelixError> {
78        let cursor = cursor_range(&selection, map);
79        if let Some(helix_object) = self.to_helix_object() {
80            Ok(helix_object.previous_range(map, cursor, around))
81        } else {
82            Err(VimToHelixError)
83        }
84    }
85}
86
87#[derive(Debug)]
88pub struct VimToHelixError;
89impl Display for VimToHelixError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(
92            f,
93            "Not all vim text objects have an implemented helix equivalent"
94        )
95    }
96}
97impl Error for VimToHelixError {}
98
99impl VimObject {
100    fn to_helix_object(self) -> Option<Box<dyn HelixTextObject>> {
101        Some(match self {
102            Self::AngleBrackets => Box::new(ImmediateBoundary::AngleBrackets),
103            Self::BackQuotes => Box::new(ImmediateBoundary::BackQuotes),
104            Self::CurlyBrackets => Box::new(ImmediateBoundary::CurlyBrackets),
105            Self::DoubleQuotes => Box::new(ImmediateBoundary::DoubleQuotes),
106            Self::Paragraph => Box::new(FuzzyBoundary::Paragraph),
107            Self::Parentheses => Box::new(ImmediateBoundary::Parentheses),
108            Self::Quotes => Box::new(ImmediateBoundary::SingleQuotes),
109            Self::Sentence => Box::new(FuzzyBoundary::Sentence),
110            Self::SquareBrackets => Box::new(ImmediateBoundary::SquareBrackets),
111            Self::Subword { ignore_punctuation } => {
112                Box::new(ImmediateBoundary::Subword { ignore_punctuation })
113            }
114            Self::VerticalBars => Box::new(ImmediateBoundary::VerticalBars),
115            Self::Word { ignore_punctuation } => {
116                Box::new(ImmediateBoundary::Word { ignore_punctuation })
117            }
118            _ => return None,
119        })
120    }
121}
122
123/// Returns the start of the cursor of a selection, whether that is collapsed or not.
124pub(crate) fn cursor_range(
125    selection: &Selection<DisplayPoint>,
126    map: &DisplaySnapshot,
127) -> Range<DisplayPoint> {
128    if selection.is_empty() | selection.reversed {
129        selection.head()..movement::right(map, selection.head())
130    } else {
131        movement::left(map, selection.head())..selection.head()
132    }
133}
134
135#[cfg(test)]
136mod test {
137    use db::indoc;
138
139    use crate::{state::Mode, test::VimTestContext};
140
141    #[gpui::test]
142    async fn test_select_word_object(cx: &mut gpui::TestAppContext) {
143        let mut cx = VimTestContext::new(cx, true).await;
144        let start = indoc! {"
145                The quick brˇowˇnˇ
146                fox «ˇjumps» ov«er
147                the laˇ»zy dogˇ
148
149                "
150        };
151
152        cx.set_state(start, Mode::HelixNormal);
153
154        cx.simulate_keystrokes("m i w");
155
156        cx.assert_state(
157            indoc! {"
158            The quick «brownˇ»
159            fox «jumpsˇ» over
160            the «lazyˇ» dogˇ
161
162            "
163            },
164            Mode::HelixNormal,
165        );
166
167        cx.set_state(start, Mode::HelixNormal);
168
169        cx.simulate_keystrokes("m a w");
170
171        cx.assert_state(
172            indoc! {"
173            The quick« brownˇ»
174            fox «jumps ˇ»over
175            the «lazy ˇ»dogˇ
176
177            "
178            },
179            Mode::HelixNormal,
180        );
181    }
182}
183
Served at tenant.openagents/omega Member data and write actions are omitted.