Files
nvim-config/ZED/keymap.json
2026-05-19 11:15:45 +03:30

343 lines
18 KiB
JSON

// Zed keymap — WASD-style vim bindings
// Mirrors keymaps.lua for Neovim.
//
// Key remapping legend (nvim lhs → nvim rhs → Zed action):
// Movement : w→k s→j a→h d→l
// Word fwd : i→w I→W j→e J→E
// Word back : (b/B unchanged)
// Insert : e→i E→a A→I D→A
// Delete op : r→d R→D
// Substitute: f→s F→S
// Replace : t→r T→R
// Open line : W→O S→o
// Visual : h→v H→V
// Find fwd : l→f L→F
// Till fwd : p→t P→T
// Paste : v→p V→P
// Nop'd : k K O (collisions cleared)
//
// Place this file at:
// macOS : ~/.config/zed/keymap.json
// Linux : ~/.config/zed/keymap.json
// Windows: %APPDATA%\Zed\keymap.json
[
// ─────────────────────────────────────────────────────────────
// NORMAL MODE
// ─────────────────────────────────────────────────────────────
{
"context": "vim_mode == normal && !menu",
"bindings": {
// ── Movement (WASD) ──────────────────────────────────────
"w": "vim::Up",
"s": "vim::Down",
"a": "vim::Left",
"d": "vim::Right",
// ── Word motions ─────────────────────────────────────────
// i → w (next word start), I → W (WORD)
"i": "vim::NextWordStart",
"I": ["vim::NextWordStart", { "ignore_punctuation": true }],
// j → e (next word end), J → E (WORD end)
"j": "vim::NextWordEnd",
"J": ["vim::NextWordEnd", { "ignore_punctuation": true }],
// b / B unchanged (previous word start)
// ── Enter insert mode ────────────────────────────────────
"e": "vim::InsertBefore", // e → i
"E": "vim::InsertAfter", // E → a
"A": "vim::InsertFirstNonWhitespace", // A → I
"D": "vim::InsertEndOfLine", // D → A
// ── Open lines ───────────────────────────────────────────
"S": "vim::InsertLineBelow", // S → o
"W": "vim::InsertLineAbove", // W → O
// ── Delete operator ──────────────────────────────────────
// r → d (push delete, waits for motion/object)
"r": "vim::PushDelete",
// R → D (delete to end of line)
"R": "vim::DeleteToEndOfLine",
// ── Change / substitute ──────────────────────────────────
// c unchanged (push change operator)
// f → s (substitute character under cursor, i.e. vim 's')
"f": "vim::Substitute",
// F → S (substitute whole line)
"F": "vim::SubstituteLine",
// ── Replace (single char) ────────────────────────────────
// t → r (push replace — waits for char)
"t": "vim::PushReplace",
// T → R (toggle replace mode)
"T": "vim::ToggleReplace",
// ── Visual mode entry ────────────────────────────────────
"h": "vim::ToggleVisual", // h → v
"H": "vim::ToggleVisualLine", // H → V
// ── Find / till ──────────────────────────────────────────
// l → f (find forward)
"l": [
"vim::PushFindForward",
{ "before": false, "multiline": false },
],
// L → F (find backward)
"L": [
"vim::PushFindBackward",
{ "after": false, "multiline": false },
],
// p → t (till forward, stops before char)
"p": [
"vim::PushFindForward",
{ "before": true, "multiline": false },
],
// P → T (till backward)
"P": [
"vim::PushFindBackward",
{ "after": true, "multiline": false },
],
// ── Paste ────────────────────────────────────────────────
"v": "vim::Paste", // v → p
"V": ["vim::Paste", { "before": true }], // V → P
// ── Nop — clear dangerous collisions ────────────────────
"k": null,
"K": null,
"o": null,
"O": null,
// ── Repeat find (unchanged) ──────────────────────────────
";": "vim::RepeatFind",
",": "vim::RepeatFindReversed",
},
},
// ─────────────────────────────────────────────────────────────
// VISUAL MODE
// ─────────────────────────────────────────────────────────────
{
"context": "vim_mode == visual && !menu",
"bindings": {
// ── Movement ─────────────────────────────────────────────
"w": "vim::Up",
"s": "vim::Down",
"a": ["vim::PushObject", { "around": true }], // 'a' text-object prefix kept
"d": "vim::Right",
// ── Word motions ─────────────────────────────────────────
"i": ["vim::PushObject", { "around": false }], // 'i' text-object prefix kept in visual
"I": ["vim::NextWordStart", { "ignore_punctuation": true }],
"j": "vim::NextWordEnd",
"J": ["vim::NextWordEnd", { "ignore_punctuation": true }],
// ── Operators ────────────────────────────────────────────
"r": "vim::VisualDelete", // r → d (delete selection)
"R": "vim::VisualDeleteLine", // R → D
"f": "vim::Substitute", // f → s (substitute selection)
"F": "vim::SubstituteLine", // F → S
// ── Replace char in selection ────────────────────────────
"t": "vim::PushReplace",
// ── Paste ────────────────────────────────────────────────
"v": "vim::Paste",
"V": ["vim::Paste", { "preserve_clipboard": true }],
// ── Find / till ──────────────────────────────────────────
"l": [
"vim::PushFindForward",
{ "before": false, "multiline": false },
],
"L": [
"vim::PushFindBackward",
{ "after": false, "multiline": false },
],
"p": [
"vim::PushFindForward",
{ "before": true, "multiline": false },
],
"P": [
"vim::PushFindBackward",
{ "after": true, "multiline": false },
],
// ── Nop ──────────────────────────────────────────────────
"k": null,
"K": null,
"O": null,
// ── Repeat find ──────────────────────────────────────────
";": "vim::RepeatFind",
",": "vim::RepeatFindReversed",
},
},
// ─────────────────────────────────────────────────────────────
// OPERATOR-PENDING MODE
// Entered after pressing an operator (r=d, c, y, etc.).
// Zed evaluates BOTH vim_mode==operator bindings AND
// vim_operator==X bindings simultaneously, most-specific wins.
//
// CRITICAL: 'a' and 'i' must NOT be movement keys here —
// they need to push the text-object context so that sequences
// like "rai" (daw) and "cai" (caw) work correctly.
// 'd' (right) is safe because it doesn't clash with any object prefix.
// 's' (down) is safe for counts: r2s = delete 2 lines down.
// ─────────────────────────────────────────────────────────────
{
"context": "vim_mode == operator && !menu",
"bindings": {
// ── Movement motions ─────────────────────────────────────
"w": "vim::Up",
"s": "vim::Down",
// NOTE: 'a' is intentionally OMITTED here — left to the
// default vim binding so it acts as PushObject {around:true},
// enabling "ra<obj>", "ca<obj>" chains.
// NOTE: 'i' is intentionally OMITTED here — same reason,
// so "ri<obj>", "ci<obj>" chains work.
"d": "vim::Right",
// ── Word motions (your i→w, j→e remaps) ─────────────────
// These only fire when NOT immediately followed by an object key.
// Zed resolves ambiguity by waiting for the next key:
// "ri" → i pushes object context (PushObject inner) → wait for obj key
// "rij" → i pushes object context → j=NextWordEnd (word end object? no —
// j in object context = vim::NextWordEnd motion, acts as object)
// So we put word motions on i/j only in contexts where they're unambiguous:
// We handle this via the object-context layer below instead.
// ── Nop ──────────────────────────────────────────────────
"k": null,
"K": null,
// ── Find / till motions ──────────────────────────────────
"l": [
"vim::PushFindForward",
{ "before": false, "multiline": false },
],
"L": [
"vim::PushFindBackward",
{ "after": false, "multiline": false },
],
"p": [
"vim::PushFindForward",
{ "before": true, "multiline": false },
],
"P": [
"vim::PushFindBackward",
{ "after": true, "multiline": false },
],
// ── Repeat find ──────────────────────────────────────────
";": "vim::RepeatFind",
",": "vim::RepeatFindReversed",
},
},
// ─────────────────────────────────────────────────────────────
// TEXT OBJECTS (vim_operator == a or vim_operator == i)
// Entered after pressing 'a' (around) or 'i' (inner) in operator mode.
//
// Your nvim remaps (operator-pending):
// i → w so "ci" = change inner word (vanilla: "cw" but really "ciw")
// ai → aw so "cai" = change a word (vanilla: "caw")
// ai → aw so "rai" = delete a word (vanilla: "daw")
//
// In Zed: after 'a' prefix → vim_operator == a → next key selects object
// after 'i' prefix → vim_operator == i → next key selects object
//
// Key remaps inside object context:
// i → Word (your word key, so "cii"=inner word, "cai"=a word)
// j → NextWordEnd used as object? No — j maps to vim::Word for end-of-word obj
// s → Sentence (s was your Down, but inside obj context it's unambiguous)
// w → Word (vanilla fallback)
// d → Right motion doesn't make sense as object, leave as Right? No —
// inside obj context all keys are object names, not motions.
// 'd' has no standard vim text object so leave unmapped / null.
// ─────────────────────────────────────────────────────────────
{
"context": "vim_operator == a || vim_operator == i || vim_operator == cs || vim_operator == helix_next || vim_operator == helix_previous",
"bindings": {
// ── Your remapped word keys → word objects ────────────────
"i": "vim::Word", // cai / cii / rai / rii → word
"I": ["vim::Word", { "ignore_punctuation": true }], // caI / ciI → WORD
// ── Standard object keys (vanilla vim, kept as-is) ────────
"w": "vim::Word",
"s": "vim::Sentence", // s=Sentence (not Down here)
"p": "vim::Paragraph",
"t": "vim::Tag",
"e": "vim::EntireFile",
"f": "vim::Method",
"c": "vim::Class",
"a": "vim::Argument", // 'a' = Argument object (e.g. "cia")
"\"": "vim::DoubleQuotes",
"'": "vim::Quotes",
"`": "vim::BackQuotes",
"q": "vim::MiniQuotes",
"|": "vim::VerticalBars",
"(": ["vim::Parentheses", { "opening": true }],
")": "vim::Parentheses",
"b": "vim::Parentheses",
"[": ["vim::SquareBrackets", { "opening": true }],
"]": "vim::SquareBrackets",
"r": "vim::SquareBrackets", // 'r' = [] object (inside obj ctx)
"{": ["vim::CurlyBrackets", { "opening": true }],
"}": "vim::CurlyBrackets",
"B": "vim::CurlyBrackets",
"<": ["vim::AngleBrackets", { "opening": true }],
">": "vim::AngleBrackets",
},
},
// ─────────────────────────────────────────────────────────────
// DELETE OPERATOR (after pressing r)
// r→d, so vim_operator == d fires. We need "rr" → dd (current line).
// ─────────────────────────────────────────────────────────────
{
"context": "vim_operator == d",
"bindings": {
"r": "vim::CurrentLine", // rr → dd (delete line)
"s": "vim::PushDeleteSurrounds", // rs → ds (delete surrounds)
},
},
// ─────────────────────────────────────────────────────────────
// CHANGE OPERATOR (after pressing c, vim_operator == c)
// c is unchanged so "cc" still works via the default binding.
// ─────────────────────────────────────────────────────────────
{
"context": "vim_operator == c",
"bindings": {
"c": "vim::CurrentLine", // cc → change line (default, kept)
"s": ["vim::PushChangeSurrounds", {}], // cs → change surrounds
},
},
// ─────────────────────────────────────────────────────────────
// YANK OPERATOR (after pressing y)
// ─────────────────────────────────────────────────────────────
{
"context": "vim_operator == y",
"bindings": {
"y": "vim::CurrentLine", // yy → yank line (default, kept)
},
},
// ─────────────────────────────────────────────────────────────
// INSERT MODE — keep ctrl shortcuts working
// ─────────────────────────────────────────────────────────────
{
"context": "vim_mode == insert",
"bindings": {
// Standard escape routes unchanged
"ctrl-[": "vim::NormalBefore",
"escape": "vim::NormalBefore",
// All other insert-mode bindings from the default vim keymap
// remain active (ctrl-w, ctrl-u, ctrl-r, ctrl-o, etc.)
},
},
]