diff --git a/Cargo.lock b/Cargo.lock index 2b3d7b2691..5c7a0aba3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,6 +313,7 @@ dependencies = [ "anyhow", "cloud_llm_client", "collections", + "editor_mode_setting", "fs", "gpui", "language_model", @@ -354,6 +355,7 @@ dependencies = [ "context_server", "db", "editor", + "editor_mode_setting", "extension", "extension_host", "feature_flags", @@ -4547,6 +4549,7 @@ dependencies = [ "db", "debugger_tools", "editor", + "editor_mode_setting", "file_icons", "futures 0.3.31", "fuzzy", @@ -5061,6 +5064,7 @@ dependencies = [ "dap", "db", "edit_prediction", + "editor_mode_setting", "emojis", "file_icons", "fs", @@ -5121,6 +5125,19 @@ dependencies = [ "zlog", ] +[[package]] +name = "editor_mode_setting" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "schemars", + "serde", + "serde_json", + "settings", + "workspace-hack", +] + [[package]] name = "either" version = "1.15.0" @@ -5612,6 +5629,7 @@ dependencies = [ "collections", "db", "editor", + "editor_mode_setting", "extension", "extension_host", "fs", @@ -5632,7 +5650,6 @@ dependencies = [ "theme", "ui", "util", - "vim_mode_setting", "workspace", "workspace-hack", "zed_actions", @@ -11101,6 +11118,7 @@ dependencies = [ "db", "documented", "editor", + "editor_mode_setting", "fs", "fuzzy", "git", @@ -11119,7 +11137,6 @@ dependencies = [ "theme", "ui", "util", - "vim_mode_setting", "workspace", "workspace-hack", "zed_actions", @@ -13962,6 +13979,7 @@ dependencies = [ "anyhow", "collections", "editor", + "editor_mode_setting", "gpui", "language", "language_model", @@ -17952,6 +17970,7 @@ dependencies = [ "command_palette_hooks", "db", "editor", + "editor_mode_setting", "env_logger 0.11.8", "futures 0.3.31", "git_ui", @@ -17981,22 +18000,11 @@ dependencies = [ "tokio", "ui", "util", - "vim_mode_setting", "workspace", "workspace-hack", "zed_actions", ] -[[package]] -name = "vim_mode_setting" -version = "0.1.0" -dependencies = [ - "anyhow", - "gpui", - "settings", - "workspace-hack", -] - [[package]] name = "vscode_theme" version = "0.2.0" @@ -20455,6 +20463,7 @@ dependencies = [ "diagnostics", "edit_prediction_button", "editor", + "editor_mode_setting", "env_logger 0.11.8", "extension", "extension_host", @@ -20554,7 +20563,6 @@ dependencies = [ "util", "uuid", "vim", - "vim_mode_setting", "watch", "web_search", "web_search_providers", diff --git a/Cargo.toml b/Cargo.toml index 84de9b30ad..468dfd67b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -181,7 +181,7 @@ members = [ "crates/util_macros", "crates/vercel", "crates/vim", - "crates/vim_mode_setting", + "crates/editor_mode_setting", "crates/watch", "crates/web_search", "crates/web_search_providers", @@ -406,7 +406,7 @@ util = { path = "crates/util" } util_macros = { path = "crates/util_macros" } vercel = { path = "crates/vercel" } vim = { path = "crates/vim" } -vim_mode_setting = { path = "crates/vim_mode_setting" } +editor_mode_setting = { path = "crates/editor_mode_setting" } watch = { path = "crates/watch" } web_search = { path = "crates/web_search" } diff --git a/assets/settings/default.json b/assets/settings/default.json index c290baf003..2f24416f42 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -111,6 +111,10 @@ // 2. Maps `Control` on Linux and Windows and to `Command` on MacOS: // "cmd_or_ctrl" (alias: "cmd", "ctrl") "multi_cursor_modifier": "alt", + /// Weather to set editor mode to vim, vim insert, helix, etc. + /// + /// Default: default + "editor_mode": "default", // Whether to enable vim modes and key bindings. "vim_mode": false, // Whether to enable helix mode and key bindings. @@ -885,7 +889,11 @@ /// Whether to have terminal cards in the agent panel expanded, showing the whole command output. /// /// Default: true - "expand_terminal_card": true + "expand_terminal_card": true, + /// Weather to inherit or override the editor mode for the agent panel. + /// + /// Default: inherit + "editor_mode": "inherit" }, // The settings for slash commands. "slash_commands": { diff --git a/crates/agent_settings/Cargo.toml b/crates/agent_settings/Cargo.toml index d34396a5d3..0791898001 100644 --- a/crates/agent_settings/Cargo.toml +++ b/crates/agent_settings/Cargo.toml @@ -19,8 +19,10 @@ gpui.workspace = true language_model.workspace = true schemars.workspace = true serde.workspace = true +serde_json.workspace = true settings.workspace = true workspace-hack.workspace = true +editor_mode_setting.workspace = true [dev-dependencies] fs.workspace = true diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs index ed1ed2b898..af220bd535 100644 --- a/crates/agent_settings/src/agent_settings.rs +++ b/crates/agent_settings/src/agent_settings.rs @@ -4,10 +4,11 @@ use std::sync::Arc; use anyhow::{Result, bail}; use collections::IndexMap; +use editor_mode_setting::EditorMode; use gpui::{App, Pixels, SharedString}; use language_model::LanguageModel; use schemars::{JsonSchema, json_schema}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use settings::{Settings, SettingsSources}; use std::borrow::Cow; @@ -48,6 +49,67 @@ pub enum NotifyWhenAgentWaiting { Never, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub enum AgentEditorMode { + EditorModeOverride(EditorMode), + #[default] + Inherit, +} + +impl<'de> Deserialize<'de> for AgentEditorMode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if s == "inherit" { + Ok(AgentEditorMode::Inherit) + } else { + let mode = EditorMode::deserialize(serde::de::value::StringDeserializer::new(s))?; + Ok(AgentEditorMode::EditorModeOverride(mode)) + } + } +} + +impl Serialize for AgentEditorMode { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + AgentEditorMode::EditorModeOverride(mode) => mode.serialize(serializer), + AgentEditorMode::Inherit => serializer.serialize_str("inherit"), + } + } +} + +impl JsonSchema for AgentEditorMode { + fn schema_name() -> Cow<'static, str> { + "AgentEditorMode".into() + } + + fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema { + use editor_mode_setting::EditorMode; + + let mut options = vec![serde_json::json!({ + "const": "inherit", + "description": "Inherit editor mode from global settings" + })]; + options.extend(EditorMode::get_schema_options()); + + json_schema!({ + "oneOf": options, + "description": "Agent editor mode - either inherit from global settings or override with a specific mode" + }) + } +} + +impl From for AgentEditorMode { + fn from(b: EditorMode) -> Self { + AgentEditorMode::EditorModeOverride(b) + } +} + #[derive(Default, Clone, Debug)] pub struct AgentSettings { pub enabled: bool, @@ -75,6 +137,7 @@ pub struct AgentSettings { pub expand_edit_card: bool, pub expand_terminal_card: bool, pub use_modifier_to_send: bool, + pub editor_mode: AgentEditorMode, } impl AgentSettings { @@ -315,6 +378,10 @@ pub struct AgentSettingsContent { /// /// Default: false use_modifier_to_send: Option, + /// Weather to inherit or override the editor mode for the agent panel. + /// + /// Default: inherit + editor_mode: Option, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)] @@ -470,6 +537,7 @@ impl Settings for AgentSettings { &mut settings.use_modifier_to_send, value.use_modifier_to_send, ); + merge(&mut settings.editor_mode, value.editor_mode); settings .model_parameters diff --git a/crates/agent_ui/Cargo.toml b/crates/agent_ui/Cargo.toml index 43e3b25124..2c9967ea31 100644 --- a/crates/agent_ui/Cargo.toml +++ b/crates/agent_ui/Cargo.toml @@ -100,6 +100,7 @@ watch.workspace = true workspace-hack.workspace = true workspace.workspace = true zed_actions.workspace = true +editor_mode_setting.workspace = true [dev-dependencies] acp_thread = { workspace = true, features = ["test-support"] } diff --git a/crates/agent_ui/src/acp/entry_view_state.rs b/crates/agent_ui/src/acp/entry_view_state.rs index 0e4080d689..0c3af286cb 100644 --- a/crates/agent_ui/src/acp/entry_view_state.rs +++ b/crates/agent_ui/src/acp/entry_view_state.rs @@ -4,7 +4,7 @@ use acp_thread::{AcpThread, AgentThreadEntry}; use agent_client_protocol::{PromptCapabilities, ToolCallId}; use agent2::HistoryStore; use collections::HashMap; -use editor::{Editor, EditorMode, MinimapVisibility}; +use editor::{Editor, EditorDisplayMode, MinimapVisibility}; use gpui::{ AnyEntity, App, AppContext as _, Entity, EntityId, EventEmitter, Focusable, TextStyleRefinement, WeakEntity, Window, @@ -87,7 +87,7 @@ impl EntryViewState { self.prompt_capabilities.clone(), "Edit message - @ to include context", self.prevent_slash_commands, - editor::EditorMode::AutoHeight { + editor::EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: None, }, @@ -287,7 +287,7 @@ fn create_editor_diff( ) -> Entity { cx.new(|cx| { let mut editor = Editor::new( - EditorMode::Full { + EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: false, show_active_line_background: false, sized_by_content: true, diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs index 7d73ebeb19..4a9f0c8cd0 100644 --- a/crates/agent_ui/src/acp/message_editor.rs +++ b/crates/agent_ui/src/acp/message_editor.rs @@ -5,14 +5,15 @@ use crate::{ use acp_thread::{MentionUri, selection_name}; use agent_client_protocol as acp; use agent_servers::AgentServer; +use agent_settings::{AgentEditorMode, AgentSettings}; use agent2::HistoryStore; use anyhow::{Context as _, Result, anyhow}; use assistant_slash_commands::codeblock_fence_for_path; use collections::{HashMap, HashSet}; use editor::{ - Addon, Anchor, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, - EditorEvent, EditorMode, EditorSnapshot, EditorStyle, ExcerptId, FoldPlaceholder, MultiBuffer, - SemanticsProvider, ToOffset, + Addon, Anchor, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, + EditorDisplayMode, EditorElement, EditorEvent, EditorSnapshot, EditorStyle, ExcerptId, + FoldPlaceholder, MultiBuffer, SemanticsProvider, ToOffset, actions::Paste, display_map::{Crease, CreaseId, FoldId}, }; @@ -90,7 +91,7 @@ impl MessageEditor { prompt_capabilities: Rc>, placeholder: impl Into>, prevent_slash_commands: bool, - mode: EditorMode, + mode: EditorDisplayMode, window: &mut Window, cx: &mut Context, ) -> Self { @@ -112,6 +113,13 @@ impl MessageEditor { range: Cell::new(None), }); let mention_set = MentionSet::default(); + + let settings = AgentSettings::get_global(cx); + let editor_mode = match settings.editor_mode { + AgentEditorMode::EditorModeOverride(mode) => mode, + AgentEditorMode::Inherit => editor_mode_setting::EditorModeSetting::get_global(cx).0, + }; + let editor = cx.new(|cx| { let buffer = cx.new(|cx| Buffer::local("", cx).with_language(Arc::new(language), cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); @@ -120,7 +128,7 @@ impl MessageEditor { editor.set_placeholder_text(placeholder, cx); editor.set_show_indent_guides(false, cx); editor.set_soft_wrap(); - editor.set_use_modal_editing(true); + editor.set_editor_mode(editor_mode, cx); editor.set_completion_provider(Some(Rc::new(completion_provider))); editor.set_context_menu_options(ContextMenuOptions { min_entries_visible: 12, @@ -159,6 +167,19 @@ impl MessageEditor { } })); + subscriptions.push(cx.observe_global::(move |this, cx| { + let settings = AgentSettings::get_global(cx); + let editor_mode = match settings.editor_mode { + AgentEditorMode::EditorModeOverride(mode) => mode, + AgentEditorMode::Inherit => { + editor_mode_setting::EditorModeSetting::get_global(cx).0 + } + }; + this.editor.update(cx, |editor, cx| { + editor.set_editor_mode(editor_mode, cx); + }); + })); + Self { editor, project, @@ -1056,9 +1077,9 @@ impl MessageEditor { }) } - pub fn set_mode(&mut self, mode: EditorMode, cx: &mut Context) { + pub fn set_display_mode(&mut self, mode: EditorDisplayMode, cx: &mut Context) { self.editor.update(cx, |editor, cx| { - editor.set_mode(mode); + editor.set_display_mode(mode); cx.notify() }); } @@ -1858,7 +1879,7 @@ mod tests { use agent_client_protocol as acp; use agent2::HistoryStore; use assistant_context::ContextStore; - use editor::{AnchorRangeExt as _, Editor, EditorMode}; + use editor::{AnchorRangeExt as _, Editor, EditorDisplayMode}; use fs::FakeFs; use futures::StreamExt as _; use gpui::{ @@ -1901,7 +1922,7 @@ mod tests { Default::default(), "Test", false, - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: None, }, @@ -2102,7 +2123,7 @@ mod tests { prompt_capabilities.clone(), "Test", false, - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { max_lines: None, min_lines: 1, }, diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 619885144a..ba900ea803 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -15,7 +15,7 @@ use buffer_diff::BufferDiff; use client::zed_urls; use collections::{HashMap, HashSet}; use editor::scroll::Autoscroll; -use editor::{Editor, EditorEvent, EditorMode, MultiBuffer, PathKey, SelectionEffects}; +use editor::{Editor, EditorDisplayMode, EditorEvent, MultiBuffer, PathKey, SelectionEffects}; use file_icons::FileIcons; use fs::Fs; use gpui::{ @@ -228,7 +228,7 @@ impl ThreadFeedbackState { let editor = cx.new(|cx| { let mut editor = Editor::new( - editor::EditorMode::AutoHeight { + editor::EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(4), }, @@ -321,7 +321,7 @@ impl AcpThreadView { prompt_capabilities.clone(), "Message the agent — @ to include context", prevent_slash_commands, - editor::EditorMode::AutoHeight { + editor::EditorDisplayMode::AutoHeight { min_lines: MIN_EDITOR_LINES, max_lines: Some(MAX_EDITOR_LINES), }, @@ -673,8 +673,8 @@ impl AcpThreadView { self.editor_expanded = is_expanded; self.message_editor.update(cx, |editor, cx| { if is_expanded { - editor.set_mode( - EditorMode::Full { + editor.set_display_mode( + EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: false, show_active_line_background: false, sized_by_content: false, @@ -682,8 +682,8 @@ impl AcpThreadView { cx, ) } else { - editor.set_mode( - EditorMode::AutoHeight { + editor.set_display_mode( + EditorDisplayMode::AutoHeight { min_lines: MIN_EDITOR_LINES, max_lines: Some(MAX_EDITOR_LINES), }, diff --git a/crates/agent_ui/src/active_thread.rs b/crates/agent_ui/src/active_thread.rs index 2cad913295..2c72c91e27 100644 --- a/crates/agent_ui/src/active_thread.rs +++ b/crates/agent_ui/src/active_thread.rs @@ -1723,7 +1723,7 @@ impl ActiveThread { let editor = cx.new(|cx| { let mut editor = Editor::new( - editor::EditorMode::AutoHeight { + editor::EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(4), }, diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index e07424987c..86e5f8a1ed 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/crates/agent_ui/src/agent_diff.rs @@ -1046,7 +1046,7 @@ impl ToolbarItemView for AgentDiffToolbar { } if let Some(editor) = item.act_as::(cx) - && editor.read(cx).mode().is_full() + && editor.read(cx).display_mode().is_full() { let agent_diff = AgentDiff::global(cx); @@ -1549,7 +1549,7 @@ impl AgentDiff { } fn full_editor_buffer(editor: &Editor, cx: &App) -> Option> { - if editor.mode().is_full() { + if editor.display_mode().is_full() { editor .buffer() .read(cx) diff --git a/crates/agent_ui/src/context_picker/completion_provider.rs b/crates/agent_ui/src/context_picker/completion_provider.rs index 020d799c79..978052712c 100644 --- a/crates/agent_ui/src/context_picker/completion_provider.rs +++ b/crates/agent_ui/src/context_picker/completion_provider.rs @@ -1244,7 +1244,7 @@ mod tests { let editor = workspace.update_in(&mut cx, |workspace, window, cx| { let editor = cx.new(|cx| { Editor::new( - editor::EditorMode::full(), + editor::EditorDisplayMode::full(), multi_buffer::MultiBuffer::build_simple("", cx), None, window, diff --git a/crates/agent_ui/src/inline_prompt_editor.rs b/crates/agent_ui/src/inline_prompt_editor.rs index a626122769..4009272816 100644 --- a/crates/agent_ui/src/inline_prompt_editor.rs +++ b/crates/agent_ui/src/inline_prompt_editor.rs @@ -16,7 +16,8 @@ use db::kvp::Dismissable; use editor::actions::Paste; use editor::display_map::EditorMargins; use editor::{ - ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer, + ContextMenuOptions, Editor, EditorDisplayMode, EditorElement, EditorEvent, EditorStyle, + MultiBuffer, actions::{MoveDown, MoveUp}, }; use feature_flags::{FeatureFlagAppExt as _, ZedProFeatureFlag}; @@ -869,7 +870,7 @@ impl PromptEditor { let prompt_editor = cx.new(|cx| { let mut editor = Editor::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(Self::MAX_LINES as usize), }, @@ -1048,7 +1049,7 @@ impl PromptEditor { let prompt_editor = cx.new(|cx| { let mut editor = Editor::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(Self::MAX_LINES as usize), }, diff --git a/crates/agent_ui/src/message_editor.rs b/crates/agent_ui/src/message_editor.rs index bed10e90a7..f94205491f 100644 --- a/crates/agent_ui/src/message_editor.rs +++ b/crates/agent_ui/src/message_editor.rs @@ -22,8 +22,8 @@ use collections::{HashMap, HashSet}; use editor::actions::{MoveUp, Paste}; use editor::display_map::CreaseId; use editor::{ - Addon, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, - EditorEvent, EditorMode, EditorStyle, MultiBuffer, + Addon, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorDisplayMode, + EditorElement, EditorEvent, EditorStyle, MultiBuffer, }; use file_icons::FileIcons; use fs::Fs; @@ -114,8 +114,17 @@ pub(crate) fn create_editor( let editor = cx.new(|cx| { let buffer = cx.new(|cx| Buffer::local("", cx).with_language(Arc::new(language), cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); + let settings = agent_settings::AgentSettings::get_global(cx); + + let editor_mode = match settings.editor_mode { + agent_settings::AgentEditorMode::EditorModeOverride(mode) => mode, + agent_settings::AgentEditorMode::Inherit => { + editor_mode_setting::EditorModeSetting::get_global(cx).0 + } + }; + let mut editor = Editor::new( - editor::EditorMode::AutoHeight { + editor::EditorDisplayMode::AutoHeight { min_lines, max_lines, }, @@ -127,7 +136,7 @@ pub(crate) fn create_editor( editor.set_placeholder_text("Message the agent – @ to include context", cx); editor.set_show_indent_guides(false, cx); editor.set_soft_wrap(); - editor.set_use_modal_editing(true); + editor.set_editor_mode(editor_mode, cx); editor.set_context_menu_options(ContextMenuOptions { min_entries_visible: 12, max_entries_visible: 12, @@ -227,6 +236,18 @@ impl MessageEditor { cx.observe(&thread.read(cx).action_log().clone(), |_, _, cx| { cx.notify() }), + cx.observe_global::(move |this, cx| { + let settings = agent_settings::AgentSettings::get_global(cx); + let editor_mode = match settings.editor_mode { + agent_settings::AgentEditorMode::EditorModeOverride(mode) => mode, + agent_settings::AgentEditorMode::Inherit => { + editor_mode_setting::EditorModeSetting::get_global(cx).0 + } + }; + this.editor.update(cx, |editor, cx| { + editor.set_editor_mode(editor_mode, cx); + }); + }), ]; let model_selector = cx.new(|cx| { @@ -299,13 +320,13 @@ impl MessageEditor { self.editor_is_expanded = is_expanded; self.editor.update(cx, |editor, _| { if self.editor_is_expanded { - editor.set_mode(EditorMode::Full { + editor.set_display_mode(EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: false, show_active_line_background: false, sized_by_content: false, }) } else { - editor.set_mode(EditorMode::AutoHeight { + editor.set_display_mode(EditorDisplayMode::AutoHeight { min_lines: MIN_EDITOR_LINES, max_lines: Some(MAX_EDITOR_LINES), }) diff --git a/crates/assistant_tools/src/edit_file_tool.rs b/crates/assistant_tools/src/edit_file_tool.rs index 95b01c40eb..165a46a5cb 100644 --- a/crates/assistant_tools/src/edit_file_tool.rs +++ b/crates/assistant_tools/src/edit_file_tool.rs @@ -11,7 +11,7 @@ use assistant_tool::{ AnyToolCard, Tool, ToolCard, ToolResult, ToolResultContent, ToolResultOutput, ToolUseStatus, }; use buffer_diff::{BufferDiff, BufferDiffSnapshot}; -use editor::{Editor, EditorMode, MinimapVisibility, MultiBuffer, PathKey}; +use editor::{Editor, EditorDisplayMode, MinimapVisibility, MultiBuffer, PathKey}; use futures::StreamExt; use gpui::{ Animation, AnimationExt, AnyWindowHandle, App, AppContext, AsyncApp, Entity, Task, @@ -582,7 +582,7 @@ impl EditFileToolCard { let editor = cx.new(|cx| { let mut editor = Editor::new( - EditorMode::Full { + EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: false, show_active_line_background: false, sized_by_content: true, diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index df4125860f..a7a9fdf61c 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -76,6 +76,7 @@ util.workspace = true workspace-hack.workspace = true workspace.workspace = true zed_actions.workspace = true +editor_mode_setting.workspace = true [dev-dependencies] dap = { workspace = true, features = ["test-support"] } diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index a801cedd26..f4b43aeacd 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -7,6 +7,7 @@ use anyhow::Result; use collections::HashMap; use dap::{CompletionItem, CompletionItemType, OutputEvent}; use editor::{Bias, CompletionProvider, Editor, EditorElement, EditorStyle, ExcerptId}; +use editor_mode_setting::EditorMode; use fuzzy::StringMatchCandidate; use gpui::{ Action as _, AppContext, Context, Corner, Entity, FocusHandle, Focusable, HighlightStyle, Hsla, @@ -74,7 +75,7 @@ impl Console { editor.set_show_wrap_guides(false, cx); editor.set_show_indent_guides(false, cx); editor.set_show_edit_predictions(Some(false), window, cx); - editor.set_use_modal_editing(false); + editor.set_editor_mode(EditorMode::Default, cx); editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx); editor }); diff --git a/crates/debugger_ui/src/tests/debugger_panel.rs b/crates/debugger_ui/src/tests/debugger_panel.rs index ab6d5cb960..e3fde56b20 100644 --- a/crates/debugger_ui/src/tests/debugger_panel.rs +++ b/crates/debugger_ui/src/tests/debugger_panel.rs @@ -14,7 +14,7 @@ use dap::{ }, }; use editor::{ - ActiveDebugLine, Editor, EditorMode, MultiBuffer, + ActiveDebugLine, Editor, EditorDisplayMode, MultiBuffer, actions::{self}, }; use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext}; @@ -1121,7 +1121,7 @@ async fn test_send_breakpoints_when_editor_has_been_saved( let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project.clone()), window, @@ -1290,7 +1290,7 @@ async fn test_unsetting_breakpoints_on_clear_breakpoint_action( let (first_editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(first, cx), Some(project.clone()), window, @@ -1300,7 +1300,7 @@ async fn test_unsetting_breakpoints_on_clear_breakpoint_action( let (second_editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(second, cx), Some(project.clone()), window, @@ -1513,7 +1513,7 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T let (main_editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(main_buffer, cx), Some(project.clone()), window, @@ -1523,7 +1523,7 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T let (second_editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(second_buffer, cx), Some(project.clone()), window, diff --git a/crates/debugger_ui/src/tests/inline_values.rs b/crates/debugger_ui/src/tests/inline_values.rs index 9f921ec969..87abb2a81b 100644 --- a/crates/debugger_ui/src/tests/inline_values.rs +++ b/crates/debugger_ui/src/tests/inline_values.rs @@ -1,7 +1,7 @@ use std::{path::Path, sync::Arc}; use dap::{Scope, StackFrame, Variable, requests::Variables}; -use editor::{Editor, EditorMode, MultiBuffer}; +use editor::{Editor, EditorDisplayMode, MultiBuffer}; use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext}; use language::{Language, LanguageConfig, LanguageMatcher, tree_sitter_python, tree_sitter_rust}; use project::{FakeFs, Project}; @@ -226,7 +226,7 @@ fn main() { let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project), window, @@ -1595,7 +1595,7 @@ def process_data(untyped_param, typed_param: int, another_typed: str): let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project), window, @@ -2093,7 +2093,7 @@ async fn test_inline_values_util( let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project), window, diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 339f98ae8b..90346a65d5 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -92,6 +92,7 @@ uuid.workspace = true workspace.workspace = true zed_actions.workspace = true workspace-hack.workspace = true +editor_mode_setting.workspace = true [dev-dependencies] ctor.workspace = true diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2af8e6c0e4..efc071a61e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -94,6 +94,7 @@ use convert_case::{Case, Casing}; use dap::TelemetrySpawnLocation; use display_map::*; use edit_prediction::{EditPredictionProvider, EditPredictionProviderHandle}; +use editor_mode_setting::{EditorMode, EditorModeSetting}; use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings}; use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line}; use futures::{ @@ -494,7 +495,7 @@ pub enum SelectMode { } #[derive(Clone, PartialEq, Eq, Debug)] -pub enum EditorMode { +pub enum EditorDisplayMode { SingleLine, AutoHeight { min_lines: usize, @@ -513,7 +514,7 @@ pub enum EditorMode { }, } -impl EditorMode { +impl EditorDisplayMode { pub fn full() -> Self { Self::Full { scale_ui_elements_with_buffer_font_size: true, @@ -757,7 +758,7 @@ pub enum MinimapVisibility { } impl MinimapVisibility { - fn for_mode(mode: &EditorMode, cx: &App) -> Self { + fn for_display_mode(mode: &EditorDisplayMode, cx: &App) -> Self { if mode.is_full() { Self::Enabled { setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(), @@ -1044,7 +1045,7 @@ pub struct Editor { show_cursor_names: bool, hovered_cursors: HashMap>, pub show_local_selections: bool, - mode: EditorMode, + display_mode: EditorDisplayMode, show_breadcrumbs: bool, show_gutter: bool, show_scrollbars: ScrollbarAxes, @@ -1092,7 +1093,6 @@ pub struct Editor { autoindent_mode: Option, workspace: Option<(WeakEntity, Option)>, input_enabled: bool, - use_modal_editing: bool, read_only: bool, leader_id: Option, remote_id: Option, @@ -1180,6 +1180,7 @@ pub struct Editor { next_color_inlay_id: usize, colors: Option, folding_newlines: Task<()>, + editor_mode: editor_mode_setting::EditorMode, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -1202,7 +1203,7 @@ impl NextScrollCursorCenterTopBottom { #[derive(Clone)] pub struct EditorSnapshot { - pub mode: EditorMode, + pub display_mode: EditorDisplayMode, show_gutter: bool, show_line_numbers: Option, show_git_diff_gutter: Option, @@ -1670,13 +1671,13 @@ impl Editor { pub fn single_line(window: &mut Window, cx: &mut Context) -> Self { let buffer = cx.new(|cx| Buffer::local("", cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::SingleLine, buffer, None, window, cx) + Self::new(EditorDisplayMode::SingleLine, buffer, None, window, cx) } pub fn multi_line(window: &mut Window, cx: &mut Context) -> Self { let buffer = cx.new(|cx| Buffer::local("", cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::full(), buffer, None, window, cx) + Self::new(EditorDisplayMode::full(), buffer, None, window, cx) } pub fn auto_height( @@ -1688,7 +1689,7 @@ impl Editor { let buffer = cx.new(|cx| Buffer::local("", cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); Self::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines, max_lines: Some(max_lines), }, @@ -1709,7 +1710,7 @@ impl Editor { let buffer = cx.new(|cx| Buffer::local("", cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); Self::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines, max_lines: None, }, @@ -1727,7 +1728,7 @@ impl Editor { cx: &mut Context, ) -> Self { let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::full(), buffer, project, window, cx) + Self::new(EditorDisplayMode::full(), buffer, project, window, cx) } pub fn for_multibuffer( @@ -1736,12 +1737,12 @@ impl Editor { window: &mut Window, cx: &mut Context, ) -> Self { - Self::new(EditorMode::full(), buffer, project, window, cx) + Self::new(EditorDisplayMode::full(), buffer, project, window, cx) } pub fn clone(&self, window: &mut Window, cx: &mut Context) -> Self { let mut clone = Self::new( - self.mode.clone(), + self.display_mode.clone(), self.buffer.clone(), self.project.clone(), window, @@ -1762,17 +1763,17 @@ impl Editor { } pub fn new( - mode: EditorMode, + display_mode: EditorDisplayMode, buffer: Entity, project: Option>, window: &mut Window, cx: &mut Context, ) -> Self { - Editor::new_internal(mode, buffer, project, None, window, cx) + Editor::new_internal(display_mode, buffer, project, None, window, cx) } fn new_internal( - mode: EditorMode, + display_mode: EditorDisplayMode, buffer: Entity, project: Option>, display_map: Option>, @@ -1780,12 +1781,12 @@ impl Editor { cx: &mut Context, ) -> Self { debug_assert!( - display_map.is_none() || mode.is_minimap(), + display_map.is_none() || display_mode.is_minimap(), "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!" ); - let full_mode = mode.is_full(); - let is_minimap = mode.is_minimap(); + let full_mode = display_mode.is_full(); + let is_minimap = display_mode.is_minimap(); let diagnostics_max_severity = if full_mode { EditorSettings::get_global(cx) .diagnostics_max_severity @@ -1854,8 +1855,8 @@ impl Editor { blink_manager }); - let soft_wrap_mode_override = - matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None); + let soft_wrap_mode_override = matches!(display_mode, EditorDisplayMode::SingleLine) + .then(|| language_settings::SoftWrap::None); let mut project_subscriptions = Vec::new(); if full_mode && let Some(project) = project.as_ref() { @@ -2034,15 +2035,19 @@ impl Editor { .detach(); } - let show_indent_guides = - if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) { - Some(false) - } else { - None - }; + let show_indent_guides = if matches!( + display_mode, + EditorDisplayMode::SingleLine | EditorDisplayMode::Minimap { .. } + ) { + Some(false) + } else { + None + }; - let breakpoint_store = match (&mode, project.as_ref()) { - (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()), + let breakpoint_store = match (&display_mode, project.as_ref()) { + (EditorDisplayMode::Full { .. }, Some(project)) => { + Some(project.read(cx).breakpoint_store()) + } _ => None, }; @@ -2098,8 +2103,8 @@ impl Editor { horizontal: full_mode, vertical: full_mode, }, - minimap_visibility: MinimapVisibility::for_mode(&mode, cx), - offset_content: !matches!(mode, EditorMode::SingleLine), + minimap_visibility: MinimapVisibility::for_display_mode(&display_mode, cx), + offset_content: !matches!(display_mode, EditorDisplayMode::SingleLine), show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs, show_gutter: full_mode, show_line_numbers: (!full_mode).then_some(false), @@ -2147,7 +2152,6 @@ impl Editor { collapse_matches: false, workspace: None, input_enabled: !is_minimap, - use_modal_editing: full_mode, read_only: is_minimap, use_autoclose: true, use_auto_surround: true, @@ -2255,9 +2259,14 @@ impl Editor { .hide_mouse .unwrap_or_default(), change_list: ChangeList::new(), - mode, + display_mode, selection_drag_state: SelectionDragState::None, folding_newlines: Task::ready(()), + editor_mode: if full_mode { + EditorModeSetting::get_global(cx).0 + } else { + editor_mode_setting::EditorMode::default() + }, }; if is_minimap { @@ -2293,7 +2302,7 @@ impl Editor { } } EditorEvent::Edited { .. } => { - if !vim_enabled(cx) { + if !editor.editor_mode().is_modal() { let (map, selections) = editor.selections.all_adjusted_display(cx); let pop_state = editor .change_list @@ -2383,7 +2392,7 @@ impl Editor { editor.update_lsp_data(false, None, window, cx); } - if editor.mode.is_full() { + if editor.display_mode.is_full() { editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx); } @@ -2452,18 +2461,18 @@ impl Editor { ) -> KeyContext { let mut key_context = KeyContext::new_with_defaults(); key_context.add("Editor"); - let mode = match self.mode { - EditorMode::SingleLine => "single_line", - EditorMode::AutoHeight { .. } => "auto_height", - EditorMode::Minimap { .. } => "minimap", - EditorMode::Full { .. } => "full", + let display_mode = match self.display_mode { + EditorDisplayMode::SingleLine => "single_line", + EditorDisplayMode::AutoHeight { .. } => "auto_height", + EditorDisplayMode::Minimap { .. } => "minimap", + EditorDisplayMode::Full { .. } => "full", }; if EditorSettings::jupyter_enabled(cx) { key_context.add("jupyter"); } - key_context.set("mode", mode); + key_context.set("mode", display_mode); if self.pending_rename.is_some() { key_context.add("renaming"); } @@ -2720,7 +2729,7 @@ impl Editor { .flatten(); EditorSnapshot { - mode: self.mode.clone(), + display_mode: self.display_mode.clone(), show_gutter: self.show_gutter, show_line_numbers: self.show_line_numbers, show_git_diff_gutter: self.show_git_diff_gutter, @@ -2757,12 +2766,12 @@ impl Editor { .excerpt_containing(self.selections.newest_anchor().head(), cx) } - pub fn mode(&self) -> &EditorMode { - &self.mode + pub fn display_mode(&self) -> &EditorDisplayMode { + &self.display_mode } - pub fn set_mode(&mut self, mode: EditorMode) { - self.mode = mode; + pub fn set_display_mode(&mut self, mode: EditorDisplayMode) { + self.display_mode = mode; } pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> { @@ -2994,12 +3003,19 @@ impl Editor { }) } - pub fn set_use_modal_editing(&mut self, to: bool) { - self.use_modal_editing = to; + pub fn set_editor_mode(&mut self, to: editor_mode_setting::EditorMode, cx: &mut Context) { + let from = self.editor_mode; + if from != to { + self.editor_mode = to; + cx.emit(EditorEvent::EditorModeChanged { + old_mode: from, + new_mode: to, + }); + } } - pub fn use_modal_editing(&self) -> bool { - self.use_modal_editing + pub fn editor_mode(&self) -> editor_mode_setting::EditorMode { + self.editor_mode } fn selections_did_change( @@ -3202,7 +3218,7 @@ impl Editor { use text::ToOffset as _; use text::ToPoint as _; - if self.mode.is_minimap() + if self.display_mode.is_minimap() || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None { return; @@ -3885,7 +3901,7 @@ impl Editor { return; } - if self.mode.is_full() + if self.display_mode.is_full() && self.change_selections(Default::default(), window, cx, |s| s.try_cancel()) { return; @@ -3928,7 +3944,9 @@ impl Editor { return true; } - if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) { + if self.display_mode.is_full() + && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) + { self.dismiss_diagnostics(cx); return true; } @@ -5142,7 +5160,7 @@ impl Editor { } fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context) { - if self.semantics_provider.is_none() || !self.mode.is_full() { + if self.semantics_provider.is_none() || !self.display_mode.is_full() { return; } @@ -6819,7 +6837,7 @@ impl Editor { &mut self, cx: &mut Context, ) -> Option<(String, Range)> { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { return None; } if !EditorSettings::get_global(cx).selection_highlight { @@ -6918,7 +6936,7 @@ impl Editor { fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context) { struct NewlineFold; let type_id = std::any::TypeId::of::(); - if !self.mode.is_single_line() { + if !self.display_mode.is_single_line() { return; } let snapshot = self.snapshot(window, cx); @@ -7144,7 +7162,7 @@ impl Editor { buffer_position: language::Anchor, cx: &App, ) -> EditPredictionSettings { - if !self.mode.is_full() + if !self.display_mode.is_full() || !self.show_edit_predictions_override.unwrap_or(true) || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx) { @@ -8483,7 +8501,7 @@ impl Editor { window: &mut Window, cx: &mut App, ) -> Option<(AnyElement, gpui::Point)> { - if self.mode().is_minimap() { + if self.display_mode().is_minimap() { return None; } let active_edit_prediction = self.active_edit_prediction.as_ref()?; @@ -9920,7 +9938,7 @@ impl Editor { } pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context) { - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -9933,7 +9951,7 @@ impl Editor { } pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context) { - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -10056,7 +10074,7 @@ impl Editor { if self.read_only(cx) { return; } - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -10164,7 +10182,7 @@ impl Editor { if self.read_only(cx) { return; } - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -10241,7 +10259,7 @@ impl Editor { if self.read_only(cx) { return; } - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -11486,7 +11504,7 @@ impl Editor { pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context) { self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx); - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -11597,7 +11615,7 @@ impl Editor { cx: &mut Context, ) { self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx); - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -11749,7 +11767,7 @@ impl Editor { pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context) { self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx); - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -12460,7 +12478,7 @@ impl Editor { return; } - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -12503,7 +12521,7 @@ impl Editor { return; } - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -12540,7 +12558,7 @@ impl Editor { return; } - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -12638,7 +12656,7 @@ impl Editor { return; } - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -12688,7 +12706,7 @@ impl Editor { pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context) { self.take_rename(true, window, cx); - if self.mode.is_single_line() { + if self.display_mode.is_single_line() { cx.propagate(); return; } @@ -12762,7 +12780,7 @@ impl Editor { return; } - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13246,7 +13264,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13267,7 +13285,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13288,7 +13306,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13309,7 +13327,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13330,7 +13348,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13355,7 +13373,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13380,7 +13398,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13405,7 +13423,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13430,7 +13448,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13451,7 +13469,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13472,7 +13490,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13493,7 +13511,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13514,7 +13532,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -13539,7 +13557,7 @@ impl Editor { } pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } @@ -14588,7 +14606,7 @@ impl Editor { let advance_downwards = action.advance_downwards && selections_on_single_row && !selections_selecting - && !matches!(this.mode, EditorMode::SingleLine); + && !matches!(this.display_mode, EditorDisplayMode::SingleLine); if advance_downwards { let snapshot = this.buffer.read(cx).snapshot(cx); @@ -16913,7 +16931,7 @@ impl Editor { } pub fn diagnostics_enabled(&self) -> bool { - self.diagnostics_enabled && self.mode.is_full() + self.diagnostics_enabled && self.display_mode.is_full() } pub fn inline_diagnostics_enabled(&self) -> bool { @@ -17073,7 +17091,7 @@ impl Editor { window: &Window, cx: &mut Context, ) -> Option<()> { - if !self.mode().is_full() { + if !self.display_mode().is_full() { return None; } let pull_diagnostics_settings = ProjectSettings::get_global(cx) @@ -18355,7 +18373,7 @@ impl Editor { const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK; let mut minimap = Editor::new_internal( - EditorMode::Minimap { + EditorDisplayMode::Minimap { parent: cx.weak_entity(), }, self.buffer.clone(), @@ -18457,7 +18475,7 @@ impl Editor { // We intentionally do not inform the display map about the minimap style // so that wrapping is not recalculated and stays consistent for the editor // and its linked minimap. - if !self.mode.is_minimap() { + if !self.display_mode.is_minimap() { let rem_size = window.rem_size(); self.display_map.update(cx, |map, cx| { map.set_font( @@ -19003,7 +19021,9 @@ impl Editor { } pub fn render_git_blame_gutter(&self, cx: &App) -> bool { - !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx) + !self.display_mode().is_minimap() + && self.show_git_blame_gutter + && self.has_blame_entries(cx) } pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool { @@ -20274,9 +20294,9 @@ impl Editor { let project_settings = ProjectSettings::get_global(cx); self.serialize_dirty_buffers = - !self.mode.is_minimap() && project_settings.session.restore_unsaved_buffers; + !self.display_mode.is_minimap() && project_settings.session.restore_unsaved_buffers; - if self.mode.is_full() { + if self.display_mode.is_full() { let show_inline_diagnostics = project_settings.diagnostics.inline.enabled; let inline_blame_enabled = project_settings.git.inline_blame_enabled(); if self.show_inline_diagnostics != show_inline_diagnostics { @@ -20294,7 +20314,7 @@ impl Editor { != minimap_settings.minimap_enabled() { self.set_minimap_visibility( - MinimapVisibility::for_mode(self.mode(), cx), + MinimapVisibility::for_display_mode(self.display_mode(), cx), window, cx, ); @@ -20643,7 +20663,7 @@ impl Editor { .and_then(|e| e.to_str()) .map(|a| a.to_string())); - let vim_mode = vim_enabled(cx); + let vim_mode = self.editor_mode.is_modal(); let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider; let copilot_enabled = edit_predictions_provider @@ -21127,7 +21147,7 @@ impl Editor { } pub fn register_addon(&mut self, instance: T) { - if self.mode.is_minimap() { + if self.display_mode.is_minimap() { return; } self.addons @@ -21180,7 +21200,7 @@ impl Editor { cx: &mut Context, ) { if self.is_singleton(cx) - && !self.mode.is_minimap() + && !self.display_mode.is_minimap() && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None { let buffer_snapshot = OnceCell::new(); @@ -21234,13 +21254,6 @@ impl Editor { } } -fn vim_enabled(cx: &App) -> bool { - cx.global::() - .raw_user_settings() - .get("vim_mode") - == Some(&serde_json::Value::Bool(true)) -} - fn process_completion_for_edit( completion: &Completion, intent: CompletionIntent, @@ -22897,6 +22910,10 @@ pub enum EditorEvent { anchor: Anchor, is_deactivate: bool, }, + EditorModeChanged { + new_mode: EditorMode, + old_mode: EditorMode, + }, } impl EventEmitter for Editor {} @@ -22911,8 +22928,8 @@ impl Render for Editor { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { let settings = ThemeSettings::get_global(cx); - let mut text_style = match self.mode { - EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle { + let mut text_style = match self.display_mode { + EditorDisplayMode::SingleLine | EditorDisplayMode::AutoHeight { .. } => TextStyle { color: cx.theme().colors().editor_foreground, font_family: settings.ui_font.family.clone(), font_features: settings.ui_font.features.clone(), @@ -22922,7 +22939,7 @@ impl Render for Editor { line_height: relative(settings.buffer_line_height.value()), ..Default::default() }, - EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle { + EditorDisplayMode::Full { .. } | EditorDisplayMode::Minimap { .. } => TextStyle { color: cx.theme().colors().editor_foreground, font_family: settings.buffer_font.family.clone(), font_features: settings.buffer_font.features.clone(), @@ -22937,11 +22954,11 @@ impl Render for Editor { text_style.refine(text_style_refinement) } - let background = match self.mode { - EditorMode::SingleLine => cx.theme().system().transparent, - EditorMode::AutoHeight { .. } => cx.theme().system().transparent, - EditorMode::Full { .. } => cx.theme().colors().editor_background, - EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7), + let background = match self.display_mode { + EditorDisplayMode::SingleLine => cx.theme().system().transparent, + EditorDisplayMode::AutoHeight { .. } => cx.theme().system().transparent, + EditorDisplayMode::Full { .. } => cx.theme().colors().editor_background, + EditorDisplayMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7), }; EditorElement::new( @@ -23626,7 +23643,7 @@ impl BreakpointPromptEditor { let prompt = cx.new(|cx| { let mut prompt = Editor::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(Self::MAX_LINES as usize), }, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 96261fdb2c..9601c70d83 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -10075,7 +10075,7 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) { }); let multi_buffer_editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(project.clone()), window, @@ -10252,7 +10252,7 @@ async fn test_autosave_with_dirty_buffers(cx: &mut TestAppContext) { let editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(project.clone()), window, @@ -12646,7 +12646,7 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte .update(cx, |_, window, cx| { cx.new(|cx| { Editor::new( - EditorMode::Full { + EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: false, show_active_line_background: false, sized_by_content: false, @@ -17433,7 +17433,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) { let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx); let multi_buffer_editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(project.clone()), window, @@ -17900,8 +17900,9 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) { multibuffer }); - let editor = - cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx)); + let editor = cx.add_window(|window, cx| { + Editor::new(EditorDisplayMode::full(), multi_buffer, None, window, cx) + }); editor .update(cx, |editor, _window, cx| { for (buffer, diff_base) in [ @@ -18011,8 +18012,9 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) { multibuffer }); - let editor = - cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx)); + let editor = cx.add_window(|window, cx| { + Editor::new(EditorDisplayMode::full(), multi_buffer, None, window, cx) + }); editor .update(cx, |editor, _window, cx| { let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx)); @@ -19693,7 +19695,13 @@ async fn test_display_diff_hunks(cx: &mut TestAppContext) { }); let editor = cx.add_window(|window, cx| { - Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx) + Editor::new( + EditorDisplayMode::full(), + multibuffer, + Some(project), + window, + cx, + ) }); cx.run_until_parked(); @@ -20205,7 +20213,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) { let editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(project.clone()), window, @@ -20332,7 +20340,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); let multi_buffer_editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer.clone(), Some(project.clone()), window, @@ -20489,7 +20497,7 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { let multi_buffer_editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(project.clone()), window, @@ -20607,7 +20615,7 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test }); let multi_buffer_editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(project.clone()), window, @@ -20659,7 +20667,13 @@ async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContex ], cx, ); - let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx); + let mut editor = Editor::new( + EditorDisplayMode::full(), + multi_buffer.clone(), + None, + window, + cx, + ); let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids(); // fold all but the second buffer, so that we test navigating between two @@ -20971,7 +20985,7 @@ async fn assert_highlighted_edits( ) { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(text, cx); - Editor::new(EditorMode::full(), buffer, None, window, cx) + Editor::new(EditorDisplayMode::full(), buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); @@ -21131,7 +21145,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) { let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project.clone()), window, @@ -21245,7 +21259,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) { let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project.clone()), window, @@ -21415,7 +21429,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) { let (editor, cx) = cx.add_window_view(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), MultiBuffer::build_from_buffer(buffer, cx), Some(project.clone()), window, @@ -22373,7 +22387,7 @@ async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) { let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx); let editor = cx.new_window_entity(|window, cx| { Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), buffer, Some(project.clone()), window, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 797b0d6634..7adbff2629 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3,7 +3,7 @@ use crate::{ CodeActionSource, ColumnarMode, ConflictsOurs, ConflictsOursMarker, ConflictsOuter, ConflictsTheirs, ConflictsTheirsMarker, ContextMenuPlacement, CursorShape, CustomBlockId, DisplayDiffHunk, DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, - EditDisplayMode, EditPrediction, Editor, EditorMode, EditorSettings, EditorSnapshot, + EditDisplayMode, EditPrediction, Editor, EditorDisplayMode, EditorSettings, EditorSnapshot, EditorStyle, FILE_HEADER_HEIGHT, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, InlayHintRefreshReason, JumpData, LineDown, LineHighlight, LineUp, MAX_LINE_LEN, MINIMAP_FONT_SIZE, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts, PageDown, @@ -1816,7 +1816,7 @@ impl EditorElement { } } - if !snapshot.mode.is_full() + if !snapshot.display_mode.is_full() || minimap_width.is_zero() || matches!( minimap_settings.show, @@ -3212,7 +3212,7 @@ impl EditorElement { cx: &mut App, ) -> Vec> { let include_fold_statuses = EditorSettings::get_global(cx).gutter.folds - && snapshot.mode.is_full() + && snapshot.display_mode.is_full() && self.editor.read(cx).is_singleton(cx); if include_fold_statuses { row_infos @@ -3314,7 +3314,7 @@ impl EditorElement { style, MAX_LINE_LEN, rows.len(), - &snapshot.mode, + &snapshot.display_mode, editor_width, is_row_soft_wrapped, window, @@ -5282,14 +5282,14 @@ impl EditorElement { if matches!( layout.mode, - EditorMode::Full { .. } | EditorMode::Minimap { .. } + EditorDisplayMode::Full { .. } | EditorDisplayMode::Minimap { .. } ) { let show_active_line_background = match layout.mode { - EditorMode::Full { + EditorDisplayMode::Full { show_active_line_background, .. } => show_active_line_background, - EditorMode::Minimap { .. } => true, + EditorDisplayMode::Minimap { .. } => true, _ => false, }; let mut active_rows = layout.active_rows.iter().peekable(); @@ -7311,7 +7311,7 @@ impl LineWithInvisibles { editor_style: &EditorStyle, max_line_len: usize, max_line_count: usize, - editor_mode: &EditorMode, + editor_mode: &EditorDisplayMode, text_width: Pixels, is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, window: &mut Window, @@ -7839,12 +7839,12 @@ impl EditorElement { /// /// This allows UI elements to scale based on the `buffer_font_size`. fn rem_size(&self, cx: &mut App) -> Option { - match self.editor.read(cx).mode { - EditorMode::Full { + match self.editor.read(cx).display_mode { + EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: true, .. } - | EditorMode::Minimap { .. } => { + | EditorDisplayMode::Minimap { .. } => { let buffer_font_size = self.style.text.font_size; match buffer_font_size { AbsoluteLength::Pixels(pixels) => { @@ -7877,7 +7877,7 @@ impl EditorElement { } fn editor_with_selections(&self, cx: &App) -> Option> { - if let EditorMode::Minimap { parent } = self.editor.read(cx).mode() { + if let EditorDisplayMode::Minimap { parent } = self.editor.read(cx).display_mode() { parent.upgrade() } else { Some(self.editor.clone()) @@ -7909,8 +7909,8 @@ impl Element for EditorElement { self.editor.update(cx, |editor, cx| { editor.set_style(self.style.clone(), window, cx); - let layout_id = match editor.mode { - EditorMode::SingleLine => { + let layout_id = match editor.display_mode { + EditorDisplayMode::SingleLine => { let rem_size = window.rem_size(); let height = self.style.text.line_height_in_pixels(rem_size); let mut style = Style::default(); @@ -7918,7 +7918,7 @@ impl Element for EditorElement { style.size.width = relative(1.).into(); window.request_layout(style, None, cx) } - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines, max_lines, } => { @@ -7945,13 +7945,13 @@ impl Element for EditorElement { }, ) } - EditorMode::Minimap { .. } => { + EditorDisplayMode::Minimap { .. } => { let mut style = Style::default(); style.size.width = relative(1.).into(); style.size.height = relative(1.).into(); window.request_layout(style, None, cx) } - EditorMode::Full { + EditorDisplayMode::Full { sized_by_content, .. } => { let mut style = Style::default(); @@ -7990,7 +7990,7 @@ impl Element for EditorElement { ..Default::default() }; - let is_minimap = self.editor.read(cx).mode.is_minimap(); + let is_minimap = self.editor.read(cx).display_mode.is_minimap(); if !is_minimap { let focus_handle = self.editor.focus_handle(cx); @@ -8065,8 +8065,9 @@ impl Element for EditorElement { editor.set_visible_column_count(editor_width / em_advance); if matches!( - editor.mode, - EditorMode::AutoHeight { .. } | EditorMode::Minimap { .. } + editor.display_mode, + EditorDisplayMode::AutoHeight { .. } + | EditorDisplayMode::Minimap { .. } ) { snapshot } else { @@ -8112,10 +8113,10 @@ impl Element for EditorElement { // The max scroll position for the top of the window let max_scroll_top = if matches!( - snapshot.mode, - EditorMode::SingleLine - | EditorMode::AutoHeight { .. } - | EditorMode::Full { + snapshot.display_mode, + EditorDisplayMode::SingleLine + | EditorDisplayMode::AutoHeight { .. } + | EditorDisplayMode::Full { sized_by_content: true, .. } @@ -8980,7 +8981,7 @@ impl Element for EditorElement { None, ); - let mode = snapshot.mode.clone(); + let mode = snapshot.display_mode.clone(); let (diff_hunk_controls, diff_hunk_control_bounds) = if is_read_only { (vec![], vec![]) @@ -9209,7 +9210,7 @@ pub struct EditorLayout { content_origin: gpui::Point, scrollbars_layout: Option, minimap: Option, - mode: EditorMode, + mode: EditorDisplayMode, wrap_guides: SmallVec<[(Pixels, bool); 2]>, indent_guides: Option>, visible_display_row_range: Range, @@ -9788,7 +9789,7 @@ pub fn layout_line( style, MAX_LINE_LEN, 1, - &snapshot.mode, + &snapshot.display_mode, text_width, is_row_soft_wrapped, window, @@ -10197,7 +10198,7 @@ mod tests { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(&"a ".to_string().repeat(100), cx); let mut editor = Editor::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: None, }, @@ -10233,7 +10234,7 @@ mod tests { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(&"a ".to_string().repeat(100), cx); - let mut editor = Editor::new(EditorMode::full(), buffer, None, window, cx); + let mut editor = Editor::new(EditorDisplayMode::full(), buffer, None, window, cx); editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); editor }); @@ -10260,7 +10261,7 @@ mod tests { init_test(cx, |_| {}); let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); - Editor::new(EditorMode::full(), buffer, None, window, cx) + Editor::new(EditorDisplayMode::full(), buffer, None, window, cx) }); let editor = window.root(cx).unwrap(); @@ -10361,7 +10362,7 @@ mod tests { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); - Editor::new(EditorMode::full(), buffer, None, window, cx) + Editor::new(EditorDisplayMode::full(), buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); let editor = window.root(cx).unwrap(); @@ -10432,7 +10433,7 @@ mod tests { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple("", cx); - Editor::new(EditorMode::full(), buffer, None, window, cx) + Editor::new(EditorDisplayMode::full(), buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); let editor = window.root(cx).unwrap(); @@ -10518,7 +10519,7 @@ mod tests { let actual_invisibles = collect_invisibles_from_new_editor( cx, - EditorMode::full(), + EditorDisplayMode::full(), input_text, px(500.0), show_line_numbers, @@ -10536,8 +10537,8 @@ mod tests { }); for editor_mode_without_invisibles in [ - EditorMode::SingleLine, - EditorMode::AutoHeight { + EditorDisplayMode::SingleLine, + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(100), }, @@ -10615,7 +10616,7 @@ mod tests { let actual_invisibles = collect_invisibles_from_new_editor( cx, - EditorMode::full(), + EditorDisplayMode::full(), &input_text, px(editor_width), show_line_numbers, @@ -10654,7 +10655,7 @@ mod tests { fn collect_invisibles_from_new_editor( cx: &mut TestAppContext, - editor_mode: EditorMode, + editor_mode: EditorDisplayMode, input_text: &str, editor_width: Pixels, show_line_numbers: bool, diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index afc5767de0..a3aa255819 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1267,7 +1267,7 @@ impl SerializableItem for Editor { window: &mut Window, cx: &mut Context, ) -> Option>> { - if self.mode.is_minimap() { + if self.display_mode.is_minimap() { return None; } let mut serialize_dirty_buffers = self.serialize_dirty_buffers; @@ -1424,7 +1424,7 @@ impl Editor { cx: &mut Context, write: impl for<'a> FnOnce(&'a mut RestorationData) + 'static, ) { - if self.mode.is_minimap() || !WorkspaceSettings::get(None, cx).restore_on_file_reopen { + if self.display_mode.is_minimap() || !WorkspaceSettings::get(None, cx).restore_on_file_reopen { return; } diff --git a/crates/editor/src/lsp_colors.rs b/crates/editor/src/lsp_colors.rs index 29eb9f249a..967e32458d 100644 --- a/crates/editor/src/lsp_colors.rs +++ b/crates/editor/src/lsp_colors.rs @@ -150,7 +150,7 @@ impl Editor { _: &Window, cx: &mut Context, ) { - if !self.mode().is_full() { + if !self.display_mode().is_full() { return; } let Some(project) = self.project.clone() else { diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 3bc334c54c..0369ea00da 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -153,7 +153,7 @@ pub fn deploy_context_menu( } // Don't show context menu for inline editors - if !editor.mode().is_full() { + if !editor.display_mode().is_full() { return; } diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index 8231448618..b7e2e0e398 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -4,7 +4,7 @@ pub(crate) mod scroll_amount; use crate::editor_settings::ScrollBeyondLastLine; use crate::{ - Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings, + Anchor, DisplayPoint, DisplayRow, Editor, EditorDisplayMode, EditorEvent, EditorSettings, InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToPoint, display_map::{DisplaySnapshot, ToDisplayPoint}, hover_popover::hide_hover, @@ -675,7 +675,7 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } diff --git a/crates/editor/src/scroll/actions.rs b/crates/editor/src/scroll/actions.rs index f8104665f9..90055bb54f 100644 --- a/crates/editor/src/scroll/actions.rs +++ b/crates/editor/src/scroll/actions.rs @@ -1,6 +1,6 @@ use super::Axis; use crate::{ - Autoscroll, Editor, EditorMode, NextScreen, NextScrollCursorCenterTopBottom, + Autoscroll, Editor, EditorDisplayMode, NextScreen, NextScrollCursorCenterTopBottom, SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT, ScrollCursorBottom, ScrollCursorCenter, ScrollCursorCenterTopBottom, ScrollCursorTop, display_map::DisplayRow, }; @@ -16,7 +16,7 @@ impl Editor { return; } - if matches!(self.mode, EditorMode::SingleLine) { + if matches!(self.display_mode, EditorDisplayMode::SingleLine) { cx.propagate(); return; } diff --git a/crates/editor/src/scroll/autoscroll.rs b/crates/editor/src/scroll/autoscroll.rs index 057d622903..712e22250e 100644 --- a/crates/editor/src/scroll/autoscroll.rs +++ b/crates/editor/src/scroll/autoscroll.rs @@ -1,5 +1,5 @@ use crate::{ - DisplayRow, Editor, EditorMode, LineWithInvisibles, RowExt, SelectionEffects, + DisplayRow, Editor, EditorDisplayMode, LineWithInvisibles, RowExt, SelectionEffects, display_map::ToDisplayPoint, scroll::WasScrolled, }; use gpui::{Bounds, Context, Pixels, Window, px}; @@ -184,7 +184,7 @@ impl Editor { } } - let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) { + let margin = if matches!(self.display_mode, EditorDisplayMode::AutoHeight { .. }) { 0. } else { ((visible_lines - (target_bottom - target_top)) / 2.0).floor() diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 960fecf59a..f1b04328a2 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -5,7 +5,7 @@ use std::{rc::Rc, sync::LazyLock}; pub use crate::rust_analyzer_ext::expand_macro_recursively; use crate::{ - DisplayPoint, Editor, EditorMode, FoldPlaceholder, MultiBuffer, SelectionEffects, + DisplayPoint, Editor, EditorDisplayMode, FoldPlaceholder, MultiBuffer, SelectionEffects, display_map::{ Block, BlockPlacement, CustomBlockId, DisplayMap, DisplayRow, DisplaySnapshot, ToDisplayPoint, @@ -121,7 +121,7 @@ pub(crate) fn build_editor( window: &mut Window, cx: &mut Context, ) -> Editor { - Editor::new(EditorMode::full(), buffer, None, window, cx) + Editor::new(EditorDisplayMode::full(), buffer, None, window, cx) } pub(crate) fn build_editor_with_project( @@ -130,7 +130,7 @@ pub(crate) fn build_editor_with_project( window: &mut Window, cx: &mut Context, ) -> Editor { - Editor::new(EditorMode::full(), buffer, Some(project), window, cx) + Editor::new(EditorDisplayMode::full(), buffer, Some(project), window, cx) } #[derive(Default)] diff --git a/crates/vim_mode_setting/Cargo.toml b/crates/editor_mode_setting/Cargo.toml similarity index 64% rename from crates/vim_mode_setting/Cargo.toml rename to crates/editor_mode_setting/Cargo.toml index fbb7f30b4c..701f053da8 100644 --- a/crates/vim_mode_setting/Cargo.toml +++ b/crates/editor_mode_setting/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "vim_mode_setting" +name = "editor_mode_setting" version = "0.1.0" edition.workspace = true publish.workspace = true @@ -9,10 +9,13 @@ license = "GPL-3.0-or-later" workspace = true [lib] -path = "src/vim_mode_setting.rs" +path = "src/editor_mode_setting.rs" [dependencies] anyhow.workspace = true gpui.workspace = true +schemars.workspace = true settings.workspace = true workspace-hack.workspace = true +serde.workspace = true +serde_json.workspace = true diff --git a/crates/vim_mode_setting/LICENSE-GPL b/crates/editor_mode_setting/LICENSE-GPL similarity index 100% rename from crates/vim_mode_setting/LICENSE-GPL rename to crates/editor_mode_setting/LICENSE-GPL diff --git a/crates/editor_mode_setting/src/editor_mode_setting.rs b/crates/editor_mode_setting/src/editor_mode_setting.rs new file mode 100644 index 0000000000..7f714bff55 --- /dev/null +++ b/crates/editor_mode_setting/src/editor_mode_setting.rs @@ -0,0 +1,200 @@ +//! Contains the [`VimModeSetting`] and [`HelixModeSetting`] used to enable/disable Vim and Helix modes. +//! +//! This is in its own crate as we want other crates to be able to enable or +//! disable Vim/Helix modes without having to depend on the `vim` crate in its +//! entirety. + +use anyhow::Result; +use gpui::App; +use schemars::{JsonSchema, Schema, json_schema}; + +use serde::de::Error; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use settings::{Settings, SettingsSources}; +use std::borrow::Cow; +use std::fmt::Display; + +/// Initializes the `editor_mode_setting` crate. +pub fn init(cx: &mut App) { + EditorModeSetting::register(cx); +} + +/// Whether or not to enable Vim mode. +/// +/// Default: `EditMode::Default` +pub struct EditorModeSetting(pub EditorMode); + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub enum EditorMode { + #[default] + Default, + Vim(ModalMode), + Helix(ModalMode), +} + +impl JsonSchema for EditorMode { + fn schema_name() -> Cow<'static, str> { + "EditorMode".into() + } + + fn json_schema(_gen: &mut schemars::SchemaGenerator) -> Schema { + let options = Self::get_schema_options(); + json_schema!({ + "oneOf": options, + "description": "Editor mode" + }) + } +} + +impl<'de> Deserialize<'de> for EditorMode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "default" => Ok(EditorMode::Default), + "vim" => Ok(EditorMode::Vim(ModalMode::Normal)), + "vim_normal" => Ok(EditorMode::Vim(ModalMode::Normal)), + "vim_insert" => Ok(EditorMode::Vim(ModalMode::Insert)), + "vim_replace" => Ok(EditorMode::Vim(ModalMode::Replace)), + "vim_visual" => Ok(EditorMode::Vim(ModalMode::Visual)), + "vim_visual_line" => Ok(EditorMode::Vim(ModalMode::VisualLine)), + "vim_visual_block" => Ok(EditorMode::Vim(ModalMode::VisualBlock)), + "helix_experimental" => Ok(EditorMode::Helix(ModalMode::HelixNormal)), + _ => Err(D::Error::custom(format!("Unknown editor mode: {}", s))), + } + } +} + +impl Serialize for EditorMode { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + EditorMode::Default => "default", + EditorMode::Vim(ModalMode::Normal) => "vim", + EditorMode::Vim(ModalMode::Insert) => "vim_insert", + EditorMode::Vim(ModalMode::Replace) => "vim_replace", + EditorMode::Vim(ModalMode::Visual) => "vim_visual", + EditorMode::Vim(ModalMode::VisualLine) => "vim_visual_line", + EditorMode::Vim(ModalMode::VisualBlock) => "vim_visual_block", + EditorMode::Helix(ModalMode::HelixNormal) => "helix_experimental", + _ => return Err(serde::ser::Error::custom("unsupported editor mode variant")), + }; + serializer.serialize_str(s) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +pub enum ModalMode { + Normal, + Insert, + Replace, + Visual, + VisualLine, + VisualBlock, + HelixNormal, +} + +impl Display for ModalMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ModalMode::Normal => write!(f, "NORMAL"), + ModalMode::Insert => write!(f, "INSERT"), + ModalMode::Replace => write!(f, "REPLACE"), + ModalMode::Visual => write!(f, "VISUAL"), + ModalMode::VisualLine => write!(f, "VISUAL LINE"), + ModalMode::VisualBlock => write!(f, "VISUAL BLOCK"), + ModalMode::HelixNormal => write!(f, "HELIX NORMAL"), + } + } +} + +impl ModalMode { + pub fn is_visual(&self) -> bool { + match self { + Self::Visual | Self::VisualLine | Self::VisualBlock => true, + Self::Normal | Self::Insert | Self::Replace | Self::HelixNormal => false, + } + } +} + +impl Default for ModalMode { + fn default() -> Self { + Self::Normal + } +} + +impl Settings for EditorModeSetting { + const KEY: Option<&'static str> = Some("editor_mode"); + + type FileContent = Option; + + fn load(sources: SettingsSources, _: &mut App) -> Result { + Ok(Self( + sources + .user + .or(sources.server) + .copied() + .flatten() + .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), + )) + } + + fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) { + // TODO: could possibly check if any of the `vim.` keys are set? + } +} + +impl EditorMode { + pub fn is_modal(&self) -> bool { + matches!(self, EditorMode::Vim(_) | EditorMode::Helix(_)) + } + + pub fn vim() -> EditorMode { + EditorMode::Vim(ModalMode::default()) + } + + pub fn get_schema_options() -> Vec { + vec![ + serde_json::json!({ + "const": "default", + "description": "Standard editing mode" + }), + serde_json::json!({ + "const": "vim", + "description": "Vim normal mode" + }), + serde_json::json!({ + "const": "vim_normal", + "description": "Vim normal mode" + }), + serde_json::json!({ + "const": "vim_insert", + "description": "Vim insert mode" + }), + serde_json::json!({ + "const": "vim_replace", + "description": "Vim replace mode" + }), + serde_json::json!({ + "const": "vim_visual", + "description": "Vim visual mode" + }), + serde_json::json!({ + "const": "vim_visual_line", + "description": "Vim visual line mode" + }), + serde_json::json!({ + "const": "vim_visual_block", + "description": "Vim visual block mode" + }), + serde_json::json!({ + "const": "helix_experimental", + "description": "Helix mode (experimental)" + }), + ] + } +} diff --git a/crates/extensions_ui/Cargo.toml b/crates/extensions_ui/Cargo.toml index c31483d763..21c09d3b1a 100644 --- a/crates/extensions_ui/Cargo.toml +++ b/crates/extensions_ui/Cargo.toml @@ -37,7 +37,7 @@ telemetry.workspace = true theme.workspace = true ui.workspace = true util.workspace = true -vim_mode_setting.workspace = true +editor_mode_setting.workspace = true workspace-hack.workspace = true workspace.workspace = true zed_actions.workspace = true diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index fd504764b6..408fc556bf 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -10,6 +10,7 @@ use anyhow::Context as _; use client::{ExtensionMetadata, ExtensionProvides}; use collections::{BTreeMap, BTreeSet}; use editor::{Editor, EditorElement, EditorStyle}; +use editor_mode_setting::{EditorMode, EditorModeSetting}; use extension_host::{ExtensionManifest, ExtensionOperation, ExtensionStore}; use fuzzy::{StringMatchCandidate, match_strings}; use gpui::{ @@ -27,7 +28,6 @@ use ui::{ CheckboxWithLabel, Chip, ContextMenu, PopoverMenu, ScrollableHandle, Scrollbar, ScrollbarState, ToggleButton, Tooltip, prelude::*, }; -use vim_mode_setting::VimModeSetting; use workspace::{ Workspace, WorkspaceId, item::{Item, ItemEvent}, @@ -1335,17 +1335,26 @@ impl ExtensionsPage { .child(CheckboxWithLabel::new( "enable-vim", Label::new("Enable vim mode"), - if VimModeSetting::get_global(cx).0 { - ui::ToggleState::Selected - } else { - ui::ToggleState::Unselected + { + let editor_mode = EditorModeSetting::get_global(cx).0; + if editor_mode.is_modal() { + ui::ToggleState::Selected + } else { + ui::ToggleState::Unselected + } }, cx.listener(move |this, selection, _, cx| { telemetry::event!("Vim Mode Toggled", source = "Feature Upsell"); - this.update_settings::( + this.update_settings::( selection, cx, - |setting, value| *setting = Some(value), + |setting, value| { + *setting = Some(if value { + EditorMode::vim() + } else { + EditorMode::default() + }); + }, ); }), )), diff --git a/crates/git_ui/src/conflict_view.rs b/crates/git_ui/src/conflict_view.rs index ee1b82920d..590ada27f6 100644 --- a/crates/git_ui/src/conflict_view.rs +++ b/crates/git_ui/src/conflict_view.rs @@ -44,7 +44,7 @@ impl editor::Addon for ConflictAddon { pub fn register_editor(editor: &mut Editor, buffer: Entity, cx: &mut Context) { // Only show conflict UI for singletons and in the project diff. - if !editor.mode().is_full() + if !editor.display_mode().is_full() || (!editor.buffer().read(cx).is_singleton() && !editor.buffer().read(cx).all_diff_hunks_expanded()) { diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 958a609a09..456f043771 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -14,7 +14,7 @@ use anyhow::Context as _; use askpass::AskPassDelegate; use db::kvp::KEY_VALUE_STORE; use editor::{ - Editor, EditorElement, EditorMode, EditorSettings, MultiBuffer, ShowScrollbar, + Editor, EditorDisplayMode, EditorElement, EditorSettings, MultiBuffer, ShowScrollbar, scroll::ScrollbarAutoHide, }; use futures::StreamExt as _; @@ -394,7 +394,7 @@ pub(crate) fn commit_message_editor( let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx)); let max_lines = if in_panel { MAX_PANEL_EDITOR_LINES } else { 18 }; let mut commit_editor = Editor::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(max_lines), }, @@ -406,7 +406,7 @@ pub(crate) fn commit_message_editor( commit_editor.set_collaboration_hub(Box::new(project)); commit_editor.set_use_autoclose(false); commit_editor.set_show_gutter(false, cx); - commit_editor.set_use_modal_editing(true); + // commit_editor.set_use_modal_editing(true); TODO commit_editor.set_show_wrap_guides(false, cx); commit_editor.set_show_indent_guides(false, cx); let placeholder = placeholder.unwrap_or("Enter commit message".into()); diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index e60a3651aa..83c372b4c3 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -104,14 +104,14 @@ impl CursorPosition { cursor_position.update(cx, |cursor_position, cx| { cursor_position.selected_count = SelectionStats::default(); cursor_position.selected_count.selections = editor.selections.count(); - match editor.mode() { - editor::EditorMode::AutoHeight { .. } - | editor::EditorMode::SingleLine - | editor::EditorMode::Minimap { .. } => { + match editor.display_mode() { + editor::EditorDisplayMode::AutoHeight { .. } + | editor::EditorDisplayMode::SingleLine + | editor::EditorDisplayMode::Minimap { .. } => { cursor_position.position = None; cursor_position.context = None; } - editor::EditorMode::Full { .. } => { + editor::EditorDisplayMode::Full { .. } => { let mut last_selection = None::>; let snapshot = editor.buffer().read(cx).snapshot(cx); if snapshot.excerpts().count() > 0 { diff --git a/crates/inspector_ui/src/div_inspector.rs b/crates/inspector_ui/src/div_inspector.rs index 0c2b16b9f4..ac57c83f10 100644 --- a/crates/inspector_ui/src/div_inspector.rs +++ b/crates/inspector_ui/src/div_inspector.rs @@ -1,6 +1,6 @@ use anyhow::{Result, anyhow}; use editor::{ - Bias, CompletionProvider, Editor, EditorEvent, EditorMode, ExcerptId, MinimapVisibility, + Bias, CompletionProvider, Editor, EditorDisplayMode, EditorEvent, ExcerptId, MinimapVisibility, MultiBuffer, }; use fuzzy::StringMatch; @@ -483,7 +483,7 @@ impl DivInspector { cx.new(|cx| { let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); let mut editor = Editor::new( - EditorMode::full(), + EditorDisplayMode::full(), multi_buffer, Some(self.project.clone()), window, diff --git a/crates/onboarding/Cargo.toml b/crates/onboarding/Cargo.toml index 4157be3172..04ab861fa0 100644 --- a/crates/onboarding/Cargo.toml +++ b/crates/onboarding/Cargo.toml @@ -40,7 +40,7 @@ telemetry.workspace = true theme.workspace = true ui.workspace = true util.workspace = true -vim_mode_setting.workspace = true +editor_mode_setting.workspace = true workspace-hack.workspace = true workspace.workspace = true zed_actions.workspace = true diff --git a/crates/onboarding/src/basics_page.rs b/crates/onboarding/src/basics_page.rs index 441d2ca4b7..afcee30f1c 100644 --- a/crates/onboarding/src/basics_page.rs +++ b/crates/onboarding/src/basics_page.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use client::TelemetrySettings; +use editor_mode_setting::{EditorMode, EditorModeSetting}; use fs::Fs; use gpui::{App, IntoElement}; use settings::{BaseKeymap, Settings, update_settings_file}; @@ -12,7 +13,6 @@ use ui::{ ParentElement as _, StatefulInteractiveElement, SwitchField, ToggleButtonGroup, ToggleButtonSimple, ToggleButtonWithIcon, prelude::*, rems_from_px, }; -use vim_mode_setting::VimModeSetting; use crate::theme_preview::{ThemePreviewStyle, ThemePreviewTile}; @@ -331,11 +331,13 @@ fn render_base_keymap_section(tab_index: &mut isize, cx: &mut App) -> impl IntoE } fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoElement { - let toggle_state = if VimModeSetting::get_global(cx).0 { + let editor_mode = EditorModeSetting::get_global(cx).0; + let toggle_state = if editor_mode.is_modal() { ui::ToggleState::Selected } else { ui::ToggleState::Unselected }; + SwitchField::new( "onboarding-vim-mode", "Vim Mode", @@ -344,10 +346,10 @@ fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoEleme { let fs = ::global(cx); move |&selection, _, cx| { - update_settings_file::(fs.clone(), cx, move |setting, _| { + update_settings_file::(fs.clone(), cx, move |setting, _| { *setting = match selection { - ToggleState::Selected => Some(true), - ToggleState::Unselected => Some(false), + ToggleState::Selected => Some(EditorMode::vim()), + ToggleState::Unselected => Some(EditorMode::Default), ToggleState::Indeterminate => None, } }); diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 8c5e78d77b..9beb62a0a0 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -88,7 +88,7 @@ impl Render for OutlineView { impl OutlineView { fn register(editor: &mut Editor, _: Option<&mut Window>, cx: &mut Context) { - if editor.mode().is_full() { + if editor.display_mode().is_full() { let handle = cx.entity().downgrade(); editor .register_action(move |action, window, cx| { diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 10698cead8..9593d60dd1 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -4949,7 +4949,7 @@ fn workspace_active_editor( let active_item = workspace.active_item(cx)?; let active_editor = active_item .act_as::(cx) - .filter(|editor| editor.read(cx).mode().is_full())?; + .filter(|editor| editor.read(cx).display_mode().is_full())?; Some((active_item, active_editor)) } diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs index 87b8e1d55a..d39efd7abf 100644 --- a/crates/repl/src/notebook/cell.rs +++ b/crates/repl/src/notebook/cell.rs @@ -1,7 +1,7 @@ #![allow(unused, dead_code)] use std::sync::Arc; -use editor::{Editor, EditorMode, MultiBuffer}; +use editor::{Editor, EditorDisplayMode, MultiBuffer}; use futures::future::Shared; use gpui::{ App, Entity, Hsla, RetainAllImageCache, Task, TextStyleRefinement, image_cache, prelude::*, @@ -177,7 +177,7 @@ impl Cell { let editor_view = cx.new(|cx| { let mut editor = Editor::new( - EditorMode::AutoHeight { + EditorDisplayMode::AutoHeight { min_lines: 1, max_lines: Some(1024), }, diff --git a/crates/repl/src/repl_sessions_ui.rs b/crates/repl/src/repl_sessions_ui.rs index 493b8aa950..8d86ee6568 100644 --- a/crates/repl/src/repl_sessions_ui.rs +++ b/crates/repl/src/repl_sessions_ui.rs @@ -75,7 +75,8 @@ pub fn init(cx: &mut App) { return; }; - if !editor.use_modal_editing() || !editor.buffer().read(cx).is_singleton() { + if !editor.editor_mode().is_modal() || !editor.buffer().read(cx).is_singleton() + { return; } diff --git a/crates/rules_library/Cargo.toml b/crates/rules_library/Cargo.toml index 298f77a2d2..a6fa0b28d1 100644 --- a/crates/rules_library/Cargo.toml +++ b/crates/rules_library/Cargo.toml @@ -33,3 +33,4 @@ util.workspace = true workspace-hack.workspace = true workspace.workspace = true zed_actions.workspace = true +editor_mode_setting.workspace = true diff --git a/crates/rules_library/src/rules_library.rs b/crates/rules_library/src/rules_library.rs index 5ad3996e78..3302b71028 100644 --- a/crates/rules_library/src/rules_library.rs +++ b/crates/rules_library/src/rules_library.rs @@ -2,6 +2,7 @@ use anyhow::Result; use collections::{HashMap, HashSet}; use editor::{CompletionProvider, SelectionEffects}; use editor::{CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle, actions::Tab}; +use editor_mode_setting::EditorMode; use gpui::{ Action, App, Bounds, Entity, EventEmitter, Focusable, PromptLevel, Subscription, Task, TextStyle, TitlebarOptions, WindowBounds, WindowHandle, WindowOptions, actions, point, size, @@ -637,7 +638,7 @@ impl RulesLibrary { editor.set_show_gutter(false, cx); editor.set_show_wrap_guides(false, cx); editor.set_show_indent_guides(false, cx); - editor.set_use_modal_editing(false); + editor.set_editor_mode(EditorMode::Default, cx); editor.set_current_line_highlight(Some(CurrentLineHighlight::None)); editor.set_completion_provider(Some(make_completion_provider())); if focus { diff --git a/crates/settings_ui/src/keybindings.rs b/crates/settings_ui/src/keybindings.rs index 9c76725972..49dbfcf0bd 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/settings_ui/src/keybindings.rs @@ -2699,7 +2699,7 @@ impl ActionArgumentsEditor { let editor = cx.new_window_entity(|window, cx| { let multi_buffer = cx.new(|cx| editor::MultiBuffer::singleton(buffer, cx)); let mut editor = Editor::new( - editor::EditorMode::Full { + editor::EditorDisplayMode::Full { scale_ui_elements_with_buffer_font_size: true, show_active_line_background: false, sized_by_content: true, diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 434b14b07c..c9f4e71f49 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -47,7 +47,7 @@ theme.workspace = true tokio = { version = "1.15", features = ["full"], optional = true } ui.workspace = true util.workspace = true -vim_mode_setting.workspace = true +editor_mode_setting.workspace = true workspace.workspace = true zed_actions.workspace = true workspace-hack.workspace = true diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index 8ef1cd7811..fd0c591bdd 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -1,10 +1,10 @@ use crate::{Vim, state::Mode}; use editor::{Bias, Editor}; +use editor_mode_setting::{EditorMode, EditorModeSetting}; use gpui::{Action, Context, Window, actions}; use language::SelectionGoal; use settings::Settings; use text::Point; -use vim_mode_setting::HelixModeSetting; use workspace::searchable::Direction; actions!( @@ -53,7 +53,7 @@ impl Vim { self.update_editor(cx, |_, editor, cx| { editor.dismiss_menus_and_popups(false, window, cx); - if !HelixModeSetting::get_global(cx).0 { + if !matches!(EditorModeSetting::get_global(cx).0, EditorMode::Helix(..)) { editor.change_selections(Default::default(), window, cx, |s| { s.move_cursors_with(|map, mut cursor, _| { *cursor.column_mut() = cursor.column().saturating_sub(1); @@ -63,7 +63,7 @@ impl Vim { } }); - if HelixModeSetting::get_global(cx).0 { + if matches!(EditorModeSetting::get_global(cx).0, EditorMode::Helix(..)) { self.switch_mode(Mode::HelixNormal, false, window, cx); } else { self.switch_mode(Mode::Normal, false, window, cx); diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 933b119d37..a32961f7d7 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -1,11 +1,11 @@ use editor::{DisplayPoint, RowExt, SelectionEffects, display_map::ToDisplayPoint, movement}; +use editor_mode_setting::{EditorMode, EditorModeSetting}; use gpui::{Action, Context, Window}; use language::{Bias, SelectionGoal}; use schemars::JsonSchema; use serde::Deserialize; use settings::Settings; use std::cmp; -use vim_mode_setting::HelixModeSetting; use crate::{ Vim, @@ -220,7 +220,7 @@ impl Vim { }); }); - if HelixModeSetting::get_global(cx).0 { + if matches!(EditorModeSetting::get_global(cx).0, EditorMode::Helix(_)) { self.switch_mode(Mode::HelixNormal, true, window, cx); } else { self.switch_mode(Mode::Normal, true, window, cx); diff --git a/crates/vim/src/normal/substitute.rs b/crates/vim/src/normal/substitute.rs index 889d487170..c30e8a2402 100644 --- a/crates/vim/src/normal/substitute.rs +++ b/crates/vim/src/normal/substitute.rs @@ -3,8 +3,9 @@ use gpui::{Context, Window, actions}; use language::Point; use crate::{ - Mode, Vim, + Vim, motion::{Motion, MotionKind}, + state::Mode, }; actions!( diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index c0176cb12c..5c7271dfbd 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -11,6 +11,7 @@ use db::define_connection; use db::sqlez_macros::sql; use editor::display_map::{is_invisible, replacement}; use editor::{Anchor, ClipboardSelection, Editor, MultiBuffer, ToPoint as EditorToPoint}; +pub use editor_mode_setting::ModalMode as Mode; use gpui::{ Action, App, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, DismissEvent, Entity, EntityId, Global, HighlightStyle, StyledText, Subscription, Task, TextStyle, WeakEntity, @@ -19,12 +20,11 @@ use language::{Buffer, BufferEvent, BufferId, Chunk, Point}; use multi_buffer::MultiBufferRow; use picker::{Picker, PickerDelegate}; use project::{Project, ProjectItem, ProjectPath}; -use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use std::borrow::BorrowMut; use std::collections::HashSet; use std::path::Path; -use std::{fmt::Display, ops::Range, sync::Arc}; +use std::{ops::Range, sync::Arc}; use text::{Bias, ToPoint}; use theme::ThemeSettings; use ui::{ @@ -35,46 +35,6 @@ use util::ResultExt; use workspace::searchable::Direction; use workspace::{Workspace, WorkspaceDb, WorkspaceId}; -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -pub enum Mode { - Normal, - Insert, - Replace, - Visual, - VisualLine, - VisualBlock, - HelixNormal, -} - -impl Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Mode::Normal => write!(f, "NORMAL"), - Mode::Insert => write!(f, "INSERT"), - Mode::Replace => write!(f, "REPLACE"), - Mode::Visual => write!(f, "VISUAL"), - Mode::VisualLine => write!(f, "VISUAL LINE"), - Mode::VisualBlock => write!(f, "VISUAL BLOCK"), - Mode::HelixNormal => write!(f, "HELIX NORMAL"), - } - } -} - -impl Mode { - pub fn is_visual(&self) -> bool { - match self { - Self::Visual | Self::VisualLine | Self::VisualBlock => true, - Self::Normal | Self::Insert | Self::Replace | Self::HelixNormal => false, - } - } -} - -impl Default for Mode { - fn default() -> Self { - Self::Normal - } -} - #[derive(Clone, Debug, PartialEq)] pub enum Operator { Change, @@ -692,7 +652,7 @@ impl VimGlobals { let mut was_enabled = None; cx.observe_global::(move |cx| { - let is_enabled = Vim::enabled(cx); + let is_enabled = Vim::global_enabled(cx); if was_enabled == Some(is_enabled) { return; } diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index ce04b621cb..a2136de2bc 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -7,7 +7,7 @@ use std::time::Duration; use collections::HashMap; use command_palette::CommandPalette; use editor::{ - AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, actions::DeleteLine, + AnchorRangeExt, DisplayPoint, Editor, EditorDisplayMode, MultiBuffer, actions::DeleteLine, display_map::DisplayRow, test::editor_test_context::EditorTestContext, }; use futures::StreamExt; @@ -1784,7 +1784,13 @@ async fn test_folded_multibuffer_excerpts(cx: &mut gpui::TestAppContext) { ], cx, ); - let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx); + let mut editor = Editor::new( + EditorDisplayMode::full(), + multi_buffer.clone(), + None, + window, + cx, + ); let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids(); // fold all but the second buffer, so that we test navigating between two diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index e7ac692df1..6ac282fbe8 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -4,7 +4,7 @@ use editor::test::editor_lsp_test_context::EditorLspTestContext; use gpui::{Context, Entity, SemanticVersion, UpdateGlobal}; use search::{BufferSearchBar, project_search::ProjectSearchBar}; -use crate::{state::Operator, *}; +use crate::{state::Mode, state::Operator, *}; pub struct VimTestContext { cx: EditorLspTestContext, @@ -64,7 +64,13 @@ impl VimTestContext { pub fn init_keybindings(enabled: bool, cx: &mut App) { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(enabled)); + store.update_user_settings::(cx, |s| { + *s = Some(if enabled { + EditorMode::vim() + } else { + EditorMode::Default + }) + }); }); let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure( "keymaps/default-macos.json", @@ -130,7 +136,9 @@ impl VimTestContext { pub fn enable_vim(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(true)); + store.update_user_settings::(cx, |s| { + *s = Some(EditorMode::vim()) + }); }); }) } @@ -138,7 +146,9 @@ impl VimTestContext { pub fn disable_vim(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(false)); + store.update_user_settings::(cx, |s| { + *s = Some(EditorMode::vim()) + }); }); }) } @@ -146,8 +156,8 @@ impl VimTestContext { pub fn enable_helix(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| { - *s = Some(true) + store.update_user_settings::(cx, |s| { + *s = Some(EditorMode::Helix(ModalMode::HelixNormal)) }); }); }) diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 9da01e6f44..3ea8a275fe 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -26,6 +26,7 @@ use editor::{ ToPoint, movement::{self, FindRange}, }; +use editor_mode_setting::{EditorMode, EditorModeSetting, ModalMode}; use gpui::{ Action, App, AppContext, Axis, Context, Entity, EventEmitter, KeyContext, KeystrokeEvent, Render, Subscription, Task, WeakEntity, Window, actions, @@ -40,13 +41,11 @@ use schemars::JsonSchema; use serde::Deserialize; use serde_derive::Serialize; use settings::{Settings, SettingsSources, SettingsStore, update_settings_file}; -use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals}; +use state::{Operator, RecordedSelection, SearchState, VimGlobals}; use std::{mem, ops::Range, sync::Arc}; use surrounds::SurroundsType; use theme::ThemeSettings; use ui::{IntoElement, SharedString, px}; -use vim_mode_setting::HelixModeSetting; -use vim_mode_setting::VimModeSetting; use workspace::{self, Pane, Workspace}; use crate::state::ReplayableAction; @@ -236,7 +235,7 @@ actions!( /// Initializes the `vim` crate. pub fn init(cx: &mut App) { - vim_mode_setting::init(cx); + editor_mode_setting::init(cx); VimSettings::register(cx); VimGlobals::register(cx); @@ -245,9 +244,13 @@ pub fn init(cx: &mut App) { cx.observe_new(|workspace: &mut Workspace, _, _| { workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| { let fs = workspace.app_state().fs.clone(); - let currently_enabled = Vim::enabled(cx); - update_settings_file::(fs, cx, move |setting, _| { - *setting = Some(!currently_enabled) + let currently_enabled = Vim::global_enabled(cx); + update_settings_file::(fs, cx, move |setting, _| { + *setting = Some(if currently_enabled { + EditorMode::Default + } else { + EditorMode::vim() + }); }) }); @@ -357,8 +360,8 @@ impl editor::Addon for VimAddon { /// The state pertaining to Vim mode. pub(crate) struct Vim { - pub(crate) mode: Mode, - pub last_mode: Mode, + pub(crate) mode: ModalMode, + pub last_mode: ModalMode, pub temp_mode: bool, pub status_label: Option, pub exit_temporary_mode: bool, @@ -366,11 +369,11 @@ pub(crate) struct Vim { operator_stack: Vec, pub(crate) replacements: Vec<(Range, String)>, - pub(crate) stored_visual_mode: Option<(Mode, Vec)>, + pub(crate) stored_visual_mode: Option<(ModalMode, Vec)>, pub(crate) current_tx: Option, pub(crate) current_anchor: Option>, - pub(crate) undo_modes: HashMap, + pub(crate) undo_modes: HashMap, pub(crate) undo_last_line_tx: Option, selected_register: Option, @@ -404,14 +407,9 @@ impl Vim { pub fn new(window: &mut Window, cx: &mut Context) -> Entity { let editor = cx.entity(); - let mut initial_mode = VimSettings::get_global(cx).default_mode; - if initial_mode == Mode::Normal && HelixModeSetting::get_global(cx).0 { - initial_mode = Mode::HelixNormal; - } - cx.new(|cx| Vim { - mode: initial_mode, - last_mode: Mode::Normal, + mode: ModalMode::Normal, + last_mode: ModalMode::Normal, temp_mode: false, exit_temporary_mode: false, operator_stack: Vec::new(), @@ -445,85 +443,81 @@ impl Vim { return; }; - if !editor.use_modal_editing() { + if !editor.editor_mode().is_modal() { return; } - let mut was_enabled = Vim::enabled(cx); let mut was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers; - cx.observe_global_in::(window, move |editor, window, cx| { - let enabled = Vim::enabled(cx); + cx.observe_global_in::(window, move |editor, _window, cx| { let toggle = VimSettings::get_global(cx).toggle_relative_line_numbers; - if enabled && was_enabled && (toggle != was_toggle) { + if toggle != was_toggle { if toggle { let is_relative = editor .addon::() - .map(|vim| vim.entity.read(cx).mode != Mode::Insert); + .map(|vim| vim.entity.read(cx).mode != ModalMode::Insert); editor.set_relative_line_number(is_relative, cx) } else { editor.set_relative_line_number(None, cx) } } was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers; - if was_enabled == enabled { - return; - } - was_enabled = enabled; - if enabled { - Self::activate(editor, window, cx) - } else { - Self::deactivate(editor, cx) - } }) .detach(); - if was_enabled { - Self::activate(editor, window, cx) - } + + Self::activate(editor, window, cx) } fn activate(editor: &mut Editor, window: &mut Window, cx: &mut Context) { let vim = Vim::new(window, cx); - if !editor.mode().is_full() { - vim.update(cx, |vim, _| { - vim.mode = Mode::Insert; - }); - } + vim.update(cx, |vim, _| { + let initial_mode = match editor.editor_mode() { + EditorMode::Default => return, + EditorMode::Vim(modal_mode) => modal_mode, + EditorMode::Helix(modal_mode) => modal_mode, + }; + vim.mode = initial_mode; + }); editor.register_addon(VimAddon { entity: vim.clone(), }); - vim.update(cx, |_, cx| { - Vim::action(editor, cx, |vim, _: &SwitchToNormalMode, window, cx| { - if HelixModeSetting::get_global(cx).0 { - vim.switch_mode(Mode::HelixNormal, false, window, cx) - } else { - vim.switch_mode(Mode::Normal, false, window, cx) - } - }); + let default_editor_mode = editor.editor_mode(); + vim.update(cx, move |_, cx| { + Vim::action( + editor, + cx, + move |vim, _: &SwitchToNormalMode, window, cx| { + if matches!(default_editor_mode, EditorMode::Helix(_)) { + vim.switch_mode(ModalMode::HelixNormal, false, window, cx) + } else { + vim.switch_mode(ModalMode::Normal, false, window, cx) + } + }, + ); Vim::action(editor, cx, |vim, _: &SwitchToInsertMode, window, cx| { - vim.switch_mode(Mode::Insert, false, window, cx) + vim.switch_mode(ModalMode::Insert, false, window, cx) }); Vim::action(editor, cx, |vim, _: &SwitchToReplaceMode, window, cx| { - vim.switch_mode(Mode::Replace, false, window, cx) + vim.switch_mode(ModalMode::Replace, false, window, cx) }); Vim::action(editor, cx, |vim, _: &SwitchToVisualMode, window, cx| { - vim.switch_mode(Mode::Visual, false, window, cx) + vim.switch_mode(ModalMode::Visual, false, window, cx) }); Vim::action(editor, cx, |vim, _: &SwitchToVisualLineMode, window, cx| { - vim.switch_mode(Mode::VisualLine, false, window, cx) + vim.switch_mode(ModalMode::VisualLine, false, window, cx) }); Vim::action( editor, cx, |vim, _: &SwitchToVisualBlockMode, window, cx| { - vim.switch_mode(Mode::VisualBlock, false, window, cx) + vim.switch_mode(ModalMode::VisualBlock, false, window, cx) }, ); @@ -531,7 +525,7 @@ impl Vim { editor, cx, |vim, _: &SwitchToHelixNormalMode, window, cx| { - vim.switch_mode(Mode::HelixNormal, false, window, cx) + vim.switch_mode(ModalMode::HelixNormal, false, window, cx) }, ); Vim::action(editor, cx, |_, _: &PushForcedMotion, _, cx| { @@ -751,8 +745,8 @@ impl Vim { // displayed (and performed by `accept_edit_prediction`). This switches to // insert mode so that the prediction is displayed after the jump. match vim.mode { - Mode::Replace => {} - _ => vim.switch_mode(Mode::Insert, true, window, cx), + ModalMode::Replace => {} + _ => vim.switch_mode(ModalMode::Insert, true, window, cx), }; }, ); @@ -818,8 +812,11 @@ impl Vim { .map(|workspace| workspace.read(cx).focused_pane(window, cx)) } - pub fn enabled(cx: &mut App) -> bool { - VimModeSetting::get_global(cx).0 || HelixModeSetting::get_global(cx).0 + pub fn global_enabled(cx: &mut App) -> bool { + if EditorModeSetting::get_global(cx).0 == EditorMode::Default { + return false; + } + return true; } /// Called whenever an keystroke is typed so vim can observe all actions @@ -838,7 +835,7 @@ impl Vim { { return; } - self.switch_mode(Mode::Insert, false, window, cx) + self.switch_mode(ModalMode::Insert, false, window, cx) } if let Some(action) = keystroke_event.action.as_ref() { // Keystroke is handled by the vim system, so continue forward @@ -914,6 +911,21 @@ impl Vim { vim.set_mark(mark, vec![*anchor], editor.buffer(), window, cx); }); } + EditorEvent::EditorModeChanged { new_mode, old_mode } => { + self.update_editor(cx, |_vim, editor, cx| { + let enabled = new_mode.is_modal(); + let was_enabled = old_mode.is_modal(); + if was_enabled == enabled { + return; + } + if enabled { + Self::activate(editor, window, cx) + } else { + editor.set_relative_line_number(None, cx); + Self::deactivate(editor, cx) + } + }); + } _ => {} } } @@ -940,18 +952,21 @@ impl Vim { pub fn switch_mode( &mut self, - mode: Mode, + mode: ModalMode, leave_selections: bool, window: &mut Window, cx: &mut Context, ) { - if self.temp_mode && mode == Mode::Normal { + if self.temp_mode && mode == ModalMode::Normal { self.temp_mode = false; - self.switch_mode(Mode::Normal, leave_selections, window, cx); - self.switch_mode(Mode::Insert, false, window, cx); + self.switch_mode(ModalMode::Normal, leave_selections, window, cx); + self.switch_mode(ModalMode::Insert, false, window, cx); return; } else if self.temp_mode - && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock) + && !matches!( + mode, + ModalMode::Visual | ModalMode::VisualLine | ModalMode::VisualBlock + ) { self.temp_mode = false; } @@ -965,7 +980,7 @@ impl Vim { self.operator_stack.clear(); self.selected_register.take(); self.cancel_running_command(window, cx); - if mode == Mode::Normal || mode != last_mode { + if mode == ModalMode::Normal || mode != last_mode { self.current_tx.take(); self.current_anchor.take(); self.update_editor(cx, |_, editor, _| { @@ -973,7 +988,7 @@ impl Vim { }); } Vim::take_forced_motion(cx); - if mode != Mode::Insert && mode != Mode::Replace { + if mode != ModalMode::Insert && mode != ModalMode::Replace { Vim::take_count(cx); } @@ -982,10 +997,10 @@ impl Vim { if VimSettings::get_global(cx).toggle_relative_line_numbers && self.mode != self.last_mode - && (self.mode == Mode::Insert || self.last_mode == Mode::Insert) + && (self.mode == ModalMode::Insert || self.last_mode == ModalMode::Insert) { self.update_editor(cx, |vim, editor, cx| { - let is_relative = vim.mode != Mode::Insert; + let is_relative = vim.mode != ModalMode::Insert; editor.set_relative_line_number(Some(is_relative), cx) }); } @@ -1000,13 +1015,15 @@ impl Vim { // Adjust selections self.update_editor(cx, |vim, editor, cx| { - if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock + if last_mode != ModalMode::VisualBlock + && last_mode.is_visual() + && mode == ModalMode::VisualBlock { vim.visual_block_motion(true, editor, window, cx, |_, point, goal| { Some((point, goal)) }) } - if (last_mode == Mode::Insert || last_mode == Mode::Replace) + if (last_mode == ModalMode::Insert || last_mode == ModalMode::Replace) && let Some(prior_tx) = prior_tx { editor.group_until_transaction(prior_tx, cx) @@ -1016,15 +1033,15 @@ impl Vim { // we cheat with visual block mode and use multiple cursors. // the cost of this cheat is we need to convert back to a single // cursor whenever vim would. - if last_mode == Mode::VisualBlock - && (mode != Mode::VisualBlock && mode != Mode::Insert) + if last_mode == ModalMode::VisualBlock + && (mode != ModalMode::VisualBlock && mode != ModalMode::Insert) { let tail = s.oldest_anchor().tail(); let head = s.newest_anchor().head(); s.select_anchor_ranges(vec![tail..head]); - } else if last_mode == Mode::Insert - && prior_mode == Mode::VisualBlock - && mode != Mode::VisualBlock + } else if last_mode == ModalMode::Insert + && prior_mode == ModalMode::VisualBlock + && mode != ModalMode::VisualBlock { let pos = s.first_anchor().head(); s.select_anchor_ranges(vec![pos..pos]) @@ -1089,7 +1106,7 @@ impl Vim { pub fn cursor_shape(&self, cx: &mut App) -> CursorShape { let cursor_shape = VimSettings::get_global(cx).cursor_shape; match self.mode { - Mode::Normal => { + ModalMode::Normal => { if let Some(operator) = self.operator_stack.last() { match operator { // Navigation operators -> Block cursor @@ -1108,12 +1125,12 @@ impl Vim { cursor_shape.normal.unwrap_or(CursorShape::Block) } } - Mode::HelixNormal => cursor_shape.normal.unwrap_or(CursorShape::Block), - Mode::Replace => cursor_shape.replace.unwrap_or(CursorShape::Underline), - Mode::Visual | Mode::VisualLine | Mode::VisualBlock => { + ModalMode::HelixNormal => cursor_shape.normal.unwrap_or(CursorShape::Block), + ModalMode::Replace => cursor_shape.replace.unwrap_or(CursorShape::Underline), + ModalMode::Visual | ModalMode::VisualLine | ModalMode::VisualBlock => { cursor_shape.visual.unwrap_or(CursorShape::Block) } - Mode::Insert => cursor_shape.insert.unwrap_or({ + ModalMode::Insert => cursor_shape.insert.unwrap_or({ let editor_settings = EditorSettings::get_global(cx); editor_settings.cursor_shape.unwrap_or_default() }), @@ -1122,45 +1139,45 @@ impl Vim { pub fn editor_input_enabled(&self) -> bool { match self.mode { - Mode::Insert => { + ModalMode::Insert => { if let Some(operator) = self.operator_stack.last() { !operator.is_waiting(self.mode) } else { true } } - Mode::Normal - | Mode::HelixNormal - | Mode::Replace - | Mode::Visual - | Mode::VisualLine - | Mode::VisualBlock => false, + ModalMode::Normal + | ModalMode::HelixNormal + | ModalMode::Replace + | ModalMode::Visual + | ModalMode::VisualLine + | ModalMode::VisualBlock => false, } } pub fn should_autoindent(&self) -> bool { - !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock) + !(self.mode == ModalMode::Insert && self.last_mode == ModalMode::VisualBlock) } pub fn clip_at_line_ends(&self) -> bool { match self.mode { - Mode::Insert - | Mode::Visual - | Mode::VisualLine - | Mode::VisualBlock - | Mode::Replace - | Mode::HelixNormal => false, - Mode::Normal => true, + ModalMode::Insert + | ModalMode::Visual + | ModalMode::VisualLine + | ModalMode::VisualBlock + | ModalMode::Replace + | ModalMode::HelixNormal => false, + ModalMode::Normal => true, } } pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) { let mut mode = match self.mode { - Mode::Normal => "normal", - Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual", - Mode::Insert => "insert", - Mode::Replace => "replace", - Mode::HelixNormal => "helix_normal", + ModalMode::Normal => "normal", + ModalMode::Visual | ModalMode::VisualLine | ModalMode::VisualBlock => "visual", + ModalMode::Insert => "insert", + ModalMode::Replace => "replace", + ModalMode::HelixNormal => "helix_normal", } .to_string(); @@ -1201,16 +1218,16 @@ impl Vim { editor.selections.newest::(cx).is_empty() }); let editor = editor.read(cx); - let editor_mode = editor.mode(); + let editor_mode = editor.display_mode(); if editor_mode.is_full() && !newest_selection_empty - && self.mode == Mode::Normal + && self.mode == ModalMode::Normal // When following someone, don't switch vim mode. && editor.leader_id().is_none() { if preserve_selection { - self.switch_mode(Mode::Visual, true, window, cx); + self.switch_mode(ModalMode::Visual, true, window, cx); } else { self.update_editor(cx, |_, editor, cx| { editor.set_clip_at_line_ends(false, cx); @@ -1236,13 +1253,13 @@ impl Vim { }); self.update_editor(cx, |vim, editor, cx| { - let is_relative = vim.mode != Mode::Insert; + let is_relative = vim.mode != ModalMode::Insert; editor.set_relative_line_number(Some(is_relative), cx) }); } } else { self.update_editor(cx, |vim, editor, cx| { - let is_relative = vim.mode != Mode::Insert; + let is_relative = vim.mode != ModalMode::Insert; editor.set_relative_line_number(Some(is_relative), cx) }); } @@ -1330,19 +1347,19 @@ impl Vim { if let Some((oldest, newest)) = selections { globals.recorded_selection = match self.mode { - Mode::Visual if newest.end.row == newest.start.row => { + ModalMode::Visual if newest.end.row == newest.start.row => { RecordedSelection::SingleLine { cols: newest.end.column - newest.start.column, } } - Mode::Visual => RecordedSelection::Visual { + ModalMode::Visual => RecordedSelection::Visual { rows: newest.end.row - newest.start.row, cols: newest.end.column, }, - Mode::VisualLine => RecordedSelection::VisualLine { + ModalMode::VisualLine => RecordedSelection::VisualLine { rows: newest.end.row - newest.start.row, }, - Mode::VisualBlock => RecordedSelection::VisualBlock { + ModalMode::VisualBlock => RecordedSelection::VisualBlock { rows: newest.end.row.abs_diff(oldest.start.row), cols: newest.end.column.abs_diff(oldest.start.column), }, @@ -1459,9 +1476,9 @@ impl Vim { _window: &mut Window, _: &mut Context, ) { - let mode = if (self.mode == Mode::Insert - || self.mode == Mode::Replace - || self.mode == Mode::Normal) + let mode = if (self.mode == ModalMode::Insert + || self.mode == ModalMode::Replace + || self.mode == ModalMode::Normal) && self.current_tx.is_none() { self.current_tx = Some(transaction_id); @@ -1469,7 +1486,7 @@ impl Vim { } else { self.mode }; - if mode == Mode::VisualLine || mode == Mode::VisualBlock { + if mode == ModalMode::VisualLine || mode == ModalMode::VisualBlock { self.undo_modes.insert(transaction_id, mode); } } @@ -1481,12 +1498,12 @@ impl Vim { cx: &mut Context, ) { match self.mode { - Mode::VisualLine | Mode::VisualBlock | Mode::Visual => { + ModalMode::VisualLine | ModalMode::VisualBlock | ModalMode::Visual => { self.update_editor(cx, |vim, editor, cx| { let original_mode = vim.undo_modes.get(transaction_id); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { match original_mode { - Some(Mode::VisualLine) => { + Some(ModalMode::VisualLine) => { s.move_with(|map, selection| { selection.collapse_to( map.prev_line_boundary(selection.start.to_point(map)).1, @@ -1494,7 +1511,7 @@ impl Vim { ) }); } - Some(Mode::VisualBlock) => { + Some(ModalMode::VisualBlock) => { let mut first = s.first_anchor(); first.collapse_to(first.start, first.goal); s.select_anchors(vec![first]); @@ -1510,9 +1527,9 @@ impl Vim { } }); }); - self.switch_mode(Mode::Normal, true, window, cx) + self.switch_mode(ModalMode::Normal, true, window, cx) } - Mode::Normal => { + ModalMode::Normal => { self.update_editor(cx, |_, editor, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.move_with(|map, selection| { @@ -1522,7 +1539,7 @@ impl Vim { }) }); } - Mode::Insert | Mode::Replace | Mode::HelixNormal => {} + ModalMode::Insert | ModalMode::Replace | ModalMode::HelixNormal => {} } } @@ -1535,7 +1552,7 @@ impl Vim { let newest = editor.read(cx).selections.newest_anchor().clone(); let is_multicursor = editor.read(cx).selections.count() > 1; - if self.mode == Mode::Insert && self.current_tx.is_some() { + if self.mode == ModalMode::Insert && self.current_tx.is_some() { if self.current_anchor.is_none() { self.current_anchor = Some(newest); } else if self.current_anchor.as_ref().unwrap() != &newest @@ -1545,17 +1562,22 @@ impl Vim { editor.group_until_transaction(tx_id, cx) }); } - } else if self.mode == Mode::Normal && newest.start != newest.end { + } else if self.mode == ModalMode::Normal && newest.start != newest.end { if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) { - self.switch_mode(Mode::VisualBlock, false, window, cx); + self.switch_mode(ModalMode::VisualBlock, false, window, cx); } else { - self.switch_mode(Mode::Visual, false, window, cx) + self.switch_mode(ModalMode::Visual, false, window, cx) } } else if newest.start == newest.end && !is_multicursor - && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode) + && [ + ModalMode::Visual, + ModalMode::VisualLine, + ModalMode::VisualBlock, + ] + .contains(&self.mode) { - self.switch_mode(Mode::Normal, true, window, cx); + self.switch_mode(ModalMode::Normal, true, window, cx); } } @@ -1628,11 +1650,11 @@ impl Vim { } } Some(Operator::Replace) => match self.mode { - Mode::Normal => self.normal_replace(text, window, cx), - Mode::Visual | Mode::VisualLine | Mode::VisualBlock => { + ModalMode::Normal => self.normal_replace(text, window, cx), + ModalMode::Visual | ModalMode::VisualLine | ModalMode::VisualBlock => { self.visual_replace(text, window, cx) } - Mode::HelixNormal => self.helix_replace(&text, window, cx), + ModalMode::HelixNormal => self.helix_replace(&text, window, cx), _ => self.clear_operator(window, cx), }, Some(Operator::Digraph { first_char }) => { @@ -1650,20 +1672,20 @@ impl Vim { self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx) } Some(Operator::AddSurrounds { target }) => match self.mode { - Mode::Normal => { + ModalMode::Normal => { if let Some(target) = target { self.add_surrounds(text, target, window, cx); self.clear_operator(window, cx); } } - Mode::Visual | Mode::VisualLine | Mode::VisualBlock => { + ModalMode::Visual | ModalMode::VisualLine | ModalMode::VisualBlock => { self.add_surrounds(text, SurroundsType::Selection, window, cx); self.clear_operator(window, cx); } _ => self.clear_operator(window, cx), }, Some(Operator::ChangeSurrounds { target }) => match self.mode { - Mode::Normal => { + ModalMode::Normal => { if let Some(target) = target { self.change_surrounds(text, target, window, cx); self.clear_operator(window, cx); @@ -1672,7 +1694,7 @@ impl Vim { _ => self.clear_operator(window, cx), }, Some(Operator::DeleteSurrounds) => match self.mode { - Mode::Normal => { + ModalMode::Normal => { self.delete_surrounds(text, window, cx); self.clear_operator(window, cx); } @@ -1686,7 +1708,7 @@ impl Vim { self.replay_register(text.chars().next().unwrap(), window, cx) } Some(Operator::Register) => match self.mode { - Mode::Insert => { + ModalMode::Insert => { self.update_editor(cx, |_, editor, cx| { if let Some(register) = Vim::update_globals(cx, |globals, cx| { globals.read_register(text.chars().next(), Some(editor), cx) @@ -1708,11 +1730,11 @@ impl Vim { }, Some(Operator::Jump { line }) => self.jump(text, line, true, window, cx), _ => { - if self.mode == Mode::Replace { + if self.mode == ModalMode::Replace { self.multi_replace(text, window, cx) } - if self.mode == Mode::Normal { + if self.mode == ModalMode::Normal { self.update_editor(cx, |_, editor, cx| { editor.accept_edit_prediction( &editor::actions::AcceptEditPrediction {}, @@ -1732,9 +1754,9 @@ impl Vim { editor.set_collapse_matches(true); editor.set_input_enabled(vim.editor_input_enabled()); editor.set_autoindent(vim.should_autoindent()); - editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine); + editor.selections.line_mode = matches!(vim.mode, ModalMode::VisualLine); - let hide_edit_predictions = !matches!(vim.mode, Mode::Insert | Mode::Replace); + let hide_edit_predictions = !matches!(vim.mode, ModalMode::Insert | ModalMode::Replace); editor.set_edit_predictions_hidden_for_vim_mode(hide_edit_predictions, window, cx); }); cx.notify() @@ -1776,7 +1798,6 @@ struct CursorShapeSettings { #[derive(Deserialize)] struct VimSettings { - pub default_mode: Mode, pub toggle_relative_line_numbers: bool, pub use_system_clipboard: UseSystemClipboard, pub use_smartcase_find: bool, @@ -1787,7 +1808,6 @@ struct VimSettings { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] struct VimSettingsContent { - pub default_mode: Option, pub toggle_relative_line_numbers: Option, pub use_system_clipboard: Option, pub use_smartcase_find: Option, @@ -1796,33 +1816,6 @@ struct VimSettingsContent { pub cursor_shape: Option, } -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ModeContent { - #[default] - Normal, - Insert, - Replace, - Visual, - VisualLine, - VisualBlock, - HelixNormal, -} - -impl From for Mode { - fn from(mode: ModeContent) -> Self { - match mode { - ModeContent::Normal => Self::Normal, - ModeContent::Insert => Self::Insert, - ModeContent::Replace => Self::Replace, - ModeContent::Visual => Self::Visual, - ModeContent::VisualLine => Self::VisualLine, - ModeContent::VisualBlock => Self::VisualBlock, - ModeContent::HelixNormal => Self::HelixNormal, - } - } -} - impl Settings for VimSettings { const KEY: Option<&'static str> = Some("vim"); @@ -1832,10 +1825,6 @@ impl Settings for VimSettings { let settings: VimSettingsContent = sources.json_merge()?; Ok(Self { - default_mode: settings - .default_mode - .ok_or_else(Self::missing_default)? - .into(), toggle_relative_line_numbers: settings .toggle_relative_line_numbers .ok_or_else(Self::missing_default)?, diff --git a/crates/vim_mode_setting/src/vim_mode_setting.rs b/crates/vim_mode_setting/src/vim_mode_setting.rs deleted file mode 100644 index 6f60d3f21f..0000000000 --- a/crates/vim_mode_setting/src/vim_mode_setting.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Contains the [`VimModeSetting`] and [`HelixModeSetting`] used to enable/disable Vim and Helix modes. -//! -//! This is in its own crate as we want other crates to be able to enable or -//! disable Vim/Helix modes without having to depend on the `vim` crate in its -//! entirety. - -use anyhow::Result; -use gpui::App; -use settings::{Settings, SettingsSources}; - -/// Initializes the `vim_mode_setting` crate. -pub fn init(cx: &mut App) { - VimModeSetting::register(cx); - HelixModeSetting::register(cx); -} - -/// Whether or not to enable Vim mode. -/// -/// Default: false -pub struct VimModeSetting(pub bool); - -impl Settings for VimModeSetting { - const KEY: Option<&'static str> = Some("vim_mode"); - - type FileContent = Option; - - fn load(sources: SettingsSources, _: &mut App) -> Result { - Ok(Self( - sources - .user - .or(sources.server) - .copied() - .flatten() - .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), - )) - } - - fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) { - // TODO: could possibly check if any of the `vim.` keys are set? - } -} - -/// Whether or not to enable Helix mode. -/// -/// Default: false -pub struct HelixModeSetting(pub bool); - -impl Settings for HelixModeSetting { - const KEY: Option<&'static str> = Some("helix_mode"); - - type FileContent = Option; - - fn load(sources: SettingsSources, _: &mut App) -> Result { - Ok(Self( - sources - .user - .or(sources.server) - .copied() - .flatten() - .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), - )) - } - - fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) { - // TODO: could possibly check if any of the `helix.` keys are set? - } -} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index c61e23f0a1..67b54e58ea 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -156,7 +156,7 @@ urlencoding.workspace = true util.workspace = true uuid.workspace = true vim.workspace = true -vim_mode_setting.workspace = true +editor_mode_setting.workspace = true watch.workspace = true web_search.workspace = true web_search_providers.workspace = true diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 638e1dca0e..647c45631f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -19,6 +19,7 @@ use collections::VecDeque; use debugger_ui::debugger_panel::DebugPanel; use editor::ProposedChangesEditorToolbar; use editor::{Editor, MultiBuffer}; +use editor_mode_setting::EditorModeSetting; use feature_flags::{FeatureFlagAppExt, PanicFeatureFlag}; use futures::future::Either; use futures::{StreamExt, channel::mpsc, select_biased}; @@ -70,7 +71,6 @@ use ui::{PopoverMenuHandle, prelude::*}; use util::markdown::MarkdownString; use util::{ResultExt, asset_str}; use uuid::Uuid; -use vim_mode_setting::VimModeSetting; use workspace::notifications::{ NotificationId, SuppressEvent, dismiss_app_notification, show_app_notification, }; @@ -1282,26 +1282,20 @@ pub fn handle_keymap_file_changes( cx: &mut App, ) { BaseKeymap::register(cx); - vim_mode_setting::init(cx); + editor_mode_setting::init(cx); let (base_keymap_tx, mut base_keymap_rx) = mpsc::unbounded(); let (keyboard_layout_tx, mut keyboard_layout_rx) = mpsc::unbounded(); let mut old_base_keymap = *BaseKeymap::get_global(cx); - let mut old_vim_enabled = VimModeSetting::get_global(cx).0; - let mut old_helix_enabled = vim_mode_setting::HelixModeSetting::get_global(cx).0; + let mut old_editor_mode = EditorModeSetting::get_global(cx).0; cx.observe_global::(move |cx| { let new_base_keymap = *BaseKeymap::get_global(cx); - let new_vim_enabled = VimModeSetting::get_global(cx).0; - let new_helix_enabled = vim_mode_setting::HelixModeSetting::get_global(cx).0; + let new_editor_mode = EditorModeSetting::get_global(cx).0; - if new_base_keymap != old_base_keymap - || new_vim_enabled != old_vim_enabled - || new_helix_enabled != old_helix_enabled - { + if new_base_keymap != old_base_keymap || new_editor_mode != old_editor_mode { old_base_keymap = new_base_keymap; - old_vim_enabled = new_vim_enabled; - old_helix_enabled = new_helix_enabled; + old_editor_mode = new_editor_mode; base_keymap_tx.unbounded_send(()).unwrap(); } @@ -1498,8 +1492,8 @@ pub fn load_default_keymap(cx: &mut App) { if let Some(asset_path) = base_keymap.asset_path() { cx.bind_keys(KeymapFile::load_asset(asset_path, Some(KeybindSource::Base), cx).unwrap()); } - - if VimModeSetting::get_global(cx).0 || vim_mode_setting::HelixModeSetting::get_global(cx).0 { + let editor_mode = EditorModeSetting::get_global(cx).0; + if editor_mode.is_modal() { cx.bind_keys( KeymapFile::load_asset(VIM_KEYMAP_PATH, Some(KeybindSource::Vim), cx).unwrap(), ); @@ -4622,7 +4616,7 @@ mod tests { app_state.languages.add(markdown_language()); gpui_tokio::init(cx); - vim_mode_setting::init(cx); + editor_mode_setting::init(cx); theme::init(theme::LoadThemes::JustBase, cx); audio::init(cx); channel::init(&app_state.client, app_state.user_store.clone(), cx); diff --git a/crates/zed/src/zed/edit_prediction_registry.rs b/crates/zed/src/zed/edit_prediction_registry.rs index a9abd9bc74..77d7022483 100644 --- a/crates/zed/src/zed/edit_prediction_registry.rs +++ b/crates/zed/src/zed/edit_prediction_registry.rs @@ -18,7 +18,7 @@ pub fn init(client: Arc, user_store: Entity, cx: &mut App) { let client = client.clone(); let user_store = user_store.clone(); move |editor: &mut Editor, window, cx: &mut Context| { - if !editor.mode().is_full() { + if !editor.display_mode().is_full() { return; } diff --git a/crates/zed/src/zed/quick_action_bar.rs b/crates/zed/src/zed/quick_action_bar.rs index e57d5d3889..b13c78d411 100644 --- a/crates/zed/src/zed/quick_action_bar.rs +++ b/crates/zed/src/zed/quick_action_bar.rs @@ -10,6 +10,7 @@ use editor::actions::{ }; use editor::code_context_menus::{CodeContextMenu, ContextMenuOrigin}; use editor::{Editor, EditorSettings}; +use editor_mode_setting::{EditorMode, EditorModeSetting}; use gpui::{ Action, AnchoredPositionMode, ClickEvent, Context, Corner, ElementId, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, ParentElement, Render, Styled, Subscription, @@ -23,7 +24,6 @@ use ui::{ ButtonStyle, ContextMenu, ContextMenuEntry, DocumentationSide, IconButton, IconName, IconSize, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*, }; -use vim_mode_setting::VimModeSetting; use workspace::{ ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::ItemHandle, }; @@ -119,7 +119,7 @@ impl Render for QuickActionBar { let selection_menu_enabled = editor_value.selection_menu_enabled(cx); let inlay_hints_enabled = editor_value.inlay_hints_enabled(); let inline_values_enabled = editor_value.inline_values_enabled(); - let supports_diagnostics = editor_value.mode().is_full(); + let supports_diagnostics = editor_value.display_mode().is_full(); let diagnostics_enabled = editor_value.diagnostics_max_severity != DiagnosticSeverity::Off; let supports_inline_diagnostics = editor_value.inline_diagnostics_enabled(); let inline_diagnostics_enabled = editor_value.show_inline_diagnostics(); @@ -301,7 +301,8 @@ impl Render for QuickActionBar { let editor_focus_handle = editor.focus_handle(cx); let editor = editor.downgrade(); let editor_settings_dropdown = { - let vim_mode_enabled = VimModeSetting::get_global(cx).0; + let editor_mode = EditorModeSetting::get_global(cx).0; + let vim_mode_enabled = editor_mode.is_modal(); PopoverMenu::new("editor-settings") .trigger_with_tooltip( @@ -576,8 +577,12 @@ impl Render for QuickActionBar { None, { move |window, cx| { - let new_value = !vim_mode_enabled; - VimModeSetting::override_global(VimModeSetting(new_value), cx); + let new_value = if vim_mode_enabled { + EditorMode::default() + } else { + EditorMode::vim() + }; + EditorModeSetting::override_global(EditorModeSetting(new_value), cx); window.refresh(); } },