mode_indicator.rs

  1use gpui::{Context, Element, Entity, Render, Subscription, WeakEntity, Window, div};
  2use ui::text_for_keystrokes;
  3use workspace::{StatusItemView, item::ItemHandle, ui::prelude::*};
  4
  5use crate::{Vim, VimEvent, VimGlobals};
  6
  7/// The ModeIndicator displays the current mode in the status bar.
  8pub struct ModeIndicator {
  9    vim: Option<WeakEntity<Vim>>,
 10    pending_keys: Option<String>,
 11    vim_subscription: Option<Subscription>,
 12}
 13
 14impl ModeIndicator {
 15    /// Construct a new mode indicator in this window.
 16    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
 17        cx.observe_pending_input(window, |this: &mut Self, window, cx| {
 18            this.update_pending_keys(window, cx);
 19            cx.notify();
 20        })
 21        .detach();
 22
 23        let handle = cx.entity().clone();
 24        let window_handle = window.window_handle();
 25        cx.observe_new::<Vim>(move |_, window, cx| {
 26            let Some(window) = window else {
 27                return;
 28            };
 29            if window.window_handle() != window_handle {
 30                return;
 31            }
 32            let vim = cx.entity().clone();
 33            handle.update(cx, |_, cx| {
 34                cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event {
 35                    VimEvent::Focused => {
 36                        mode_indicator.vim_subscription =
 37                            Some(cx.observe(&vim, |_, _, cx| cx.notify()));
 38                        mode_indicator.vim = Some(vim.downgrade());
 39                    }
 40                })
 41                .detach()
 42            })
 43        })
 44        .detach();
 45
 46        Self {
 47            vim: None,
 48            pending_keys: None,
 49            vim_subscription: None,
 50        }
 51    }
 52
 53    // TODO:
 54    // what's this?
 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(
 80                vim.operator_stack
 81                    .iter()
 82                    .map(|item| item.status().to_string()),
 83            )
 84            .chain(
 85                cx.global::<VimGlobals>()
 86                    .post_count
 87                    .map(|count| format!("{}", count)),
 88            )
 89            .collect::<Vec<_>>()
 90            .join("")
 91    }
 92}
 93
 94impl Render for ModeIndicator {
 95    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 96        let vim = self.vim();
 97        let Some(vim) = vim else {
 98            return div().into_any();
 99        };
100
101        let vim_readable = vim.read(cx);
102        let label = if let Some(label) = vim_readable.status_label.clone() {
103            label
104        } else {
105            let mode = if vim_readable.temp_mode {
106                format!("(insert) {}", vim_readable.mode)
107            } else {
108                vim_readable.mode.to_string()
109            };
110
111            let current_operators_description = self.current_operators_description(vim.clone(), cx);
112            let pending = self
113                .pending_keys
114                .as_ref()
115                .unwrap_or(&current_operators_description);
116            format!("{} -- {} --", pending, mode).into()
117        };
118
119        Label::new(label)
120            .size(LabelSize::Small)
121            .line_height_style(LineHeightStyle::UiLabel)
122            .into_any_element()
123    }
124}
125
126impl StatusItemView for ModeIndicator {
127    fn set_active_pane_item(
128        &mut self,
129        _active_pane_item: Option<&dyn ItemHandle>,
130        _window: &mut Window,
131        _cx: &mut Context<Self>,
132    ) {
133    }
134}