Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
pokey committed Nov 24, 2023
1 parent 65fbf6c commit 13885f3
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 52 deletions.
41 changes: 0 additions & 41 deletions packages/cursorless-vscode/src/keyboard/KeyboardCommandHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export class KeyboardCommandHandler {
}
}

// ================ Argument types ================
interface DecoratedMarkArg {
decoratedMark: {
color?: HatColor;
Expand All @@ -106,43 +105,3 @@ interface Offset {
direction?: "forward" | "backward" | null;
number?: number | null;
}

// ================= Type helpers =================
/**
* Maps from the name of a method in KeyboardCommandHandler to the type of its
* argument.
*/
export type KeyboardCommandArgTypes = {
[K in keyof KeyboardCommandHandler]: KeyboardCommandHandler[K] extends (
arg: infer T,
) => void
? T
: never;
};

export type KeyboardCommandTypeMap = {
[K in keyof KeyboardCommandHandler]: {
type: K;
arg: KeyboardCommandArgTypes[K];
};
};

export type KeyboardCommand<T extends keyof KeyboardCommandHandler> = {
type: T;
arg: KeyboardCommandArgTypes[T];
};

// Ensure that all methods in KeyboardCommandHandler take an object as their
// first argument, and return void or Promise<void>. Note that the first check
// may look backwards, because the arg type is contravariant, so the 'extends'
// needs to be flipped.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function assertExtends<A extends B, B>() {}
assertExtends<
Record<keyof KeyboardCommandArgTypes, (arg?: object) => never>,
Pick<KeyboardCommandHandler, keyof KeyboardCommandArgTypes>
>;
assertExtends<
Pick<KeyboardCommandHandler, keyof KeyboardCommandArgTypes>,
Record<keyof KeyboardCommandArgTypes, (arg: never) => void | Promise<void>>
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { KeyboardCommandHandler } from "./KeyboardCommandHandler";

/**
* Maps from the name of a method in KeyboardCommandHandler to the type of its
* argument.
*/
export type KeyboardCommandArgTypes = {
[K in keyof KeyboardCommandHandler]: KeyboardCommandHandler[K] extends (
arg: infer T,
) => void
? T
: never;
};

export type KeyboardCommandTypeMap = {
[K in keyof KeyboardCommandHandler]: {
type: K;
arg: KeyboardCommandArgTypes[K];
};
};

export type KeyboardCommand<T extends keyof KeyboardCommandHandler> = {
type: T;
arg: KeyboardCommandArgTypes[T];
};

// Ensure that all methods in KeyboardCommandHandler take an object as their
// first argument, and return void or Promise<void>. Note that the first check
// may look backwards, because the arg type is contravariant, so the 'extends'
// needs to be flipped.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function assertExtends<A extends B, B>() {}
assertExtends<
Record<keyof KeyboardCommandArgTypes, (arg?: object) => never>,
Pick<KeyboardCommandHandler, keyof KeyboardCommandArgTypes>
>;
assertExtends<
Pick<KeyboardCommandHandler, keyof KeyboardCommandArgTypes>,
Record<keyof KeyboardCommandArgTypes, (arg: never) => void | Promise<void>>
>;
25 changes: 25 additions & 0 deletions packages/cursorless-vscode/src/keyboard/getTokenTypeKeyMaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ import {
getSectionEntries,
} from "./sectionKeyMapExtractors";

/**
* Returns a map from token type names to a keymap for that token type. Something like:
*
* ```ts
* {
* action: {
* "c": {
* type: "action",
* value: "clearAndSetSelection",
* },
* "t": {
* type: "action",
* value: "setSelection",
* },
* },
* makeRange: {
* "r": {
* type: "makeRange",
* },
* },
* ...
* }
* ```
* @returns A map from token type names to a keymap for that token type.
*/
export function getTokenTypeKeyMaps(): TokenTypeKeyMapMap {
const misc = getSectionKeyMapRaw("misc");
const modifier = getSectionKeyMapRaw("modifier");
Expand Down
18 changes: 11 additions & 7 deletions packages/cursorless-vscode/src/keyboard/grammar/MyPostProcess.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { KeyboardCommandArgTypes } from "../KeyboardCommandHandler";
import {
KeyboardCommand,
KeyboardCommandArgTypes,
} from "../KeyboardCommandTypeHelpers";

/**
* Represents a post-processing function in our grammar. This is a function that
* takes the output of a rule and transforms it into a different format. We also
* keep metadata on the rule so that we can display information about it to the
* user. The reason we keep the metadata here is that the postprocess function
* is the only thing we have control over in nearley.
* Represents a post-processing function for a top-level rule of our grammar.
* This is a function that takes the output of a rule and transforms it into a
* command usable by our command handler. We also keep metadata about the rule
* on the postprocess function so that we can display it to the user, eg in the
* sidebar. The reason we keep the metadata here is that the postprocess
* function is the only thing we have control over in the nearley parser.
*/
export interface MyPostProcess<
T extends keyof KeyboardCommandArgTypes = keyof KeyboardCommandArgTypes,
> {
(args: any[]): any;
(args: any[]): KeyboardCommand<T>;
metadata: {
/** The command type */
type: T;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import grammar from "./generated/grammar";
import assert from "assert";
import { KeyDescriptor } from "../TokenTypeHelpers";
import {
KeyboardCommand,
KeyboardCommandHandler,
} from "../KeyboardCommandHandler";
import { KeyboardCommand } from "../KeyboardCommandTypeHelpers";

interface TestCase {
tokens: KeyDescriptor[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { KeyboardCommandArgTypes } from "../KeyboardCommandHandler";
import { KeyboardCommandArgTypes } from "../KeyboardCommandTypeHelpers";
import { MyPostProcess } from "./MyPostProcess";

function constructPayload(args: any[], argNames: (string | null)[]) {
Expand All @@ -18,7 +18,13 @@ export function command<T extends keyof KeyboardCommandArgTypes>(
argNames: (keyof KeyboardCommandArgTypes[T] | null)[],
): MyPostProcess<T> {
function ret(args: any[]) {
return { type, arg: constructPayload(args, argNames as (string | null)[]) };
return {
type,
arg: constructPayload(
args,
argNames as (string | null)[],
) as KeyboardCommandArgTypes[T],
};
}
ret.metadata = { type, argNames };
return ret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { KeyMap, SectionName, TokenType } from "./TokenTypeHelpers";
import { SectionTypes } from "./TokenTypes";
import { defaultMap } from "./defaultKeyMaps";

/**
* Returns a keymap for a given config section that is intended to be further
* processed by eg {@link getSectionEntries} or {@link getSingularSectionEntry}.
* @param sectionName The name of the config section
* @returns A keymap for a given config section
*/
export function getSectionKeyMapRaw<S extends SectionName>(
sectionName: S,
): KeyMap<SectionTypes[S]> {
Expand All @@ -18,6 +24,22 @@ export function getSectionKeyMapRaw<S extends SectionName>(
return merge({}, defaultKeyMap, userOverrides);
}

/**
* Returns a keymap for a given config section. For example:
*
* ```typescript
* assert.equal(
* getSectionKeyMap("colors", "color"),
* {
* "r": { type: "color", value: "red" },
* "g": { type: "color", value: "green" },
* }
* );
* ```
*
* @param sectionName The name of the config section
* @returns A keymap for a given config section
*/
export function getSectionKeyMap<S extends SectionName, T extends TokenType>(
sectionName: S,
type: T,
Expand All @@ -34,8 +56,28 @@ export function getSectionKeyMap<S extends SectionName, T extends TokenType>(
);
}

/**
* Returns a keymap with entries only for the given value. This map will usually
* just contain a single entry, but it's possible for a user to have aliases for
* a given value, in which case it will contain multiple entries.
*
* Example:
*
* ```ts
* assert.equal(
* getSingularSectionEntry(getSectionKeyMapRaw("misc"), "makeRange"),
* {
* "r": { type: "makeRange" },
* },
* );
* ```
*
* @param keyMap The keymap for the given config section
* @param value The value to get the entries for
* @returns A keymap with entries only for the given value
*/
export function getSingularSectionEntry<
K extends keyof SectionTypes,
K extends SectionName,
V extends SectionTypes[K] & TokenType,
>(keyMap: KeyMap<SectionTypes[K]>, value: V): KeyMap<{ type: V }> {
return mapValues(
Expand All @@ -46,6 +88,25 @@ export function getSingularSectionEntry<
);
}

/**
* Returns a keymap with a subset of entries from a given config section.
*
* Example:
*
* ```ts
* assert.equal(
* getSingularSectionEntry(getSectionKeyMapRaw("misc"), "direction", ["forward", "backward"]),
* {
* "f": { type: "direction", value: "forward" },
* "b": { type: "direction", value: "backward" },
* },
* );
* ```
*
* @param keyMap The keymap for the given config section
* @param value The value to get the entries for
* @returns A keymap with entries only for the given value
*/
export function getSectionEntries<
K extends keyof SectionTypes,
T extends TokenType,
Expand Down

0 comments on commit 13885f3

Please sign in to comment.