Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-29T05:20:06.003Z 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

line_ending_selector.rs

197 lines · 5.5 KB · rust
1mod line_ending_indicator;
2
3use editor::Editor;
4use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, actions};
5use language::{Buffer, LineEnding};
6pub use line_ending_indicator::LineEndingIndicator;
7use picker::{Picker, PickerDelegate};
8use project::Project;
9use std::sync::Arc;
10use ui::{ListItem, ListItemSpacing, prelude::*};
11use util::ResultExt;
12use workspace::ModalView;
13
14actions!(
15    line_ending_selector,
16    [
17        /// Toggles the line ending selector modal.
18        Toggle
19    ]
20);
21
22pub fn init(cx: &mut App) {
23    cx.observe_new(LineEndingSelector::register).detach();
24}
25
26pub struct LineEndingSelector {
27    picker: Entity<Picker<LineEndingSelectorDelegate>>,
28}
29
30impl LineEndingSelector {
31    fn register(editor: &mut Editor, _window: Option<&mut Window>, cx: &mut Context<Editor>) {
32        let editor_handle = cx.weak_entity();
33        editor
34            .register_action(move |_: &Toggle, window, cx| {
35                Self::toggle(&editor_handle, window, cx);
36            })
37            .detach();
38    }
39
40    fn toggle(editor: &WeakEntity<Editor>, window: &mut Window, cx: &mut App) {
41        let Some((workspace, buffer)) = editor
42            .update(cx, |editor, cx| {
43                Some((editor.workspace()?, editor.active_buffer(cx)?))
44            })
45            .ok()
46            .flatten()
47        else {
48            return;
49        };
50
51        workspace.update(cx, |workspace, cx| {
52            let project = workspace.project().clone();
53            workspace.toggle_modal(window, cx, move |window, cx| {
54                LineEndingSelector::new(buffer, project, window, cx)
55            });
56        })
57    }
58
59    fn new(
60        buffer: Entity<Buffer>,
61        project: Entity<Project>,
62        window: &mut Window,
63        cx: &mut Context<Self>,
64    ) -> Self {
65        let line_ending = buffer.read(cx).line_ending();
66        let delegate =
67            LineEndingSelectorDelegate::new(cx.entity().downgrade(), buffer, project, line_ending);
68        let picker = cx.new(|cx| Picker::nonsearchable_uniform_list(delegate, window, cx));
69        Self { picker }
70    }
71}
72
73impl Render for LineEndingSelector {
74    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
75        v_flex().child(self.picker.clone())
76    }
77}
78
79impl Focusable for LineEndingSelector {
80    fn focus_handle(&self, cx: &App) -> FocusHandle {
81        self.picker.focus_handle(cx)
82    }
83}
84
85impl EventEmitter<DismissEvent> for LineEndingSelector {}
86impl ModalView for LineEndingSelector {}
87
88struct LineEndingSelectorDelegate {
89    line_ending_selector: WeakEntity<LineEndingSelector>,
90    buffer: Entity<Buffer>,
91    project: Entity<Project>,
92    line_ending: LineEnding,
93    matches: Vec<LineEnding>,
94    selected_index: usize,
95}
96
97impl LineEndingSelectorDelegate {
98    fn new(
99        line_ending_selector: WeakEntity<LineEndingSelector>,
100        buffer: Entity<Buffer>,
101        project: Entity<Project>,
102        line_ending: LineEnding,
103    ) -> Self {
104        Self {
105            line_ending_selector,
106            buffer,
107            project,
108            line_ending,
109            matches: vec![LineEnding::Unix, LineEnding::Windows],
110            selected_index: 0,
111        }
112    }
113}
114
115impl PickerDelegate for LineEndingSelectorDelegate {
116    type ListItem = ListItem;
117
118    fn name() -> &'static str {
119        "line ending selector"
120    }
121
122    fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
123        "Select a line ending…".into()
124    }
125
126    fn match_count(&self) -> usize {
127        self.matches.len()
128    }
129
130    fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
131        if let Some(line_ending) = self.matches.get(self.selected_index) {
132            self.buffer.update(cx, |this, cx| {
133                this.set_line_ending(*line_ending, cx);
134            });
135            let buffer = self.buffer.clone();
136            let project = self.project.clone();
137            cx.defer(move |cx| {
138                project.update(cx, |this, cx| {
139                    this.save_buffer(buffer, cx).detach();
140                });
141            });
142        }
143        self.dismissed(window, cx);
144    }
145
146    fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
147        self.line_ending_selector
148            .update(cx, |_, cx| cx.emit(DismissEvent))
149            .log_err();
150    }
151
152    fn selected_index(&self) -> usize {
153        self.selected_index
154    }
155
156    fn set_selected_index(
157        &mut self,
158        ix: usize,
159        _window: &mut Window,
160        _: &mut Context<Picker<Self>>,
161    ) {
162        self.selected_index = ix;
163    }
164
165    fn update_matches(
166        &mut self,
167        _query: String,
168        _window: &mut Window,
169        _cx: &mut Context<Picker<Self>>,
170    ) -> gpui::Task<()> {
171        return Task::ready(());
172    }
173
174    fn render_match(
175        &self,
176        ix: usize,
177        selected: bool,
178        _: &mut Window,
179        _: &mut Context<Picker<Self>>,
180    ) -> Option<Self::ListItem> {
181        let line_ending = self.matches.get(ix)?;
182        let label = line_ending.label();
183
184        let mut list_item = ListItem::new(ix)
185            .inset(true)
186            .spacing(ListItemSpacing::Sparse)
187            .toggle_state(selected)
188            .child(Label::new(label));
189
190        if &self.line_ending == line_ending {
191            list_item = list_item.end_slot(Icon::new(IconName::Check).color(Color::Muted));
192        }
193
194        Some(list_item)
195    }
196}
197
Served at tenant.openagents/omega Member data and write actions are omitted.