Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:34:48.547Z 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

change_list.rs

221 lines · 6.6 KB · rust
1use editor::{Bias, Direction, Editor, display_map::ToDisplayPoint, movement};
2use gpui::{Context, Window, actions};
3
4use crate::{Vim, state::Mode};
5
6actions!(
7    vim,
8    [
9        /// Navigates to an older position in the change list.
10        ChangeListOlder,
11        /// Navigates to a newer position in the change list.
12        ChangeListNewer
13    ]
14);
15
16pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
17    Vim::action(editor, cx, |vim, _: &ChangeListOlder, window, cx| {
18        vim.move_to_change(Direction::Prev, window, cx);
19    });
20    Vim::action(editor, cx, |vim, _: &ChangeListNewer, window, cx| {
21        vim.move_to_change(Direction::Next, window, cx);
22    });
23}
24
25impl Vim {
26    fn move_to_change(
27        &mut self,
28        direction: Direction,
29        window: &mut Window,
30        cx: &mut Context<Self>,
31    ) {
32        let count = Vim::take_count(cx).unwrap_or(1);
33        Vim::take_forced_motion(cx);
34        self.update_editor(cx, |_, editor, cx| {
35            if let Some(selections) = editor
36                .change_list
37                .next_change(count, direction)
38                .map(|s| s.to_vec())
39            {
40                editor.change_selections(Default::default(), window, cx, |s| {
41                    let map = s.display_snapshot();
42                    s.select_display_ranges(selections.iter().map(|a| {
43                        let point = a.to_display_point(&map);
44                        point..point
45                    }))
46                })
47            };
48        });
49    }
50
51    pub(crate) fn push_to_change_list(&mut self, window: &mut Window, cx: &mut Context<Self>) {
52        let Some((new_positions, buffer)) = self.update_editor(cx, |vim, editor, cx| {
53            let display_map = editor.display_snapshot(cx);
54            let selections = editor.selections.all_adjusted_display(&display_map);
55            let buffer = editor.buffer().clone();
56
57            let pop_state = editor
58                .change_list
59                .last()
60                .map(|previous| {
61                    previous.len() == selections.len()
62                        && previous.iter().enumerate().all(|(ix, p)| {
63                            p.to_display_point(&display_map).row() == selections[ix].head().row()
64                        })
65                })
66                .unwrap_or(false);
67
68            let new_positions = selections
69                .into_iter()
70                .map(|s| {
71                    let point = if vim.mode == Mode::Insert {
72                        movement::saturating_left(&display_map, s.head())
73                    } else {
74                        s.head()
75                    };
76                    display_map.display_point_to_anchor(point, Bias::Left)
77                })
78                .collect::<Vec<_>>();
79
80            editor
81                .change_list
82                .push_to_change_list(pop_state, new_positions.clone());
83
84            (new_positions, buffer)
85        }) else {
86            return;
87        };
88
89        self.set_mark(".".to_string(), new_positions, &buffer, window, cx)
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use indoc::indoc;
96
97    use crate::{state::Mode, test::NeovimBackedTestContext};
98
99    #[gpui::test]
100    async fn test_change_list_insert(cx: &mut gpui::TestAppContext) {
101        let mut cx = NeovimBackedTestContext::new(cx).await;
102
103        cx.set_shared_state("ˇ").await;
104
105        cx.simulate_shared_keystrokes("i 1 1 escape shift-o 2 2 escape shift-g o 3 3 escape")
106            .await;
107
108        cx.shared_state().await.assert_eq(indoc! {
109            "22
110             11
111             3ˇ3"
112        });
113
114        cx.simulate_shared_keystrokes("g ;").await;
115        // NOTE: this matches nvim when I type it into it
116        // but in tests, nvim always reports the column as 0...
117        cx.assert_state(
118            indoc! {
119            "22
120             11
121             3ˇ3"
122            },
123            Mode::Normal,
124        );
125        cx.simulate_shared_keystrokes("g ;").await;
126        cx.assert_state(
127            indoc! {
128            "2ˇ2
129             11
130             33"
131            },
132            Mode::Normal,
133        );
134        cx.simulate_shared_keystrokes("g ;").await;
135        cx.assert_state(
136            indoc! {
137            "22
138             1ˇ1
139             33"
140            },
141            Mode::Normal,
142        );
143        cx.simulate_shared_keystrokes("g ,").await;
144        cx.assert_state(
145            indoc! {
146            "2ˇ2
147             11
148             33"
149            },
150            Mode::Normal,
151        );
152        cx.simulate_shared_keystrokes("shift-g i 4 4 escape").await;
153        cx.simulate_shared_keystrokes("g ;").await;
154        cx.assert_state(
155            indoc! {
156            "22
157             11
158             34ˇ43"
159            },
160            Mode::Normal,
161        );
162        cx.simulate_shared_keystrokes("g ;").await;
163        cx.assert_state(
164            indoc! {
165            "2ˇ2
166             11
167             3443"
168            },
169            Mode::Normal,
170        );
171    }
172
173    #[gpui::test]
174    async fn test_change_list_delete(cx: &mut gpui::TestAppContext) {
175        let mut cx = NeovimBackedTestContext::new(cx).await;
176        cx.set_shared_state(indoc! {
177        "one two
178        three fˇour"})
179            .await;
180        cx.simulate_shared_keystrokes("x k d i w ^ x").await;
181        cx.shared_state().await.assert_eq(indoc! {
182        "ˇne•
183        three fur"});
184        cx.simulate_shared_keystrokes("2 g ;").await;
185        cx.shared_state().await.assert_eq(indoc! {
186        "ne•
187        three fˇur"});
188        cx.simulate_shared_keystrokes("g ,").await;
189        cx.shared_state().await.assert_eq(indoc! {
190        "ˇne•
191        three fur"});
192    }
193
194    #[gpui::test]
195    async fn test_gi(cx: &mut gpui::TestAppContext) {
196        let mut cx = NeovimBackedTestContext::new(cx).await;
197        cx.set_shared_state(indoc! {
198        "one two
199        three fˇr"})
200            .await;
201        cx.simulate_shared_keystrokes("i o escape k g i").await;
202        cx.simulate_shared_keystrokes("u escape").await;
203        cx.shared_state().await.assert_eq(indoc! {
204        "one two
205        three foˇur"});
206    }
207
208    #[gpui::test]
209    async fn test_dot_mark(cx: &mut gpui::TestAppContext) {
210        let mut cx = NeovimBackedTestContext::new(cx).await;
211        cx.set_shared_state(indoc! {
212        "one two
213        three fˇr"})
214            .await;
215        cx.simulate_shared_keystrokes("i o escape k ` .").await;
216        cx.shared_state().await.assert_eq(indoc! {
217        "one two
218        three fˇor"});
219    }
220}
221
Served at tenant.openagents/omega Member data and write actions are omitted.