Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document keymap context #14718

Closed
1 task done
bersace opened this issue Jul 18, 2024 · 28 comments
Closed
1 task done

Document keymap context #14718

bersace opened this issue Jul 18, 2024 · 28 comments
Labels
documentation [core label] enhancement [core label] keymap / key binding Feedback for keyboard shortcuts, key mapping, etc

Comments

@bersace
Copy link

bersace commented Jul 18, 2024

Check for existing issues

  • Completed

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:

  • What is mode ?
  • What context when no file are opens ?
  • What is Markdown ?
  • How to know the current context ?
  • How to know available shortcut in a context ? Doom Emacs has a bottom drawer showing next shortcut.

Regards,
Étienne

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

No response

@bersace bersace added admin read Pending admin review enhancement [core label] triage Maintainer needs to classify the issue labels Jul 18, 2024
@notpeter
Copy link
Member

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.

@notpeter notpeter added documentation [core label] keymap / key binding Feedback for keyboard shortcuts, key mapping, etc and removed triage Maintainer needs to classify the issue admin read Pending admin review labels Jul 18, 2024
@bersace
Copy link
Author

bersace commented Jul 18, 2024

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
Copy link
Author

bersace commented Jul 18, 2024

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

@bersace
Copy link
Author

bersace commented Jul 18, 2024

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
Copy link
Author

bersace commented Jul 18, 2024

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
Copy link
Author

bersace commented Jul 18, 2024

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
Copy link

neunato commented Jul 27, 2024

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:

  • context represents the focus location within the cascading hierarchy of graphical elements

  • the last keybind definition for a key wins

  • any action can be called from within any context, but not all actions will work from all contexts

  • when an action for a keybind cannot be applied, it falls back to the previous one

  • an immediate child of an element can be selected with Parent > Child

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:

  • the above explanations present in keybind documentation
  • the context hierarchy documented
  • the commands documented and accessible with inline completions, to provide an experience similar to settings
  • a better way of specifying multiple commands for same key and context
  • better organisation in the default keybinds

A few observations:

  • Editor && mode == full vs Editor && mode == auto_height actually has nothing to do with multibuffers.
    From my testing, it looks like Editor includes everything you can type in - from menu and find panel to rename box in sidebar.
    • mode == full seems to include any tab (?), including search tab
    • mode == auto_height every other typable element?
  • ProjectSearchView no matter what the name implies couldn't apply any keybind
  • Workspace is always active? same as global then

And some additional suggestions:

  • non-direct child context selector
  • binding a chain of commands to a key

@test3211234
Copy link

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:

#15401

@malssid
Copy link

malssid commented Aug 26, 2024

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"
    }
  }
]

@aurexav
Copy link
Contributor

aurexav commented Aug 28, 2024

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
Copy link

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

@baptisteArno
Copy link

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
Copy link

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
Copy link

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
Copy link
Member

notpeter commented Oct 6, 2024

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 :

{
"context": "Workspace && !Terminal",
"bindings": {
"alt-t": "task::Rerun",
"alt-shift-t": "task::Spawn"
}
},

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
Copy link

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
Copy link

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
Copy link

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
Copy link
Contributor

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
Copy link
Contributor

kurtbuilds commented Oct 16, 2024

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.

@kurtbuilds
Copy link
Contributor

Detailing another issue with key binding resolution:

I'm coming from intellij, where cmd+enter is "open code actions menu". In editor, cmd-enter creates a newline, and in the assistant, cmd-enter submits the query to the assistant. Looking at default macos keybindings, cmd-enter exists in both Editor && mode == full and ContextEditor > Editor contexts, which the commands you'd expect for this behavior, EnterNewLine and assistant::Assist, respectively.

Ok, then, I just need a custom keybinding for Editor && mode == full to map cmd-enter to ToggleCodeActions. I do that, and it works in the editor (good), but it disables any action in the Assistant. Ok, that's weird, but maybe I can fix it by adding an explicit "assistant::Assist" to ContextEditor > Editor back again. No, that doesn't do anything. Okay, what about the AssistantPanel context instead? No, that also doesn't do anything.

One short-term solution, is it possible to add debug or trace log lines when keybindings are dispatched, so that I can launch Zed with that log level, and see both the context and the command being executed?

@kurtbuilds
Copy link
Contributor

kurtbuilds commented Oct 17, 2024

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 ?

@leo91000 After lots of trial and error, I found that Editor && mode == full && !(ContextEditor > Editor) works for "only the main editor". Notably !AssistantPanel does not work. Quick explainer for each clause: Editor is a text entry editor (can be inline assistant, the main editor, assistant panel, maybe others). mode == full contrasts with auto_height, which implies the inline assistant, so mode == full means a full height editor (either main editor or assistant panel), and finally !(ContextEditor > Editor) excludes the assistant panel. I don't know why, other than a bug that !AssistantPanel wouldn't work just as well. I tried !(AssistantPanel > Editor) and that also doesn't work.

Summary

Quick summary of the rules I've discovered so far, for myself and others:

  1. Precedence works based on hierarchy (so Editor beats Workspace), but doesn't appear to work on "narrowing" of any kind, so Editor && mode == full does not beat Editor, and ContextEditor > Editor does not beat Editor.
  2. Precedence seems to be hardcoded for system hotkeys like cmd-o. I had to create a global keybinding for cmd-o instead of creating it at context Workspace.
  3. null doesn't always clear a hotkey, neither does zed::NoAction. You can use another no-op action (I use workspace::Unfollow - which is what escape does) to clear a binding. This seems especially true for system hotkeys like cmd-o and cmd-p.

Quick summary of contexts I'm using:

  1. ContextEditor > Editor is the assistant panel. Using AssistantPanel is at best flakey.
  2. Editor && mode == auto_height is the inline assistant panel.
  3. Editor && mode == full && !(ContextEditor > Editor) should be your context for the main editor. It excludes code completion menus, the inline assistant, the assistant panel.

Lastly, it's worth noting that the internal keybindings seem to have different precedence rules. The example I described in my previous post is one example. Looking at cmd-enter, builtin keybindings have EnterNewLine for Editor && mode == full and assistant::Assist for ContextEditor > Editor. However, changing those commands using the same directives doesn't work for user keybindings. I instead have to use Editor && mode == full && !(ContextEditor > Editor), otherwise the bindings conflict.

@lcrownover
Copy link

The lack of clear documentation around contexts is the last thing keeping me from adopting Zed over Neovim for most editing sessions. I use a 99% keyboard-centric workflow and I, for the life of me, can't get some of my simple keybinding cases to work.

Having a debug window that displays the current context would be immensely helpful, as well as documentation around precedence.

@opsnull
Copy link

opsnull commented Nov 15, 2024

@lcrownover try command "debug: open key context view",I find it's very helpful to view UI context stack and keybinding matching.
image

@ConradIrwin
Copy link
Member

Closing this now the documentation is more up-to-date and the debug view exists.

@KorigamiK
Copy link
Contributor

I also want to know the meaning of use_key_equivalents in the keymaps file as well

@notpeter
Copy link
Member

I also want to know the meaning of use_key_equivalents in the keymaps file as well

See: https://zed.dev/docs/key-bindings#non-qwerty-keyboards

@KorigamiK
Copy link
Contributor

See: https://zed.dev/docs/key-bindings#non-qwerty-keyboards

got it, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation [core label] enhancement [core label] keymap / key binding Feedback for keyboard shortcuts, key mapping, etc
Projects
None yet
Development

No branches or pull requests