From cba14c8c27653f7ed81befbbafd2df4f6b99d600 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 20 Nov 2023 19:35:19 +0100 Subject: [PATCH 01/16] Added glyph modifier --- cursorless-talon/src/modifiers/glyph.py | 16 ++++++ cursorless-talon/src/modifiers/modifiers.py | 1 + cursorless-talon/src/spoken_forms.json | 3 ++ .../command/PartialTargetDescriptor.types.ts | 6 +++ .../ModifierStageFactoryImpl.ts | 3 ++ .../processTargets/modifiers/GlyphStage.ts | 53 +++++++++++++++++++ .../spokenForms/defaultSpokenFormMapCore.ts | 1 + .../recorded/modifiers/changeGlyph.yml | 22 ++++++++ .../recorded/modifiers/changeGlyph2.yml | 22 ++++++++ .../recorded/modifiers/changeGlyph3.yml | 22 ++++++++ .../recorded/modifiers/changeGlyph4.yml | 22 ++++++++ 11 files changed, 171 insertions(+) create mode 100644 cursorless-talon/src/modifiers/glyph.py create mode 100644 packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml diff --git a/cursorless-talon/src/modifiers/glyph.py b/cursorless-talon/src/modifiers/glyph.py new file mode 100644 index 0000000000..ff8ea84510 --- /dev/null +++ b/cursorless-talon/src/modifiers/glyph.py @@ -0,0 +1,16 @@ +from talon import Module + +mod = Module() + +mod.list( + "cursorless_glyph_modifier", + desc="Cursorless glyph, modifier", +) + + +@mod.capture(rule="{user.cursorless_glyph_modifier} ") +def cursorless_glyph_modifier(m) -> dict[str, str]: + return { + "type": "glyph", + "glyph": m.any_alphanumeric_key, + } diff --git a/cursorless-talon/src/modifiers/modifiers.py b/cursorless-talon/src/modifiers/modifiers.py index d63351ab37..57ab24a8cf 100644 --- a/cursorless-talon/src/modifiers/modifiers.py +++ b/cursorless-talon/src/modifiers/modifiers.py @@ -34,6 +34,7 @@ def cursorless_simple_modifier(m) -> dict[str, str]: "", # inside "", # head, tail "", # start of, end of + "", # glyph air/dollar *head_tail_swallowed_modifiers, ] diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index 40a0fd16b1..52201dbd9e 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -86,6 +86,9 @@ "head": "extendThroughStartOf", "tail": "extendThroughEndOf" }, + "glyph_modifier": { + "glyph": "glyph" + }, "range_type": { "slice": "verticalRange" }, diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 270c7b7f6e..7b1b2988ad 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -321,6 +321,11 @@ export interface EndOfModifier { type: "endOf"; } +export interface GlyphModifier { + type: "glyph"; + glyph: string; +} + export interface HeadModifier { type: "extendThroughStartOf"; modifiers?: Modifier[]; @@ -379,6 +384,7 @@ export type Modifier = | EveryScopeModifier | OrdinalScopeModifier | RelativeScopeModifier + | GlyphModifier | HeadModifier | TailModifier | LeadingModifier diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index f3a6dfad3d..fedf665b14 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -16,6 +16,7 @@ import { KeepContentFilterStage, KeepEmptyFilterStage, } from "./modifiers/FilterStages"; +import { GlyphStage } from "./modifiers/GlyphStage"; import { HeadStage, TailStage } from "./modifiers/HeadTailStage"; import { InstanceStage } from "./modifiers/InstanceStage"; import { @@ -68,6 +69,8 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { return new LeadingStage(this, modifier); case "trailing": return new TrailingStage(this, modifier); + case "glyph": + return new GlyphStage(this, modifier); case "containingScope": return new ContainingScopeStage( this, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts new file mode 100644 index 0000000000..f471bd9f30 --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts @@ -0,0 +1,53 @@ +import { GlyphModifier, Range } from "@cursorless/common"; +import { Target } from "../../typings/target.types"; +import { ModifierStageFactory } from "../ModifierStageFactory"; +import { ModifierStage } from "../PipelineStages.types"; +import { PlainTarget } from "../targets"; + +export class GlyphStage implements ModifierStage { + constructor( + private modifierStageFactory: ModifierStageFactory, + private modifier: GlyphModifier, + ) {} + + run(target: Target): Target[] { + const { editor, contentRange, isReversed } = target; + + const text = (() => { + if (contentRange.isEmpty) { + const line = editor.document.lineAt(contentRange.start); + return editor.document.getText( + new Range(contentRange.start, line.range.end), + ); + } + return target.contentText; + })(); + + const index = text.indexOf(this.modifier.glyph); + + if (index === -1) { + throw new GlyphNotFoundError(this.modifier.glyph); + } + + const offset = editor.document.offsetAt(contentRange.start); + const start = editor.document.positionAt(offset + index); + const end = editor.document.positionAt( + offset + index + this.modifier.glyph.length, + ); + + return [ + new PlainTarget({ + editor, + contentRange: new Range(start, end), + isReversed, + }), + ]; + } +} + +export class GlyphNotFoundError extends Error { + constructor(glyph: string) { + super(`Couldn't find glyph '${glyph}'.`); + this.name = "GlyphNotFoundError"; + } +} diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 5b825407bb..fbb1e637e4 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -120,6 +120,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { extendThroughStartOf: "head", extendThroughEndOf: "tail", everyScope: "every", + glyph: "glyph", }, modifierExtra: { diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml new file mode 100644 index 0000000000..57de666cf0 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 6 + spokenForm: change glyph + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: glyph, glyph: a} + usePrePhraseSnapshot: true +initialState: + documentContents: a a a + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: " a a" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml new file mode 100644 index 0000000000..de2c60c60d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 6 + spokenForm: change glyph + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: glyph, glyph: b} + usePrePhraseSnapshot: true +initialState: + documentContents: a $b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: a $ + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml new file mode 100644 index 0000000000..c52365d0d1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 6 + spokenForm: change glyph + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: glyph, glyph: $} + usePrePhraseSnapshot: true +initialState: + documentContents: a $b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: a b + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml new file mode 100644 index 0000000000..22a53a5283 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 6 + spokenForm: change glyph + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: glyph, glyph: a} + usePrePhraseSnapshot: true +initialState: + documentContents: a b a + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 5} + marks: {} +finalState: + documentContents: " b a" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} From 736a736143dfc6dc86789b91f7faaeade5f81737 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 20 Nov 2023 19:40:46 +0100 Subject: [PATCH 02/16] Update cheat cheat --- .../src/cheatsheet/sections/modifiers.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cursorless-talon/src/cheatsheet/sections/modifiers.py b/cursorless-talon/src/cheatsheet/sections/modifiers.py index 576edc4721..dac22a7547 100644 --- a/cursorless-talon/src/cheatsheet/sections/modifiers.py +++ b/cursorless-talon/src/cheatsheet/sections/modifiers.py @@ -10,6 +10,7 @@ "previous_next_modifier", "forward_backward_modifier", "position", + "glyph_modifier", ] @@ -28,6 +29,7 @@ def get_modifiers(): "next", "backward", "forward", + "glyph", ] simple_modifiers = { key: value @@ -101,6 +103,16 @@ def get_modifiers(): }, ], }, + { + "id": "glyph", + "type": "modifier", + "variations": [ + { + "spokenForm": f"{complex_modifiers['glyph']} ", + "description": "First instance of ", + }, + ], + }, { "id": "relativeScope", "type": "modifier", From 96b00667e9d987ececedb20ada4ae3a172a625db Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 23 Nov 2023 04:43:43 +0100 Subject: [PATCH 03/16] Converted to scope --- cursorless-talon/src/modifiers/glyph.py | 16 ------ cursorless-talon/src/modifiers/glyph_scope.py | 30 +++++++++++ cursorless-talon/src/modifiers/modifiers.py | 1 - cursorless-talon/src/modifiers/scopes.py | 28 +++++++--- cursorless-talon/src/spoken_forms.json | 6 +-- cursorless-talon/src/spoken_forms.py | 2 +- .../command/PartialTargetDescriptor.types.ts | 14 ++--- .../primitiveTargetToSpokenForm.ts | 1 + .../ModifierStageFactoryImpl.ts | 3 -- .../processTargets/modifiers/GlyphStage.ts | 53 ------------------- .../scopeHandlers/GlyphScopeHandler.ts | 42 +++++++++++++++ .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 3 ++ .../modifiers/scopeHandlers/index.ts | 1 + .../src/scopeProviders/ScopeInfoProvider.ts | 1 + .../spokenForms/defaultSpokenFormMapCore.ts | 1 - .../recorded/modifiers/changeGlyph3.yml | 22 -------- .../glyph/clearEveryGlyphAir.yml} | 14 +++-- .../scope/glyph/clearFinalGlyphAir.yml | 26 +++++++++ .../glyph/clearGlyphAir.yml} | 10 ++-- .../glyph/clearNextGlyphDollar.yml} | 13 +++-- 20 files changed, 160 insertions(+), 127 deletions(-) delete mode 100644 cursorless-talon/src/modifiers/glyph.py create mode 100644 cursorless-talon/src/modifiers/glyph_scope.py delete mode 100644 packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts create mode 100644 packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml rename packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/{modifiers/changeGlyph4.yml => scope/glyph/clearEveryGlyphAir.yml} (51%) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml rename packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/{modifiers/changeGlyph.yml => scope/glyph/clearGlyphAir.yml} (64%) rename packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/{modifiers/changeGlyph2.yml => scope/glyph/clearNextGlyphDollar.yml} (57%) diff --git a/cursorless-talon/src/modifiers/glyph.py b/cursorless-talon/src/modifiers/glyph.py deleted file mode 100644 index ff8ea84510..0000000000 --- a/cursorless-talon/src/modifiers/glyph.py +++ /dev/null @@ -1,16 +0,0 @@ -from talon import Module - -mod = Module() - -mod.list( - "cursorless_glyph_modifier", - desc="Cursorless glyph, modifier", -) - - -@mod.capture(rule="{user.cursorless_glyph_modifier} ") -def cursorless_glyph_modifier(m) -> dict[str, str]: - return { - "type": "glyph", - "glyph": m.any_alphanumeric_key, - } diff --git a/cursorless-talon/src/modifiers/glyph_scope.py b/cursorless-talon/src/modifiers/glyph_scope.py new file mode 100644 index 0000000000..beff4c35ed --- /dev/null +++ b/cursorless-talon/src/modifiers/glyph_scope.py @@ -0,0 +1,30 @@ +from talon import Module + +mod = Module() + +mod.list( + "cursorless_glyph_scope_type", + desc="Cursorless glyph scope type", +) +mod.list( + "cursorless_glyph_scope_type_plural", + desc="Plural version of Cursorless glyph scope type", +) + + +@mod.capture(rule="{user.cursorless_glyph_scope_type} ") +def cursorless_glyph_scope_type(m) -> dict[str, str]: + return { + "type": "glyph", + "character": m.any_alphanumeric_key, + } + + +@mod.capture( + rule="{user.cursorless_glyph_scope_type_plural} " +) +def cursorless_glyph_scope_type_plural(m) -> dict[str, str]: + return { + "type": "glyph", + "character": m.any_alphanumeric_key, + } diff --git a/cursorless-talon/src/modifiers/modifiers.py b/cursorless-talon/src/modifiers/modifiers.py index 57ab24a8cf..d63351ab37 100644 --- a/cursorless-talon/src/modifiers/modifiers.py +++ b/cursorless-talon/src/modifiers/modifiers.py @@ -34,7 +34,6 @@ def cursorless_simple_modifier(m) -> dict[str, str]: "", # inside "", # head, tail "", # start of, end of - "", # glyph air/dollar *head_tail_swallowed_modifiers, ] diff --git a/cursorless-talon/src/modifiers/scopes.py b/cursorless-talon/src/modifiers/scopes.py index 02b98da19e..ee1efb9b2c 100644 --- a/cursorless-talon/src/modifiers/scopes.py +++ b/cursorless-talon/src/modifiers/scopes.py @@ -15,25 +15,39 @@ @mod.capture( - rule="{user.cursorless_scope_type} | {user.cursorless_custom_regex_scope_type}" + rule="{user.cursorless_scope_type} | | {user.cursorless_custom_regex_scope_type}" ) def cursorless_scope_type(m) -> dict[str, str]: """Cursorless scope type singular""" try: return {"type": m.cursorless_scope_type} except AttributeError: - return {"type": "customRegex", "regex": m.cursorless_custom_regex_scope_type} + pass + + try: + return m.cursorless_glyph_scope_type + except AttributeError: + pass + + return {"type": "customRegex", "regex": m.cursorless_custom_regex_scope_type} @mod.capture( - rule="{user.cursorless_scope_type_plural} | {user.cursorless_custom_regex_scope_type_plural}" + rule="{user.cursorless_scope_type_plural} | | {user.cursorless_custom_regex_scope_type_plural}" ) def cursorless_scope_type_plural(m) -> dict[str, str]: """Cursorless scope type plural""" try: return {"type": m.cursorless_scope_type_plural} except AttributeError: - return { - "type": "customRegex", - "regex": m.cursorless_custom_regex_scope_type_plural, - } + pass + + try: + return m.cursorless_glyph_scope_type_plural + except AttributeError: + pass + + return { + "type": "customRegex", + "regex": m.cursorless_custom_regex_scope_type_plural, + } diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index 52201dbd9e..c9a6ab5c01 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -86,9 +86,6 @@ "head": "extendThroughStartOf", "tail": "extendThroughEndOf" }, - "glyph_modifier": { - "glyph": "glyph" - }, "range_type": { "slice": "verticalRange" }, @@ -171,6 +168,9 @@ }, "surrounding_pair_scope_type": { "string": "string" + }, + "glyph_scope_type": { + "glyph": "glyph" } }, "paired_delimiters.csv": { diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index e166eb0fa1..35f230e795 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -125,7 +125,7 @@ def handle_new_values(csv_name: str, values: list[SpokenFormEntry]): handle_csv("experimental/miscellaneous.csv"), handle_csv( "modifier_scope_types.csv", - pluralize_lists=["scope_type"], + pluralize_lists=["scope_type", "glyph_scope_type"], extra_allowed_values=["private.fieldAccess"], default_list_name="scope_type", ), diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 7b1b2988ad..77b8a16270 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -205,11 +205,17 @@ export interface OneOfScopeType { scopeTypes: ScopeType[]; } +export interface GlyphScopeType { + type: "glyph"; + character: string; +} + export type ScopeType = | SimpleScopeType | SurroundingPairScopeType | CustomRegexScopeType - | OneOfScopeType; + | OneOfScopeType + | GlyphScopeType; export interface ContainingSurroundingPairModifier extends ContainingScopeModifier { @@ -321,11 +327,6 @@ export interface EndOfModifier { type: "endOf"; } -export interface GlyphModifier { - type: "glyph"; - glyph: string; -} - export interface HeadModifier { type: "extendThroughStartOf"; modifiers?: Modifier[]; @@ -384,7 +385,6 @@ export type Modifier = | EveryScopeModifier | OrdinalScopeModifier | RelativeScopeModifier - | GlyphModifier | HeadModifier | TailModifier | LeadingModifier diff --git a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index 05b22e0e5f..d62d1ddc5c 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -207,6 +207,7 @@ export class PrimitiveTargetSpokenFormGenerator { handleScopeType(scopeType: ScopeType): SpokenFormComponent { switch (scopeType.type) { case "oneOf": + case "glyph": throw new NoSpokenFormError(`Scope type '${scopeType.type}'`); case "surroundingPair": { const pair = this.spokenFormMap.pairedDelimiter[scopeType.delimiter]; diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index fedf665b14..f3a6dfad3d 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -16,7 +16,6 @@ import { KeepContentFilterStage, KeepEmptyFilterStage, } from "./modifiers/FilterStages"; -import { GlyphStage } from "./modifiers/GlyphStage"; import { HeadStage, TailStage } from "./modifiers/HeadTailStage"; import { InstanceStage } from "./modifiers/InstanceStage"; import { @@ -69,8 +68,6 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { return new LeadingStage(this, modifier); case "trailing": return new TrailingStage(this, modifier); - case "glyph": - return new GlyphStage(this, modifier); case "containingScope": return new ContainingScopeStage( this, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts deleted file mode 100644 index f471bd9f30..0000000000 --- a/packages/cursorless-engine/src/processTargets/modifiers/GlyphStage.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { GlyphModifier, Range } from "@cursorless/common"; -import { Target } from "../../typings/target.types"; -import { ModifierStageFactory } from "../ModifierStageFactory"; -import { ModifierStage } from "../PipelineStages.types"; -import { PlainTarget } from "../targets"; - -export class GlyphStage implements ModifierStage { - constructor( - private modifierStageFactory: ModifierStageFactory, - private modifier: GlyphModifier, - ) {} - - run(target: Target): Target[] { - const { editor, contentRange, isReversed } = target; - - const text = (() => { - if (contentRange.isEmpty) { - const line = editor.document.lineAt(contentRange.start); - return editor.document.getText( - new Range(contentRange.start, line.range.end), - ); - } - return target.contentText; - })(); - - const index = text.indexOf(this.modifier.glyph); - - if (index === -1) { - throw new GlyphNotFoundError(this.modifier.glyph); - } - - const offset = editor.document.offsetAt(contentRange.start); - const start = editor.document.positionAt(offset + index); - const end = editor.document.positionAt( - offset + index + this.modifier.glyph.length, - ); - - return [ - new PlainTarget({ - editor, - contentRange: new Range(start, end), - isReversed, - }), - ]; - } -} - -export class GlyphNotFoundError extends Error { - constructor(glyph: string) { - super(`Couldn't find glyph '${glyph}'.`); - this.name = "GlyphNotFoundError"; - } -} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts new file mode 100644 index 0000000000..c0b74ab936 --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts @@ -0,0 +1,42 @@ +import { Direction, GlyphScopeType } from "@cursorless/common"; +import { imap } from "itertools"; +import { NestedScopeHandler, ScopeHandlerFactory } from "."; +import { generateMatchesInRange } from "../../../util/getMatchesInRange"; +import { PlainTarget } from "../../targets"; +import type { TargetScope } from "./scope.types"; +import { escapeRegExp } from "lodash"; + +export class GlyphScopeHandler extends NestedScopeHandler { + public readonly scopeType = { type: "glyph", character: "" } as const; + public readonly iterationScopeType = { type: "document" } as const; + private readonly regex: RegExp; + + constructor( + scopeHandlerFactory: ScopeHandlerFactory, + scopeType: GlyphScopeType, + languageId: string, + ) { + super(scopeHandlerFactory, scopeType, languageId); + this.regex = new RegExp(escapeRegExp(scopeType.character), "g"); + } + + protected generateScopesInSearchScope( + direction: Direction, + { editor, domain }: TargetScope, + ): Iterable { + return imap( + generateMatchesInRange(this.regex, editor, domain, direction), + (range) => ({ + editor, + domain: range, + getTargets: (isReversed) => [ + new PlainTarget({ + editor, + contentRange: range, + isReversed, + }), + ], + }), + ); + } +} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 3efd7f8770..18e79e4349 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -3,6 +3,7 @@ import { CharacterScopeHandler, CustomRegexScopeHandler, DocumentScopeHandler, + GlyphScopeHandler, IdentifierScopeHandler, LineScopeHandler, NonWhitespaceSequenceScopeHandler, @@ -72,6 +73,8 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { return new UrlScopeHandler(this, scopeType, languageId); case "customRegex": return new CustomRegexScopeHandler(this, scopeType, languageId); + case "glyph": + return new GlyphScopeHandler(this, scopeType, languageId); case "custom": return scopeType.scopeHandler; case "instance": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts index e5bf84d6a9..c9cf84d3a2 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts @@ -10,5 +10,6 @@ export * from "./OneOfScopeHandler"; export * from "./ParagraphScopeHandler"; export * from "./SentenceScopeHandler/SentenceScopeHandler"; export * from "./RegexScopeHandler"; +export * from "./GlyphScopeHandler"; export * from "./ScopeHandlerFactory"; export * from "./ScopeHandlerFactoryImpl"; diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index 5646746b07..cceb9a39bb 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -173,6 +173,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "notebookCell": case "surroundingPair": case "customRegex": + case "glyph": return false; case "oneOf": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index fbb1e637e4..5b825407bb 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -120,7 +120,6 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { extendThroughStartOf: "head", extendThroughEndOf: "tail", everyScope: "every", - glyph: "glyph", }, modifierExtra: { diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml deleted file mode 100644 index c52365d0d1..0000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph3.yml +++ /dev/null @@ -1,22 +0,0 @@ -languageId: plaintext -command: - version: 6 - spokenForm: change glyph - action: - name: clearAndSetSelection - target: - type: primitive - modifiers: - - {type: glyph, glyph: $} - usePrePhraseSnapshot: true -initialState: - documentContents: a $b - selections: - - anchor: {line: 0, character: 0} - active: {line: 0, character: 0} - marks: {} -finalState: - documentContents: a b - selections: - - anchor: {line: 0, character: 2} - active: {line: 0, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml similarity index 51% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml index 22a53a5283..a5ad82fb8b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph4.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml @@ -1,22 +1,26 @@ languageId: plaintext command: version: 6 - spokenForm: change glyph + spokenForm: clear every glyph air action: name: clearAndSetSelection target: type: primitive modifiers: - - {type: glyph, glyph: a} + - type: everyScope + scopeType: {type: glyph, character: a} usePrePhraseSnapshot: true +spokenFormError: Scope type 'glyph' initialState: - documentContents: a b a + documentContents: abc$!4123a selections: - anchor: {line: 0, character: 0} - active: {line: 0, character: 5} + active: {line: 0, character: 0} marks: {} finalState: - documentContents: " b a" + documentContents: bc$!4123 selections: - anchor: {line: 0, character: 0} active: {line: 0, character: 0} + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml new file mode 100644 index 0000000000..c1d94ddaed --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml @@ -0,0 +1,26 @@ +languageId: plaintext +command: + version: 6 + spokenForm: clear final glyph air + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: ordinalScope + scopeType: {type: glyph, character: a} + start: -1 + length: 1 + usePrePhraseSnapshot: true +spokenFormError: Scope type 'glyph' +initialState: + documentContents: abc$!4123a + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: abc$!4123 + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml similarity index 64% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml index 57de666cf0..8ab1cdf231 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml @@ -1,22 +1,24 @@ languageId: plaintext command: version: 6 - spokenForm: change glyph + spokenForm: clear glyph air action: name: clearAndSetSelection target: type: primitive modifiers: - - {type: glyph, glyph: a} + - type: containingScope + scopeType: {type: glyph, character: a} usePrePhraseSnapshot: true +spokenFormError: Scope type 'glyph' initialState: - documentContents: a a a + documentContents: abc$!4123a selections: - anchor: {line: 0, character: 0} active: {line: 0, character: 0} marks: {} finalState: - documentContents: " a a" + documentContents: bc$!4123a selections: - anchor: {line: 0, character: 0} active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml similarity index 57% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml index de2c60c60d..100212c171 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/modifiers/changeGlyph2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml @@ -1,22 +1,27 @@ languageId: plaintext command: version: 6 - spokenForm: change glyph + spokenForm: clear next glyph dollar action: name: clearAndSetSelection target: type: primitive modifiers: - - {type: glyph, glyph: b} + - type: relativeScope + scopeType: {type: glyph, character: $} + offset: 1 + length: 1 + direction: forward usePrePhraseSnapshot: true +spokenFormError: Scope type 'glyph' initialState: - documentContents: a $b + documentContents: abc$!4123a selections: - anchor: {line: 0, character: 0} active: {line: 0, character: 0} marks: {} finalState: - documentContents: a $ + documentContents: abc!4123a selections: - anchor: {line: 0, character: 3} active: {line: 0, character: 3} From 1504e10b773296258c0214a845b2697837735894 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 23 Nov 2023 04:52:03 +0100 Subject: [PATCH 04/16] cleanup --- .../scopeHandlers/GlyphScopeHandler.ts | 42 ------------------- .../scopeHandlers/RegexScopeHandler.ts | 22 +++++++++- .../modifiers/scopeHandlers/index.ts | 1 - 3 files changed, 21 insertions(+), 44 deletions(-) delete mode 100644 packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts deleted file mode 100644 index c0b74ab936..0000000000 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/GlyphScopeHandler.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Direction, GlyphScopeType } from "@cursorless/common"; -import { imap } from "itertools"; -import { NestedScopeHandler, ScopeHandlerFactory } from "."; -import { generateMatchesInRange } from "../../../util/getMatchesInRange"; -import { PlainTarget } from "../../targets"; -import type { TargetScope } from "./scope.types"; -import { escapeRegExp } from "lodash"; - -export class GlyphScopeHandler extends NestedScopeHandler { - public readonly scopeType = { type: "glyph", character: "" } as const; - public readonly iterationScopeType = { type: "document" } as const; - private readonly regex: RegExp; - - constructor( - scopeHandlerFactory: ScopeHandlerFactory, - scopeType: GlyphScopeType, - languageId: string, - ) { - super(scopeHandlerFactory, scopeType, languageId); - this.regex = new RegExp(escapeRegExp(scopeType.character), "g"); - } - - protected generateScopesInSearchScope( - direction: Direction, - { editor, domain }: TargetScope, - ): Iterable { - return imap( - generateMatchesInRange(this.regex, editor, domain, direction), - (range) => ({ - editor, - domain: range, - getTargets: (isReversed) => [ - new PlainTarget({ - editor, - contentRange: range, - isReversed, - }), - ], - }), - ); - } -} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts index 209ede051c..c955bde11d 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts @@ -1,9 +1,15 @@ -import { CustomRegexScopeType, Direction, ScopeType } from "@cursorless/common"; +import { + CustomRegexScopeType, + Direction, + GlyphScopeType, + ScopeType, +} from "@cursorless/common"; import { imap } from "itertools"; import { NestedScopeHandler, ScopeHandlerFactory } from "."; import { generateMatchesInRange } from "../../../util/getMatchesInRange"; import { TokenTarget } from "../../targets"; import { TargetScope } from "./scope.types"; +import { escapeRegExp } from "lodash"; abstract class RegexStageBase extends NestedScopeHandler { public readonly iterationScopeType: ScopeType = { type: "line" }; @@ -61,3 +67,17 @@ export class CustomRegexScopeHandler extends RegexStageBase { super(scopeHandlerFactory, scopeType, languageId); } } + +export class GlyphScopeHandler extends RegexStageBase { + get regex() { + return new RegExp(escapeRegExp(this.scopeType.character), "g"); + } + + constructor( + scopeHandlerFactory: ScopeHandlerFactory, + readonly scopeType: GlyphScopeType, + languageId: string, + ) { + super(scopeHandlerFactory, scopeType, languageId); + } +} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts index c9cf84d3a2..e5bf84d6a9 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/index.ts @@ -10,6 +10,5 @@ export * from "./OneOfScopeHandler"; export * from "./ParagraphScopeHandler"; export * from "./SentenceScopeHandler/SentenceScopeHandler"; export * from "./RegexScopeHandler"; -export * from "./GlyphScopeHandler"; export * from "./ScopeHandlerFactory"; export * from "./ScopeHandlerFactoryImpl"; From 727581654ab348e97404c746c9b29d44c7293d60 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 23 Nov 2023 04:59:55 +0100 Subject: [PATCH 05/16] Added unicode test --- .../scopeHandlers/RegexScopeHandler.ts | 2 +- .../scope/glyph/clearNextGlyphOnyx.yml | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts index c955bde11d..ea8f0c5514 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts @@ -70,7 +70,7 @@ export class CustomRegexScopeHandler extends RegexStageBase { export class GlyphScopeHandler extends RegexStageBase { get regex() { - return new RegExp(escapeRegExp(this.scopeType.character), "g"); + return new RegExp(escapeRegExp(this.scopeType.character), "gu"); } constructor( diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml new file mode 100644 index 0000000000..c0fc9e31a1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml @@ -0,0 +1,27 @@ +languageId: plaintext +command: + version: 6 + spokenForm: clear next glyph onyx + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: relativeScope + scopeType: {type: glyph, character: å} + offset: 1 + length: 1 + direction: forward + usePrePhraseSnapshot: true +spokenFormError: Scope type 'glyph' +initialState: + documentContents: abå + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: ab + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} From e50f6334d95efa5ad845bd4f621bf27ffb59e6a0 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 23 Nov 2023 05:01:05 +0100 Subject: [PATCH 06/16] Organise imports --- .../processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts index ea8f0c5514..c198f78cb0 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts @@ -5,11 +5,11 @@ import { ScopeType, } from "@cursorless/common"; import { imap } from "itertools"; +import { escapeRegExp } from "lodash"; import { NestedScopeHandler, ScopeHandlerFactory } from "."; import { generateMatchesInRange } from "../../../util/getMatchesInRange"; import { TokenTarget } from "../../targets"; import { TargetScope } from "./scope.types"; -import { escapeRegExp } from "lodash"; abstract class RegexStageBase extends NestedScopeHandler { public readonly iterationScopeType: ScopeType = { type: "line" }; From 6e36c8cc134c8b8c7c13d5fe08ba155ce2432ea5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Nov 2023 14:54:19 +0100 Subject: [PATCH 07/16] Made glyph case insensitive --- .../scopeHandlers/RegexScopeHandler.ts | 2 +- .../scope/glyph/clearNextGlyphBat.yml | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts index c198f78cb0..4f943bc33f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts @@ -70,7 +70,7 @@ export class CustomRegexScopeHandler extends RegexStageBase { export class GlyphScopeHandler extends RegexStageBase { get regex() { - return new RegExp(escapeRegExp(this.scopeType.character), "gu"); + return new RegExp(escapeRegExp(this.scopeType.character), "gui"); } constructor( diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml new file mode 100644 index 0000000000..3ad8be2d1f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml @@ -0,0 +1,27 @@ +languageId: plaintext +command: + version: 6 + spokenForm: clear next glyph bat + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: relativeScope + scopeType: {type: glyph, character: b} + offset: 1 + length: 1 + direction: forward + usePrePhraseSnapshot: true +spokenFormError: Scope type 'glyph' +initialState: + documentContents: aB + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: a + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} From f71f10bb58899de588c834b02effd5005a2d0987 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:29:46 +0000 Subject: [PATCH 08/16] fixup --- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index d84dfb911b..25c3575553 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -1,19 +1,21 @@ import type { ScopeType } from "@cursorless/common"; +import { LanguageDefinitions } from "../../../languages/LanguageDefinitions"; import { CharacterScopeHandler } from "./CharacterScopeHandler"; -import { CustomRegexScopeHandler } from "./RegexScopeHandler"; import { DocumentScopeHandler } from "./DocumentScopeHandler"; -import { GlyphScopeHandler } from "./GlyphScopeHandler"; import { IdentifierScopeHandler } from "./IdentifierScopeHandler"; import { LineScopeHandler } from "./LineScopeHandler"; -import { NonWhitespaceSequenceScopeHandler } from "./RegexScopeHandler"; import { OneOfScopeHandler } from "./OneOfScopeHandler"; import { ParagraphScopeHandler } from "./ParagraphScopeHandler"; +import { + CustomRegexScopeHandler, + GlyphScopeHandler, + NonWhitespaceSequenceScopeHandler, + UrlScopeHandler, +} from "./RegexScopeHandler"; import { ScopeHandlerFactory } from "./ScopeHandlerFactory"; import { SentenceScopeHandler } from "./SentenceScopeHandler/SentenceScopeHandler"; import { TokenScopeHandler } from "./TokenScopeHandler"; -import { UrlScopeHandler } from "./RegexScopeHandler"; import { WordScopeHandler } from "./WordScopeHandler/WordScopeHandler"; -import { LanguageDefinitions } from "../../../languages/LanguageDefinitions"; import type { CustomScopeType, ScopeHandler } from "./scopeHandler.types"; /** From 3d5dfc53591738c37b99bcc67a3e2de51bad8f1d Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:57:11 +0000 Subject: [PATCH 09/16] Add spoken form generation for "glyph" --- cursorless-talon/src/spoken_forms.py | 1 + .../src/generateSpokenForm/primitiveTargetToSpokenForm.ts | 8 +++++++- .../src/scopeProviders/TalonSpokenForms.ts | 1 + .../cursorless-engine/src/spokenForms/SpokenFormType.ts | 1 + .../src/spokenForms/defaultSpokenFormMapCore.ts | 3 +++ .../fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml | 3 +-- .../fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml | 3 +-- .../suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml | 3 +-- .../fixtures/recorded/scope/glyph/clearNextGlyphBat.yml | 3 +-- .../recorded/scope/glyph/clearNextGlyphDollar.yml | 3 +-- .../fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml | 2 +- 11 files changed, 19 insertions(+), 12 deletions(-) diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index 3ffa950aea..19a2766e84 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -68,6 +68,7 @@ def ret(filename: str, *args: P.args, **kwargs: P.kwargs) -> R: "wrapper_only_paired_delimiter": "pairedDelimiter", "surrounding_pair_scope_type": "pairedDelimiter", "scope_type": "simpleScopeTypeType", + "glyph_scope_type": "complexScopeType", "custom_regex_scope_type": "customRegex", } diff --git a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index d62d1ddc5c..73aa01f572 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -207,8 +207,14 @@ export class PrimitiveTargetSpokenFormGenerator { handleScopeType(scopeType: ScopeType): SpokenFormComponent { switch (scopeType.type) { case "oneOf": - case "glyph": throw new NoSpokenFormError(`Scope type '${scopeType.type}'`); + case "glyph": { + const { character } = scopeType; + return [ + this.spokenFormMap.complexScopeTypeType.glyph, + characterToSpokenForm(character), + ]; + } case "surroundingPair": { const pair = this.spokenFormMap.pairedDelimiter[scopeType.delimiter]; if (scopeType.forceDirection != null) { diff --git a/packages/cursorless-engine/src/scopeProviders/TalonSpokenForms.ts b/packages/cursorless-engine/src/scopeProviders/TalonSpokenForms.ts index a6331be5d2..c3c10dfd4a 100644 --- a/packages/cursorless-engine/src/scopeProviders/TalonSpokenForms.ts +++ b/packages/cursorless-engine/src/scopeProviders/TalonSpokenForms.ts @@ -19,6 +19,7 @@ export interface TalonSpokenForms { */ export const SUPPORTED_ENTRY_TYPES = [ "simpleScopeTypeType", + "complexScopeTypeType", "customRegex", "pairedDelimiter", ] as const; diff --git a/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts b/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts index 06a52eae60..e1d604fce3 100644 --- a/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts +++ b/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts @@ -14,6 +14,7 @@ import { export interface SpokenFormMapKeyTypes { pairedDelimiter: SpeakableSurroundingPairName; simpleScopeTypeType: SimpleScopeTypeType; + complexScopeTypeType: "glyph"; surroundingPairForceDirection: "left" | "right"; /** diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 15b4e5ee2b..6b94ec67ba 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -100,6 +100,9 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { string: isPrivate("parse tree string"), ["private.switchStatementSubject"]: isPrivate("subject"), }, + complexScopeTypeType: { + glyph: "glyph", + }, surroundingPairForceDirection: { left: "left", diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml index a5ad82fb8b..c79ff8bef0 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml @@ -1,7 +1,7 @@ languageId: plaintext command: version: 6 - spokenForm: clear every glyph air + spokenForm: change every glyph air action: name: clearAndSetSelection target: @@ -10,7 +10,6 @@ command: - type: everyScope scopeType: {type: glyph, character: a} usePrePhraseSnapshot: true -spokenFormError: Scope type 'glyph' initialState: documentContents: abc$!4123a selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml index c1d94ddaed..782322b77f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml @@ -1,7 +1,7 @@ languageId: plaintext command: version: 6 - spokenForm: clear final glyph air + spokenForm: change last glyph air action: name: clearAndSetSelection target: @@ -12,7 +12,6 @@ command: start: -1 length: 1 usePrePhraseSnapshot: true -spokenFormError: Scope type 'glyph' initialState: documentContents: abc$!4123a selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml index 8ab1cdf231..151edb4d22 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml @@ -1,7 +1,7 @@ languageId: plaintext command: version: 6 - spokenForm: clear glyph air + spokenForm: change glyph air action: name: clearAndSetSelection target: @@ -10,7 +10,6 @@ command: - type: containingScope scopeType: {type: glyph, character: a} usePrePhraseSnapshot: true -spokenFormError: Scope type 'glyph' initialState: documentContents: abc$!4123a selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml index 3ad8be2d1f..4185b97b45 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml @@ -1,7 +1,7 @@ languageId: plaintext command: version: 6 - spokenForm: clear next glyph bat + spokenForm: change next glyph bat action: name: clearAndSetSelection target: @@ -13,7 +13,6 @@ command: length: 1 direction: forward usePrePhraseSnapshot: true -spokenFormError: Scope type 'glyph' initialState: documentContents: aB selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml index 100212c171..b6fdc326d9 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml @@ -1,7 +1,7 @@ languageId: plaintext command: version: 6 - spokenForm: clear next glyph dollar + spokenForm: change next glyph dollar action: name: clearAndSetSelection target: @@ -13,7 +13,6 @@ command: length: 1 direction: forward usePrePhraseSnapshot: true -spokenFormError: Scope type 'glyph' initialState: documentContents: abc$!4123a selections: diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml index c0fc9e31a1..84e35bc89d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml @@ -13,7 +13,7 @@ command: length: 1 direction: forward usePrePhraseSnapshot: true -spokenFormError: Scope type 'glyph' +spokenFormError: Unknown character 'å' initialState: documentContents: abå selections: From a9473540a5e42be455a9d2d349b22b47b7ac1d59 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:58:17 +0000 Subject: [PATCH 10/16] Tweaks --- .../src/generateSpokenForm/primitiveTargetToSpokenForm.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index 73aa01f572..6dba6a8f65 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -208,13 +208,11 @@ export class PrimitiveTargetSpokenFormGenerator { switch (scopeType.type) { case "oneOf": throw new NoSpokenFormError(`Scope type '${scopeType.type}'`); - case "glyph": { - const { character } = scopeType; + case "glyph": return [ this.spokenFormMap.complexScopeTypeType.glyph, - characterToSpokenForm(character), + characterToSpokenForm(scopeType.character), ]; - } case "surroundingPair": { const pair = this.spokenFormMap.pairedDelimiter[scopeType.delimiter]; if (scopeType.forceDirection != null) { From ac00a54222c6023dbb89d380611d48ab2c14a7af Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:28:31 +0000 Subject: [PATCH 11/16] fix --- cursorless-talon/src/spoken_forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index 19a2766e84..30ad16498d 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -68,7 +68,7 @@ def ret(filename: str, *args: P.args, **kwargs: P.kwargs) -> R: "wrapper_only_paired_delimiter": "pairedDelimiter", "surrounding_pair_scope_type": "pairedDelimiter", "scope_type": "simpleScopeTypeType", - "glyph_scope_type": "complexScopeType", + "glyph_scope_type": "complexScopeTypeType", "custom_regex_scope_type": "customRegex", } From 4ed60fbb4435028ff9564683826581d2ed410d19 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 8 Dec 2023 08:15:17 +0100 Subject: [PATCH 12/16] Renamed glyph_scope to literal_scope --- .../src/cheatsheet/sections/modifiers.py | 10 +++---- cursorless-talon/src/modifiers/glyph_scope.py | 30 ------------------- .../src/modifiers/literal_scope.py | 30 +++++++++++++++++++ cursorless-talon/src/modifiers/scopes.py | 8 ++--- cursorless-talon/src/spoken_forms.json | 4 +-- cursorless-talon/src/spoken_forms.py | 4 +-- .../command/PartialTargetDescriptor.types.ts | 8 ++--- .../primitiveTargetToSpokenForm.ts | 6 ++-- .../scopeHandlers/RegexScopeHandler.ts | 8 ++--- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 6 ++-- .../src/scopeProviders/ScopeInfoProvider.ts | 2 +- .../src/spokenForms/SpokenFormType.ts | 2 +- .../spokenForms/defaultSpokenFormMapCore.ts | 2 +- .../scope/glyph/clearEveryGlyphAir.yml | 2 +- .../scope/glyph/clearFinalGlyphAir.yml | 2 +- .../recorded/scope/glyph/clearGlyphAir.yml | 2 +- .../scope/glyph/clearNextGlyphBat.yml | 2 +- .../scope/glyph/clearNextGlyphDollar.yml | 2 +- .../scope/glyph/clearNextGlyphOnyx.yml | 2 +- 19 files changed, 66 insertions(+), 66 deletions(-) delete mode 100644 cursorless-talon/src/modifiers/glyph_scope.py create mode 100644 cursorless-talon/src/modifiers/literal_scope.py diff --git a/cursorless-talon/src/cheatsheet/sections/modifiers.py b/cursorless-talon/src/cheatsheet/sections/modifiers.py index dac22a7547..95c04be221 100644 --- a/cursorless-talon/src/cheatsheet/sections/modifiers.py +++ b/cursorless-talon/src/cheatsheet/sections/modifiers.py @@ -10,7 +10,7 @@ "previous_next_modifier", "forward_backward_modifier", "position", - "glyph_modifier", + "literal_modifier", ] @@ -29,7 +29,7 @@ def get_modifiers(): "next", "backward", "forward", - "glyph", + "literal", ] simple_modifiers = { key: value @@ -104,12 +104,12 @@ def get_modifiers(): ], }, { - "id": "glyph", + "id": "literal", "type": "modifier", "variations": [ { - "spokenForm": f"{complex_modifiers['glyph']} ", - "description": "First instance of ", + "spokenForm": f"{complex_modifiers['literal']} ", + "description": "First instance of ", }, ], }, diff --git a/cursorless-talon/src/modifiers/glyph_scope.py b/cursorless-talon/src/modifiers/glyph_scope.py deleted file mode 100644 index beff4c35ed..0000000000 --- a/cursorless-talon/src/modifiers/glyph_scope.py +++ /dev/null @@ -1,30 +0,0 @@ -from talon import Module - -mod = Module() - -mod.list( - "cursorless_glyph_scope_type", - desc="Cursorless glyph scope type", -) -mod.list( - "cursorless_glyph_scope_type_plural", - desc="Plural version of Cursorless glyph scope type", -) - - -@mod.capture(rule="{user.cursorless_glyph_scope_type} ") -def cursorless_glyph_scope_type(m) -> dict[str, str]: - return { - "type": "glyph", - "character": m.any_alphanumeric_key, - } - - -@mod.capture( - rule="{user.cursorless_glyph_scope_type_plural} " -) -def cursorless_glyph_scope_type_plural(m) -> dict[str, str]: - return { - "type": "glyph", - "character": m.any_alphanumeric_key, - } diff --git a/cursorless-talon/src/modifiers/literal_scope.py b/cursorless-talon/src/modifiers/literal_scope.py new file mode 100644 index 0000000000..e6259df152 --- /dev/null +++ b/cursorless-talon/src/modifiers/literal_scope.py @@ -0,0 +1,30 @@ +from talon import Module + +mod = Module() + +mod.list( + "cursorless_literal_scope_type", + desc="Cursorless literal scope type", +) +mod.list( + "cursorless_literal_scope_type_plural", + desc="Plural version of Cursorless literal scope type", +) + + +@mod.capture(rule="{user.cursorless_literal_scope_type} ") +def cursorless_literal_scope_type(m) -> dict[str, str]: + return { + "type": "literal", + "literal": m.any_alphanumeric_key, + } + + +@mod.capture( + rule="{user.cursorless_literal_scope_type_plural} " +) +def cursorless_literal_scope_type_plural(m) -> dict[str, str]: + return { + "type": "literal", + "literal": m.any_alphanumeric_key, + } diff --git a/cursorless-talon/src/modifiers/scopes.py b/cursorless-talon/src/modifiers/scopes.py index ee1efb9b2c..7a69bd4c5d 100644 --- a/cursorless-talon/src/modifiers/scopes.py +++ b/cursorless-talon/src/modifiers/scopes.py @@ -15,7 +15,7 @@ @mod.capture( - rule="{user.cursorless_scope_type} | | {user.cursorless_custom_regex_scope_type}" + rule="{user.cursorless_scope_type} | | {user.cursorless_custom_regex_scope_type}" ) def cursorless_scope_type(m) -> dict[str, str]: """Cursorless scope type singular""" @@ -25,7 +25,7 @@ def cursorless_scope_type(m) -> dict[str, str]: pass try: - return m.cursorless_glyph_scope_type + return m.cursorless_literal_scope_type except AttributeError: pass @@ -33,7 +33,7 @@ def cursorless_scope_type(m) -> dict[str, str]: @mod.capture( - rule="{user.cursorless_scope_type_plural} | | {user.cursorless_custom_regex_scope_type_plural}" + rule="{user.cursorless_scope_type_plural} | | {user.cursorless_custom_regex_scope_type_plural}" ) def cursorless_scope_type_plural(m) -> dict[str, str]: """Cursorless scope type plural""" @@ -43,7 +43,7 @@ def cursorless_scope_type_plural(m) -> dict[str, str]: pass try: - return m.cursorless_glyph_scope_type_plural + return m.cursorless_literal_scope_type_plural except AttributeError: pass diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index 4069db5e94..4ea91e0dfb 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -171,8 +171,8 @@ "surrounding_pair_scope_type": { "string": "string" }, - "glyph_scope_type": { - "glyph": "glyph" + "literal_scope_type": { + "glyph": "literal" } }, "paired_delimiters.csv": { diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index 30ad16498d..561a0d8da8 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -68,7 +68,7 @@ def ret(filename: str, *args: P.args, **kwargs: P.kwargs) -> R: "wrapper_only_paired_delimiter": "pairedDelimiter", "surrounding_pair_scope_type": "pairedDelimiter", "scope_type": "simpleScopeTypeType", - "glyph_scope_type": "complexScopeTypeType", + "literal_scope_type": "complexScopeTypeType", "custom_regex_scope_type": "customRegex", } @@ -126,7 +126,7 @@ def handle_new_values(csv_name: str, values: list[SpokenFormEntry]): handle_csv("experimental/miscellaneous.csv"), handle_csv( "modifier_scope_types.csv", - pluralize_lists=["scope_type", "glyph_scope_type"], + pluralize_lists=["scope_type", "literal_scope_type"], extra_allowed_values=[ "private.fieldAccess", "private.switchStatementSubject", diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 1f0b02bb9e..ce1524857a 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -205,9 +205,9 @@ export interface OneOfScopeType { scopeTypes: ScopeType[]; } -export interface GlyphScopeType { - type: "glyph"; - character: string; +export interface LiteralScopeType { + type: "literal"; + literal: string; } export type ScopeType = @@ -215,7 +215,7 @@ export type ScopeType = | SurroundingPairScopeType | CustomRegexScopeType | OneOfScopeType - | GlyphScopeType; + | LiteralScopeType; export interface ContainingSurroundingPairModifier extends ContainingScopeModifier { diff --git a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index 6dba6a8f65..9ca7b53ffc 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -208,10 +208,10 @@ export class PrimitiveTargetSpokenFormGenerator { switch (scopeType.type) { case "oneOf": throw new NoSpokenFormError(`Scope type '${scopeType.type}'`); - case "glyph": + case "literal": return [ - this.spokenFormMap.complexScopeTypeType.glyph, - characterToSpokenForm(scopeType.character), + this.spokenFormMap.complexScopeTypeType.literal, + characterToSpokenForm(scopeType.literal), ]; case "surroundingPair": { const pair = this.spokenFormMap.pairedDelimiter[scopeType.delimiter]; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts index a8bb130540..59cd85c9fa 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts @@ -1,7 +1,7 @@ import { CustomRegexScopeType, Direction, - GlyphScopeType, + LiteralScopeType, ScopeType, } from "@cursorless/common"; import { imap } from "itertools"; @@ -69,14 +69,14 @@ export class CustomRegexScopeHandler extends RegexStageBase { } } -export class GlyphScopeHandler extends RegexStageBase { +export class LiteralScopeHandler extends RegexStageBase { get regex() { - return new RegExp(escapeRegExp(this.scopeType.character), "gui"); + return new RegExp(escapeRegExp(this.scopeType.literal), "gui"); } constructor( scopeHandlerFactory: ScopeHandlerFactory, - readonly scopeType: GlyphScopeType, + readonly scopeType: LiteralScopeType, languageId: string, ) { super(scopeHandlerFactory, scopeType, languageId); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 25c3575553..ee2ec50c68 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -8,7 +8,7 @@ import { OneOfScopeHandler } from "./OneOfScopeHandler"; import { ParagraphScopeHandler } from "./ParagraphScopeHandler"; import { CustomRegexScopeHandler, - GlyphScopeHandler, + LiteralScopeHandler, NonWhitespaceSequenceScopeHandler, UrlScopeHandler, } from "./RegexScopeHandler"; @@ -73,8 +73,8 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { return new UrlScopeHandler(this, scopeType, languageId); case "customRegex": return new CustomRegexScopeHandler(this, scopeType, languageId); - case "glyph": - return new GlyphScopeHandler(this, scopeType, languageId); + case "literal": + return new LiteralScopeHandler(this, scopeType, languageId); case "custom": return scopeType.scopeHandler; case "instance": diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index f183ee1f3d..6fa7928697 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -173,7 +173,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "notebookCell": case "surroundingPair": case "customRegex": - case "glyph": + case "literal": return false; case "oneOf": diff --git a/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts b/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts index e1d604fce3..959aef419a 100644 --- a/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts +++ b/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts @@ -14,7 +14,7 @@ import { export interface SpokenFormMapKeyTypes { pairedDelimiter: SpeakableSurroundingPairName; simpleScopeTypeType: SimpleScopeTypeType; - complexScopeTypeType: "glyph"; + complexScopeTypeType: "literal"; surroundingPairForceDirection: "left" | "right"; /** diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 6b94ec67ba..296a09ff52 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -101,7 +101,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { ["private.switchStatementSubject"]: isPrivate("subject"), }, complexScopeTypeType: { - glyph: "glyph", + literal: "glyph", }, surroundingPairForceDirection: { diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml index c79ff8bef0..5c2ed6eb37 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: everyScope - scopeType: {type: glyph, character: a} + scopeType: {type: literal, literal: a} usePrePhraseSnapshot: true initialState: documentContents: abc$!4123a diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml index 782322b77f..e376a9b037 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: ordinalScope - scopeType: {type: glyph, character: a} + scopeType: {type: literal, literal: a} start: -1 length: 1 usePrePhraseSnapshot: true diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml index 151edb4d22..637d89091f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: containingScope - scopeType: {type: glyph, character: a} + scopeType: {type: literal, literal: a} usePrePhraseSnapshot: true initialState: documentContents: abc$!4123a diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml index 4185b97b45..7eeed9a4b1 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: relativeScope - scopeType: {type: glyph, character: b} + scopeType: {type: literal, literal: b} offset: 1 length: 1 direction: forward diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml index b6fdc326d9..3ad570955e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: relativeScope - scopeType: {type: glyph, character: $} + scopeType: {type: literal, literal: $} offset: 1 length: 1 direction: forward diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml index 84e35bc89d..52c9e3b685 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: relativeScope - scopeType: {type: glyph, character: å} + scopeType: {type: literal, literal: å} offset: 1 length: 1 direction: forward From 31f4d3d4a0de65826005cd3a1a2d43d63fae6d77 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 9 Dec 2023 09:54:40 +0100 Subject: [PATCH 13/16] Revert "Renamed glyph_scope to literal_scope" This reverts commit 4ed60fbb4435028ff9564683826581d2ed410d19. --- .../src/cheatsheet/sections/modifiers.py | 10 +++---- cursorless-talon/src/modifiers/glyph_scope.py | 30 +++++++++++++++++++ .../src/modifiers/literal_scope.py | 30 ------------------- cursorless-talon/src/modifiers/scopes.py | 8 ++--- cursorless-talon/src/spoken_forms.json | 4 +-- cursorless-talon/src/spoken_forms.py | 4 +-- .../command/PartialTargetDescriptor.types.ts | 8 ++--- .../primitiveTargetToSpokenForm.ts | 6 ++-- .../scopeHandlers/RegexScopeHandler.ts | 8 ++--- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 6 ++-- .../src/scopeProviders/ScopeInfoProvider.ts | 2 +- .../src/spokenForms/SpokenFormType.ts | 2 +- .../spokenForms/defaultSpokenFormMapCore.ts | 2 +- .../scope/glyph/clearEveryGlyphAir.yml | 2 +- .../scope/glyph/clearFinalGlyphAir.yml | 2 +- .../recorded/scope/glyph/clearGlyphAir.yml | 2 +- .../scope/glyph/clearNextGlyphBat.yml | 2 +- .../scope/glyph/clearNextGlyphDollar.yml | 2 +- .../scope/glyph/clearNextGlyphOnyx.yml | 2 +- 19 files changed, 66 insertions(+), 66 deletions(-) create mode 100644 cursorless-talon/src/modifiers/glyph_scope.py delete mode 100644 cursorless-talon/src/modifiers/literal_scope.py diff --git a/cursorless-talon/src/cheatsheet/sections/modifiers.py b/cursorless-talon/src/cheatsheet/sections/modifiers.py index 95c04be221..dac22a7547 100644 --- a/cursorless-talon/src/cheatsheet/sections/modifiers.py +++ b/cursorless-talon/src/cheatsheet/sections/modifiers.py @@ -10,7 +10,7 @@ "previous_next_modifier", "forward_backward_modifier", "position", - "literal_modifier", + "glyph_modifier", ] @@ -29,7 +29,7 @@ def get_modifiers(): "next", "backward", "forward", - "literal", + "glyph", ] simple_modifiers = { key: value @@ -104,12 +104,12 @@ def get_modifiers(): ], }, { - "id": "literal", + "id": "glyph", "type": "modifier", "variations": [ { - "spokenForm": f"{complex_modifiers['literal']} ", - "description": "First instance of ", + "spokenForm": f"{complex_modifiers['glyph']} ", + "description": "First instance of ", }, ], }, diff --git a/cursorless-talon/src/modifiers/glyph_scope.py b/cursorless-talon/src/modifiers/glyph_scope.py new file mode 100644 index 0000000000..beff4c35ed --- /dev/null +++ b/cursorless-talon/src/modifiers/glyph_scope.py @@ -0,0 +1,30 @@ +from talon import Module + +mod = Module() + +mod.list( + "cursorless_glyph_scope_type", + desc="Cursorless glyph scope type", +) +mod.list( + "cursorless_glyph_scope_type_plural", + desc="Plural version of Cursorless glyph scope type", +) + + +@mod.capture(rule="{user.cursorless_glyph_scope_type} ") +def cursorless_glyph_scope_type(m) -> dict[str, str]: + return { + "type": "glyph", + "character": m.any_alphanumeric_key, + } + + +@mod.capture( + rule="{user.cursorless_glyph_scope_type_plural} " +) +def cursorless_glyph_scope_type_plural(m) -> dict[str, str]: + return { + "type": "glyph", + "character": m.any_alphanumeric_key, + } diff --git a/cursorless-talon/src/modifiers/literal_scope.py b/cursorless-talon/src/modifiers/literal_scope.py deleted file mode 100644 index e6259df152..0000000000 --- a/cursorless-talon/src/modifiers/literal_scope.py +++ /dev/null @@ -1,30 +0,0 @@ -from talon import Module - -mod = Module() - -mod.list( - "cursorless_literal_scope_type", - desc="Cursorless literal scope type", -) -mod.list( - "cursorless_literal_scope_type_plural", - desc="Plural version of Cursorless literal scope type", -) - - -@mod.capture(rule="{user.cursorless_literal_scope_type} ") -def cursorless_literal_scope_type(m) -> dict[str, str]: - return { - "type": "literal", - "literal": m.any_alphanumeric_key, - } - - -@mod.capture( - rule="{user.cursorless_literal_scope_type_plural} " -) -def cursorless_literal_scope_type_plural(m) -> dict[str, str]: - return { - "type": "literal", - "literal": m.any_alphanumeric_key, - } diff --git a/cursorless-talon/src/modifiers/scopes.py b/cursorless-talon/src/modifiers/scopes.py index 7a69bd4c5d..ee1efb9b2c 100644 --- a/cursorless-talon/src/modifiers/scopes.py +++ b/cursorless-talon/src/modifiers/scopes.py @@ -15,7 +15,7 @@ @mod.capture( - rule="{user.cursorless_scope_type} | | {user.cursorless_custom_regex_scope_type}" + rule="{user.cursorless_scope_type} | | {user.cursorless_custom_regex_scope_type}" ) def cursorless_scope_type(m) -> dict[str, str]: """Cursorless scope type singular""" @@ -25,7 +25,7 @@ def cursorless_scope_type(m) -> dict[str, str]: pass try: - return m.cursorless_literal_scope_type + return m.cursorless_glyph_scope_type except AttributeError: pass @@ -33,7 +33,7 @@ def cursorless_scope_type(m) -> dict[str, str]: @mod.capture( - rule="{user.cursorless_scope_type_plural} | | {user.cursorless_custom_regex_scope_type_plural}" + rule="{user.cursorless_scope_type_plural} | | {user.cursorless_custom_regex_scope_type_plural}" ) def cursorless_scope_type_plural(m) -> dict[str, str]: """Cursorless scope type plural""" @@ -43,7 +43,7 @@ def cursorless_scope_type_plural(m) -> dict[str, str]: pass try: - return m.cursorless_literal_scope_type_plural + return m.cursorless_glyph_scope_type_plural except AttributeError: pass diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index 88adfe3478..eec6aac6e2 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -172,8 +172,8 @@ "surrounding_pair_scope_type": { "string": "string" }, - "literal_scope_type": { - "glyph": "literal" + "glyph_scope_type": { + "glyph": "glyph" } }, "paired_delimiters.csv": { diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index 561a0d8da8..30ad16498d 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -68,7 +68,7 @@ def ret(filename: str, *args: P.args, **kwargs: P.kwargs) -> R: "wrapper_only_paired_delimiter": "pairedDelimiter", "surrounding_pair_scope_type": "pairedDelimiter", "scope_type": "simpleScopeTypeType", - "literal_scope_type": "complexScopeTypeType", + "glyph_scope_type": "complexScopeTypeType", "custom_regex_scope_type": "customRegex", } @@ -126,7 +126,7 @@ def handle_new_values(csv_name: str, values: list[SpokenFormEntry]): handle_csv("experimental/miscellaneous.csv"), handle_csv( "modifier_scope_types.csv", - pluralize_lists=["scope_type", "literal_scope_type"], + pluralize_lists=["scope_type", "glyph_scope_type"], extra_allowed_values=[ "private.fieldAccess", "private.switchStatementSubject", diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index ce1524857a..1f0b02bb9e 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -205,9 +205,9 @@ export interface OneOfScopeType { scopeTypes: ScopeType[]; } -export interface LiteralScopeType { - type: "literal"; - literal: string; +export interface GlyphScopeType { + type: "glyph"; + character: string; } export type ScopeType = @@ -215,7 +215,7 @@ export type ScopeType = | SurroundingPairScopeType | CustomRegexScopeType | OneOfScopeType - | LiteralScopeType; + | GlyphScopeType; export interface ContainingSurroundingPairModifier extends ContainingScopeModifier { diff --git a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index 9ca7b53ffc..6dba6a8f65 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -208,10 +208,10 @@ export class PrimitiveTargetSpokenFormGenerator { switch (scopeType.type) { case "oneOf": throw new NoSpokenFormError(`Scope type '${scopeType.type}'`); - case "literal": + case "glyph": return [ - this.spokenFormMap.complexScopeTypeType.literal, - characterToSpokenForm(scopeType.literal), + this.spokenFormMap.complexScopeTypeType.glyph, + characterToSpokenForm(scopeType.character), ]; case "surroundingPair": { const pair = this.spokenFormMap.pairedDelimiter[scopeType.delimiter]; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts index 59cd85c9fa..a8bb130540 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/RegexScopeHandler.ts @@ -1,7 +1,7 @@ import { CustomRegexScopeType, Direction, - LiteralScopeType, + GlyphScopeType, ScopeType, } from "@cursorless/common"; import { imap } from "itertools"; @@ -69,14 +69,14 @@ export class CustomRegexScopeHandler extends RegexStageBase { } } -export class LiteralScopeHandler extends RegexStageBase { +export class GlyphScopeHandler extends RegexStageBase { get regex() { - return new RegExp(escapeRegExp(this.scopeType.literal), "gui"); + return new RegExp(escapeRegExp(this.scopeType.character), "gui"); } constructor( scopeHandlerFactory: ScopeHandlerFactory, - readonly scopeType: LiteralScopeType, + readonly scopeType: GlyphScopeType, languageId: string, ) { super(scopeHandlerFactory, scopeType, languageId); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index ee2ec50c68..25c3575553 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -8,7 +8,7 @@ import { OneOfScopeHandler } from "./OneOfScopeHandler"; import { ParagraphScopeHandler } from "./ParagraphScopeHandler"; import { CustomRegexScopeHandler, - LiteralScopeHandler, + GlyphScopeHandler, NonWhitespaceSequenceScopeHandler, UrlScopeHandler, } from "./RegexScopeHandler"; @@ -73,8 +73,8 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { return new UrlScopeHandler(this, scopeType, languageId); case "customRegex": return new CustomRegexScopeHandler(this, scopeType, languageId); - case "literal": - return new LiteralScopeHandler(this, scopeType, languageId); + case "glyph": + return new GlyphScopeHandler(this, scopeType, languageId); case "custom": return scopeType.scopeHandler; case "instance": diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index 6fa7928697..f183ee1f3d 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -173,7 +173,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "notebookCell": case "surroundingPair": case "customRegex": - case "literal": + case "glyph": return false; case "oneOf": diff --git a/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts b/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts index 959aef419a..e1d604fce3 100644 --- a/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts +++ b/packages/cursorless-engine/src/spokenForms/SpokenFormType.ts @@ -14,7 +14,7 @@ import { export interface SpokenFormMapKeyTypes { pairedDelimiter: SpeakableSurroundingPairName; simpleScopeTypeType: SimpleScopeTypeType; - complexScopeTypeType: "literal"; + complexScopeTypeType: "glyph"; surroundingPairForceDirection: "left" | "right"; /** diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 296a09ff52..6b94ec67ba 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -101,7 +101,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { ["private.switchStatementSubject"]: isPrivate("subject"), }, complexScopeTypeType: { - literal: "glyph", + glyph: "glyph", }, surroundingPairForceDirection: { diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml index 5c2ed6eb37..c79ff8bef0 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearEveryGlyphAir.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: everyScope - scopeType: {type: literal, literal: a} + scopeType: {type: glyph, character: a} usePrePhraseSnapshot: true initialState: documentContents: abc$!4123a diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml index e376a9b037..782322b77f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearFinalGlyphAir.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: ordinalScope - scopeType: {type: literal, literal: a} + scopeType: {type: glyph, character: a} start: -1 length: 1 usePrePhraseSnapshot: true diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml index 637d89091f..151edb4d22 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearGlyphAir.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: containingScope - scopeType: {type: literal, literal: a} + scopeType: {type: glyph, character: a} usePrePhraseSnapshot: true initialState: documentContents: abc$!4123a diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml index 7eeed9a4b1..4185b97b45 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphBat.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: relativeScope - scopeType: {type: literal, literal: b} + scopeType: {type: glyph, character: b} offset: 1 length: 1 direction: forward diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml index 3ad570955e..b6fdc326d9 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphDollar.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: relativeScope - scopeType: {type: literal, literal: $} + scopeType: {type: glyph, character: $} offset: 1 length: 1 direction: forward diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml index 52c9e3b685..84e35bc89d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/scope/glyph/clearNextGlyphOnyx.yml @@ -8,7 +8,7 @@ command: type: primitive modifiers: - type: relativeScope - scopeType: {type: literal, literal: å} + scopeType: {type: glyph, character: å} offset: 1 length: 1 direction: forward From f739ba3c904032bfa411bf06d11de327c25b356b Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:41:53 +0000 Subject: [PATCH 14/16] Add test --- .../CustomSpokenFormGeneratorImpl.test.ts | 35 +++++++++++++++++++ .../CustomSpokenFormGeneratorImpl.ts | 7 ++++ .../src/spokenForms/CustomSpokenForms.ts | 23 +++++------- 3 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.test.ts diff --git a/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.test.ts b/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.test.ts new file mode 100644 index 0000000000..bdfd6b0954 --- /dev/null +++ b/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.test.ts @@ -0,0 +1,35 @@ +import assert from "node:assert"; +import { CustomSpokenFormGeneratorImpl } from "./CustomSpokenFormGeneratorImpl"; +import { asyncSafety } from "@cursorless/common"; + +suite("CustomSpokenFormGeneratorImpl", async function () { + test( + "glyph", + asyncSafety(async () => { + const generator = new CustomSpokenFormGeneratorImpl({ + async getSpokenFormEntries() { + return [ + { + type: "complexScopeTypeType", + id: "glyph", + spokenForms: ["foo"], + }, + ]; + }, + onDidChange: () => ({ dispose() {} }), + }); + + await generator.customSpokenFormsInitialized; + + const spokenForm = generator.scopeTypeToSpokenForm({ + type: "glyph", + character: "a", + }); + + assert.deepStrictEqual(spokenForm, { + type: "success", + spokenForms: ["foo air"], + }); + }), + ); +}); diff --git a/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.ts b/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.ts index 481a69818d..a572d12b83 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/CustomSpokenFormGeneratorImpl.ts @@ -21,8 +21,15 @@ export class CustomSpokenFormGeneratorImpl private spokenFormGenerator: SpokenFormGenerator; private disposable: Disposable; + /** + * A promise that resolves when the custom spoken forms have been loaded. + */ + public readonly customSpokenFormsInitialized: Promise; + constructor(talonSpokenForms: TalonSpokenForms) { this.customSpokenForms = new CustomSpokenForms(talonSpokenForms); + this.customSpokenFormsInitialized = + this.customSpokenForms.customSpokenFormsInitialized; this.spokenFormGenerator = new SpokenFormGenerator( this.customSpokenForms.spokenFormMap, ); diff --git a/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts b/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts index 28ff5e3c8f..4e650bdee2 100644 --- a/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts +++ b/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts @@ -34,13 +34,17 @@ export class CustomSpokenForms { private disposable: Disposable; private notifier = new Notifier(); + /** + * A promise that resolves when the custom spoken forms have been loaded. + */ + public readonly customSpokenFormsInitialized: Promise; + private spokenFormMap_: Writable = { ...defaultSpokenFormMap }; get spokenFormMap(): SpokenFormMap { return this.spokenFormMap_; } - private customSpokenFormsInitialized_ = false; private needsInitialTalonUpdate_: boolean | undefined; /** @@ -51,21 +55,12 @@ export class CustomSpokenForms { return this.needsInitialTalonUpdate_; } - /** - * Whether the custom spoken forms have been initialized. If `false`, the - * default spoken forms are currently being used while the custom spoken forms - * are being loaded. - */ - get customSpokenFormsInitialized() { - return this.customSpokenFormsInitialized_; - } - constructor(private talonSpokenForms: TalonSpokenForms) { this.disposable = talonSpokenForms.onDidChange(() => this.updateSpokenFormMaps(), ); - this.updateSpokenFormMaps(); + this.customSpokenFormsInitialized = this.updateSpokenFormMaps(); } /** @@ -98,10 +93,9 @@ export class CustomSpokenForms { } this.spokenFormMap_ = { ...defaultSpokenFormMap }; - this.customSpokenFormsInitialized_ = false; this.notifier.notifyListeners(); - return; + throw err; } for (const entryType of SUPPORTED_ENTRY_TYPES) { @@ -117,7 +111,6 @@ export class CustomSpokenForms { ); } - this.customSpokenFormsInitialized_ = true; this.notifier.notifyListeners(); } @@ -175,7 +168,7 @@ function updateEntriesForType( defaultSpokenForms, spokenForms: customSpokenForms, requiresTalonUpdate: false, - isCustom: isEqual(defaultSpokenForms, customSpokenForms), + isCustom: !isEqual(defaultSpokenForms, customSpokenForms), isPrivate, }; } From 518cd6e4795f0dcb9700d147d1a33eb35d5e9872 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 11 Dec 2023 18:53:56 +0100 Subject: [PATCH 15/16] Update cheat sheet --- .../src/cheatsheet/sections/modifiers.py | 12 -------- .../src/cheatsheet/sections/scopes.py | 28 ++++++++++++++----- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/cursorless-talon/src/cheatsheet/sections/modifiers.py b/cursorless-talon/src/cheatsheet/sections/modifiers.py index dac22a7547..576edc4721 100644 --- a/cursorless-talon/src/cheatsheet/sections/modifiers.py +++ b/cursorless-talon/src/cheatsheet/sections/modifiers.py @@ -10,7 +10,6 @@ "previous_next_modifier", "forward_backward_modifier", "position", - "glyph_modifier", ] @@ -29,7 +28,6 @@ def get_modifiers(): "next", "backward", "forward", - "glyph", ] simple_modifiers = { key: value @@ -103,16 +101,6 @@ def get_modifiers(): }, ], }, - { - "id": "glyph", - "type": "modifier", - "variations": [ - { - "spokenForm": f"{complex_modifiers['glyph']} ", - "description": "First instance of ", - }, - ], - }, { "id": "relativeScope", "type": "modifier", diff --git a/cursorless-talon/src/cheatsheet/sections/scopes.py b/cursorless-talon/src/cheatsheet/sections/scopes.py index b7133c9a72..0ac8051b8c 100644 --- a/cursorless-talon/src/cheatsheet/sections/scopes.py +++ b/cursorless-talon/src/cheatsheet/sections/scopes.py @@ -1,12 +1,26 @@ -from ..get_list import get_lists +from ..get_list import get_lists, get_raw_list def get_scopes(): - return get_lists( - ["scope_type"], - "scopeType", + complex_scopes = get_raw_list("glyph_scope_type") + return [ + *get_lists( + ["scope_type"], + "scopeType", + { + "argumentOrParameter": "Argument", + "boundedNonWhitespaceSequence": "Non whitespace sequence stopped by surrounding pair delimeters", + "glyph": "stuff", + }, + ), { - "argumentOrParameter": "Argument", - "boundedNonWhitespaceSequence": "Non whitespace sequence stopped by surrounding pair delimeters", + "id": "glyph", + "type": "modifier", + "variations": [ + { + "spokenForm": f"{complex_scopes['glyph']} ", + "description": "First instance of ", + }, + ], }, - ) + ] From bc474c956eb3bd00eb674597d03588cab7e0e534 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:53:19 +0000 Subject: [PATCH 16/16] Fix cheatsheet --- .../src/cheatsheet/sections/scopes.py | 7 ++- .../lib/sampleSpokenFormInfos/defaults.json | 52 ++++++++++++++++++- .../cursorless-engine/src/core/Cheatsheet.ts | 25 ++------- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/cursorless-talon/src/cheatsheet/sections/scopes.py b/cursorless-talon/src/cheatsheet/sections/scopes.py index 0ac8051b8c..9c610891fe 100644 --- a/cursorless-talon/src/cheatsheet/sections/scopes.py +++ b/cursorless-talon/src/cheatsheet/sections/scopes.py @@ -10,16 +10,15 @@ def get_scopes(): { "argumentOrParameter": "Argument", "boundedNonWhitespaceSequence": "Non whitespace sequence stopped by surrounding pair delimeters", - "glyph": "stuff", }, ), { "id": "glyph", - "type": "modifier", + "type": "scopeType", "variations": [ { - "spokenForm": f"{complex_scopes['glyph']} ", - "description": "First instance of ", + "spokenForm": f"{complex_scopes['glyph']} ", + "description": "Instance of single character ", }, ], }, diff --git a/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json b/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json index 1c1e45f74a..f70df30696 100644 --- a/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json +++ b/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json @@ -14,6 +14,16 @@ } ] }, + { + "id": "breakLine", + "type": "action", + "variations": [ + { + "spokenForm": "break ", + "description": "Break line" + } + ] + }, { "id": "callAsFunction", "type": "action", @@ -228,6 +238,16 @@ } ] }, + { + "id": "joinLines", + "type": "action", + "variations": [ + { + "spokenForm": "join ", + "description": "Join lines" + } + ] + }, { "id": "moveToTarget", "type": "action", @@ -905,6 +925,16 @@ "description": "Trailing delimiter range" } ] + }, + { + "id": "visible", + "type": "modifier", + "variations": [ + { + "spokenForm": "visible", + "description": "Visible" + } + ] } ] }, @@ -1058,6 +1088,16 @@ } ] }, + { + "id": "show_scope_sidebar", + "type": "command", + "variations": [ + { + "spokenForm": "bar cursorless", + "description": "Show cursorless sidebar" + } + ] + }, { "id": "show_scope_visualizer", "type": "command", @@ -1272,6 +1312,16 @@ } ] }, + { + "id": "glyph", + "type": "scopeType", + "variations": [ + { + "spokenForm": "glyph ", + "description": "Instance of single character " + } + ] + }, { "id": "identifier", "type": "scopeType", @@ -1537,7 +1587,7 @@ "type": "scopeType", "variations": [ { - "spokenForm": "word", + "spokenForm": "sub", "description": "Word" } ] diff --git a/packages/cursorless-engine/src/core/Cheatsheet.ts b/packages/cursorless-engine/src/core/Cheatsheet.ts index 2c8089fac0..5cfd18fa48 100644 --- a/packages/cursorless-engine/src/core/Cheatsheet.ts +++ b/packages/cursorless-engine/src/core/Cheatsheet.ts @@ -4,6 +4,7 @@ import produce from "immer"; import { sortBy } from "lodash"; import { ide } from "../singletons/ide.singleton"; import path from "path"; +import { getCursorlessRepoRoot } from "@cursorless/common"; /** * The argument expected by the cheatsheet command. @@ -56,27 +57,9 @@ export async function showCheatsheet({ * @param spokenFormInfo The new value to use for default spoken forms. */ export async function updateDefaults(spokenFormInfo: CheatsheetInfo) { - const { runMode, assetsRoot, workspaceFolders } = ide(); - - const workspacePath = - runMode === "development" - ? assetsRoot - : workspaceFolders?.[0].uri.path ?? null; - - if (workspacePath == null) { - throw new Error( - "Please update defaults from Cursorless workspace or running in debug", - ); - } - const defaultsPath = path.join( - workspacePath, - "packages", - "cheatsheet", - "src", - "lib", - "sampleSpokenFormInfos", - "defaults.json", + getCursorlessRepoRoot(), + "packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json", ); const outputObject = produce(spokenFormInfo, (draft) => { @@ -86,7 +69,7 @@ export async function updateDefaults(spokenFormInfo: CheatsheetInfo) { }); }); - await writeFile(defaultsPath, JSON.stringify(outputObject, null, "\t")); + await writeFile(defaultsPath, JSON.stringify(outputObject, null, 2) + "\n"); } // FIXME: Stop duplicating these types once we have #945