Skip to repository content

tenant.openagents/omega

No repository description is available.

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

yank.rs

246 lines · 8.2 KB · rust
1use std::{ops::Range, time::Duration};
2
3use crate::{
4    Vim, VimSettings,
5    motion::{Motion, MotionKind},
6    object::Object,
7    state::{Mode, Register},
8};
9use collections::HashMap;
10use editor::{ClipboardSelection, Editor, HighlightKey, SelectionEffects};
11use gpui::Context;
12use gpui::Window;
13use language::Point;
14use settings::Settings;
15
16impl Vim {
17    pub fn yank_motion(
18        &mut self,
19        motion: Motion,
20        times: Option<usize>,
21        forced_motion: bool,
22        window: &mut Window,
23        cx: &mut Context<Self>,
24    ) {
25        self.update_editor(cx, |vim, editor, cx| {
26            let text_layout_details = editor.text_layout_details(window, cx);
27            editor.transact(window, cx, |editor, window, cx| {
28                editor.set_clip_at_line_ends(false, cx);
29                let mut original_positions: HashMap<_, _> = Default::default();
30                let mut kind = None;
31                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
32                    s.move_with(&mut |map, selection| {
33                        let original_position = (selection.head(), selection.goal);
34                        kind = motion.expand_selection(
35                            map,
36                            selection,
37                            times,
38                            &text_layout_details,
39                            forced_motion,
40                        );
41                        if kind == Some(MotionKind::Exclusive) {
42                            original_positions
43                                .insert(selection.id, (selection.start, selection.goal));
44                        } else {
45                            original_positions.insert(selection.id, original_position);
46                        }
47                    })
48                });
49                let Some(kind) = kind else { return };
50                vim.yank_selections_content(editor, kind, window, cx);
51                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
52                    s.move_with(&mut |_, selection| {
53                        let (head, goal) = original_positions.remove(&selection.id).unwrap();
54                        selection.collapse_to(head, goal);
55                    });
56                });
57            });
58        });
59        self.exit_temporary_normal(window, cx);
60    }
61
62    pub fn yank_object(
63        &mut self,
64        object: Object,
65        around: bool,
66        times: Option<usize>,
67        window: &mut Window,
68        cx: &mut Context<Self>,
69    ) {
70        self.update_editor(cx, |vim, editor, cx| {
71            editor.transact(window, cx, |editor, window, cx| {
72                editor.set_clip_at_line_ends(false, cx);
73                let mut start_positions: HashMap<_, _> = Default::default();
74                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
75                    s.move_with(&mut |map, selection| {
76                        object.expand_selection(map, selection, around, times);
77                        let start_position = (selection.start, selection.goal);
78                        start_positions.insert(selection.id, start_position);
79                    });
80                });
81                let kind = match object.target_visual_mode(vim.mode, around) {
82                    Mode::VisualLine => MotionKind::Linewise,
83                    _ => MotionKind::Exclusive,
84                };
85                vim.yank_selections_content(editor, kind, window, cx);
86                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
87                    s.move_with(&mut |_, selection| {
88                        let (head, goal) = start_positions.remove(&selection.id).unwrap();
89                        selection.collapse_to(head, goal);
90                    });
91                });
92            });
93        });
94        self.exit_temporary_normal(window, cx);
95    }
96
97    pub fn yank_selections_content(
98        &mut self,
99        editor: &mut Editor,
100        kind: MotionKind,
101        window: &mut Window,
102        cx: &mut Context<Editor>,
103    ) {
104        self.copy_ranges(
105            editor,
106            kind,
107            true,
108            editor
109                .selections
110                .all_adjusted(&editor.display_snapshot(cx))
111                .iter()
112                .map(|s| s.range())
113                .collect(),
114            window,
115            cx,
116        )
117    }
118
119    pub fn copy_selections_content(
120        &mut self,
121        editor: &mut Editor,
122        kind: MotionKind,
123        window: &mut Window,
124        cx: &mut Context<Editor>,
125    ) {
126        self.copy_ranges(
127            editor,
128            kind,
129            false,
130            editor
131                .selections
132                .all_adjusted(&editor.display_snapshot(cx))
133                .iter()
134                .map(|s| s.range())
135                .collect(),
136            window,
137            cx,
138        )
139    }
140
141    pub(crate) fn copy_ranges(
142        &mut self,
143        editor: &mut Editor,
144        kind: MotionKind,
145        is_yank: bool,
146        selections: Vec<Range<Point>>,
147        window: &mut Window,
148        cx: &mut Context<Editor>,
149    ) {
150        let buffer = editor.buffer().read(cx).snapshot(cx);
151        self.set_mark(
152            "[".to_string(),
153            selections
154                .iter()
155                .map(|s| buffer.anchor_before(s.start))
156                .collect(),
157            editor.buffer(),
158            window,
159            cx,
160        );
161        self.set_mark(
162            "]".to_string(),
163            selections
164                .iter()
165                .map(|s| buffer.anchor_after(s.end))
166                .collect(),
167            editor.buffer(),
168            window,
169            cx,
170        );
171
172        let mut text = String::new();
173        let mut clipboard_selections = Vec::with_capacity(selections.len());
174        let mut ranges_to_highlight = Vec::new();
175
176        {
177            let mut is_first = true;
178            for selection in selections.iter() {
179                let start = selection.start;
180                let end = selection.end;
181                if is_first {
182                    is_first = false;
183                } else {
184                    text.push('\n');
185                }
186                let initial_len = text.len();
187
188                let start_anchor = buffer.anchor_after(start);
189                let end_anchor = buffer.anchor_before(end);
190                ranges_to_highlight.push(start_anchor..end_anchor);
191
192                for chunk in buffer.text_for_range(start..end) {
193                    text.push_str(chunk);
194                }
195                if kind.linewise() {
196                    text.push('\n');
197                }
198                clipboard_selections.push(ClipboardSelection::for_buffer(
199                    text.len() - initial_len,
200                    false,
201                    start..end,
202                    &buffer,
203                    editor.project(),
204                    cx,
205                ));
206            }
207        }
208
209        let selected_register = self.selected_register.take();
210        Vim::update_globals(cx, |globals, cx| {
211            globals.write_registers(
212                Register {
213                    text: text.into(),
214                    clipboard_selections: Some(clipboard_selections),
215                },
216                selected_register,
217                is_yank,
218                kind,
219                cx,
220            )
221        });
222
223        let highlight_duration = VimSettings::get_global(cx).highlight_on_yank_duration;
224        if !is_yank || self.mode == Mode::Visual || highlight_duration == 0 {
225            return;
226        }
227
228        editor.highlight_background(
229            HighlightKey::HighlightOnYank,
230            &ranges_to_highlight,
231            |_, colors| colors.colors().vim_yank_background,
232            cx,
233        );
234        cx.spawn(async move |this, cx| {
235            cx.background_executor()
236                .timer(Duration::from_millis(highlight_duration))
237                .await;
238            this.update(cx, |editor, cx| {
239                editor.clear_background_highlights(HighlightKey::HighlightOnYank, cx)
240            })
241            .ok();
242        })
243        .detach();
244    }
245}
246
Served at tenant.openagents/omega Member data and write actions are omitted.