commit c4295039c9c5074e0aaf15d11f6698d4cb398e0f Author: Bart Riemens Date: Thu Oct 10 13:17:57 2019 +0200 Initial commit of the qmk keymap generator diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..bc8c616 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,63 @@ +const path = require('path'); + +module.exports = { + env: { + es6: true, + node: true, + }, + extends: ['airbnb-base'], + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + }, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + }, + settings: { + 'import/resolver': { + node: { + paths: [path.resolve(__dirname)], + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + }, + }, + plugins: ['@typescript-eslint'], + rules: { + 'no-console': 0, + '@typescript-eslint/explicit-function-return-type': ['error'], + // note you must disable the base rule as it can report incorrect errors + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: false, + }, + ], + 'lines-between-class-members': 0, + 'no-plusplus': 0, + 'max-len': ['warn', 120], + 'no-restricted-syntax': 0, + 'generator-star-spacing': 0, + 'object-curly-newline': [ + 'warn', + { + // ObjectExpression: { + // multiline: true, + // minProperties: 1, + // consistent: true, + // }, + // ObjectPattern: { + // multiline: true, + // minProperties: 2, + // consistent: true, + // }, + ImportDeclaration: 'never', + ExportDeclaration: 'never', + }, + ], + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8ea77ce --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "jsxBracketSameLine": true, + "arrowParens": "always", + "singleQuote": true, + "trailingComma": "all" +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..6fa058a --- /dev/null +++ b/index.ts @@ -0,0 +1,47 @@ +/* eslint max-len: ["warn", 1000] */ +/* eslint no-multi-spaces: 0 */ +import { K, LT, MO, DF, RC, LS, RALT, LGUI } from './lib/keys'; +import row from './lib/Row'; +import layer, { ILayer } from './lib/Layer'; +import keymap from './lib/keymap'; +import { fKeys, numbers, letterRow1, letterRow2, letterRow3, KC_3, KC_4 } from './lib/groups'; + +const layer0: ILayer = layer( + row(K.ESC, ...fKeys, K.PSCR, K.SLCK, K.PAUS), + row(K.GRV, ...numbers, K.MINS, K.EQL, K.BSPC, K.INS, K.HOME, K.PGUP), + row(K.TAB, ...letterRow1, K.LBRC, K.RBRC, K.BSLS, K.DEL, K.END, K.PGDN), + row(LT(2, K.ESC), ...letterRow2, K.SCLN, K.QUOT, K.ENT), + row(K.LSFT, ...letterRow3, K.COMM, K.DOT, K.SLSH, K.RSFT, K.UP), + row(K.LCTL, K.LALT, K.LGUI, K.SPC, K.RGUI, MO(1), K.APP, K.RC, K.LEFT, K.DOWN, K.RGHT), +); + +const layer1: ILayer = layer( + row(K.ESC, ...fKeys, K.PSCR, K.SLCK, K.PAUS), + row(K.GRV, ...fKeys, K.BSPC, K.INS, K.HOME, K.PGUP), + row(K.TAB, ...letterRow1, K.LBRC, K.RBRC, K.BSLS, K.DEL, K.END, K.PGDN).replace({ [K.W]: K.UP }), + row(LT(2, K.ESC), ...letterRow2, K.SCLN, K.QUOT, K.ENT).replace({ [K.A]: K.LEFT, [K.S]: K.DOWN, [K.D]: K.RGHT }), + row(K.LSFT, ...letterRow3, K.COMM, K.DOT, K.SLSH, K.RSFT, K.UP).replace({ [K.LSFT]: [K.LSFT, K.LSFT] }), + row(K.LCTL, K.LALT, K.LGUI, K.SPC, K.RGUI, MO(1), K.APP, K.RC, K.LEFT, K.DOWN, K.RGHT), +); +/* eslint-disable-line no-multi-spaces */ +// prettier-ignore +const layer2: ILayer = layer( + row(K._, K._, K._, K._, K._, K._, K._, K._, K._, K._, K._, K._, K._, K.MUTE, K._, K._), + row(K._, K.BDWN, K.BUP, DF(0), DF(1), ...KC_3, K.MPT, K.MNT, K.MPP, K.VOLD, K.VOLU, K.BSLS, ...KC_3), + row(K._, RC(K.Q), RC(K.W), RC(K.E), RC(K.R), RC(K.T), RC(K.Y), K.PGUP, K.HOME, K.END, K.PGDN, LS(RC(K.TAB)), RC(K.TAB), ...KC_4), + row(K._, RC(K.A), RC(K.S), RC(K.D), RC(K.F), RC(K.G), K.LEFT, K.DOWN, K.UP, K.RGHT, RC(RALT(K.SCOLON)), RC(RALT(K.QUOTE)), LGUI(K.TAB)), + row(K.LSFT, RC(K.Z), RC(K.X), RC(K.C), RC(K.V), RC(K.B), RALT(K.BSPACE), K.BSPACE, K.DEL, RALT(K.DELETE), LGUI(K.F), K.RSFT, K.UP), + row(K.LCTL, K.LALT, K.LGUI, RC(RALT(K.ENT)), K.RGUI, MO(1), K.APP, K.RC, K.LEFT, K.DOWN, K.RGHT), +); + +const tkl = keymap(layer0, layer1, layer2); +console.log(tkl[1]); +/* +console.log(!!layer0, layer1, !!layer2); + +layer0.forEach((r, index) => console.log('L1: Row', index, r.length)); +console.log(); +layer1.forEach((r, index) => console.log('L2: Row', index, r.length)); +console.log(); +layer2.forEach((r, index) => console.log('L3: Row', index, r.length)); +*/ diff --git a/lib/KeyMap.ts b/lib/KeyMap.ts new file mode 100644 index 0000000..82c1463 --- /dev/null +++ b/lib/KeyMap.ts @@ -0,0 +1,50 @@ +import { ILayer, ILayers } from './Layer'; + +export interface IKeyMap extends IterableIterator { + layers: ILayers; + [index: number]: ILayer; +} + +export class KeyMap implements IKeyMap { + layers: ILayers = []; + index: number = 0; + + constructor(layers: ILayers) { + this.layers = layers; + this.index = 0; + } + + [index: number]: ILayer; + + forEach(fn: (k: ILayer, i: number) => any): void { + return this.layers.forEach(fn); + } + + map(fn: (k: ILayer, i: number) => ILayer): ILayers { + return this.layers.map(fn); + } + + get length(): number { + return this.layers.length; + } + + public next(): IteratorResult { + if (this.index < this.layers.length) { + return { + done: false, + value: this.layers[this.index++], + }; + } + return { + done: true, + value: null, + }; + } + + [Symbol.iterator](): IterableIterator { + return this; + } +} + +export const keymap = (...layers: ILayers): IKeyMap => new KeyMap(layers); +export default keymap; diff --git a/lib/Layer.ts b/lib/Layer.ts new file mode 100644 index 0000000..40bb4d0 --- /dev/null +++ b/lib/Layer.ts @@ -0,0 +1,53 @@ +import { IKeyRow, IKeyRows } from './Row'; + +export interface ILayer extends IterableIterator { + rows: IKeyRows; + index: number; + length: number; + forEach(fn: (k: IKeyRow, i: number) => any): void; + map(fn: (k: IKeyRow, i: number) => IKeyRow): IKeyRows; +} + +export type ILayers = ILayer[]; + +class Layer implements ILayer { + rows: IKeyRows = []; + index: number = 0; + + constructor(rows: IKeyRows) { + this.rows = rows; + this.index = 0; + } + + forEach(fn: (k: IKeyRow, i: number) => any): void { + return this.rows.forEach(fn); + } + + map(fn: (k: IKeyRow, i: number) => IKeyRow): IKeyRows { + return this.rows.map(fn); + } + + get length(): number { + return this.rows.length; + } + + public next(): IteratorResult { + if (this.index < this.rows.length) { + return { + done: false, + value: this.rows[this.index++], + }; + } + return { + done: true, + value: null, + }; + } + + [Symbol.iterator](): IterableIterator { + return this; + } +} + +export const layer = (...rows: IKeyRows): ILayer => new Layer(rows); +export default layer; diff --git a/lib/Row.ts b/lib/Row.ts new file mode 100644 index 0000000..3c24f26 --- /dev/null +++ b/lib/Row.ts @@ -0,0 +1,73 @@ +import { IKey, IKeys } from './keys'; + +export interface IKeyRow extends IterableIterator { + keys: IKeys; + index: number; + length: number; + forEach(fn: (k: IKey, i: number) => any): void; + map(fn: (k: IKey, i: number) => IKey): IKeys; + replace(keys: { [name: string]: IKey | IKeys }): IKeyRow; +} + +export class KeyRow implements IKeyRow { + keys: IKeys = []; + index: number = 0; + + constructor(keys: IKeys) { + this.keys = keys; + this.index = 0; + } + + forEach(fn: (k: IKey, i: number) => any): void { + return this.keys.forEach(fn); + } + + map(fn: (k: IKey, i: number) => IKey): IKeys { + return this.keys.map(fn); + } + + get length(): number { + return this.keys.length; + } + + replace(keys: { [name: string]: IKey | IKeys }): IKeyRow { + const updatedKeys: IKeys = []; + for (let i: number = 0; i < this.keys.length; i++) { + const key: IKey | IKeys = this.keys[i]; + + if (key && keys[key]) { + if (keys[key] instanceof Array) { + (keys[key]).forEach((k) => updatedKeys.push(k)); + } else { + updatedKeys.push(keys[key]); + } + } else { + updatedKeys.push(key); + } + } + this.keys = updatedKeys; + return this; + } + + public next(): IteratorResult { + if (this.index < this.keys.length) { + return { + done: false, + value: this.keys[this.index++], + }; + } + return { + done: true, + value: null, + }; + } + + [Symbol.iterator](): IterableIterator { + return this; + } +} + +export type IKeyRows = IKeyRow[]; + +export const row = (...keys: IKeys): IKeyRow => new KeyRow(keys); +export default row; diff --git a/lib/groups.ts b/lib/groups.ts new file mode 100644 index 0000000..a903c8f --- /dev/null +++ b/lib/groups.ts @@ -0,0 +1,18 @@ +import { KC, IKeys } from './keys'; + +export const fKeys: IKeys = [KC.F1, KC.F2, KC.F3, KC.F4, KC.F5, KC.F6, KC.F7, KC.F8, KC.F9, KC.F10, KC.F11, KC.F12]; + +export const KC_2 = new Array(2).fill(KC.TRNS); +export const KC_3 = new Array(3).fill(KC.TRNS); +export const KC_4 = new Array(4).fill(KC.TRNS); +export const KC_5 = new Array(5).fill(KC.TRNS); +export const KC_6 = new Array(6).fill(KC.TRNS); +export const KC_7 = new Array(7).fill(KC.TRNS); +export const KC_8 = new Array(8).fill(KC.TRNS); +export const KC_9 = new Array(9).fill(KC.TRNS); +export const KC_10 = new Array(10).fill(KC.TRNS); + +export const numbers: IKeys = [KC.N1, KC.N2, KC.N3, KC.N4, KC.N5, KC.N6, KC.N7, KC.N8, KC.N9, KC.N0]; +export const letterRow1: IKeys = [KC.Q, KC.W, KC.E, KC.R, KC.T, KC.Y, KC.U, KC.I, KC.O, KC.P]; +export const letterRow2: IKeys = [KC.A, KC.S, KC.D, KC.F, KC.G, KC.H, KC.J, KC.K, KC.L]; +export const letterRow3: IKeys = [KC.Z, KC.X, KC.C, KC.V, KC.B, KC.N, KC.M]; diff --git a/lib/keys.ts b/lib/keys.ts new file mode 100644 index 0000000..d6ef1f9 --- /dev/null +++ b/lib/keys.ts @@ -0,0 +1,132 @@ +export enum KC { + ESC = 'KC_ESC', + F1 = 'KC_F1', + F2 = 'KC_F2', + F3 = 'KC_F3', + F4 = 'KC_F4', + F5 = 'KC_F5', + F6 = 'KC_F6', + F7 = 'KC_F7', + F8 = 'KC_F8', + F9 = 'KC_F9', + F10 = 'KC_F10', + F11 = 'KC_F11', + F12 = 'KC_F12', + PSCR = 'KC_PSCR', + SLCK = 'KC_SLCK', + PAUS = 'KC_PAUS', + GRV = 'KC_GRV', + N1 = 'KC_1', + N2 = 'KC_2', + N3 = 'KC_3', + N4 = 'KC_4', + N5 = 'KC_5', + N6 = 'KC_6', + N7 = 'KC_7', + N8 = 'KC_8', + N9 = 'KC_9', + N0 = 'KC_0', + MINS = 'KC_MINS', + EQL = 'KC_EQL', + BSPC = 'KC_BSPC', + BSPACE = 'KC_BSPC', + INS = 'KC_INS', + HOME = 'KC_HOME', + PGUP = 'KC_PGUP', + TAB = 'KC_TAB', + Q = 'KC_Q', + W = 'KC_W', + E = 'KC_E', + R = 'KC_R', + T = 'KC_T', + Y = 'KC_Y', + U = 'KC_U', + I = 'KC_I', + O = 'KC_O', + P = 'KC_P', + LBRC = 'KC_LBRC', + RBRC = 'KC_RBRC', + BSLS = 'KC_BSLS', + NONUS_BSLASH = 'NONUS_BSLASH', + NUBS = 'NONUS_BSLASH', + DEL = 'KC_DEL', + DELETE = 'KC_DEL', + END = 'KC_END', + PGDN = 'KC_PGDN', + A = 'KC_A', + S = 'KC_S', + D = 'KC_D', + F = 'KC_F', + G = 'KC_G', + H = 'KC_H', + J = 'KC_J', + K = 'KC_K', + L = 'KC_L', + SCLN = 'KC_SCLN', + SCOLON = 'KC_SCLN', + QUOT = 'KC_QUOT', + QUOTE = 'KC_QUOT', + ENT = 'KC_ENT', + LSFT = 'KC_LSFT', + Z = 'KC_Z', + X = 'KC_X', + C = 'KC_C', + V = 'KC_V', + B = 'KC_B', + N = 'KC_N', + M = 'KC_M', + COMM = 'KC_COMM', + DOT = 'KC_DOT', + SLSH = 'KC_SLSH', + RSFT = 'KC_RSFT', + UP = 'KC_UP', + LCTL = 'KC_LCTL', + LC = 'KC_LCTL', + LALT = 'KC_LALT', + LGUI = 'KC_LGUI', + SPC = 'KC_SPC', + RGUI = 'KC_RGUI', + APP = 'KC_APP', + RCTL = 'KC_RCTL', + RC = 'KC_RCTL', + LEFT = 'KC_LEFT', + DOWN = 'KC_DOWN', + RGHT = 'KC_RGHT', + TRNS = 'KC_TRNS', + _ = 'KC_TRNS', + BRIGHTNESS_DOWN = 'KC_BRIGHTNESS_DOWN', + BDWN = 'KC_BRIGHTNESS_DOWN', + BRIGHTNESS_UP = 'KC_BRIGHTNESS_UP', + BUP = 'KC_BRIGHTNESS_UP', + MEDIA_PREV_TRACK = 'KC_MEDIA_PREV_TRACK', + MPT = 'KC_MEDIA_PREV_TRACK', + MEDIA_NEXT_TRACK = 'KC_MEDIA_NEXT_TRACK', + MNT = 'KC_MEDIA_NEXT_TRACK', + MEDIA_PLAY_PAUSE = 'KC_MEDIA_PLAY_PAUSE', + MPP = 'KC_MEDIA_PLAY_PAUSE', + MUTE = 'KC_MUTE', + VOLD = 'KC_VOLD', + VOLU = 'KC_VOLU', +} +export const K = KC; + +const enum KeyComboType {} +export type KeyCombo = string & KeyComboType; +const enum LayerKeyEnum {} +export type LayerKey = string & LayerKeyEnum; +export type IKey = KC | KeyCombo | LayerKey; +export type IKeys = IKey[]; + +export const LT = (layer: number, key: IKey): LayerKey => `LT(${layer}, ${key})` as LayerKey; +export const MO = (layer: number): LayerKey => `MO(${layer})` as LayerKey; +export const DF = (layer: number): LayerKey => `DF(${layer})` as LayerKey; +export const LCTL = (key: IKey): KeyCombo => `LCTL(${key})` as KeyCombo; +export const RCTL = (key: IKey): KeyCombo => `RCTL(${key})` as KeyCombo; +export const RC = RCTL; +export const LSFT = (key: IKey): KeyCombo => `LSFT(${key})` as KeyCombo; +export const LS = LSFT; +export const RSFT = (key: IKey): KeyCombo => `RSFT(${key})` as KeyCombo; +export const LALT = (key: IKey): KeyCombo => `LALT(${key})` as KeyCombo; +export const RALT = (key: IKey): KeyCombo => `RALT(${key})` as KeyCombo; +export const LGUI = (key: IKey): KeyCombo => `LGUI(${key})` as KeyCombo; +export const RGUI = (key: IKey): KeyCombo => `RGUI(${key})` as KeyCombo; diff --git a/package.json b/package.json new file mode 100644 index 0000000..0f91ca0 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "qmk-keymap-generator", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "tsc": "tsc", + "start": "ts-node-dev --respawn index.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/node": "^12.7.12", + "@typescript-eslint/eslint-plugin": "^2.3.3", + "@typescript-eslint/parser": "^2.3.3", + "eslint": "^6.5.1", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-plugin-import": "^2.18.2", + "ts-node-dev": "^1.0.0-pre.43", + "typescript": "^3.6.3" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..63b80fe --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,63 @@ +{ + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + "strictNullChecks": true /* Enable strict null checks. */, + "strictFunctionTypes": true /* Enable strict checking of function types. */, + "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, + "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, + "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + + /* Additional Checks */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +}