zed-industries / zed

Code at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.
https://zed.dev
Other
48.44k stars 2.89k forks source link

Document keymap context #14718

Open bersace opened 3 months ago

bersace commented 3 months ago

Check for existing issues

Describe the feature

Hi,

Reading https://github.com/zed-industries/zed/blob/main/docs/src/key-bindings.md, I don't find a proper documentation of "context" query in keymaps.json. Could you document this ?

My questions:

Regards, Étienne

If applicable, add mockups / screenshots to help present your vision of the feature

No response

notpeter commented 3 months ago

Yep. We could use some better documentation around context for keybindings. Here's a non-exhaustive list of contexts used by the default bindings.

Plus a bunch more in vim.json


What is mode ?

Editor mode can be either full or auto_height. Full is the normal editor, I believe auto_height is during the find in files where you can directly edit the results (in a height constrained space).

What context when no file are opens ?

If you have no panels in focus, no files open Workspace or global (no context) will apply.

What is Markdown?

Markdown preview.

How to know the current context ? How to know available shortcut in a context ? Doom Emacs has a bottom drawer showing next shortcut.

This would be a useful enhancement.

bersace commented 3 months ago

Thanks @notpeter

Workspace also applies if a panel is opened. I want space space to trigger file_finder::Toggle only if no panel.

Anyway, a documentation on this powerfull feature would be great, e.g. for community to implement #4642 .

Regards,

bersace commented 3 months ago

What does "BufferSearchBar && !in_replace > Editor" mean ? Especially the greater than sign ?

bersace commented 3 months ago

Workspace also applies if a panel is opened. I want space space to trigger file_finder::Toggle only if no panel.

EmptyPane do the trick. Thanks !

bersace commented 3 months ago

A primer:

Contextes:

AssistantPanel
BufferSearchBar
ChannelModal
CollabPanel
ContextEditor
Dock
Editor
EmptyPane
FileFinder
Markdown
OutlinePanel
Pane
Picker
ProjectPanel
ProjectSearchBar
ProjectSearchView
PromptLibrary
TabSwitcher
Terminal
VimControl
Workspace

vim_mode values :

insert
normal
operator
replace
visual
waiting

Context operators:

Variables:

auto_height
editing
full
inline_completion
in_replace
jupyter
menu
mode
not_editing
renaming
showing_code_actions
showing_completions
vim_mode
vim_operator

I grepped from keymaps, not Rust.

bersace commented 3 months ago

From grepping Rust code:

Identifiers (key_context.add(...), .key_context(...), dispatch_context.add(...)):

alternate_scroll
any_mouse_reporting
AssistantPanel
bracketed_paste
BufferSearchBar
ChannelModal
ChatPanel
child-1
child-2
CollabPanel
ContextEditor
copilot_suggestion
DECAWM
DECCKM
DECOM
DECPAM
DECPNM
DECTCEM
DevServerModal
Dock
Editor
EmptyPane
FileFinder
GiveFeedback
GoToLine
inline_completion
in_replace
IRM
jupyter
LNM
MarkdownPreview
menu
nested
OutlinePanel
Pane
parent
Picker
ProjectPanel
ProjectSearchBar
Prompt
PromptLibrary
renaming
report_focus
SharedScreen
showing_code_actions
showing_completions
TabSwitcher
TasksModal
Terminal
TextInput

Context Key/values (key_context.set(...)):

mode
extension
mouse_reporting
mouse_format
screen
neunato commented 2 months ago

I spent the last few days configuring zed, and have found keybind configuration has some room for improvement :) A few notes:

It took a while to get over the initial confusion of what contexts actually are. do they relate to the mere presence of an element? or does it have to have focus? if one context is active does it deactivate others or do they coexist? is there some hierarchical structure to them? how do they relate to actions, given the similar prefixes? how are keybind conflicts resolved?

Here are my conclusions, please correct me if I'm wrong:

One could compare it to the following html and css selectors:

   <div class="Global">
      <div class="Workspace">
         <div class="Pane">
            <div class="BufferSearchBar">
               <div class="Editor">
                  <!-- search -->              <!-- .Workspace > .Pane > .BufferSearchBar > .Editor -->
               </div>
            </div>
            <div class="Editor">
               <!-- buffer -->                 <!-- .Workspace > .Pane > .Editor -->
            </div>
         </div>
      </div>
   </div>

It's possible to apply rules Parent > Child just like in css, but this is hard to use when the document structure is unknown. Having a non-direct child selector would probably make things easier to use too (like Workspace > Editor)

It's possible to specify the same context multiple times, where the last one wins. This can be useful when a command may not succeed to make it fallback to a previous one, see this example:

[{
   "context": "Editor && mode == full",
   "bindings": {
      "f1": "buffer_search::Deploy"          // only gets called if "buffer_search::FocusSearch" fails
   }
},{
   "context": "Editor && mode == full",
   "bindings": {
      "f1": "buffer_search::FocusSearch"     // focus if open, else look for next action
   }
}]

Which feels like an anti-pattern, would be nice to have a way of specifying fallback commands. This is how micro deals with it: "Tab": "Autocomplete|IndentSelection|InsertTab".


What I would like to see:


A few observations:


And some additional suggestions:

test3211234 commented 2 months ago

Hi @neunato. You seem to know how the whole context system works, and I had multiple questions about that. Could you answer them please? Here:

https://github.com/zed-industries/zed/discussions/15401

malssid commented 1 month ago

A primer:

Contextes:

AssistantPanel
BufferSearchBar
ChannelModal
CollabPanel
ContextEditor
Dock
Editor
EmptyPane
FileFinder
Markdown
OutlinePanel
Pane
Picker
ProjectPanel
ProjectSearchBar
ProjectSearchView
PromptLibrary
TabSwitcher
Terminal
VimControl
Workspace

vim_mode values :

insert
normal
operator
replace
visual
waiting

Context operators:

  • &&
  • !=
  • ==
  • >
  • ()
  • !
  • ||

Variables:

auto_height
editing
full
inline_completion
in_replace
jupyter
menu
mode
not_editing
renaming
showing_code_actions
showing_completions
vim_mode
vim_operator

I grepped from keymaps, not Rust.

I feel like these variables should be documented somewhere! I had to use the editing variable here to bind a/A/r within the ProjectPanel. Without this, I wouldn't be able to type these letters in the input box.

  { 
    "context": "ProjectPanel && !editing",
    "bindings": { 
      "space e": "workspace::ToggleLeftDock",
      "a": "project_panel::NewFile",
      "A": "project_panel::NewDirectory",
      "r": "project_panel::Rename",
      "d": "project_panel::Delete"
    }
  }
]
AurevoirXavier commented 1 month ago

Yep. We could use some better documentation around context for keybindings. Here's a non-exhaustive list of contexts used by the default bindings.

  • (no context) == global shortcut
  • Workspace
  • Pane
  • ProjectPanel (left files sidebar)
    • ProjectPanel && not_editing
  • Editor
    • Editor && mode == full
    • Editor && mode == full && inline_completion
    • Editor && mode == full && !jupyter
    • Editor && mode == full && inline_completion
    • Editor && !inline_completion
    • Editor && mode == auto_height
    • Editor && renaming
    • Editor && showing_completions
    • Editor && inline_completion && !showing_completions
    • Editor && showing_code_actions
    • Editor && (showing_code_actions || showing_completions)
    • ContextEditor > Editor
    • Picker > Editor
  • BufferSearchBar
    • BufferSearchBar && in_replace
    • BufferSearchBar && !in_replace > Editor
  • ProjectSearchBar
    • ProjectSearchBar > Editor
    • ProjectSearchBar && in_replace > Editor
    • ProjectSearchBar && !in_replace
  • ProjectSearchView
  • Dock
  • AssistantPanel
  • PromptLibrary
  • Terminal
  • OutlinePanel
  • CollabPanel
    • CollabPanel && not_editing
    • (CollabPanel && editing) > Editor
  • ChannelModal
    • ChannelModal > Picker > Editor
  • TabSwitcher (ctrl-tab / ctrl-shift modal)
  • FileFinder (cmd+p modal "Search project files")
  • Markdown (markdown preview window)
  • EmptyPane || SharedScreen

Plus a bunch more in vim.json

What is mode ?

Editor mode can be either full or auto_height. Full is the normal editor, I believe auto_height is during the find in files where you can directly edit the results (in a height constrained space).

What context when no file are opens ?

If you have no panels in focus, no files open Workspace or global (no context) will apply.

What is Markdown?

Markdown preview.

How to know the current context ? How to know available shortcut in a context ? Doom Emacs has a bottom drawer showing next shortcut.

This would be a useful enhancement.

image

Apparently, this document is outdated.

I tried Search and BufferSearch. Finally, BufferSearchBar context works for me.

baptisteArno commented 1 month ago

Yes, same for Assistant, I belive now it is AssistantPanel

baptisteArno commented 1 month ago

I am confused, I thought that this would work for file finder, project finder and buffer list but it's not

{
    "context": "menu",
    "bindings": {
      "ctrl-j": "menu::SelectNext",
      "ctrl-k": "menu::SelectPrev"
    }
  }

It works if I remove context. I also tried ProjectSearchBar, BufferSearchBar etc... no context work. I often times am confused about what to insert here and what are the possibilities.

This really need a better documentation because it's most likely a deal breaker compared to the way we can configure Neovim

leo91000 commented 3 weeks ago

I have setup some keybinds like this : Editor && !AssistantPanel && (vim_mode == normal || vim_mode == visual)

However, when I open the assitant panel, it stills uses this keyind, so the !AssistantPanel does not seems to take effect.

Is it because of cascading rules ?

Is there a way to have keyinds disabled in the assistant panel ?

AntonEriksson978 commented 1 week ago

Would be super useful if there was a command that would show/print what context you are currently in to make it easier to create your keymaps.

notpeter commented 1 week ago

Would be super useful if there was a command that would show/print what context you are currently in to make it easier to create your keymaps.

Yeah, we could certainly do better here, but I recommend you look at the existing Contexts in default-linux.json or default-macos.json as a reference.

Because sometimes in addition to knowing your current context (e.g. Root > Workspace > Pane > Editor) you may want to exclude certain context for mapping. For example : https://github.com/zed-industries/zed/blob/b83b42b9beba389c3b77eef5f0323b68eff9ea5a/assets/keymaps/default-macos.json#L446-L452

There has been discussion about building a debug window (like debug: open syntax tree view does for tree-sitter) which displays your GPUI context for keymapping or your syntax highlighting context for themes.

andrewbanchich commented 4 days ago

There has been discussion about building a debug window (like debug: open syntax tree view does for tree-sitter) which displays your GPUI context for keymapping or your syntax highlighting context for themes.

that would be super awesome. confusion about contexts and why some keybindings are not working / being overridden has been by far my biggest pain point in adopting Zed (although that isn't saying much - it's been wonderful with almost everything else).

andrewbanchich commented 4 days ago

IMO it would be ideal if we could have a command like debug: key bindings which shows the name of the current context, a list of all active context values from your custom keybindings + the default ones, and then a list of all values per unique binding / context tuple, e.g.

"currentContext": "ContextC",
"activeContexts": ["ContextB", "ContextC"]
"keys": {
  "ctrl-g": {
    "activeBindings": [
      "ContextB": "bar",
      "ContextC": "baz"
    ]
  }
}

if the last one is the winner, then that makes it easy to figure out why it's doing baz instead of bar / how overrides work.

you can also see ContextA is not active at all, which helps explain issues with adding a binding under the wrong context.

Apromixately commented 22 hours ago

I agree with Andrew. There needs to be some debuggability here. I am trying to remove the cmd-r (which toggles the right dock) with "cmd-r": null and adding that to Workspace and Editor did not help.

kurtbuilds commented 10 hours ago

It'd be a huge huge win to have a way to debug keybindings. Specifically, it would have two bits of functionality:

  1. See the current "context" that my cursor is in.
  2. See the actions that I just took using existing editor commands / keybindings.

That would make it much easier to rebind keys to the hotkeys that I want. Right now, the context and the keybinding actions are really undiscoverable.

@Apromixately I've seen null not working a lot of the time, so I sometimes instead remap to an action that seems to be a no-op, while still being a command. I found "workspace::Unfollow" to suit this purpose - I'm not actually sure what it's intended to do.

kurtbuilds commented 10 hours ago

One thing I'm finding surprising, it seems that "context": "Editor", has higher precedence than "context": "Editor && mode == auto_height",, so the precedence logic seems to respect "nearer" context (so Editor beats Workspace), but it doesn't respect "specificity" precedence, so "mode == auto_height" either randomly beats or has lower precedence than having no mode filter (haven't figured out if random precedence or lower precedence). Intuitively, more specific contexts should beat less specific contexts.

I'm trying to redo how assist/inline assist get opened and submitted, so I had a cmd-enter binding in context: Editor and overwrote it in context: Editor && mode == auto_height to override the value while in the inline_assist window, but that didn't actually work.

I had to move the cmd-enter binding to context: Editor && mode == full to eliminate the conflict.