Advanced Templating 1, List Awesome Emacs Keymaps - go-easygen/easygen GitHub Wiki
"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.
- 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.
Convert the Awesome Emacs Keymap keybindings json definition file from json to plain text.
- 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
easygento 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 the json to plain text using
easygen. - Translate from the Awesome Emacs Keymap keybindings naming convention to Emacs'.
- Remove
C-gfrom the list. - Prefix all
startRectCommandcommands withC-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
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}}
-
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.jsonoutput. 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
14and19in above/tmp/mcx.jsonoutput, 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 anif/elsecondition 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 ofKeysarray. Thus need to define the{{$Command := .Command }}variable so that it can be used within the{{range .Keys}}loop.
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