Skip to content

Commit

Permalink
Create modifier rule in keyboard grammar
Browse files Browse the repository at this point in the history
  • Loading branch information
pokey committed Jan 15, 2024
1 parent 8e046a8 commit 9fc74ca
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 167 deletions.
69 changes: 6 additions & 63 deletions packages/cursorless-vscode/src/keyboard/KeyboardCommandHandler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ScopeType, SurroundingPairName } from "@cursorless/common";
import { Modifier, SurroundingPairName } from "@cursorless/common";
import * as vscode from "vscode";
import { HatColor, HatShape } from "../ide/vscode/hatStyles.types";
import { SimpleKeyboardActionType } from "./KeyboardActionType";
Expand Down Expand Up @@ -28,22 +28,8 @@ import { surroundingPairsDelimiters } from "@cursorless/cursorless-engine";
export class KeyboardCommandHandler {
constructor(private targeted: KeyboardCommandsTargeted) {}

targetDecoratedMarkReplace({ decoratedMark }: DecoratedMarkArg) {
this.targeted.targetDecoratedMark(decoratedMark);
}

targetDecoratedMarkExtend({ decoratedMark }: DecoratedMarkArg) {
this.targeted.targetDecoratedMark({
...decoratedMark,
mode: "extend",
});
}

targetDecoratedMarkAppend({ decoratedMark }: DecoratedMarkArg) {
this.targeted.targetDecoratedMark({
...decoratedMark,
mode: "append",
});
targetDecoratedMark({ decoratedMark, mode }: DecoratedMarkArg) {
this.targeted.targetDecoratedMark({ ...decoratedMark, mode });
}

async vscodeCommand({
Expand Down Expand Up @@ -86,9 +72,6 @@ export class KeyboardCommandHandler {
this.targeted.performSimpleActionOnTarget(actionName);
}

modifyTargetContainingScope(arg: { scopeType: ScopeType }) {
this.targeted.modifyTargetContainingScope(arg);
}
performWrapActionOnTarget({ delimiter }: { delimiter: SurroundingPairName }) {
const [left, right] = surroundingPairsDelimiters[delimiter]!;
this.targeted.performActionOnTarget((target) => ({
Expand All @@ -99,35 +82,8 @@ export class KeyboardCommandHandler {
}));
}

targetEveryScopeType(arg: { scopeType: ScopeType }) {
this.targeted.modifyTargetContainingScope({ ...arg, type: "everyScope" });
}

targetRelativeExclusiveScope({
offset,
length,
scopeType,
}: TargetRelativeExclusiveScopeArg) {
this.targeted.targetModifier({
type: "relativeScope",
offset: offset?.number ?? 1,
direction: offset?.direction ?? "forward",
length: length ?? 1,
scopeType,
});
}

targetRelativeInclusiveScope({
offset,
scopeType,
}: TargetRelativeInclusiveScopeArg) {
this.targeted.targetModifier({
type: "relativeScope",
offset: 0,
direction: offset?.direction ?? "forward",
length: offset?.number ?? 1,
scopeType,
});
modifyTarget({ modifier }: { modifier: Modifier }) {
this.targeted.targetModifier(modifier);
}
}

Expand All @@ -136,20 +92,7 @@ interface DecoratedMarkArg {
color?: HatColor;
shape?: HatShape;
};
}
interface TargetRelativeExclusiveScopeArg {
offset: Offset;
length: number | null;
scopeType: ScopeType;
}
interface TargetRelativeInclusiveScopeArg {
offset: Offset;
scopeType: ScopeType;
}

interface Offset {
direction: "forward" | "backward" | null;
number: number | null;
mode: "replace" | "extend" | "append";
}

function isString(input: any): input is string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export default class KeyboardCommandsModal {

modeOn = async () => {
if (this.isModeOn()) {
// Set target to current selection if we re-run mode on
await this.targeted.targetSelection();
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
KeyboardCommand,
KeyboardCommandArgTypes,
} from "../KeyboardCommandTypeHelpers";
import { Unused } from "./grammarHelpers";

/**
* Represents a post-processing function for a top-level rule of our grammar.
Expand All @@ -19,7 +18,5 @@ export interface CommandRulePostProcessor<
metadata: {
/** The command type */
type: T;
/** The names of the arguments to the command's argument payload */
argNames: (keyof KeyboardCommandArgTypes[T] | Unused)[];
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@
function id(d: any[]): any { return d[0]; }
declare var makeRange: any;
declare var makeList: any;
declare var every: any;
declare var nextPrev: any;
declare var simpleAction: any;
declare var wrap: any;
declare var pairedDelimiter: any;
declare var vscodeCommand: any;
declare var every: any;
declare var nextPrev: any;
declare var simpleScopeTypeType: any;
declare var color: any;
declare var shape: any;
declare var combineColorAndShape: any;
declare var direction: any;
declare var digit: any;

import { capture, command, UNUSED as _ } from "../grammarHelpers"
import { capture, command, UNUSED as _, argPositions } from "../grammarHelpers"
import { keyboardLexer } from "../keyboardLexer";

const { $0, $1, $2 } = argPositions;

interface NearleyToken {
value: any;
[key: string]: any;
Expand Down Expand Up @@ -51,33 +53,45 @@ interface Grammar {
const grammar: Grammar = {
Lexer: keyboardLexer,
ParserRules: [
{"name": "main", "symbols": ["decoratedMark"], "postprocess": command("targetDecoratedMarkReplace", ["decoratedMark"])},
{"name": "main", "symbols": ["decoratedMark"], "postprocess":
command("targetDecoratedMark", { decoratedMark: $0, mode: "replace" })
},
{"name": "main", "symbols": [(keyboardLexer.has("makeRange") ? {type: "makeRange"} : makeRange), "decoratedMark"], "postprocess":
command("targetDecoratedMarkExtend", [_, "decoratedMark"])
command("targetDecoratedMark", { decoratedMark: $1, mode: "extend" })
},
{"name": "main", "symbols": [(keyboardLexer.has("makeList") ? {type: "makeList"} : makeList), "decoratedMark"], "postprocess":
command("targetDecoratedMarkAppend", [_, "decoratedMark"])
},
{"name": "main", "symbols": ["scopeType"], "postprocess": command("modifyTargetContainingScope", ["scopeType"])},
{"name": "main", "symbols": [(keyboardLexer.has("every") ? {type: "every"} : every), "scopeType"], "postprocess": command("targetEveryScopeType", [_, "scopeType"])},
{"name": "main$ebnf$1", "symbols": ["offset"], "postprocess": id},
{"name": "main$ebnf$1", "symbols": [], "postprocess": () => null},
{"name": "main$ebnf$2", "symbols": ["number"], "postprocess": id},
{"name": "main$ebnf$2", "symbols": [], "postprocess": () => null},
{"name": "main", "symbols": ["main$ebnf$1", (keyboardLexer.has("nextPrev") ? {type: "nextPrev"} : nextPrev), "main$ebnf$2", "scopeType"], "postprocess":
command(
"targetRelativeExclusiveScope",
["offset", _, "length", "scopeType"],
)
},
{"name": "main", "symbols": ["offset", "scopeType"], "postprocess":
command("targetRelativeInclusiveScope", ["offset", "scopeType"])
command("targetDecoratedMark", { decoratedMark: $1, mode: "append" })
},
{"name": "main", "symbols": ["modifier"], "postprocess": command("modifyTarget", { modifier: $0 })},
{"name": "main", "symbols": [(keyboardLexer.has("simpleAction") ? {type: "simpleAction"} : simpleAction)], "postprocess": command("performSimpleActionOnTarget", ["actionName"])},
{"name": "main", "symbols": [(keyboardLexer.has("wrap") ? {type: "wrap"} : wrap), (keyboardLexer.has("pairedDelimiter") ? {type: "pairedDelimiter"} : pairedDelimiter)], "postprocess":
command("performWrapActionOnTarget", [_, "delimiter"])
},
{"name": "main", "symbols": [(keyboardLexer.has("vscodeCommand") ? {type: "vscodeCommand"} : vscodeCommand)], "postprocess": command("vscodeCommand", ["command"])},
{"name": "modifier", "symbols": ["scopeType"], "postprocess": capture({ type: "containingScope", scopeType: $0 })},
{"name": "modifier", "symbols": [(keyboardLexer.has("every") ? {type: "every"} : every), "scopeType"], "postprocess": capture({ type: "everyScope", scopeType: $1 })},
{"name": "modifier$ebnf$1", "symbols": ["offset"], "postprocess": id},
{"name": "modifier$ebnf$1", "symbols": [], "postprocess": () => null},
{"name": "modifier$ebnf$2", "symbols": ["number"], "postprocess": id},
{"name": "modifier$ebnf$2", "symbols": [], "postprocess": () => null},
{"name": "modifier", "symbols": ["modifier$ebnf$1", (keyboardLexer.has("nextPrev") ? {type: "nextPrev"} : nextPrev), "modifier$ebnf$2", "scopeType"], "postprocess":
([offset, _, length, scopeType]) => ({
type: "relativeScope",
offset: offset?.number ?? 1,
direction: offset?.direction ?? "forward",
length: length ?? 1,
scopeType,
})
},
{"name": "modifier", "symbols": ["offset", "scopeType"], "postprocess":
([offset, scopeType]) => ({
type: "relativeScope",
offset: 0,
direction: offset?.direction ?? "forward",
length: offset?.number ?? 1,
scopeType,
})
},
{"name": "scopeType", "symbols": [(keyboardLexer.has("simpleScopeTypeType") ? {type: "simpleScopeTypeType"} : simpleScopeTypeType)], "postprocess": capture("type")},
{"name": "scopeType", "symbols": [(keyboardLexer.has("pairedDelimiter") ? {type: "pairedDelimiter"} : pairedDelimiter)], "postprocess":
([delimiter]) => ({ type: "surroundingPair", delimiter })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import { isEqual } from "lodash";
import { CommandRulePostProcessor } from "./CommandRulePostProcessor";
import { UniqueWorkQueue } from "./UniqueWorkQueue";
import { uniqWithHash } from "@cursorless/common";
import { UNUSED } from "./grammarHelpers";
import { KeyboardCommandHandler } from "../KeyboardCommandHandler";
import { KeyboardCommandArgTypes } from "../KeyboardCommandTypeHelpers";

/**
* Given a parser, returns a list of acceptable token types at the current state
Expand Down Expand Up @@ -76,19 +74,8 @@ function getRootStates(state: nearley.State) {
* @returns A partial argument for the command that the state represents
*/
function computePartialArg<T extends keyof KeyboardCommandHandler>(
state: nearley.State,
) {
const { argNames } = getMetadata<T>(state);
let currentState = state;
const partialArg: Partial<Record<keyof KeyboardCommandArgTypes[T], any>> = {};

while (currentState.dot > 0) {
const argName = argNames[currentState.dot - 1]!;
if (argName !== UNUSED) {
partialArg[argName] = currentState.right?.data;
}
currentState = currentState.left!;
}

return partialArg;
_state: nearley.State,
): Partial<Record<T, any>> {
// TODO: Fill this out
return {};
}
70 changes: 48 additions & 22 deletions packages/cursorless-vscode/src/keyboard/grammar/grammar.ne
Original file line number Diff line number Diff line change
@@ -1,43 +1,35 @@
@preprocessor typescript
@{%
import { capture, command, UNUSED as _ } from "../grammarHelpers"
import { capture, command, UNUSED as _, argPositions } from "../grammarHelpers"
import { keyboardLexer } from "../keyboardLexer";

const { $0, $1, $2 } = argPositions;
%}
@lexer keyboardLexer

# ===================== Top-level commands ===================

# --------------------------- Marks --------------------------
# "air"
main -> decoratedMark {% command("targetDecoratedMarkReplace", ["decoratedMark"]) %}
main -> decoratedMark {%
command("targetDecoratedMark", { decoratedMark: $0, mode: "replace" })
%}

# "past air"
main -> %makeRange decoratedMark {%
command("targetDecoratedMarkExtend", [_, "decoratedMark"])
command("targetDecoratedMark", { decoratedMark: $1, mode: "extend" })
%}

# "and air"
main -> %makeList decoratedMark {%
command("targetDecoratedMarkAppend", [_, "decoratedMark"])
command("targetDecoratedMark", { decoratedMark: $1, mode: "append" })
%}

# "funk"
main -> scopeType {% command("modifyTargetContainingScope", ["scopeType"]) %}

# "every funk"
main -> %every scopeType {% command("targetEveryScopeType", [_, "scopeType"]) %}
# --------------------------- Modifier --------------------------

# "[third] next [two] funks"
# "[third] previous [two] funks"
main -> offset:? %nextPrev number:? scopeType {%
command(
"targetRelativeExclusiveScope",
["offset", _, "length", "scopeType"],
)
%}
main -> modifier {% command("modifyTarget", { modifier: $0 }) %}

# "three funks [backward]"
main -> offset scopeType {%
command("targetRelativeInclusiveScope", ["offset", "scopeType"])
%}
# --------------------------- Actions --------------------------

# "chuck"
main -> %simpleAction {% command("performSimpleActionOnTarget", ["actionName"]) %}
Expand All @@ -50,12 +42,46 @@ main -> %wrap %pairedDelimiter {%
# Custom vscode command
main -> %vscodeCommand {% command("vscodeCommand", ["command"]) %}

# ========================== Captures =========================
# ========================== Captures =============================

# --------------------------- Modifiers ---------------------------

# "funk"
modifier -> scopeType {% capture({ type: "containingScope", scopeType: $0 }) %}

# "every funk"
modifier -> %every scopeType {% capture({ type: "everyScope", scopeType: $1 }) %}

# "[third] next [two] funks"
# "[third] previous [two] funks"
modifier -> offset:? %nextPrev number:? scopeType {%
([offset, _, length, scopeType]) => ({
type: "relativeScope",
offset: offset?.number ?? 1,
direction: offset?.direction ?? "forward",
length: length ?? 1,
scopeType,
})
%}

# "three funks [backward]"
modifier -> offset scopeType {%
([offset, scopeType]) => ({
type: "relativeScope",
offset: 0,
direction: offset?.direction ?? "forward",
length: offset?.number ?? 1,
scopeType,
})
%}

# --------------------------- Scope types ---------------------------
scopeType -> %simpleScopeTypeType {% capture("type") %}
scopeType -> %pairedDelimiter {%
([delimiter]) => ({ type: "surroundingPair", delimiter })
%}

# --------------------------- Other ---------------------------
decoratedMark ->
%color {% capture("color") %}
| %shape {% capture("shape") %}
Expand Down
Loading

0 comments on commit 9fc74ca

Please sign in to comment.