Skip to repository content197 lines · 5.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-29T05:20:06.003Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
line_ending_selector.rs
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