use gpui::{div, Element, Render, Subscription, View, ViewContext, WeakView};use itertools::Itertools;use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView}; use crate::{Vim, VimEvent, VimGlobals}; /// The ModeIndicator displays the current mode in the status bar.pub struct ModeIndicator { vim: Option<WeakView<Vim>>, pending_keys: Option<String>, vim_subscription: Option<Subscription>,} impl ModeIndicator { /// Construct a new mode indicator in this window. pub fn new(cx: &mut ViewContext<Self>) -> Self { cx.observe_pending_input(|this, cx| { this.update_pending_keys(cx); cx.notify(); }) .detach(); let handle = cx.view().clone(); let window = cx.window_handle(); cx.observe_new_views::<Vim>(move |_, cx| { if cx.window_handle() != window { return; } let vim = cx.view().clone(); handle.update(cx, |_, cx| { cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event { VimEvent::Focused => { mode_indicator.vim_subscription = Some(cx.observe(&vim, |_, _, cx| cx.notify())); mode_indicator.vim = Some(vim.downgrade()); } }) .detach() }) }) .detach(); Self { vim: None, pending_keys: None, vim_subscription: None, } } fn update_pending_keys(&mut self, cx: &mut ViewContext<Self>) { self.pending_keys = cx.pending_input_keystrokes().map(|keystrokes| { keystrokes .iter() .map(|keystroke| format!("{}", keystroke)) .join(" ") }); } fn vim(&self) -> Option<View<Vim>> { self.vim.as_ref().and_then(|vim| vim.upgrade()) } fn current_operators_description(&self, vim: View<Vim>, cx: &mut ViewContext<Self>) -> String { let recording = Vim::globals(cx) .recording_register .map(|reg| format!("recording @{reg} ")) .into_iter(); let vim = vim.read(cx); recording .chain( cx.global::<VimGlobals>() .pre_count .map(|count| format!("{}", count)), ) .chain(vim.selected_register.map(|reg| format!("\"{reg}"))) .chain( vim.operator_stack .iter() .map(|item| item.status().to_string()), ) .chain( cx.global::<VimGlobals>() .post_count .map(|count| format!("{}", count)), ) .collect::<Vec<_>>() .join("") }} impl Render for ModeIndicator { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { let vim = self.vim(); let Some(vim) = vim else { return div().into_any(); }; let vim_readable = vim.read(cx); let mode = if vim_readable.temp_mode { format!("(insert) {}", vim_readable.mode) } else { vim_readable.mode.to_string() }; let current_operators_description = self.current_operators_description(vim.clone(), cx); let pending = self .pending_keys .as_ref() .unwrap_or(¤t_operators_description); Label::new(format!("{} -- {} --", pending, mode)) .size(LabelSize::Small) .line_height_style(LineHeightStyle::UiLabel) .into_any_element() }} impl StatusItemView for ModeIndicator { fn set_active_pane_item( &mut self, _active_pane_item: Option<&dyn ItemHandle>, _cx: &mut ViewContext<Self>, ) { }}
use gpui::{div, Element, Render, Subscription, View, ViewContext, WeakView};
use itertools::Itertools;
use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView};
use crate::{Vim, VimEvent, VimGlobals};
/// The ModeIndicator displays the current mode in the status bar.
pub struct ModeIndicator {
vim: Option<WeakView<Vim>>,
pending_keys: Option<String>,
vim_subscription: Option<Subscription>,
}
impl ModeIndicator {
/// Construct a new mode indicator in this window.
pub fn new(cx: &mut ViewContext<Self>) -> Self {
cx.observe_pending_input(|this, cx| {
this.update_pending_keys(cx);
cx.notify();
})
.detach();
let handle = cx.view().clone();
let window = cx.window_handle();
cx.observe_new_views::<Vim>(move |_, cx| {
if cx.window_handle() != window {
return;
}
let vim = cx.view().clone();
handle.update(cx, |_, cx| {
cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event {
VimEvent::Focused => {
mode_indicator.vim_subscription =
Some(cx.observe(&vim, |_, _, cx| cx.notify()));
mode_indicator.vim = Some(vim.downgrade());
}
})
.detach()
})
})
.detach();
Self {
vim: None,
pending_keys: None,
vim_subscription: None,
}
}
fn update_pending_keys(&mut self, cx: &mut ViewContext<Self>) {
self.pending_keys = cx.pending_input_keystrokes().map(|keystrokes| {
keystrokes
.iter()
.map(|keystroke| format!("{}", keystroke))
.join(" ")
});
}
fn vim(&self) -> Option<View<Vim>> {
self.vim.as_ref().and_then(|vim| vim.upgrade())
}
fn current_operators_description(&self, vim: View<Vim>, cx: &mut ViewContext<Self>) -> String {
let recording = Vim::globals(cx)
.recording_register
.map(|reg| format!("recording @{reg} "))
.into_iter();
let vim = vim.read(cx);
recording
.chain(
cx.global::<VimGlobals>()
.pre_count
.map(|count| format!("{}", count)),
)
.chain(vim.selected_register.map(|reg| format!("\"{reg}")))
.chain(
vim.operator_stack
.iter()
.map(|item| item.status().to_string()),
)
.chain(
cx.global::<VimGlobals>()
.post_count
.map(|count| format!("{}", count)),
)
.collect::<Vec<_>>()
.join("")
}
}
impl Render for ModeIndicator {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let vim = self.vim();
let Some(vim) = vim else {
return div().into_any();
};
let vim_readable = vim.read(cx);
let mode = if vim_readable.temp_mode {
format!("(insert) {}", vim_readable.mode)
} else {
vim_readable.mode.to_string()
};
let current_operators_description = self.current_operators_description(vim.clone(), cx);
let pending = self
.pending_keys
.as_ref()
.unwrap_or(¤t_operators_description);
Label::new(format!("{} -- {} --", pending, mode))
.size(LabelSize::Small)
.line_height_style(LineHeightStyle::UiLabel)
.into_any_element()
}
}
impl StatusItemView for ModeIndicator {
fn set_active_pane_item(
&mut self,
_active_pane_item: Option<&dyn ItemHandle>,
_cx: &mut ViewContext<Self>,
) {
}
}