Detailed changes
@@ -168,6 +168,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109"
+[[package]]
+name = "assets"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "gpui",
+ "rust-embed",
+]
+
[[package]]
name = "async-attributes"
version = "1.1.2"
@@ -4300,6 +4309,7 @@ dependencies = [
"log",
"postage",
"project",
+ "serde",
"serde_json",
"settings",
"theme",
@@ -4464,6 +4474,8 @@ name = "settings"
version = "0.1.0"
dependencies = [
"anyhow",
+ "assets",
+ "collections",
"gpui",
"schemars",
"serde",
@@ -5787,6 +5799,7 @@ checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
name = "vim"
version = "0.1.0"
dependencies = [
+ "assets",
"collections",
"editor",
"gpui",
@@ -5794,6 +5807,7 @@ dependencies = [
"language",
"log",
"project",
+ "serde",
"settings",
"util",
"workspace",
@@ -6061,6 +6075,7 @@ name = "zed"
version = "0.25.0"
dependencies = [
"anyhow",
+ "assets",
"async-compression",
"async-recursion",
"async-trait",
@@ -6181,6 +6196,7 @@ dependencies = [
"sha-1 0.9.6",
"sqlx 0.5.5",
"surf",
+ "theme",
"tide",
"tide-compress",
"time 0.2.27",
@@ -0,0 +1,259 @@
+{
+ "*": {
+ "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator",
+ "cmd-s": "workspace::Save",
+ "cmd-alt-i": "workspace::DebugElements",
+ "cmd-k cmd-left": "workspace::ActivatePreviousPane",
+ "cmd-k cmd-right": "workspace::ActivateNextPane",
+ "cmd-=": "zed::IncreaseBufferFontSize",
+ "cmd--": "zed::DecreaseBufferFontSize",
+ "cmd-,": "zed::OpenSettings"
+ },
+ "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"
+ },
+ "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"
+ },
+ "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",
+ "alt-shift-D": "diagnostics::Deploy",
+ "ctrl-alt-cmd-j": "journal::NewJournalEntry"
+ },
+ "ProjectSearchBar": {
+ "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"
+ },
+ "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"
+ },
+ "Editor && renaming": {
+ "enter": "editor::ConfirmRename"
+ },
+ "Editor && showing_completions": {
+ "enter": "editor::ConfirmCompletion"
+ },
+ "Editor && showing_code_actions": {
+ "enter": "editor::ConfirmCodeAction"
+ },
+ "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"
+ },
+ "Editor && mode == auto_height": {
+ "alt-enter": [
+ "editor::Input",
+ "\n"
+ ]
+ },
+ "OutlineView": {
+ "escape": "outline::Toggle"
+ },
+ "ProjectSymbolsView": {
+ "escape": "project_symbols::Toggle"
+ },
+ "ThemeSelector": {
+ "escape": "theme_selector::Toggle"
+ },
+ "GoToLine": {
+ "escape": "go_to_line::Toggle",
+ "enter": "go_to_line::Confirm"
+ },
+ "FileFinder": {
+ "escape": "file_finder::Toggle"
+ },
+ "ChatPanel": {
+ "enter": "chat_panel::Send"
+ },
+ "ProjectPanel": {
+ "left": "project_panel::CollapseSelectedEntry",
+ "right": "project_panel::ExpandSelectedEntry"
+ }
+}
@@ -0,0 +1,58 @@
+{
+ "Editor && vim_mode == insert": {
+ "escape": "vim::NormalBefore",
+ "ctrl-c": "vim::NormalBefore"
+ },
+ "Editor && vim_mode == normal && vim_submode == g": {
+ "g": "vim::MoveToStart",
+ "escape": [
+ "vim::SwitchMode",
+ {
+ "Normal": "None"
+ }
+ ]
+ },
+ "Editor && vim_mode == normal": {
+ "i": [
+ "vim::SwitchMode",
+ "Insert"
+ ],
+ "g": [
+ "vim::SwitchMode",
+ {
+ "Normal": "GPrefix"
+ }
+ ],
+ "h": "vim::MoveLeft",
+ "j": "vim::MoveDown",
+ "k": "vim::MoveUp",
+ "l": "vim::MoveRight",
+ "0": "vim::MoveToStartOfLine",
+ "shift-$": "vim::MoveToEndOfLine",
+ "shift-G": "vim::MoveToEnd",
+ "w": [
+ "vim::MoveToNextWordStart",
+ false
+ ],
+ "shift-W": [
+ "vim::MoveToNextWordStart",
+ true
+ ],
+ "e": [
+ "vim::MoveToNextWordEnd",
+ false
+ ],
+ "shift-E": [
+ "vim::MoveToNextWordEnd",
+ true
+ ],
+ "b": [
+ "vim::MoveToPreviousWordStart",
+ false
+ ],
+ "shift-B": [
+ "vim::MoveToPreviousWordStart",
+ true
+ ]
+ }
+}
@@ -0,0 +1,14 @@
+[package]
+name = "assets"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "src/assets.rs"
+doctest = false
+
+[dependencies]
+gpui = { path = "../gpui" }
+anyhow = "1.0.38"
+rust-embed = { version = "6.3", features = ["include-exclude"] }
+
@@ -3,7 +3,7 @@ use gpui::AssetSource;
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
-#[folder = "assets"]
+#[folder = "../../assets"]
#[exclude = "*.DS_Store"]
pub struct Assets;
@@ -6,7 +6,6 @@ use editor::Editor;
use gpui::{
actions,
elements::*,
- keymap::Binding,
platform::CursorStyle,
views::{ItemType, Select, SelectStyle},
AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View,
@@ -38,8 +37,6 @@ actions!(chat_panel, [Send, LoadMoreMessages]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ChatPanel::send);
cx.add_action(ChatPanel::load_more_messages);
-
- cx.add_bindings(vec![Binding::new("enter", Send, Some("ChatPanel"))]);
}
impl ChatPanel {
@@ -8,9 +8,8 @@ use editor::{
highlight_diagnostic_message, Editor, ExcerptId, MultiBuffer, ToOffset,
};
use gpui::{
- actions, elements::*, fonts::TextStyle, keymap::Binding, AnyViewHandle, AppContext, Entity,
- ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
- WeakViewHandle,
+ actions, elements::*, fonts::TextStyle, AnyViewHandle, AppContext, Entity, ModelHandle,
+ MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use language::{
Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, SelectionGoal,
@@ -33,7 +32,6 @@ actions!(diagnostics, [Deploy]);
const CONTEXT_LINE_COUNT: u32 = 1;
pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings([Binding::new("alt-shift-D", Deploy, Some("Workspace"))]);
cx.add_action(ProjectDiagnosticsEditor::deploy);
}
@@ -22,8 +22,7 @@ use gpui::{
executor,
fonts::{self, HighlightStyle, TextStyle},
geometry::vector::{vec2f, Vector2F},
- impl_actions,
- keymap::Binding,
+ impl_actions, impl_internal_actions,
platform::CursorStyle,
text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
@@ -66,8 +65,11 @@ const MAX_LINE_LEN: usize = 1024;
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
const MAX_SELECTION_HISTORY_LEN: usize = 1024;
-#[derive(Clone)]
-pub struct SelectNext(pub bool);
+#[derive(Clone, Deserialize)]
+pub struct SelectNext {
+ #[serde(default)]
+ pub replace_newest: bool,
+}
#[derive(Clone)]
pub struct GoToDiagnostic(pub Direction);
@@ -78,47 +80,38 @@ pub struct Scroll(pub Vector2F);
#[derive(Clone)]
pub struct Select(pub SelectPhase);
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct Input(pub String);
-#[derive(Clone)]
-pub struct Tab(pub Direction);
-
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct SelectToBeginningOfLine {
+ #[serde(default)]
stop_at_soft_wraps: bool,
}
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct SelectToEndOfLine {
+ #[serde(default)]
stop_at_soft_wraps: bool,
}
-#[derive(Clone)]
-pub struct ToggleCodeActions(pub bool);
-
-#[derive(Clone)]
-pub struct ConfirmCompletion(pub Option<usize>);
+#[derive(Clone, Deserialize)]
+pub struct ToggleCodeActions {
+ #[serde(default)]
+ pub deployed_from_indicator: bool,
+}
-#[derive(Clone)]
-pub struct ConfirmCodeAction(pub Option<usize>);
+#[derive(Clone, Default, Deserialize)]
+pub struct ConfirmCompletion {
+ #[serde(default)]
+ pub item_ix: Option<usize>,
+}
-impl_actions!(
- editor,
- [
- SelectNext,
- GoToDiagnostic,
- Scroll,
- Select,
- Input,
- Tab,
- SelectToBeginningOfLine,
- SelectToEndOfLine,
- ToggleCodeActions,
- ConfirmCompletion,
- ConfirmCodeAction,
- ]
-);
+#[derive(Clone, Default, Deserialize)]
+pub struct ConfirmCodeAction {
+ #[serde(default)]
+ pub item_ix: Option<usize>,
+}
actions!(
editor,
@@ -127,6 +120,8 @@ actions!(
Backspace,
Delete,
Newline,
+ GoToNextDiagnostic,
+ GoToPrevDiagnostic,
Indent,
Outdent,
DeleteLine,
@@ -172,6 +167,8 @@ actions!(
SplitSelectionIntoLines,
AddSelectionAbove,
AddSelectionBelow,
+ Tab,
+ TabPrev,
ToggleComments,
SelectLargerSyntaxNode,
SelectSmallerSyntaxNode,
@@ -193,6 +190,21 @@ actions!(
]
);
+impl_actions!(
+ editor,
+ [
+ Input,
+ SelectNext,
+ SelectToBeginningOfLine,
+ SelectToEndOfLine,
+ ToggleCodeActions,
+ ConfirmCompletion,
+ ConfirmCodeAction,
+ ]
+);
+
+impl_internal_actions!(editor, [Scroll, Select]);
+
enum DocumentHighlightRead {}
enum DocumentHighlightWrite {}
@@ -203,175 +215,6 @@ pub enum Direction {
}
pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings(vec![
- Binding::new("escape", Cancel, Some("Editor")),
- Binding::new("backspace", Backspace, Some("Editor")),
- Binding::new("ctrl-h", Backspace, Some("Editor")),
- Binding::new("delete", Delete, Some("Editor")),
- Binding::new("ctrl-d", Delete, Some("Editor")),
- Binding::new("enter", Newline, Some("Editor && mode == full")),
- Binding::new(
- "alt-enter",
- Input("\n".into()),
- Some("Editor && mode == auto_height"),
- ),
- Binding::new(
- "enter",
- ConfirmCompletion(None),
- Some("Editor && showing_completions"),
- ),
- Binding::new(
- "enter",
- ConfirmCodeAction(None),
- Some("Editor && showing_code_actions"),
- ),
- Binding::new("enter", ConfirmRename, Some("Editor && renaming")),
- Binding::new("tab", Tab(Direction::Next), Some("Editor")),
- Binding::new("shift-tab", Tab(Direction::Prev), Some("Editor")),
- Binding::new(
- "tab",
- ConfirmCompletion(None),
- Some("Editor && showing_completions"),
- ),
- Binding::new("cmd-[", Outdent, Some("Editor")),
- Binding::new("cmd-]", Indent, Some("Editor")),
- Binding::new("ctrl-shift-K", DeleteLine, Some("Editor")),
- Binding::new("alt-backspace", DeleteToPreviousWordStart, Some("Editor")),
- Binding::new("alt-h", DeleteToPreviousWordStart, Some("Editor")),
- Binding::new(
- "ctrl-alt-backspace",
- DeleteToPreviousSubwordStart,
- Some("Editor"),
- ),
- Binding::new("ctrl-alt-h", DeleteToPreviousSubwordStart, Some("Editor")),
- Binding::new("alt-delete", DeleteToNextWordEnd, Some("Editor")),
- Binding::new("alt-d", DeleteToNextWordEnd, Some("Editor")),
- Binding::new("ctrl-alt-delete", DeleteToNextSubwordEnd, Some("Editor")),
- Binding::new("ctrl-alt-d", DeleteToNextSubwordEnd, Some("Editor")),
- Binding::new("cmd-backspace", DeleteToBeginningOfLine, Some("Editor")),
- Binding::new("cmd-delete", DeleteToEndOfLine, Some("Editor")),
- Binding::new("ctrl-k", CutToEndOfLine, Some("Editor")),
- Binding::new("cmd-shift-D", DuplicateLine, Some("Editor")),
- Binding::new("ctrl-cmd-up", MoveLineUp, Some("Editor")),
- Binding::new("ctrl-cmd-down", MoveLineDown, Some("Editor")),
- Binding::new("cmd-x", Cut, Some("Editor")),
- Binding::new("cmd-c", Copy, Some("Editor")),
- Binding::new("cmd-v", Paste, Some("Editor")),
- Binding::new("cmd-z", Undo, Some("Editor")),
- Binding::new("cmd-shift-Z", Redo, Some("Editor")),
- Binding::new("up", MoveUp, Some("Editor")),
- Binding::new("down", MoveDown, Some("Editor")),
- Binding::new("left", MoveLeft, Some("Editor")),
- Binding::new("right", MoveRight, Some("Editor")),
- Binding::new("ctrl-p", MoveUp, Some("Editor")),
- Binding::new("ctrl-n", MoveDown, Some("Editor")),
- Binding::new("ctrl-b", MoveLeft, Some("Editor")),
- Binding::new("ctrl-f", MoveRight, Some("Editor")),
- Binding::new("alt-left", MoveToPreviousWordStart, Some("Editor")),
- Binding::new("alt-b", MoveToPreviousWordStart, Some("Editor")),
- Binding::new("ctrl-alt-left", MoveToPreviousSubwordStart, Some("Editor")),
- Binding::new("ctrl-alt-b", MoveToPreviousSubwordStart, Some("Editor")),
- Binding::new("alt-right", MoveToNextWordEnd, Some("Editor")),
- Binding::new("alt-f", MoveToNextWordEnd, Some("Editor")),
- Binding::new("ctrl-alt-right", MoveToNextSubwordEnd, Some("Editor")),
- Binding::new("ctrl-alt-f", MoveToNextSubwordEnd, Some("Editor")),
- Binding::new("cmd-left", MoveToBeginningOfLine, Some("Editor")),
- Binding::new("ctrl-a", MoveToBeginningOfLine, Some("Editor")),
- Binding::new("cmd-right", MoveToEndOfLine, Some("Editor")),
- Binding::new("ctrl-e", MoveToEndOfLine, Some("Editor")),
- Binding::new("cmd-up", MoveToBeginning, Some("Editor")),
- Binding::new("cmd-down", MoveToEnd, Some("Editor")),
- Binding::new("shift-up", SelectUp, Some("Editor")),
- Binding::new("ctrl-shift-P", SelectUp, Some("Editor")),
- Binding::new("shift-down", SelectDown, Some("Editor")),
- Binding::new("ctrl-shift-N", SelectDown, Some("Editor")),
- Binding::new("shift-left", SelectLeft, Some("Editor")),
- Binding::new("ctrl-shift-B", SelectLeft, Some("Editor")),
- Binding::new("shift-right", SelectRight, Some("Editor")),
- Binding::new("ctrl-shift-F", SelectRight, Some("Editor")),
- Binding::new("alt-shift-left", SelectToPreviousWordStart, Some("Editor")),
- Binding::new("alt-shift-B", SelectToPreviousWordStart, Some("Editor")),
- Binding::new(
- "ctrl-alt-shift-left",
- SelectToPreviousSubwordStart,
- Some("Editor"),
- ),
- Binding::new(
- "ctrl-alt-shift-B",
- SelectToPreviousSubwordStart,
- Some("Editor"),
- ),
- Binding::new("alt-shift-right", SelectToNextWordEnd, Some("Editor")),
- Binding::new("alt-shift-F", SelectToNextWordEnd, Some("Editor")),
- Binding::new(
- "cmd-shift-left",
- SelectToBeginningOfLine {
- stop_at_soft_wraps: true,
- },
- Some("Editor"),
- ),
- Binding::new(
- "ctrl-alt-shift-right",
- SelectToNextSubwordEnd,
- Some("Editor"),
- ),
- Binding::new("ctrl-alt-shift-F", SelectToNextSubwordEnd, Some("Editor")),
- Binding::new(
- "ctrl-shift-A",
- SelectToBeginningOfLine {
- stop_at_soft_wraps: true,
- },
- Some("Editor"),
- ),
- Binding::new(
- "cmd-shift-right",
- SelectToEndOfLine {
- stop_at_soft_wraps: true,
- },
- Some("Editor"),
- ),
- Binding::new(
- "ctrl-shift-E",
- SelectToEndOfLine {
- stop_at_soft_wraps: true,
- },
- Some("Editor"),
- ),
- Binding::new("cmd-shift-up", SelectToBeginning, Some("Editor")),
- Binding::new("cmd-shift-down", SelectToEnd, Some("Editor")),
- Binding::new("cmd-a", SelectAll, Some("Editor")),
- Binding::new("cmd-l", SelectLine, Some("Editor")),
- Binding::new("cmd-shift-L", SplitSelectionIntoLines, Some("Editor")),
- Binding::new("cmd-alt-up", AddSelectionAbove, Some("Editor")),
- Binding::new("cmd-ctrl-p", AddSelectionAbove, Some("Editor")),
- Binding::new("cmd-alt-down", AddSelectionBelow, Some("Editor")),
- Binding::new("cmd-ctrl-n", AddSelectionBelow, Some("Editor")),
- Binding::new("cmd-d", SelectNext(false), Some("Editor")),
- Binding::new("cmd-k cmd-d", SelectNext(true), Some("Editor")),
- Binding::new("cmd-/", ToggleComments, Some("Editor")),
- Binding::new("alt-up", SelectLargerSyntaxNode, Some("Editor")),
- Binding::new("ctrl-w", SelectLargerSyntaxNode, Some("Editor")),
- Binding::new("alt-down", SelectSmallerSyntaxNode, Some("Editor")),
- Binding::new("ctrl-shift-W", SelectSmallerSyntaxNode, Some("Editor")),
- Binding::new("cmd-u", UndoSelection, Some("Editor")),
- Binding::new("cmd-shift-U", RedoSelection, Some("Editor")),
- Binding::new("f8", GoToDiagnostic(Direction::Next), Some("Editor")),
- Binding::new("shift-f8", GoToDiagnostic(Direction::Prev), Some("Editor")),
- Binding::new("f2", Rename, Some("Editor")),
- Binding::new("f12", GoToDefinition, Some("Editor")),
- Binding::new("alt-shift-f12", FindAllReferences, Some("Editor")),
- Binding::new("ctrl-m", MoveToEnclosingBracket, Some("Editor")),
- Binding::new("pageup", PageUp, Some("Editor")),
- Binding::new("pagedown", PageDown, Some("Editor")),
- Binding::new("alt-cmd-[", Fold, Some("Editor")),
- Binding::new("alt-cmd-]", UnfoldLines, Some("Editor")),
- Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")),
- Binding::new("ctrl-space", ShowCompletions, Some("Editor")),
- Binding::new("cmd-.", ToggleCodeActions(false), Some("Editor")),
- Binding::new("alt-enter", OpenExcerpts, Some("Editor")),
- Binding::new("cmd-f10", RestartLanguageServer, Some("Editor")),
- ]);
-
cx.add_action(Editor::open_new);
cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
cx.add_action(Editor::select);
@@ -381,6 +224,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(Editor::backspace);
cx.add_action(Editor::delete);
cx.add_action(Editor::tab);
+ cx.add_action(Editor::tab_prev);
cx.add_action(Editor::indent);
cx.add_action(Editor::outdent);
cx.add_action(Editor::delete_line);
@@ -435,7 +279,8 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(Editor::move_to_enclosing_bracket);
cx.add_action(Editor::undo_selection);
cx.add_action(Editor::redo_selection);
- cx.add_action(Editor::go_to_diagnostic);
+ cx.add_action(Editor::go_to_next_diagnostic);
+ cx.add_action(Editor::go_to_prev_diagnostic);
cx.add_action(Editor::go_to_definition);
cx.add_action(Editor::page_up);
cx.add_action(Editor::page_down);
@@ -833,7 +678,9 @@ impl CompletionsMenu {
)
.with_cursor_style(CursorStyle::PointingHand)
.on_mouse_down(move |cx| {
- cx.dispatch_action(ConfirmCompletion(Some(item_ix)));
+ cx.dispatch_action(ConfirmCompletion {
+ item_ix: Some(item_ix),
+ });
})
.boxed(),
);
@@ -959,7 +806,9 @@ impl CodeActionsMenu {
})
.with_cursor_style(CursorStyle::PointingHand)
.on_mouse_down(move |cx| {
- cx.dispatch_action(ConfirmCodeAction(Some(item_ix)));
+ cx.dispatch_action(ConfirmCodeAction {
+ item_ix: Some(item_ix),
+ });
})
.boxed(),
);
@@ -2461,7 +2310,7 @@ impl Editor {
pub fn confirm_completion(
&mut self,
- ConfirmCompletion(completion_ix): &ConfirmCompletion,
+ action: &ConfirmCompletion,
cx: &mut ViewContext<Self>,
) -> Option<Task<Result<()>>> {
use language::ToOffset as _;
@@ -2474,7 +2323,7 @@ impl Editor {
let mat = completions_menu
.matches
- .get(completion_ix.unwrap_or(completions_menu.selected_item))?;
+ .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
let buffer_handle = completions_menu.buffer;
let completion = completions_menu.completions.get(mat.candidate_id)?;
@@ -2564,11 +2413,7 @@ impl Editor {
}))
}
- pub fn toggle_code_actions(
- &mut self,
- &ToggleCodeActions(deployed_from_indicator): &ToggleCodeActions,
- cx: &mut ViewContext<Self>,
- ) {
+ pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
if matches!(
self.context_menu.as_ref(),
Some(ContextMenu::CodeActions(_))
@@ -2578,6 +2423,7 @@ impl Editor {
return;
}
+ let deployed_from_indicator = action.deployed_from_indicator;
let mut task = self.code_actions_task.take();
cx.spawn_weak(|this, mut cx| async move {
while let Some(prev_task) = task {
@@ -2612,7 +2458,7 @@ impl Editor {
pub fn confirm_code_action(
workspace: &mut Workspace,
- ConfirmCodeAction(action_ix): &ConfirmCodeAction,
+ action: &ConfirmCodeAction,
cx: &mut ViewContext<Workspace>,
) -> Option<Task<Result<()>>> {
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
@@ -2623,7 +2469,7 @@ impl Editor {
} else {
return None;
};
- let action_ix = action_ix.unwrap_or(actions_menu.selected_item);
+ let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
let action = actions_menu.actions.get(action_ix)?.clone();
let title = action.lsp_action.title.clone();
let buffer = actions_menu.buffer;
@@ -2850,7 +2696,9 @@ impl Editor {
.with_cursor_style(CursorStyle::PointingHand)
.with_padding(Padding::uniform(3.))
.on_mouse_down(|cx| {
- cx.dispatch_action(ToggleCodeActions(true));
+ cx.dispatch_action(ToggleCodeActions {
+ deployed_from_indicator: true,
+ });
})
.boxed(),
)
@@ -2944,8 +2792,8 @@ impl Editor {
self.move_to_snippet_tabstop(Bias::Right, cx)
}
- pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) {
- self.move_to_snippet_tabstop(Bias::Left, cx);
+ pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
+ self.move_to_snippet_tabstop(Bias::Left, cx)
}
pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
@@ -3050,54 +2898,46 @@ impl Editor {
});
}
- pub fn tab(&mut self, &Tab(direction): &Tab, cx: &mut ViewContext<Self>) {
- match direction {
- Direction::Prev => {
- if !self.snippet_stack.is_empty() {
- self.move_to_prev_snippet_tabstop(cx);
- return;
- }
+ pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
+ if self.move_to_prev_snippet_tabstop(cx) {
+ return;
+ }
- self.outdent(&Outdent, cx);
- }
- Direction::Next => {
- if self.move_to_next_snippet_tabstop(cx) {
- return;
- }
+ self.outdent(&Outdent, cx);
+ }
- let mut selections = self.local_selections::<Point>(cx);
- if selections.iter().all(|s| s.is_empty()) {
- self.transact(cx, |this, cx| {
- this.buffer.update(cx, |buffer, cx| {
- for selection in &mut selections {
- let language_name =
- buffer.language_at(selection.start, cx).map(|l| l.name());
- let tab_size =
- cx.global::<Settings>().tab_size(language_name.as_deref());
- let char_column = buffer
- .read(cx)
- .text_for_range(
- Point::new(selection.start.row, 0)..selection.start,
- )
- .flat_map(str::chars)
- .count();
- let chars_to_next_tab_stop =
- tab_size - (char_column as u32 % tab_size);
- buffer.edit(
- [selection.start..selection.start],
- " ".repeat(chars_to_next_tab_stop as usize),
- cx,
- );
- selection.start.column += chars_to_next_tab_stop;
- selection.end = selection.start;
- }
- });
- this.update_selections(selections, Some(Autoscroll::Fit), cx);
- });
- } else {
- self.indent(&Indent, cx);
- }
- }
+ pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
+ if self.move_to_next_snippet_tabstop(cx) {
+ return;
+ }
+
+ let mut selections = self.local_selections::<Point>(cx);
+ if selections.iter().all(|s| s.is_empty()) {
+ self.transact(cx, |this, cx| {
+ this.buffer.update(cx, |buffer, cx| {
+ for selection in &mut selections {
+ let language_name =
+ buffer.language_at(selection.start, cx).map(|l| l.name());
+ let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
+ let char_column = buffer
+ .read(cx)
+ .text_for_range(Point::new(selection.start.row, 0)..selection.start)
+ .flat_map(str::chars)
+ .count();
+ let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
+ buffer.edit(
+ [selection.start..selection.start],
+ " ".repeat(chars_to_next_tab_stop as usize),
+ cx,
+ );
+ selection.start.column += chars_to_next_tab_stop;
+ selection.end = selection.start;
+ }
+ });
+ this.update_selections(selections, Some(Autoscroll::Fit), cx);
+ });
+ } else {
+ self.indent(&Indent, cx);
}
}
@@ -4241,7 +4081,6 @@ impl Editor {
pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
self.push_to_selection_history();
- let replace_newest = action.0;
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = &display_map.buffer_snapshot;
let mut selections = self.local_selections::<usize>(cx);
@@ -4280,7 +4119,7 @@ impl Editor {
}
if let Some(next_selected_range) = next_selected_range {
- if replace_newest {
+ if action.replace_newest {
if let Some(newest_id) =
selections.iter().max_by_key(|s| s.id).map(|s| s.id)
{
@@ -4551,11 +4390,15 @@ impl Editor {
self.selection_history.mode = SelectionHistoryMode::Normal;
}
- pub fn go_to_diagnostic(
- &mut self,
- &GoToDiagnostic(direction): &GoToDiagnostic,
- cx: &mut ViewContext<Self>,
- ) {
+ fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
+ self.go_to_diagnostic(Direction::Next, cx)
+ }
+
+ fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
+ self.go_to_diagnostic(Direction::Prev, cx)
+ }
+
+ pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
let buffer = self.buffer.read(cx).snapshot(cx);
let selection = self.newest_selection_with_snapshot::<usize>(&buffer);
let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
@@ -7775,7 +7618,7 @@ mod tests {
);
// indent from mid-tabstop to full tabstop
- view.tab(&Tab(Direction::Next), cx);
+ view.tab(&Tab, cx);
assert_text_with_selections(
view,
indoc! {"
@@ -7786,7 +7629,7 @@ mod tests {
);
// outdent from 1 tabstop to 0 tabstops
- view.tab(&Tab(Direction::Prev), cx);
+ view.tab_prev(&TabPrev, cx);
assert_text_with_selections(
view,
indoc! {"
@@ -7807,7 +7650,7 @@ mod tests {
);
// indent and outdent affect only the preceding line
- view.tab(&Tab(Direction::Next), cx);
+ view.tab(&Tab, cx);
assert_text_with_selections(
view,
indoc! {"
@@ -7816,7 +7659,7 @@ mod tests {
] four"},
cx,
);
- view.tab(&Tab(Direction::Prev), cx);
+ view.tab_prev(&TabPrev, cx);
assert_text_with_selections(
view,
indoc! {"
@@ -7835,7 +7678,7 @@ mod tests {
four"},
cx,
);
- view.tab(&Tab(Direction::Next), cx);
+ view.tab(&Tab, cx);
assert_text_with_selections(
view,
indoc! {"
@@ -7853,7 +7696,7 @@ mod tests {
four"},
cx,
);
- view.tab(&Tab(Direction::Prev), cx);
+ view.tab_prev(&TabPrev, cx);
assert_text_with_selections(
view,
indoc! {"
@@ -7943,7 +7786,7 @@ mod tests {
cx,
);
- editor.tab(&Tab(Direction::Next), cx);
+ editor.tab(&Tab, cx);
assert_text_with_selections(
&mut editor,
indoc! {"
@@ -7954,7 +7797,7 @@ mod tests {
"},
cx,
);
- editor.tab(&Tab(Direction::Prev), cx);
+ editor.tab_prev(&TabPrev, cx);
assert_text_with_selections(
&mut editor,
indoc! {"
@@ -8697,10 +8540,20 @@ mod tests {
view.update(cx, |view, cx| {
view.select_ranges([ranges[1].start + 1..ranges[1].start + 1], None, cx);
- view.select_next(&SelectNext(false), cx);
+ view.select_next(
+ &SelectNext {
+ replace_newest: false,
+ },
+ cx,
+ );
assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
- view.select_next(&SelectNext(false), cx);
+ view.select_next(
+ &SelectNext {
+ replace_newest: false,
+ },
+ cx,
+ );
assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
view.undo_selection(&UndoSelection, cx);
@@ -8709,10 +8562,20 @@ mod tests {
view.redo_selection(&RedoSelection, cx);
assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
- view.select_next(&SelectNext(false), cx);
+ view.select_next(
+ &SelectNext {
+ replace_newest: false,
+ },
+ cx,
+ );
assert_eq!(view.selected_ranges(cx), &ranges[1..4]);
- view.select_next(&SelectNext(false), cx);
+ view.select_next(
+ &SelectNext {
+ replace_newest: false,
+ },
+ cx,
+ );
assert_eq!(view.selected_ranges(cx), &ranges[0..4]);
});
}
@@ -9367,7 +9230,7 @@ mod tests {
let apply_additional_edits = editor.update(cx, |editor, cx| {
editor.move_down(&MoveDown, cx);
let apply_additional_edits = editor
- .confirm_completion(&ConfirmCompletion(None), cx)
+ .confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap();
assert_eq!(
editor.text(cx),
@@ -9450,7 +9313,7 @@ mod tests {
let apply_additional_edits = editor.update(cx, |editor, cx| {
let apply_additional_edits = editor
- .confirm_completion(&ConfirmCompletion(None), cx)
+ .confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap();
assert_eq!(
editor.text(cx),
@@ -1,12 +1,8 @@
use editor::Editor;
use fuzzy::PathMatch;
use gpui::{
- actions,
- elements::*,
- impl_actions,
- keymap::{self, Binding},
- AppContext, Axis, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View,
- ViewContext, ViewHandle, WeakViewHandle,
+ actions, elements::*, impl_internal_actions, keymap, AppContext, Axis, Entity, ModelHandle,
+ MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use project::{Project, ProjectPath, WorktreeId};
use settings::Settings;
@@ -41,8 +37,8 @@ pub struct FileFinder {
#[derive(Clone)]
pub struct Select(pub ProjectPath);
-impl_actions!(file_finder, [Select]);
actions!(file_finder, [Toggle]);
+impl_internal_actions!(file_finder, [Select]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(FileFinder::toggle);
@@ -50,11 +46,6 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(FileFinder::select);
cx.add_action(FileFinder::select_prev);
cx.add_action(FileFinder::select_next);
-
- cx.add_bindings(vec![
- Binding::new("cmd-p", Toggle, None),
- Binding::new("escape", Toggle, Some("FileFinder")),
- ]);
}
pub enum Event {
@@ -1,7 +1,7 @@
use editor::{display_map::ToDisplayPoint, Autoscroll, DisplayPoint, Editor};
use gpui::{
- actions, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
- MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
+ actions, elements::*, geometry::vector::Vector2F, Axis, Entity, MutableAppContext,
+ RenderContext, View, ViewContext, ViewHandle,
};
use settings::Settings;
use text::{Bias, Point};
@@ -10,11 +10,6 @@ use workspace::Workspace;
actions!(go_to_line, [Toggle, Confirm]);
pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings([
- Binding::new("ctrl-g", Toggle, Some("Editor")),
- Binding::new("escape", Toggle, Some("GoToLine")),
- Binding::new("enter", Confirm, Some("GoToLine")),
- ]);
cx.add_action(GoToLine::toggle);
cx.add_action(GoToLine::confirm);
}
@@ -10,7 +10,7 @@ use crate::{
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
};
pub use action::*;
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, Context, Result};
use collections::btree_map;
use keymap::MatchResult;
use lazy_static::lazy_static;
@@ -715,12 +715,14 @@ type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext
type ObservationCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
type GlobalObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
+type DeserializeActionCallback = fn(json: &str) -> anyhow::Result<Box<dyn Action>>;
pub struct MutableAppContext {
weak_self: Option<rc::Weak<RefCell<Self>>>,
foreground_platform: Rc<dyn platform::ForegroundPlatform>,
assets: Arc<AssetCache>,
cx: AppContext,
+ action_deserializers: HashMap<&'static str, DeserializeActionCallback>,
capture_actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
global_actions: HashMap<TypeId, Box<GlobalActionCallback>>,
@@ -773,6 +775,7 @@ impl MutableAppContext {
font_cache,
platform,
},
+ action_deserializers: HashMap::new(),
capture_actions: HashMap::new(),
actions: HashMap::new(),
global_actions: HashMap::new(),
@@ -857,6 +860,19 @@ impl MutableAppContext {
.and_then(|(presenter, _)| presenter.borrow().debug_elements(self))
}
+ pub fn deserialize_action(
+ &self,
+ name: &str,
+ argument: Option<&str>,
+ ) -> Result<Box<dyn Action>> {
+ let callback = self
+ .action_deserializers
+ .get(name)
+ .ok_or_else(|| anyhow!("unknown action {}", name))?;
+ callback(argument.unwrap_or("{}"))
+ .with_context(|| format!("invalid data for action {}", name))
+ }
+
pub fn add_action<A, V, F>(&mut self, handler: F)
where
A: Action,
@@ -899,6 +915,10 @@ impl MutableAppContext {
},
);
+ self.action_deserializers
+ .entry(A::qualified_name())
+ .or_insert(A::from_json_str);
+
let actions = if capture {
&mut self.capture_actions
} else {
@@ -934,6 +954,10 @@ impl MutableAppContext {
handler(action, cx);
});
+ self.action_deserializers
+ .entry(A::qualified_name())
+ .or_insert(A::from_json_str);
+
if self
.global_actions
.insert(TypeId::of::<A>(), handler)
@@ -1334,6 +1358,10 @@ impl MutableAppContext {
self.keystroke_matcher.add_bindings(bindings);
}
+ pub fn clear_bindings(&mut self) {
+ self.keystroke_matcher.clear_bindings();
+ }
+
pub fn dispatch_keystroke(
&mut self,
window_id: usize,
@@ -4575,7 +4603,8 @@ impl RefCounts {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{elements::*, impl_actions};
+ use crate::{actions, elements::*, impl_actions};
+ use serde::Deserialize;
use smol::future::poll_once;
use std::{
cell::Cell,
@@ -5683,6 +5712,42 @@ mod tests {
);
}
+ #[crate::test(self)]
+ fn test_deserialize_actions(cx: &mut MutableAppContext) {
+ #[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
+ pub struct ComplexAction {
+ arg: String,
+ count: usize,
+ }
+
+ actions!(test::something, [SimpleAction]);
+ impl_actions!(test::something, [ComplexAction]);
+
+ cx.add_global_action(move |_: &SimpleAction, _: &mut MutableAppContext| {});
+ cx.add_global_action(move |_: &ComplexAction, _: &mut MutableAppContext| {});
+
+ let action1 = cx
+ .deserialize_action(
+ "test::something::ComplexAction",
+ Some(r#"{"arg": "a", "count": 5}"#),
+ )
+ .unwrap();
+ let action2 = cx
+ .deserialize_action("test::something::SimpleAction", None)
+ .unwrap();
+ assert_eq!(
+ action1.as_any().downcast_ref::<ComplexAction>().unwrap(),
+ &ComplexAction {
+ arg: "a".to_string(),
+ count: 5,
+ }
+ );
+ assert_eq!(
+ action2.as_any().downcast_ref::<SimpleAction>().unwrap(),
+ &SimpleAction
+ );
+ }
+
#[crate::test(self)]
fn test_dispatch_action(cx: &mut MutableAppContext) {
struct ViewA {
@@ -5721,32 +5786,32 @@ mod tests {
}
}
- #[derive(Clone)]
- pub struct Action(pub &'static str);
+ #[derive(Clone, Deserialize)]
+ pub struct Action(pub String);
impl_actions!(test, [Action]);
let actions = Rc::new(RefCell::new(Vec::new()));
- {
+ cx.add_global_action({
let actions = actions.clone();
- cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| {
+ move |_: &Action, _: &mut MutableAppContext| {
actions.borrow_mut().push("global".to_string());
- });
- }
+ }
+ });
- {
+ cx.add_action({
let actions = actions.clone();
- cx.add_action(move |view: &mut ViewA, action: &Action, cx| {
+ move |view: &mut ViewA, action: &Action, cx| {
assert_eq!(action.0, "bar");
cx.propagate_action();
actions.borrow_mut().push(format!("{} a", view.id));
- });
- }
+ }
+ });
- {
+ cx.add_action({
let actions = actions.clone();
- cx.add_action(move |view: &mut ViewA, _: &Action, cx| {
+ move |view: &mut ViewA, _: &Action, cx| {
if view.id != 1 {
cx.add_view(|cx| {
cx.propagate_action(); // Still works on a nested ViewContext
@@ -5754,32 +5819,32 @@ mod tests {
});
}
actions.borrow_mut().push(format!("{} b", view.id));
- });
- }
+ }
+ });
- {
+ cx.add_action({
let actions = actions.clone();
- cx.add_action(move |view: &mut ViewB, _: &Action, cx| {
+ move |view: &mut ViewB, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} c", view.id));
- });
- }
+ }
+ });
- {
+ cx.add_action({
let actions = actions.clone();
- cx.add_action(move |view: &mut ViewB, _: &Action, cx| {
+ move |view: &mut ViewB, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} d", view.id));
- });
- }
+ }
+ });
- {
+ cx.capture_action({
let actions = actions.clone();
- cx.capture_action(move |view: &mut ViewA, _: &Action, cx| {
+ move |view: &mut ViewA, _: &Action, cx| {
cx.propagate_action();
actions.borrow_mut().push(format!("{} capture", view.id));
- });
- }
+ }
+ });
let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 });
let view_2 = cx.add_view(window_id, |_| ViewB { id: 2 });
@@ -5789,7 +5854,7 @@ mod tests {
cx.dispatch_action(
window_id,
vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()],
- &Action("bar"),
+ &Action("bar".to_string()),
);
assert_eq!(
@@ -5812,7 +5877,7 @@ mod tests {
cx.dispatch_action(
window_id,
vec![view_2.id(), view_3.id(), view_4.id()],
- &Action("bar"),
+ &Action("bar".to_string()),
);
assert_eq!(
@@ -5832,8 +5897,8 @@ mod tests {
#[crate::test(self)]
fn test_dispatch_keystroke(cx: &mut MutableAppContext) {
- #[derive(Clone)]
- pub struct Action(pub &'static str);
+ #[derive(Clone, Deserialize)]
+ pub struct Action(String);
impl_actions!(test, [Action]);
@@ -5887,16 +5952,20 @@ mod tests {
// "a" and "b" in its context, but not "c".
cx.add_bindings(vec![keymap::Binding::new(
"a",
- Action("a"),
+ Action("a".to_string()),
Some("a && b && !c"),
)]);
- cx.add_bindings(vec![keymap::Binding::new("b", Action("b"), None)]);
+ cx.add_bindings(vec![keymap::Binding::new(
+ "b",
+ Action("b".to_string()),
+ None,
+ )]);
let actions = Rc::new(RefCell::new(Vec::new()));
- {
+ cx.add_action({
let actions = actions.clone();
- cx.add_action(move |view: &mut View, action: &Action, cx| {
+ move |view: &mut View, action: &Action, cx| {
if action.0 == "a" {
actions.borrow_mut().push(format!("{} a", view.id));
} else {
@@ -5905,14 +5974,15 @@ mod tests {
.push(format!("{} {}", view.id, action.0));
cx.propagate_action();
}
- });
- }
- {
+ }
+ });
+
+ cx.add_global_action({
let actions = actions.clone();
- cx.add_global_action(move |action: &Action, _| {
+ move |action: &Action, _| {
actions.borrow_mut().push(format!("global {}", action.0));
- });
- }
+ }
+ });
cx.dispatch_keystroke(
window_id,
@@ -2,55 +2,108 @@ use std::any::{Any, TypeId};
pub trait Action: 'static {
fn id(&self) -> TypeId;
- fn namespace(&self) -> &'static str;
fn name(&self) -> &'static str;
fn as_any(&self) -> &dyn Any;
fn boxed_clone(&self) -> Box<dyn Action>;
- fn boxed_clone_as_any(&self) -> Box<dyn Any>;
+
+ fn qualified_name() -> &'static str
+ where
+ Self: Sized;
+ fn from_json_str(json: &str) -> anyhow::Result<Box<dyn Action>>
+ where
+ Self: Sized;
}
+/// Define a set of unit struct types that all implement the `Action` trait.
+///
+/// The first argument is a namespace that will be associated with each of
+/// the given action types, to ensure that they have globally unique
+/// qualified names for use in keymap files.
#[macro_export]
-macro_rules! impl_actions {
+macro_rules! actions {
($namespace:path, [ $($name:ident),* $(,)? ]) => {
$(
- impl $crate::action::Action for $name {
- fn id(&self) -> std::any::TypeId {
- std::any::TypeId::of::<$name>()
- }
-
- fn namespace(&self) -> &'static str {
- stringify!($namespace)
- }
-
- fn name(&self) -> &'static str {
- stringify!($name)
- }
-
- fn as_any(&self) -> &dyn std::any::Any {
- self
- }
-
- fn boxed_clone(&self) -> Box<dyn $crate::action::Action> {
- Box::new(self.clone())
+ #[derive(Clone, Debug, Default, PartialEq, Eq)]
+ pub struct $name;
+ $crate::__impl_action! {
+ $namespace,
+ $name,
+ fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
+ Ok(Box::new(Self))
}
+ }
+ )*
+ };
+}
- fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
- Box::new(self.clone())
+/// Implement the `Action` trait for a set of existing types.
+///
+/// The first argument is a namespace that will be associated with each of
+/// the given action types, to ensure that they have globally unique
+/// qualified names for use in keymap files.
+#[macro_export]
+macro_rules! impl_actions {
+ ($namespace:path, [ $($name:ident),* $(,)? ]) => {
+ $(
+ $crate::__impl_action! {
+ $namespace,
+ $name,
+ fn from_json_str(json: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
+ Ok(Box::new($crate::serde_json::from_str::<Self>(json)?))
}
}
)*
};
}
+/// Implement the `Action` trait for a set of existing types that are
+/// not intended to be constructed via a keymap file, but only dispatched
+/// internally.
#[macro_export]
-macro_rules! actions {
+macro_rules! impl_internal_actions {
($namespace:path, [ $($name:ident),* $(,)? ]) => {
-
$(
- #[derive(Clone, Debug, Default, PartialEq, Eq)]
- pub struct $name;
+ $crate::__impl_action! {
+ $namespace,
+ $name,
+ fn from_json_str(_: &str) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
+ Err($crate::anyhow::anyhow!("internal action"))
+ }
+ }
)*
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __impl_action {
+ ($namespace:path, $name:ident, $from_json_fn:item) => {
+ impl $crate::action::Action for $name {
+ fn name(&self) -> &'static str {
+ stringify!($name)
+ }
+
+ fn qualified_name() -> &'static str {
+ concat!(
+ stringify!($namespace),
+ "::",
+ stringify!($name),
+ )
+ }
+
+ fn id(&self) -> std::any::TypeId {
+ std::any::TypeId::of::<$name>()
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
+ fn boxed_clone(&self) -> Box<dyn $crate::Action> {
+ Box::new(self.clone())
+ }
- $crate::impl_actions!($namespace, [ $($name),* ]);
+ $from_json_fn
+ }
};
}
@@ -33,3 +33,6 @@ pub use platform::{Event, NavigationDirection, PathPromptOptions, Platform, Prom
pub use presenter::{
Axis, DebugContext, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
};
+
+pub use anyhow;
+pub use serde_json;
@@ -1,5 +1,5 @@
use crate::Action;
-use anyhow::anyhow;
+use anyhow::{anyhow, Result};
use std::{
any::Any,
collections::{HashMap, HashSet},
@@ -106,6 +106,11 @@ impl Matcher {
self.keymap.add_bindings(bindings);
}
+ pub fn clear_bindings(&mut self) {
+ self.pending.clear();
+ self.keymap.clear();
+ }
+
pub fn clear_pending(&mut self) {
self.pending.clear();
}
@@ -164,24 +169,34 @@ impl Keymap {
fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
self.0.extend(bindings.into_iter());
}
+
+ fn clear(&mut self) {
+ self.0.clear();
+ }
}
impl Binding {
pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
+ Self::load(keystrokes, Box::new(action), context).unwrap()
+ }
+
+ pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
let context = if let Some(context) = context {
- Some(ContextPredicate::parse(context).unwrap())
+ Some(ContextPredicate::parse(context)?)
} else {
None
};
- Self {
- keystrokes: keystrokes
- .split_whitespace()
- .map(|key| Keystroke::parse(key).unwrap())
- .collect(),
- action: Box::new(action),
+ let keystrokes = keystrokes
+ .split_whitespace()
+ .map(|key| Keystroke::parse(key))
+ .collect::<Result<_>>()?;
+
+ Ok(Self {
+ keystrokes,
+ action,
context,
- }
+ })
}
}
@@ -328,6 +343,8 @@ impl ContextPredicate {
#[cfg(test)]
mod tests {
+ use serde::Deserialize;
+
use crate::{actions, impl_actions};
use super::*;
@@ -419,30 +436,18 @@ mod tests {
#[test]
fn test_matcher() -> anyhow::Result<()> {
- #[derive(Clone)]
- pub struct A(pub &'static str);
+ #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
+ pub struct A(pub String);
impl_actions!(test, [A]);
actions!(test, [B, Ab]);
- impl PartialEq for A {
- fn eq(&self, other: &Self) -> bool {
- self.0 == other.0
- }
- }
- impl Eq for A {}
- impl Debug for A {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "A({:?})", &self.0)
- }
- }
-
#[derive(Clone, Debug, Eq, PartialEq)]
struct ActionArg {
a: &'static str,
}
let keymap = Keymap(vec![
- Binding::new("a", A("x"), Some("a")),
+ Binding::new("a", A("x".to_string()), Some("a")),
Binding::new("b", B, Some("a")),
Binding::new("a b", Ab, Some("a || b")),
]);
@@ -456,40 +461,54 @@ mod tests {
let mut matcher = Matcher::new(keymap);
// Basic match
- assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
+ assert_eq!(
+ downcast(&matcher.test_keystroke("a", 1, &ctx_a)),
+ Some(&A("x".to_string()))
+ );
// Multi-keystroke match
- assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
- assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
+ assert!(matcher.test_keystroke("a", 1, &ctx_b).is_none());
+ assert_eq!(downcast(&matcher.test_keystroke("b", 1, &ctx_b)), Some(&Ab));
// Failed matches don't interfere with matching subsequent keys
- assert_eq!(matcher.test_keystroke::<A>("x", 1, &ctx_a), None);
- assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
+ assert!(matcher.test_keystroke("x", 1, &ctx_a).is_none());
+ assert_eq!(
+ downcast(&matcher.test_keystroke("a", 1, &ctx_a)),
+ Some(&A("x".to_string()))
+ );
// Pending keystrokes are cleared when the context changes
- assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
- assert_eq!(matcher.test_keystroke("b", 1, &ctx_a), Some(B));
+ assert!(&matcher.test_keystroke("a", 1, &ctx_b).is_none());
+ assert_eq!(downcast(&matcher.test_keystroke("b", 1, &ctx_a)), Some(&B));
let mut ctx_c = Context::default();
ctx_c.set.insert("c".into());
// Pending keystrokes are maintained per-view
- assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
- assert_eq!(matcher.test_keystroke::<A>("a", 2, &ctx_c), None);
- assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
+ assert!(matcher.test_keystroke("a", 1, &ctx_b).is_none());
+ assert!(matcher.test_keystroke("a", 2, &ctx_c).is_none());
+ assert_eq!(downcast(&matcher.test_keystroke("b", 1, &ctx_b)), Some(&Ab));
Ok(())
}
+ fn downcast<'a, A: Action>(action: &'a Option<Box<dyn Action>>) -> Option<&'a A> {
+ action
+ .as_ref()
+ .and_then(|action| action.as_any().downcast_ref())
+ }
+
impl Matcher {
- fn test_keystroke<A>(&mut self, keystroke: &str, view_id: usize, cx: &Context) -> Option<A>
- where
- A: Action + Debug + Eq,
- {
+ fn test_keystroke(
+ &mut self,
+ keystroke: &str,
+ view_id: usize,
+ cx: &Context,
+ ) -> Option<Box<dyn Action>> {
if let MatchResult::Action(action) =
self.push_keystroke(Keystroke::parse(keystroke).unwrap(), view_id, cx)
{
- Some(*action.boxed_clone_as_any().downcast().unwrap())
+ Some(action.boxed_clone())
} else {
None
}
@@ -1,3 +1,5 @@
+use serde::Deserialize;
+
use crate::{
actions, elements::*, impl_actions, AppContext, Entity, MutableAppContext, RenderContext, View,
ViewContext, WeakViewHandle,
@@ -25,7 +27,7 @@ pub enum ItemType {
Unselected,
}
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct SelectItem(pub usize);
actions!(select, [ToggleSelect]);
@@ -1,6 +1,6 @@
use chrono::{Datelike, Local, Timelike};
use editor::{Autoscroll, Editor};
-use gpui::{actions, keymap::Binding, MutableAppContext};
+use gpui::{actions, MutableAppContext};
use std::{fs::OpenOptions, sync::Arc};
use util::TryFutureExt as _;
use workspace::AppState;
@@ -8,7 +8,6 @@ use workspace::AppState;
actions!(journal, [NewJournalEntry]);
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
- cx.add_bindings(vec![Binding::new("ctrl-alt-cmd-j", NewJournalEntry, None)]);
cx.add_global_action(move |_: &NewJournalEntry, cx| new_journal_entry(app_state.clone(), cx));
}
@@ -4,12 +4,8 @@ use editor::{
};
use fuzzy::StringMatch;
use gpui::{
- actions,
- elements::*,
- geometry::vector::Vector2F,
- keymap::{self, Binding},
- AppContext, Axis, Entity, MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
- WeakViewHandle,
+ actions, elements::*, geometry::vector::Vector2F, keymap, AppContext, Axis, Entity,
+ MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle,
};
use language::Outline;
use ordered_float::OrderedFloat;
@@ -23,10 +19,6 @@ use workspace::{
actions!(outline, [Toggle]);
pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings([
- Binding::new("cmd-shift-O", Toggle, Some("Editor")),
- Binding::new("escape", Toggle, Some("OutlineView")),
- ]);
cx.add_action(OutlineView::toggle);
cx.add_action(OutlineView::confirm);
cx.add_action(OutlineView::select_prev);
@@ -4,8 +4,7 @@ use gpui::{
Align, ConstrainedBox, Empty, Flex, Label, MouseEventHandler, ParentElement, ScrollTarget,
Svg, UniformList, UniformListState,
},
- impl_actions,
- keymap::{self, Binding},
+ impl_internal_actions, keymap,
platform::CursorStyle,
AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, View, ViewContext,
ViewHandle, WeakViewHandle,
@@ -54,7 +53,7 @@ pub struct ToggleExpanded(pub ProjectEntryId);
pub struct Open(pub ProjectEntryId);
actions!(project_panel, [ExpandSelectedEntry, CollapseSelectedEntry]);
-impl_actions!(project_panel, [Open, ToggleExpanded]);
+impl_internal_actions!(project_panel, [Open, ToggleExpanded]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ProjectPanel::expand_selected_entry);
@@ -63,10 +62,6 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ProjectPanel::select_prev);
cx.add_action(ProjectPanel::select_next);
cx.add_action(ProjectPanel::open_entry);
- cx.add_bindings([
- Binding::new("right", ExpandSelectedEntry, Some("ProjectPanel")),
- Binding::new("left", CollapseSelectedEntry, Some("ProjectPanel")),
- ]);
}
pub enum Event {
@@ -3,11 +3,8 @@ use editor::{
};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
- actions,
- elements::*,
- keymap::{self, Binding},
- AppContext, Axis, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View,
- ViewContext, ViewHandle, WeakViewHandle,
+ actions, elements::*, keymap, AppContext, Axis, Entity, ModelHandle, MutableAppContext,
+ RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use ordered_float::OrderedFloat;
use project::{Project, Symbol};
@@ -25,10 +22,6 @@ use workspace::{
actions!(project_symbols, [Toggle]);
pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings([
- Binding::new("cmd-t", Toggle, None),
- Binding::new("escape", Toggle, Some("ProjectSymbolsView")),
- ]);
cx.add_action(ProjectSymbolsView::toggle);
cx.add_action(ProjectSymbolsView::confirm);
cx.add_action(ProjectSymbolsView::select_prev);
@@ -20,6 +20,7 @@ workspace = { path = "../workspace" }
anyhow = "1.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
postage = { version = "0.4.1", features = ["futures-traits"] }
+serde = { version = "1", features = ["derive"] }
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
@@ -1,55 +1,46 @@
-use crate::{active_match_index, match_index_for_direction, Direction, SearchOption, SelectMatch};
+use crate::{
+ active_match_index, match_index_for_direction, Direction, SearchOption, SelectNextMatch,
+ SelectPrevMatch,
+};
use collections::HashMap;
use editor::{display_map::ToDisplayPoint, Anchor, Autoscroll, Bias, Editor};
use gpui::{
- actions, elements::*, impl_actions, keymap::Binding, platform::CursorStyle, AppContext, Entity,
- MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
+ actions, elements::*, impl_actions, impl_internal_actions, platform::CursorStyle, AppContext,
+ Entity, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
WeakViewHandle,
};
use language::OffsetRangeExt;
use project::search::SearchQuery;
+use serde::Deserialize;
use settings::Settings;
use std::ops::Range;
use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView};
-#[derive(Clone)]
-pub struct Deploy(pub bool);
+#[derive(Clone, Deserialize)]
+pub struct Deploy {
+ pub focus: bool,
+}
#[derive(Clone)]
pub struct ToggleSearchOption(pub SearchOption);
actions!(buffer_search, [Dismiss, FocusEditor]);
-impl_actions!(buffer_search, [Deploy, ToggleSearchOption]);
+impl_actions!(buffer_search, [Deploy]);
+impl_internal_actions!(buffer_search, [ToggleSearchOption]);
pub enum Event {
UpdateLocation,
}
pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings([
- Binding::new("cmd-f", Deploy(true), Some("Editor && mode == full")),
- Binding::new("cmd-e", Deploy(false), Some("Editor && mode == full")),
- Binding::new("escape", Dismiss, Some("BufferSearchBar")),
- Binding::new("cmd-f", FocusEditor, Some("BufferSearchBar")),
- Binding::new(
- "enter",
- SelectMatch(Direction::Next),
- Some("BufferSearchBar"),
- ),
- Binding::new(
- "shift-enter",
- SelectMatch(Direction::Prev),
- Some("BufferSearchBar"),
- ),
- Binding::new("cmd-g", SelectMatch(Direction::Next), Some("Pane")),
- Binding::new("cmd-shift-G", SelectMatch(Direction::Prev), Some("Pane")),
- ]);
cx.add_action(BufferSearchBar::deploy);
cx.add_action(BufferSearchBar::dismiss);
cx.add_action(BufferSearchBar::focus_editor);
cx.add_action(BufferSearchBar::toggle_search_option);
- cx.add_action(BufferSearchBar::select_match);
- cx.add_action(BufferSearchBar::select_match_on_pane);
+ cx.add_action(BufferSearchBar::select_next_match);
+ cx.add_action(BufferSearchBar::select_prev_match);
+ cx.add_action(BufferSearchBar::select_next_match_on_pane);
+ cx.add_action(BufferSearchBar::select_prev_match_on_pane);
cx.add_action(BufferSearchBar::handle_editor_cancel);
}
@@ -326,14 +317,17 @@ impl BufferSearchBar {
.with_style(style.container)
.boxed()
})
- .on_click(move |cx| cx.dispatch_action(SelectMatch(direction)))
+ .on_click(move |cx| match direction {
+ Direction::Prev => cx.dispatch_action(SelectPrevMatch),
+ Direction::Next => cx.dispatch_action(SelectNextMatch),
+ })
.with_cursor_style(CursorStyle::PointingHand)
.boxed()
}
- fn deploy(pane: &mut Pane, Deploy(focus): &Deploy, cx: &mut ViewContext<Pane>) {
+ fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext<Pane>) {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
- if search_bar.update(cx, |search_bar, cx| search_bar.show(*focus, cx)) {
+ if search_bar.update(cx, |search_bar, cx| search_bar.show(action.focus, cx)) {
return;
}
}
@@ -379,7 +373,15 @@ impl BufferSearchBar {
cx.notify();
}
- fn select_match(&mut self, &SelectMatch(direction): &SelectMatch, cx: &mut ViewContext<Self>) {
+ fn select_next_match(&mut self, _: &SelectNextMatch, cx: &mut ViewContext<Self>) {
+ self.select_match(Direction::Next, cx);
+ }
+
+ fn select_prev_match(&mut self, _: &SelectPrevMatch, cx: &mut ViewContext<Self>) {
+ self.select_match(Direction::Prev, cx);
+ }
+
+ fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
if let Some(index) = self.active_match_index {
if let Some(editor) = self.active_editor.as_ref() {
editor.update(cx, |editor, cx| {
@@ -400,9 +402,23 @@ impl BufferSearchBar {
}
}
- fn select_match_on_pane(pane: &mut Pane, action: &SelectMatch, cx: &mut ViewContext<Pane>) {
+ fn select_next_match_on_pane(
+ pane: &mut Pane,
+ action: &SelectNextMatch,
+ cx: &mut ViewContext<Pane>,
+ ) {
+ if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
+ search_bar.update(cx, |bar, cx| bar.select_next_match(action, cx));
+ }
+ }
+
+ fn select_prev_match_on_pane(
+ pane: &mut Pane,
+ action: &SelectPrevMatch,
+ cx: &mut ViewContext<Pane>,
+ ) {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
- search_bar.update(cx, |search_bar, cx| search_bar.select_match(action, cx));
+ search_bar.update(cx, |bar, cx| bar.select_prev_match(action, cx));
}
}
@@ -710,7 +726,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.active_match_index, Some(0));
- search_bar.select_match(&SelectMatch(Direction::Next), cx);
+ search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
@@ -721,7 +737,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
- search_bar.select_match(&SelectMatch(Direction::Next), cx);
+ search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
@@ -732,7 +748,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
- search_bar.select_match(&SelectMatch(Direction::Next), cx);
+ search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
@@ -743,7 +759,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
- search_bar.select_match(&SelectMatch(Direction::Next), cx);
+ search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
@@ -754,7 +770,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
- search_bar.select_match(&SelectMatch(Direction::Prev), cx);
+ search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
@@ -765,7 +781,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
- search_bar.select_match(&SelectMatch(Direction::Prev), cx);
+ search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
@@ -776,7 +792,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
- search_bar.select_match(&SelectMatch(Direction::Prev), cx);
+ search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
@@ -793,7 +809,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.active_match_index, Some(1));
- search_bar.select_match(&SelectMatch(Direction::Prev), cx);
+ search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
@@ -810,7 +826,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.active_match_index, Some(1));
- search_bar.select_match(&SelectMatch(Direction::Next), cx);
+ search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
@@ -827,7 +843,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.active_match_index, Some(2));
- search_bar.select_match(&SelectMatch(Direction::Prev), cx);
+ search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
@@ -844,7 +860,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.active_match_index, Some(2));
- search_bar.select_match(&SelectMatch(Direction::Next), cx);
+ search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
@@ -861,7 +877,7 @@ mod tests {
});
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.active_match_index, Some(0));
- search_bar.select_match(&SelectMatch(Direction::Prev), cx);
+ search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selected_display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
@@ -1,13 +1,13 @@
use crate::{
- active_match_index, match_index_for_direction, Direction, SearchOption, SelectMatch,
- ToggleSearchOption,
+ active_match_index, match_index_for_direction, Direction, SearchOption, SelectNextMatch,
+ SelectPrevMatch, ToggleSearchOption,
};
use collections::HashMap;
use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll};
use gpui::{
- actions, elements::*, keymap::Binding, platform::CursorStyle, AppContext, ElementBox, Entity,
- ModelContext, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View,
- ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
+ actions, elements::*, platform::CursorStyle, AppContext, ElementBox, Entity, ModelContext,
+ ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext,
+ ViewHandle, WeakModelHandle, WeakViewHandle,
};
use project::{search::SearchQuery, Project};
use settings::Settings;
@@ -28,20 +28,12 @@ struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakViewHandle<ProjectSe
pub fn init(cx: &mut MutableAppContext) {
cx.set_global(ActiveSearches::default());
- cx.add_bindings([
- Binding::new("cmd-shift-F", ToggleFocus, Some("Pane")),
- Binding::new("cmd-f", ToggleFocus, Some("Pane")),
- Binding::new("cmd-shift-F", Deploy, Some("Workspace")),
- Binding::new("enter", Search, Some("ProjectSearchBar")),
- Binding::new("cmd-enter", SearchInNew, Some("ProjectSearchBar")),
- Binding::new("cmd-g", SelectMatch(Direction::Next), Some("Pane")),
- Binding::new("cmd-shift-G", SelectMatch(Direction::Prev), Some("Pane")),
- ]);
cx.add_action(ProjectSearchView::deploy);
cx.add_action(ProjectSearchBar::search);
cx.add_action(ProjectSearchBar::search_in_new);
cx.add_action(ProjectSearchBar::toggle_search_option);
- cx.add_action(ProjectSearchBar::select_match);
+ cx.add_action(ProjectSearchBar::select_next_match);
+ cx.add_action(ProjectSearchBar::select_prev_match);
cx.add_action(ProjectSearchBar::toggle_focus);
cx.capture_action(ProjectSearchBar::tab);
}
@@ -136,6 +128,7 @@ impl ProjectSearch {
pub enum ViewEvent {
UpdateTab,
+ EditorEvent(editor::Event),
}
impl Entity for ProjectSearchView {
@@ -304,6 +297,14 @@ impl Item for ProjectSearchView {
.update(cx, |editor, cx| editor.navigate(data, cx))
}
+ fn should_activate_item_on_event(event: &Self::Event) -> bool {
+ if let ViewEvent::EditorEvent(editor_event) = event {
+ Editor::should_activate_item_on_event(editor_event)
+ } else {
+ false
+ }
+ }
+
fn should_update_tab_on_event(event: &ViewEvent) -> bool {
matches!(event, ViewEvent::UpdateTab)
}
@@ -338,6 +339,11 @@ impl ProjectSearchView {
editor.set_text(query_text, cx);
editor
});
+ // Subcribe to query_editor in order to reraise editor events for workspace item activation purposes
+ cx.subscribe(&query_editor, |_, _, event, cx| {
+ cx.emit(ViewEvent::EditorEvent(event.clone()))
+ })
+ .detach();
let results_editor = cx.add_view(|cx| {
let mut editor = Editor::for_multibuffer(excerpts, Some(project), cx);
@@ -350,6 +356,8 @@ impl ProjectSearchView {
if matches!(event, editor::Event::SelectionsChanged { .. }) {
this.update_match_index(cx);
}
+ // Reraise editor events for workspace item activation purposes
+ cx.emit(ViewEvent::EditorEvent(event.clone()));
})
.detach();
@@ -545,18 +553,23 @@ impl ProjectSearchBar {
}
}
- fn select_match(
- pane: &mut Pane,
- &SelectMatch(direction): &SelectMatch,
- cx: &mut ViewContext<Pane>,
- ) {
+ fn select_next_match(pane: &mut Pane, _: &SelectNextMatch, cx: &mut ViewContext<Pane>) {
if let Some(search_view) = pane
.active_item()
.and_then(|item| item.downcast::<ProjectSearchView>())
{
- search_view.update(cx, |search_view, cx| {
- search_view.select_match(direction, cx);
- });
+ search_view.update(cx, |view, cx| view.select_match(Direction::Next, cx));
+ } else {
+ cx.propagate_action();
+ }
+ }
+
+ fn select_prev_match(pane: &mut Pane, _: &SelectPrevMatch, cx: &mut ViewContext<Pane>) {
+ if let Some(search_view) = pane
+ .active_item()
+ .and_then(|item| item.downcast::<ProjectSearchView>())
+ {
+ search_view.update(cx, |view, cx| view.select_match(Direction::Prev, cx));
} else {
cx.propagate_action();
}
@@ -635,7 +648,10 @@ impl ProjectSearchBar {
.with_style(style.container)
.boxed()
})
- .on_click(move |cx| cx.dispatch_action(SelectMatch(direction)))
+ .on_click(move |cx| match direction {
+ Direction::Prev => cx.dispatch_action(SelectPrevMatch),
+ Direction::Next => cx.dispatch_action(SelectNextMatch),
+ })
.with_cursor_style(CursorStyle::PointingHand)
.boxed()
}
@@ -1,6 +1,6 @@
pub use buffer_search::BufferSearchBar;
use editor::{Anchor, MultiBufferSnapshot};
-use gpui::{impl_actions, MutableAppContext};
+use gpui::{actions, impl_internal_actions, MutableAppContext};
pub use project_search::{ProjectSearchBar, ProjectSearchView};
use std::{
cmp::{self, Ordering},
@@ -18,10 +18,8 @@ pub fn init(cx: &mut MutableAppContext) {
#[derive(Clone)]
pub struct ToggleSearchOption(pub SearchOption);
-#[derive(Clone)]
-pub struct SelectMatch(pub Direction);
-
-impl_actions!(search, [ToggleSearchOption, SelectMatch]);
+actions!(search, [SelectNextMatch, SelectPrevMatch]);
+impl_internal_actions!(search, [ToggleSearchOption]);
#[derive(Clone, Copy)]
pub enum SearchOption {
@@ -66,6 +66,7 @@ language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
+theme = { path = "../theme" }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"
env_logger = "0.8"
@@ -1117,6 +1117,7 @@ mod tests {
},
time::Duration,
};
+ use theme::ThemeRegistry;
use util::TryFutureExt;
use workspace::{Item, SplitDirection, ToggleFollow, Workspace, WorkspaceParams};
@@ -2418,7 +2419,7 @@ mod tests {
.condition(&cx_b, |editor, _| editor.context_menu_visible())
.await;
editor_b.update(cx_b, |editor, cx| {
- editor.confirm_completion(&ConfirmCompletion(Some(0)), cx);
+ editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
});
@@ -3607,7 +3608,12 @@ mod tests {
// Toggle code actions and wait for them to display.
editor_b.update(cx_b, |editor, cx| {
- editor.toggle_code_actions(&ToggleCodeActions(false), cx);
+ editor.toggle_code_actions(
+ &ToggleCodeActions {
+ deployed_from_indicator: false,
+ },
+ cx,
+ );
});
editor_b
.condition(&cx_b, |editor, _| editor.context_menu_visible())
@@ -3618,7 +3624,7 @@ mod tests {
// Confirming the code action will trigger a resolve request.
let confirm_action = workspace_b
.update(cx_b, |workspace, cx| {
- Editor::confirm_code_action(workspace, &ConfirmCodeAction(Some(0)), cx)
+ Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
})
.unwrap();
fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
@@ -5633,6 +5639,7 @@ mod tests {
project: project.clone(),
user_store: self.user_store.clone(),
languages: self.language_registry.clone(),
+ themes: ThemeRegistry::new((), cx.font_cache().clone()),
channel_list: cx.add_model(|cx| {
ChannelList::new(self.user_store.clone(), self.client.clone(), cx)
}),
@@ -11,6 +11,8 @@ doctest = false
test-support = []
[dependencies]
+assets = { path = "../assets" }
+collections = { path = "../collections" }
gpui = { path = "../gpui" }
theme = { path = "../theme" }
util = { path = "../util" }
@@ -0,0 +1,62 @@
+use anyhow::{Context, Result};
+use assets::Assets;
+use collections::BTreeMap;
+use gpui::{keymap::Binding, MutableAppContext};
+use serde::Deserialize;
+use serde_json::value::RawValue;
+
+#[derive(Deserialize, Default, Clone)]
+#[serde(transparent)]
+pub struct KeymapFile(BTreeMap<String, ActionsByKeystroke>);
+
+type ActionsByKeystroke = BTreeMap<String, Box<RawValue>>;
+
+#[derive(Deserialize)]
+struct ActionWithData<'a>(#[serde(borrow)] &'a str, #[serde(borrow)] &'a RawValue);
+
+impl KeymapFile {
+ pub fn load_defaults(cx: &mut MutableAppContext) {
+ for path in ["keymaps/default.json", "keymaps/vim.json"] {
+ Self::load(path, cx).unwrap();
+ }
+ }
+
+ pub fn load(asset_path: &str, cx: &mut MutableAppContext) -> Result<()> {
+ let content = Assets::get(asset_path).unwrap().data;
+ let content_str = std::str::from_utf8(content.as_ref()).unwrap();
+ Ok(serde_json::from_str::<Self>(content_str)?.add(cx)?)
+ }
+
+ pub fn add(self, cx: &mut MutableAppContext) -> Result<()> {
+ for (context, actions) in self.0 {
+ let context = if context == "*" { None } else { Some(context) };
+ cx.add_bindings(
+ actions
+ .into_iter()
+ .map(|(keystroke, action)| {
+ let action = action.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
+ // deserialize the action itself dynamically directly from the JSON
+ // string. But `RawValue` currently does not work inside of an untagged enum.
+ let action = if action.starts_with('[') {
+ let ActionWithData(name, data) = serde_json::from_str(action)?;
+ cx.deserialize_action(name, Some(data.get()))
+ } else {
+ let name = serde_json::from_str(action)?;
+ cx.deserialize_action(name, None)
+ }
+ .with_context(|| {
+ format!(
+ "invalid binding value for keystroke {keystroke}, context {context:?}"
+ )
+ })?;
+ Binding::load(&keystroke, action, context.as_deref())
+ })
+ .collect::<Result<Vec<_>>>()?,
+ )
+ }
+ Ok(())
+ }
+}
@@ -1,3 +1,5 @@
+mod keymap_file;
+
use anyhow::Result;
use gpui::font_cache::{FamilyId, FontCache};
use schemars::{
@@ -13,6 +15,8 @@ use std::{collections::HashMap, sync::Arc};
use theme::{Theme, ThemeRegistry};
use util::ResultExt as _;
+pub use keymap_file::KeymapFile;
+
#[derive(Clone)]
pub struct Settings {
pub buffer_font_family: FamilyId,
@@ -1,11 +1,8 @@
use editor::Editor;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{
- elements::*,
- impl_actions,
- keymap::{self, Binding},
- AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, View,
- ViewContext, ViewHandle,
+ actions, elements::*, keymap, AppContext, Axis, Element, ElementBox, Entity, MutableAppContext,
+ RenderContext, View, ViewContext, ViewHandle,
};
use settings::Settings;
use std::{cmp, sync::Arc};
@@ -25,26 +22,14 @@ pub struct ThemeSelector {
selection_completed: bool,
}
-#[derive(Clone)]
-pub struct Toggle(pub Arc<ThemeRegistry>);
+actions!(theme_selector, [Toggle, Reload]);
-#[derive(Clone)]
-pub struct Reload(pub Arc<ThemeRegistry>);
-
-impl_actions!(theme_selector, [Toggle, Reload]);
-
-pub fn init(themes: Arc<ThemeRegistry>, cx: &mut MutableAppContext) {
+pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ThemeSelector::confirm);
cx.add_action(ThemeSelector::select_prev);
cx.add_action(ThemeSelector::select_next);
cx.add_action(ThemeSelector::toggle);
cx.add_action(ThemeSelector::reload);
-
- cx.add_bindings(vec![
- Binding::new("cmd-k cmd-t", Toggle(themes.clone()), None),
- Binding::new("cmd-k t", Reload(themes.clone()), None),
- Binding::new("escape", Toggle(themes.clone()), Some("ThemeSelector")),
- ]);
}
pub enum Event {
@@ -79,18 +64,20 @@ impl ThemeSelector {
this
}
- fn toggle(workspace: &mut Workspace, action: &Toggle, cx: &mut ViewContext<Workspace>) {
+ fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
+ let themes = workspace.themes();
workspace.toggle_modal(cx, |cx, _| {
- let selector = cx.add_view(|cx| Self::new(action.0.clone(), cx));
+ let selector = cx.add_view(|cx| Self::new(themes, cx));
cx.subscribe(&selector, Self::on_event).detach();
selector
});
}
- fn reload(_: &mut Workspace, action: &Reload, cx: &mut ViewContext<Workspace>) {
+ fn reload(workspace: &mut Workspace, _: &Reload, cx: &mut ViewContext<Workspace>) {
let current_theme_name = cx.global::<Settings>().theme.name.clone();
- action.0.clear();
- match action.0.get(¤t_theme_name) {
+ let themes = workspace.themes();
+ themes.clear();
+ match themes.get(¤t_theme_name) {
Ok(theme) => {
Self::set_theme(theme, cx);
log::info!("reloaded theme {}", current_theme_name);
@@ -8,10 +8,12 @@ path = "src/vim.rs"
doctest = false
[dependencies]
+assets = { path = "../assets" }
collections = { path = "../collections" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
language = { path = "../language" }
+serde = { version = "1", features = ["derive"] }
settings = { path = "../settings" }
workspace = { path = "../workspace" }
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
@@ -1,18 +1,12 @@
use crate::{mode::Mode, SwitchMode, VimState};
use editor::Bias;
-use gpui::{actions, keymap::Binding, MutableAppContext, ViewContext};
+use gpui::{actions, MutableAppContext, ViewContext};
use language::SelectionGoal;
use workspace::Workspace;
actions!(vim, [NormalBefore]);
pub fn init(cx: &mut MutableAppContext) {
- let context = Some("Editor && vim_mode == insert");
- cx.add_bindings(vec![
- Binding::new("escape", NormalBefore, context),
- Binding::new("ctrl-c", NormalBefore, context),
- ]);
-
cx.add_action(normal_before);
}
@@ -1,7 +1,8 @@
use editor::CursorShape;
use gpui::keymap::Context;
+use serde::Deserialize;
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
pub enum Mode {
Normal(NormalState),
Insert,
@@ -44,7 +45,7 @@ impl Default for Mode {
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
pub enum NormalState {
None,
GPrefix,
@@ -1,18 +1,19 @@
mod g_prefix;
-use crate::{mode::NormalState, Mode, SwitchMode, VimState};
+use crate::VimState;
use editor::{char_kind, movement, Bias};
-use gpui::{actions, impl_actions, keymap::Binding, MutableAppContext, ViewContext};
+use gpui::{actions, impl_actions, MutableAppContext, ViewContext};
use language::SelectionGoal;
+use serde::Deserialize;
use workspace::Workspace;
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
struct MoveToNextWordStart(pub bool);
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
struct MoveToNextWordEnd(pub bool);
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
struct MoveToPreviousWordStart(pub bool);
impl_actions!(
@@ -39,26 +40,7 @@ actions!(
);
pub fn init(cx: &mut MutableAppContext) {
- let context = Some("Editor && vim_mode == normal");
- cx.add_bindings(vec![
- Binding::new("i", SwitchMode(Mode::Insert), context),
- Binding::new("g", SwitchMode(Mode::Normal(NormalState::GPrefix)), context),
- Binding::new("h", MoveLeft, context),
- Binding::new("j", MoveDown, context),
- Binding::new("k", MoveUp, context),
- Binding::new("l", MoveRight, context),
- Binding::new("0", MoveToStartOfLine, context),
- Binding::new("shift-$", MoveToEndOfLine, context),
- Binding::new("shift-G", MoveToEnd, context),
- Binding::new("w", MoveToNextWordStart(false), context),
- Binding::new("shift-W", MoveToNextWordStart(true), context),
- Binding::new("e", MoveToNextWordEnd(false), context),
- Binding::new("shift-E", MoveToNextWordEnd(true), context),
- Binding::new("b", MoveToPreviousWordStart(false), context),
- Binding::new("shift-B", MoveToPreviousWordStart(true), context),
- ]);
g_prefix::init(cx);
-
cx.add_action(move_left);
cx.add_action(move_down);
cx.add_action(move_up);
@@ -1,16 +1,10 @@
use crate::{mode::Mode, SwitchMode, VimState};
-use gpui::{actions, keymap::Binding, MutableAppContext, ViewContext};
+use gpui::{actions, MutableAppContext, ViewContext};
use workspace::Workspace;
actions!(vim, [MoveToStart]);
pub fn init(cx: &mut MutableAppContext) {
- let context = Some("Editor && vim_mode == normal && vim_submode == g");
- cx.add_bindings(vec![
- Binding::new("g", MoveToStart, context),
- Binding::new("escape", SwitchMode(Mode::normal()), context),
- ]);
-
cx.add_action(move_to_start);
}
@@ -8,12 +8,13 @@ mod vim_test_context;
use collections::HashMap;
use editor::{CursorShape, Editor};
use gpui::{impl_actions, MutableAppContext, ViewContext, WeakViewHandle};
+use serde::Deserialize;
use mode::Mode;
use settings::Settings;
use workspace::{self, Workspace};
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct SwitchMode(pub Mode);
impl_actions!(vim, [SwitchMode]);
@@ -23,7 +23,10 @@ impl<'a> VimTestContext<'a> {
cx.update(|cx| {
editor::init(cx);
crate::init(cx);
+
+ settings::KeymapFile::load("keymaps/vim.json", cx).unwrap();
});
+
let params = cx.update(WorkspaceParams::test);
cx.update(|cx| {
@@ -8,7 +8,7 @@ path = "src/workspace.rs"
doctest = false
[features]
-test-support = ["client/test-support", "project/test-support"]
+test-support = ["client/test-support", "project/test-support", "settings/test-support"]
[dependencies]
client = { path = "../client" }
@@ -1,18 +1,4 @@
-use gpui::{actions, keymap::Binding, MutableAppContext};
-
-actions!(
+gpui::actions!(
menu,
- [Confirm, SelectPrev, SelectNext, SelectFirst, SelectLast,]
+ [Confirm, SelectPrev, SelectNext, SelectFirst, SelectLast]
);
-
-pub fn init(cx: &mut MutableAppContext) {
- cx.add_bindings([
- Binding::new("up", SelectPrev, Some("menu")),
- Binding::new("ctrl-p", SelectPrev, Some("menu")),
- Binding::new("down", SelectNext, Some("menu")),
- Binding::new("ctrl-n", SelectNext, Some("menu")),
- Binding::new("cmd-up", SelectFirst, Some("menu")),
- Binding::new("cmd-down", SelectLast, Some("menu")),
- Binding::new("enter", Confirm, Some("menu")),
- ]);
-}
@@ -7,13 +7,13 @@ use gpui::{
actions,
elements::*,
geometry::{rect::RectF, vector::vec2f},
- impl_actions,
- keymap::Binding,
+ impl_actions, impl_internal_actions,
platform::{CursorStyle, NavigationDirection},
AppContext, Entity, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
ViewContext, ViewHandle, WeakViewHandle,
};
use project::{ProjectEntryId, ProjectPath};
+use serde::Deserialize;
use settings::Settings;
use std::{any::Any, cell::RefCell, cmp, mem, path::Path, rc::Rc};
use util::ResultExt;
@@ -28,29 +28,33 @@ actions!(
]
);
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct Split(pub SplitDirection);
#[derive(Clone)]
-pub struct CloseItem(pub CloseItemParams);
+pub struct CloseItem {
+ pub item_id: usize,
+ pub pane: WeakViewHandle<Pane>,
+}
-#[derive(Clone)]
+#[derive(Clone, Deserialize)]
pub struct ActivateItem(pub usize);
-#[derive(Clone)]
-pub struct GoBack(pub Option<WeakViewHandle<Pane>>);
-
-#[derive(Clone)]
-pub struct GoForward(pub Option<WeakViewHandle<Pane>>);
-
-impl_actions!(pane, [Split, CloseItem, ActivateItem, GoBack, GoForward,]);
+#[derive(Clone, Deserialize)]
+pub struct GoBack {
+ #[serde(skip_deserializing)]
+ pub pane: Option<WeakViewHandle<Pane>>,
+}
-#[derive(Clone)]
-pub struct CloseItemParams {
- pub item_id: usize,
- pub pane: WeakViewHandle<Pane>,
+#[derive(Clone, Deserialize)]
+pub struct GoForward {
+ #[serde(skip_deserializing)]
+ pub pane: Option<WeakViewHandle<Pane>>,
}
+impl_actions!(pane, [Split, GoBack, GoForward]);
+impl_internal_actions!(pane, [CloseItem, ActivateItem]);
+
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
pub fn init(cx: &mut MutableAppContext) {
@@ -66,8 +70,8 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_async_action(Pane::close_active_item);
cx.add_async_action(Pane::close_inactive_items);
cx.add_async_action(|workspace: &mut Workspace, action: &CloseItem, cx| {
- let pane = action.0.pane.upgrade(cx)?;
- Some(Pane::close_item(workspace, pane, action.0.item_id, cx))
+ let pane = action.pane.upgrade(cx)?;
+ Some(Pane::close_item(workspace, pane, action.item_id, cx))
});
cx.add_action(|pane: &mut Pane, action: &Split, cx| {
pane.split(action.0, cx);
@@ -76,7 +80,7 @@ pub fn init(cx: &mut MutableAppContext) {
Pane::go_back(
workspace,
action
- .0
+ .pane
.as_ref()
.and_then(|weak_handle| weak_handle.upgrade(cx)),
cx,
@@ -87,26 +91,13 @@ pub fn init(cx: &mut MutableAppContext) {
Pane::go_forward(
workspace,
action
- .0
+ .pane
.as_ref()
.and_then(|weak_handle| weak_handle.upgrade(cx)),
cx,
)
.detach();
});
-
- cx.add_bindings(vec![
- Binding::new("shift-cmd-{", ActivatePrevItem, Some("Pane")),
- Binding::new("shift-cmd-}", ActivateNextItem, Some("Pane")),
- Binding::new("cmd-w", CloseActiveItem, Some("Pane")),
- Binding::new("alt-cmd-w", CloseInactiveItems, Some("Pane")),
- Binding::new("cmd-k up", Split(SplitDirection::Up), Some("Pane")),
- Binding::new("cmd-k down", Split(SplitDirection::Down), Some("Pane")),
- Binding::new("cmd-k left", Split(SplitDirection::Left), Some("Pane")),
- Binding::new("cmd-k right", Split(SplitDirection::Right), Some("Pane")),
- Binding::new("ctrl--", GoBack(None), Some("Pane")),
- Binding::new("shift-ctrl-_", GoForward(None), Some("Pane")),
- ]);
}
pub enum Event {
@@ -747,10 +738,10 @@ impl Pane {
.on_click({
let pane = pane.clone();
move |cx| {
- cx.dispatch_action(CloseItem(CloseItemParams {
+ cx.dispatch_action(CloseItem {
item_id,
pane: pane.clone(),
- }))
+ })
}
})
.named("close-tab-icon")
@@ -816,8 +807,8 @@ impl View for Pane {
.on_navigate_mouse_down(move |direction, cx| {
let this = this.clone();
match direction {
- NavigationDirection::Back => cx.dispatch_action(GoBack(Some(this))),
- NavigationDirection::Forward => cx.dispatch_action(GoForward(Some(this))),
+ NavigationDirection::Back => cx.dispatch_action(GoBack { pane: Some(this) }),
+ NavigationDirection::Forward => cx.dispatch_action(GoForward { pane: Some(this) }),
}
true
@@ -4,6 +4,7 @@ use client::PeerId;
use collections::HashMap;
use gpui::{elements::*, Axis, Border, ViewHandle};
use project::Collaborator;
+use serde::Deserialize;
use theme::Theme;
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -254,7 +255,7 @@ impl PaneAxis {
}
}
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Deserialize)]
pub enum SplitDirection {
Up,
Down,
@@ -1,5 +1,7 @@
use super::Workspace;
-use gpui::{elements::*, impl_actions, platform::CursorStyle, AnyViewHandle, RenderContext};
+use gpui::{
+ elements::*, impl_internal_actions, platform::CursorStyle, AnyViewHandle, RenderContext,
+};
use std::{cell::RefCell, rc::Rc};
use theme::Theme;
@@ -27,7 +29,7 @@ pub struct ToggleSidebarItem(pub SidebarItemId);
#[derive(Clone)]
pub struct ToggleSidebarItemFocus(pub SidebarItemId);
-impl_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
+impl_internal_actions!(workspace, [ToggleSidebarItem, ToggleSidebarItemFocus]);
#[derive(Clone)]
pub struct SidebarItemId {
@@ -17,9 +17,8 @@ use gpui::{
color::Color,
elements::*,
geometry::{rect::RectF, vector::vec2f, PathBuilder},
- impl_actions,
+ impl_internal_actions,
json::{self, to_string_pretty, ToJson},
- keymap::Binding,
platform::{CursorStyle, WindowOptions},
AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Border, ClipboardItem, Entity,
ImageData, ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task,
@@ -32,7 +31,7 @@ pub use pane_group::*;
use postage::prelude::Stream;
use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, Worktree};
use settings::Settings;
-use sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus};
+use sidebar::{Side, Sidebar, ToggleSidebarItem, ToggleSidebarItemFocus};
use status_bar::StatusBar;
pub use status_bar::StatusItemView;
use std::{
@@ -101,14 +100,13 @@ pub struct ToggleFollow(pub PeerId);
#[derive(Clone)]
pub struct JoinProject(pub JoinProjectParams);
-impl_actions!(
+impl_internal_actions!(
workspace,
[Open, OpenNew, OpenPaths, ToggleFollow, JoinProject]
);
pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
pane::init(cx);
- menu::init(cx);
cx.add_global_action(open);
cx.add_global_action(move |action: &OpenPaths, cx: &mut MutableAppContext| {
@@ -144,29 +142,6 @@ pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
workspace.activate_next_pane(cx)
});
- cx.add_bindings(vec![
- Binding::new("ctrl-alt-cmd-f", FollowNextCollaborator, None),
- Binding::new("cmd-s", Save, None),
- Binding::new("cmd-alt-i", DebugElements, None),
- Binding::new("cmd-k cmd-left", ActivatePreviousPane, None),
- Binding::new("cmd-k cmd-right", ActivateNextPane, None),
- Binding::new(
- "cmd-shift-!",
- ToggleSidebarItem(SidebarItemId {
- side: Side::Left,
- item_index: 0,
- }),
- None,
- ),
- Binding::new(
- "cmd-1",
- ToggleSidebarItemFocus(SidebarItemId {
- side: Side::Left,
- item_index: 0,
- }),
- None,
- ),
- ]);
client.add_view_request_handler(Workspace::handle_follow);
client.add_view_message_handler(Workspace::handle_unfollow);
@@ -630,6 +605,7 @@ pub struct WorkspaceParams {
pub client: Arc<Client>,
pub fs: Arc<dyn Fs>,
pub languages: Arc<LanguageRegistry>,
+ pub themes: Arc<ThemeRegistry>,
pub user_store: ModelHandle<UserStore>,
pub channel_list: ModelHandle<ChannelList>,
}
@@ -659,6 +635,7 @@ impl WorkspaceParams {
channel_list: cx
.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
client,
+ themes: ThemeRegistry::new((), cx.font_cache().clone()),
fs,
languages,
user_store,
@@ -677,6 +654,7 @@ impl WorkspaceParams {
),
client: app_state.client.clone(),
fs: app_state.fs.clone(),
+ themes: app_state.themes.clone(),
languages: app_state.languages.clone(),
user_store: app_state.user_store.clone(),
channel_list: app_state.channel_list.clone(),
@@ -694,6 +672,7 @@ pub struct Workspace {
user_store: ModelHandle<client::UserStore>,
remote_entity_subscription: Option<Subscription>,
fs: Arc<dyn Fs>,
+ themes: Arc<ThemeRegistry>,
modal: Option<AnyViewHandle>,
center: PaneGroup,
left_sidebar: Sidebar,
@@ -802,6 +781,7 @@ impl Workspace {
remote_entity_subscription: None,
user_store: params.user_store.clone(),
fs: params.fs.clone(),
+ themes: params.themes.clone(),
left_sidebar: Sidebar::new(Side::Left),
right_sidebar: Sidebar::new(Side::Right),
project: params.project.clone(),
@@ -834,6 +814,10 @@ impl Workspace {
&self.project
}
+ pub fn themes(&self) -> Arc<ThemeRegistry> {
+ self.themes.clone()
+ }
+
pub fn worktrees<'a>(
&self,
cx: &'a AppContext,
@@ -29,6 +29,7 @@ test-support = [
]
[dependencies]
+assets = { path = "../assets" }
breadcrumbs = { path = "../breadcrumbs" }
chat_panel = { path = "../chat_panel" }
collections = { path = "../collections" }
@@ -2,6 +2,7 @@
#![allow(non_snake_case)]
use anyhow::{anyhow, Context, Result};
+use assets::Assets;
use client::{self, http, ChannelList, UserStore};
use fs::OpenOptions;
use futures::{channel::oneshot, StreamExt};
@@ -9,19 +10,17 @@ use gpui::{App, AssetSource, Task};
use log::LevelFilter;
use parking_lot::Mutex;
use project::Fs;
-use settings::{self, Settings};
+use settings::{self, KeymapFile, Settings, SettingsFileContent};
use smol::process::Command;
use std::{env, fs, path::PathBuf, sync::Arc};
use theme::{ThemeRegistry, DEFAULT_THEME_NAME};
use util::ResultExt;
use workspace::{self, AppState, OpenNew, OpenPaths};
use zed::{
- self,
- assets::Assets,
- build_window_options, build_workspace,
+ self, build_window_options, build_workspace,
fs::RealFs,
languages, menus,
- settings_file::{settings_from_files, SettingsFile},
+ settings_file::{settings_from_files, watch_keymap_file, WatchedJsonFile},
};
fn main() {
@@ -62,8 +61,16 @@ fn main() {
tab_size: Some(2),
..Default::default()
},
+ )
+ .with_overrides(
+ "TSX",
+ settings::LanguageOverride {
+ tab_size: Some(2),
+ ..Default::default()
+ },
);
- let settings_file = load_settings_file(&app, fs.clone());
+
+ let config_files = load_config_files(&app, fs.clone());
let login_shell_env_loaded = if stdout_is_a_pty() {
Task::ready(())
@@ -112,13 +119,16 @@ fn main() {
})
.detach_and_log_err(cx);
- let settings_file = cx.background().block(settings_file).unwrap();
+ let (settings_file, keymap_file) = cx.background().block(config_files).unwrap();
let mut settings_rx = settings_from_files(
default_settings,
vec![settings_file],
themes.clone(),
cx.font_cache().clone(),
);
+
+ cx.spawn(|cx| watch_keymap_file(keymap_file, cx)).detach();
+
let settings = cx.background().block(settings_rx.next()).unwrap();
cx.spawn(|mut cx| async move {
while let Some(settings) = settings_rx.next().await {
@@ -145,8 +155,8 @@ fn main() {
build_workspace: &build_workspace,
});
journal::init(app_state.clone(), cx);
+ theme_selector::init(cx);
zed::init(&app_state, cx);
- theme_selector::init(app_state.themes.clone(), cx);
cx.set_menus(menus::menus(&app_state.clone()));
@@ -254,14 +264,22 @@ fn load_embedded_fonts(app: &App) {
.unwrap();
}
-fn load_settings_file(app: &App, fs: Arc<dyn Fs>) -> oneshot::Receiver<SettingsFile> {
+fn load_config_files(
+ app: &App,
+ fs: Arc<dyn Fs>,
+) -> oneshot::Receiver<(
+ WatchedJsonFile<SettingsFileContent>,
+ WatchedJsonFile<KeymapFile>,
+)> {
let executor = app.background();
let (tx, rx) = oneshot::channel();
executor
.clone()
.spawn(async move {
- let file = SettingsFile::new(fs, &executor, zed::SETTINGS_PATH.clone()).await;
- tx.send(file).ok()
+ let settings_file =
+ WatchedJsonFile::new(fs.clone(), &executor, zed::SETTINGS_PATH.clone()).await;
+ let keymap_file = WatchedJsonFile::new(fs, &executor, zed::KEYMAP_PATH.clone()).await;
+ tx.send((settings_file, keymap_file)).ok()
})
.detach();
rx
@@ -1,17 +1,21 @@
use futures::{stream, StreamExt};
-use gpui::{executor, FontCache};
+use gpui::{executor, AsyncAppContext, FontCache};
use postage::sink::Sink as _;
use postage::{prelude::Stream, watch};
use project::Fs;
-use settings::{Settings, SettingsFileContent};
+use serde::Deserialize;
+use settings::{KeymapFile, Settings, SettingsFileContent};
use std::{path::Path, sync::Arc, time::Duration};
use theme::ThemeRegistry;
use util::ResultExt;
#[derive(Clone)]
-pub struct SettingsFile(watch::Receiver<SettingsFileContent>);
+pub struct WatchedJsonFile<T>(watch::Receiver<T>);
-impl SettingsFile {
+impl<T> WatchedJsonFile<T>
+where
+ T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync,
+{
pub async fn new(
fs: Arc<dyn Fs>,
executor: &executor::Background,
@@ -35,21 +39,21 @@ impl SettingsFile {
Self(rx)
}
- async fn load(fs: Arc<dyn Fs>, path: &Path) -> Option<SettingsFileContent> {
+ async fn load(fs: Arc<dyn Fs>, path: &Path) -> Option<T> {
if fs.is_file(&path).await {
fs.load(&path)
.await
.log_err()
.and_then(|data| serde_json::from_str(&data).log_err())
} else {
- Some(SettingsFileContent::default())
+ Some(T::default())
}
}
}
pub fn settings_from_files(
defaults: Settings,
- sources: Vec<SettingsFile>,
+ sources: Vec<WatchedJsonFile<SettingsFileContent>>,
theme_registry: Arc<ThemeRegistry>,
font_cache: Arc<FontCache>,
) -> impl futures::stream::Stream<Item = Settings> {
@@ -72,6 +76,16 @@ pub fn settings_from_files(
})
}
+pub async fn watch_keymap_file(mut file: WatchedJsonFile<KeymapFile>, mut cx: AsyncAppContext) {
+ while let Some(content) = file.0.recv().await {
+ cx.update(|cx| {
+ cx.clear_bindings();
+ settings::KeymapFile::load_defaults(cx);
+ content.add(cx).log_err();
+ });
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -102,9 +116,9 @@ mod tests {
.await
.unwrap();
- let source1 = SettingsFile::new(fs.clone(), &executor, "/settings1.json".as_ref()).await;
- let source2 = SettingsFile::new(fs.clone(), &executor, "/settings2.json".as_ref()).await;
- let source3 = SettingsFile::new(fs.clone(), &executor, "/settings3.json".as_ref()).await;
+ let source1 = WatchedJsonFile::new(fs.clone(), &executor, "/settings1.json".as_ref()).await;
+ let source2 = WatchedJsonFile::new(fs.clone(), &executor, "/settings2.json".as_ref()).await;
+ let source3 = WatchedJsonFile::new(fs.clone(), &executor, "/settings3.json".as_ref()).await;
let mut settings_rx = settings_from_files(
cx.read(Settings::test),
@@ -1,4 +1,5 @@
-use crate::{assets::Assets, build_window_options, build_workspace, AppState};
+use crate::{build_window_options, build_workspace, AppState};
+use assets::Assets;
use client::{test::FakeHttpClient, ChannelList, Client, UserStore};
use gpui::MutableAppContext;
use language::LanguageRegistry;
@@ -1,4 +1,3 @@
-pub mod assets;
pub mod languages;
pub mod menus;
pub mod settings_file;
@@ -14,8 +13,6 @@ pub use editor;
use gpui::{
actions,
geometry::vector::vec2f,
- impl_actions,
- keymap::Binding,
platform::{WindowBounds, WindowOptions},
ModelHandle, ViewContext,
};
@@ -30,12 +27,16 @@ use std::{path::PathBuf, sync::Arc};
pub use workspace;
use workspace::{AppState, Workspace, WorkspaceParams};
-actions!(zed, [About, Quit, OpenSettings]);
-
-#[derive(Clone)]
-pub struct AdjustBufferFontSize(pub f32);
-
-impl_actions!(zed, [AdjustBufferFontSize]);
+actions!(
+ zed,
+ [
+ About,
+ Quit,
+ OpenSettings,
+ IncreaseBufferFontSize,
+ DecreaseBufferFontSize
+ ]
+);
const MIN_FONT_SIZE: f32 = 6.0;
@@ -44,20 +45,23 @@ lazy_static! {
.expect("failed to determine home directory")
.join(".zed");
pub static ref SETTINGS_PATH: PathBuf = ROOT_PATH.join("settings.json");
+ pub static ref KEYMAP_PATH: PathBuf = ROOT_PATH.join("keymap.json");
}
pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
cx.add_global_action(quit);
- cx.add_global_action({
- move |action: &AdjustBufferFontSize, cx| {
- cx.update_global::<Settings, _, _>(|settings, cx| {
- settings.buffer_font_size =
- (settings.buffer_font_size + action.0).max(MIN_FONT_SIZE);
- cx.refresh_windows();
- });
- }
+ cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
+ cx.update_global::<Settings, _, _>(|settings, cx| {
+ settings.buffer_font_size = (settings.buffer_font_size + 1.0).max(MIN_FONT_SIZE);
+ cx.refresh_windows();
+ });
+ });
+ cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| {
+ cx.update_global::<Settings, _, _>(|settings, cx| {
+ settings.buffer_font_size = (settings.buffer_font_size - 1.0).max(MIN_FONT_SIZE);
+ cx.refresh_windows();
+ });
});
-
cx.add_action({
let app_state = app_state.clone();
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
@@ -99,11 +103,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
workspace::lsp_status::init(cx);
- cx.add_bindings(vec![
- Binding::new("cmd-=", AdjustBufferFontSize(1.), None),
- Binding::new("cmd--", AdjustBufferFontSize(-1.), None),
- Binding::new("cmd-,", OpenSettings, None),
- ])
+ settings::KeymapFile::load_defaults(cx);
}
pub fn build_workspace(
@@ -134,6 +134,7 @@ pub fn build_workspace(
client: app_state.client.clone(),
fs: app_state.fs.clone(),
languages: app_state.languages.clone(),
+ themes: app_state.themes.clone(),
user_store: app_state.user_store.clone(),
channel_list: app_state.channel_list.clone(),
};
@@ -205,9 +206,8 @@ fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
#[cfg(test)]
mod tests {
- use crate::assets::Assets;
-
use super::*;
+ use assets::Assets;
use editor::{DisplayPoint, Editor};
use gpui::{AssetSource, MutableAppContext, TestAppContext, ViewHandle};
use project::{Fs, ProjectPath};
@@ -10,7 +10,7 @@ for (let theme of themes) {
let styleTree = snakeCase(app(theme));
let styleTreeJSON = JSON.stringify(styleTree, null, 2);
let outPath = path.resolve(
- `${__dirname}/../../crates/zed/assets/themes/${theme.name}.json`
+ `${__dirname}/../assets/themes/${theme.name}.json`
);
fs.writeFileSync(outPath, styleTreeJSON);
console.log(`- ${outPath} created`);