forked from zmkfirmware/zmk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(keymap-upgrader): Upgrade encoder resolution
Added an upgrade function to the keymap upgrader to replace the encoder "resolution" property with "steps" and (if it is not already present) "triggers-per-rotation".
- Loading branch information
1 parent
be75da0
commit 3a4cf18
Showing
3 changed files
with
171 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import type { SyntaxNode, Tree } from "web-tree-sitter"; | ||
|
||
import { | ||
getContainingDevicetreeNode, | ||
getDevicetreeNodePath, | ||
findDevicetreeProperty, | ||
} from "./parser"; | ||
import { TextEdit } from "./textedit"; | ||
|
||
const ALPS_EC11_COMPATIBLE = '"alps,ec11"'; | ||
const DEFAULT_RESOLUTION = 4; | ||
const TRIGGERS_PER_ROTATION = 20; | ||
const TRIGGERS_PER_ROTATION_DT = ` | ||
&sensors { | ||
// Change this to your encoder's number of detents. | ||
// If you have multiple encoders with different detents, see | ||
// https://zmk.dev/docs/config/encoders#keymap-sensor-config | ||
triggers-per-rotation = <${TRIGGERS_PER_ROTATION}>; | ||
};`; | ||
|
||
export function upgradeEncoderResolution(tree: Tree) { | ||
const edits: TextEdit[] = []; | ||
|
||
const resolutionProps = findEncoderResolution(tree); | ||
edits.push(...resolutionProps.flatMap(upgradeResolutionProperty)); | ||
|
||
if (resolutionProps.length > 0) { | ||
edits.push(...addTriggersPerRotation(tree)); | ||
} | ||
|
||
return edits; | ||
} | ||
|
||
function findEncoderResolution(tree: Tree): SyntaxNode[] { | ||
const props = findDevicetreeProperty(tree.rootNode, "resolution", { | ||
recursive: true, | ||
}); | ||
|
||
return props.filter((prop) => { | ||
const node = getContainingDevicetreeNode(prop); | ||
return node && isEncoderNode(node); | ||
}); | ||
} | ||
|
||
function isEncoderNode(node: SyntaxNode) { | ||
// If a compatible property is set, then we know for sure if this is an encoder. | ||
const compatible = findDevicetreeProperty(node, "compatible"); | ||
if (compatible) { | ||
return compatible.childForFieldName("value")?.text === ALPS_EC11_COMPATIBLE; | ||
} | ||
|
||
// Compatible properties rarely appear in a keymap though, so just guess based | ||
// on the node path/reference otherwise. | ||
return getDevicetreeNodePath(node).toLowerCase().includes("encoder"); | ||
} | ||
|
||
function upgradeResolutionProperty(prop: SyntaxNode): TextEdit[] { | ||
const name = prop.childForFieldName("name"); | ||
const value = prop.childForFieldName("value"); | ||
|
||
if (!name || !value) { | ||
return []; | ||
} | ||
|
||
// Try to set the new steps to be triggers-per-rotation * resolution, but fall | ||
// back to a default if the value is something more complex than a single int. | ||
const resolution = value.text.trim().replaceAll(/^<|>$/g, ""); | ||
const steps = | ||
(parseInt(resolution) || DEFAULT_RESOLUTION) * TRIGGERS_PER_ROTATION; | ||
|
||
const hint = `/* Change this to your encoder's number of detents times ${resolution} */`; | ||
|
||
return [ | ||
TextEdit.fromNode(name, "steps"), | ||
TextEdit.fromNode(value, `<${steps}> ${hint}`), | ||
]; | ||
} | ||
|
||
function addTriggersPerRotation(tree: Tree): TextEdit[] { | ||
// The keymap might already contain "triggers-per-rotation" for example if the | ||
// user already upgraded some but not all "resolution" properties. Don't add | ||
// another one if it already exists. | ||
if (keymapHasTriggersPerRotation(tree)) { | ||
return []; | ||
} | ||
|
||
// Inserting a new property into an existing node while keeping the code | ||
// readable in all cases is hard, so just append a new &sensors node to the | ||
// end of the keymap. | ||
const end = tree.rootNode.endIndex; | ||
return [new TextEdit(end, end, TRIGGERS_PER_ROTATION_DT)]; | ||
} | ||
|
||
function keymapHasTriggersPerRotation(tree: Tree) { | ||
const props = findDevicetreeProperty(tree.rootNode, "triggers-per-rotation", { | ||
recursive: true, | ||
}); | ||
|
||
return props.length > 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters