typescriptvisual-studio-codevscode-keybinding

Is there an easy way to access the Display name (title) of each command listed in the Keyboard Shortcuts JSON file?


I'm trying to write a script which converts the user's keyboard shortcuts into flashcards. I want to essentially make a list of (shortcut, action title) for each shortcut.

When I open the default shortcut JSON, I see (key, command, when) for each shortcut. For example:

{ "key": "escape escape", "command": "workbench.action.exitZenMode", "when": "inZenMode" },

This is perfect, except that I want the command to be human-readable. When I open the "Keyboard Shortcut" editor, shortcuts are listed by English name, for example:

"Add Cursor Above" - "Ctrl + Alt + UpArrow" - "editorTextFocus"

This is what I want, but I can't export these.

I dug through the repo on GitHub. It looks like the commands are each defined in their own file, and each one has a property called title that contains the English name, but I couldn't figure out how the Keyboard Shortcut editor accesses each of these commands and displays their title (I'm not a typescript pro). What's the best way to get a list of keyboard command English names?


Solution

  • First of all, I'm pretty sure you don't actually want the contents of the default keybindings JSON pseudo file. I assume that if you want flashcards, you want to help someone remember their keybindings, along with any customizations they've made to them.

    I dunno about easy or scriptable, but... here goes (the best I can think of right now):

    1. Enable and activate all extensions that contribute keybindings that you want to be part of the result. Extensions have activation events (so they don't get loaded unless they are actually needed).

    2. Open the VS Code devtools (Developer: Toggle Developer Tools in the command palette)

    3. Open the "Sources" tab and go open "Electron Isolated Context" > ".../resources/app" (or whatever the path to app resources is for your VS Code installation) > out/vs/workbench/workbench.desktop.main.js.

    4. ctrl/cmd+f and search for getKeybindings() { and put a breakpoint on the results. Ex. for me, I got two search results, and put the breakpoint here (you may see something slightly different because this is minified code):

      getKeybindings() {
          return this.e // <- put breakpoint here
      }
      

      The breakpoint will be rendered in a prettified / source-mapped(?) view of the code.

    5. Go back to the actual VS Code window, and now open the keyboard shortcuts UI (Preferences: Open Keyboard Shortcuts in the command palette). This should trigger the breakpoint.

    6. In the devtools' "Sources" tab, press the "Step out of current function" button twice, which should take you into a function called resolve in keybindingsEditorModel.ts.

    7. Right click the line that declares const commandsWithDefaultKeybindings and click "Continue to here". Note that if you actually want to get default keybindings instead of user keybindings + non-overridden default keybindings, then the for loop that follows this is the one that initializes the data you want.

    8. In the right sidebar, under the "Scope" section, under its "Local" section, look for a property of this that is an array with (probably lots of) stuff in it. The property names will be minified. In the original source code, the name of what we're looking for is _keybindingItemsSortedByPrecedence. Once you find it, right click it and select "Store object as global variable". You'll see a new variable in the console panel, probably named something like temp1.

    9. In the console, run temp1.map(e => ({command:e.commandLabel,keyChord:e.keybindingItem.chords})).filter(e=>e.command!=="") (or whatever transformation you want to get the properties you want and in whatever format you want) The filter removes mappings that just have "" as their label. I think those mappings are just for keys that aren't bound to a command and just "insert" themselves / do the normal thing they would do in any application.

    10. right click the result and select "Copy object".

    11. Click "continue" in the debugger

    12. Paste the clipboard contents wherever you want and do whatever you want with the result (it's JSON).

    This would probably be a lot easier if there was / I could find an extension API to get keybindings, but I looked and didn't find one. (Maybe I just missed it?)

    If you want to filter by the source (what registers the keybinding), then you can do that too. Ex. to filter for keybindings written by the user: temp1.filter(e=>e.source==="User") .... To get a list of sources, use new Set(temp1.map(e=>e.source)). Note that some of the sources are objects instead of strings (Ex. objects about extension sources as opposed to "System" or "User").


    If you have any interest in extension API access, see API: Expose list of keybindings #162433. It's closed for not receiving enough community support in time, but you can try it again and try to get enough support for it.