From 066b4faf6154b9934c522f1ee0c245707ec18219 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 21 Apr 2022 13:33:39 -0700 Subject: [PATCH] Restructure KeyMap file, make it easy to edit in Zed Add a JSON schema for this file so that autocomplete can be used for the actions. --- assets/keymaps/default.json | 544 ++++++++++++++++------------- assets/keymaps/vim.json | 192 +++++----- crates/gpui/src/app.rs | 4 + crates/settings/src/keymap_file.rs | 73 +++- crates/settings/src/settings.rs | 170 ++++----- crates/zed/src/zed.rs | 93 +++-- 6 files changed, 610 insertions(+), 466 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 506dd362d4c3ec8262ac472aa0321951a3a96ef3..810a95c40c3aaf041ecd1be05258b922307d888e 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -1,264 +1,310 @@ -{ - "*": { - "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator", - "cmd-s": "workspace::Save", - "cmd-alt-i": "zed::DebugElements", - "cmd-k cmd-left": "workspace::ActivatePreviousPane", - "cmd-k cmd-right": "workspace::ActivateNextPane", - "cmd-=": "zed::IncreaseBufferFontSize", - "cmd--": "zed::DecreaseBufferFontSize", - "cmd-,": "zed::OpenSettings" +[ + { + "bindings": { + "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator", + "cmd-s": "workspace::Save", + "cmd-alt-i": "zed::DebugElements", + "cmd-k cmd-left": "workspace::ActivatePreviousPane", + "cmd-k cmd-right": "workspace::ActivateNextPane", + "cmd-=": "zed::IncreaseBufferFontSize", + "cmd--": "zed::DecreaseBufferFontSize", + "cmd-,": "zed::OpenSettings", + "alt-cmd-,": "zed::OpenKeymap" + } }, - "menu": { - "up": "menu::SelectPrev", - "ctrl-p": "menu::SelectPrev", - "down": "menu::SelectNext", - "ctrl-n": "menu::SelectNext", - "cmd-up": "menu::SelectFirst", - "cmd-down": "menu::SelectLast", - "enter": "menu::Confirm", - "escape": "menu::Cancel" + { + "context": "menu", + "bindings": { + "up": "menu::SelectPrev", + "ctrl-p": "menu::SelectPrev", + "down": "menu::SelectNext", + "ctrl-n": "menu::SelectNext", + "cmd-up": "menu::SelectFirst", + "cmd-down": "menu::SelectLast", + "enter": "menu::Confirm", + "escape": "menu::Cancel", + "ctrl-c": "menu::Cancel" + } }, - "Pane": { - "shift-cmd-{": "pane::ActivatePrevItem", - "shift-cmd-}": "pane::ActivateNextItem", - "cmd-w": "pane::CloseActiveItem", - "alt-cmd-w": "pane::CloseInactiveItems", - "ctrl--": "pane::GoBack", - "shift-ctrl-_": "pane::GoForward", - "cmd-k up": [ - "pane::Split", - "Up" - ], - "cmd-k down": [ - "pane::Split", - "Down" - ], - "cmd-k left": [ - "pane::Split", - "Left" - ], - "cmd-k right": [ - "pane::Split", - "Right" - ], - "cmd-shift-F": "project_search::ToggleFocus", - "cmd-f": "project_search::ToggleFocus", - "cmd-g": "search::SelectNextMatch", - "cmd-shift-G": "search::SelectPrevMatch" + { + "context": "Pane", + "bindings": { + "shift-cmd-{": "pane::ActivatePrevItem", + "shift-cmd-}": "pane::ActivateNextItem", + "cmd-w": "pane::CloseActiveItem", + "alt-cmd-w": "pane::CloseInactiveItems", + "ctrl--": "pane::GoBack", + "shift-ctrl-_": "pane::GoForward", + "cmd-k up": [ + "pane::Split", + "Up" + ], + "cmd-k down": [ + "pane::Split", + "Down" + ], + "cmd-k left": [ + "pane::Split", + "Left" + ], + "cmd-k right": [ + "pane::Split", + "Right" + ], + "cmd-shift-F": "project_search::ToggleFocus", + "cmd-f": "project_search::ToggleFocus", + "cmd-g": "search::SelectNextMatch", + "cmd-shift-G": "search::SelectPrevMatch" + } }, - "Workspace": { - "cmd-shift-F": "project_search::Deploy", - "cmd-k cmd-t": "theme_selector::Toggle", - "cmd-k t": "theme_selector::Reload", - "cmd-t": "project_symbols::Toggle", - "cmd-p": "file_finder::Toggle", - "cmd-shift-P": "command_palette::Toggle", - "alt-shift-D": "diagnostics::Deploy", - "ctrl-alt-cmd-j": "journal::NewJournalEntry", - "cmd-1": [ - "workspace::ToggleSidebarItemFocus", - { - "side": "Left", - "item_index": 0 - } - ], - "cmd-shift-!": [ - "workspace::ToggleSidebarItem", - { - "side": "Left", - "item_index": 0 - } - ] + { + "context": "Workspace", + "bindings": { + "cmd-shift-F": "project_search::Deploy", + "cmd-k cmd-t": "theme_selector::Toggle", + "cmd-k t": "theme_selector::Reload", + "cmd-t": "project_symbols::Toggle", + "cmd-p": "file_finder::Toggle", + "cmd-shift-P": "command_palette::Toggle", + "alt-shift-D": "diagnostics::Deploy", + "ctrl-alt-cmd-j": "journal::NewJournalEntry", + "cmd-1": [ + "workspace::ToggleSidebarItemFocus", + { + "side": "Left", + "item_index": 0 + } + ], + "cmd-shift-!": [ + "workspace::ToggleSidebarItem", + { + "side": "Left", + "item_index": 0 + } + ] + } }, - "ProjectSearchBar": { - "enter": "project_search::Search", - "cmd-enter": "project_search::SearchInNew" + { + "context": "ProjectSearchBar", + "bindings": { + "enter": "project_search::Search", + "cmd-enter": "project_search::SearchInNew" + } }, - "BufferSearchBar": { - "escape": "buffer_search::Dismiss", - "cmd-f": "buffer_search::FocusEditor", - "enter": "search::SelectNextMatch", - "shift-enter": "search::SelectPrevMatch" + { + "context": "BufferSearchBar", + "bindings": { + "escape": "buffer_search::Dismiss", + "cmd-f": "buffer_search::FocusEditor", + "enter": "search::SelectNextMatch", + "shift-enter": "search::SelectPrevMatch" + } }, - "Editor": { - "escape": "editor::Cancel", - "backspace": "editor::Backspace", - "ctrl-h": "editor::Backspace", - "delete": "editor::Delete", - "ctrl-d": "editor::Delete", - "tab": "editor::Tab", - "shift-tab": "editor::TabPrev", - "cmd-[": "editor::Outdent", - "cmd-]": "editor::Indent", - "ctrl-shift-K": "editor::DeleteLine", - "alt-backspace": "editor::DeleteToPreviousWordStart", - "alt-h": "editor::DeleteToPreviousWordStart", - "ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart", - "ctrl-alt-h": "editor::DeleteToPreviousSubwordStart", - "alt-delete": "editor::DeleteToNextWordEnd", - "alt-d": "editor::DeleteToNextWordEnd", - "ctrl-alt-delete": "editor::DeleteToNextSubwordEnd", - "ctrl-alt-d": "editor::DeleteToNextSubwordEnd", - "cmd-backspace": "editor::DeleteToBeginningOfLine", - "cmd-delete": "editor::DeleteToEndOfLine", - "ctrl-k": "editor::CutToEndOfLine", - "cmd-shift-D": "editor::DuplicateLine", - "ctrl-cmd-up": "editor::MoveLineUp", - "ctrl-cmd-down": "editor::MoveLineDown", - "cmd-x": "editor::Cut", - "cmd-c": "editor::Copy", - "cmd-v": "editor::Paste", - "cmd-z": "editor::Undo", - "cmd-shift-Z": "editor::Redo", - "up": "editor::MoveUp", - "down": "editor::MoveDown", - "left": "editor::MoveLeft", - "right": "editor::MoveRight", - "ctrl-p": "editor::MoveUp", - "ctrl-n": "editor::MoveDown", - "ctrl-b": "editor::MoveLeft", - "ctrl-f": "editor::MoveRight", - "alt-left": "editor::MoveToPreviousWordStart", - "alt-b": "editor::MoveToPreviousWordStart", - "ctrl-alt-left": "editor::MoveToPreviousSubwordStart", - "ctrl-alt-b": "editor::MoveToPreviousSubwordStart", - "alt-right": "editor::MoveToNextWordEnd", - "alt-f": "editor::MoveToNextWordEnd", - "ctrl-alt-right": "editor::MoveToNextSubwordEnd", - "ctrl-alt-f": "editor::MoveToNextSubwordEnd", - "cmd-left": "editor::MoveToBeginningOfLine", - "ctrl-a": "editor::MoveToBeginningOfLine", - "cmd-right": "editor::MoveToEndOfLine", - "ctrl-e": "editor::MoveToEndOfLine", - "cmd-up": "editor::MoveToBeginning", - "cmd-down": "editor::MoveToEnd", - "shift-up": "editor::SelectUp", - "ctrl-shift-P": "editor::SelectUp", - "shift-down": "editor::SelectDown", - "ctrl-shift-N": "editor::SelectDown", - "shift-left": "editor::SelectLeft", - "ctrl-shift-B": "editor::SelectLeft", - "shift-right": "editor::SelectRight", - "ctrl-shift-F": "editor::SelectRight", - "alt-shift-left": "editor::SelectToPreviousWordStart", - "alt-shift-B": "editor::SelectToPreviousWordStart", - "ctrl-alt-shift-left": "editor::SelectToPreviousSubwordStart", - "ctrl-alt-shift-B": "editor::SelectToPreviousSubwordStart", - "alt-shift-right": "editor::SelectToNextWordEnd", - "alt-shift-F": "editor::SelectToNextWordEnd", - "ctrl-alt-shift-right": "editor::SelectToNextSubwordEnd", - "cmd-shift-up": "editor::SelectToBeginning", - "cmd-shift-down": "editor::SelectToEnd", - "cmd-a": "editor::SelectAll", - "cmd-l": "editor::SelectLine", - "cmd-shift-L": "editor::SplitSelectionIntoLines", - "cmd-alt-up": "editor::AddSelectionAbove", - "cmd-ctrl-p": "editor::AddSelectionAbove", - "cmd-alt-down": "editor::AddSelectionBelow", - "cmd-ctrl-n": "editor::AddSelectionBelow", - "ctrl-alt-shift-F": "editor::SelectToNextSubwordEnd", - "cmd-shift-left": [ - "editor::SelectToBeginningOfLine", - { - "stop_at_soft_wraps": true - } - ], - "ctrl-shift-A": [ - "editor::SelectToBeginningOfLine", - { - "stop_at_soft_wraps": true - } - ], - "cmd-shift-right": [ - "editor::SelectToEndOfLine", - { - "stop_at_soft_wraps": true - } - ], - "ctrl-shift-E": [ - "editor::SelectToEndOfLine", - { - "stop_at_soft_wraps": true - } - ], - "cmd-d": [ - "editor::SelectNext", - { - "replace_newest": false - } - ], - "cmd-k cmd-d": [ - "editor::SelectNext", - { - "replace_newest": true - } - ], - "cmd-/": "editor::ToggleComments", - "alt-up": "editor::SelectLargerSyntaxNode", - "ctrl-w": "editor::SelectLargerSyntaxNode", - "alt-down": "editor::SelectSmallerSyntaxNode", - "ctrl-shift-W": "editor::SelectSmallerSyntaxNode", - "cmd-u": "editor::UndoSelection", - "cmd-shift-U": "editor::RedoSelection", - "f8": "editor::GoToNextDiagnostic", - "shift-f8": "editor::GoToPrevDiagnostic", - "f2": "editor::Rename", - "f12": "editor::GoToDefinition", - "alt-shift-f12": "editor::FindAllReferences", - "ctrl-m": "editor::MoveToEnclosingBracket", - "pageup": "editor::PageUp", - "pagedown": "editor::PageDown", - "alt-cmd-[": "editor::Fold", - "alt-cmd-]": "editor::UnfoldLines", - "alt-cmd-f": "editor::FoldSelectedRanges", - "ctrl-space": "editor::ShowCompletions", - "cmd-.": "editor::ToggleCodeActions", - "alt-enter": "editor::OpenExcerpts", - "cmd-f10": "editor::RestartLanguageServer" + { + "context": "Editor", + "bindings": { + "escape": "editor::Cancel", + "backspace": "editor::Backspace", + "ctrl-h": "editor::Backspace", + "delete": "editor::Delete", + "ctrl-d": "editor::Delete", + "tab": "editor::Tab", + "shift-tab": "editor::TabPrev", + "cmd-[": "editor::Outdent", + "cmd-]": "editor::Indent", + "ctrl-shift-K": "editor::DeleteLine", + "alt-backspace": "editor::DeleteToPreviousWordStart", + "alt-h": "editor::DeleteToPreviousWordStart", + "ctrl-alt-backspace": "editor::DeleteToPreviousSubwordStart", + "ctrl-alt-h": "editor::DeleteToPreviousSubwordStart", + "alt-delete": "editor::DeleteToNextWordEnd", + "alt-d": "editor::DeleteToNextWordEnd", + "ctrl-alt-delete": "editor::DeleteToNextSubwordEnd", + "ctrl-alt-d": "editor::DeleteToNextSubwordEnd", + "cmd-backspace": "editor::DeleteToBeginningOfLine", + "cmd-delete": "editor::DeleteToEndOfLine", + "ctrl-k": "editor::CutToEndOfLine", + "cmd-shift-D": "editor::DuplicateLine", + "ctrl-cmd-up": "editor::MoveLineUp", + "ctrl-cmd-down": "editor::MoveLineDown", + "cmd-x": "editor::Cut", + "cmd-c": "editor::Copy", + "cmd-v": "editor::Paste", + "cmd-z": "editor::Undo", + "cmd-shift-Z": "editor::Redo", + "up": "editor::MoveUp", + "down": "editor::MoveDown", + "left": "editor::MoveLeft", + "right": "editor::MoveRight", + "ctrl-p": "editor::MoveUp", + "ctrl-n": "editor::MoveDown", + "ctrl-b": "editor::MoveLeft", + "ctrl-f": "editor::MoveRight", + "alt-left": "editor::MoveToPreviousWordStart", + "alt-b": "editor::MoveToPreviousWordStart", + "ctrl-alt-left": "editor::MoveToPreviousSubwordStart", + "ctrl-alt-b": "editor::MoveToPreviousSubwordStart", + "alt-right": "editor::MoveToNextWordEnd", + "alt-f": "editor::MoveToNextWordEnd", + "ctrl-alt-right": "editor::MoveToNextSubwordEnd", + "ctrl-alt-f": "editor::MoveToNextSubwordEnd", + "cmd-left": "editor::MoveToBeginningOfLine", + "ctrl-a": "editor::MoveToBeginningOfLine", + "cmd-right": "editor::MoveToEndOfLine", + "ctrl-e": "editor::MoveToEndOfLine", + "cmd-up": "editor::MoveToBeginning", + "cmd-down": "editor::MoveToEnd", + "shift-up": "editor::SelectUp", + "ctrl-shift-P": "editor::SelectUp", + "shift-down": "editor::SelectDown", + "ctrl-shift-N": "editor::SelectDown", + "shift-left": "editor::SelectLeft", + "ctrl-shift-B": "editor::SelectLeft", + "shift-right": "editor::SelectRight", + "ctrl-shift-F": "editor::SelectRight", + "alt-shift-left": "editor::SelectToPreviousWordStart", + "alt-shift-B": "editor::SelectToPreviousWordStart", + "ctrl-alt-shift-left": "editor::SelectToPreviousSubwordStart", + "ctrl-alt-shift-B": "editor::SelectToPreviousSubwordStart", + "alt-shift-right": "editor::SelectToNextWordEnd", + "alt-shift-F": "editor::SelectToNextWordEnd", + "ctrl-alt-shift-right": "editor::SelectToNextSubwordEnd", + "cmd-shift-up": "editor::SelectToBeginning", + "cmd-shift-down": "editor::SelectToEnd", + "cmd-a": "editor::SelectAll", + "cmd-l": "editor::SelectLine", + "cmd-shift-L": "editor::SplitSelectionIntoLines", + "cmd-alt-up": "editor::AddSelectionAbove", + "cmd-ctrl-p": "editor::AddSelectionAbove", + "cmd-alt-down": "editor::AddSelectionBelow", + "cmd-ctrl-n": "editor::AddSelectionBelow", + "ctrl-alt-shift-F": "editor::SelectToNextSubwordEnd", + "cmd-shift-left": [ + "editor::SelectToBeginningOfLine", + { + "stop_at_soft_wraps": true + } + ], + "ctrl-shift-A": [ + "editor::SelectToBeginningOfLine", + { + "stop_at_soft_wraps": true + } + ], + "cmd-shift-right": [ + "editor::SelectToEndOfLine", + { + "stop_at_soft_wraps": true + } + ], + "ctrl-shift-E": [ + "editor::SelectToEndOfLine", + { + "stop_at_soft_wraps": true + } + ], + "cmd-d": [ + "editor::SelectNext", + { + "replace_newest": false + } + ], + "cmd-k cmd-d": [ + "editor::SelectNext", + { + "replace_newest": true + } + ], + "cmd-/": "editor::ToggleComments", + "alt-up": "editor::SelectLargerSyntaxNode", + "ctrl-w": "editor::SelectLargerSyntaxNode", + "alt-down": "editor::SelectSmallerSyntaxNode", + "ctrl-shift-W": "editor::SelectSmallerSyntaxNode", + "cmd-u": "editor::UndoSelection", + "cmd-shift-U": "editor::RedoSelection", + "f8": "editor::GoToNextDiagnostic", + "shift-f8": "editor::GoToPrevDiagnostic", + "f2": "editor::Rename", + "f12": "editor::GoToDefinition", + "alt-shift-f12": "editor::FindAllReferences", + "ctrl-m": "editor::MoveToEnclosingBracket", + "pageup": "editor::PageUp", + "pagedown": "editor::PageDown", + "alt-cmd-[": "editor::Fold", + "alt-cmd-]": "editor::UnfoldLines", + "alt-cmd-f": "editor::FoldSelectedRanges", + "ctrl-space": "editor::ShowCompletions", + "cmd-.": "editor::ToggleCodeActions", + "alt-enter": "editor::OpenExcerpts", + "cmd-f10": "editor::RestartLanguageServer" + } }, - "Editor && renaming": { - "enter": "editor::ConfirmRename" + { + "context": "Editor && mode == full", + "bindings": { + "enter": "editor::Newline", + "cmd-f": [ + "buffer_search::Deploy", + { + "focus": true + } + ], + "cmd-e": [ + "buffer_search::Deploy", + { + "focus": false + } + ], + "cmd-shift-O": "outline::Toggle", + "ctrl-g": "go_to_line::Toggle" + } }, - "Editor && showing_completions": { - "enter": "editor::ConfirmCompletion", - "tab": "editor::ConfirmCompletion" + { + "context": "Editor && renaming", + "bindings": { + "enter": "editor::ConfirmRename" + } }, - "Editor && showing_code_actions": { - "enter": "editor::ConfirmCodeAction" + { + "context": "Editor && showing_completions", + "bindings": { + "enter": "editor::ConfirmCompletion", + "tab": "editor::ConfirmCompletion" + } }, - "Editor && mode == full": { - "enter": "editor::Newline", - "cmd-f": [ - "buffer_search::Deploy", - { - "focus": true - } - ], - "cmd-e": [ - "buffer_search::Deploy", - { - "focus": false - } - ], - "cmd-shift-O": "outline::Toggle", - "ctrl-g": "go_to_line::Toggle" + { + "context": "Editor && showing_code_actions", + "bindings": { + "enter": "editor::ConfirmCodeAction" + } }, - "Editor && mode == auto_height": { - "alt-enter": [ - "editor::Input", - "\n" - ] + { + "context": "Editor && mode == auto_height", + "bindings": { + "alt-enter": [ + "editor::Input", + "\n" + ] + } }, - "GoToLine": { - "escape": "go_to_line::Toggle", - "enter": "go_to_line::Confirm" + { + "context": "GoToLine", + "bindings": { + "escape": "go_to_line::Toggle", + "enter": "go_to_line::Confirm" + } }, - "ChatPanel": { - "enter": "chat_panel::Send" + { + "context": "ChatPanel", + "bindings": { + "enter": "chat_panel::Send" + } }, - "ProjectPanel": { - "left": "project_panel::CollapseSelectedEntry", - "right": "project_panel::ExpandSelectedEntry" + { + "context": "ProjectPanel", + "bindings": { + "left": "project_panel::CollapseSelectedEntry", + "right": "project_panel::ExpandSelectedEntry" + } } -} \ No newline at end of file +] \ No newline at end of file diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 312416f42cd114ca1e2e313b7c9c1a4d8e4e62e3..c0745afd9967fc17ab5ae69eb3643b370bbc4220 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -1,93 +1,111 @@ -{ - "Editor && VimControl": { - "i": [ - "vim::SwitchMode", - "Insert" - ], - "g": [ - "vim::PushOperator", - { - "Namespace": "G" - } - ], - "h": "vim::Left", - "j": "vim::Down", - "k": "vim::Up", - "l": "vim::Right", - "0": "vim::StartOfLine", - "shift-$": "vim::EndOfLine", - "shift-G": "vim::EndOfDocument", - "w": "vim::NextWordStart", - "shift-W": [ - "vim::NextWordStart", - { - "ignorePunctuation": true - } - ], - "e": "vim::NextWordEnd", - "shift-E": [ - "vim::NextWordEnd", - { - "ignorePunctuation": true - } - ], - "b": "vim::PreviousWordStart", - "shift-B": [ - "vim::PreviousWordStart", - { - "ignorePunctuation": true - } - ], - "escape": [ - "vim::SwitchMode", - "Normal" - ] +[ + { + "context": "Editor && VimControl", + "bindings": { + "i": [ + "vim::SwitchMode", + "Insert" + ], + "g": [ + "vim::PushOperator", + { + "Namespace": "G" + } + ], + "h": "vim::Left", + "j": "vim::Down", + "k": "vim::Up", + "l": "vim::Right", + "0": "vim::StartOfLine", + "shift-$": "vim::EndOfLine", + "shift-G": "vim::EndOfDocument", + "w": "vim::NextWordStart", + "shift-W": [ + "vim::NextWordStart", + { + "ignorePunctuation": true + } + ], + "e": "vim::NextWordEnd", + "shift-E": [ + "vim::NextWordEnd", + { + "ignorePunctuation": true + } + ], + "b": "vim::PreviousWordStart", + "shift-B": [ + "vim::PreviousWordStart", + { + "ignorePunctuation": true + } + ], + "escape": [ + "vim::SwitchMode", + "Normal" + ] + } }, - "Editor && vim_operator == g": { - "g": "vim::StartOfDocument" + { + "context": "Editor && vim_operator == g", + "bindings": { + "g": "vim::StartOfDocument" + } }, - "Editor && vim_mode == insert": { - "escape": "vim::NormalBefore", - "ctrl-c": "vim::NormalBefore" + { + "context": "Editor && vim_mode == insert", + "bindings": { + "escape": "vim::NormalBefore", + "ctrl-c": "vim::NormalBefore" + } }, - "Editor && vim_mode == normal": { - "c": [ - "vim::PushOperator", - "Change" - ], - "d": [ - "vim::PushOperator", - "Delete" - ] + { + "context": "Editor && vim_mode == normal", + "bindings": { + "c": [ + "vim::PushOperator", + "Change" + ], + "d": [ + "vim::PushOperator", + "Delete" + ] + } }, - "Editor && vim_operator == c": { - "w": [ - "vim::NextWordEnd", - { - "ignorePunctuation": false - } - ], - "shift-W": [ - "vim::NextWordEnd", - { - "ignorePunctuation": true - } - ] + { + "context": "Editor && vim_operator == c", + "bindings": { + "w": [ + "vim::NextWordEnd", + { + "ignorePunctuation": false + } + ], + "shift-W": [ + "vim::NextWordEnd", + { + "ignorePunctuation": true + } + ] + } }, - "Editor && vim_operator == d": { - "w": [ - "vim::NextWordStart", - { - "ignorePunctuation": false, - "stopAtNewline": true - } - ], - "shift-W": [ - "vim::NextWordStart", - { - "ignorePunctuation": true, - "stopAtNewline": true - } - ] + { + "context": "Editor && vim_operator == d", + "bindings": { + "w": [ + "vim::NextWordStart", + { + "ignorePunctuation": false, + "stopAtNewline": true + } + ], + "shift-W": [ + "vim::NextWordStart", + { + "ignorePunctuation": true, + "stopAtNewline": true + } + ] + } } -} \ No newline at end of file +] \ No newline at end of file diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 37cf03a3cc63a09add10c601b359cc056eb55936..91497c178f1638c4f330a2a83f0c117c1818f245 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1306,6 +1306,10 @@ impl MutableAppContext { } } + pub fn all_action_names<'a>(&'a self) -> impl Iterator + 'a { + self.action_deserializers.keys().copied() + } + pub fn available_actions( &self, window_id: usize, diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index 1f6914989ba646faacbd22a349af53e450a14cbf..99cf81c6ba65abc2ed0373342e35e8a41853502c 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -3,14 +3,38 @@ use anyhow::{Context, Result}; use assets::Assets; use collections::BTreeMap; use gpui::{keymap::Binding, MutableAppContext}; +use schemars::{ + gen::{SchemaGenerator, SchemaSettings}, + schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation}, + JsonSchema, +}; use serde::Deserialize; -use serde_json::value::RawValue; +use serde_json::{value::RawValue, Value}; + +#[derive(Deserialize, Default, Clone, JsonSchema)] +#[serde(transparent)] +pub struct KeymapFileContent(Vec); + +#[derive(Deserialize, Default, Clone, JsonSchema)] +pub struct KeymapBlock { + #[serde(default)] + context: Option, + bindings: BTreeMap, +} #[derive(Deserialize, Default, Clone)] #[serde(transparent)] -pub struct KeymapFileContent(BTreeMap); +pub struct KeymapAction(Box); -type ActionsByKeystroke = BTreeMap>; +impl JsonSchema for KeymapAction { + fn schema_name() -> String { + "KeymapAction".into() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + Schema::Bool(true) + } +} #[derive(Deserialize)] struct ActionWithData(Box, Box); @@ -29,13 +53,12 @@ impl KeymapFileContent { } pub fn add(self, cx: &mut MutableAppContext) -> Result<()> { - for (context, actions) in self.0 { - let context = if context == "*" { None } else { Some(context) }; + for KeymapBlock { context, bindings } in self.0 { cx.add_bindings( - actions + bindings .into_iter() .map(|(keystroke, action)| { - let action = action.get(); + let action = action.0.get(); // This is a workaround for a limitation in serde: serde-rs/json#497 // We want to deserialize the action data as a `RawValue` so that we can @@ -61,3 +84,39 @@ impl KeymapFileContent { Ok(()) } } + +pub fn keymap_file_json_schema(action_names: &[&'static str]) -> serde_json::Value { + let mut root_schema = SchemaSettings::draft07() + .with(|settings| settings.option_add_null_type = false) + .into_generator() + .into_root_schema_for::(); + + let action_schema = Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + one_of: Some(vec![ + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + enum_values: Some( + action_names + .into_iter() + .map(|name| Value::String(name.to_string())) + .collect(), + ), + ..Default::default() + }), + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))), + ..Default::default() + }), + ]), + ..Default::default() + })), + ..Default::default() + }); + + root_schema + .definitions + .insert("KeymapAction".to_owned(), action_schema); + + serde_json::to_value(root_schema).unwrap() +} diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 6ddf9021eefc5f76d025fdaca9229cbc3d99084d..02e83882499a6c6d99bdd4c3344120570d62c586 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -15,7 +15,7 @@ use std::{collections::HashMap, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; -pub use keymap_file::KeymapFileContent; +pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; #[derive(Clone)] pub struct Settings { @@ -78,90 +78,6 @@ impl Settings { }) } - pub fn file_json_schema( - theme_names: Vec, - language_names: Vec, - ) -> serde_json::Value { - let settings = SchemaSettings::draft07().with(|settings| { - settings.option_add_null_type = false; - }); - let generator = SchemaGenerator::new(settings); - let mut root_schema = generator.into_root_schema_for::(); - - // Construct theme names reference type - let theme_names = theme_names - .into_iter() - .map(|name| Value::String(name)) - .collect(); - let theme_names_schema = Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - enum_values: Some(theme_names), - ..Default::default() - }); - root_schema - .definitions - .insert("ThemeName".to_owned(), theme_names_schema); - - // Construct language overrides reference type - let language_override_schema_reference = Schema::Object(SchemaObject { - reference: Some("#/definitions/LanguageOverride".to_owned()), - ..Default::default() - }); - let language_overrides_properties = language_names - .into_iter() - .map(|name| { - ( - name, - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(vec![language_override_schema_reference.clone()]), - ..Default::default() - })), - ..Default::default() - }), - ) - }) - .collect(); - let language_overrides_schema = Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), - object: Some(Box::new(ObjectValidation { - properties: language_overrides_properties, - ..Default::default() - })), - ..Default::default() - }); - root_schema - .definitions - .insert("LanguageOverrides".to_owned(), language_overrides_schema); - - // Modify theme property to use new theme reference type - let settings_file_schema = root_schema.schema.object.as_mut().unwrap(); - let language_overrides_schema_reference = Schema::Object(SchemaObject { - reference: Some("#/definitions/ThemeName".to_owned()), - ..Default::default() - }); - settings_file_schema.properties.insert( - "theme".to_owned(), - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(vec![language_overrides_schema_reference]), - ..Default::default() - })), - ..Default::default() - }), - ); - - // Modify language_overrides property to use LanguageOverrides reference - settings_file_schema.properties.insert( - "language_overrides".to_owned(), - Schema::Object(SchemaObject { - reference: Some("#/definitions/LanguageOverrides".to_owned()), - ..Default::default() - }), - ); - serde_json::to_value(root_schema).unwrap() - } - pub fn with_overrides( mut self, language_name: impl Into>, @@ -249,6 +165,90 @@ impl Settings { } } +pub fn settings_file_json_schema( + theme_names: Vec, + language_names: Vec, +) -> serde_json::Value { + let settings = SchemaSettings::draft07().with(|settings| { + settings.option_add_null_type = false; + }); + let generator = SchemaGenerator::new(settings); + let mut root_schema = generator.into_root_schema_for::(); + + // Construct theme names reference type + let theme_names = theme_names + .into_iter() + .map(|name| Value::String(name)) + .collect(); + let theme_names_schema = Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + enum_values: Some(theme_names), + ..Default::default() + }); + root_schema + .definitions + .insert("ThemeName".to_owned(), theme_names_schema); + + // Construct language overrides reference type + let language_override_schema_reference = Schema::Object(SchemaObject { + reference: Some("#/definitions/LanguageOverride".to_owned()), + ..Default::default() + }); + let language_overrides_properties = language_names + .into_iter() + .map(|name| { + ( + name, + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + all_of: Some(vec![language_override_schema_reference.clone()]), + ..Default::default() + })), + ..Default::default() + }), + ) + }) + .collect(); + let language_overrides_schema = Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), + object: Some(Box::new(ObjectValidation { + properties: language_overrides_properties, + ..Default::default() + })), + ..Default::default() + }); + root_schema + .definitions + .insert("LanguageOverrides".to_owned(), language_overrides_schema); + + // Modify theme property to use new theme reference type + let settings_file_schema = root_schema.schema.object.as_mut().unwrap(); + let language_overrides_schema_reference = Schema::Object(SchemaObject { + reference: Some("#/definitions/ThemeName".to_owned()), + ..Default::default() + }); + settings_file_schema.properties.insert( + "theme".to_owned(), + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + all_of: Some(vec![language_overrides_schema_reference]), + ..Default::default() + })), + ..Default::default() + }), + ); + + // Modify language_overrides property to use LanguageOverrides reference + settings_file_schema.properties.insert( + "language_overrides".to_owned(), + Schema::Object(SchemaObject { + reference: Some("#/definitions/LanguageOverrides".to_owned()), + ..Default::default() + }), + ); + serde_json::to_value(root_schema).unwrap() +} + fn merge(target: &mut T, value: Option) { if let Some(value) = value { *target = value; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 07774b86e04ad0e4c6cbc932d5007192aae8e036..33525400dde33999673c47fbea2f516c94a491ca 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -25,7 +25,7 @@ pub use project::{self, fs}; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde_json::to_string_pretty; -use settings::Settings; +use settings::{keymap_file_json_schema, settings_file_json_schema, Settings}; use std::{ path::{Path, PathBuf}, sync::Arc, @@ -41,6 +41,7 @@ actions!( Quit, DebugElements, OpenSettings, + OpenKeymap, IncreaseBufferFontSize, DecreaseBufferFontSize, InstallCommandLineInterface, @@ -78,39 +79,13 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.add_action({ let app_state = app_state.clone(); move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { - let app_state = app_state.clone(); - cx.spawn(move |workspace, mut cx| async move { - let fs = &app_state.fs; - if !fs.is_file(&SETTINGS_PATH).await { - fs.create_dir(&ROOT_PATH).await?; - fs.create_file(&SETTINGS_PATH, Default::default()).await?; - } - - workspace - .update(&mut cx, |workspace, cx| { - if workspace.project().read(cx).is_local() { - workspace.open_paths(&[SETTINGS_PATH.clone()], cx) - } else { - let (_, workspace) = - cx.add_window((app_state.build_window_options)(), |cx| { - let project = Project::local( - app_state.client.clone(), - app_state.user_store.clone(), - app_state.languages.clone(), - app_state.fs.clone(), - cx, - ); - (app_state.build_workspace)(project, &app_state, cx) - }); - workspace.update(cx, |workspace, cx| { - workspace.open_paths(&[SETTINGS_PATH.clone()], cx) - }) - } - }) - .await; - Ok::<_, anyhow::Error>(()) - }) - .detach_and_log_err(cx); + open_config_file(&SETTINGS_PATH, app_state.clone(), cx); + } + }); + cx.add_action({ + let app_state = app_state.clone(); + move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext| { + open_config_file(&KEYMAP_PATH, app_state.clone(), cx); } }); cx.add_action( @@ -137,7 +112,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { ); workspace::lsp_status::init(cx); - settings::KeymapFileContent::load_defaults(cx); } @@ -179,13 +153,18 @@ pub fn build_workspace( let theme_names = app_state.themes.list().collect(); let language_names = app_state.languages.language_names(); - project.update(cx, |project, _| { + project.update(cx, |project, cx| { + let action_names = cx.all_action_names().collect::>(); project.set_language_server_settings(serde_json::json!({ "json": { "schemas": [ { - "fileMatch": "**/.zed/settings.json", - "schema": Settings::file_json_schema(theme_names, language_names), + "fileMatch": [".zed/settings.json"], + "schema": settings_file_json_schema(theme_names, language_names), + }, + { + "fileMatch": [".zed/keymap.json"], + "schema": keymap_file_json_schema(&action_names), } ] } @@ -289,6 +268,44 @@ async fn install_cli(cx: &AsyncAppContext) -> Result<()> { } } +fn open_config_file( + path: &'static Path, + app_state: Arc, + cx: &mut ViewContext, +) { + cx.spawn(|workspace, mut cx| async move { + let fs = &app_state.fs; + if !fs.is_file(path).await { + fs.create_dir(&ROOT_PATH).await?; + fs.create_file(path, Default::default()).await?; + } + + workspace + .update(&mut cx, |workspace, cx| { + if workspace.project().read(cx).is_local() { + workspace.open_paths(&[path.to_path_buf()], cx) + } else { + let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| { + let project = Project::local( + app_state.client.clone(), + app_state.user_store.clone(), + app_state.languages.clone(), + app_state.fs.clone(), + cx, + ); + (app_state.build_workspace)(project, &app_state, cx) + }); + workspace.update(cx, |workspace, cx| { + workspace.open_paths(&[path.to_path_buf()], cx) + }) + } + }) + .await; + Ok::<_, anyhow::Error>(()) + }) + .detach_and_log_err(cx) +} + #[cfg(test)] mod tests { use super::*;