diff --git a/Cargo.lock b/Cargo.lock index f0c8917aa2..e805a87230 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8523,6 +8523,7 @@ dependencies = [ "serde_derive", "serde_json", "settings", + "theme", "tokio", "util", "workspace", diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 81ae7a65ca..de0701a343 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -402,6 +402,7 @@ pub struct StatusBar { pub height: f32, pub item_spacing: f32, pub cursor_position: TextStyle, + pub vim_mode: TextStyle, pub active_language: Interactive, pub auto_update_progress_message: TextStyle, pub auto_update_done_message: TextStyle, diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 47a85f4ed3..3a5974d6c9 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -32,6 +32,7 @@ language = { path = "../language" } search = { path = "../search" } settings = { path = "../settings" } workspace = { path = "../workspace" } +theme = { path = "../theme" } [dev-dependencies] indoc.workspace = true @@ -44,3 +45,4 @@ project = { path = "../project", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } settings = { path = "../settings" } workspace = { path = "../workspace", features = ["test-support"] } +theme = { path = "../theme", features = ["test-support"] } diff --git a/crates/vim/src/mode_indicator.rs b/crates/vim/src/mode_indicator.rs new file mode 100644 index 0000000000..ef8d159018 --- /dev/null +++ b/crates/vim/src/mode_indicator.rs @@ -0,0 +1,68 @@ +use gpui::{ + elements::{Empty, Label}, + AnyElement, Element, Entity, View, ViewContext, +}; +use workspace::{item::ItemHandle, StatusItemView}; + +use crate::{state::Mode, Vim}; + +pub struct ModeIndicator { + mode: Option, +} + +impl ModeIndicator { + pub fn new(cx: &mut ViewContext) -> Self { + cx.observe_global::(|this, cx| { + let vim = Vim::read(cx); + if vim.enabled { + this.set_mode(Some(Vim::read(cx).state.mode), cx) + } else { + this.set_mode(None, cx) + } + }) + .detach(); + Self { mode: None } + } + + pub fn set_mode(&mut self, mode: Option, cx: &mut ViewContext) { + if mode != self.mode { + self.mode = mode; + cx.notify(); + } + } +} + +impl Entity for ModeIndicator { + type Event = (); +} + +impl View for ModeIndicator { + fn ui_name() -> &'static str { + "ModeIndicator" + } + + fn render(&mut self, cx: &mut ViewContext) -> AnyElement { + if let Some(mode) = self.mode { + let theme = &theme::current(cx).workspace.status_bar; + let text = match mode { + Mode::Normal => "", + Mode::Insert => "--- INSERT ---", + Mode::Visual { line: false } => "--- VISUAL ---", + Mode::Visual { line: true } => "--- VISUAL LINE ---", + }; + Label::new(text, theme.vim_mode.clone()).into_any() + } else { + Empty::new().into_any() + } + } +} + +impl StatusItemView for ModeIndicator { + fn set_active_pane_item( + &mut self, + _active_pane_item: Option<&dyn ItemHandle>, + _cx: &mut ViewContext, + ) { + // nothing to do. + } +} diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 69b94428dd..8ab110ba1e 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -3,6 +3,7 @@ mod test; mod editor_events; mod insert; +mod mode_indicator; mod motion; mod normal; mod object; @@ -18,6 +19,7 @@ use gpui::{ ViewHandle, WeakViewHandle, WindowContext, }; use language::CursorShape; +pub use mode_indicator::ModeIndicator; use motion::Motion; use normal::normal_replace; use serde::Deserialize; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 6bbba0bd02..645371d419 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -312,8 +312,10 @@ pub fn initialize_workspace( feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace) }); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); + let vim_mode = cx.add_view(|cx| vim::ModeIndicator::new(cx)); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); + status_bar.add_left_item(vim_mode, cx); status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(feedback_button, cx); status_bar.add_right_item(copilot, cx); diff --git a/styles/src/style_tree/status_bar.ts b/styles/src/style_tree/status_bar.ts index 9aeea866f3..b4273cbf99 100644 --- a/styles/src/style_tree/status_bar.ts +++ b/styles/src/style_tree/status_bar.ts @@ -27,6 +27,7 @@ export default function status_bar(): any { }, border: border(layer, { top: true, overlay: true }), cursor_position: text(layer, "sans", "variant"), + vim_mode: text(layer, "sans", "variant"), active_language: interactive({ base: { padding: { left: 6, right: 6 },