package main
import (
"io/ioutil"
"fmt"
"encoding/json"
"os"
"path/filepath"
"strings"
"sort"
"hash/crc64"
)
func main() {
if len(os.Args) < 3 {
fmt.Println("Usage: ./keymap-gen src-dir out-dir")
fmt.Println("Outputs c files in out-dir")
fmt.Println("Make sure the dirs exist.")
return
}
files, err := filepath.Glob(os.Args[1] + "/*.json")
if err != nil {
fmt.Printf("Could not open src-dir: %v\n", err)
return
}
for _, fname := range(files) {
fmt.Println("Processing: ", fname)
data, err := ioutil.ReadFile(fname)
if err != nil {
panic(err)
}
var FullDict map[string]Entry
json.Unmarshal(data, &FullDict)
var output []string
for i,v := range FullDict {
if i == "0" {
continue
}
var entry string
if strings.Contains(fname, "num") {
entry = v.toKeymap("NUM|")
} else {
entry = v.toKeymap("")
}
if entry != "" {
output = append(output, entry)
}
}
sort.Slice(output, func (i,j int) bool {
var maxLen int
if len(output[i]) > len(output[j]) {
maxLen = len(output[i])
} else {
maxLen = len(output[j])
}
return maxLen-strings.Count(output[i][:40], " ") < maxLen-strings.Count(output[j][:40], " ")
})
output = append([]string{"// This file is automatically generated. Do not edit it!\n\n"}, output...)
outName := filepath.Base(fname)
outName = os.Args[2] + outName[:len(outName)-5]+".def"
fmt.Println("Saving: ", outName)
ioutil.WriteFile(outName, []byte(strings.Join(output, "")), 0755)
}
}
func (e Entry) toKeymap(prefix string) (string) {
var command, chord, arg string
wordInfo := parseWords(e)
if prefix != "" {
chord = prefix
}
keys := []string{"AA", "AS", "AE", "AT", "AN", "AI", "AO", "AP"}
for i, v := range e.Input {
chord += keys[v-1]
if i != len(e.Input)-1 {
chord += "|"
}
}
var ok bool
var v []string
if e.Special != "" {
v, ok = QMKLookup[e.Special]
}
if !ok && e.Base != "" {
v, ok = QMKLookup[e.Base]
}
if ok {
if len(v) == 1 {
command = "PRES("
} else {
command = "KEYS("
}
}
if ok {
if len(v) > 1 {
arg += "{"
}
for ii, vv := range(v) {
arg += vv
if ii == len(v)-1 && len(v) > 1 {
arg += ", COMBO_END}"
} else if ii != len(v)-1 {
arg += ", "
}
}
hash := crc64.Checksum([]byte(fmt.Sprintf("%v%v", arg, chord)), crc64.MakeTable(crc64.ECMA))
hashStr := fmt.Sprintf("cmb_%x", hash)
wordSpacer := strings.Repeat(" ", 42-len(arg))
if command == "KEYS(" {
arg = fmt.Sprintf("%v, %v %v", hashStr, wordSpacer, arg)
} else {
arg = fmt.Sprintf("%65v", arg)
}
goto Found
}
if wordInfo.LRank == 0 && wordInfo.RRank == 0 {
goto Blank
}
if wordInfo.LRank != 0 || wordInfo.RRank != 0 {
if wordInfo.LRank != 0 && wordInfo.RRank != 0 {
left, right := e, e
left.Trw = nil
right.Tlw = nil
return fmt.Sprintf("%v%v", left.toKeymap("LFT|"), right.toKeymap("RGT|"))
}
var word string
if wordInfo.LRank > wordInfo.RRank {
word = wordInfo.LWord
} else {
word = wordInfo.RWord
}
chord = "AR|" + chord
hash := crc64.Checksum([]byte(word), crc64.MakeTable(crc64.ECMA))
hashStr := fmt.Sprintf("str_%016X", hash)
command = "SUBS("
wordSpacer := strings.Repeat(" ", 40-len(word))
arg = fmt.Sprintf("%v, %v \"%v \"", hashStr, wordSpacer, word)
goto Found
}
panic(e.String())
Found:
chord += ","
return fmt.Sprintf("%v%-35v%v)\n", command, chord, arg)
Blank:
return ""
}
type Entry struct {
Input []int
Base string
Tlw []interface{}
Trw []interface{}
Special string
}
type Word struct {
LWord string
LRank float64
RWord string
RRank float64
}
func parseWords(e Entry) (ret Word) {
if len(e.Tlw) > 0 {
ret.LWord = e.Tlw[0].(string)
ret.LRank= e.Tlw[1].(float64)
}
if len(e.Trw) > 0 {
ret.RWord = e.Trw[0].(string)
ret.RRank= e.Trw[1].(float64)
}
return ret
}
func (e Entry) String() (ret string) {
ret = fmt.Sprintln("Input: ", e.Input)
ret += fmt.Sprintln("Base: ", e.Base)
ret += fmt.Sprintln("Left: ", e.Tlw)
ret += fmt.Sprintln("Right: ", e.Trw)
ret += fmt.Sprintln("Special: ", e.Special)
return ret
}
var QMKLookup = map[string][]string {
"!":[]string{"KC_LSFT", "KC_1"},
"'":[]string{"KC_QUOT"},
"(":[]string{"KC_LSFT", "KC_9"},
")":[]string{"KC_LSFT", "KC_0"},
",":[]string{"KC_COMM"},
"-":[]string{"KC_MINS"},
".":[]string{"KC_DOT"},
";":[]string{"KC_SCLN"},
"?":[]string{"KC_QUOT"},
"a":[]string{"KC_A"},
"b":[]string{"KC_B"},
"c":[]string{"KC_C"},
"d":[]string{"KC_D"},
"e":[]string{"KC_E"},
"f":[]string{"KC_F"},
"g":[]string{"KC_G"},
"h":[]string{"KC_H"},
"i":[]string{"KC_I"},
"j":[]string{"KC_J"},
"k":[]string{"KC_K"},
"l":[]string{"KC_L"},
"m":[]string{"KC_M"},
"n":[]string{"KC_N"},
"o":[]string{"KC_O"},
"p":[]string{"KC_P"},
"q":[]string{"KC_Q"},
"r":[]string{"KC_R"},
"s":[]string{"KC_S"},
"t":[]string{"KC_T"},
"u":[]string{"KC_U"},
"v":[]string{"KC_V"},
"w":[]string{"KC_W"},
"x":[]string{"KC_X"},
"y":[]string{"KC_Y"},
"z":[]string{"KC_Z"},
"bksp":[]string{"KC_BSPC"},
"enter":[]string{"KC_ENT"},
"[":[]string{"KC_LBRC"},
"]":[]string{"KC_RBRC"},
" ":[]string{"KC_SPC"},
"1":[]string{"KC_1"},
"2":[]string{"KC_2"},
"3":[]string{"KC_3"},
"4":[]string{"KC_4"},
"5":[]string{"KC_5"},
"6":[]string{"KC_6"},
"7":[]string{"KC_7"},
"8":[]string{"KC_8"},
"9":[]string{"KC_9"},
"0":[]string{"KC_0"},
"=":[]string{"KC_EQL"},
"Fn":[]string{"KC_NO"},
"SPACE":[]string{"KC_SPC"},
"Home":[]string{"KC_HOME"},
"End":[]string{"KC_END"},
" ":[]string{"KC_TAB"},
" ":[]string{"KC_TAB"},
"\t":[]string{"KC_TAB"},
"`":[]string{"KC_GRV"},
}