From 466fe6466524d424576ecf3fa4c3cc3defa219ac Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:37:15 +0000 Subject: [PATCH 01/59] Initial tutorial work --- .../src/get_action_spoken_form.py | 30 +++++++ cursorless-talon/src/tutorial.py | 80 +++++++++++++++++++ .../tutorial/extra-cloning-a-talon-list.py | 9 +++ data/playground/tutorial/unit-1-basics.txt | 11 +++ .../tutorial/unit-2-basic-coding.py | 13 +++ .../bringBlockMade.yml | 58 ++++++++++++++ .../clearCoreSun.yml | 59 ++++++++++++++ .../clearCoreYank.yml | 61 ++++++++++++++ .../clearDownScoreAndCap.yml | 66 +++++++++++++++ .../clearRepperLeper.yml | 57 +++++++++++++ .../extra-cloning-a-talon-list/clearWhale.yml | 55 +++++++++++++ .../takeLookCloneLineWordFrown.yml | 55 +++++++++++++ .../takeLookCloneLineWordFrown2.yml | 58 ++++++++++++++ .../tutorial/unit-1-basics/chuckDrum.yml | 53 ++++++++++++ .../tutorial/unit-1-basics/chuckLineLook.yml | 55 +++++++++++++ .../tutorial/unit-1-basics/clearTrap.yml | 47 +++++++++++ .../unit-1-basics/clearWhaleWordYou.yml | 47 +++++++++++ .../tutorial/unit-1-basics/postLook.yml | 47 +++++++++++ .../tutorial/unit-1-basics/preInk.yml | 49 ++++++++++++ .../tutorial/unit-1-basics/script.json | 14 ++++ .../tutorial/unit-1-basics/takeBlueSun.yml | 49 ++++++++++++ .../unit-1-basics/takeCapAndWhale.yml | 60 ++++++++++++++ .../unit-1-basics/takeEachPastKick.yml | 59 ++++++++++++++ .../tutorial/unit-1-basics/takeLine.yml | 52 ++++++++++++ .../tutorial/unit-1-basics/takeWhale.yml | 49 ++++++++++++ .../bringBlueCapToValueRisk.yml | 70 ++++++++++++++++ .../unit-2-basic-coding/bringStateUrge.yml | 66 +++++++++++++++ .../chuckArgueBlueVest.yml | 62 ++++++++++++++ .../unit-2-basic-coding/cloneStateInk.yml | 61 ++++++++++++++ .../unit-2-basic-coding/dedentThis.yml | 56 +++++++++++++ .../tutorial/unit-2-basic-coding/pourUrge.yml | 58 ++++++++++++++ .../tutorial/unit-2-basic-coding/script.json | 12 +++ .../swapStringAirWithWhale.yml | 68 ++++++++++++++++ 33 files changed, 1646 insertions(+) create mode 100644 cursorless-talon/src/get_action_spoken_form.py create mode 100644 cursorless-talon/src/tutorial.py create mode 100644 data/playground/tutorial/extra-cloning-a-talon-list.py create mode 100644 data/playground/tutorial/unit-1-basics.txt create mode 100644 data/playground/tutorial/unit-2-basic-coding.py create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml diff --git a/cursorless-talon/src/get_action_spoken_form.py b/cursorless-talon/src/get_action_spoken_form.py new file mode 100644 index 0000000000..f271be666a --- /dev/null +++ b/cursorless-talon/src/get_action_spoken_form.py @@ -0,0 +1,30 @@ +from talon import registry + +from .actions.actions import ACTION_LIST_NAMES +from .conventions import get_cursorless_list_name + + +def make_cursorless_list_reverse_look_up(*raw_list_names: str): + return make_list_reverse_look_up( + *[get_cursorless_list_name(raw_list_name) for raw_list_name in raw_list_names] + ) + + +def make_list_reverse_look_up(*list_names: str): + """ + Given a list of talon list names, returns a function that does a reverse + look-up in all lists to find the spoken form for its input. + """ + + def return_func(argument: str): + for list_name in list_names: + for spoken_form, value in registry.lists[list_name][-1].items(): + if value == argument: + return list_name, spoken_form + + raise LookupError(f"Unknown identifier `{argument}`") + + return return_func + + +lookup_action = make_cursorless_list_reverse_look_up(*ACTION_LIST_NAMES) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py new file mode 100644 index 0000000000..e500422b84 --- /dev/null +++ b/cursorless-talon/src/tutorial.py @@ -0,0 +1,80 @@ +import json +import re +from pathlib import Path +from typing import Callable + +import yaml +from talon import actions, app + +from .get_action_spoken_form import lookup_action + +regex = re.compile(r"\{(\w+):([^}]+)\}") +tutorial_dir = Path( + "/Users/pokey/src/cursorless-vscode/src/test/suite/fixtures/recorded/tutorial/unit-2-basic-coding" +) + + +def process_literal_step(argument: str): + return f"" + + +def process_action(argument: str): + _, spoken_form = lookup_action(argument) + return f'<*"{spoken_form}"/>' + + +def process_scope_type(argument: str): + _, spoken_form = lookup_scope_type(argument) + return f'<*"{spoken_form}"/>' + + +def process_cursorless_command_step(argument: str): + step_fixture = yaml.safe_load((tutorial_dir / argument).read_text()) + return f"" + + +interpolation_processor_map: dict[str, Callable[[str], str]] = { + "literalStep": process_literal_step, + "action": process_action, + "scopeType": process_scope_type, + "step": process_cursorless_command_step, +} + + +def process_tutorial_step(raw: str): + print(f"{raw=}") + current_index = 0 + content = "" + for match in regex.finditer(raw): + content += raw[current_index : match.start()] + content += interpolation_processor_map[match.group(1)](match.group(2)) + current_index = match.end() + content += raw[current_index : len(raw)] + print(f"{content=}") + + return { + "content": content, + "restore_callback": print, + "modes": ["command"], + "app": "Code", + "context_hint": "Please open VSCode and enter command mode", + } + + +def get_basic_coding_walkthrough(): + with open(tutorial_dir / "script.json") as f: + script = json.load(f) + + return [ + actions.user.hud_create_walkthrough_step(**process_tutorial_step(step)) + for step in script + ] + + +def on_ready(): + actions.user.hud_add_lazy_walkthrough( + "Cursorless basic coding", get_basic_coding_walkthrough + ) + + +app.register("ready", on_ready) diff --git a/data/playground/tutorial/extra-cloning-a-talon-list.py b/data/playground/tutorial/extra-cloning-a-talon-list.py new file mode 100644 index 0000000000..75d0c0b759 --- /dev/null +++ b/data/playground/tutorial/extra-cloning-a-talon-list.py @@ -0,0 +1,9 @@ +from talon import Context, Module + +mod = Module() +ctx = Context() + +mod.list("cursorless_walkthrough_list", desc="My tutorial list") +ctx.list["user.cursorless_walkthrough_list"] = { + "spoken form": "whatever", +} diff --git a/data/playground/tutorial/unit-1-basics.txt b/data/playground/tutorial/unit-1-basics.txt new file mode 100644 index 0000000000..ac66cd3ef8 --- /dev/null +++ b/data/playground/tutorial/unit-1-basics.txt @@ -0,0 +1,11 @@ +================================================== +========== ========== +========== Welcome to Cursorless! ========== +========== ========== +========== Let's start using marks ========== +========== ========== +========== so we can navigate around ========== +========== ========== +========== without lifting a finger! ========== +========== ========== +================================================== diff --git a/data/playground/tutorial/unit-2-basic-coding.py b/data/playground/tutorial/unit-2-basic-coding.py new file mode 100644 index 0000000000..636809337d --- /dev/null +++ b/data/playground/tutorial/unit-2-basic-coding.py @@ -0,0 +1,13 @@ +def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + +def invert_color(color): + if color == "black": + return "white" + + +print_color("black") diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml new file mode 100644 index 0000000000..238c50abd2 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml @@ -0,0 +1,58 @@ +languageId: python +command: + spokenForm: bring block made + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: m} + modifiers: + - type: containingScope + scopeType: {type: paragraph} + - {type: primitive, isImplicit: true} + usePrePhraseSnapshot: false + action: {name: replaceWithTarget} +initialState: + documentContents: |+ + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + selections: + - anchor: {line: 10, character: 0} + active: {line: 10, character: 0} + marks: + default.m: + start: {line: 5, character: 0} + end: {line: 5, character: 3} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + selections: + - anchor: {line: 13, character: 1} + active: {line: 13, character: 1} + thatMark: + - anchor: {line: 10, character: 0} + active: {line: 13, character: 1} + sourceMark: + - anchor: {line: 5, character: 0} + active: {line: 8, character: 1} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: paragraph, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: cursor}, selectionType: paragraph, position: contents, insideOutsideType: null, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml new file mode 100644 index 0000000000..cf44b6ecbb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml @@ -0,0 +1,59 @@ +languageId: python +command: + spokenForm: clear core sun + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: s} + modifiers: + - {type: interiorOnly} + - type: containingScope + scopeType: {type: surroundingPair, delimiter: any} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "spoken form": "whatever", + } + selections: + - anchor: {line: 10, character: 30} + active: {line: 10, character: 30} + marks: + default.s: + start: {line: 12, character: 5} + end: {line: 12, character: 11} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "": "whatever", + } + selections: + - anchor: {line: 12, character: 5} + active: {line: 12, character: 5} + thatMark: + - anchor: {line: 12, character: 5} + active: {line: 12, character: 5} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: s}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: interiorOnly}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml new file mode 100644 index 0000000000..3042279c5f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml @@ -0,0 +1,61 @@ +languageId: python +command: + spokenForm: clear core yank + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: 'y'} + modifiers: + - {type: interiorOnly} + - type: containingScope + scopeType: {type: surroundingPair, delimiter: any} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="My tutorial list") + ctx.list['user.emoji'] = { + "spoken form": "whatever", + } + selections: + - anchor: {line: 10, character: 15} + active: {line: 10, character: 15} + - anchor: {line: 11, character: 20} + active: {line: 11, character: 20} + marks: + default.y: + start: {line: 10, character: 24} + end: {line: 10, character: 26} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="") + ctx.list['user.emoji'] = { + "spoken form": "whatever", + } + selections: + - anchor: {line: 10, character: 24} + active: {line: 10, character: 24} + thatMark: + - anchor: {line: 10, character: 24} + active: {line: 10, character: 24} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: 'y'}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: interiorOnly}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml new file mode 100644 index 0000000000..37bb6d4323 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml @@ -0,0 +1,66 @@ +languageId: python +command: + spokenForm: clear down score and cap + version: 2 + targets: + - type: list + elements: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: _} + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: c} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + selections: + - anchor: {line: 13, character: 1} + active: {line: 13, character: 1} + marks: + default._: + start: {line: 10, character: 10} + end: {line: 10, character: 37} + default.c: + start: {line: 11, character: 15} + end: {line: 11, character: 42} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("", desc="My tutorial list") + ctx.list['user.'] = { + "spoken form": "whatever", + } + selections: + - anchor: {line: 10, character: 10} + active: {line: 10, character: 10} + - anchor: {line: 11, character: 15} + active: {line: 11, character: 15} + thatMark: + - anchor: {line: 10, character: 10} + active: {line: 10, character: 10} + - anchor: {line: 11, character: 15} + active: {line: 11, character: 15} +fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: _}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: c}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}]}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml new file mode 100644 index 0000000000..e389c2cb3c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml @@ -0,0 +1,57 @@ +languageId: python +command: + spokenForm: clear repper leper + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: )} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": ":-)", + "frown": ":-)", + } + selections: + - anchor: {line: 13, character: 10} + active: {line: 13, character: 10} + marks: + default.): + start: {line: 13, character: 16} + end: {line: 13, character: 17} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": ":-)", + "frown": ":-", + } + selections: + - anchor: {line: 13, character: 16} + active: {line: 13, character: 16} + thatMark: + - anchor: {line: 13, character: 16} + active: {line: 13, character: 16} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: )}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml new file mode 100644 index 0000000000..ab2f464158 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml @@ -0,0 +1,55 @@ +languageId: python +command: + spokenForm: clear whale + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: w} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": "whatever", + } + selections: + - anchor: {line: 12, character: 10} + active: {line: 12, character: 10} + marks: + default.w: + start: {line: 12, character: 14} + end: {line: 12, character: 22} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": "", + } + selections: + - anchor: {line: 12, character: 14} + active: {line: 12, character: 14} + thatMark: + - anchor: {line: 12, character: 14} + active: {line: 12, character: 14} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml new file mode 100644 index 0000000000..ca6be3fce3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml @@ -0,0 +1,55 @@ +languageId: python +command: + spokenForm: take look clone line word frown + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: l} + usePrePhraseSnapshot: false + action: {name: setSelection} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": ":-)", + } + selections: + - anchor: {line: 12, character: 17} + active: {line: 12, character: 17} + marks: + default.l: + start: {line: 12, character: 5} + end: {line: 12, character: 10} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": ":-)", + } + selections: + - anchor: {line: 12, character: 5} + active: {line: 12, character: 10} + thatMark: + - anchor: {line: 12, character: 5} + active: {line: 12, character: 10} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml new file mode 100644 index 0000000000..b1b76eb6e0 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml @@ -0,0 +1,58 @@ +languageId: python +command: + spokenForm: take look clone line word frown + version: 2 + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: line} + usePrePhraseSnapshot: false + action: {name: insertCopyAfter} +initialState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": ":-)", + } + selections: + - anchor: {line: 12, character: 5} + active: {line: 12, character: 10} + marks: {} +finalState: + documentContents: |- + from talon import Context, Module + + mod = Module() + ctx = Context() + + mod.list("cursorless_walkthrough_list", desc="My tutorial list") + ctx.list['user.cursorless_walkthrough_list'] = { + "spoken form": "whatever", + } + + mod.list("emoji", desc="Emojis") + ctx.list['user.emoji'] = { + "smile": ":-)", + "smile": ":-)", + } + selections: + - anchor: {line: 13, character: 5} + active: {line: 13, character: 10} + thatMark: + - anchor: {line: 13, character: 0} + active: {line: 13, character: 19} + sourceMark: + - anchor: {line: 12, character: 0} + active: {line: 12, character: 19} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: line, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml new file mode 100644 index 0000000000..ba511755ae --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml @@ -0,0 +1,53 @@ +languageId: plaintext +command: + spokenForm: chuck drum + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: d} + usePrePhraseSnapshot: false + action: {name: remove} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 6, character: 15} + active: {line: 6, character: 17} + marks: + default.d: + start: {line: 6, character: 31} + end: {line: 6, character: 37} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 6, character: 15} + active: {line: 6, character: 17} + thatMark: + - anchor: {line: 6, character: 31} + active: {line: 6, character: 31} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: d}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml new file mode 100644 index 0000000000..2be87d39d0 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml @@ -0,0 +1,55 @@ +languageId: plaintext +command: + spokenForm: chuck line look + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: l} + modifiers: + - type: containingScope + scopeType: {type: line} + usePrePhraseSnapshot: false + action: {name: remove} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 6, character: 15} + active: {line: 6, character: 17} + marks: + default.l: + start: {line: 4, character: 13} + end: {line: 4, character: 16} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 5, character: 15} + active: {line: 5, character: 17} + thatMark: + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: line, position: contents, insideOutsideType: outside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml new file mode 100644 index 0000000000..f1e03058d4 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml @@ -0,0 +1,47 @@ +languageId: plaintext +command: + spokenForm: clear trap + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: t} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 7, character: 27} + active: {line: 7, character: 27} + marks: + default.t: + start: {line: 2, character: 22} + end: {line: 2, character: 24} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 22} + active: {line: 2, character: 22} + thatMark: + - anchor: {line: 2, character: 22} + active: {line: 2, character: 22} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml new file mode 100644 index 0000000000..5d4a639ddd --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml @@ -0,0 +1,47 @@ +languageId: plaintext +command: + spokenForm: clear whale word you + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: w} + usePrePhraseSnapshot: false + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 22} + active: {line: 2, character: 22} + marks: + default.w: + start: {line: 5, character: 15} + end: {line: 5, character: 17} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome Cursorless! ========== + ========== ========== + ========== ========== + ========== so can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 5, character: 15} + active: {line: 5, character: 15} + thatMark: + - anchor: {line: 5, character: 15} + active: {line: 5, character: 15} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml new file mode 100644 index 0000000000..ffce401801 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml @@ -0,0 +1,47 @@ +languageId: plaintext +command: + spokenForm: post look + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: l} + usePrePhraseSnapshot: false + action: {name: setSelectionAfter} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 7, character: 12} + active: {line: 7, character: 12} + marks: + default.l: + start: {line: 7, character: 20} + end: {line: 7, character: 27} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 7, character: 27} + active: {line: 7, character: 27} + thatMark: + - anchor: {line: 7, character: 20} + active: {line: 7, character: 27} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml new file mode 100644 index 0000000000..d3d5155b5b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml @@ -0,0 +1,49 @@ +languageId: plaintext +command: + spokenForm: pre ink + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: i} + usePrePhraseSnapshot: false + action: {name: setSelectionBefore} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 50} + - anchor: {line: 5, character: 0} + active: {line: 5, character: 41} + marks: + default.i: + start: {line: 7, character: 12} + end: {line: 7, character: 19} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 7, character: 12} + active: {line: 7, character: 12} + thatMark: + - anchor: {line: 7, character: 12} + active: {line: 7, character: 19} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: i}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json new file mode 100644 index 0000000000..3265b29967 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json @@ -0,0 +1,14 @@ +[ + "Every cursorless command consists of an action performed on a target. For example, the command {step:takeWhale.yml} selects the token with a grey hat over the 'w'.", + "When a hat is not gray, we need to use a color to refer to it: {step:takeBlueSun.yml}", + "Selecting a single token is great but oftentimes we need something bigger. Say {step:takeEachPastKick.yml} to select a range.", + "Despite its name, one of the most powerful aspects of cursorless is the ability to use more than one cursor. Let's try that: {step:takeCapAndWhale.yml}", + "But let's show that cursorless can live up to its name: we can say {step:chuckDrum.yml} to delete a word without ever moving our cursor.", + "Tokens are great, but they're just one way to think of a document. Let's try working with lines: {step:chuckLineLook.yml}", + "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}", + "You now know how to select and delete; let's give you a couple more actions to play with: say {action:setSelectionBefore} to place the cursor before a target, as in {step:preInk.yml}", + "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", + "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", + "Chaining commands is a great way to code faster: {step:clearWhaleWordYou.yml+wordYou}", + "And that wraps up unit 1 of the cursorless tutorial! Next time, we'll write some code ☺️" +] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml new file mode 100644 index 0000000000..628a9d8cae --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml @@ -0,0 +1,49 @@ +languageId: plaintext +command: + spokenForm: take blue sun + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: blue, character: s} + usePrePhraseSnapshot: false + action: {name: setSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + marks: + blue.s: + start: {line: 6, character: 12} + end: {line: 6, character: 14} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 6, character: 12} + active: {line: 6, character: 14} + thatMark: + - anchor: {line: 6, character: 12} + active: {line: 6, character: 14} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: s}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml new file mode 100644 index 0000000000..b53ee00d51 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml @@ -0,0 +1,60 @@ +languageId: plaintext +command: + spokenForm: take cap and whale + version: 2 + targets: + - type: list + elements: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: c} + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: w} + usePrePhraseSnapshot: false + action: {name: setSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 4, character: 13} + active: {line: 4, character: 36} + marks: + default.c: + start: {line: 2, character: 14} + end: {line: 2, character: 21} + default.w: + start: {line: 6, character: 15} + end: {line: 6, character: 17} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 6, character: 15} + active: {line: 6, character: 17} + thatMark: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 6, character: 15} + active: {line: 6, character: 17} +fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: c}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}]}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml new file mode 100644 index 0000000000..c195a54ac7 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml @@ -0,0 +1,59 @@ +languageId: plaintext +command: + spokenForm: take each past kick + version: 2 + targets: + - type: range + anchor: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: e} + active: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: k} + excludeAnchor: false + excludeActive: false + usePrePhraseSnapshot: false + action: {name: setSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 6, character: 12} + active: {line: 6, character: 14} + marks: + default.e: + start: {line: 4, character: 13} + end: {line: 4, character: 16} + default.k: + start: {line: 4, character: 31} + end: {line: 4, character: 36} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 4, character: 13} + active: {line: 4, character: 36} + thatMark: + - anchor: {line: 4, character: 13} + active: {line: 4, character: 36} +fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, rangeType: continuous, anchor: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: e}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, active: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: k}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml new file mode 100644 index 0000000000..0939b23830 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml @@ -0,0 +1,52 @@ +languageId: plaintext +command: + spokenForm: take line + version: 2 + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: line} + usePrePhraseSnapshot: false + action: {name: setSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + - anchor: {line: 5, character: 15} + active: {line: 5, character: 17} + marks: {} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== ========== + ========== so we can navigate ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 50} + - anchor: {line: 5, character: 0} + active: {line: 5, character: 41} + thatMark: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 50} + - anchor: {line: 5, character: 0} + active: {line: 5, character: 41} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: line, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml new file mode 100644 index 0000000000..e65de0ef80 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml @@ -0,0 +1,49 @@ +languageId: plaintext +command: + spokenForm: take whale + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: w} + usePrePhraseSnapshot: false + action: {name: setSelection} +initialState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: + default.w: + start: {line: 2, character: 14} + end: {line: 2, character: 21} +finalState: + documentContents: |- + ================================================== + ========== ========== + ========== Welcome to Cursorless! ========== + ========== ========== + ========== Let's start using marks ========== + ========== ========== + ========== so we can navigate around ========== + ========== ========== + ========== without lifting a finger! ========== + ========== ========== + ================================================== + selections: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} + thatMark: + - anchor: {line: 2, character: 14} + active: {line: 2, character: 21} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml new file mode 100644 index 0000000000..4a08a2aeb3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml @@ -0,0 +1,70 @@ +languageId: python +command: + spokenForm: bring blue cap to value risk + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: blue, character: c} + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: r} + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: false + action: {name: replaceWithTarget} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + return "black" + + + print_color("black") + selections: + - anchor: {line: 12, character: 18} + active: {line: 12, character: 18} + marks: + blue.c: + start: {line: 7, character: 17} + end: {line: 7, character: 22} + default.r: + start: {line: 12, character: 4} + end: {line: 12, character: 10} +finalState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + return color + + + print_color("black") + selections: + - anchor: {line: 12, character: 16} + active: {line: 12, character: 16} + thatMark: + - anchor: {line: 12, character: 11} + active: {line: 12, character: 16} + sourceMark: + - anchor: {line: 7, character: 17} + active: {line: 7, character: 22} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: c}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}, isImplicit: false}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: r}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: value, includeSiblings: false}, isImplicit: false}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml new file mode 100644 index 0000000000..530096ffd3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml @@ -0,0 +1,66 @@ +languageId: python +command: + spokenForm: bring state urge + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: u} + modifiers: + - type: containingScope + scopeType: {type: statement} + - {type: primitive, isImplicit: true} + usePrePhraseSnapshot: false + action: {name: replaceWithTarget} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + + + + print_color("black") + selections: + - anchor: {line: 12, character: 4} + active: {line: 12, character: 4} + marks: + default.u: + start: {line: 11, character: 8} + end: {line: 11, character: 14} +finalState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + return "black" + + + print_color("black") + selections: + - anchor: {line: 12, character: 18} + active: {line: 12, character: 18} + thatMark: + - anchor: {line: 12, character: 4} + active: {line: 12, character: 18} + sourceMark: + - anchor: {line: 11, character: 8} + active: {line: 11, character: 22} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: u}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}, isImplicit: false}, {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}, isImplicit: true}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml new file mode 100644 index 0000000000..ecf5b9c34e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml @@ -0,0 +1,62 @@ +languageId: python +command: + spokenForm: chuck argue blue vest + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: blue, character: v} + modifiers: + - type: containingScope + scopeType: {type: argumentOrParameter} + usePrePhraseSnapshot: false + action: {name: remove} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + return color + + + print_color("black") + selections: + - anchor: {line: 12, character: 16} + active: {line: 12, character: 16} + marks: + blue.v: + start: {line: 0, character: 23} + end: {line: 0, character: 29} +finalState: + documentContents: | + def print_color(color): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + return color + + + print_color("black") + selections: + - anchor: {line: 12, character: 16} + active: {line: 12, character: 16} + thatMark: + - anchor: {line: 0, character: 21} + active: {line: 0, character: 21} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: v}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml new file mode 100644 index 0000000000..48322df894 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml @@ -0,0 +1,61 @@ +languageId: python +command: + spokenForm: clone state ink + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: i} + modifiers: + - type: containingScope + scopeType: {type: statement} + usePrePhraseSnapshot: false + action: {name: insertCopyAfter} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + + + print_color("black") + selections: + - anchor: {line: 13, character: 0} + active: {line: 13, character: 0} + marks: + default.i: + start: {line: 8, character: 4} + end: {line: 8, character: 6} +finalState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "black": + return "white" + + + print_color("black") + selections: + - anchor: {line: 15, character: 0} + active: {line: 15, character: 0} + thatMark: + - anchor: {line: 10, character: 0} + active: {line: 11, character: 22} + sourceMark: + - anchor: {line: 8, character: 0} + active: {line: 9, character: 22} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: i}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml new file mode 100644 index 0000000000..0b9d827372 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml @@ -0,0 +1,56 @@ +languageId: python +command: + spokenForm: dedent this + version: 2 + targets: + - type: primitive + mark: {type: cursor} + usePrePhraseSnapshot: false + action: {name: outdentLine} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + + + + print_color("black") + selections: + - anchor: {line: 12, character: 8} + active: {line: 12, character: 8} + marks: {} +finalState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + + + + print_color("black") + selections: + - anchor: {line: 12, character: 4} + active: {line: 12, character: 4} + thatMark: + - anchor: {line: 12, character: 4} + active: {line: 12, character: 4} +fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}, isImplicit: false}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml new file mode 100644 index 0000000000..6268e8ca23 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml @@ -0,0 +1,58 @@ +languageId: python +command: + spokenForm: pour urge + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: u} + usePrePhraseSnapshot: false + action: {name: editNewLineAfter} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + + + print_color("black") + selections: + - anchor: {line: 15, character: 0} + active: {line: 15, character: 0} + marks: + default.u: + start: {line: 11, character: 8} + end: {line: 11, character: 14} +finalState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + + + + print_color("black") + selections: + - anchor: {line: 12, character: 8} + active: {line: 12, character: 8} + thatMark: + - anchor: {line: 12, character: 8} + active: {line: 12, character: 8} +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: u}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json new file mode 100644 index 0000000000..635ee1a777 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json @@ -0,0 +1,12 @@ +[ + "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", + "{scopeType:statement} is one of many scopes supported by cursorless. To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section.", + "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}", + "Great. Let's learn a new action. The {action:editNewLineAfter} action lets you start editing a new line below any line on your screen: {step:pourUrge.yml}", + "Now let's try applying a cursorless action to the current line: {step:dedentThis.yml}", + "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the {action:replaceWithTarget} command: {step:bringStateUrge.yml}", + "{action:replaceWithTarget} also works with two targets just like {action:swapTargets}: {step:bringBlueCapToValueRisk.yml}", + "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: {step:chuckArgueBlueVest.yml}", + "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say {literalStep:cursorless help} to see a list.", + "As always, feel free to stick around and play with this file to practice what you've just learned. Happy coding :)" +] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml new file mode 100644 index 0000000000..5c63ecd989 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml @@ -0,0 +1,68 @@ +languageId: python +command: + spokenForm: swap string air with whale + version: 2 + targets: + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: w} + usePrePhraseSnapshot: false + action: {name: swapTargets} +initialState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "black": + return "white" + + + print_color("black") + selections: + - anchor: {line: 15, character: 0} + active: {line: 15, character: 0} + marks: + default.a: + start: {line: 10, character: 17} + end: {line: 10, character: 22} + default.w: + start: {line: 11, character: 16} + end: {line: 11, character: 21} +finalState: + documentContents: | + def print_color(color, invert=False): + if invert: + print(invert_color(color)) + else: + print(color) + + + def invert_color(color): + if color == "black": + return "white" + if color == "white": + return "black" + + + print_color("black") + selections: + - anchor: {line: 15, character: 0} + active: {line: 15, character: 0} + thatMark: + - anchor: {line: 11, character: 15} + active: {line: 11, character: 22} + - anchor: {line: 10, character: 16} + active: {line: 10, character: 23} + sourceMark: [] +fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: surroundingPair, delimiter: string}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: surroundingPair, delimiter: string}}] From 617ea7ccc4a0a4c023efebd839c7b8388a393b56 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:39:06 +0000 Subject: [PATCH 02/59] [pre-commit.ci lite] apply automatic fixes --- .../bringBlockMade.yml | 1 - .../clearCoreSun.yml | 1 - .../clearCoreYank.yml | 1 - .../clearDownScoreAndCap.yml | 1 - .../clearRepperLeper.yml | 1 - .../extra-cloning-a-talon-list/clearWhale.yml | 1 - .../takeLookCloneLineWordFrown.yml | 1 - .../takeLookCloneLineWordFrown2.yml | 1 - .../tutorial/unit-1-basics/chuckDrum.yml | 1 - .../tutorial/unit-1-basics/chuckLineLook.yml | 1 - .../tutorial/unit-1-basics/clearTrap.yml | 1 - .../unit-1-basics/clearWhaleWordYou.yml | 1 - .../tutorial/unit-1-basics/postLook.yml | 1 - .../tutorial/unit-1-basics/preInk.yml | 1 - .../tutorial/unit-1-basics/script.json | 24 +++++++++---------- .../tutorial/unit-1-basics/takeBlueSun.yml | 1 - .../unit-1-basics/takeCapAndWhale.yml | 1 - .../unit-1-basics/takeEachPastKick.yml | 1 - .../tutorial/unit-1-basics/takeLine.yml | 1 - .../tutorial/unit-1-basics/takeWhale.yml | 1 - .../bringBlueCapToValueRisk.yml | 1 - .../unit-2-basic-coding/bringStateUrge.yml | 1 - .../chuckArgueBlueVest.yml | 1 - .../unit-2-basic-coding/cloneStateInk.yml | 1 - .../unit-2-basic-coding/dedentThis.yml | 1 - .../tutorial/unit-2-basic-coding/pourUrge.yml | 1 - .../tutorial/unit-2-basic-coding/script.json | 20 ++++++++-------- .../swapStringAirWithWhale.yml | 1 - 28 files changed, 22 insertions(+), 48 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml index 238c50abd2..c2b7aafb24 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml @@ -55,4 +55,3 @@ finalState: sourceMark: - anchor: {line: 5, character: 0} active: {line: 8, character: 1} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: m}, selectionType: paragraph, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: cursor}, selectionType: paragraph, position: contents, insideOutsideType: null, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml index cf44b6ecbb..2d6732f969 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml @@ -56,4 +56,3 @@ finalState: thatMark: - anchor: {line: 12, character: 5} active: {line: 12, character: 5} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: s}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: interiorOnly}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml index 3042279c5f..7d5d73ae7e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml @@ -58,4 +58,3 @@ finalState: thatMark: - anchor: {line: 10, character: 24} active: {line: 10, character: 24} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: 'y'}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: interiorOnly}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml index 37bb6d4323..c826892826 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml @@ -63,4 +63,3 @@ finalState: active: {line: 10, character: 10} - anchor: {line: 11, character: 15} active: {line: 11, character: 15} -fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: _}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: c}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}]}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml index e389c2cb3c..728095fa43 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml @@ -54,4 +54,3 @@ finalState: thatMark: - anchor: {line: 13, character: 16} active: {line: 13, character: 16} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: )}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml index ab2f464158..a837dca5e9 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml @@ -52,4 +52,3 @@ finalState: thatMark: - anchor: {line: 12, character: 14} active: {line: 12, character: 14} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml index ca6be3fce3..723716a703 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml @@ -52,4 +52,3 @@ finalState: thatMark: - anchor: {line: 12, character: 5} active: {line: 12, character: 10} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml index b1b76eb6e0..1cd9eb87ca 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml @@ -55,4 +55,3 @@ finalState: sourceMark: - anchor: {line: 12, character: 0} active: {line: 12, character: 19} -fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: line, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml index ba511755ae..17eef3dacf 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml @@ -50,4 +50,3 @@ finalState: thatMark: - anchor: {line: 6, character: 31} active: {line: 6, character: 31} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: d}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml index 2be87d39d0..3469ebcb56 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml @@ -52,4 +52,3 @@ finalState: thatMark: - anchor: {line: 4, character: 0} active: {line: 4, character: 0} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: line, position: contents, insideOutsideType: outside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml index f1e03058d4..2456dcd916 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml @@ -44,4 +44,3 @@ finalState: thatMark: - anchor: {line: 2, character: 22} active: {line: 2, character: 22} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml index 5d4a639ddd..3bb58abc7c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml @@ -44,4 +44,3 @@ finalState: thatMark: - anchor: {line: 5, character: 15} active: {line: 5, character: 15} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml index ffce401801..62b5af1d6c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml @@ -44,4 +44,3 @@ finalState: thatMark: - anchor: {line: 7, character: 20} active: {line: 7, character: 27} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: l}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml index d3d5155b5b..c3168427ec 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml @@ -46,4 +46,3 @@ finalState: thatMark: - anchor: {line: 7, character: 12} active: {line: 7, character: 19} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: i}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json index 3265b29967..bdf3952d25 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json @@ -1,14 +1,14 @@ [ - "Every cursorless command consists of an action performed on a target. For example, the command {step:takeWhale.yml} selects the token with a grey hat over the 'w'.", - "When a hat is not gray, we need to use a color to refer to it: {step:takeBlueSun.yml}", - "Selecting a single token is great but oftentimes we need something bigger. Say {step:takeEachPastKick.yml} to select a range.", - "Despite its name, one of the most powerful aspects of cursorless is the ability to use more than one cursor. Let's try that: {step:takeCapAndWhale.yml}", - "But let's show that cursorless can live up to its name: we can say {step:chuckDrum.yml} to delete a word without ever moving our cursor.", - "Tokens are great, but they're just one way to think of a document. Let's try working with lines: {step:chuckLineLook.yml}", - "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}", - "You now know how to select and delete; let's give you a couple more actions to play with: say {action:setSelectionBefore} to place the cursor before a target, as in {step:preInk.yml}", - "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", - "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", - "Chaining commands is a great way to code faster: {step:clearWhaleWordYou.yml+wordYou}", - "And that wraps up unit 1 of the cursorless tutorial! Next time, we'll write some code ☺️" + "Every cursorless command consists of an action performed on a target. For example, the command {step:takeWhale.yml} selects the token with a grey hat over the 'w'.", + "When a hat is not gray, we need to use a color to refer to it: {step:takeBlueSun.yml}", + "Selecting a single token is great but oftentimes we need something bigger. Say {step:takeEachPastKick.yml} to select a range.", + "Despite its name, one of the most powerful aspects of cursorless is the ability to use more than one cursor. Let's try that: {step:takeCapAndWhale.yml}", + "But let's show that cursorless can live up to its name: we can say {step:chuckDrum.yml} to delete a word without ever moving our cursor.", + "Tokens are great, but they're just one way to think of a document. Let's try working with lines: {step:chuckLineLook.yml}", + "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}", + "You now know how to select and delete; let's give you a couple more actions to play with: say {action:setSelectionBefore} to place the cursor before a target, as in {step:preInk.yml}", + "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", + "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", + "Chaining commands is a great way to code faster: {step:clearWhaleWordYou.yml+wordYou}", + "And that wraps up unit 1 of the cursorless tutorial! Next time, we'll write some code ☺️" ] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml index 628a9d8cae..ef1c81681f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml @@ -46,4 +46,3 @@ finalState: thatMark: - anchor: {line: 6, character: 12} active: {line: 6, character: 14} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: s}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml index b53ee00d51..1cfb6fbbfb 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml @@ -57,4 +57,3 @@ finalState: active: {line: 2, character: 21} - anchor: {line: 6, character: 15} active: {line: 6, character: 17} -fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: c}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}]}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml index c195a54ac7..327d0d60f4 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml @@ -56,4 +56,3 @@ finalState: thatMark: - anchor: {line: 4, character: 13} active: {line: 4, character: 36} -fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, rangeType: continuous, anchor: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: e}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, active: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: k}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml index 0939b23830..831ad9afb6 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml @@ -49,4 +49,3 @@ finalState: active: {line: 2, character: 50} - anchor: {line: 5, character: 0} active: {line: 5, character: 41} -fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: line, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml index e65de0ef80..e0194a4fe6 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml @@ -46,4 +46,3 @@ finalState: thatMark: - anchor: {line: 2, character: 14} active: {line: 2, character: 21} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml index 4a08a2aeb3..af4143142d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml @@ -67,4 +67,3 @@ finalState: sourceMark: - anchor: {line: 7, character: 17} active: {line: 7, character: 22} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: c}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}, isImplicit: false}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: r}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: value, includeSiblings: false}, isImplicit: false}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml index 530096ffd3..bc10619023 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml @@ -63,4 +63,3 @@ finalState: sourceMark: - anchor: {line: 11, character: 8} active: {line: 11, character: 22} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: u}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}, isImplicit: false}, {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}, isImplicit: true}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml index ecf5b9c34e..da3c2aa752 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml @@ -59,4 +59,3 @@ finalState: thatMark: - anchor: {line: 0, character: 21} active: {line: 0, character: 21} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: v}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml index 48322df894..ace53cfd5e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml @@ -58,4 +58,3 @@ finalState: sourceMark: - anchor: {line: 8, character: 0} active: {line: 9, character: 22} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: i}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: statement, includeSiblings: false}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml index 0b9d827372..a05a7f133d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml @@ -53,4 +53,3 @@ finalState: thatMark: - anchor: {line: 12, character: 4} active: {line: 12, character: 4} -fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}, isImplicit: false}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml index 6268e8ca23..ebac86dba0 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml @@ -55,4 +55,3 @@ finalState: thatMark: - anchor: {line: 12, character: 8} active: {line: 12, character: 8} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: u}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json index 635ee1a777..898810504e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json @@ -1,12 +1,12 @@ [ - "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", - "{scopeType:statement} is one of many scopes supported by cursorless. To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section.", - "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}", - "Great. Let's learn a new action. The {action:editNewLineAfter} action lets you start editing a new line below any line on your screen: {step:pourUrge.yml}", - "Now let's try applying a cursorless action to the current line: {step:dedentThis.yml}", - "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the {action:replaceWithTarget} command: {step:bringStateUrge.yml}", - "{action:replaceWithTarget} also works with two targets just like {action:swapTargets}: {step:bringBlueCapToValueRisk.yml}", - "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: {step:chuckArgueBlueVest.yml}", - "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say {literalStep:cursorless help} to see a list.", - "As always, feel free to stick around and play with this file to practice what you've just learned. Happy coding :)" + "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", + "{scopeType:statement} is one of many scopes supported by cursorless. To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section.", + "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}", + "Great. Let's learn a new action. The {action:editNewLineAfter} action lets you start editing a new line below any line on your screen: {step:pourUrge.yml}", + "Now let's try applying a cursorless action to the current line: {step:dedentThis.yml}", + "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the {action:replaceWithTarget} command: {step:bringStateUrge.yml}", + "{action:replaceWithTarget} also works with two targets just like {action:swapTargets}: {step:bringBlueCapToValueRisk.yml}", + "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: {step:chuckArgueBlueVest.yml}", + "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say {literalStep:cursorless help} to see a list.", + "As always, feel free to stick around and play with this file to practice what you've just learned. Happy coding :)" ] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml index 5c63ecd989..5ab83ad5a3 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml @@ -65,4 +65,3 @@ finalState: - anchor: {line: 10, character: 16} active: {line: 10, character: 23} sourceMark: [] -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: surroundingPair, delimiter: string}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: surroundingPair, delimiter: string}}] From ef6668862924e2f598abb756f972b25ff45160ee Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:43:34 +0000 Subject: [PATCH 03/59] Update spoken forms --- .../tutorial/extra-cloning-a-talon-list/clearCoreSun.yml | 2 +- .../tutorial/extra-cloning-a-talon-list/clearCoreYank.yml | 2 +- .../extra-cloning-a-talon-list/clearDownScoreAndCap.yml | 2 +- .../tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml | 2 +- .../recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml | 2 +- .../extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml | 2 +- .../extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml | 2 +- .../fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml | 2 +- .../recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml | 2 +- .../suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml | 2 +- .../recorded/tutorial/unit-1-basics/takeEachPastKick.yml | 2 +- .../tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml | 2 +- .../tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml | 2 +- .../recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml index 2d6732f969..6eb8173f67 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: clear core sun + spokenForm: change inside pair sun version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml index 7d5d73ae7e..8a609eed47 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: clear core yank + spokenForm: change inside pair yank version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml index c826892826..85632630fc 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: clear down score and cap + spokenForm: change underscore and cap version: 2 targets: - type: list diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml index 728095fa43..aff135a70a 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: clear repper leper + spokenForm: change right paren version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml index a837dca5e9..b63292d76d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: clear whale + spokenForm: change whale version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml index 723716a703..d0a766e95e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: take look clone line word frown + spokenForm: take look version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml index 1cd9eb87ca..efa76fbf16 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: take look clone line word frown + spokenForm: clone line version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml index 2456dcd916..984dab22fb 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml @@ -1,6 +1,6 @@ languageId: plaintext command: - spokenForm: clear trap + spokenForm: change trap version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml index 3bb58abc7c..592e5a6315 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml @@ -1,6 +1,6 @@ languageId: plaintext command: - spokenForm: clear whale word you + spokenForm: change whale version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml index c3168427ec..bd0b57d483 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml @@ -1,6 +1,6 @@ languageId: plaintext command: - spokenForm: pre ink + spokenForm: pre sit version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml index 327d0d60f4..8ded2609e7 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml @@ -1,6 +1,6 @@ languageId: plaintext command: - spokenForm: take each past kick + spokenForm: take each past crunch version: 2 targets: - type: range diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml index af4143142d..67f5cfbcb0 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: bring blue cap to value risk + spokenForm: bring blue cap to value red version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml index da3c2aa752..0ab1a4f88b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: chuck argue blue vest + spokenForm: chuck arg blue vest version: 2 targets: - type: primitive diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml index ace53cfd5e..60c7a6e9cc 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml @@ -1,6 +1,6 @@ languageId: python command: - spokenForm: clone state ink + spokenForm: clone state sit version: 2 targets: - type: primitive From ae958d3d1e06ce9134945f7304837f9cbb3b1a07 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:47:12 +0000 Subject: [PATCH 04/59] Upgrade commands --- .../bringBlockMade.yml | 13 ++++++----- .../clearCoreSun.yml | 11 +++++---- .../clearCoreYank.yml | 11 +++++---- .../clearDownScoreAndCap.yml | 9 ++++---- .../clearRepperLeper.yml | 9 ++++---- .../extra-cloning-a-talon-list/clearWhale.yml | 9 ++++---- .../takeLookCloneLineWordFrown.yml | 9 ++++---- .../takeLookCloneLineWordFrown2.yml | 9 ++++---- .../tutorial/unit-1-basics/chuckDrum.yml | 9 ++++---- .../tutorial/unit-1-basics/chuckLineLook.yml | 11 +++++---- .../tutorial/unit-1-basics/clearTrap.yml | 9 ++++---- .../unit-1-basics/clearWhaleWordYou.yml | 9 ++++---- .../tutorial/unit-1-basics/postLook.yml | 9 ++++---- .../tutorial/unit-1-basics/preInk.yml | 9 ++++---- .../tutorial/unit-1-basics/takeBlueSun.yml | 9 ++++---- .../unit-1-basics/takeCapAndWhale.yml | 9 ++++---- .../unit-1-basics/takeEachPastKick.yml | 9 ++++---- .../tutorial/unit-1-basics/takeLine.yml | 9 ++++---- .../tutorial/unit-1-basics/takeWhale.yml | 9 ++++---- .../bringBlueCapToValueRisk.yml | 23 +++++++++++-------- .../unit-2-basic-coding/bringStateUrge.yml | 13 ++++++----- .../chuckArgueBlueVest.yml | 11 +++++---- .../unit-2-basic-coding/cloneStateInk.yml | 11 +++++---- .../unit-2-basic-coding/dedentThis.yml | 9 ++++---- .../tutorial/unit-2-basic-coding/pourUrge.yml | 9 ++++---- .../swapStringAirWithWhale.yml | 14 ++++++----- 26 files changed, 151 insertions(+), 120 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml index c2b7aafb24..c5d22ee29d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml @@ -1,16 +1,17 @@ languageId: python command: + version: 6 spokenForm: bring block made - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: m} + action: + name: replaceWithTarget + source: + type: primitive modifiers: - type: containingScope scopeType: {type: paragraph} - - {type: primitive, isImplicit: true} + mark: {type: decoratedSymbol, symbolColor: default, character: m} + destination: {type: implicit} usePrePhraseSnapshot: false - action: {name: replaceWithTarget} initialState: documentContents: |+ from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml index 6eb8173f67..6213f167ed 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml @@ -1,16 +1,17 @@ languageId: python command: + version: 6 spokenForm: change inside pair sun - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: s} + action: + name: clearAndSetSelection + target: + type: primitive modifiers: - {type: interiorOnly} - type: containingScope scopeType: {type: surroundingPair, delimiter: any} + mark: {type: decoratedSymbol, symbolColor: default, character: s} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml index 8a609eed47..9a30b84a8c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml @@ -1,16 +1,17 @@ languageId: python command: + version: 6 spokenForm: change inside pair yank - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: 'y'} + action: + name: clearAndSetSelection + target: + type: primitive modifiers: - {type: interiorOnly} - type: containingScope scopeType: {type: surroundingPair, delimiter: any} + mark: {type: decoratedSymbol, symbolColor: default, character: 'y'} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml index 85632630fc..b96a66177c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml @@ -1,16 +1,17 @@ languageId: python command: + version: 6 spokenForm: change underscore and cap - version: 2 - targets: - - type: list + action: + name: clearAndSetSelection + target: + type: list elements: - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: _} - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: c} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml index aff135a70a..22c70f87c9 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml @@ -1,12 +1,13 @@ languageId: python command: + version: 6 spokenForm: change right paren - version: 2 - targets: - - type: primitive + action: + name: clearAndSetSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: )} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml index b63292d76d..f4a30c0592 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml @@ -1,12 +1,13 @@ languageId: python command: + version: 6 spokenForm: change whale - version: 2 - targets: - - type: primitive + action: + name: clearAndSetSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: w} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml index d0a766e95e..d2188d0fd4 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml @@ -1,12 +1,13 @@ languageId: python command: + version: 6 spokenForm: take look - version: 2 - targets: - - type: primitive + action: + name: setSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: l} usePrePhraseSnapshot: false - action: {name: setSelection} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml index efa76fbf16..30167cf1cb 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml @@ -1,14 +1,15 @@ languageId: python command: + version: 6 spokenForm: clone line - version: 2 - targets: - - type: primitive + action: + name: insertCopyAfter + target: + type: primitive modifiers: - type: containingScope scopeType: {type: line} usePrePhraseSnapshot: false - action: {name: insertCopyAfter} initialState: documentContents: |- from talon import Context, Module diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml index 17eef3dacf..e8800dd704 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: chuck drum - version: 2 - targets: - - type: primitive + action: + name: remove + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: d} usePrePhraseSnapshot: false - action: {name: remove} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml index 3469ebcb56..0d3b39b9cc 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml @@ -1,15 +1,16 @@ languageId: plaintext command: + version: 6 spokenForm: chuck line look - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: l} + action: + name: remove + target: + type: primitive modifiers: - type: containingScope scopeType: {type: line} + mark: {type: decoratedSymbol, symbolColor: default, character: l} usePrePhraseSnapshot: false - action: {name: remove} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml index 984dab22fb..5b031e83dd 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: change trap - version: 2 - targets: - - type: primitive + action: + name: clearAndSetSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: t} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml index 592e5a6315..8685800215 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: change whale - version: 2 - targets: - - type: primitive + action: + name: clearAndSetSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: w} usePrePhraseSnapshot: false - action: {name: clearAndSetSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml index 62b5af1d6c..579afc774c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: post look - version: 2 - targets: - - type: primitive + action: + name: setSelectionAfter + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: l} usePrePhraseSnapshot: false - action: {name: setSelectionAfter} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml index bd0b57d483..3866c1a915 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: pre sit - version: 2 - targets: - - type: primitive + action: + name: setSelectionBefore + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: i} usePrePhraseSnapshot: false - action: {name: setSelectionBefore} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml index ef1c81681f..82c2cdf2fd 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: take blue sun - version: 2 - targets: - - type: primitive + action: + name: setSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: blue, character: s} usePrePhraseSnapshot: false - action: {name: setSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml index 1cfb6fbbfb..b79d6a61e9 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml @@ -1,16 +1,17 @@ languageId: plaintext command: + version: 6 spokenForm: take cap and whale - version: 2 - targets: - - type: list + action: + name: setSelection + target: + type: list elements: - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: c} - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: w} usePrePhraseSnapshot: false - action: {name: setSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml index 8ded2609e7..02b2396bcc 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml @@ -1,9 +1,11 @@ languageId: plaintext command: + version: 6 spokenForm: take each past crunch - version: 2 - targets: - - type: range + action: + name: setSelection + target: + type: range anchor: type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: e} @@ -13,7 +15,6 @@ command: excludeAnchor: false excludeActive: false usePrePhraseSnapshot: false - action: {name: setSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml index 831ad9afb6..cf0445a794 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml @@ -1,14 +1,15 @@ languageId: plaintext command: + version: 6 spokenForm: take line - version: 2 - targets: - - type: primitive + action: + name: setSelection + target: + type: primitive modifiers: - type: containingScope scopeType: {type: line} usePrePhraseSnapshot: false - action: {name: setSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml index e0194a4fe6..77293390bc 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml @@ -1,12 +1,13 @@ languageId: plaintext command: + version: 6 spokenForm: take whale - version: 2 - targets: - - type: primitive + action: + name: setSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: w} usePrePhraseSnapshot: false - action: {name: setSelection} initialState: documentContents: |- ================================================== diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml index 67f5cfbcb0..e1867ae21b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml @@ -1,17 +1,22 @@ languageId: python command: + version: 6 spokenForm: bring blue cap to value red - version: 2 - targets: - - type: primitive + action: + name: replaceWithTarget + source: + type: primitive mark: {type: decoratedSymbol, symbolColor: blue, character: c} - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: r} - modifiers: - - type: containingScope - scopeType: {type: value} + destination: + type: primitive + insertionMode: to + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + mark: {type: decoratedSymbol, symbolColor: default, character: r} usePrePhraseSnapshot: false - action: {name: replaceWithTarget} initialState: documentContents: | def print_color(color, invert=False): diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml index bc10619023..0ba48e3427 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml @@ -1,16 +1,17 @@ languageId: python command: + version: 6 spokenForm: bring state urge - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: u} + action: + name: replaceWithTarget + source: + type: primitive modifiers: - type: containingScope scopeType: {type: statement} - - {type: primitive, isImplicit: true} + mark: {type: decoratedSymbol, symbolColor: default, character: u} + destination: {type: implicit} usePrePhraseSnapshot: false - action: {name: replaceWithTarget} initialState: documentContents: | def print_color(color, invert=False): diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml index 0ab1a4f88b..2fc67be091 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml @@ -1,15 +1,16 @@ languageId: python command: + version: 6 spokenForm: chuck arg blue vest - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: blue, character: v} + action: + name: remove + target: + type: primitive modifiers: - type: containingScope scopeType: {type: argumentOrParameter} + mark: {type: decoratedSymbol, symbolColor: blue, character: v} usePrePhraseSnapshot: false - action: {name: remove} initialState: documentContents: | def print_color(color, invert=False): diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml index 60c7a6e9cc..e04894ed13 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml @@ -1,15 +1,16 @@ languageId: python command: + version: 6 spokenForm: clone state sit - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: i} + action: + name: insertCopyAfter + target: + type: primitive modifiers: - type: containingScope scopeType: {type: statement} + mark: {type: decoratedSymbol, symbolColor: default, character: i} usePrePhraseSnapshot: false - action: {name: insertCopyAfter} initialState: documentContents: | def print_color(color, invert=False): diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml index a05a7f133d..cd20361265 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml @@ -1,12 +1,13 @@ languageId: python command: + version: 6 spokenForm: dedent this - version: 2 - targets: - - type: primitive + action: + name: outdentLine + target: + type: primitive mark: {type: cursor} usePrePhraseSnapshot: false - action: {name: outdentLine} initialState: documentContents: | def print_color(color, invert=False): diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml index ebac86dba0..2d31603412 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml @@ -1,12 +1,13 @@ languageId: python command: + version: 6 spokenForm: pour urge - version: 2 - targets: - - type: primitive + action: + name: editNewLineAfter + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: u} usePrePhraseSnapshot: false - action: {name: editNewLineAfter} initialState: documentContents: | def print_color(color, invert=False): diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml index 5ab83ad5a3..7a5e57b9f8 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml @@ -1,17 +1,19 @@ languageId: python command: + version: 6 spokenForm: swap string air with whale - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: a} + action: + name: swapTargets + target1: + type: primitive modifiers: - type: containingScope scopeType: {type: surroundingPair, delimiter: string} - - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + target2: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: w} usePrePhraseSnapshot: false - action: {name: swapTargets} initialState: documentContents: | def print_color(color, invert=False): From 1b4cae378717726a2d90f10543db5dea268b4585 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:06:42 +0000 Subject: [PATCH 05/59] Fix extension tests --- .../extra-cloning-a-talon-list/bringBlockMade.yml | 6 ------ .../tutorial/extra-cloning-a-talon-list/clearCoreSun.yml | 3 --- .../tutorial/extra-cloning-a-talon-list/clearCoreYank.yml | 3 --- .../extra-cloning-a-talon-list/clearDownScoreAndCap.yml | 5 ----- .../extra-cloning-a-talon-list/clearRepperLeper.yml | 3 --- .../tutorial/extra-cloning-a-talon-list/clearWhale.yml | 3 --- .../takeLookCloneLineWordFrown.yml | 3 --- .../takeLookCloneLineWordFrown2.yml | 6 ------ .../recorded/tutorial/unit-1-basics/chuckDrum.yml | 3 --- .../recorded/tutorial/unit-1-basics/chuckLineLook.yml | 3 --- .../recorded/tutorial/unit-1-basics/clearTrap.yml | 3 --- .../recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml | 3 --- .../fixtures/recorded/tutorial/unit-1-basics/postLook.yml | 3 --- .../fixtures/recorded/tutorial/unit-1-basics/preInk.yml | 3 --- .../recorded/tutorial/unit-1-basics/takeBlueSun.yml | 3 --- .../recorded/tutorial/unit-1-basics/takeCapAndWhale.yml | 5 ----- .../recorded/tutorial/unit-1-basics/takeEachPastKick.yml | 3 --- .../fixtures/recorded/tutorial/unit-1-basics/takeLine.yml | 5 ----- .../recorded/tutorial/unit-1-basics/takeWhale.yml | 3 --- .../unit-2-basic-coding/bringBlueCapToValueRisk.yml | 6 ------ .../tutorial/unit-2-basic-coding/bringStateUrge.yml | 6 ------ .../tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml | 3 --- .../tutorial/unit-2-basic-coding/cloneStateInk.yml | 6 ------ .../recorded/tutorial/unit-2-basic-coding/dedentThis.yml | 3 --- .../recorded/tutorial/unit-2-basic-coding/pourUrge.yml | 3 --- .../unit-2-basic-coding/swapStringAirWithWhale.yml | 6 ------ .../src/suite/recorded.vscode.test.ts | 8 ++------ 27 files changed, 2 insertions(+), 108 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml index c5d22ee29d..212981bacf 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/bringBlockMade.yml @@ -50,9 +50,3 @@ finalState: selections: - anchor: {line: 13, character: 1} active: {line: 13, character: 1} - thatMark: - - anchor: {line: 10, character: 0} - active: {line: 13, character: 1} - sourceMark: - - anchor: {line: 5, character: 0} - active: {line: 8, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml index 6213f167ed..8a52a4f680 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreSun.yml @@ -54,6 +54,3 @@ finalState: selections: - anchor: {line: 12, character: 5} active: {line: 12, character: 5} - thatMark: - - anchor: {line: 12, character: 5} - active: {line: 12, character: 5} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml index 9a30b84a8c..3e2eb34a2f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearCoreYank.yml @@ -56,6 +56,3 @@ finalState: selections: - anchor: {line: 10, character: 24} active: {line: 10, character: 24} - thatMark: - - anchor: {line: 10, character: 24} - active: {line: 10, character: 24} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml index b96a66177c..4285f52d7a 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearDownScoreAndCap.yml @@ -59,8 +59,3 @@ finalState: active: {line: 10, character: 10} - anchor: {line: 11, character: 15} active: {line: 11, character: 15} - thatMark: - - anchor: {line: 10, character: 10} - active: {line: 10, character: 10} - - anchor: {line: 11, character: 15} - active: {line: 11, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml index 22c70f87c9..2ac1b7da70 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearRepperLeper.yml @@ -52,6 +52,3 @@ finalState: selections: - anchor: {line: 13, character: 16} active: {line: 13, character: 16} - thatMark: - - anchor: {line: 13, character: 16} - active: {line: 13, character: 16} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml index f4a30c0592..48a9e9ce5b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/clearWhale.yml @@ -50,6 +50,3 @@ finalState: selections: - anchor: {line: 12, character: 14} active: {line: 12, character: 14} - thatMark: - - anchor: {line: 12, character: 14} - active: {line: 12, character: 14} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml index d2188d0fd4..8afbba4b72 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown.yml @@ -50,6 +50,3 @@ finalState: selections: - anchor: {line: 12, character: 5} active: {line: 12, character: 10} - thatMark: - - anchor: {line: 12, character: 5} - active: {line: 12, character: 10} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml index 30167cf1cb..44706fe1ae 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/extra-cloning-a-talon-list/takeLookCloneLineWordFrown2.yml @@ -50,9 +50,3 @@ finalState: selections: - anchor: {line: 13, character: 5} active: {line: 13, character: 10} - thatMark: - - anchor: {line: 13, character: 0} - active: {line: 13, character: 19} - sourceMark: - - anchor: {line: 12, character: 0} - active: {line: 12, character: 19} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml index e8800dd704..e3b02d98f4 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckDrum.yml @@ -48,6 +48,3 @@ finalState: active: {line: 2, character: 21} - anchor: {line: 6, character: 15} active: {line: 6, character: 17} - thatMark: - - anchor: {line: 6, character: 31} - active: {line: 6, character: 31} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml index 0d3b39b9cc..3cbbb5d3e5 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/chuckLineLook.yml @@ -50,6 +50,3 @@ finalState: active: {line: 2, character: 21} - anchor: {line: 5, character: 15} active: {line: 5, character: 17} - thatMark: - - anchor: {line: 4, character: 0} - active: {line: 4, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml index 5b031e83dd..d4ebd4430f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearTrap.yml @@ -42,6 +42,3 @@ finalState: selections: - anchor: {line: 2, character: 22} active: {line: 2, character: 22} - thatMark: - - anchor: {line: 2, character: 22} - active: {line: 2, character: 22} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml index 8685800215..e195c11635 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/clearWhaleWordYou.yml @@ -42,6 +42,3 @@ finalState: selections: - anchor: {line: 5, character: 15} active: {line: 5, character: 15} - thatMark: - - anchor: {line: 5, character: 15} - active: {line: 5, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml index 579afc774c..5e8f0c8355 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/postLook.yml @@ -42,6 +42,3 @@ finalState: selections: - anchor: {line: 7, character: 27} active: {line: 7, character: 27} - thatMark: - - anchor: {line: 7, character: 20} - active: {line: 7, character: 27} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml index 3866c1a915..4e93164b93 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/preInk.yml @@ -44,6 +44,3 @@ finalState: selections: - anchor: {line: 7, character: 12} active: {line: 7, character: 12} - thatMark: - - anchor: {line: 7, character: 12} - active: {line: 7, character: 19} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml index 82c2cdf2fd..04689305df 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeBlueSun.yml @@ -44,6 +44,3 @@ finalState: selections: - anchor: {line: 6, character: 12} active: {line: 6, character: 14} - thatMark: - - anchor: {line: 6, character: 12} - active: {line: 6, character: 14} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml index b79d6a61e9..87307a9be7 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeCapAndWhale.yml @@ -53,8 +53,3 @@ finalState: active: {line: 2, character: 21} - anchor: {line: 6, character: 15} active: {line: 6, character: 17} - thatMark: - - anchor: {line: 2, character: 14} - active: {line: 2, character: 21} - - anchor: {line: 6, character: 15} - active: {line: 6, character: 17} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml index 02b2396bcc..455ba3923a 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeEachPastKick.yml @@ -54,6 +54,3 @@ finalState: selections: - anchor: {line: 4, character: 13} active: {line: 4, character: 36} - thatMark: - - anchor: {line: 4, character: 13} - active: {line: 4, character: 36} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml index cf0445a794..65e11df143 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeLine.yml @@ -45,8 +45,3 @@ finalState: active: {line: 2, character: 50} - anchor: {line: 5, character: 0} active: {line: 5, character: 41} - thatMark: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 50} - - anchor: {line: 5, character: 0} - active: {line: 5, character: 41} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml index 77293390bc..37cdf6c209 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/takeWhale.yml @@ -44,6 +44,3 @@ finalState: selections: - anchor: {line: 2, character: 14} active: {line: 2, character: 21} - thatMark: - - anchor: {line: 2, character: 14} - active: {line: 2, character: 21} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml index e1867ae21b..f91b7b3d8b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringBlueCapToValueRisk.yml @@ -66,9 +66,3 @@ finalState: selections: - anchor: {line: 12, character: 16} active: {line: 12, character: 16} - thatMark: - - anchor: {line: 12, character: 11} - active: {line: 12, character: 16} - sourceMark: - - anchor: {line: 7, character: 17} - active: {line: 7, character: 22} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml index 0ba48e3427..8e7da2f51b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/bringStateUrge.yml @@ -58,9 +58,3 @@ finalState: selections: - anchor: {line: 12, character: 18} active: {line: 12, character: 18} - thatMark: - - anchor: {line: 12, character: 4} - active: {line: 12, character: 18} - sourceMark: - - anchor: {line: 11, character: 8} - active: {line: 11, character: 22} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml index 2fc67be091..de28729d20 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/chuckArgueBlueVest.yml @@ -57,6 +57,3 @@ finalState: selections: - anchor: {line: 12, character: 16} active: {line: 12, character: 16} - thatMark: - - anchor: {line: 0, character: 21} - active: {line: 0, character: 21} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml index e04894ed13..f3bd735023 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/cloneStateInk.yml @@ -53,9 +53,3 @@ finalState: selections: - anchor: {line: 15, character: 0} active: {line: 15, character: 0} - thatMark: - - anchor: {line: 10, character: 0} - active: {line: 11, character: 22} - sourceMark: - - anchor: {line: 8, character: 0} - active: {line: 9, character: 22} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml index cd20361265..5d7482ce83 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/dedentThis.yml @@ -51,6 +51,3 @@ finalState: selections: - anchor: {line: 12, character: 4} active: {line: 12, character: 4} - thatMark: - - anchor: {line: 12, character: 4} - active: {line: 12, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml index 2d31603412..9cb6944012 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/pourUrge.yml @@ -53,6 +53,3 @@ finalState: selections: - anchor: {line: 12, character: 8} active: {line: 12, character: 8} - thatMark: - - anchor: {line: 12, character: 8} - active: {line: 12, character: 8} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml index 7a5e57b9f8..3c07f87549 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/swapStringAirWithWhale.yml @@ -61,9 +61,3 @@ finalState: selections: - anchor: {line: 15, character: 0} active: {line: 15, character: 0} - thatMark: - - anchor: {line: 11, character: 15} - active: {line: 11, character: 22} - - anchor: {line: 10, character: 16} - active: {line: 10, character: 23} - sourceMark: [] diff --git a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts index 14cb13c9c1..1490d06f38 100644 --- a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts @@ -168,13 +168,9 @@ async function runTest(file: string, spyIde: SpyIDE) { excludeFields.push("clipboard"); } - if (fixture.finalState?.thatMark == null) { - excludeFields.push("thatMark"); - } + excludeFields.push("thatMark"); - if (fixture.finalState?.sourceMark == null) { - excludeFields.push("sourceMark"); - } + excludeFields.push("sourceMark"); if (fixture.finalState?.instanceReferenceMark == null) { excludeFields.push("instanceReferenceMark"); From bb303aa92fe598f8c7541e02e5ff60eee4cb0d95 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:13:43 +0000 Subject: [PATCH 06/59] whoops --- .../src/suite/recorded.vscode.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts index 1490d06f38..14cb13c9c1 100644 --- a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts @@ -168,9 +168,13 @@ async function runTest(file: string, spyIde: SpyIDE) { excludeFields.push("clipboard"); } - excludeFields.push("thatMark"); + if (fixture.finalState?.thatMark == null) { + excludeFields.push("thatMark"); + } - excludeFields.push("sourceMark"); + if (fixture.finalState?.sourceMark == null) { + excludeFields.push("sourceMark"); + } if (fixture.finalState?.instanceReferenceMark == null) { excludeFields.push("instanceReferenceMark"); From 016e4d8dc6bf04c954c4a5b5b4e9df2b59dd12bf Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Wed, 13 Dec 2023 15:44:50 +0000 Subject: [PATCH 07/59] basic communication between talon and the extension for the tutorial --- cursorless-talon/src/tutorial.py | 18 +++++++++- .../cursorless-engine/src/core/Tutorial.ts | 35 +++++++++++++++++++ packages/cursorless-engine/src/index.ts | 1 + .../src/keyboard/grammar/generated/grammar.ts | 6 ++-- .../cursorless-vscode/src/registerCommands.ts | 5 +++ 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 packages/cursorless-engine/src/core/Tutorial.ts diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index e500422b84..c6d953449f 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -10,7 +10,7 @@ regex = re.compile(r"\{(\w+):([^}]+)\}") tutorial_dir = Path( - "/Users/pokey/src/cursorless-vscode/src/test/suite/fixtures/recorded/tutorial/unit-2-basic-coding" + r"C:\work\tools\voicecoding\cursorless_fork\packages\cursorless-vscode-e2e\src\suite\fixtures\recorded\tutorial\unit-2-basic-coding" ) @@ -29,8 +29,24 @@ def process_scope_type(argument: str): def process_cursorless_command_step(argument: str): + print(f"{argument=}") step_fixture = yaml.safe_load((tutorial_dir / argument).read_text()) + print(f"{step_fixture['command']=}") + result = actions.user.private_cursorless_run_rpc_command_get( + "cursorless.tutorial.create", + { + "version": 0, + "stepFixture": step_fixture, + }, + ) + print(f"{result=}") return f"" + # return f"" + + +# TODO get this information from the extension +def cursorless_command_to_spoken_form(command: dict[str, str]): + return command["spokenForm"] interpolation_processor_map: dict[str, Callable[[str], str]] = { diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts new file mode 100644 index 0000000000..d958652727 --- /dev/null +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -0,0 +1,35 @@ +// import { readFile, writeFile } from "fs/promises"; +// import { parse } from "node-html-parser"; +// import produce from "immer"; +// import { sortBy } from "lodash"; +// import { ide } from "../singletons/ide.singleton"; +// import path from "path"; +// import { getCursorlessRepoRoot } from "@cursorless/common"; + +import { Dictionary } from "lodash"; + +/** + * The argument expected by the tutorial command. + */ +interface TutorialCommandArg { + /** + * The version of the tutorial command. + */ + version: 0; + + /** + * A representation of the yaml file + */ + stepFixture: Dictionary; +} + +export async function tutorialCreate({ + version, + stepFixture, +}: TutorialCommandArg) { + if (version !== 0) { + throw new Error(`Unsupported tutorial api version: ${version}`); + } + + return stepFixture; +} diff --git a/packages/cursorless-engine/src/index.ts b/packages/cursorless-engine/src/index.ts index 9348e847e2..b76df35a78 100644 --- a/packages/cursorless-engine/src/index.ts +++ b/packages/cursorless-engine/src/index.ts @@ -1,5 +1,6 @@ export * from "./testUtil/plainObjectToTarget"; export * from "./core/Cheatsheet"; +export * from "./core/Tutorial"; export * from "./testUtil/takeSnapshot"; export * from "./testCaseRecorder/TestCaseRecorder"; export * from "./core/StoredTargets"; diff --git a/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts b/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts index fd2b867d6b..cea81bdda5 100644 --- a/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts +++ b/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts @@ -23,11 +23,11 @@ interface NearleyToken { }; interface NearleyLexer { - reset: (chunk: any, info: any) => void; + reset: (chunk: string, info: any) => void; next: () => NearleyToken | undefined; save: () => any; - formatError: (token: any, message: string) => string; - has: (tokenType: any) => boolean; + formatError: (token: never) => string; + has: (tokenType: string) => boolean; }; interface NearleyRule { diff --git a/packages/cursorless-vscode/src/registerCommands.ts b/packages/cursorless-vscode/src/registerCommands.ts index a6a5e08a8d..ef5901b8f6 100644 --- a/packages/cursorless-vscode/src/registerCommands.ts +++ b/packages/cursorless-vscode/src/registerCommands.ts @@ -8,6 +8,7 @@ import { TestCaseRecorder, showCheatsheet, updateDefaults, + tutorialCreate, } from "@cursorless/cursorless-engine"; import * as vscode from "vscode"; import { showDocumentation, showQuickPick } from "./commands"; @@ -88,6 +89,10 @@ export function registerCommands( ["cursorless.keyboard.modal.modeOn"]: keyboardCommands.modal.modeOn, ["cursorless.keyboard.modal.modeOff"]: keyboardCommands.modal.modeOff, ["cursorless.keyboard.modal.modeToggle"]: keyboardCommands.modal.modeToggle, + + // Tutorial commands + ["cursorless.tutorial.create"]: tutorialCreate, + // ["cursorless.tutorial.create"]: tutorialCommands.create, }; extensionContext.subscriptions.push( From f7613060d363b37fad025174c65170d173452ae1 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Wed, 13 Dec 2023 16:52:49 +0000 Subject: [PATCH 08/59] initial way to populate a window even if not really the right way --- cursorless-talon/src/tutorial.py | 2 ++ packages/cursorless-engine/package.json | 1 + .../cursorless-engine/src/core/Tutorial.ts | 32 ++++++++++++++++++- pnpm-lock.yaml | 3 ++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index c6d953449f..e1136178dd 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -24,6 +24,7 @@ def process_action(argument: str): def process_scope_type(argument: str): + # TODO not sure what we are trying to achieve here _, spoken_form = lookup_scope_type(argument) return f'<*"{spoken_form}"/>' @@ -37,6 +38,7 @@ def process_cursorless_command_step(argument: str): { "version": 0, "stepFixture": step_fixture, + "yamlFilename": argument, }, ) print(f"{result=}") diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index 6978b91348..6ab74ff954 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -17,6 +17,7 @@ "license": "MIT", "dependencies": { "@cursorless/common": "workspace:*", + "@cursorless/vscode-common": "workspace:*", "immer": "^9.0.15", "immutability-helper": "^3.1.1", "itertools": "^2.1.1", diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index d958652727..3cee09e39f 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -3,11 +3,23 @@ // import produce from "immer"; // import { sortBy } from "lodash"; // import { ide } from "../singletons/ide.singleton"; -// import path from "path"; +import path from "path"; // import { getCursorlessRepoRoot } from "@cursorless/common"; +// TODO the engine is editor agnostic so we shouldn't really import that +// TODO Editor specific features are accessed via the injected ide instance. +// TODO packages\cursorless-engine\src\singletons\ide.singleton.ts +import { openNewEditor } from "@cursorless/vscode-common"; + +import * as yaml from "js-yaml"; +import { promises as fsp } from "node:fs"; + +import { TestCaseFixture } from "@cursorless/common"; import { Dictionary } from "lodash"; +const tutorial_dir = + "C:\\work\\tools\\voicecoding\\cursorless_fork\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\tutorial\\unit-2-basic-coding"; + /** * The argument expected by the tutorial command. */ @@ -21,15 +33,33 @@ interface TutorialCommandArg { * A representation of the yaml file */ stepFixture: Dictionary; + + /** + * The yaml file for the current step + */ + yamlFilename: string; } export async function tutorialCreate({ version, stepFixture, + yamlFilename, }: TutorialCommandArg) { if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); } + // const fixture = stepFixture as TestCaseFixture; + createEnvironment(yamlFilename); + // TODO need to answer to the talon side only what is necessary return stepFixture; } + +async function createEnvironment(yamlFilename: string) { + const buffer = await fsp.readFile(path.join(tutorial_dir, yamlFilename)); + const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + + const editor = await openNewEditor(fixture.initialState.documentContents, { + languageId: fixture.languageId, + }); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1b34e9a87..4332025962 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,6 +238,9 @@ importers: '@cursorless/common': specifier: workspace:* version: link:../common + '@cursorless/vscode-common': + specifier: workspace:* + version: link:../vscode-common immer: specifier: ^9.0.15 version: 9.0.19 From d4b8aa6d465bd94e4a685b6940f9800135ceeb04 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Tue, 19 Dec 2023 15:44:17 +0000 Subject: [PATCH 09/59] use the injected ide --- packages/cursorless-engine/src/core/Tutorial.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 3cee09e39f..975dd2fe8c 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -6,16 +6,12 @@ import path from "path"; // import { getCursorlessRepoRoot } from "@cursorless/common"; -// TODO the engine is editor agnostic so we shouldn't really import that -// TODO Editor specific features are accessed via the injected ide instance. -// TODO packages\cursorless-engine\src\singletons\ide.singleton.ts -import { openNewEditor } from "@cursorless/vscode-common"; - import * as yaml from "js-yaml"; import { promises as fsp } from "node:fs"; import { TestCaseFixture } from "@cursorless/common"; import { Dictionary } from "lodash"; +import { ide } from "../singletons/ide.singleton"; const tutorial_dir = "C:\\work\\tools\\voicecoding\\cursorless_fork\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\tutorial\\unit-2-basic-coding"; @@ -59,7 +55,8 @@ async function createEnvironment(yamlFilename: string) { const buffer = await fsp.readFile(path.join(tutorial_dir, yamlFilename)); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; - const editor = await openNewEditor(fixture.initialState.documentContents, { - languageId: fixture.languageId, + const editor = ide().openUntitledTextDocument({ + content: fixture.initialState.documentContents, + language: fixture.languageId, }); } From 9673f527e4a63dc20ff62a75721f08fcaec184f3 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Tue, 19 Dec 2023 15:58:39 +0000 Subject: [PATCH 10/59] remove dependencies unneeded now --- packages/cursorless-engine/package.json | 1 - pnpm-lock.yaml | 3 --- 2 files changed, 4 deletions(-) diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index 6ab74ff954..6978b91348 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -17,7 +17,6 @@ "license": "MIT", "dependencies": { "@cursorless/common": "workspace:*", - "@cursorless/vscode-common": "workspace:*", "immer": "^9.0.15", "immutability-helper": "^3.1.1", "itertools": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4332025962..f1b34e9a87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,9 +238,6 @@ importers: '@cursorless/common': specifier: workspace:* version: link:../common - '@cursorless/vscode-common': - specifier: workspace:* - version: link:../vscode-common immer: specifier: ^9.0.15 version: 9.0.19 From 8b95e0256a4f2846bf6a87330972937019454975 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Wed, 20 Dec 2023 15:07:19 +0000 Subject: [PATCH 11/59] moved tutorial to a class --- packages/common/src/cursorlessCommandIds.ts | 4 ++ .../src/api/CursorlessEngineApi.ts | 2 + .../cursorless-engine/src/core/Tutorial.ts | 51 +++++++++++-------- .../cursorless-engine/src/cursorlessEngine.ts | 5 ++ packages/cursorless-vscode/src/extension.ts | 2 + .../cursorless-vscode/src/registerCommands.ts | 6 +-- 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/packages/common/src/cursorlessCommandIds.ts b/packages/common/src/cursorlessCommandIds.ts index fb9b0fc7de..063bc295d2 100644 --- a/packages/common/src/cursorlessCommandIds.ts +++ b/packages/common/src/cursorlessCommandIds.ts @@ -46,6 +46,7 @@ export const cursorlessCommandIds = [ "cursorless.toggleDecorations", "cursorless.showScopeVisualizer", "cursorless.hideScopeVisualizer", + "cursorless.tutorial.create", ] as const satisfies readonly `cursorless.${string}`[]; export type CursorlessCommandId = (typeof cursorlessCommandIds)[number]; @@ -116,4 +117,7 @@ export const cursorlessCommandDescriptions: Record< ["cursorless.keyboard.modal.modeToggle"]: new HiddenCommand( "Toggle the cursorless modal mode", ), + ["cursorless.tutorial.create"]: new VisibleCommand( + "Create the tutorial based on Talon HUD", + ), }; diff --git a/packages/cursorless-engine/src/api/CursorlessEngineApi.ts b/packages/cursorless-engine/src/api/CursorlessEngineApi.ts index 331d535a75..4f9cc08af9 100644 --- a/packages/cursorless-engine/src/api/CursorlessEngineApi.ts +++ b/packages/cursorless-engine/src/api/CursorlessEngineApi.ts @@ -4,6 +4,7 @@ import { StoredTargetMap } from "../core/StoredTargets"; import { ScopeProvider } from "@cursorless/common"; import { CommandRunner } from "../CommandRunner"; import { ReadOnlyHatMap } from "@cursorless/common"; +import { Tutorial } from "../core/Tutorial"; export interface CursorlessEngine { commandApi: CommandApi; @@ -12,6 +13,7 @@ export interface CursorlessEngine { storedTargets: StoredTargetMap; hatTokenMap: HatTokenMap; snippets: Snippets; + tutorial: Tutorial; injectIde: (ide: IDE | undefined) => void; runIntegrationTests: () => Promise; addCommandRunnerDecorator: ( diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 975dd2fe8c..2d18ab629c 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -12,8 +12,10 @@ import { promises as fsp } from "node:fs"; import { TestCaseFixture } from "@cursorless/common"; import { Dictionary } from "lodash"; import { ide } from "../singletons/ide.singleton"; +import { HatTokenMapImpl } from "./HatTokenMapImpl"; +import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; -const tutorial_dir = +const tutorialDirectory = "C:\\work\\tools\\voicecoding\\cursorless_fork\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\tutorial\\unit-2-basic-coding"; /** @@ -36,27 +38,34 @@ interface TutorialCommandArg { yamlFilename: string; } -export async function tutorialCreate({ - version, - stepFixture, - yamlFilename, -}: TutorialCommandArg) { - if (version !== 0) { - throw new Error(`Unsupported tutorial api version: ${version}`); +export class Tutorial { + constructor( + hatTokenMap: HatTokenMapImpl, + customSpokenFormGenerator: CustomSpokenFormGeneratorImpl, + ) { + this.create = this.create.bind(this); } - // const fixture = stepFixture as TestCaseFixture; - createEnvironment(yamlFilename); - // TODO need to answer to the talon side only what is necessary - return stepFixture; -} + async create({ version, stepFixture, yamlFilename }: TutorialCommandArg) { + if (version !== 0) { + throw new Error(`Unsupported tutorial api version: ${version}`); + } -async function createEnvironment(yamlFilename: string) { - const buffer = await fsp.readFile(path.join(tutorial_dir, yamlFilename)); - const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + // const fixture = stepFixture as TestCaseFixture; + this.createEnvironment(yamlFilename); + // TODO need to answer to the talon side only what is necessary + return stepFixture; + } - const editor = ide().openUntitledTextDocument({ - content: fixture.initialState.documentContents, - language: fixture.languageId, - }); -} + async createEnvironment(yamlFilename: string) { + const buffer = await fsp.readFile( + path.join(tutorialDirectory, yamlFilename), + ); + const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + + const editor = ide().openUntitledTextDocument({ + content: fixture.initialState.documentContents, + language: fixture.languageId, + }); + } +} \ No newline at end of file diff --git a/packages/cursorless-engine/src/cursorlessEngine.ts b/packages/cursorless-engine/src/cursorlessEngine.ts index cee5a2aa85..71025dc401 100644 --- a/packages/cursorless-engine/src/cursorlessEngine.ts +++ b/packages/cursorless-engine/src/cursorlessEngine.ts @@ -30,6 +30,7 @@ import { ScopeSupportChecker } from "./scopeProviders/ScopeSupportChecker"; import { ScopeSupportWatcher } from "./scopeProviders/ScopeSupportWatcher"; import { TalonSpokenFormsJsonReader } from "./nodeCommon/TalonSpokenFormsJsonReader"; import { injectIde } from "./singletons/ide.singleton"; +import { Tutorial } from "./core/Tutorial"; export function createCursorlessEngine( treeSitter: TreeSitter, @@ -65,6 +66,9 @@ export function createCursorlessEngine( talonSpokenForms, ); + //debugger; + const tutorial = new Tutorial(hatTokenMap, customSpokenFormGenerator); + ide.disposeOnExit(rangeUpdater, languageDefinitions, hatTokenMap, debug); const commandRunnerDecorators: CommandRunnerDecorator[] = []; @@ -114,6 +118,7 @@ export function createCursorlessEngine( addCommandRunnerDecorator: (decorator: CommandRunnerDecorator) => { commandRunnerDecorators.push(decorator); }, + tutorial, }; } diff --git a/packages/cursorless-vscode/src/extension.ts b/packages/cursorless-vscode/src/extension.ts index 50104316c0..91dc009ca4 100644 --- a/packages/cursorless-vscode/src/extension.ts +++ b/packages/cursorless-vscode/src/extension.ts @@ -91,6 +91,7 @@ export async function activate( runIntegrationTests, addCommandRunnerDecorator, customSpokenFormGenerator, + tutorial, } = createCursorlessEngine( treeSitter, normalizedIde, @@ -130,6 +131,7 @@ export async function activate( scopeVisualizer, keyboardCommands, hats, + tutorial, ); new ReleaseNotes(vscodeApi, context, normalizedIde.messages).maybeShow(); diff --git a/packages/cursorless-vscode/src/registerCommands.ts b/packages/cursorless-vscode/src/registerCommands.ts index ef5901b8f6..b9e82e223a 100644 --- a/packages/cursorless-vscode/src/registerCommands.ts +++ b/packages/cursorless-vscode/src/registerCommands.ts @@ -8,7 +8,7 @@ import { TestCaseRecorder, showCheatsheet, updateDefaults, - tutorialCreate, + Tutorial, } from "@cursorless/cursorless-engine"; import * as vscode from "vscode"; import { showDocumentation, showQuickPick } from "./commands"; @@ -25,6 +25,7 @@ export function registerCommands( scopeVisualizer: ScopeVisualizer, keyboardCommands: KeyboardCommands, hats: VscodeHats, + tutorial: Tutorial, ): void { const commands: Record any> = { // The core Cursorless command @@ -91,8 +92,7 @@ export function registerCommands( ["cursorless.keyboard.modal.modeToggle"]: keyboardCommands.modal.modeToggle, // Tutorial commands - ["cursorless.tutorial.create"]: tutorialCreate, - // ["cursorless.tutorial.create"]: tutorialCommands.create, + ["cursorless.tutorial.create"]: tutorial.create, }; extensionContext.subscriptions.push( From 25be0ca7b5ae5ac353d81db600c7bdfe01040cc8 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Tue, 2 Jan 2024 17:24:42 +0000 Subject: [PATCH 12/59] start parsing things from the extension side we have 2 commands (getContent and setupStep) respectively to get the full content of the tutorial and to ask the extension to set up the state in vscode for the current step. we already support parsing the spoken form for the command to be said by the user during the getContent command and spawning the contents in vscode for the setupStep. We need to parse the other spoken forms and to initialize better the vscode content with the hats, etc. --- cursorless-talon/src/tutorial.py | 134 ++++++++------- packages/common/src/cursorlessCommandIds.ts | 12 +- .../cursorless-engine/src/core/Tutorial.ts | 153 +++++++++++++++--- packages/cursorless-vscode/package.json | 12 +- .../src/keyboard/grammar/generated/grammar.ts | 6 +- .../cursorless-vscode/src/registerCommands.ts | 3 +- 6 files changed, 223 insertions(+), 97 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index e1136178dd..fd2449a87a 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -3,92 +3,90 @@ from pathlib import Path from typing import Callable -import yaml from talon import actions, app from .get_action_spoken_form import lookup_action -regex = re.compile(r"\{(\w+):([^}]+)\}") -tutorial_dir = Path( - r"C:\work\tools\voicecoding\cursorless_fork\packages\cursorless-vscode-e2e\src\suite\fixtures\recorded\tutorial\unit-2-basic-coding" -) - - +# {literalStep:...} +# "To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section." def process_literal_step(argument: str): return f"" - +# {action:...} +# "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", +# "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", +# The {action:editNewLineAfter} action" => "The "pour" action" def process_action(argument: str): _, spoken_form = lookup_action(argument) return f'<*"{spoken_form}"/>' - +# {scopeType:...} +# "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}" +# "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}" def process_scope_type(argument: str): - # TODO not sure what we are trying to achieve here - _, spoken_form = lookup_scope_type(argument) - return f'<*"{spoken_form}"/>' - - -def process_cursorless_command_step(argument: str): - print(f"{argument=}") - step_fixture = yaml.safe_load((tutorial_dir / argument).read_text()) - print(f"{step_fixture['command']=}") - result = actions.user.private_cursorless_run_rpc_command_get( - "cursorless.tutorial.create", - { - "version": 0, - "stepFixture": step_fixture, - "yamlFilename": argument, - }, - ) - print(f"{result=}") - return f"" - # return f"" - - -# TODO get this information from the extension -def cursorless_command_to_spoken_form(command: dict[str, str]): - return command["spokenForm"] - - + #_, spoken_form = lookup_scope_type(argument) + #return f'<*"{spoken_form}"/>' + return f'<*"SCOPETYPE_{argument}"/>' + +# "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", +# +# this builds a dictionary which has keys and values (each one is a spoken form) +# each value is built by calling the function with the argument being passed +# eg interpolation_processor_map["step"]() interpolation_processor_map: dict[str, Callable[[str], str]] = { + # this will exist extension side + # nothing needed as not a Cursorless command "literalStep": process_literal_step, + # import https://github.com/cursorless-dev/cursorless/blob/7341d0f707b1d0a0950a19894be3aebbb33582c8/packages/cursorless-engine/src/generateSpokenForm/defaultSpokenForms/actions.ts#L7C1-L7C1 + # hardcoded list of default spoken form for an action (not yet the customized one) "action": process_action, + # scopeTypeType == "line", etc. +# generator.processScopeType({type: scopeTypeType}) "scopeType": process_scope_type, - "step": process_cursorless_command_step, } - -def process_tutorial_step(raw: str): - print(f"{raw=}") - current_index = 0 - content = "" - for match in regex.finditer(raw): - content += raw[current_index : match.start()] - content += interpolation_processor_map[match.group(1)](match.group(2)) - current_index = match.end() - content += raw[current_index : len(raw)] - print(f"{content=}") - - return { - "content": content, - "restore_callback": print, - "modes": ["command"], - "app": "Code", - "context_hint": "Please open VSCode and enter command mode", - } - - -def get_basic_coding_walkthrough(): - with open(tutorial_dir / "script.json") as f: - script = json.load(f) - - return [ - actions.user.hud_create_walkthrough_step(**process_tutorial_step(step)) - for step in script - ] - - +# TODO all the above will be deleted + +def step_callback(x): + print(f"step_callback8: {x}") + yamlFilename = tutorial_content['yamlFilenames'][x] + if yamlFilename: + response = actions.user.private_cursorless_run_rpc_command_get( + "cursorless.tutorial.setupStep", + { + "version": 0, + "tutorialName": "unit-2-basic-coding", + "yamlFilename": yamlFilename + }, + ) + +tutorial_content = None +def get_basic_coding_walkthrough(): + global tutorial_content + print("get_basic_coding_walkthrough start") + tutorial_content = actions.user.private_cursorless_run_rpc_command_get( + "cursorless.tutorial.getContent", + { + "version": 0, + "tutorialName": "unit-2-basic-coding" + }, + ) + print(f"{tutorial_content=}") + walkthrough_steps = [] + for content in tutorial_content['content']: + walkthrough_steps.append(actions.user.hud_create_walkthrough_step( + content=content, + restore_callback=step_callback, + modes=["command"], + app="Visual Studio Code", # Windows + # app="Code", # OS X? + context_hint="Please open VSCode and enter command mode" + )) + print("get_basic_coding_walkthrough end") + return walkthrough_steps + +# this is adding the menu to the hud +# by adding a list of HudWalkThroughStep def on_ready(): actions.user.hud_add_lazy_walkthrough( "Cursorless basic coding", get_basic_coding_walkthrough diff --git a/packages/common/src/cursorlessCommandIds.ts b/packages/common/src/cursorlessCommandIds.ts index b5f9aa5e5c..21ec0d4516 100644 --- a/packages/common/src/cursorlessCommandIds.ts +++ b/packages/common/src/cursorlessCommandIds.ts @@ -47,7 +47,8 @@ export const cursorlessCommandIds = [ "cursorless.toggleDecorations", "cursorless.showScopeVisualizer", "cursorless.hideScopeVisualizer", - "cursorless.tutorial.create", + "cursorless.tutorial.getContent", + "cursorless.tutorial.setupStep", ] as const satisfies readonly `cursorless.${string}`[]; export type CursorlessCommandId = (typeof cursorlessCommandIds)[number]; @@ -77,6 +78,12 @@ export const cursorlessCommandDescriptions: Record< ["cursorless.hideScopeVisualizer"]: new VisibleCommand( "Hide the scope visualizer", ), + ["cursorless.tutorial.getContent"]: new VisibleCommand( + "Get the tutorial content based on Talon HUD", + ), + ["cursorless.tutorial.setupStep"]: new VisibleCommand( + "Setup the current step for the tutorial based on Talon HUD", + ), ["cursorless.command"]: new HiddenCommand("The core cursorless command"), ["cursorless.showQuickPick"]: new HiddenCommand( @@ -121,7 +128,4 @@ export const cursorlessCommandDescriptions: Record< ["cursorless.keyboard.modal.modeToggle"]: new HiddenCommand( "Toggle the cursorless modal mode", ), - ["cursorless.tutorial.create"]: new VisibleCommand( - "Create the tutorial based on Talon HUD", - ), }; diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 2d18ab629c..cce68846b3 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -9,28 +9,59 @@ import path from "path"; import * as yaml from "js-yaml"; import { promises as fsp } from "node:fs"; -import { TestCaseFixture } from "@cursorless/common"; +import { SpokenForm, SpokenFormSuccess, TestCaseFixture } from "@cursorless/common"; import { Dictionary } from "lodash"; import { ide } from "../singletons/ide.singleton"; import { HatTokenMapImpl } from "./HatTokenMapImpl"; import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; +import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; -const tutorialDirectory = - "C:\\work\\tools\\voicecoding\\cursorless_fork\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\tutorial\\unit-2-basic-coding"; +const fs = require('node:fs'); + +// TODO use a relative path but I'm not sure where these source files are at running time +// and CWD is C:\Users\User\AppData\Local\Programs\Microsoft VS Code\ +const tutorialRootDir = + "C:\\work\\tools\\voicecoding\\cursorless_fork\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\tutorial\\"; + +interface TutorialGetContentArg { + /** + * The version of the tutorial command. + */ + version: 0; + + /** + * The name of the current tutorial + */ + tutorialName: string; +} + +interface TutorialGetContentResponse { + /** + * The version of the tutorial command. + */ + version: 0; + + /** + * The text content of the different steps of the current tutorial + */ + content: Array; -/** - * The argument expected by the tutorial command. - */ -interface TutorialCommandArg { + /** + * The yaml files of the different steps of the current tutorial (if any) + */ + yamlFilenames: Array; +} + +interface TutorialSetupStepArg { /** * The version of the tutorial command. */ version: 0; /** - * A representation of the yaml file + * The name of the current tutorial */ - stepFixture: Dictionary; + tutorialName: string; /** * The yaml file for the current step @@ -39,33 +70,115 @@ interface TutorialCommandArg { } export class Tutorial { + private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; + constructor( hatTokenMap: HatTokenMapImpl, customSpokenFormGenerator: CustomSpokenFormGeneratorImpl, ) { - this.create = this.create.bind(this); + this.getContent = this.getContent.bind(this); + this.setupStep = this.setupStep.bind(this); + + this.customSpokenFormGenerator = customSpokenFormGenerator; } - async create({ version, stepFixture, yamlFilename }: TutorialCommandArg) { + async getContent({ version, tutorialName }: TutorialGetContentArg) { + console.log("getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", tutorialName); if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); } - // const fixture = stepFixture as TestCaseFixture; - this.createEnvironment(yamlFilename); - // TODO need to answer to the talon side only what is necessary - return stepFixture; + const tutorialDir = path.join(tutorialRootDir, tutorialName); + if (!fs.existsSync(tutorialDir)) { + throw new Error(`Invalid tutorial name: ${tutorialName}`); + } + + const scriptFile = path.join(tutorialDir, "script.json"); + if (!fs.existsSync(scriptFile)) { + throw new Error(`Can't file script file: ${scriptFile} in tutorial name: ${tutorialName}`); + } + const buffer = await fsp.readFile(scriptFile); + const contentList = JSON.parse(buffer.toString()); + console.log(contentList); + + // this is trying to catch occurrences of things like "{step:cloneStateInk.yml}" + const re = /\{(\w+):([^}]+)\}/g; + + var m; + var response : TutorialGetContentResponse = { + version: 0, + content: [], + yamlFilenames: [], + }; + // we need to replace the {...} with the right content + for (var content of contentList) { + var yamlFilename = ""; + do { + m = re.exec(content); + if (m) { + const name = m[1]; + const arg = m[2]; + console.log(name, arg); + if (name === "step") + { + const tutorialDir = path.join(tutorialRootDir, tutorialName); + if (!fs.existsSync(tutorialDir)) { + throw new Error(`Invalid tutorial name: ${tutorialName}`); + } + + const yamlFile = path.join(tutorialDir, arg) + if (!fs.existsSync(yamlFile)) { + throw new Error(`Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`); + } + yamlFilename = arg; + + const buffer = await fsp.readFile(yamlFile); + const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + + // command to be said for moving to the next step + const spoken_form = this.customSpokenFormGenerator.commandToSpokenForm( + canonicalizeAndValidateCommand(fixture.command) + ) as SpokenFormSuccess; + console.log("\t", spoken_form.spokenForms[0]); + content = content.replace(m[0], ``) + } + } + } while (m); + response.yamlFilenames.push(yamlFilename); + response.content.push(content); + } + + // return to the talon side + return response; } - async createEnvironment(yamlFilename: string) { - const buffer = await fsp.readFile( - path.join(tutorialDirectory, yamlFilename), - ); - const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + async setupStep({ version, tutorialName, yamlFilename }: TutorialSetupStepArg) { + console.log("setupStep()", tutorialName, yamlFilename); + if (version !== 0) { + throw new Error(`Unsupported tutorial api version: ${version}`); + } + const tutorialDir = path.join(tutorialRootDir, tutorialName); + if (!fs.existsSync(tutorialDir)) { + throw new Error(`Invalid tutorial name: ${tutorialName}`); + } + + // TODO check for directory traversal? + const yamlFile = path.join(tutorialDir, yamlFilename); + if (!fs.existsSync(yamlFile)) { + throw new Error(`Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`); + } + const buffer = await fsp.readFile(yamlFile); + const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + const editor = ide().openUntitledTextDocument({ content: fixture.initialState.documentContents, language: fixture.languageId, }); + + // TODO set up the right hats + + // return to the talon side + return true; } } \ No newline at end of file diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index 0600bd9d8d..f58cd33dc3 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -70,7 +70,9 @@ "onCommand:cursorless.takeSnapshot", "onCommand:cursorless.toggleDecorations", "onCommand:cursorless.showScopeVisualizer", - "onCommand:cursorless.hideScopeVisualizer" + "onCommand:cursorless.hideScopeVisualizer", + "onCommand:cursorless.tutorial.getContent", + "onCommand:cursorless.tutorial.setupStep" ], "main": "./extension.cjs", "capabilities": { @@ -198,6 +200,14 @@ "command": "cursorless.keyboard.modal.modeToggle", "title": "Cursorless: Toggle the cursorless modal mode", "enablement": "false" + }, + { + "command": "cursorless.tutorial.getContent", + "title": "Cursorless: Get the tutorial content based on Talon HUD" + }, + { + "command": "cursorless.tutorial.setupStep", + "title": "Cursorless: Setup the current step for the tutorial based on Talon HUD" } ], "colors": [ diff --git a/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts b/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts index cea81bdda5..fd2b867d6b 100644 --- a/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts +++ b/packages/cursorless-vscode/src/keyboard/grammar/generated/grammar.ts @@ -23,11 +23,11 @@ interface NearleyToken { }; interface NearleyLexer { - reset: (chunk: string, info: any) => void; + reset: (chunk: any, info: any) => void; next: () => NearleyToken | undefined; save: () => any; - formatError: (token: never) => string; - has: (tokenType: string) => boolean; + formatError: (token: any, message: string) => string; + has: (tokenType: any) => boolean; }; interface NearleyRule { diff --git a/packages/cursorless-vscode/src/registerCommands.ts b/packages/cursorless-vscode/src/registerCommands.ts index 69105def2c..42e5df3966 100644 --- a/packages/cursorless-vscode/src/registerCommands.ts +++ b/packages/cursorless-vscode/src/registerCommands.ts @@ -95,7 +95,8 @@ export function registerCommands( ["cursorless.keyboard.modal.modeToggle"]: keyboardCommands.modal.modeToggle, // Tutorial commands - ["cursorless.tutorial.create"]: tutorial.create, + ["cursorless.tutorial.getContent"]: tutorial.getContent, + ["cursorless.tutorial.setupStep"]: tutorial.setupStep, }; extensionContext.subscriptions.push( From a7c7fa2a605ea4e79a20414a8cb3e71941a63a98 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:30:32 +0000 Subject: [PATCH 13/59] [pre-commit.ci lite] apply automatic fixes --- cursorless-talon/src/tutorial.py | 65 ++++++++------- .../cursorless-engine/src/core/Tutorial.ts | 81 +++++++++++-------- 2 files changed, 83 insertions(+), 63 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index fd2449a87a..434734bf95 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -1,17 +1,16 @@ -import json -import re -from pathlib import Path from typing import Callable from talon import actions, app from .get_action_spoken_form import lookup_action + # {literalStep:...} # "To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section." def process_literal_step(argument: str): return f"" + # {action:...} # "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", # "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", @@ -20,16 +19,18 @@ def process_action(argument: str): _, spoken_form = lookup_action(argument) return f'<*"{spoken_form}"/>' + # {scopeType:...} # "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}" # "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}" def process_scope_type(argument: str): - #_, spoken_form = lookup_scope_type(argument) - #return f'<*"{spoken_form}"/>' + # _, spoken_form = lookup_scope_type(argument) + # return f'<*"{spoken_form}"/>' return f'<*"SCOPETYPE_{argument}"/>' + # "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", -# +# # this builds a dictionary which has keys and values (each one is a spoken form) # each value is built by calling the function with the argument being passed # eg interpolation_processor_map["step"]() @@ -41,50 +42,54 @@ def process_scope_type(argument: str): # hardcoded list of default spoken form for an action (not yet the customized one) "action": process_action, # scopeTypeType == "line", etc. -# generator.processScopeType({type: scopeTypeType}) + # generator.processScopeType({type: scopeTypeType}) "scopeType": process_scope_type, } # TODO all the above will be deleted + def step_callback(x): print(f"step_callback8: {x}") - yamlFilename = tutorial_content['yamlFilenames'][x] + yamlFilename = tutorial_content["yamlFilenames"][x] if yamlFilename: - response = actions.user.private_cursorless_run_rpc_command_get( - "cursorless.tutorial.setupStep", - { - "version": 0, - "tutorialName": "unit-2-basic-coding", - "yamlFilename": yamlFilename - }, - ) + actions.user.private_cursorless_run_rpc_command_get( + "cursorless.tutorial.setupStep", + { + "version": 0, + "tutorialName": "unit-2-basic-coding", + "yamlFilename": yamlFilename, + }, + ) + tutorial_content = None -def get_basic_coding_walkthrough(): + + +def get_basic_coding_walkthrough(): global tutorial_content print("get_basic_coding_walkthrough start") tutorial_content = actions.user.private_cursorless_run_rpc_command_get( "cursorless.tutorial.getContent", - { - "version": 0, - "tutorialName": "unit-2-basic-coding" - }, + {"version": 0, "tutorialName": "unit-2-basic-coding"}, ) print(f"{tutorial_content=}") walkthrough_steps = [] - for content in tutorial_content['content']: - walkthrough_steps.append(actions.user.hud_create_walkthrough_step( - content=content, - restore_callback=step_callback, - modes=["command"], - app="Visual Studio Code", # Windows - # app="Code", # OS X? - context_hint="Please open VSCode and enter command mode" - )) + for content in tutorial_content["content"]: + walkthrough_steps.append( + actions.user.hud_create_walkthrough_step( + content=content, + restore_callback=step_callback, + modes=["command"], + app="Visual Studio Code", # Windows + # app="Code", # OS X? + context_hint="Please open VSCode and enter command mode", + ) + ) print("get_basic_coding_walkthrough end") return walkthrough_steps + # this is adding the menu to the hud # by adding a list of HudWalkThroughStep def on_ready(): diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index cce68846b3..8ce40705ee 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -9,14 +9,13 @@ import path from "path"; import * as yaml from "js-yaml"; import { promises as fsp } from "node:fs"; -import { SpokenForm, SpokenFormSuccess, TestCaseFixture } from "@cursorless/common"; -import { Dictionary } from "lodash"; +import { SpokenFormSuccess, TestCaseFixture } from "@cursorless/common"; import { ide } from "../singletons/ide.singleton"; import { HatTokenMapImpl } from "./HatTokenMapImpl"; import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; -const fs = require('node:fs'); +const fs = require("node:fs"); // TODO use a relative path but I'm not sure where these source files are at running time // and CWD is C:\Users\User\AppData\Local\Programs\Microsoft VS Code\ @@ -83,19 +82,24 @@ export class Tutorial { } async getContent({ version, tutorialName }: TutorialGetContentArg) { - console.log("getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", tutorialName); + console.log( + "getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", + tutorialName, + ); if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); } const tutorialDir = path.join(tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); - } - + throw new Error(`Invalid tutorial name: ${tutorialName}`); + } + const scriptFile = path.join(tutorialDir, "script.json"); if (!fs.existsSync(scriptFile)) { - throw new Error(`Can't file script file: ${scriptFile} in tutorial name: ${tutorialName}`); + throw new Error( + `Can't file script file: ${scriptFile} in tutorial name: ${tutorialName}`, + ); } const buffer = await fsp.readFile(scriptFile); const contentList = JSON.parse(buffer.toString()); @@ -103,56 +107,65 @@ export class Tutorial { // this is trying to catch occurrences of things like "{step:cloneStateInk.yml}" const re = /\{(\w+):([^}]+)\}/g; - - var m; - var response : TutorialGetContentResponse = { + + let m; + const response: TutorialGetContentResponse = { version: 0, content: [], yamlFilenames: [], }; // we need to replace the {...} with the right content - for (var content of contentList) { - var yamlFilename = ""; + for (let content of contentList) { + let yamlFilename = ""; do { m = re.exec(content); if (m) { const name = m[1]; const arg = m[2]; console.log(name, arg); - if (name === "step") - { + if (name === "step") { const tutorialDir = path.join(tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); - } - - const yamlFile = path.join(tutorialDir, arg) + throw new Error(`Invalid tutorial name: ${tutorialName}`); + } + + const yamlFile = path.join(tutorialDir, arg); if (!fs.existsSync(yamlFile)) { - throw new Error(`Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`); + throw new Error( + `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, + ); } yamlFilename = arg; - + const buffer = await fsp.readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; - + // command to be said for moving to the next step - const spoken_form = this.customSpokenFormGenerator.commandToSpokenForm( - canonicalizeAndValidateCommand(fixture.command) - ) as SpokenFormSuccess; + const spoken_form = + this.customSpokenFormGenerator.commandToSpokenForm( + canonicalizeAndValidateCommand(fixture.command), + ) as SpokenFormSuccess; console.log("\t", spoken_form.spokenForms[0]); - content = content.replace(m[0], ``) + content = content.replace( + m[0], + ``, + ); } } } while (m); response.yamlFilenames.push(yamlFilename); response.content.push(content); } - + // return to the talon side return response; } - async setupStep({ version, tutorialName, yamlFilename }: TutorialSetupStepArg) { + async setupStep({ + version, + tutorialName, + yamlFilename, + }: TutorialSetupStepArg) { console.log("setupStep()", tutorialName, yamlFilename); if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); @@ -160,17 +173,19 @@ export class Tutorial { const tutorialDir = path.join(tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); + throw new Error(`Invalid tutorial name: ${tutorialName}`); } - + // TODO check for directory traversal? const yamlFile = path.join(tutorialDir, yamlFilename); if (!fs.existsSync(yamlFile)) { - throw new Error(`Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`); + throw new Error( + `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, + ); } const buffer = await fsp.readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; - + const editor = ide().openUntitledTextDocument({ content: fixture.initialState.documentContents, language: fixture.languageId, @@ -181,4 +196,4 @@ export class Tutorial { // return to the talon side return true; } -} \ No newline at end of file +} From 203bdd2539617c6c8d3f75f4d99fdd349130e6f2 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Thu, 4 Jan 2024 14:20:29 +0000 Subject: [PATCH 14/59] refactor and parse literalStep --- cursorless-talon/src/tutorial.py | 2 +- .../cursorless-engine/src/core/Tutorial.ts | 78 ++++++++++++------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index 434734bf95..41d980dfe8 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -50,7 +50,7 @@ def process_scope_type(argument: str): def step_callback(x): - print(f"step_callback8: {x}") + print(f"step_callback1: {x}") yamlFilename = tutorial_content["yamlFilenames"][x] if yamlFilename: actions.user.private_cursorless_run_rpc_command_get( diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 8ce40705ee..97d570da83 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -70,6 +70,8 @@ interface TutorialSetupStepArg { export class Tutorial { private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; + private tutorialName: string; + private yamlFilename: string; constructor( hatTokenMap: HatTokenMapImpl, @@ -79,6 +81,33 @@ export class Tutorial { this.setupStep = this.setupStep.bind(this); this.customSpokenFormGenerator = customSpokenFormGenerator; + this.tutorialName = ""; + this.yamlFilename = ""; + } + + async processStep(content: string, arg: string) { + const tutorialDir = path.join(tutorialRootDir, this.tutorialName); + if (!fs.existsSync(tutorialDir)) { + throw new Error(`Invalid tutorial name: ${this.tutorialName}`); + } + + const yamlFile = path.join(tutorialDir, arg); + if (!fs.existsSync(yamlFile)) { + throw new Error( + `Can't file yaml file: ${yamlFile} in tutorial name: ${this.tutorialName}`, + ); + } + this.yamlFilename = arg; + + const buffer = await fsp.readFile(yamlFile); + const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + + // command to be said for moving to the next step + const spokenForm = this.customSpokenFormGenerator.commandToSpokenForm( + canonicalizeAndValidateCommand(fixture.command), + ) as SpokenFormSuccess; + console.log("\t", spokenForm.spokenForms[0]); + return spokenForm.spokenForms[0]; } async getContent({ version, tutorialName }: TutorialGetContentArg) { @@ -94,6 +123,7 @@ export class Tutorial { if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); } + this.tutorialName = tutorialName; const scriptFile = path.join(tutorialDir, "script.json"); if (!fs.existsSync(scriptFile)) { @@ -109,6 +139,7 @@ export class Tutorial { const re = /\{(\w+):([^}]+)\}/g; let m; + let spokenForm; const response: TutorialGetContentResponse = { version: 0, content: [], @@ -116,44 +147,33 @@ export class Tutorial { }; // we need to replace the {...} with the right content for (let content of contentList) { - let yamlFilename = ""; + this.yamlFilename = ""; do { m = re.exec(content); if (m) { const name = m[1]; const arg = m[2]; console.log(name, arg); - if (name === "step") { - const tutorialDir = path.join(tutorialRootDir, tutorialName); - if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); - } - - const yamlFile = path.join(tutorialDir, arg); - if (!fs.existsSync(yamlFile)) { - throw new Error( - `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, - ); - } - yamlFilename = arg; - - const buffer = await fsp.readFile(yamlFile); - const fixture = yaml.load(buffer.toString()) as TestCaseFixture; - - // command to be said for moving to the next step - const spoken_form = - this.customSpokenFormGenerator.commandToSpokenForm( - canonicalizeAndValidateCommand(fixture.command), - ) as SpokenFormSuccess; - console.log("\t", spoken_form.spokenForms[0]); - content = content.replace( - m[0], - ``, - ); + switch (name) { + case "step": + spokenForm = await this.processStep(content, arg); + content = content.replace(m[0], ``); + break; + case "literalStep": + content = content.replace(m[0], ``); + break; + case "action": + // TODO + break; + case "scopeType": + // TODO + break; + default: + throw new Error(`Unknown name: ${name}`); } } } while (m); - response.yamlFilenames.push(yamlFilename); + response.yamlFilenames.push(this.yamlFilename); response.content.push(content); } From 1bbfe4419e504e74a3007bceab8e03306e240287 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Thu, 4 Jan 2024 17:10:56 +0000 Subject: [PATCH 15/59] finished converting all the spoken forms --- cursorless-talon/src/tutorial.py | 48 +------ .../cursorless-engine/src/core/Tutorial.ts | 117 +++++++++++------- .../tutorial/unit-2-basic-coding/script.json | 18 +-- 3 files changed, 80 insertions(+), 103 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index 41d980dfe8..496f4e57cd 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -5,52 +5,11 @@ from .get_action_spoken_form import lookup_action -# {literalStep:...} -# "To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section." -def process_literal_step(argument: str): - return f"" - - -# {action:...} -# "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", -# "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", -# The {action:editNewLineAfter} action" => "The "pour" action" -def process_action(argument: str): - _, spoken_form = lookup_action(argument) - return f'<*"{spoken_form}"/>' - - -# {scopeType:...} -# "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}" -# "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}" -def process_scope_type(argument: str): - # _, spoken_form = lookup_scope_type(argument) - # return f'<*"{spoken_form}"/>' - return f'<*"SCOPETYPE_{argument}"/>' - - -# "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", -# -# this builds a dictionary which has keys and values (each one is a spoken form) -# each value is built by calling the function with the argument being passed -# eg interpolation_processor_map["step"]() -interpolation_processor_map: dict[str, Callable[[str], str]] = { - # this will exist extension side - # nothing needed as not a Cursorless command - "literalStep": process_literal_step, - # import https://github.com/cursorless-dev/cursorless/blob/7341d0f707b1d0a0950a19894be3aebbb33582c8/packages/cursorless-engine/src/generateSpokenForm/defaultSpokenForms/actions.ts#L7C1-L7C1 - # hardcoded list of default spoken form for an action (not yet the customized one) - "action": process_action, - # scopeTypeType == "line", etc. - # generator.processScopeType({type: scopeTypeType}) - "scopeType": process_scope_type, -} - -# TODO all the above will be deleted +tutorial_content = None def step_callback(x): - print(f"step_callback1: {x}") + print(f"step_callback8: {x}") yamlFilename = tutorial_content["yamlFilenames"][x] if yamlFilename: actions.user.private_cursorless_run_rpc_command_get( @@ -63,9 +22,6 @@ def step_callback(x): ) -tutorial_content = None - - def get_basic_coding_walkthrough(): global tutorial_content print("get_basic_coding_walkthrough start") diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 97d570da83..816857d3db 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -9,11 +9,17 @@ import path from "path"; import * as yaml from "js-yaml"; import { promises as fsp } from "node:fs"; -import { SpokenFormSuccess, TestCaseFixture } from "@cursorless/common"; +import { + ScopeType, + SimpleScopeType, + SpokenFormSuccess, + TestCaseFixture, +} from "@cursorless/common"; import { ide } from "../singletons/ide.singleton"; import { HatTokenMapImpl } from "./HatTokenMapImpl"; import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; +import { actions } from "../generateSpokenForm/defaultSpokenForms/actions"; const fs = require("node:fs"); @@ -70,8 +76,6 @@ interface TutorialSetupStepArg { export class Tutorial { private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; - private tutorialName: string; - private yamlFilename: string; constructor( hatTokenMap: HatTokenMapImpl, @@ -81,23 +85,21 @@ export class Tutorial { this.setupStep = this.setupStep.bind(this); this.customSpokenFormGenerator = customSpokenFormGenerator; - this.tutorialName = ""; - this.yamlFilename = ""; } - async processStep(content: string, arg: string) { - const tutorialDir = path.join(tutorialRootDir, this.tutorialName); + async processStep(arg: string, tutorialName: string) { + const tutorialDir = path.join(tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${this.tutorialName}`); + throw new Error(`Invalid tutorial name: ${tutorialName}`); } const yamlFile = path.join(tutorialDir, arg); if (!fs.existsSync(yamlFile)) { throw new Error( - `Can't file yaml file: ${yamlFile} in tutorial name: ${this.tutorialName}`, + `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, ); } - this.yamlFilename = arg; + const yamlFilename = arg; const buffer = await fsp.readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; @@ -107,23 +109,23 @@ export class Tutorial { canonicalizeAndValidateCommand(fixture.command), ) as SpokenFormSuccess; console.log("\t", spokenForm.spokenForms[0]); - return spokenForm.spokenForms[0]; + return [spokenForm.spokenForms[0], yamlFilename]; } - async getContent({ version, tutorialName }: TutorialGetContentArg) { - console.log( - "getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", - tutorialName, - ); - if (version !== 0) { - throw new Error(`Unsupported tutorial api version: ${version}`); - } + async processScopeType(arg: any) { + const scopeType = yaml.load(arg.toString()) as ScopeType; + const spokenForm_ = + this.customSpokenFormGenerator.scopeTypeToSpokenForm(scopeType); + const spokenForm = spokenForm_ as SpokenFormSuccess; + console.log("\t", spokenForm.spokenForms[0]); + return spokenForm.spokenForms[0]; + } + async loadTutorialScript(tutorialName: string) { const tutorialDir = path.join(tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); } - this.tutorialName = tutorialName; const scriptFile = path.join(tutorialDir, "script.json"); if (!fs.existsSync(scriptFile)) { @@ -134,9 +136,22 @@ export class Tutorial { const buffer = await fsp.readFile(scriptFile); const contentList = JSON.parse(buffer.toString()); console.log(contentList); + return contentList; + } + + async getContent({ version, tutorialName }: TutorialGetContentArg) { + console.log( + "getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", + tutorialName, + ); + if (version !== 0) { + throw new Error(`Unsupported tutorial api version: ${version}`); + } - // this is trying to catch occurrences of things like "{step:cloneStateInk.yml}" - const re = /\{(\w+):([^}]+)\}/g; + const contentList = await this.loadTutorialScript(tutorialName); + + // this is trying to catch occurrences of things like "%%step:cloneStateInk.yml%%" + const re = /%%(\w+):([^%]+)%%/; let m; let spokenForm; @@ -147,33 +162,39 @@ export class Tutorial { }; // we need to replace the {...} with the right content for (let content of contentList) { - this.yamlFilename = ""; - do { - m = re.exec(content); - if (m) { - const name = m[1]; - const arg = m[2]; - console.log(name, arg); - switch (name) { - case "step": - spokenForm = await this.processStep(content, arg); - content = content.replace(m[0], ``); - break; - case "literalStep": - content = content.replace(m[0], ``); - break; - case "action": - // TODO - break; - case "scopeType": - // TODO - break; - default: - throw new Error(`Unknown name: ${name}`); - } + let yamlFilename = ""; + m = re.exec(content); + while (m) { + const name = m[1]; + const arg = m[2]; + console.log(name, arg); + switch (name) { + case "step": + [spokenForm, yamlFilename] = await this.processStep( + arg, + tutorialName, + ); + content = content.replace(m[0], ``); + break; + case "literalStep": + content = content.replace(m[0], ``); + break; + case "action": + // hardcoded list of default spoken form for an action (not yet the user customized one) + spokenForm = actions[arg as keyof typeof actions]; + console.log("\t", spokenForm); + content = content.replace(m[0], `<*"${spokenForm}"/>`); + break; + case "scopeType": + spokenForm = await this.processScopeType(arg); + content = content.replace(m[0], `<*"${spokenForm}"/>`); + break; + default: + throw new Error(`Unknown name: ${name}`); } - } while (m); - response.yamlFilenames.push(this.yamlFilename); + m = re.exec(content); + } + response.yamlFilenames.push(yamlFilename); response.content.push(content); } diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json index 898810504e..5da1a2cd08 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json @@ -1,12 +1,12 @@ [ - "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: {step:cloneStateInk.yml}", - "{scopeType:statement} is one of many scopes supported by cursorless. To see all available scopes, use the command {literalStep:cursorless help}, and look at the Scopes section.", - "Cursorless tries its best to keep your commands short. In the following command, we just say {scopeType:string} once, but cursorless infers that both targets are strings: {step:swapStringAirWithWhale.yml}", - "Great. Let's learn a new action. The {action:editNewLineAfter} action lets you start editing a new line below any line on your screen: {step:pourUrge.yml}", - "Now let's try applying a cursorless action to the current line: {step:dedentThis.yml}", - "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the {action:replaceWithTarget} command: {step:bringStateUrge.yml}", - "{action:replaceWithTarget} also works with two targets just like {action:swapTargets}: {step:bringBlueCapToValueRisk.yml}", - "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: {step:chuckArgueBlueVest.yml}", - "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say {literalStep:cursorless help} to see a list.", + "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: %%step:cloneStateInk.yml%%", + "%%scopeType:{type: statement}%% is one of many scopes supported by cursorless. To see all available scopes, use the command %%literalStep:cursorless help%%, and look at the Scopes section.", + "Cursorless tries its best to keep your commands short. In the following command, we just say %%scopeType:{type: surroundingPair, delimiter: string}%% once, but cursorless infers that both targets are strings: %%step:swapStringAirWithWhale.yml%%", + "Great. Let's learn a new action. The %%action:editNewLineAfter%% action lets you start editing a new line below any line on your screen: %%step:pourUrge.yml%%", + "Now let's try applying a cursorless action to the current line: %%step:dedentThis.yml%%", + "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the %%action:replaceWithTarget%% command: %%step:bringStateUrge.yml%%", + "%%action:replaceWithTarget%% also works with two targets just like %%action:swapTargets%%: %%step:bringBlueCapToValueRisk.yml%%", + "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: %%step:chuckArgueBlueVest.yml%%", + "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say %%literalStep:cursorless help%% to see a list.", "As always, feel free to stick around and play with this file to practice what you've just learned. Happy coding :)" ] From f3a3bf36b44ee9370de60e3cdbe9486c5e36e041 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 17:12:23 +0000 Subject: [PATCH 16/59] [pre-commit.ci lite] apply automatic fixes --- cursorless-talon/src/tutorial.py | 5 ----- packages/cursorless-engine/src/core/Tutorial.ts | 1 - 2 files changed, 6 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index 496f4e57cd..b8a3361cd3 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -1,10 +1,5 @@ -from typing import Callable - from talon import actions, app -from .get_action_spoken_form import lookup_action - - tutorial_content = None diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 816857d3db..eb3a2d67a5 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -11,7 +11,6 @@ import { promises as fsp } from "node:fs"; import { ScopeType, - SimpleScopeType, SpokenFormSuccess, TestCaseFixture, } from "@cursorless/common"; From 14363531f62fb1af43f9eeba1f2964e8aa4288d9 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Fri, 5 Jan 2024 10:58:21 +0000 Subject: [PATCH 17/59] document functions and use a tutorial directory directly in the assets --- cursorless-talon/src/tutorial.py | 2 +- .../cursorless-engine/src/core/Tutorial.ts | 40 ++++++++++--------- .../src/scripts/populateDist/assets.ts | 5 +++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index b8a3361cd3..f4beee30ee 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -4,7 +4,7 @@ def step_callback(x): - print(f"step_callback8: {x}") + print(f"step_callback1: {x}") yamlFilename = tutorial_content["yamlFilenames"][x] if yamlFilename: actions.user.private_cursorless_run_rpc_command_get( diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index eb3a2d67a5..a7a9a98bae 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -1,13 +1,6 @@ -// import { readFile, writeFile } from "fs/promises"; -// import { parse } from "node-html-parser"; -// import produce from "immer"; -// import { sortBy } from "lodash"; -// import { ide } from "../singletons/ide.singleton"; import path from "path"; -// import { getCursorlessRepoRoot } from "@cursorless/common"; - import * as yaml from "js-yaml"; -import { promises as fsp } from "node:fs"; +import fs, { promises as fsp } from "node:fs"; import { ScopeType, @@ -20,13 +13,6 @@ import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpoke import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; import { actions } from "../generateSpokenForm/defaultSpokenForms/actions"; -const fs = require("node:fs"); - -// TODO use a relative path but I'm not sure where these source files are at running time -// and CWD is C:\Users\User\AppData\Local\Programs\Microsoft VS Code\ -const tutorialRootDir = - "C:\\work\\tools\\voicecoding\\cursorless_fork\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\tutorial\\"; - interface TutorialGetContentArg { /** * The version of the tutorial command. @@ -75,6 +61,7 @@ interface TutorialSetupStepArg { export class Tutorial { private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; + private tutorialRootDir: string; constructor( hatTokenMap: HatTokenMapImpl, @@ -84,10 +71,15 @@ export class Tutorial { this.setupStep = this.setupStep.bind(this); this.customSpokenFormGenerator = customSpokenFormGenerator; + const extensionPath = ide().assetsRoot; + this.tutorialRootDir = path.join(extensionPath, "tutorial"); } + /** + * Handle the argument of a "%%step:cloneStateInk.yml%%"" + */ async processStep(arg: string, tutorialName: string) { - const tutorialDir = path.join(tutorialRootDir, tutorialName); + const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); } @@ -111,6 +103,9 @@ export class Tutorial { return [spokenForm.spokenForms[0], yamlFilename]; } + /** + * Handle the argument of a "%%scopeType:{type: statement}%%" + */ async processScopeType(arg: any) { const scopeType = yaml.load(arg.toString()) as ScopeType; const spokenForm_ = @@ -120,8 +115,11 @@ export class Tutorial { return spokenForm.spokenForms[0]; } + /** + * Load the "script.json" script for the current tutorial + */ async loadTutorialScript(tutorialName: string) { - const tutorialDir = path.join(tutorialRootDir, tutorialName); + const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); } @@ -138,6 +136,9 @@ export class Tutorial { return contentList; } + /** + * Handle the "cursorless.tutorial.getContent" command + */ async getContent({ version, tutorialName }: TutorialGetContentArg) { console.log( "getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", @@ -201,6 +202,9 @@ export class Tutorial { return response; } + /** + * Handle the "cursorless.tutorial.setupStep" command + */ async setupStep({ version, tutorialName, @@ -211,7 +215,7 @@ export class Tutorial { throw new Error(`Unsupported tutorial api version: ${version}`); } - const tutorialDir = path.join(tutorialRootDir, tutorialName); + const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); } diff --git a/packages/cursorless-vscode/src/scripts/populateDist/assets.ts b/packages/cursorless-vscode/src/scripts/populateDist/assets.ts index 8331b75715..77b4c2c248 100644 --- a/packages/cursorless-vscode/src/scripts/populateDist/assets.ts +++ b/packages/cursorless-vscode/src/scripts/populateDist/assets.ts @@ -24,6 +24,11 @@ export const assets: Asset[] = [ destination: "fonts/cursorless.woff", }, { source: "../../images/hats", destination: "images/hats" }, + { + source: + "../../packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial", + destination: "tutorial", + }, { source: "./images/logo.png", destination: "images/logo.png" }, { source: "../../images/logo.svg", destination: "images/logo.svg" }, { source: "../../schemas", destination: "schemas" }, From 43734e73204716486616ffefc4a35bfd8b861d04 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Fri, 5 Jan 2024 17:16:39 +0000 Subject: [PATCH 18/59] initial working version of the tutorial --- cursorless-talon/src/tutorial.py | 17 ++++++++- packages/common/src/types/HatTokenMap.ts | 32 +++++++++++++++++ packages/common/src/types/Position.ts | 6 +++- packages/common/src/types/Selection.ts | 8 ++++- .../src/core/IndividualHatMap.ts | 2 +- .../cursorless-engine/src/core/Tutorial.ts | 33 +++++++++++++++-- .../cursorless-engine/src/cursorlessEngine.ts | 1 - .../src/suite/recorded.vscode.test.ts | 35 +------------------ 8 files changed, 93 insertions(+), 41 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index f4beee30ee..201661c3b1 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -4,7 +4,7 @@ def step_callback(x): - print(f"step_callback1: {x}") + print(f"step_callback5: {x}") yamlFilename = tutorial_content["yamlFilenames"][x] if yamlFilename: actions.user.private_cursorless_run_rpc_command_get( @@ -15,6 +15,21 @@ def step_callback(x): "yamlFilename": yamlFilename, }, ) + # the below seems not needed + # while True: + # # this is a hack to make sure vscode window was correctly reloaded + # # for instance if the user focused another window like the browser + # # when saying "help cursorless" + # ret = actions.user.private_cursorless_run_rpc_command_get( + # "cursorless.tutorial.setupStep", + # { + # "version": 1, + # "tutorialName": "unit-1-basic-coding", + # "yamlFilename": yamlFilename, + # }, + # ) + # if ret: + # break def get_basic_coding_walkthrough(): diff --git a/packages/common/src/types/HatTokenMap.ts b/packages/common/src/types/HatTokenMap.ts index ee2f8dc398..27cd31e416 100644 --- a/packages/common/src/types/HatTokenMap.ts +++ b/packages/common/src/types/HatTokenMap.ts @@ -1,3 +1,4 @@ +import { SerializedMarks, TextEditor, plainObjectToRange, splitKey } from ".."; import { HatStyleName } from "../ide/types/hatStyles.types"; import { Range } from "./Range"; import { Token } from "./Token"; @@ -21,3 +22,34 @@ export interface ReadOnlyHatMap { getEntries(): readonly [string, Token][]; getToken(hatStyle: HatStyleName, character: string): Token; } + +export function getTokenHats( + marks: SerializedMarks | undefined, + editor: TextEditor, +): TokenHat[] { + if (marks == null) { + return []; + } + + return Object.entries(marks).map(([key, token]) => { + const { hatStyle, character } = splitKey(key); + const range = plainObjectToRange(token); + + return { + hatStyle, + grapheme: character, + token: { + editor, + range, + offsets: { + start: editor.document.offsetAt(range.start), + end: editor.document.offsetAt(range.end), + }, + text: editor.document.getText(range), + }, + + // NB: We don't care about the hat range for this test + hatRange: range, + }; + }); +} diff --git a/packages/common/src/types/Position.ts b/packages/common/src/types/Position.ts index 5751530929..7cc5213fec 100644 --- a/packages/common/src/types/Position.ts +++ b/packages/common/src/types/Position.ts @@ -1,4 +1,4 @@ -import { Range, TextDocument } from ".."; +import { PositionPlainObject, Range, TextDocument } from ".."; export class Position { /** @@ -167,3 +167,7 @@ export function adjustPosition( ): Position { return doc.positionAt(doc.offsetAt(pos) + by); } + +export function createPosition(position: PositionPlainObject) { + return new Position(position.line, position.character); +} diff --git a/packages/common/src/types/Selection.ts b/packages/common/src/types/Selection.ts index a202c47251..536512d4d3 100644 --- a/packages/common/src/types/Selection.ts +++ b/packages/common/src/types/Selection.ts @@ -1,4 +1,4 @@ -import { Position, Range } from ".."; +import { Position, Range, SelectionPlainObject, createPosition } from ".."; export class Selection extends Range { /** @@ -85,3 +85,9 @@ export class Selection extends Range { return this.concise(); } } + +export function createSelection(selection: SelectionPlainObject): Selection { + const active = createPosition(selection.active); + const anchor = createPosition(selection.anchor); + return new Selection(anchor, active); +} diff --git a/packages/cursorless-engine/src/core/IndividualHatMap.ts b/packages/cursorless-engine/src/core/IndividualHatMap.ts index 5b00390b4e..334c15b516 100644 --- a/packages/cursorless-engine/src/core/IndividualHatMap.ts +++ b/packages/cursorless-engine/src/core/IndividualHatMap.ts @@ -58,7 +58,7 @@ export class IndividualHatMap implements ReadOnlyHatMap { } /** - * Overwrites the hat assignemnt for this hat token map. + * Overwrites the hat assignment for this hat token map. * * @param tokenHats The new hat assignments */ diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index a7a9a98bae..c87e35a22d 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -3,9 +3,17 @@ import * as yaml from "js-yaml"; import fs, { promises as fsp } from "node:fs"; import { + Position, ScopeType, + Selection, + SerializedMarks, SpokenFormSuccess, TestCaseFixture, + TextEditor, + Token, + TokenHat, + createSelection, + getTokenHats, } from "@cursorless/common"; import { ide } from "../singletons/ide.singleton"; import { HatTokenMapImpl } from "./HatTokenMapImpl"; @@ -60,6 +68,7 @@ interface TutorialSetupStepArg { } export class Tutorial { + private hatTokenMap: HatTokenMapImpl; private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; private tutorialRootDir: string; @@ -70,7 +79,9 @@ export class Tutorial { this.getContent = this.getContent.bind(this); this.setupStep = this.setupStep.bind(this); + this.hatTokenMap = hatTokenMap; this.customSpokenFormGenerator = customSpokenFormGenerator; + const extensionPath = ide().assetsRoot; this.tutorialRootDir = path.join(extensionPath, "tutorial"); } @@ -204,6 +215,7 @@ export class Tutorial { /** * Handle the "cursorless.tutorial.setupStep" command + * @see packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts */ async setupStep({ version, @@ -230,12 +242,29 @@ export class Tutorial { const buffer = await fsp.readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; - const editor = ide().openUntitledTextDocument({ + const editor = await ide().openUntitledTextDocument({ content: fixture.initialState.documentContents, language: fixture.languageId, }); + const editableEditor = ide().getEditableTextEditor(editor); + + // Ensure that the expected cursor/selections are present + editableEditor.selections = + fixture.initialState.selections.map(createSelection); + // in case we don't want to use the createSelection helper function + // editableEditor.selections = fixture.initialState.selections.map( + // (selections) => { + // return new Selection( + // new Position(selections.anchor.line, selections.anchor.character), + // new Position(selections.active.line, selections.active.character), + // ); + // }, + // ); - // TODO set up the right hats + // Ensure that the expected hats are present + await this.hatTokenMap.allocateHats( + getTokenHats(fixture.initialState.marks, editor), + ); // return to the talon side return true; diff --git a/packages/cursorless-engine/src/cursorlessEngine.ts b/packages/cursorless-engine/src/cursorlessEngine.ts index 71025dc401..92c3a4cabc 100644 --- a/packages/cursorless-engine/src/cursorlessEngine.ts +++ b/packages/cursorless-engine/src/cursorlessEngine.ts @@ -66,7 +66,6 @@ export function createCursorlessEngine( talonSpokenForms, ); - //debugger; const tutorial = new Tutorial(hatTokenMap, customSpokenFormGenerator); ide.disposeOnExit(rangeUpdater, languageDefinitions, hatTokenMap, debug); diff --git a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts index 23a440c5ca..fd32d5dd90 100644 --- a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts @@ -4,10 +4,10 @@ import { ExcludableSnapshotField, extractTargetedMarks, getRecordedTestPaths, + getTokenHats, HatStability, marksToPlainObject, omitByDeep, - plainObjectToRange, PositionPlainObject, rangeToPlainObject, ReadOnlyHatMap, @@ -19,8 +19,6 @@ import { SpyIDE, spyIDERecordedValuesToPlainObject, TestCaseFixtureLegacy, - TextEditor, - TokenHat, } from "@cursorless/common"; import { getCursorlessApi, @@ -244,34 +242,3 @@ function checkMarks( assert.deepStrictEqual(rangeToPlainObject(currentToken.range), token); }); } - -function getTokenHats( - marks: SerializedMarks | undefined, - editor: TextEditor, -): TokenHat[] { - if (marks == null) { - return []; - } - - return Object.entries(marks).map(([key, token]) => { - const { hatStyle, character } = splitKey(key); - const range = plainObjectToRange(token); - - return { - hatStyle, - grapheme: character, - token: { - editor, - range, - offsets: { - start: editor.document.offsetAt(range.start), - end: editor.document.offsetAt(range.end), - }, - text: editor.document.getText(range), - }, - - // NB: We don't care about the hat range for this test - hatRange: range, - }; - }); -} From 171abdd2e9e1d0580fb01bb6059041c7ba1b645d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Fri, 5 Jan 2024 17:18:45 +0000 Subject: [PATCH 19/59] [pre-commit.ci lite] apply automatic fixes --- packages/cursorless-engine/src/core/Tutorial.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index c87e35a22d..4ac7893584 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -3,15 +3,9 @@ import * as yaml from "js-yaml"; import fs, { promises as fsp } from "node:fs"; import { - Position, ScopeType, - Selection, - SerializedMarks, SpokenFormSuccess, TestCaseFixture, - TextEditor, - Token, - TokenHat, createSelection, getTokenHats, } from "@cursorless/common"; From cd5bfc6d93f49c27b8e153288ce395b8ede97d81 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:36:48 +0000 Subject: [PATCH 20/59] tweaks --- .../src/get_action_spoken_form.py | 30 ------------------- cursorless-talon/src/tutorial.py | 3 +- 2 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 cursorless-talon/src/get_action_spoken_form.py diff --git a/cursorless-talon/src/get_action_spoken_form.py b/cursorless-talon/src/get_action_spoken_form.py deleted file mode 100644 index f271be666a..0000000000 --- a/cursorless-talon/src/get_action_spoken_form.py +++ /dev/null @@ -1,30 +0,0 @@ -from talon import registry - -from .actions.actions import ACTION_LIST_NAMES -from .conventions import get_cursorless_list_name - - -def make_cursorless_list_reverse_look_up(*raw_list_names: str): - return make_list_reverse_look_up( - *[get_cursorless_list_name(raw_list_name) for raw_list_name in raw_list_names] - ) - - -def make_list_reverse_look_up(*list_names: str): - """ - Given a list of talon list names, returns a function that does a reverse - look-up in all lists to find the spoken form for its input. - """ - - def return_func(argument: str): - for list_name in list_names: - for spoken_form, value in registry.lists[list_name][-1].items(): - if value == argument: - return list_name, spoken_form - - raise LookupError(f"Unknown identifier `{argument}`") - - return return_func - - -lookup_action = make_cursorless_list_reverse_look_up(*ACTION_LIST_NAMES) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index 201661c3b1..7ce2457ffd 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -48,7 +48,8 @@ def get_basic_coding_walkthrough(): restore_callback=step_callback, modes=["command"], app="Visual Studio Code", # Windows - # app="Code", # OS X? + # app="Code", # OS X? + # TODO: Fix this; should just be "vscode". Prob need fix to talon_hud itself context_hint="Please open VSCode and enter command mode", ) ) From e022e69b5072aa65758154126e127d8c59c40d4a Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:51:58 +0000 Subject: [PATCH 21/59] change function --- cursorless-talon/src/tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index 7ce2457ffd..0a62c274be 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -7,7 +7,7 @@ def step_callback(x): print(f"step_callback5: {x}") yamlFilename = tutorial_content["yamlFilenames"][x] if yamlFilename: - actions.user.private_cursorless_run_rpc_command_get( + actions.user.private_cursorless_run_rpc_command_and_wait( "cursorless.tutorial.setupStep", { "version": 0, From e0f3c02061c6af2aaf12053a12f875516a0e273a Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:05:26 +0000 Subject: [PATCH 22/59] more cleanup --- packages/common/src/cursorlessCommandIds.ts | 8 ++--- packages/common/src/index.ts | 1 + packages/common/src/types/HatTokenMap.ts | 32 ----------------- packages/common/src/types/Position.ts | 6 +--- packages/common/src/types/Selection.ts | 8 +---- .../src/util/serializedMarksToTokenHats.ts | 36 +++++++++++++++++++ .../cursorless-engine/src/core/Tutorial.ts | 17 ++++----- .../src/suite/recorded.vscode.test.ts | 4 +-- 8 files changed, 54 insertions(+), 58 deletions(-) create mode 100644 packages/common/src/util/serializedMarksToTokenHats.ts diff --git a/packages/common/src/cursorlessCommandIds.ts b/packages/common/src/cursorlessCommandIds.ts index 21ec0d4516..5e8b1705cd 100644 --- a/packages/common/src/cursorlessCommandIds.ts +++ b/packages/common/src/cursorlessCommandIds.ts @@ -78,11 +78,11 @@ export const cursorlessCommandDescriptions: Record< ["cursorless.hideScopeVisualizer"]: new VisibleCommand( "Hide the scope visualizer", ), - ["cursorless.tutorial.getContent"]: new VisibleCommand( - "Get the tutorial content based on Talon HUD", + ["cursorless.tutorial.getContent"]: new HiddenCommand( + "Get tutorial content; used by the Talon HUD during tutorial initialization", ), - ["cursorless.tutorial.setupStep"]: new VisibleCommand( - "Setup the current step for the tutorial based on Talon HUD", + ["cursorless.tutorial.setupStep"]: new HiddenCommand( + "Setup the current tutorial step; used by the Talon HUD before each tutorial step", ), ["cursorless.command"]: new HiddenCommand("The core cursorless command"), diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index b35bd962f7..9bae34f549 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -48,6 +48,7 @@ export * from "./types/HatTokenMap"; export * from "./types/ScopeProvider"; export * from "./types/SpokenForm"; export * from "./util/textFormatters"; +export * from "./util/serializedMarksToTokenHats"; export * from "./types/snippet.types"; export * from "./testUtil/fromPlainObject"; export * from "./testUtil/spyToPlainObject"; diff --git a/packages/common/src/types/HatTokenMap.ts b/packages/common/src/types/HatTokenMap.ts index 27cd31e416..ee2f8dc398 100644 --- a/packages/common/src/types/HatTokenMap.ts +++ b/packages/common/src/types/HatTokenMap.ts @@ -1,4 +1,3 @@ -import { SerializedMarks, TextEditor, plainObjectToRange, splitKey } from ".."; import { HatStyleName } from "../ide/types/hatStyles.types"; import { Range } from "./Range"; import { Token } from "./Token"; @@ -22,34 +21,3 @@ export interface ReadOnlyHatMap { getEntries(): readonly [string, Token][]; getToken(hatStyle: HatStyleName, character: string): Token; } - -export function getTokenHats( - marks: SerializedMarks | undefined, - editor: TextEditor, -): TokenHat[] { - if (marks == null) { - return []; - } - - return Object.entries(marks).map(([key, token]) => { - const { hatStyle, character } = splitKey(key); - const range = plainObjectToRange(token); - - return { - hatStyle, - grapheme: character, - token: { - editor, - range, - offsets: { - start: editor.document.offsetAt(range.start), - end: editor.document.offsetAt(range.end), - }, - text: editor.document.getText(range), - }, - - // NB: We don't care about the hat range for this test - hatRange: range, - }; - }); -} diff --git a/packages/common/src/types/Position.ts b/packages/common/src/types/Position.ts index 7cc5213fec..5751530929 100644 --- a/packages/common/src/types/Position.ts +++ b/packages/common/src/types/Position.ts @@ -1,4 +1,4 @@ -import { PositionPlainObject, Range, TextDocument } from ".."; +import { Range, TextDocument } from ".."; export class Position { /** @@ -167,7 +167,3 @@ export function adjustPosition( ): Position { return doc.positionAt(doc.offsetAt(pos) + by); } - -export function createPosition(position: PositionPlainObject) { - return new Position(position.line, position.character); -} diff --git a/packages/common/src/types/Selection.ts b/packages/common/src/types/Selection.ts index 536512d4d3..a202c47251 100644 --- a/packages/common/src/types/Selection.ts +++ b/packages/common/src/types/Selection.ts @@ -1,4 +1,4 @@ -import { Position, Range, SelectionPlainObject, createPosition } from ".."; +import { Position, Range } from ".."; export class Selection extends Range { /** @@ -85,9 +85,3 @@ export class Selection extends Range { return this.concise(); } } - -export function createSelection(selection: SelectionPlainObject): Selection { - const active = createPosition(selection.active); - const anchor = createPosition(selection.anchor); - return new Selection(anchor, active); -} diff --git a/packages/common/src/util/serializedMarksToTokenHats.ts b/packages/common/src/util/serializedMarksToTokenHats.ts new file mode 100644 index 0000000000..94203c2901 --- /dev/null +++ b/packages/common/src/util/serializedMarksToTokenHats.ts @@ -0,0 +1,36 @@ +import { plainObjectToRange } from "../testUtil/fromPlainObject"; +import { splitKey } from "./splitKey"; +import { SerializedMarks } from "./toPlainObject"; +import { TextEditor } from "../types/TextEditor"; +import { TokenHat } from "../types/HatTokenMap"; + +export function serializedMarksToTokenHats( + marks: SerializedMarks | undefined, + editor: TextEditor, +): TokenHat[] { + if (marks == null) { + return []; + } + + return Object.entries(marks).map(([key, token]) => { + const { hatStyle, character } = splitKey(key); + const range = plainObjectToRange(token); + + return { + hatStyle, + grapheme: character, + token: { + editor, + range, + offsets: { + start: editor.document.offsetAt(range.start), + end: editor.document.offsetAt(range.end), + }, + text: editor.document.getText(range), + }, + + // NB: We don't care about the hat range for this test + hatRange: range, + }; + }); +} diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/Tutorial.ts index 4ac7893584..ea3e294c47 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/Tutorial.ts @@ -1,19 +1,19 @@ -import path from "path"; import * as yaml from "js-yaml"; import fs, { promises as fsp } from "node:fs"; +import path from "path"; import { ScopeType, SpokenFormSuccess, TestCaseFixture, - createSelection, - getTokenHats, + serializedMarksToTokenHats, + plainObjectToSelection, } from "@cursorless/common"; +import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; +import { actions } from "../generateSpokenForm/defaultSpokenForms/actions"; import { ide } from "../singletons/ide.singleton"; import { HatTokenMapImpl } from "./HatTokenMapImpl"; -import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; -import { actions } from "../generateSpokenForm/defaultSpokenForms/actions"; interface TutorialGetContentArg { /** @@ -243,8 +243,9 @@ export class Tutorial { const editableEditor = ide().getEditableTextEditor(editor); // Ensure that the expected cursor/selections are present - editableEditor.selections = - fixture.initialState.selections.map(createSelection); + editableEditor.selections = fixture.initialState.selections.map( + plainObjectToSelection, + ); // in case we don't want to use the createSelection helper function // editableEditor.selections = fixture.initialState.selections.map( // (selections) => { @@ -257,7 +258,7 @@ export class Tutorial { // Ensure that the expected hats are present await this.hatTokenMap.allocateHats( - getTokenHats(fixture.initialState.marks, editor), + serializedMarksToTokenHats(fixture.initialState.marks, editor), ); // return to the talon side diff --git a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts index fd32d5dd90..a65a3273ec 100644 --- a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts @@ -4,7 +4,7 @@ import { ExcludableSnapshotField, extractTargetedMarks, getRecordedTestPaths, - getTokenHats, + serializedMarksToTokenHats, HatStability, marksToPlainObject, omitByDeep, @@ -105,7 +105,7 @@ async function runTest(file: string, spyIde: SpyIDE) { // Ensure that the expected hats are present await hatTokenMap.allocateHats( - getTokenHats(fixture.initialState.marks, spyIde.activeTextEditor!), + serializedMarksToTokenHats(fixture.initialState.marks, spyIde.activeTextEditor!), ); const readableHatMap = await hatTokenMap.getReadableMap(usePrePhraseSnapshot); From 142ec58faf186213fe834e6c03918a25cab86ed5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:06:51 +0000 Subject: [PATCH 23/59] [pre-commit.ci lite] apply automatic fixes --- .../cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts index a65a3273ec..dbb566439c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts @@ -105,7 +105,10 @@ async function runTest(file: string, spyIde: SpyIDE) { // Ensure that the expected hats are present await hatTokenMap.allocateHats( - serializedMarksToTokenHats(fixture.initialState.marks, spyIde.activeTextEditor!), + serializedMarksToTokenHats( + fixture.initialState.marks, + spyIde.activeTextEditor!, + ), ); const readableHatMap = await hatTokenMap.getReadableMap(usePrePhraseSnapshot); From 7ed3aacf850c9fe07483d619194d8f02bec81488 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:08:57 +0000 Subject: [PATCH 24/59] fix --- packages/cursorless-vscode/package.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index f58cd33dc3..5cdec92719 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -126,6 +126,16 @@ "command": "cursorless.hideScopeVisualizer", "title": "Cursorless: Hide the scope visualizer" }, + { + "command": "cursorless.tutorial.getContent", + "title": "Cursorless: Get tutorial content; used by the Talon HUD during tutorial initialization", + "enablement": "false" + }, + { + "command": "cursorless.tutorial.setupStep", + "title": "Cursorless: Setup the current tutorial step; used by the Talon HUD before each tutorial step", + "enablement": "false" + }, { "command": "cursorless.command", "title": "Cursorless: The core cursorless command", @@ -200,14 +210,6 @@ "command": "cursorless.keyboard.modal.modeToggle", "title": "Cursorless: Toggle the cursorless modal mode", "enablement": "false" - }, - { - "command": "cursorless.tutorial.getContent", - "title": "Cursorless: Get the tutorial content based on Talon HUD" - }, - { - "command": "cursorless.tutorial.setupStep", - "title": "Cursorless: Setup the current step for the tutorial based on Talon HUD" } ], "colors": [ From a5ed0ff4638c3670ba4b95f04cd8145cdf9f2a79 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:17:26 +0000 Subject: [PATCH 25/59] More cleanup --- .../src/api/CursorlessEngineApi.ts | 2 +- .../cursorless-engine/src/api/Tutorial.ts | 50 ++++++++++++++ .../src/core/{Tutorial.ts => TutorialImpl.ts} | 65 ++++--------------- .../cursorless-engine/src/cursorlessEngine.ts | 4 +- packages/cursorless-engine/src/index.ts | 2 +- 5 files changed, 65 insertions(+), 58 deletions(-) create mode 100644 packages/cursorless-engine/src/api/Tutorial.ts rename packages/cursorless-engine/src/core/{Tutorial.ts => TutorialImpl.ts} (87%) diff --git a/packages/cursorless-engine/src/api/CursorlessEngineApi.ts b/packages/cursorless-engine/src/api/CursorlessEngineApi.ts index 4f9cc08af9..994ba64c0f 100644 --- a/packages/cursorless-engine/src/api/CursorlessEngineApi.ts +++ b/packages/cursorless-engine/src/api/CursorlessEngineApi.ts @@ -4,7 +4,7 @@ import { StoredTargetMap } from "../core/StoredTargets"; import { ScopeProvider } from "@cursorless/common"; import { CommandRunner } from "../CommandRunner"; import { ReadOnlyHatMap } from "@cursorless/common"; -import { Tutorial } from "../core/Tutorial"; +import { Tutorial } from "./Tutorial"; export interface CursorlessEngine { commandApi: CommandApi; diff --git a/packages/cursorless-engine/src/api/Tutorial.ts b/packages/cursorless-engine/src/api/Tutorial.ts new file mode 100644 index 0000000000..83848f6eb1 --- /dev/null +++ b/packages/cursorless-engine/src/api/Tutorial.ts @@ -0,0 +1,50 @@ +export interface TutorialGetContentArg { + /** + * The version of the tutorial command. + */ + version: 0; + + /** + * The name of the current tutorial + */ + tutorialName: string; +} + +export interface TutorialGetContentResponse { + /** + * The version of the tutorial command. + */ + version: 0; + + /** + * The text content of the different steps of the current tutorial + */ + content: Array; + + /** + * The yaml files of the different steps of the current tutorial (if any) + */ + yamlFilenames: Array; +} + +export interface TutorialSetupStepArg { + /** + * The version of the tutorial command. + */ + version: 0; + + /** + * The name of the current tutorial + */ + tutorialName: string; + + /** + * The yaml file for the current step + */ + yamlFilename: string; +} + +export interface Tutorial { + getContent(arg: TutorialGetContentArg): Promise; + setupStep(arg: TutorialSetupStepArg): Promise; +} diff --git a/packages/cursorless-engine/src/core/Tutorial.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts similarity index 87% rename from packages/cursorless-engine/src/core/Tutorial.ts rename to packages/cursorless-engine/src/core/TutorialImpl.ts index ea3e294c47..8231240bd0 100644 --- a/packages/cursorless-engine/src/core/Tutorial.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -6,62 +6,22 @@ import { ScopeType, SpokenFormSuccess, TestCaseFixture, - serializedMarksToTokenHats, plainObjectToSelection, + serializedMarksToTokenHats, } from "@cursorless/common"; +import { + Tutorial, + TutorialGetContentArg, + TutorialGetContentResponse, + TutorialSetupStepArg, +} from "../api/Tutorial"; import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; import { actions } from "../generateSpokenForm/defaultSpokenForms/actions"; import { ide } from "../singletons/ide.singleton"; import { HatTokenMapImpl } from "./HatTokenMapImpl"; import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; -interface TutorialGetContentArg { - /** - * The version of the tutorial command. - */ - version: 0; - - /** - * The name of the current tutorial - */ - tutorialName: string; -} - -interface TutorialGetContentResponse { - /** - * The version of the tutorial command. - */ - version: 0; - - /** - * The text content of the different steps of the current tutorial - */ - content: Array; - - /** - * The yaml files of the different steps of the current tutorial (if any) - */ - yamlFilenames: Array; -} - -interface TutorialSetupStepArg { - /** - * The version of the tutorial command. - */ - version: 0; - - /** - * The name of the current tutorial - */ - tutorialName: string; - - /** - * The yaml file for the current step - */ - yamlFilename: string; -} - -export class Tutorial { +export class TutorialImpl implements Tutorial { private hatTokenMap: HatTokenMapImpl; private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; private tutorialRootDir: string; @@ -83,7 +43,7 @@ export class Tutorial { /** * Handle the argument of a "%%step:cloneStateInk.yml%%"" */ - async processStep(arg: string, tutorialName: string) { + private async processStep(arg: string, tutorialName: string) { const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); @@ -111,7 +71,7 @@ export class Tutorial { /** * Handle the argument of a "%%scopeType:{type: statement}%%" */ - async processScopeType(arg: any) { + private async processScopeType(arg: any) { const scopeType = yaml.load(arg.toString()) as ScopeType; const spokenForm_ = this.customSpokenFormGenerator.scopeTypeToSpokenForm(scopeType); @@ -123,7 +83,7 @@ export class Tutorial { /** * Load the "script.json" script for the current tutorial */ - async loadTutorialScript(tutorialName: string) { + private async loadTutorialScript(tutorialName: string) { const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); @@ -260,8 +220,5 @@ export class Tutorial { await this.hatTokenMap.allocateHats( serializedMarksToTokenHats(fixture.initialState.marks, editor), ); - - // return to the talon side - return true; } } diff --git a/packages/cursorless-engine/src/cursorlessEngine.ts b/packages/cursorless-engine/src/cursorlessEngine.ts index 92c3a4cabc..3bb6b67818 100644 --- a/packages/cursorless-engine/src/cursorlessEngine.ts +++ b/packages/cursorless-engine/src/cursorlessEngine.ts @@ -30,7 +30,7 @@ import { ScopeSupportChecker } from "./scopeProviders/ScopeSupportChecker"; import { ScopeSupportWatcher } from "./scopeProviders/ScopeSupportWatcher"; import { TalonSpokenFormsJsonReader } from "./nodeCommon/TalonSpokenFormsJsonReader"; import { injectIde } from "./singletons/ide.singleton"; -import { Tutorial } from "./core/Tutorial"; +import { TutorialImpl } from "./core/TutorialImpl"; export function createCursorlessEngine( treeSitter: TreeSitter, @@ -66,7 +66,7 @@ export function createCursorlessEngine( talonSpokenForms, ); - const tutorial = new Tutorial(hatTokenMap, customSpokenFormGenerator); + const tutorial = new TutorialImpl(hatTokenMap, customSpokenFormGenerator); ide.disposeOnExit(rangeUpdater, languageDefinitions, hatTokenMap, debug); diff --git a/packages/cursorless-engine/src/index.ts b/packages/cursorless-engine/src/index.ts index b76df35a78..3402dc2ff7 100644 --- a/packages/cursorless-engine/src/index.ts +++ b/packages/cursorless-engine/src/index.ts @@ -1,6 +1,6 @@ export * from "./testUtil/plainObjectToTarget"; export * from "./core/Cheatsheet"; -export * from "./core/Tutorial"; +export * from "./api/Tutorial"; export * from "./testUtil/takeSnapshot"; export * from "./testCaseRecorder/TestCaseRecorder"; export * from "./core/StoredTargets"; From f789d2408720b8000bf6310e8e2d0026b5407ac4 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:21:55 +0000 Subject: [PATCH 26/59] More cleanup --- packages/cursorless-engine/src/core/TutorialImpl.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 8231240bd0..05f4309314 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -22,22 +22,16 @@ import { HatTokenMapImpl } from "./HatTokenMapImpl"; import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonicalizeAndValidateCommand"; export class TutorialImpl implements Tutorial { - private hatTokenMap: HatTokenMapImpl; - private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl; private tutorialRootDir: string; constructor( - hatTokenMap: HatTokenMapImpl, - customSpokenFormGenerator: CustomSpokenFormGeneratorImpl, + private hatTokenMap: HatTokenMapImpl, + private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl, ) { this.getContent = this.getContent.bind(this); this.setupStep = this.setupStep.bind(this); - this.hatTokenMap = hatTokenMap; - this.customSpokenFormGenerator = customSpokenFormGenerator; - - const extensionPath = ide().assetsRoot; - this.tutorialRootDir = path.join(extensionPath, "tutorial"); + this.tutorialRootDir = path.join(ide().assetsRoot, "tutorial"); } /** From f607f160a0b66b694a95d267d04cb952caa2273d Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:27:56 +0000 Subject: [PATCH 27/59] Tweak imports --- .../cursorless-engine/src/core/TutorialImpl.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 05f4309314..ea58e42a18 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -1,7 +1,3 @@ -import * as yaml from "js-yaml"; -import fs, { promises as fsp } from "node:fs"; -import path from "path"; - import { ScopeType, SpokenFormSuccess, @@ -9,6 +5,10 @@ import { plainObjectToSelection, serializedMarksToTokenHats, } from "@cursorless/common"; +import * as yaml from "js-yaml"; +import fs from "node:fs"; +import { readFile } from "node:fs/promises"; +import path from "path"; import { Tutorial, TutorialGetContentArg, @@ -51,7 +51,7 @@ export class TutorialImpl implements Tutorial { } const yamlFilename = arg; - const buffer = await fsp.readFile(yamlFile); + const buffer = await readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; // command to be said for moving to the next step @@ -89,7 +89,7 @@ export class TutorialImpl implements Tutorial { `Can't file script file: ${scriptFile} in tutorial name: ${tutorialName}`, ); } - const buffer = await fsp.readFile(scriptFile); + const buffer = await readFile(scriptFile); const contentList = JSON.parse(buffer.toString()); console.log(contentList); return contentList; @@ -187,7 +187,7 @@ export class TutorialImpl implements Tutorial { `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, ); } - const buffer = await fsp.readFile(yamlFile); + const buffer = await readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; const editor = await ide().openUntitledTextDocument({ From 01fc50589d6bf7403b1908387cd1279a22749757 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:30:29 +0000 Subject: [PATCH 28/59] More tweaks# Please enter the commit message for your changes. Lines starting --- packages/cursorless-engine/src/core/TutorialImpl.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index ea58e42a18..937c7fe2c7 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -37,21 +37,20 @@ export class TutorialImpl implements Tutorial { /** * Handle the argument of a "%%step:cloneStateInk.yml%%"" */ - private async processStep(arg: string, tutorialName: string) { + private async processStep(yamlFilename: string, tutorialName: string) { const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); } - const yamlFile = path.join(tutorialDir, arg); - if (!fs.existsSync(yamlFile)) { + const yamlPath = path.join(tutorialDir, yamlFilename); + if (!fs.existsSync(yamlPath)) { throw new Error( - `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, + `Can't file yaml file: ${yamlPath} in tutorial name: ${tutorialName}`, ); } - const yamlFilename = arg; - const buffer = await readFile(yamlFile); + const buffer = await readFile(yamlPath); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; // command to be said for moving to the next step From af491e93b352f21ae779debf3e20b2c6cb8e8590 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:02:36 +0000 Subject: [PATCH 29/59] More cleanup --- cursorless-talon/src/tutorial.py | 42 ++++++++++++++----- .../cursorless-engine/src/api/Tutorial.ts | 18 +++++--- .../src/core/TutorialImpl.ts | 39 +++++++++-------- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index 0a62c274be..f1ae2b79b8 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -1,18 +1,38 @@ +from dataclasses import dataclass +from typing import NotRequired, TypedDict + from talon import actions, app -tutorial_content = None + +# TypedDict of tutorial step +class Step(TypedDict): + content: str + fixturePath: NotRequired[str] + + +@dataclass +class Tutorial: + tutorial_name: str + steps: list[Step] -def step_callback(x): +current_tutorial: Tutorial | None = None + + +def step_callback(x: int): print(f"step_callback5: {x}") - yamlFilename = tutorial_content["yamlFilenames"][x] - if yamlFilename: + if current_tutorial is None: + raise Exception("current_tutorial is None") + + fixture_path = current_tutorial.steps[x].get("fixturePath", None) + + if fixture_path: actions.user.private_cursorless_run_rpc_command_and_wait( "cursorless.tutorial.setupStep", { "version": 0, - "tutorialName": "unit-2-basic-coding", - "yamlFilename": yamlFilename, + "tutorialName": current_tutorial.tutorial_name, + "fixturePath": fixture_path, }, ) # the below seems not needed @@ -32,13 +52,14 @@ def step_callback(x): # break -def get_basic_coding_walkthrough(): - global tutorial_content +def start_cursorless_walkthrough(tutorial_name: str): + global current_tutorial print("get_basic_coding_walkthrough start") tutorial_content = actions.user.private_cursorless_run_rpc_command_get( "cursorless.tutorial.getContent", - {"version": 0, "tutorialName": "unit-2-basic-coding"}, + {"version": 0, "tutorialName": tutorial_name}, ) + current_tutorial = Tutorial(tutorial_name, tutorial_content["content"]) print(f"{tutorial_content=}") walkthrough_steps = [] for content in tutorial_content["content"]: @@ -61,7 +82,8 @@ def get_basic_coding_walkthrough(): # by adding a list of HudWalkThroughStep def on_ready(): actions.user.hud_add_lazy_walkthrough( - "Cursorless basic coding", get_basic_coding_walkthrough + "Cursorless basic coding", + lambda: start_cursorless_walkthrough("unit-2-basic-coding"), ) diff --git a/packages/cursorless-engine/src/api/Tutorial.ts b/packages/cursorless-engine/src/api/Tutorial.ts index 83848f6eb1..b0d180efc4 100644 --- a/packages/cursorless-engine/src/api/Tutorial.ts +++ b/packages/cursorless-engine/src/api/Tutorial.ts @@ -17,14 +17,22 @@ export interface TutorialGetContentResponse { version: 0; /** - * The text content of the different steps of the current tutorial + * The steps of the current tutorial */ - content: Array; + steps: Array; +} + +export interface TutorialStep { + /** + * The text content of the current step + */ + content: string; /** - * The yaml files of the different steps of the current tutorial (if any) + * The path to the yaml file that should be used to setup the current step (if + * any). The path is relative to the tutorial directory for the given tutorial. */ - yamlFilenames: Array; + fixturePath?: string; } export interface TutorialSetupStepArg { @@ -41,7 +49,7 @@ export interface TutorialSetupStepArg { /** * The yaml file for the current step */ - yamlFilename: string; + fixturePath: string; } export interface Tutorial { diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 937c7fe2c7..93adf2847a 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -76,7 +76,7 @@ export class TutorialImpl implements Tutorial { /** * Load the "script.json" script for the current tutorial */ - private async loadTutorialScript(tutorialName: string) { + private async loadTutorialScript(tutorialName: string): Promise { const tutorialDir = path.join(this.tutorialRootDir, tutorialName); if (!fs.existsSync(tutorialDir)) { throw new Error(`Invalid tutorial name: ${tutorialName}`); @@ -111,49 +111,48 @@ export class TutorialImpl implements Tutorial { // this is trying to catch occurrences of things like "%%step:cloneStateInk.yml%%" const re = /%%(\w+):([^%]+)%%/; - let m; let spokenForm; const response: TutorialGetContentResponse = { version: 0, - content: [], - yamlFilenames: [], + steps: [], }; // we need to replace the {...} with the right content for (let content of contentList) { - let yamlFilename = ""; - m = re.exec(content); + let fixturePath: string | undefined = undefined; + let m = re.exec(content); while (m) { - const name = m[1]; - const arg = m[2]; - console.log(name, arg); - switch (name) { + const [fullMatch, type, arg] = m; + console.log(type, arg); + switch (type) { case "step": - [spokenForm, yamlFilename] = await this.processStep( + [spokenForm, fixturePath] = await this.processStep( arg, tutorialName, ); - content = content.replace(m[0], ``); + content = content.replace(fullMatch, ``); break; case "literalStep": - content = content.replace(m[0], ``); + content = content.replace(fullMatch, ``); break; case "action": - // hardcoded list of default spoken form for an action (not yet the user customized one) + // TODO: don't use hardcoded list of default spoken form for an action (not yet the user customized one) spokenForm = actions[arg as keyof typeof actions]; console.log("\t", spokenForm); - content = content.replace(m[0], `<*"${spokenForm}"/>`); + content = content.replace(fullMatch, `<*"${spokenForm}"/>`); break; case "scopeType": spokenForm = await this.processScopeType(arg); - content = content.replace(m[0], `<*"${spokenForm}"/>`); + content = content.replace(fullMatch, `<*"${spokenForm}"/>`); break; default: - throw new Error(`Unknown name: ${name}`); + throw new Error(`Unknown name: ${type}`); } m = re.exec(content); } - response.yamlFilenames.push(yamlFilename); - response.content.push(content); + response.steps.push({ + content, + fixturePath, + }); } // return to the talon side @@ -167,7 +166,7 @@ export class TutorialImpl implements Tutorial { async setupStep({ version, tutorialName, - yamlFilename, + fixturePath: yamlFilename, }: TutorialSetupStepArg) { console.log("setupStep()", tutorialName, yamlFilename); if (version !== 0) { From 06025d4b2730659c0acf2887139d34913c663b8a Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:09:12 +0000 Subject: [PATCH 30/59] more tweaks --- .../src/core/TutorialImpl.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 93adf2847a..31302c70c6 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -1,5 +1,4 @@ import { - ScopeType, SpokenFormSuccess, TestCaseFixture, plainObjectToSelection, @@ -58,14 +57,14 @@ export class TutorialImpl implements Tutorial { canonicalizeAndValidateCommand(fixture.command), ) as SpokenFormSuccess; console.log("\t", spokenForm.spokenForms[0]); - return [spokenForm.spokenForms[0], yamlFilename]; + return spokenForm.spokenForms[0]; } /** * Handle the argument of a "%%scopeType:{type: statement}%%" */ - private async processScopeType(arg: any) { - const scopeType = yaml.load(arg.toString()) as ScopeType; + private async processScopeType(arg: string) { + const scopeType = JSON.parse(arg); const spokenForm_ = this.customSpokenFormGenerator.scopeTypeToSpokenForm(scopeType); const spokenForm = spokenForm_ as SpokenFormSuccess; @@ -125,10 +124,8 @@ export class TutorialImpl implements Tutorial { console.log(type, arg); switch (type) { case "step": - [spokenForm, fixturePath] = await this.processStep( - arg, - tutorialName, - ); + fixturePath = arg; + spokenForm = await this.processStep(arg, tutorialName); content = content.replace(fullMatch, ``); break; case "literalStep": @@ -166,9 +163,9 @@ export class TutorialImpl implements Tutorial { async setupStep({ version, tutorialName, - fixturePath: yamlFilename, + fixturePath, }: TutorialSetupStepArg) { - console.log("setupStep()", tutorialName, yamlFilename); + console.log("setupStep()", tutorialName, fixturePath); if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); } @@ -179,7 +176,7 @@ export class TutorialImpl implements Tutorial { } // TODO check for directory traversal? - const yamlFile = path.join(tutorialDir, yamlFilename); + const yamlFile = path.join(tutorialDir, fixturePath); if (!fs.existsSync(yamlFile)) { throw new Error( `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, From 55b596d1bdcaebb260086c7e66038f7c9a9eb7ac Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:11:55 +0000 Subject: [PATCH 31/59] Remove comment --- packages/cursorless-engine/src/core/TutorialImpl.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 31302c70c6..9013dad5f7 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -195,15 +195,6 @@ export class TutorialImpl implements Tutorial { editableEditor.selections = fixture.initialState.selections.map( plainObjectToSelection, ); - // in case we don't want to use the createSelection helper function - // editableEditor.selections = fixture.initialState.selections.map( - // (selections) => { - // return new Selection( - // new Position(selections.anchor.line, selections.anchor.character), - // new Position(selections.active.line, selections.active.character), - // ); - // }, - // ); // Ensure that the expected hats are present await this.hatTokenMap.allocateHats( From 6e101de262d3705b285121b980e6d28e66775b51 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:17:48 +0000 Subject: [PATCH 32/59] Bugfixes --- cursorless-talon/src/tutorial.py | 7 ++++--- packages/cursorless-engine/src/core/TutorialImpl.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py index f1ae2b79b8..54dd0c515d 100644 --- a/cursorless-talon/src/tutorial.py +++ b/cursorless-talon/src/tutorial.py @@ -59,13 +59,14 @@ def start_cursorless_walkthrough(tutorial_name: str): "cursorless.tutorial.getContent", {"version": 0, "tutorialName": tutorial_name}, ) - current_tutorial = Tutorial(tutorial_name, tutorial_content["content"]) + steps = tutorial_content["steps"] + current_tutorial = Tutorial(tutorial_name, steps) print(f"{tutorial_content=}") walkthrough_steps = [] - for content in tutorial_content["content"]: + for step in steps: walkthrough_steps.append( actions.user.hud_create_walkthrough_step( - content=content, + content=step["content"], restore_callback=step_callback, modes=["command"], app="Visual Studio Code", # Windows diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 9013dad5f7..4ae77bbdbe 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -64,7 +64,7 @@ export class TutorialImpl implements Tutorial { * Handle the argument of a "%%scopeType:{type: statement}%%" */ private async processScopeType(arg: string) { - const scopeType = JSON.parse(arg); + const scopeType = yaml.load(arg); const spokenForm_ = this.customSpokenFormGenerator.scopeTypeToSpokenForm(scopeType); const spokenForm = spokenForm_ as SpokenFormSuccess; From 0e1cf6bd83be5bc159682c57acc9a13eced8c4a8 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:21:30 +0000 Subject: [PATCH 33/59] More fixes --- packages/cursorless-engine/src/core/TutorialImpl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 4ae77bbdbe..eb17a2405c 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -1,4 +1,5 @@ import { + ScopeType, SpokenFormSuccess, TestCaseFixture, plainObjectToSelection, @@ -64,7 +65,7 @@ export class TutorialImpl implements Tutorial { * Handle the argument of a "%%scopeType:{type: statement}%%" */ private async processScopeType(arg: string) { - const scopeType = yaml.load(arg); + const scopeType = yaml.load(arg) as ScopeType; const spokenForm_ = this.customSpokenFormGenerator.scopeTypeToSpokenForm(scopeType); const spokenForm = spokenForm_ as SpokenFormSuccess; From 46be1bd76a676e5af7a1aaf7489c99375222895e Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:24:46 +0000 Subject: [PATCH 34/59] run meta-updater --- packages/cursorless-vscode/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index 259edf0664..5b49d8c975 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -127,6 +127,10 @@ "command": "cursorless.hideScopeVisualizer", "title": "Cursorless: Hide the scope visualizer" }, + { + "command": "cursorless.analyzeCommandHistory", + "title": "Cursorless: Analyze collected command history" + }, { "command": "cursorless.tutorial.getContent", "title": "Cursorless: Get tutorial content; used by the Talon HUD during tutorial initialization", @@ -137,10 +141,6 @@ "title": "Cursorless: Setup the current tutorial step; used by the Talon HUD before each tutorial step", "enablement": "false" }, - { - "command": "cursorless.analyzeCommandHistory", - "title": "Cursorless: Analyze collected command history" - }, { "command": "cursorless.command", "title": "Cursorless: The core cursorless command", From 20bd5ed159a26a8f85107441a60de97143857e73 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:25:36 +0000 Subject: [PATCH 35/59] Initial cursorless-vscode-tutorial scaffolding --- .../cursorless-vscode-tutorial/package.json | 27 +++++++++++++++++++ .../cursorless-vscode-tutorial/tsconfig.json | 9 +++++++ pnpm-lock.yaml | 2 ++ tsconfig.json | 3 +++ 4 files changed, 41 insertions(+) create mode 100644 packages/cursorless-vscode-tutorial/package.json create mode 100644 packages/cursorless-vscode-tutorial/tsconfig.json diff --git a/packages/cursorless-vscode-tutorial/package.json b/packages/cursorless-vscode-tutorial/package.json new file mode 100644 index 0000000000..c1a8577d2c --- /dev/null +++ b/packages/cursorless-vscode-tutorial/package.json @@ -0,0 +1,27 @@ +{ + "name": "@cursorless/cursorless-vscode-tutorial", + "version": "1.0.0", + "description": "Contains the VSCode frontend for the Cursorless tutorial", + "main": "./out/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "compile:tsc": "tsc --build", + "compile:esbuild": "esbuild ./src/index.ts --sourcemap --format=esm --bundle --packages=external --outfile=./out/index.js", + "compile": "pnpm compile:tsc && pnpm compile:esbuild", + "watch:tsc": "pnpm compile:tsc --watch", + "watch:esbuild": "pnpm compile:esbuild --watch", + "watch": "pnpm run --filter @cursorless/cursorless-vscode-tutorial --parallel '/^watch:.*/'", + "clean": "rm -rf ./out tsconfig.tsbuildinfo ./dist ./build" + }, + "keywords": [], + "author": "", + "license": "MIT", + "type": "module", + "types": "./out/index.d.ts", + "exports": { + ".": { + "cursorless:bundler": "./src/index.ts", + "default": "./out/index.js" + } + } +} diff --git a/packages/cursorless-vscode-tutorial/tsconfig.json b/packages/cursorless-vscode-tutorial/tsconfig.json new file mode 100644 index 0000000000..cea565539b --- /dev/null +++ b/packages/cursorless-vscode-tutorial/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "out" + }, + "references": [], + "include": ["src/**/*.ts", "src/**/*.json", "../../typings/**/*.d.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44dbb01d84..37ca656459 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -574,6 +574,8 @@ importers: specifier: ^11.1.1 version: 11.1.2 + packages/cursorless-vscode-tutorial: {} + packages/meta-updater: dependencies: '@cursorless/common': diff --git a/tsconfig.json b/tsconfig.json index e05ff589f5..2712eb2b35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,9 @@ { "path": "./packages/cursorless-vscode-e2e" }, + { + "path": "./packages/cursorless-vscode-tutorial" + }, { "path": "./packages/meta-updater" }, From ea0481c1fae16fc02b5f1757863de47ece3ff016 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:39:02 +0000 Subject: [PATCH 36/59] cursorless-vscode-tutorial => cursorless-vscode-tutorial-webview --- .../package.json | 9 ++++++--- .../tsconfig.json | 0 pnpm-lock.yaml | 10 +++++++++- tsconfig.json | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) rename packages/{cursorless-vscode-tutorial => cursorless-vscode-tutorial-webview}/package.json (75%) rename packages/{cursorless-vscode-tutorial => cursorless-vscode-tutorial-webview}/tsconfig.json (100%) diff --git a/packages/cursorless-vscode-tutorial/package.json b/packages/cursorless-vscode-tutorial-webview/package.json similarity index 75% rename from packages/cursorless-vscode-tutorial/package.json rename to packages/cursorless-vscode-tutorial-webview/package.json index c1a8577d2c..6a90d56473 100644 --- a/packages/cursorless-vscode-tutorial/package.json +++ b/packages/cursorless-vscode-tutorial-webview/package.json @@ -1,7 +1,7 @@ { - "name": "@cursorless/cursorless-vscode-tutorial", + "name": "@cursorless/cursorless-vscode-tutorial-webview", "version": "1.0.0", - "description": "Contains the VSCode frontend for the Cursorless tutorial", + "description": "Contains the VSCode webview frontend for the Cursorless tutorial", "main": "./out/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", @@ -10,7 +10,7 @@ "compile": "pnpm compile:tsc && pnpm compile:esbuild", "watch:tsc": "pnpm compile:tsc --watch", "watch:esbuild": "pnpm compile:esbuild --watch", - "watch": "pnpm run --filter @cursorless/cursorless-vscode-tutorial --parallel '/^watch:.*/'", + "watch": "pnpm run --filter @cursorless/cursorless-vscode-tutorial-webview --parallel '/^watch:.*/'", "clean": "rm -rf ./out tsconfig.tsbuildinfo ./dist ./build" }, "keywords": [], @@ -23,5 +23,8 @@ "cursorless:bundler": "./src/index.ts", "default": "./out/index.js" } + }, + "devDependencies": { + "@types/vscode-webview": "1.57.5" } } diff --git a/packages/cursorless-vscode-tutorial/tsconfig.json b/packages/cursorless-vscode-tutorial-webview/tsconfig.json similarity index 100% rename from packages/cursorless-vscode-tutorial/tsconfig.json rename to packages/cursorless-vscode-tutorial-webview/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37ca656459..d2c127db17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -574,7 +574,11 @@ importers: specifier: ^11.1.1 version: 11.1.2 - packages/cursorless-vscode-tutorial: {} + packages/cursorless-vscode-tutorial-webview: + devDependencies: + '@types/vscode-webview': + specifier: 1.57.5 + version: 1.57.5 packages/meta-updater: dependencies: @@ -5210,6 +5214,10 @@ packages: '@types/expect': 1.20.4 '@types/node': 18.18.9 + /@types/vscode-webview@1.57.5: + resolution: {integrity: sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==} + dev: true + /@types/vscode@1.66.0: resolution: {integrity: sha512-ZfJck4M7nrGasfs4A4YbUoxis3Vu24cETw3DERsNYtDZmYSYtk6ljKexKFKhImO/ZmY6ZMsmegu2FPkXoUFImA==} dev: true diff --git a/tsconfig.json b/tsconfig.json index 2712eb2b35..185f941dae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ "path": "./packages/cursorless-vscode-e2e" }, { - "path": "./packages/cursorless-vscode-tutorial" + "path": "./packages/cursorless-vscode-tutorial-webview" }, { "path": "./packages/meta-updater" From 47fd057447c2afc2f26af8de45ae054d1fccb8b1 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:57:35 +0000 Subject: [PATCH 37/59] more tutorial hacking --- .../src/index.ts | 89 ++++++++++++ packages/cursorless-vscode/package.json | 7 + .../cursorless-vscode/src/VscodeTutorial.ts | 134 ++++++++++++++++++ packages/cursorless-vscode/src/extension.ts | 5 +- 4 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 packages/cursorless-vscode-tutorial-webview/src/index.ts create mode 100644 packages/cursorless-vscode/src/VscodeTutorial.ts diff --git a/packages/cursorless-vscode-tutorial-webview/src/index.ts b/packages/cursorless-vscode-tutorial-webview/src/index.ts new file mode 100644 index 0000000000..ba114a8dca --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/src/index.ts @@ -0,0 +1,89 @@ +const vscode = acquireVsCodeApi(); + +const oldState = vscode.getState() || { colors: [] }; + +/** @type {Array<{ value: string }>} */ +let colors = oldState.colors; + +updateColorList(colors); + +document.querySelector(".add-color-button").addEventListener("click", () => { + addColor(); +}); + +// Handle messages sent from the extension to the webview +window.addEventListener("message", (event) => { + const message = event.data; // The json data that the extension sent + switch (message.type) { + case "addColor": { + addColor(); + break; + } + case "clearColors": { + colors = []; + updateColorList(colors); + break; + } + } +}); + +/** + * @param {Array<{ value: string }>} colors + */ +function updateColorList(colors) { + const ul = document.querySelector(".color-list"); + ul.textContent = ""; + for (const color of colors) { + const li = document.createElement("li"); + li.className = "color-entry"; + + const colorPreview = document.createElement("div"); + colorPreview.className = "color-preview"; + colorPreview.style.backgroundColor = `#${color.value}`; + colorPreview.addEventListener("click", () => { + onColorClicked(color.value); + }); + li.appendChild(colorPreview); + + const input = document.createElement("input"); + input.className = "color-input"; + input.type = "text"; + input.value = color.value; + input.addEventListener("change", (e) => { + const value = e.target.value; + if (!value) { + // Treat empty value as delete + colors.splice(colors.indexOf(color), 1); + } else { + color.value = value; + } + updateColorList(colors); + }); + li.appendChild(input); + + ul.appendChild(li); + } + + // Update the saved state + vscode.setState({ colors: colors }); +} + +/** + * @param {string} color + */ +function onColorClicked(color) { + vscode.postMessage({ type: "colorSelected", value: color }); +} + +/** + * @returns string + */ +function getNewCalicoColor() { + const colors = ["020202", "f1eeee", "a85b20", "daab70", "efcb99"]; + return colors[Math.floor(Math.random() * colors.length)]; +} + +function addColor() { + colors.push({ value: getNewCalicoColor() }); + updateColorList(colors); +} diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index 5b49d8c975..70928b7837 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -88,6 +88,13 @@ "id": "cursorless.scopes", "name": "Scopes" } + ], + "explorer": [ + { + "type": "webview", + "id": "cursorless.tutorial", + "name": "Calico Colors" + } ] }, "commands": [ diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts new file mode 100644 index 0000000000..50a7c98ab5 --- /dev/null +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -0,0 +1,134 @@ +import { Tutorial } from "@cursorless/cursorless-engine"; +import { VscodeApi } from "@cursorless/vscode-common"; +import { + CancellationToken, + ExtensionContext, + SnippetString, + Uri, + Webview, + WebviewView, + WebviewViewProvider, + WebviewViewResolveContext, + window, +} from "vscode"; + +const VSCODE_TUTORIAL_WEBVIEW_ID = "cursorless.tutorial"; + +export class VscodeTutorial implements WebviewViewProvider { + constructor( + private context: ExtensionContext, + vscodeApi: VscodeApi, + tutorial: Tutorial, + ) { + context.subscriptions.push( + vscodeApi.window.registerWebviewViewProvider( + VSCODE_TUTORIAL_WEBVIEW_ID, + this, + ), + ); + } + + private _view?: WebviewView; + + public resolveWebviewView( + webviewView: WebviewView, + _context: WebviewViewResolveContext, + _token: CancellationToken, + ) { + this._view = webviewView; + + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + + localResourceRoots: [this.context.extensionUri], + }; + + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + + webviewView.webview.onDidReceiveMessage((data) => { + switch (data.type) { + case "colorSelected": { + window.activeTextEditor?.insertSnippet( + new SnippetString(`#${data.value}`), + ); + break; + } + } + }); + } + + public addColor() { + if (this._view) { + this._view.show?.(true); // `show` is not implemented in 1.49 but is for 1.50 insiders + this._view.webview.postMessage({ type: "addColor" }); + } + } + + public clearColors() { + if (this._view) { + this._view.webview.postMessage({ type: "clearColors" }); + } + } + + private _getHtmlForWebview(webview: Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri( + Uri.joinPath(this.context.extensionUri, "tutorialWebview.js"), + ); + + // Do the same for the stylesheet. + const styleResetUri = webview.asWebviewUri( + Uri.joinPath(this.context.extensionUri, "media", "reset.css"), + ); + const styleVSCodeUri = webview.asWebviewUri( + Uri.joinPath(this.context.extensionUri, "media", "css"), + ); + const styleMainUri = webview.asWebviewUri( + Uri.joinPath(this.context.extensionUri, "media", "main.css"), + ); + + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + + return ` + + + + + + + + + + + + + + Cat Colors + + +
    +
+ + + + + + `; + } +} + +function getNonce() { + let text = ""; + const possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/packages/cursorless-vscode/src/extension.ts b/packages/cursorless-vscode/src/extension.ts index 87a6f272be..41011eb822 100644 --- a/packages/cursorless-vscode/src/extension.ts +++ b/packages/cursorless-vscode/src/extension.ts @@ -48,8 +48,9 @@ import { VisualizationType, } from "./ScopeVisualizerCommandApi"; import { StatusBarItem } from "./StatusBarItem"; -import { vscodeApi } from "./vscodeApi"; import { storedTargetHighlighter } from "./storedTargetHighlighter"; +import { vscodeApi } from "./vscodeApi"; +import { VscodeTutorial } from "./VscodeTutorial"; /** * Extension entrypoint called by VSCode on Cursorless startup. @@ -130,6 +131,8 @@ export async function activate( context.subscriptions.push(storedTargetHighlighter(vscodeIDE, storedTargets)); + new VscodeTutorial(context, vscodeApi, tutorial); + registerCommands( context, vscodeIDE, From 69bd66bc9fdbfc3ec49058d9df3b7cdb1b744992 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:25:43 +0000 Subject: [PATCH 38/59] Tweak package.json --- .../cursorless-vscode-tutorial-webview/package.json | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-vscode-tutorial-webview/package.json b/packages/cursorless-vscode-tutorial-webview/package.json index 6a90d56473..846f406cff 100644 --- a/packages/cursorless-vscode-tutorial-webview/package.json +++ b/packages/cursorless-vscode-tutorial-webview/package.json @@ -2,11 +2,12 @@ "name": "@cursorless/cursorless-vscode-tutorial-webview", "version": "1.0.0", "description": "Contains the VSCode webview frontend for the Cursorless tutorial", + "private": true, "main": "./out/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "compile:tsc": "tsc --build", - "compile:esbuild": "esbuild ./src/index.ts --sourcemap --format=esm --bundle --packages=external --outfile=./out/index.js", + "compile:esbuild": "esbuild ./src/index.ts --sourcemap --format=cjs --bundle --outfile=./out/index.js", "compile": "pnpm compile:tsc && pnpm compile:esbuild", "watch:tsc": "pnpm compile:tsc --watch", "watch:esbuild": "pnpm compile:esbuild --watch", @@ -17,13 +18,6 @@ "author": "", "license": "MIT", "type": "module", - "types": "./out/index.d.ts", - "exports": { - ".": { - "cursorless:bundler": "./src/index.ts", - "default": "./out/index.js" - } - }, "devDependencies": { "@types/vscode-webview": "1.57.5" } From e9d8dbe2ff565ca9bdfa2f4d5991e0265e1dd9b9 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:59:59 +0000 Subject: [PATCH 39/59] Update adding-a-new-package.md (#2247) ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- docs/contributing/adding-a-new-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/adding-a-new-package.md b/docs/contributing/adding-a-new-package.md index e6aa4a3136..b5caee0047 100644 --- a/docs/contributing/adding-a-new-package.md +++ b/docs/contributing/adding-a-new-package.md @@ -7,7 +7,7 @@ Replace `foo` with your new package name in the instructions below. 3. `pnpm init` 4. `code package.json` and update the `description` field 5. `pnpm install` -6. `pnpm -w meta-updater` +6. `pnpm -w fix:meta` For any packages that you need to depend on, you can run From 5cbc8e1cb65bf2bc514ee0ae3b5528faf346f3ca Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 24 Feb 2024 11:44:55 +0000 Subject: [PATCH 40/59] bump pnpm => 8.15.3 (#2248) ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4575605286..bb74081913 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,6 @@ "engines": { "node": ">=18.18.2" }, - "packageManager": "pnpm@8.12.0", + "packageManager": "pnpm@8.15.3", "type": "module" } From 6dc5a2da6b8114134e166df3f3e0a4ffeeb1db50 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 24 Feb 2024 20:15:23 +0000 Subject: [PATCH 41/59] Update adding-a-new-package.md (#2249) ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- docs/contributing/adding-a-new-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/adding-a-new-package.md b/docs/contributing/adding-a-new-package.md index b5caee0047..5767f2d7ec 100644 --- a/docs/contributing/adding-a-new-package.md +++ b/docs/contributing/adding-a-new-package.md @@ -15,4 +15,4 @@ For any packages that you need to depend on, you can run pnpm add some-package ``` -from the `packages/foo` directory. Note that `some-package` could be a local package, eg `@cursorless/common`. In that case, you need to re-run `pnpm -w meta-updater` after adding the dependency, so that the Typescript references can be updated. +from the `packages/foo` directory. Note that `some-package` could be a local package, eg `@cursorless/common`. In that case, you need to re-run `pnpm -w fix:meta` after adding the dependency, so that the Typescript references can be updated. From 5cd2c124f9a0853fe2e31dbd9de9de43139dec9f Mon Sep 17 00:00:00 2001 From: Jacob Egner Date: Sun, 25 Feb 2024 13:12:22 -0600 Subject: [PATCH 42/59] {grand, every} rephrasings for clarity and consistency (#2250) Related discussion: https://talonvoice.slack.com/archives/C026KPTJE6T/p1708813018817909 ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [X] I have not broken the cheatsheet --- docs/user/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/user/README.md b/docs/user/README.md index 2198bbb24f..4589addccc 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -207,22 +207,22 @@ And here is a table of the spoken forms: ##### `"every"` -The command `"every"` can be used to select a syntactic element and all of its matching siblings. +The modifier `"every"` can be used to select a syntactic element and all of its matching siblings. - `"take every key air"` - `"take every funk air"` - `"take every key"` (if cursor is currently within a key) -For example, the command `take every key [blue] air` will select every key in the map/object/dict including the token with a blue hat over the letter 'a'. +For example, the command `"take every key [blue] air"` will select every key in the map/object/dict including the token with a blue hat over the letter 'a'. ##### `"grand"` -The command `"grand"` can be used to select the grand parent of the containing syntactic element. +The modifier `"grand"` can be used to select the parent of the containing syntactic element. - `"take grand statement air"` - `"take grand funk air"` -For example, the command `take grand statement [blue] air` will select that parent statement of the statement containing the token with a blue hat over the letter 'a'. +For example, the command `"take grand statement [blue] air"` will select that parent statement of the statement containing the token with a blue hat over the letter 'a'. ##### Sub-token modifiers From ed7ae67b4ba318b2ae8a511ef8b24bf24f2e42de Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:22:39 +0000 Subject: [PATCH 43/59] Update adding-a-new-package.md (#2255) ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- docs/contributing/adding-a-new-package.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/adding-a-new-package.md b/docs/contributing/adding-a-new-package.md index 5767f2d7ec..ebdb2f136d 100644 --- a/docs/contributing/adding-a-new-package.md +++ b/docs/contributing/adding-a-new-package.md @@ -8,6 +8,7 @@ Replace `foo` with your new package name in the instructions below. 4. `code package.json` and update the `description` field 5. `pnpm install` 6. `pnpm -w fix:meta` +7. If your package is not a library (ie it's an entrypoint), add a `"private": true,` line to your `package.json`, then run another `pnpm -w fix:meta` for good measure. For any packages that you need to depend on, you can run From 401cb100cc749ed447740f3721aebcf496eb849d Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:51:49 +0000 Subject: [PATCH 44/59] Get actual js from our webview package --- .vscode/tasks.json | 11 +++++++++++ packages/cursorless-vscode/package.json | 10 ++++------ .../src/scripts/populateDist/assets.ts | 7 +++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 69cd1ca783..cfdca466f9 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -32,6 +32,16 @@ }, "group": "build" }, + { + "label": "Build tutorial webview", + "type": "npm", + "script": "compile:esbuild", + "path": "packages/cursorless-vscode-tutorial-webview", + "presentation": { + "reveal": "silent" + }, + "group": "build" + }, { "label": "Build test harness", "type": "npm", @@ -57,6 +67,7 @@ "type": "npm", "script": "populate-dist", "path": "packages/cursorless-vscode", + "dependsOn": ["Build tutorial webview"], "presentation": { "reveal": "silent" }, diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index 70928b7837..9d602c0028 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -87,13 +87,11 @@ { "id": "cursorless.scopes", "name": "Scopes" - } - ], - "explorer": [ + }, { "type": "webview", "id": "cursorless.tutorial", - "name": "Calico Colors" + "name": "Tutorial" } ] }, @@ -1200,8 +1198,8 @@ }, "funding": "https://github.com/sponsors/pokey", "scripts": { - "build": "pnpm run esbuild:prod && pnpm -F cheatsheet-local build:prod && pnpm run populate-dist", - "build:dev": "pnpm generate-grammar && pnpm run esbuild && pnpm -F cheatsheet-local build && pnpm run populate-dist", + "build": "pnpm run esbuild:prod && pnpm -F cheatsheet-local build:prod && pnpm -F cursorless-vscode-tutorial-webview build:prod && pnpm run populate-dist", + "build:dev": "pnpm generate-grammar && pnpm run esbuild && pnpm -F cheatsheet-local build && pnpm -F cursorless-vscode-tutorial-webview build && pnpm run populate-dist", "esbuild:base": "esbuild ./src/extension.ts --conditions=cursorless:bundler --bundle --outfile=dist/extension.cjs --external:vscode --format=cjs --platform=node", "install-local": "bash ./scripts/install-local.sh", "install-from-pr": "bash ./scripts/install-from-pr.sh", diff --git a/packages/cursorless-vscode/src/scripts/populateDist/assets.ts b/packages/cursorless-vscode/src/scripts/populateDist/assets.ts index 77b4c2c248..1b80d3bcd0 100644 --- a/packages/cursorless-vscode/src/scripts/populateDist/assets.ts +++ b/packages/cursorless-vscode/src/scripts/populateDist/assets.ts @@ -25,10 +25,13 @@ export const assets: Asset[] = [ }, { source: "../../images/hats", destination: "images/hats" }, { - source: - "../../packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial", + source: "../cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial", destination: "tutorial", }, + { + source: "../cursorless-vscode-tutorial-webview/out/index.js", + destination: "tutorialWebview.js", + }, { source: "./images/logo.png", destination: "images/logo.png" }, { source: "../../images/logo.svg", destination: "images/logo.svg" }, { source: "../../schemas", destination: "schemas" }, From d5dddffff64f7956fdc878ab68de8291ddc053ab Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:08:30 +0000 Subject: [PATCH 45/59] Initial React scaffolding --- packages/common/src/index.ts | 1 + packages/common/src/types/tutorial.types.ts | 8 ++ .../package.json | 9 +- .../src/App.tsx | 41 +++++++++ .../src/index.ts | 89 ------------------- .../src/index.tsx | 13 +++ .../src/types.ts | 12 +++ .../tsconfig.json | 6 +- .../cursorless-vscode/src/VscodeTutorial.ts | 22 ++--- pnpm-lock.yaml | 16 ++++ 10 files changed, 113 insertions(+), 104 deletions(-) create mode 100644 packages/common/src/types/tutorial.types.ts create mode 100644 packages/cursorless-vscode-tutorial-webview/src/App.tsx delete mode 100644 packages/cursorless-vscode-tutorial-webview/src/index.ts create mode 100644 packages/cursorless-vscode-tutorial-webview/src/index.tsx create mode 100644 packages/cursorless-vscode-tutorial-webview/src/types.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index e7ccb457a9..2309ef073e 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -48,6 +48,7 @@ export * from "./types/HatTokenMap"; export * from "./types/ScopeProvider"; export * from "./types/SpokenForm"; export * from "./types/commandHistory"; +export * from "./types/tutorial.types"; export * from "./util/textFormatters"; export * from "./util/serializedMarksToTokenHats"; export * from "./types/snippet.types"; diff --git a/packages/common/src/types/tutorial.types.ts b/packages/common/src/types/tutorial.types.ts new file mode 100644 index 0000000000..fbd7b2a4b8 --- /dev/null +++ b/packages/common/src/types/tutorial.types.ts @@ -0,0 +1,8 @@ +export type TutorialId = "introduction"; + +interface StartTutorialMessage { + type: "startTutorial"; + tutorialId: TutorialId; +} + +export type TutorialMessage = StartTutorialMessage; diff --git a/packages/cursorless-vscode-tutorial-webview/package.json b/packages/cursorless-vscode-tutorial-webview/package.json index 846f406cff..83ae1361e7 100644 --- a/packages/cursorless-vscode-tutorial-webview/package.json +++ b/packages/cursorless-vscode-tutorial-webview/package.json @@ -7,7 +7,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "compile:tsc": "tsc --build", - "compile:esbuild": "esbuild ./src/index.ts --sourcemap --format=cjs --bundle --outfile=./out/index.js", + "compile:esbuild": "esbuild ./src/index.tsx --sourcemap --format=cjs --bundle --outfile=./out/index.js", "compile": "pnpm compile:tsc && pnpm compile:esbuild", "watch:tsc": "pnpm compile:tsc --watch", "watch:esbuild": "pnpm compile:esbuild --watch", @@ -19,6 +19,13 @@ "license": "MIT", "type": "module", "devDependencies": { + "@types/react": "18.0.28", + "@types/react-dom": "18.0.11", "@types/vscode-webview": "1.57.5" + }, + "dependencies": { + "@cursorless/common": "workspace:*", + "react": "^18.2.0", + "react-dom": "^18.2.0" } } diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx new file mode 100644 index 0000000000..4c671816cc --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react"; +import type { State } from "./types"; +import { TutorialMessage } from "@cursorless/common"; + +interface Props { + initialState: State; + vscode: WebviewApi; +} + +export const App: React.FunctionComponent = ({ + initialState, + vscode, +}) => { + const [state, setState] = useState(initialState); + + useEffect(() => { + vscode.setState(state); + }, [state]); + + useEffect(() => { + // Handle messages sent from the extension to the webview + window.addEventListener("message", (event) => { + const message: TutorialMessage = event.data; // The json data that the extension sent + switch (message.type) { + case "startTutorial": + setState({ + type: "doingTutorial", + tutorialId: message.tutorialId, + stepNumber: 0, + }); + break; + } + }); + }, []); + + return state.type === "pickingTutorial" ? ( + Say "cursorless tutorial" + ) : ( + {state.tutorialId} + ); +}; diff --git a/packages/cursorless-vscode-tutorial-webview/src/index.ts b/packages/cursorless-vscode-tutorial-webview/src/index.ts deleted file mode 100644 index ba114a8dca..0000000000 --- a/packages/cursorless-vscode-tutorial-webview/src/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -const vscode = acquireVsCodeApi(); - -const oldState = vscode.getState() || { colors: [] }; - -/** @type {Array<{ value: string }>} */ -let colors = oldState.colors; - -updateColorList(colors); - -document.querySelector(".add-color-button").addEventListener("click", () => { - addColor(); -}); - -// Handle messages sent from the extension to the webview -window.addEventListener("message", (event) => { - const message = event.data; // The json data that the extension sent - switch (message.type) { - case "addColor": { - addColor(); - break; - } - case "clearColors": { - colors = []; - updateColorList(colors); - break; - } - } -}); - -/** - * @param {Array<{ value: string }>} colors - */ -function updateColorList(colors) { - const ul = document.querySelector(".color-list"); - ul.textContent = ""; - for (const color of colors) { - const li = document.createElement("li"); - li.className = "color-entry"; - - const colorPreview = document.createElement("div"); - colorPreview.className = "color-preview"; - colorPreview.style.backgroundColor = `#${color.value}`; - colorPreview.addEventListener("click", () => { - onColorClicked(color.value); - }); - li.appendChild(colorPreview); - - const input = document.createElement("input"); - input.className = "color-input"; - input.type = "text"; - input.value = color.value; - input.addEventListener("change", (e) => { - const value = e.target.value; - if (!value) { - // Treat empty value as delete - colors.splice(colors.indexOf(color), 1); - } else { - color.value = value; - } - updateColorList(colors); - }); - li.appendChild(input); - - ul.appendChild(li); - } - - // Update the saved state - vscode.setState({ colors: colors }); -} - -/** - * @param {string} color - */ -function onColorClicked(color) { - vscode.postMessage({ type: "colorSelected", value: color }); -} - -/** - * @returns string - */ -function getNewCalicoColor() { - const colors = ["020202", "f1eeee", "a85b20", "daab70", "efcb99"]; - return colors[Math.floor(Math.random() * colors.length)]; -} - -function addColor() { - colors.push({ value: getNewCalicoColor() }); - updateColorList(colors); -} diff --git a/packages/cursorless-vscode-tutorial-webview/src/index.tsx b/packages/cursorless-vscode-tutorial-webview/src/index.tsx new file mode 100644 index 0000000000..0b7b140429 --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/src/index.tsx @@ -0,0 +1,13 @@ +import { createRoot } from "react-dom/client"; +import React from "react"; +import { App } from "./App"; +import type { State } from "./types"; + +const vscode = acquireVsCodeApi(); + +createRoot(document.getElementById("root")!).render( + , +); diff --git a/packages/cursorless-vscode-tutorial-webview/src/types.ts b/packages/cursorless-vscode-tutorial-webview/src/types.ts new file mode 100644 index 0000000000..b4e4f8a8a8 --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/src/types.ts @@ -0,0 +1,12 @@ +import { TutorialId } from "@cursorless/common"; + +interface PickingTutorialState { + type: "pickingTutorial"; +} +interface ActiveTutorialState { + type: "doingTutorial"; + tutorialId: TutorialId; + stepNumber: number; +} + +export type State = PickingTutorialState | ActiveTutorialState; diff --git a/packages/cursorless-vscode-tutorial-webview/tsconfig.json b/packages/cursorless-vscode-tutorial-webview/tsconfig.json index cea565539b..4fc64f396b 100644 --- a/packages/cursorless-vscode-tutorial-webview/tsconfig.json +++ b/packages/cursorless-vscode-tutorial-webview/tsconfig.json @@ -4,6 +4,10 @@ "rootDir": "src", "outDir": "out" }, - "references": [], + "references": [ + { + "path": "../common" + } + ], "include": ["src/**/*.ts", "src/**/*.json", "../../typings/**/*.d.ts"] } diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index 50a7c98ab5..89e8569412 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -1,3 +1,4 @@ +import { TutorialId, TutorialMessage } from "@cursorless/common"; import { Tutorial } from "@cursorless/cursorless-engine"; import { VscodeApi } from "@cursorless/vscode-common"; import { @@ -58,16 +59,14 @@ export class VscodeTutorial implements WebviewViewProvider { }); } - public addColor() { + public startTutorial(tutorialId: TutorialId) { if (this._view) { - this._view.show?.(true); // `show` is not implemented in 1.49 but is for 1.50 insiders - this._view.webview.postMessage({ type: "addColor" }); - } - } - - public clearColors() { - if (this._view) { - this._view.webview.postMessage({ type: "clearColors" }); + this._view.show(true); + const message: TutorialMessage = { + type: "startTutorial", + tutorialId, + }; + this._view.webview.postMessage(message); } } @@ -112,10 +111,7 @@ export class VscodeTutorial implements WebviewViewProvider { Cat Colors -
    -
- - +
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2c127db17..f9d5832cc4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -575,7 +575,23 @@ importers: version: 11.1.2 packages/cursorless-vscode-tutorial-webview: + dependencies: + '@cursorless/common': + specifier: workspace:* + version: link:../common + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) devDependencies: + '@types/react': + specifier: 18.0.28 + version: 18.0.28 + '@types/react-dom': + specifier: 18.0.11 + version: 18.0.11 '@types/vscode-webview': specifier: 1.57.5 version: 1.57.5 From ff3f0cd53e830204ded5ba29cb4ce3b73cb8fc53 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:28:33 +0000 Subject: [PATCH 46/59] Fix tsconfig --- .../cursorless-vscode-tutorial-webview/src/App.tsx | 7 ++++--- .../cursorless-vscode-tutorial-webview/tsconfig.json | 11 +++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index 4c671816cc..672d4e1ff6 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -1,10 +1,11 @@ +import { TutorialMessage } from "@cursorless/common"; import React, { useEffect, useState } from "react"; +import { WebviewApi } from "vscode-webview"; import type { State } from "./types"; -import { TutorialMessage } from "@cursorless/common"; interface Props { initialState: State; - vscode: WebviewApi; + vscode: WebviewApi; } export const App: React.FunctionComponent = ({ @@ -19,7 +20,7 @@ export const App: React.FunctionComponent = ({ useEffect(() => { // Handle messages sent from the extension to the webview - window.addEventListener("message", (event) => { + window.addEventListener("message", (event: { data: TutorialMessage }) => { const message: TutorialMessage = event.data; // The json data that the extension sent switch (message.type) { case "startTutorial": diff --git a/packages/cursorless-vscode-tutorial-webview/tsconfig.json b/packages/cursorless-vscode-tutorial-webview/tsconfig.json index 4fc64f396b..985fe03564 100644 --- a/packages/cursorless-vscode-tutorial-webview/tsconfig.json +++ b/packages/cursorless-vscode-tutorial-webview/tsconfig.json @@ -2,12 +2,19 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", - "outDir": "out" + "outDir": "out", + "jsx": "react-jsx", + "lib": ["es2022", "dom"] }, "references": [ { "path": "../common" } ], - "include": ["src/**/*.ts", "src/**/*.json", "../../typings/**/*.d.ts"] + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.json", + "../../typings/**/*.d.ts" + ] } From 419d8307429c04fb61f0bc3026367b9dd8088c20 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:31:19 +0000 Subject: [PATCH 47/59] more cleanup --- packages/cursorless-vscode-tutorial-webview/src/App.tsx | 9 +++------ .../cursorless-vscode-tutorial-webview/src/index.tsx | 1 - packages/cursorless-vscode-tutorial-webview/src/types.ts | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index 672d4e1ff6..d0e931e816 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -1,5 +1,5 @@ -import { TutorialMessage } from "@cursorless/common"; -import React, { useEffect, useState } from "react"; +import type { TutorialMessage } from "@cursorless/common"; +import { useEffect, useState, type FunctionComponent } from "react"; import { WebviewApi } from "vscode-webview"; import type { State } from "./types"; @@ -8,10 +8,7 @@ interface Props { vscode: WebviewApi; } -export const App: React.FunctionComponent = ({ - initialState, - vscode, -}) => { +export const App: FunctionComponent = ({ initialState, vscode }) => { const [state, setState] = useState(initialState); useEffect(() => { diff --git a/packages/cursorless-vscode-tutorial-webview/src/index.tsx b/packages/cursorless-vscode-tutorial-webview/src/index.tsx index 0b7b140429..8a6412472d 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/index.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/index.tsx @@ -1,5 +1,4 @@ import { createRoot } from "react-dom/client"; -import React from "react"; import { App } from "./App"; import type { State } from "./types"; diff --git a/packages/cursorless-vscode-tutorial-webview/src/types.ts b/packages/cursorless-vscode-tutorial-webview/src/types.ts index b4e4f8a8a8..05e00f60f3 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/types.ts +++ b/packages/cursorless-vscode-tutorial-webview/src/types.ts @@ -1,4 +1,4 @@ -import { TutorialId } from "@cursorless/common"; +import type { TutorialId } from "@cursorless/common"; interface PickingTutorialState { type: "pickingTutorial"; From 571a979a083e00ebbbcfa498e759864e983bae66 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:38:01 +0000 Subject: [PATCH 48/59] fix:meta --- packages/cursorless-vscode-tutorial-webview/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-vscode-tutorial-webview/tsconfig.json b/packages/cursorless-vscode-tutorial-webview/tsconfig.json index 985fe03564..121770ed68 100644 --- a/packages/cursorless-vscode-tutorial-webview/tsconfig.json +++ b/packages/cursorless-vscode-tutorial-webview/tsconfig.json @@ -13,8 +13,8 @@ ], "include": [ "src/**/*.ts", - "src/**/*.tsx", "src/**/*.json", + "src/**/*.tsx", "../../typings/**/*.d.ts" ] } From 2f1d78608d5558bc577b4925b5c4044a60cdd6f2 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:41:09 +0000 Subject: [PATCH 49/59] more cleanup --- .../src/App.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index d0e931e816..abec857072 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -17,18 +17,20 @@ export const App: FunctionComponent = ({ initialState, vscode }) => { useEffect(() => { // Handle messages sent from the extension to the webview - window.addEventListener("message", (event: { data: TutorialMessage }) => { - const message: TutorialMessage = event.data; // The json data that the extension sent - switch (message.type) { - case "startTutorial": - setState({ - type: "doingTutorial", - tutorialId: message.tutorialId, - stepNumber: 0, - }); - break; - } - }); + window.addEventListener( + "message", + ({ data: message }: { data: TutorialMessage }) => { + switch (message.type) { + case "startTutorial": + setState({ + type: "doingTutorial", + tutorialId: message.tutorialId, + stepNumber: 0, + }); + break; + } + }, + ); }, []); return state.type === "pickingTutorial" ? ( From db75743c24c23493e107d504acae8d19f29e32cc Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:34:37 +0000 Subject: [PATCH 50/59] Add tailwind --- .vscode/tasks.json | 2 +- .../package.json | 8 ++++++-- .../src/index.css | 3 +++ .../tailwind.config.js | 13 +++++++++++++ packages/cursorless-vscode/src/VscodeTutorial.ts | 14 +++----------- .../src/scripts/populateDist/assets.ts | 6 +++++- pnpm-lock.yaml | 3 +++ 7 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 packages/cursorless-vscode-tutorial-webview/src/index.css create mode 100644 packages/cursorless-vscode-tutorial-webview/tailwind.config.js diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cfdca466f9..b58d9e71b1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -35,7 +35,7 @@ { "label": "Build tutorial webview", "type": "npm", - "script": "compile:esbuild", + "script": "compile:dev", "path": "packages/cursorless-vscode-tutorial-webview", "presentation": { "reveal": "silent" diff --git a/packages/cursorless-vscode-tutorial-webview/package.json b/packages/cursorless-vscode-tutorial-webview/package.json index 83ae1361e7..1c07173f0d 100644 --- a/packages/cursorless-vscode-tutorial-webview/package.json +++ b/packages/cursorless-vscode-tutorial-webview/package.json @@ -8,9 +8,12 @@ "test": "echo \"Error: no test specified\" && exit 1", "compile:tsc": "tsc --build", "compile:esbuild": "esbuild ./src/index.tsx --sourcemap --format=cjs --bundle --outfile=./out/index.js", - "compile": "pnpm compile:tsc && pnpm compile:esbuild", + "compile:tailwind": "tailwindcss -i ./src/index.css -o ./out/index.css", + "compile": "pnpm compile:tsc && pnpm compile:esbuild --minify && pnpm compile:tailwind --minify", + "compile:dev": "pnpm compile:esbuild && pnpm compile:tailwind", "watch:tsc": "pnpm compile:tsc --watch", "watch:esbuild": "pnpm compile:esbuild --watch", + "watch:tailwind": "pnpm compile:tailwind --watch", "watch": "pnpm run --filter @cursorless/cursorless-vscode-tutorial-webview --parallel '/^watch:.*/'", "clean": "rm -rf ./out tsconfig.tsbuildinfo ./dist ./build" }, @@ -21,7 +24,8 @@ "devDependencies": { "@types/react": "18.0.28", "@types/react-dom": "18.0.11", - "@types/vscode-webview": "1.57.5" + "@types/vscode-webview": "1.57.5", + "tailwindcss": "3.3.5" }, "dependencies": { "@cursorless/common": "workspace:*", diff --git a/packages/cursorless-vscode-tutorial-webview/src/index.css b/packages/cursorless-vscode-tutorial-webview/src/index.css new file mode 100644 index 0000000000..b5c61c9567 --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/packages/cursorless-vscode-tutorial-webview/tailwind.config.js b/packages/cursorless-vscode-tutorial-webview/tailwind.config.js new file mode 100644 index 0000000000..2da9910980 --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/tailwind.config.js @@ -0,0 +1,13 @@ +import { readFileSync } from "fs"; + +const references = JSON.parse( + readFileSync("tsconfig.json", "utf-8"), +).references.map((ref) => ref.path); + +export const content = [".", ...references].map( + (dir) => `${dir}/src/**/*!(*.stories|*.spec).{ts,tsx,html}`, +); +export const theme = { + extend: {}, +}; +export const plugins = []; diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index 89e8569412..04d03bd1b2 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -73,18 +73,12 @@ export class VscodeTutorial implements WebviewViewProvider { private _getHtmlForWebview(webview: Webview) { // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. const scriptUri = webview.asWebviewUri( - Uri.joinPath(this.context.extensionUri, "tutorialWebview.js"), + Uri.joinPath(this.context.extensionUri, "media", "tutorialWebview.js"), ); // Do the same for the stylesheet. - const styleResetUri = webview.asWebviewUri( - Uri.joinPath(this.context.extensionUri, "media", "reset.css"), - ); - const styleVSCodeUri = webview.asWebviewUri( - Uri.joinPath(this.context.extensionUri, "media", "css"), - ); const styleMainUri = webview.asWebviewUri( - Uri.joinPath(this.context.extensionUri, "media", "main.css"), + Uri.joinPath(this.context.extensionUri, "media", "tutorialWebview.css"), ); // Use a nonce to only allow a specific script to be run. @@ -104,9 +98,7 @@ export class VscodeTutorial implements WebviewViewProvider { - - - + Cat Colors diff --git a/packages/cursorless-vscode/src/scripts/populateDist/assets.ts b/packages/cursorless-vscode/src/scripts/populateDist/assets.ts index 1ce9231dc4..80c812da9b 100644 --- a/packages/cursorless-vscode/src/scripts/populateDist/assets.ts +++ b/packages/cursorless-vscode/src/scripts/populateDist/assets.ts @@ -30,7 +30,11 @@ export const assets: Asset[] = [ }, { source: "../cursorless-vscode-tutorial-webview/out/index.js", - destination: "tutorialWebview.js", + destination: "media/tutorialWebview.js", + }, + { + source: "../cursorless-vscode-tutorial-webview/out/index.css", + destination: "media/tutorialWebview.css", }, { source: "./images/logo.png", destination: "images/logo.png" }, { source: "../../images/logo.svg", destination: "images/logo.svg" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c62e57427c..2cf2a8690c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -595,6 +595,9 @@ importers: '@types/vscode-webview': specifier: 1.57.5 version: 1.57.5 + tailwindcss: + specifier: 3.3.5 + version: 3.3.5(ts-node@10.9.1) packages/meta-updater: dependencies: From 4d63d67bddc14540232629582603904d9ba2caa4 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:15:07 +0000 Subject: [PATCH 51/59] Let VscodeTutorial own tutorial state --- packages/common/src/types/tutorial.types.ts | 11 +++++-- .../src/App.tsx | 32 +++++++------------ .../src/index.tsx | 8 +---- .../src/types.ts | 12 ------- .../cursorless-vscode/src/VscodeTutorial.ts | 18 +++++------ 5 files changed, 29 insertions(+), 52 deletions(-) delete mode 100644 packages/cursorless-vscode-tutorial-webview/src/types.ts diff --git a/packages/common/src/types/tutorial.types.ts b/packages/common/src/types/tutorial.types.ts index fbd7b2a4b8..ea1d02a718 100644 --- a/packages/common/src/types/tutorial.types.ts +++ b/packages/common/src/types/tutorial.types.ts @@ -1,8 +1,13 @@ export type TutorialId = "introduction"; -interface StartTutorialMessage { - type: "startTutorial"; +interface PickingTutorialState { + type: "pickingTutorial"; +} + +interface ActiveTutorialState { + type: "doingTutorial"; tutorialId: TutorialId; + stepNumber: number; } -export type TutorialMessage = StartTutorialMessage; +export type TutorialState = PickingTutorialState | ActiveTutorialState; diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index abec857072..118455d2a9 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -1,38 +1,30 @@ -import type { TutorialMessage } from "@cursorless/common"; +import { TutorialState } from "@cursorless/common"; import { useEffect, useState, type FunctionComponent } from "react"; import { WebviewApi } from "vscode-webview"; -import type { State } from "./types"; interface Props { - initialState: State; - vscode: WebviewApi; + vscode: WebviewApi; } -export const App: FunctionComponent = ({ initialState, vscode }) => { - const [state, setState] = useState(initialState); - - useEffect(() => { - vscode.setState(state); - }, [state]); +export const App: FunctionComponent = ({ vscode }) => { + const [state, setState] = useState(); useEffect(() => { // Handle messages sent from the extension to the webview window.addEventListener( "message", - ({ data: message }: { data: TutorialMessage }) => { - switch (message.type) { - case "startTutorial": - setState({ - type: "doingTutorial", - tutorialId: message.tutorialId, - stepNumber: 0, - }); - break; - } + ({ data: newState }: { data: TutorialState }) => { + setState(newState); }, ); + + vscode.postMessage({ type: "getInitialState" }); }, []); + if (state == null) { + return <>; + } + return state.type === "pickingTutorial" ? ( Say "cursorless tutorial" ) : ( diff --git a/packages/cursorless-vscode-tutorial-webview/src/index.tsx b/packages/cursorless-vscode-tutorial-webview/src/index.tsx index 8a6412472d..c52cfae5bc 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/index.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/index.tsx @@ -1,12 +1,6 @@ import { createRoot } from "react-dom/client"; import { App } from "./App"; -import type { State } from "./types"; - -const vscode = acquireVsCodeApi(); createRoot(document.getElementById("root")!).render( - , + , ); diff --git a/packages/cursorless-vscode-tutorial-webview/src/types.ts b/packages/cursorless-vscode-tutorial-webview/src/types.ts deleted file mode 100644 index 05e00f60f3..0000000000 --- a/packages/cursorless-vscode-tutorial-webview/src/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { TutorialId } from "@cursorless/common"; - -interface PickingTutorialState { - type: "pickingTutorial"; -} -interface ActiveTutorialState { - type: "doingTutorial"; - tutorialId: TutorialId; - stepNumber: number; -} - -export type State = PickingTutorialState | ActiveTutorialState; diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index 04d03bd1b2..06f61bcec8 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -1,16 +1,14 @@ -import { TutorialId, TutorialMessage } from "@cursorless/common"; +import { TutorialId, TutorialState } from "@cursorless/common"; import { Tutorial } from "@cursorless/cursorless-engine"; import { VscodeApi } from "@cursorless/vscode-common"; import { CancellationToken, ExtensionContext, - SnippetString, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, - window, } from "vscode"; const VSCODE_TUTORIAL_WEBVIEW_ID = "cursorless.tutorial"; @@ -49,10 +47,9 @@ export class VscodeTutorial implements WebviewViewProvider { webviewView.webview.onDidReceiveMessage((data) => { switch (data.type) { - case "colorSelected": { - window.activeTextEditor?.insertSnippet( - new SnippetString(`#${data.value}`), - ); + case "getInitialState": { + const state: TutorialState = { type: "pickingTutorial" }; + this._view!.webview.postMessage(state); break; } } @@ -62,11 +59,12 @@ export class VscodeTutorial implements WebviewViewProvider { public startTutorial(tutorialId: TutorialId) { if (this._view) { this._view.show(true); - const message: TutorialMessage = { - type: "startTutorial", + const state: TutorialState = { + type: "doingTutorial", tutorialId, + stepNumber: 0, }; - this._view.webview.postMessage(message); + this._view.webview.postMessage(state); } } From 04c565327d696e4002299fcb1e44902aaedba8de Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:44:08 +0000 Subject: [PATCH 52/59] Clean up some TutorialImpl stuff --- .../src/core/TutorialImpl.ts | 77 ++++++++----------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index eb17a2405c..5dd9f53db7 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -1,12 +1,10 @@ import { ScopeType, - SpokenFormSuccess, TestCaseFixture, plainObjectToSelection, serializedMarksToTokenHats, } from "@cursorless/common"; import * as yaml from "js-yaml"; -import fs from "node:fs"; import { readFile } from "node:fs/promises"; import path from "path"; import { @@ -37,18 +35,12 @@ export class TutorialImpl implements Tutorial { /** * Handle the argument of a "%%step:cloneStateInk.yml%%"" */ - private async processStep(yamlFilename: string, tutorialName: string) { - const tutorialDir = path.join(this.tutorialRootDir, tutorialName); - if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); - } - - const yamlPath = path.join(tutorialDir, yamlFilename); - if (!fs.existsSync(yamlPath)) { - throw new Error( - `Can't file yaml file: ${yamlPath} in tutorial name: ${tutorialName}`, - ); - } + private async processStep(tutorialName: string, yamlFilename: string) { + const yamlPath = path.join( + this.tutorialRootDir, + tutorialName, + yamlFilename, + ); const buffer = await readFile(yamlPath); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; @@ -56,8 +48,14 @@ export class TutorialImpl implements Tutorial { // command to be said for moving to the next step const spokenForm = this.customSpokenFormGenerator.commandToSpokenForm( canonicalizeAndValidateCommand(fixture.command), - ) as SpokenFormSuccess; - console.log("\t", spokenForm.spokenForms[0]); + ); + + if (spokenForm.type === "error") { + throw new Error( + `Error while processing spoken form for command ${fixture.command}: ${spokenForm.reason}`, + ); + } + return spokenForm.spokenForms[0]; } @@ -66,10 +64,15 @@ export class TutorialImpl implements Tutorial { */ private async processScopeType(arg: string) { const scopeType = yaml.load(arg) as ScopeType; - const spokenForm_ = + const spokenForm = this.customSpokenFormGenerator.scopeTypeToSpokenForm(scopeType); - const spokenForm = spokenForm_ as SpokenFormSuccess; - console.log("\t", spokenForm.spokenForms[0]); + + if (spokenForm.type === "error") { + throw new Error( + `Error while processing spoken form for command ${arg}: ${spokenForm.reason}`, + ); + } + return spokenForm.spokenForms[0]; } @@ -77,20 +80,14 @@ export class TutorialImpl implements Tutorial { * Load the "script.json" script for the current tutorial */ private async loadTutorialScript(tutorialName: string): Promise { - const tutorialDir = path.join(this.tutorialRootDir, tutorialName); - if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); - } + const scriptFile = path.join( + this.tutorialRootDir, + tutorialName, + "script.json", + ); - const scriptFile = path.join(tutorialDir, "script.json"); - if (!fs.existsSync(scriptFile)) { - throw new Error( - `Can't file script file: ${scriptFile} in tutorial name: ${tutorialName}`, - ); - } const buffer = await readFile(scriptFile); const contentList = JSON.parse(buffer.toString()); - console.log(contentList); return contentList; } @@ -98,10 +95,6 @@ export class TutorialImpl implements Tutorial { * Handle the "cursorless.tutorial.getContent" command */ async getContent({ version, tutorialName }: TutorialGetContentArg) { - console.log( - "getContent(){ version, tutorialName, yamlFilename }: TutorialSetupStepArg", - tutorialName, - ); if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); } @@ -126,7 +119,7 @@ export class TutorialImpl implements Tutorial { switch (type) { case "step": fixturePath = arg; - spokenForm = await this.processStep(arg, tutorialName); + spokenForm = await this.processStep(tutorialName, arg); content = content.replace(fullMatch, ``); break; case "literalStep": @@ -166,23 +159,13 @@ export class TutorialImpl implements Tutorial { tutorialName, fixturePath, }: TutorialSetupStepArg) { - console.log("setupStep()", tutorialName, fixturePath); if (version !== 0) { throw new Error(`Unsupported tutorial api version: ${version}`); } - const tutorialDir = path.join(this.tutorialRootDir, tutorialName); - if (!fs.existsSync(tutorialDir)) { - throw new Error(`Invalid tutorial name: ${tutorialName}`); - } - // TODO check for directory traversal? - const yamlFile = path.join(tutorialDir, fixturePath); - if (!fs.existsSync(yamlFile)) { - throw new Error( - `Can't file yaml file: ${yamlFile} in tutorial name: ${tutorialName}`, - ); - } + const yamlFile = path.join(this.tutorialRootDir, tutorialName, fixturePath); + const buffer = await readFile(yamlFile); const fixture = yaml.load(buffer.toString()) as TestCaseFixture; From b59cccb696f835c47ed39fca30c26485cbb5ec03 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:53:00 +0000 Subject: [PATCH 53/59] More PR feedback --- cursorless-talon/src/tutorial.py | 91 ------------------- .../src/core/TutorialImpl.ts | 30 +++--- 2 files changed, 16 insertions(+), 105 deletions(-) delete mode 100644 cursorless-talon/src/tutorial.py diff --git a/cursorless-talon/src/tutorial.py b/cursorless-talon/src/tutorial.py deleted file mode 100644 index 54dd0c515d..0000000000 --- a/cursorless-talon/src/tutorial.py +++ /dev/null @@ -1,91 +0,0 @@ -from dataclasses import dataclass -from typing import NotRequired, TypedDict - -from talon import actions, app - - -# TypedDict of tutorial step -class Step(TypedDict): - content: str - fixturePath: NotRequired[str] - - -@dataclass -class Tutorial: - tutorial_name: str - steps: list[Step] - - -current_tutorial: Tutorial | None = None - - -def step_callback(x: int): - print(f"step_callback5: {x}") - if current_tutorial is None: - raise Exception("current_tutorial is None") - - fixture_path = current_tutorial.steps[x].get("fixturePath", None) - - if fixture_path: - actions.user.private_cursorless_run_rpc_command_and_wait( - "cursorless.tutorial.setupStep", - { - "version": 0, - "tutorialName": current_tutorial.tutorial_name, - "fixturePath": fixture_path, - }, - ) - # the below seems not needed - # while True: - # # this is a hack to make sure vscode window was correctly reloaded - # # for instance if the user focused another window like the browser - # # when saying "help cursorless" - # ret = actions.user.private_cursorless_run_rpc_command_get( - # "cursorless.tutorial.setupStep", - # { - # "version": 1, - # "tutorialName": "unit-1-basic-coding", - # "yamlFilename": yamlFilename, - # }, - # ) - # if ret: - # break - - -def start_cursorless_walkthrough(tutorial_name: str): - global current_tutorial - print("get_basic_coding_walkthrough start") - tutorial_content = actions.user.private_cursorless_run_rpc_command_get( - "cursorless.tutorial.getContent", - {"version": 0, "tutorialName": tutorial_name}, - ) - steps = tutorial_content["steps"] - current_tutorial = Tutorial(tutorial_name, steps) - print(f"{tutorial_content=}") - walkthrough_steps = [] - for step in steps: - walkthrough_steps.append( - actions.user.hud_create_walkthrough_step( - content=step["content"], - restore_callback=step_callback, - modes=["command"], - app="Visual Studio Code", # Windows - # app="Code", # OS X? - # TODO: Fix this; should just be "vscode". Prob need fix to talon_hud itself - context_hint="Please open VSCode and enter command mode", - ) - ) - print("get_basic_coding_walkthrough end") - return walkthrough_steps - - -# this is adding the menu to the hud -# by adding a list of HudWalkThroughStep -def on_ready(): - actions.user.hud_add_lazy_walkthrough( - "Cursorless basic coding", - lambda: start_cursorless_walkthrough("unit-2-basic-coding"), - ) - - -app.register("ready", on_ready) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 5dd9f53db7..3eb7bc5930 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -36,14 +36,7 @@ export class TutorialImpl implements Tutorial { * Handle the argument of a "%%step:cloneStateInk.yml%%"" */ private async processStep(tutorialName: string, yamlFilename: string) { - const yamlPath = path.join( - this.tutorialRootDir, - tutorialName, - yamlFilename, - ); - - const buffer = await readFile(yamlPath); - const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + const fixture = await this.loadFixture(tutorialName, yamlFilename); // command to be said for moving to the next step const spokenForm = this.customSpokenFormGenerator.commandToSpokenForm( @@ -59,6 +52,20 @@ export class TutorialImpl implements Tutorial { return spokenForm.spokenForms[0]; } + private async loadFixture( + tutorialName: string, + yamlFilename: string, + ): Promise { + const yamlPath = path.join( + this.tutorialRootDir, + tutorialName, + yamlFilename, + ); + + const buffer = await readFile(yamlPath); + return yaml.load(buffer.toString()) as TestCaseFixture; + } + /** * Handle the argument of a "%%scopeType:{type: statement}%%" */ @@ -115,7 +122,6 @@ export class TutorialImpl implements Tutorial { let m = re.exec(content); while (m) { const [fullMatch, type, arg] = m; - console.log(type, arg); switch (type) { case "step": fixturePath = arg; @@ -128,7 +134,6 @@ export class TutorialImpl implements Tutorial { case "action": // TODO: don't use hardcoded list of default spoken form for an action (not yet the user customized one) spokenForm = actions[arg as keyof typeof actions]; - console.log("\t", spokenForm); content = content.replace(fullMatch, `<*"${spokenForm}"/>`); break; case "scopeType": @@ -164,10 +169,7 @@ export class TutorialImpl implements Tutorial { } // TODO check for directory traversal? - const yamlFile = path.join(this.tutorialRootDir, tutorialName, fixturePath); - - const buffer = await readFile(yamlFile); - const fixture = yaml.load(buffer.toString()) as TestCaseFixture; + const fixture = await this.loadFixture(tutorialName, fixturePath); const editor = await ide().openUntitledTextDocument({ content: fixture.initialState.documentContents, From ac51e1f9f790c57a85c2b33508585f21953935fa Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:41:12 +0000 Subject: [PATCH 54/59] Initial step content and step init code connected to webview --- cursorless-talon/src/cursorless.py | 6 ++ cursorless-talon/src/cursorless.talon | 3 + packages/common/src/cursorlessCommandIds.ts | 10 +--- packages/common/src/types/tutorial.types.ts | 1 + .../cursorless-engine/src/api/Tutorial.ts | 4 +- .../src/core/TutorialImpl.ts | 8 +-- .../src/App.tsx | 2 +- packages/cursorless-vscode/package.json | 12 +--- .../cursorless-vscode/src/VscodeTutorial.ts | 55 ++++++++++++++----- packages/cursorless-vscode/src/extension.ts | 4 +- .../cursorless-vscode/src/registerCommands.ts | 7 +-- 11 files changed, 67 insertions(+), 45 deletions(-) diff --git a/cursorless-talon/src/cursorless.py b/cursorless-talon/src/cursorless.py index 9617f51593..4f0e1b7b01 100644 --- a/cursorless-talon/src/cursorless.py +++ b/cursorless-talon/src/cursorless.py @@ -21,3 +21,9 @@ def private_cursorless_show_command_statistics(): actions.user.private_cursorless_run_rpc_command_no_wait( "cursorless.analyzeCommandHistory" ) + + def private_cursorless_start_tutorial(): + """Start Cursorless tutorial""" + actions.user.private_cursorless_run_rpc_command_no_wait( + "cursorless.tutorial.start", "unit-2-basic-coding" + ) diff --git a/cursorless-talon/src/cursorless.talon b/cursorless-talon/src/cursorless.talon index f12bab8a3e..14c6ac9839 100644 --- a/cursorless-talon/src/cursorless.talon +++ b/cursorless-talon/src/cursorless.talon @@ -46,3 +46,6 @@ bar {user.cursorless_homophone}: {user.cursorless_homophone} stats: user.private_cursorless_show_command_statistics() + +{user.cursorless_homophone} tutorial: + user.private_cursorless_start_tutorial() diff --git a/packages/common/src/cursorlessCommandIds.ts b/packages/common/src/cursorlessCommandIds.ts index 5f49501bad..1abf347e57 100644 --- a/packages/common/src/cursorlessCommandIds.ts +++ b/packages/common/src/cursorlessCommandIds.ts @@ -47,8 +47,7 @@ export const cursorlessCommandIds = [ "cursorless.toggleDecorations", "cursorless.showScopeVisualizer", "cursorless.hideScopeVisualizer", - "cursorless.tutorial.getContent", - "cursorless.tutorial.setupStep", + "cursorless.tutorial.start", "cursorless.analyzeCommandHistory", ] as const satisfies readonly `cursorless.${string}`[]; @@ -83,12 +82,7 @@ export const cursorlessCommandDescriptions: Record< "Analyze collected command history", ), - ["cursorless.tutorial.getContent"]: new HiddenCommand( - "Get tutorial content; used by the Talon HUD during tutorial initialization", - ), - ["cursorless.tutorial.setupStep"]: new HiddenCommand( - "Setup the current tutorial step; used by the Talon HUD before each tutorial step", - ), + ["cursorless.tutorial.start"]: new HiddenCommand("Start a tutorial"), ["cursorless.command"]: new HiddenCommand("The core cursorless command"), ["cursorless.showQuickPick"]: new HiddenCommand( "Pop up a quick pick of all cursorless commands", diff --git a/packages/common/src/types/tutorial.types.ts b/packages/common/src/types/tutorial.types.ts index ea1d02a718..f157b2844b 100644 --- a/packages/common/src/types/tutorial.types.ts +++ b/packages/common/src/types/tutorial.types.ts @@ -8,6 +8,7 @@ interface ActiveTutorialState { type: "doingTutorial"; tutorialId: TutorialId; stepNumber: number; + stepContent: string; } export type TutorialState = PickingTutorialState | ActiveTutorialState; diff --git a/packages/cursorless-engine/src/api/Tutorial.ts b/packages/cursorless-engine/src/api/Tutorial.ts index b0d180efc4..b4bbbde20b 100644 --- a/packages/cursorless-engine/src/api/Tutorial.ts +++ b/packages/cursorless-engine/src/api/Tutorial.ts @@ -1,3 +1,5 @@ +import { TutorialId } from "@cursorless/common"; + export interface TutorialGetContentArg { /** * The version of the tutorial command. @@ -53,6 +55,6 @@ export interface TutorialSetupStepArg { } export interface Tutorial { - getContent(arg: TutorialGetContentArg): Promise; + getContent(id: TutorialId): Promise; setupStep(arg: TutorialSetupStepArg): Promise; } diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 3eb7bc5930..a462866760 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -1,6 +1,7 @@ import { ScopeType, TestCaseFixture, + TutorialId, plainObjectToSelection, serializedMarksToTokenHats, } from "@cursorless/common"; @@ -9,7 +10,6 @@ import { readFile } from "node:fs/promises"; import path from "path"; import { Tutorial, - TutorialGetContentArg, TutorialGetContentResponse, TutorialSetupStepArg, } from "../api/Tutorial"; @@ -101,11 +101,7 @@ export class TutorialImpl implements Tutorial { /** * Handle the "cursorless.tutorial.getContent" command */ - async getContent({ version, tutorialName }: TutorialGetContentArg) { - if (version !== 0) { - throw new Error(`Unsupported tutorial api version: ${version}`); - } - + async getContent(tutorialName: TutorialId) { const contentList = await this.loadTutorialScript(tutorialName); // this is trying to catch occurrences of things like "%%step:cloneStateInk.yml%%" diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index 118455d2a9..4b8044719b 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -28,6 +28,6 @@ export const App: FunctionComponent = ({ vscode }) => { return state.type === "pickingTutorial" ? ( Say "cursorless tutorial" ) : ( - {state.tutorialId} + {state.stepContent} ); }; diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index 9d602c0028..ea6c7182b7 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -71,8 +71,7 @@ "onCommand:cursorless.toggleDecorations", "onCommand:cursorless.showScopeVisualizer", "onCommand:cursorless.hideScopeVisualizer", - "onCommand:cursorless.tutorial.getContent", - "onCommand:cursorless.tutorial.setupStep", + "onCommand:cursorless.tutorial.start", "onCommand:cursorless.analyzeCommandHistory" ], "main": "./extension.cjs", @@ -137,13 +136,8 @@ "title": "Cursorless: Analyze collected command history" }, { - "command": "cursorless.tutorial.getContent", - "title": "Cursorless: Get tutorial content; used by the Talon HUD during tutorial initialization", - "enablement": "false" - }, - { - "command": "cursorless.tutorial.setupStep", - "title": "Cursorless: Setup the current tutorial step; used by the Talon HUD before each tutorial step", + "command": "cursorless.tutorial.start", + "title": "Cursorless: Start a tutorial", "enablement": "false" }, { diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index 06f61bcec8..59f6ea6901 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -1,5 +1,8 @@ import { TutorialId, TutorialState } from "@cursorless/common"; -import { Tutorial } from "@cursorless/cursorless-engine"; +import { + Tutorial, + TutorialGetContentResponse, +} from "@cursorless/cursorless-engine"; import { VscodeApi } from "@cursorless/vscode-common"; import { CancellationToken, @@ -9,15 +12,19 @@ import { WebviewView, WebviewViewProvider, WebviewViewResolveContext, + commands, } from "vscode"; const VSCODE_TUTORIAL_WEBVIEW_ID = "cursorless.tutorial"; export class VscodeTutorial implements WebviewViewProvider { + private state: TutorialState = { type: "pickingTutorial" }; + private currentTutorial?: TutorialGetContentResponse; + constructor( private context: ExtensionContext, vscodeApi: VscodeApi, - tutorial: Tutorial, + private tutorial: Tutorial, ) { context.subscriptions.push( vscodeApi.window.registerWebviewViewProvider( @@ -25,6 +32,7 @@ export class VscodeTutorial implements WebviewViewProvider { this, ), ); + this.start = this.start.bind(this); } private _view?: WebviewView; @@ -47,25 +55,44 @@ export class VscodeTutorial implements WebviewViewProvider { webviewView.webview.onDidReceiveMessage((data) => { switch (data.type) { - case "getInitialState": { - const state: TutorialState = { type: "pickingTutorial" }; - this._view!.webview.postMessage(state); + case "getInitialState": + this._view!.webview.postMessage(this.state); break; - } } }); } - public startTutorial(tutorialId: TutorialId) { - if (this._view) { + public async start(tutorialId: TutorialId) { + this.currentTutorial = await this.tutorial.getContent(tutorialId); + + this.setState({ + type: "doingTutorial", + tutorialId, + stepNumber: 0, + stepContent: this.currentTutorial.steps[0].content, + }); + + if (this._view == null) { + await commands.executeCommand("cursorless.tutorial.focus"); + } else { this._view.show(true); - const state: TutorialState = { - type: "doingTutorial", - tutorialId, - stepNumber: 0, - }; - this._view.webview.postMessage(state); } + + await this.tutorial.setupStep({ + version: 0, + fixturePath: this.currentTutorial.steps[0].fixturePath!, + tutorialName: tutorialId, + }); + } + + private setState(state: TutorialState) { + this.state = state; + + if (!this._view) { + return; + } + + this._view.webview.postMessage(state); } private _getHtmlForWebview(webview: Webview) { diff --git a/packages/cursorless-vscode/src/extension.ts b/packages/cursorless-vscode/src/extension.ts index 41011eb822..406bab2b05 100644 --- a/packages/cursorless-vscode/src/extension.ts +++ b/packages/cursorless-vscode/src/extension.ts @@ -131,7 +131,7 @@ export async function activate( context.subscriptions.push(storedTargetHighlighter(vscodeIDE, storedTargets)); - new VscodeTutorial(context, vscodeApi, tutorial); + const vscodeTutorial = new VscodeTutorial(context, vscodeApi, tutorial); registerCommands( context, @@ -142,7 +142,7 @@ export async function activate( scopeVisualizer, keyboardCommands, hats, - tutorial, + vscodeTutorial, ); new ReleaseNotes(vscodeApi, context, normalizedIde.messages).maybeShow(); diff --git a/packages/cursorless-vscode/src/registerCommands.ts b/packages/cursorless-vscode/src/registerCommands.ts index 299b04dfb3..6ff48e54ce 100644 --- a/packages/cursorless-vscode/src/registerCommands.ts +++ b/packages/cursorless-vscode/src/registerCommands.ts @@ -10,10 +10,10 @@ import { analyzeCommandHistory, showCheatsheet, updateDefaults, - Tutorial, } from "@cursorless/cursorless-engine"; import * as vscode from "vscode"; import { ScopeVisualizer } from "./ScopeVisualizerCommandApi"; +import { VscodeTutorial } from "./VscodeTutorial"; import { showDocumentation, showQuickPick } from "./commands"; import { VscodeIDE } from "./ide/vscode/VscodeIDE"; import { VscodeHats } from "./ide/vscode/hats/VscodeHats"; @@ -29,7 +29,7 @@ export function registerCommands( scopeVisualizer: ScopeVisualizer, keyboardCommands: KeyboardCommands, hats: VscodeHats, - tutorial: Tutorial, + tutorial: VscodeTutorial, ): void { const commands: Record any> = { // The core Cursorless command @@ -102,8 +102,7 @@ export function registerCommands( ["cursorless.keyboard.modal.modeToggle"]: keyboardCommands.modal.modeToggle, // Tutorial commands - ["cursorless.tutorial.getContent"]: tutorial.getContent, - ["cursorless.tutorial.setupStep"]: tutorial.setupStep, + ["cursorless.tutorial.start"]: tutorial.start, }; extensionContext.subscriptions.push( From 26a2cd9d25db703b6f5106d56c4f659e242515ab Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 16 Mar 2024 18:44:46 +0000 Subject: [PATCH 55/59] tweaks --- .../cursorless-vscode/src/VscodeTutorial.ts | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index 59f6ea6901..f8eae7fb79 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -20,6 +20,7 @@ const VSCODE_TUTORIAL_WEBVIEW_ID = "cursorless.tutorial"; export class VscodeTutorial implements WebviewViewProvider { private state: TutorialState = { type: "pickingTutorial" }; private currentTutorial?: TutorialGetContentResponse; + private view?: WebviewView; constructor( private context: ExtensionContext, @@ -35,14 +36,12 @@ export class VscodeTutorial implements WebviewViewProvider { this.start = this.start.bind(this); } - private _view?: WebviewView; - public resolveWebviewView( webviewView: WebviewView, _context: WebviewViewResolveContext, _token: CancellationToken, ) { - this._view = webviewView; + this.view = webviewView; webviewView.webview.options = { // Allow scripts in the webview @@ -51,12 +50,12 @@ export class VscodeTutorial implements WebviewViewProvider { localResourceRoots: [this.context.extensionUri], }; - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + webviewView.webview.html = this.getHtmlForWebview(webviewView.webview); webviewView.webview.onDidReceiveMessage((data) => { switch (data.type) { case "getInitialState": - this._view!.webview.postMessage(this.state); + this.view!.webview.postMessage(this.state); break; } }); @@ -72,10 +71,10 @@ export class VscodeTutorial implements WebviewViewProvider { stepContent: this.currentTutorial.steps[0].content, }); - if (this._view == null) { - await commands.executeCommand("cursorless.tutorial.focus"); + if (this.view != null) { + this.view.show(true); } else { - this._view.show(true); + await commands.executeCommand("cursorless.tutorial.focus"); } await this.tutorial.setupStep({ @@ -88,14 +87,12 @@ export class VscodeTutorial implements WebviewViewProvider { private setState(state: TutorialState) { this.state = state; - if (!this._view) { - return; + if (this.view != null) { + this.view.webview.postMessage(state); } - - this._view.webview.postMessage(state); } - private _getHtmlForWebview(webview: Webview) { + private getHtmlForWebview(webview: Webview) { // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. const scriptUri = webview.asWebviewUri( Uri.joinPath(this.context.extensionUri, "media", "tutorialWebview.js"), From 1f0707702bdca8298527127d7d51c297822e0622 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:56:55 +0000 Subject: [PATCH 56/59] tweaks --- packages/common/src/types/tutorial.types.ts | 1 + packages/cursorless-vscode-tutorial-webview/src/App.tsx | 1 + packages/cursorless-vscode/package.json | 8 ++++---- packages/cursorless-vscode/src/VscodeTutorial.ts | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/common/src/types/tutorial.types.ts b/packages/common/src/types/tutorial.types.ts index f157b2844b..e9aff244a3 100644 --- a/packages/common/src/types/tutorial.types.ts +++ b/packages/common/src/types/tutorial.types.ts @@ -9,6 +9,7 @@ interface ActiveTutorialState { tutorialId: TutorialId; stepNumber: number; stepContent: string; + stepCount: number; } export type TutorialState = PickingTutorialState | ActiveTutorialState; diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index 4b8044719b..a4ee0871aa 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -22,6 +22,7 @@ export const App: FunctionComponent = ({ vscode }) => { }, []); if (state == null) { + // Just show nothing while we're waiting for initial state return <>; } diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index ea6c7182b7..1d021891ba 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -83,14 +83,14 @@ "contributes": { "views": { "cursorless": [ - { - "id": "cursorless.scopes", - "name": "Scopes" - }, { "type": "webview", "id": "cursorless.tutorial", "name": "Tutorial" + }, + { + "id": "cursorless.scopes", + "name": "Scopes" } ] }, diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index f8eae7fb79..7aa9a61c47 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -69,6 +69,7 @@ export class VscodeTutorial implements WebviewViewProvider { tutorialId, stepNumber: 0, stepContent: this.currentTutorial.steps[0].content, + stepCount: this.currentTutorial.steps.length, }); if (this.view != null) { From 0dbbfcd70c094cd639949334cc2427810f4cd119 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:42:39 +0000 Subject: [PATCH 57/59] some more tweaks --- packages/common/src/types/tutorial.types.ts | 3 +- .../cursorless-engine/src/api/Tutorial.ts | 29 ++++------ .../src/core/TutorialImpl.ts | 55 +++++++++++-------- .../tutorial/unit-1-basics/script.json | 31 ++++++----- .../tutorial/unit-2-basic-coding/script.json | 27 +++++---- .../src/App.tsx | 3 +- .../src/TutorialStep.tsx | 17 ++++++ .../cursorless-vscode/src/VscodeTutorial.ts | 11 ++-- 8 files changed, 100 insertions(+), 76 deletions(-) create mode 100644 packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx diff --git a/packages/common/src/types/tutorial.types.ts b/packages/common/src/types/tutorial.types.ts index e9aff244a3..00f51fb9af 100644 --- a/packages/common/src/types/tutorial.types.ts +++ b/packages/common/src/types/tutorial.types.ts @@ -4,8 +4,9 @@ interface PickingTutorialState { type: "pickingTutorial"; } -interface ActiveTutorialState { +export interface ActiveTutorialState { type: "doingTutorial"; + title: string; tutorialId: TutorialId; stepNumber: number; stepContent: string; diff --git a/packages/cursorless-engine/src/api/Tutorial.ts b/packages/cursorless-engine/src/api/Tutorial.ts index b4bbbde20b..7174d05398 100644 --- a/packages/cursorless-engine/src/api/Tutorial.ts +++ b/packages/cursorless-engine/src/api/Tutorial.ts @@ -1,27 +1,27 @@ import { TutorialId } from "@cursorless/common"; -export interface TutorialGetContentArg { +export interface TutorialContent { /** - * The version of the tutorial command. + * The title of the tutorial */ - version: 0; + title: string; /** - * The name of the current tutorial + * The steps of the current tutorial */ - tutorialName: string; + steps: Array; } -export interface TutorialGetContentResponse { +export interface RawTutorialContent { /** - * The version of the tutorial command. + * The title of the tutorial */ - version: 0; + title: string; /** * The steps of the current tutorial */ - steps: Array; + steps: string[]; } export interface TutorialStep { @@ -39,14 +39,9 @@ export interface TutorialStep { export interface TutorialSetupStepArg { /** - * The version of the tutorial command. - */ - version: 0; - - /** - * The name of the current tutorial + * The id of the current tutorial */ - tutorialName: string; + tutorialId: string; /** * The yaml file for the current step @@ -55,6 +50,6 @@ export interface TutorialSetupStepArg { } export interface Tutorial { - getContent(id: TutorialId): Promise; + getContent(id: TutorialId): Promise; setupStep(arg: TutorialSetupStepArg): Promise; } diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index a462866760..eda9bcce89 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -1,6 +1,7 @@ import { ScopeType, TestCaseFixture, + TextEditor, TutorialId, plainObjectToSelection, serializedMarksToTokenHats, @@ -9,8 +10,9 @@ import * as yaml from "js-yaml"; import { readFile } from "node:fs/promises"; import path from "path"; import { + RawTutorialContent, Tutorial, - TutorialGetContentResponse, + TutorialContent, TutorialSetupStepArg, } from "../api/Tutorial"; import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl"; @@ -21,6 +23,7 @@ import { canonicalizeAndValidateCommand } from "./commandVersionUpgrades/canonic export class TutorialImpl implements Tutorial { private tutorialRootDir: string; + private editor?: TextEditor; constructor( private hatTokenMap: HatTokenMapImpl, @@ -86,7 +89,9 @@ export class TutorialImpl implements Tutorial { /** * Load the "script.json" script for the current tutorial */ - private async loadTutorialScript(tutorialName: string): Promise { + private async loadTutorialScript( + tutorialName: string, + ): Promise { const scriptFile = path.join( this.tutorialRootDir, tutorialName, @@ -94,26 +99,25 @@ export class TutorialImpl implements Tutorial { ); const buffer = await readFile(scriptFile); - const contentList = JSON.parse(buffer.toString()); - return contentList; + return JSON.parse(buffer.toString()); } /** * Handle the "cursorless.tutorial.getContent" command */ - async getContent(tutorialName: TutorialId) { - const contentList = await this.loadTutorialScript(tutorialName); + async getContent(tutorialId: TutorialId) { + const rawContent = await this.loadTutorialScript(tutorialId); // this is trying to catch occurrences of things like "%%step:cloneStateInk.yml%%" const re = /%%(\w+):([^%]+)%%/; let spokenForm; - const response: TutorialGetContentResponse = { - version: 0, + const response: TutorialContent = { + title: rawContent.title, steps: [], }; // we need to replace the {...} with the right content - for (let content of contentList) { + for (let content of rawContent.steps) { let fixturePath: string | undefined = undefined; let m = re.exec(content); while (m) { @@ -121,7 +125,7 @@ export class TutorialImpl implements Tutorial { switch (type) { case "step": fixturePath = arg; - spokenForm = await this.processStep(tutorialName, arg); + spokenForm = await this.processStep(tutorialId, arg); content = content.replace(fullMatch, ``); break; case "literalStep": @@ -155,23 +159,24 @@ export class TutorialImpl implements Tutorial { * Handle the "cursorless.tutorial.setupStep" command * @see packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts */ - async setupStep({ - version, - tutorialName, - fixturePath, - }: TutorialSetupStepArg) { - if (version !== 0) { - throw new Error(`Unsupported tutorial api version: ${version}`); + async setupStep({ tutorialId, fixturePath }: TutorialSetupStepArg) { + const fixture = await this.loadFixture(tutorialId, fixturePath); + + if (this.editor == null) { + this.editor = await ide().openUntitledTextDocument({ + content: fixture.initialState.documentContents, + language: fixture.languageId, + }); } - // TODO check for directory traversal? - const fixture = await this.loadFixture(tutorialName, fixturePath); + const editableEditor = ide().getEditableTextEditor(this.editor); - const editor = await ide().openUntitledTextDocument({ - content: fixture.initialState.documentContents, - language: fixture.languageId, + await editableEditor.edit((editBuilder) => { + editBuilder.replace( + editableEditor.document.range, + fixture.initialState.documentContents, + ); }); - const editableEditor = ide().getEditableTextEditor(editor); // Ensure that the expected cursor/selections are present editableEditor.selections = fixture.initialState.selections.map( @@ -180,7 +185,9 @@ export class TutorialImpl implements Tutorial { // Ensure that the expected hats are present await this.hatTokenMap.allocateHats( - serializedMarksToTokenHats(fixture.initialState.marks, editor), + serializedMarksToTokenHats(fixture.initialState.marks, this.editor), ); + + await editableEditor.focus(); } } diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json index bdf3952d25..c2260aee1b 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-1-basics/script.json @@ -1,14 +1,17 @@ -[ - "Every cursorless command consists of an action performed on a target. For example, the command {step:takeWhale.yml} selects the token with a grey hat over the 'w'.", - "When a hat is not gray, we need to use a color to refer to it: {step:takeBlueSun.yml}", - "Selecting a single token is great but oftentimes we need something bigger. Say {step:takeEachPastKick.yml} to select a range.", - "Despite its name, one of the most powerful aspects of cursorless is the ability to use more than one cursor. Let's try that: {step:takeCapAndWhale.yml}", - "But let's show that cursorless can live up to its name: we can say {step:chuckDrum.yml} to delete a word without ever moving our cursor.", - "Tokens are great, but they're just one way to think of a document. Let's try working with lines: {step:chuckLineLook.yml}", - "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}", - "You now know how to select and delete; let's give you a couple more actions to play with: say {action:setSelectionBefore} to place the cursor before a target, as in {step:preInk.yml}", - "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", - "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", - "Chaining commands is a great way to code faster: {step:clearWhaleWordYou.yml+wordYou}", - "And that wraps up unit 1 of the cursorless tutorial! Next time, we'll write some code ☺️" -] +{ + "title": "Introduction", + "steps": [ + "Every cursorless command consists of an action performed on a target. For example, the command {step:takeWhale.yml} selects the token with a grey hat over the 'w'.", + "When a hat is not gray, we need to use a color to refer to it: {step:takeBlueSun.yml}", + "Selecting a single token is great but oftentimes we need something bigger. Say {step:takeEachPastKick.yml} to select a range.", + "Despite its name, one of the most powerful aspects of cursorless is the ability to use more than one cursor. Let's try that: {step:takeCapAndWhale.yml}", + "But let's show that cursorless can live up to its name: we can say {step:chuckDrum.yml} to delete a word without ever moving our cursor.", + "Tokens are great, but they're just one way to think of a document. Let's try working with lines: {step:chuckLineLook.yml}", + "We can also use {scopeType:line} to refer to the line containing our cursor: {step:takeLine.yml}", + "You now know how to select and delete; let's give you a couple more actions to play with: say {action:setSelectionBefore} to place the cursor before a target, as in {step:preInk.yml}", + "Say {action:setSelectionAfter} to place the cursor after a target: {step:postLook.yml}", + "Say {action:clearAndSetSelection} to delete a word and move your cursor to where it used to be: {step:clearTrap.yml}", + "Chaining commands is a great way to code faster: {step:clearWhaleWordYou.yml+wordYou}", + "And that wraps up unit 1 of the cursorless tutorial! Next time, we'll write some code ☺️" + ] +} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json index 5da1a2cd08..b50194bde6 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/tutorial/unit-2-basic-coding/script.json @@ -1,12 +1,15 @@ -[ - "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: %%step:cloneStateInk.yml%%", - "%%scopeType:{type: statement}%% is one of many scopes supported by cursorless. To see all available scopes, use the command %%literalStep:cursorless help%%, and look at the Scopes section.", - "Cursorless tries its best to keep your commands short. In the following command, we just say %%scopeType:{type: surroundingPair, delimiter: string}%% once, but cursorless infers that both targets are strings: %%step:swapStringAirWithWhale.yml%%", - "Great. Let's learn a new action. The %%action:editNewLineAfter%% action lets you start editing a new line below any line on your screen: %%step:pourUrge.yml%%", - "Now let's try applying a cursorless action to the current line: %%step:dedentThis.yml%%", - "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the %%action:replaceWithTarget%% command: %%step:bringStateUrge.yml%%", - "%%action:replaceWithTarget%% also works with two targets just like %%action:swapTargets%%: %%step:bringBlueCapToValueRisk.yml%%", - "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: %%step:chuckArgueBlueVest.yml%%", - "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say %%literalStep:cursorless help%% to see a list.", - "As always, feel free to stick around and play with this file to practice what you've just learned. Happy coding :)" -] +{ + "title": "Basic coding", + "steps": [ + "When editing code, we often think in terms of statements, functions, etc. Let's clone a statement: %%step:cloneStateInk.yml%%", + "%%scopeType:{type: statement}%% is one of many scopes supported by cursorless. To see all available scopes, use the command %%literalStep:cursorless help%%, and look at the Scopes section.", + "Cursorless tries its best to keep your commands short. In the following command, we just say %%scopeType:{type: surroundingPair, delimiter: string}%% once, but cursorless infers that both targets are strings: %%step:swapStringAirWithWhale.yml%%", + "Great. Let's learn a new action. The %%action:editNewLineAfter%% action lets you start editing a new line below any line on your screen: %%step:pourUrge.yml%%", + "Now let's try applying a cursorless action to the current line: %%step:dedentThis.yml%%", + "Code reuse is a fact of life as a programmer. Cursorless makes this easy with the %%action:replaceWithTarget%% command: %%step:bringStateUrge.yml%%", + "%%action:replaceWithTarget%% also works with two targets just like %%action:swapTargets%%: %%step:bringBlueCapToValueRisk.yml%%", + "Cursorless tries its best to use its knowledge of programming languages to leave you with syntactically valid code. Note how it cleans up the comma here: %%step:chuckArgueBlueVest.yml%%", + "We introduced a lot of different scopes today. If you're anything like us, you've already forgotten them all. The important thing to remember is that you can always say %%literalStep:cursorless help%% to see a list.", + "As always, feel free to stick around and play with this file to practice what you've just learned. Happy coding :)" + ] +} diff --git a/packages/cursorless-vscode-tutorial-webview/src/App.tsx b/packages/cursorless-vscode-tutorial-webview/src/App.tsx index a4ee0871aa..40de0f255d 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/App.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/App.tsx @@ -1,6 +1,7 @@ import { TutorialState } from "@cursorless/common"; import { useEffect, useState, type FunctionComponent } from "react"; import { WebviewApi } from "vscode-webview"; +import { TutorialStep } from "./TutorialStep"; interface Props { vscode: WebviewApi; @@ -29,6 +30,6 @@ export const App: FunctionComponent = ({ vscode }) => { return state.type === "pickingTutorial" ? ( Say "cursorless tutorial" ) : ( - {state.stepContent} + ); }; diff --git a/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx b/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx new file mode 100644 index 0000000000..27747dbbae --- /dev/null +++ b/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx @@ -0,0 +1,17 @@ +import { ActiveTutorialState } from "@cursorless/common"; +import { type FunctionComponent } from "react"; + +interface TutorialStepProps { + state: ActiveTutorialState; +} + +export const TutorialStep: FunctionComponent = ({ + state, +}) => { + return ( +
+

{state.title}

+ {state.stepContent} +
+ ); +}; diff --git a/packages/cursorless-vscode/src/VscodeTutorial.ts b/packages/cursorless-vscode/src/VscodeTutorial.ts index 7aa9a61c47..ca66e35bf3 100644 --- a/packages/cursorless-vscode/src/VscodeTutorial.ts +++ b/packages/cursorless-vscode/src/VscodeTutorial.ts @@ -1,8 +1,5 @@ import { TutorialId, TutorialState } from "@cursorless/common"; -import { - Tutorial, - TutorialGetContentResponse, -} from "@cursorless/cursorless-engine"; +import { Tutorial, TutorialContent } from "@cursorless/cursorless-engine"; import { VscodeApi } from "@cursorless/vscode-common"; import { CancellationToken, @@ -19,7 +16,7 @@ const VSCODE_TUTORIAL_WEBVIEW_ID = "cursorless.tutorial"; export class VscodeTutorial implements WebviewViewProvider { private state: TutorialState = { type: "pickingTutorial" }; - private currentTutorial?: TutorialGetContentResponse; + private currentTutorial?: TutorialContent; private view?: WebviewView; constructor( @@ -70,6 +67,7 @@ export class VscodeTutorial implements WebviewViewProvider { stepNumber: 0, stepContent: this.currentTutorial.steps[0].content, stepCount: this.currentTutorial.steps.length, + title: this.currentTutorial.title, }); if (this.view != null) { @@ -79,9 +77,8 @@ export class VscodeTutorial implements WebviewViewProvider { } await this.tutorial.setupStep({ - version: 0, fixturePath: this.currentTutorial.steps[0].fixturePath!, - tutorialName: tutorialId, + tutorialId: tutorialId, }); } From c9f56b3342d9322ebede8bfbf295dd8fafd3ee6c Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:51:55 +0000 Subject: [PATCH 58/59] more tweaks --- packages/cursorless-engine/src/core/TutorialImpl.ts | 12 ++++++------ .../src/TutorialStep.tsx | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index eda9bcce89..50bdd00fd5 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -171,12 +171,11 @@ export class TutorialImpl implements Tutorial { const editableEditor = ide().getEditableTextEditor(this.editor); - await editableEditor.edit((editBuilder) => { - editBuilder.replace( - editableEditor.document.range, - fixture.initialState.documentContents, - ); - }); + await editableEditor.edit([{ + range: editableEditor.document.range, + text: fixture.initialState.documentContents, + isReplace: true, + }]); // Ensure that the expected cursor/selections are present editableEditor.selections = fixture.initialState.selections.map( @@ -188,6 +187,7 @@ export class TutorialImpl implements Tutorial { serializedMarksToTokenHats(fixture.initialState.marks, this.editor), ); + // TODO: Handle case where editor is in a background tab await editableEditor.focus(); } } diff --git a/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx b/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx index 27747dbbae..731713fa52 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx @@ -10,7 +10,8 @@ export const TutorialStep: FunctionComponent = ({ }) => { return (
-

{state.title}

+

{state.title}

+ {state.stepContent}
); From ad8a152d22ed60bacf541a3fd72da2c66cbd9f03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 21:49:57 +0000 Subject: [PATCH 59/59] [pre-commit.ci lite] apply automatic fixes --- packages/cursorless-engine/src/core/TutorialImpl.ts | 6 ++++-- .../cursorless-vscode-tutorial-webview/src/TutorialStep.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-engine/src/core/TutorialImpl.ts b/packages/cursorless-engine/src/core/TutorialImpl.ts index 50bdd00fd5..ebe892a8da 100644 --- a/packages/cursorless-engine/src/core/TutorialImpl.ts +++ b/packages/cursorless-engine/src/core/TutorialImpl.ts @@ -171,11 +171,13 @@ export class TutorialImpl implements Tutorial { const editableEditor = ide().getEditableTextEditor(this.editor); - await editableEditor.edit([{ + await editableEditor.edit([ + { range: editableEditor.document.range, text: fixture.initialState.documentContents, isReplace: true, - }]); + }, + ]); // Ensure that the expected cursor/selections are present editableEditor.selections = fixture.initialState.selections.map( diff --git a/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx b/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx index 731713fa52..5aec9e2e96 100644 --- a/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx +++ b/packages/cursorless-vscode-tutorial-webview/src/TutorialStep.tsx @@ -10,8 +10,10 @@ export const TutorialStep: FunctionComponent = ({ }) => { return (
-

{state.title}

- +

+ {state.title} +

+ {state.stepContent}
);