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
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 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 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.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
and19
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 anif/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 ofKeys
array. 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