Skip to content

Advanced Templating 1, List Awesome Emacs Keymaps

suntong edited this page Jun 20, 2021 · 2 revisions

"easygen (and its template) is to YAML or JSON what XSLT (and its eXtensible Stylesheet Language Transformations rule) is to XML, but only more flexible and advanced".

Here is an example that illustrate so.

About Awesome Emacs Keymap

  • The Awesome Emacs Keymap is available from Visual Studio Marketplace.
  • Awesome Emacs Keymap (emacs-mcx) is a Visual Studio Code extension that provides emacs-like keybindings and operations.
  • Its keybindings is basically defined in json here.

Goal

Convert the Awesome Emacs Keymap keybindings json definition file from json to plain text.

Steps

Get the json definition file

  • Download the json definition file so that it can be consumed by easygen.
  • It is "basically" json because there are comments in the file, so need to filter out those comments.
  • In order for easygen to make use of the driving json data, all useful field names have to be in CamelCase.
  • Filter out the prefix of emacs-mcx. along the transformation too (as we don't need that part).

All above can be done in:

curl -sL https://raw.githubusercontent.com/whitphx/vscode-emacs-mcx/master/keybindings.json | sed 's| ///* *.*$||; s/keybindings/Keybindings/; s/"key/"Key/; s/"command"/"Command"/; s/emacs-mcx.//; ' > /tmp/mcx.json

Convert to plain text

  • Convert the json to plain text using easygen.
  • Translate from the Awesome Emacs Keymap keybindings naming convention to Emacs'.
  • Remove C-g from the list.
  • Prefix all startRectCommand commands with C-x r.

All above are done in:

easygen test/mcx-vscode-emacs.tmpl /tmp/mcx.json | sed 's/ctrl+/C-/g; s/meta+/M-/g; s/shift+/S-/g; /^C-g\t/d; /Rectangle/s/^/C-x r /;' | tee /tmp/mcx.lst

Explantion

The /tmp/mcx.json looks like this:

$ head -31 /tmp/mcx.json | cat -n
     1  {
     2    "Keybindings": [
     3     
     4      {
     5        "Key": "ctrl+u",
     6        "Command": "universalArgument",
     7        "when": "editorTextFocus"
     8      },
     9      {
    10        "$special": "universalArgumentTypes"
    11      },
    12     
    13      {
    14        "Keys": ["right", "ctrl+f"],
    15        "Command": "forwardChar",
    16        "whens": ["editorTextFocus", "terminalFocus"] 
    17      },
    18      {
    19        "Keys": ["right", "ctrl+f"],
    20        "Command": "isearchExit",
    21        "when": "!config.cursorMoveOnFindWidget && editorFocus && findWidgetVisible && !replaceInputFocussed && !isComposing",
    22        "args": {
    23          "then": "forwardChar"
    24        }
    25      },
    26     
    27      {
    28        "Keys": ["left", "ctrl+b"],
    29        "Command": "backwardChar",
    30        "whens": ["editorTextFocus", "terminalFocus"]
    31      },

The content of test/mcx-vscode-emacs.tmpl is:

{{range .Keybindings}}
{{- if .Command }}{{ if ne .Command "isearchExit" -}}
{{- if .Key -}}{{.Key}}	{{.Command}}
{{ else }}{{$Command := .Command }}{{range .Keys}}{{.}}	{{$Command}}
{{end}}{{end}}{{end}}{{end}}{{end}}
Notes:
  • The {{range .Keybindings}} loops over all keybindings map entries.

  • The {{- if .Command }} is necessary because there are cases that there is no "Command" key/value pair. See line 9~11 in above /tmp/mcx.json output. Without it, will get error:

    [easygen] Fatal error - template: mcx-vscode-emacs.tmpl:2:7: executing "mcx-vscode-emacs.tmpl" at <ne .Command "isearchExit">: error calling ne: incompatible types for comparison

  • Although internally the json values are mapped to interface{}, you don't need to add type assertion .(string) when using them. See example here, which can be a helpful starting point if you need to troubleshoot some weird cases like the above error.

  • Due to Awesome Emacs Keymap internal logic, the same keybindings map might show up twice, e.g. line 14 and 19 in above /tmp/mcx.json output, so need to filter out the repeated entry. That's why the {{ if ne .Command "isearchExit" -}} condition is there.

  • When Awesome Emacs Keymap keybindings only has a single key map, it'll be defined with key "Key"; however, when there are multiple key maps, then will be defined in array with the key of "Keys". Thus an if/else condition is necessary.

  • When there are multiple key maps, output each keybindings on its own, in separated lines, that's what the {{range .Keys}} loop is for.

  • The {{.Command}} can be directly used in the single key map case, however, within the {{range .Keys}} loop, the {{.Command}} is no longer accessible because it resides outside of Keys array. Thus need to define the {{$Command := .Command }} variable so that it can be used within the {{range .Keys}} loop.

Final Result

Final Result of the Awesome Emacs Keymap keybindings in plain text
C-u     universalArgument
right   forwardChar
C-f     forwardChar
left    backwardChar
C-b     backwardChar
up      previousLine
C-p     previousLine
up      selectPrevSuggestion
C-p     selectPrevSuggestion
up      showPrevParameterHint
C-p     showPrevParameterHint
down    nextLine
C-n     nextLine
down    selectNextSuggestion
C-n     selectNextSuggestion
down    showNextParameterHint
C-n     showNextParameterHint
home    moveBeginningOfLine
C-a     moveBeginningOfLine
end     moveEndOfLine
C-e     moveEndOfLine
M-f     forwardWord
M-b     backwardWord
M-m     backToIndentation
pagedown        scrollUpCommand
C-v     scrollUpCommand
pageup  scrollDownCommand
M-v     scrollDownCommand
M-S-[   backwardParagraph
M-S-]   forwardParagraph
M-S-.   endOfBuffer
M-S-,   beginningOfBuffer
M-g M-g workbench.action.gotoLine
M-g g   workbench.action.gotoLine
escape g        workbench.action.gotoLine
M-g n   editor.action.marker.next
M-g M-n editor.action.marker.next
C-x `   editor.action.marker.next
M-g p   editor.action.marker.prev
M-g M-p editor.action.marker.prev
C-l     recenterTopBottom
C-s     isearchForward
C-s     editor.action.nextMatchFindAction
C-r     isearchBackward
C-r     editor.action.previousMatchFindAction
M-S-5   editor.action.startFindReplaceAction
C-M-n   addSelectionToNextFindMatch
C-M-p   addSelectionToPreviousFindMatch
C-d     deleteForwardChar
C-h     deleteBackwardChar
M-d     killWord
M-backspace     backwardKillWord
C-k     killLine
C-S-backspace   killWholeLine
C-w     killRegion
M-w     copyRegion
C-y     yank
M-y     yank-pop
C-o     lineBreakInsert
C-m     newLine
C-j     newLine
C-x C-o deleteBlankLines
C-x h   editor.action.selectAll
C-x u   undo
C-/     undo
C-S--   undo
C-;     editor.action.commentLine
M-;     editor.action.blockComment
C-x C-l transformToLowercase
M-l     transformToLowercase
C-x C-u transformToUppercase
M-u     transformToUppercase
M-c     transformToTitlecase
M-S-6   executeCommands
escape  cancel
escape  cancel
C-space setMarkCommand
C-S-2   setMarkCommand
escape space    setMarkCommand
C-x C-x exchangePointAndMark
C-x space       rectangleMarkMode
C-x r   startRectCommand
C-x r k killRectangle
C-x r y yankRectangle
C-x r d deleteRectangle
C-x r M-w       copyRectangleAsKill
C-x r o openRectangle
C-x r c clearRectangle
C-'     editor.action.triggerSuggest
C-'     toggleSuggestionDetails
M-/     editor.action.triggerSuggest
M-/     toggleSuggestionDetails
M-x     workbench.action.showCommands
C-M-space       workbench.action.toggleSidebarVisibility
C-x C-c workbench.action.closeWindow
C-x z   workbench.action.toggleZenMode
C-x C-f workbench.action.quickOpen
C-x C-s workbench.action.files.save
C-x C-w workbench.action.files.saveAs
C-x s   workbench.action.files.saveAll
C-x C-n workbench.action.newWindow
C-x b   workbench.action.showAllEditorsByMostRecentlyUsed
C-x k   workbench.action.closeActiveEditor
C-x ctrl-k      workbench.action.closeAllEditors
C-x 0   workbench.action.closeEditorsInGroup
C-x 1   workbench.action.closeEditorsInOtherGroups
C-x 2   workbench.action.splitEditorDown
C-x 3   workbench.action.splitEditorRight
C-x 4   workbench.action.toggleEditorGroupLayout
C-x o   workbench.action.navigateEditorGroups
C-M-f   paredit.forwardSexp
C-M-b   paredit.backwardSexp
C-M-k   paredit.killSexp
C-p     selectPrevQuickFix
C-n     selectNextQuickFix
C-p     workbench.action.quickOpenSelectPrevious
C-n     workbench.action.quickOpenSelectNext
C-m     workbench.action.acceptSelectedQuickOpenItem
C-S-'   editor.action.triggerParameterHints
C-x j   workbench.action.togglePanel
C-i     executeCommands