Skip to repository content197 lines · 7.4 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:32:43.134Z 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
mode_indicator.rs
1use gpui::{
2 App, Context, Element, Entity, FontWeight, Render, Subscription, WeakEntity, Window, div,
3};
4use ui::text_for_keystrokes;
5use workspace::{HideStatusItem, StatusItemView, item::ItemHandle, ui::prelude::*};
6
7use crate::{Vim, VimEvent, VimGlobals};
8
9/// The ModeIndicator displays the current mode in the status bar.
10pub struct ModeIndicator {
11 vim: Option<WeakEntity<Vim>>,
12 pending_keys: Option<String>,
13 vim_subscription: Option<Subscription>,
14}
15
16impl ModeIndicator {
17 /// Construct a new mode indicator in this window.
18 pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
19 cx.observe_pending_input(window, |this: &mut Self, window, cx| {
20 this.update_pending_keys(window, cx);
21 cx.notify();
22 })
23 .detach();
24
25 let handle = cx.entity();
26 let window_handle = window.window_handle();
27 cx.observe_new::<Vim>(move |_, window, cx| {
28 let Some(window) = window else {
29 return;
30 };
31 if window.window_handle() != window_handle {
32 return;
33 }
34 let vim = cx.entity();
35 handle.update(cx, |_, cx| {
36 cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event {
37 VimEvent::Focused => {
38 mode_indicator.vim_subscription =
39 Some(cx.observe(&vim, |_, _, cx| cx.notify()));
40 mode_indicator.vim = Some(vim.downgrade());
41 }
42 })
43 .detach()
44 })
45 })
46 .detach();
47
48 Self {
49 vim: None,
50 pending_keys: None,
51 vim_subscription: None,
52 }
53 }
54
55 fn update_pending_keys(&mut self, window: &mut Window, cx: &App) {
56 self.pending_keys = window
57 .pending_input_keystrokes()
58 .map(|keystrokes| text_for_keystrokes(keystrokes, cx));
59 }
60
61 fn vim(&self) -> Option<Entity<Vim>> {
62 self.vim.as_ref().and_then(|vim| vim.upgrade())
63 }
64
65 fn current_operators_description(&self, vim: Entity<Vim>, cx: &mut Context<Self>) -> String {
66 let recording = Vim::globals(cx)
67 .recording_register
68 .map(|reg| format!("recording @{reg} "))
69 .into_iter();
70
71 let vim = vim.read(cx);
72 recording
73 .chain(
74 cx.global::<VimGlobals>()
75 .pre_count
76 .map(|count| format!("{}", count)),
77 )
78 .chain(vim.selected_register.map(|reg| format!("\"{reg}")))
79 .chain(vim.operator_stack.iter().map(|item| item.status()))
80 .chain(
81 cx.global::<VimGlobals>()
82 .post_count
83 .map(|count| format!("{}", count)),
84 )
85 .collect::<Vec<_>>()
86 .concat()
87 }
88}
89
90impl Render for ModeIndicator {
91 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
92 let vim = self.vim();
93 let Some(vim) = vim else {
94 return div().hidden().into_any_element();
95 };
96
97 let vim_readable = vim.read(cx);
98 let status_label = vim_readable.status_label.clone();
99 let temp_mode = vim_readable.temp_mode;
100 let mode = vim_readable.mode;
101
102 let theme = cx.theme();
103 let colors = theme.colors();
104 let system_transparent = gpui::hsla(0.0, 0.0, 0.0, 0.0);
105 let vim_mode_text = match mode {
106 crate::state::Mode::Normal => colors.vim_normal_foreground,
107 crate::state::Mode::Insert => colors.vim_insert_foreground,
108 crate::state::Mode::Replace => colors.vim_replace_foreground,
109 crate::state::Mode::Visual => colors.vim_visual_foreground,
110 crate::state::Mode::VisualLine => colors.vim_visual_line_foreground,
111 crate::state::Mode::VisualBlock => colors.vim_visual_block_foreground,
112 crate::state::Mode::HelixNormal => colors.vim_helix_normal_foreground,
113 crate::state::Mode::HelixSelect => colors.vim_helix_select_foreground,
114 };
115 let bg_color = match mode {
116 crate::state::Mode::Normal => colors.vim_normal_background,
117 crate::state::Mode::Insert => colors.vim_insert_background,
118 crate::state::Mode::Replace => colors.vim_replace_background,
119 crate::state::Mode::Visual => colors.vim_visual_background,
120 crate::state::Mode::VisualLine => colors.vim_visual_line_background,
121 crate::state::Mode::VisualBlock => colors.vim_visual_block_background,
122 crate::state::Mode::HelixNormal => colors.vim_helix_normal_background,
123 crate::state::Mode::HelixSelect => colors.vim_helix_select_background,
124 };
125
126 let (label, mode): (SharedString, Option<SharedString>) = if let Some(label) = status_label
127 {
128 (label, None)
129 } else {
130 let mode_str = if temp_mode {
131 format!("(insert) {}", mode)
132 } else {
133 mode.to_string()
134 };
135
136 let current_operators_description = self.current_operators_description(vim.clone(), cx);
137 let pending = self
138 .pending_keys
139 .as_ref()
140 .unwrap_or(¤t_operators_description);
141 let mode = if bg_color != system_transparent {
142 mode_str.into()
143 } else {
144 format!("-- {} --", mode_str).into()
145 };
146 (pending.into(), Some(mode))
147 };
148 h_flex()
149 .gap_1()
150 .when(!label.is_empty(), |el| {
151 el.child(
152 Label::new(label)
153 .line_height_style(LineHeightStyle::UiLabel)
154 .weight(FontWeight::MEDIUM),
155 )
156 })
157 .when_some(mode, |el, mode| {
158 el.child(
159 v_flex()
160 .when(bg_color != system_transparent, |el| el.px_2())
161 // match with other icons at the bottom that use default buttons
162 .h(ButtonSize::Default.rems())
163 .justify_center()
164 .rounded_sm()
165 .bg(bg_color)
166 .child(
167 Label::new(mode)
168 .size(LabelSize::Small)
169 .line_height_style(LineHeightStyle::UiLabel)
170 .weight(FontWeight::MEDIUM)
171 .when(
172 bg_color != system_transparent
173 && vim_mode_text != system_transparent,
174 |el| el.color(Color::Custom(vim_mode_text)),
175 ),
176 ),
177 )
178 })
179 .into_any()
180 }
181}
182
183impl StatusItemView for ModeIndicator {
184 fn set_active_pane_item(
185 &mut self,
186 _active_pane_item: Option<&dyn ItemHandle>,
187 _window: &mut Window,
188 _cx: &mut Context<Self>,
189 ) {
190 }
191
192 fn hide_setting(&self, _: &App) -> Option<HideStatusItem> {
193 // The Vim mode indicator is only visible while Vim mode is on.
194 None
195 }
196}
197