Skip to content

Commit

Permalink
Remove node references
Browse files Browse the repository at this point in the history
  • Loading branch information
pokey committed Jul 15, 2024
1 parent c0e3512 commit 6dad7f0
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 101 deletions.
45 changes: 45 additions & 0 deletions packages/common/src/ide/types/TutorialContentProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { TestCaseFixtureLegacy } from "../../types/TestCaseFixture";
import { TutorialId } from "../../types/tutorial.types";

export interface TutorialContentProvider {
getRawTutorials(): Promise<RawTutorialContent[]>;

/**
* Load the "script.json" script for the current tutorial
*/
loadTutorialScript(tutorialId: string): Promise<RawTutorialContent>;

/**
* Loads a fixture file from the tutorial directory, eg "takeNear.yml"
*
* @param tutorialId The tutorial id
* @param fixtureName The name of the fixture, eg "takeNear.yml"
* @returns A promise that resolves to the parsed fixture content
*/
loadFixture(
tutorialId: TutorialId,
fixtureName: string,
): Promise<TestCaseFixtureLegacy>;
}

export interface RawTutorialContent {
/**
* The unique identifier for the tutorial
*/
id: TutorialId;

/**
* The title of the tutorial
*/
title: string;

/**
* The version of the tutorial
*/
version: number;

/**
* The steps of the current tutorial
*/
steps: string[];
}
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export * from "./ide/types/events.types";
export * from "./ide/types/Paths";
export * from "./ide/types/CommandHistoryStorage";
export * from "./ide/types/RawTreeSitterQueryProvider";
export * from "./ide/types/TutorialContentProvider";
export * from "./ide/types/FileSystem.types";
export * from "./types/RangeExpansionBehavior";
export * from "./types/InputBoxOptions";
Expand Down
17 changes: 0 additions & 17 deletions packages/cursorless-engine/src/api/Tutorial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,6 @@ export interface TutorialContent {
steps: Array<TutorialStep>;
}

export interface RawTutorialContent {
/**
* The title of the tutorial
*/
title: string;

/**
* The version of the tutorial
*/
version: number;

/**
* The steps of the current tutorial
*/
steps: string[];
}

/**
* Advance to the next step when the user completes a command
*/
Expand Down
25 changes: 20 additions & 5 deletions packages/cursorless-engine/src/cursorlessEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Hats,
IDE,
ScopeProvider,
TutorialContentProvider,
ensureCommandShape,
type RawTreeSitterQueryProvider,
type TalonSpokenForms,
Expand Down Expand Up @@ -41,14 +42,17 @@ import { ScopeRangeWatcher } from "./scopeProviders/ScopeRangeWatcher";
import { ScopeSupportChecker } from "./scopeProviders/ScopeSupportChecker";
import { ScopeSupportWatcher } from "./scopeProviders/ScopeSupportWatcher";
import { injectIde } from "./singletons/ide.singleton";
import { DisabledTutorial } from "./disabledComponents/DisabledTutorial";
import { Tutorial } from "./api/Tutorial";

interface Props {
export interface EngineProps {
ide: IDE;
hats?: Hats;
treeSitterQueryProvider?: RawTreeSitterQueryProvider;
treeSitter?: TreeSitter;
commandServerApi?: CommandServerApi;
talonSpokenForms?: TalonSpokenForms;
tutorialContentProvider?: TutorialContentProvider;
snippets?: Snippets;
}

Expand All @@ -59,8 +63,9 @@ export async function createCursorlessEngine({
treeSitter = new DisabledTreeSitter(),
commandServerApi = new DisabledCommandServerApi(),
talonSpokenForms = new DisabledTalonSpokenForms(),
tutorialContentProvider,
snippets = new DisabledSnippets(),
}: Props): Promise<CursorlessEngine> {
}: EngineProps): Promise<CursorlessEngine> {
injectIde(ide);

const debug = new Debug(ide);
Expand Down Expand Up @@ -91,16 +96,26 @@ export async function createCursorlessEngine({
commandRunnerDecorators.push(decorator);
};

const tutorial = new TutorialImpl(hatTokenMap, customSpokenFormGenerator);
addCommandRunnerDecorator(tutorial);
let tutorial: Tutorial;
if (tutorialContentProvider != null) {
const tutorialImpl = new TutorialImpl(
hatTokenMap,
customSpokenFormGenerator,
tutorialContentProvider,
);
ide.disposeOnExit(tutorialImpl);
addCommandRunnerDecorator(tutorialImpl);
tutorial = tutorialImpl;
} else {
tutorial = new DisabledTutorial();
}

ide.disposeOnExit(
debug,
hatTokenMap,
keyboardTargetUpdater,
languageDefinitions,
rangeUpdater,
tutorial,
);

let previousCommand: Command | undefined = undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
TutorialId,
TutorialState,
Disposable,
ScopeType,
} from "@cursorless/common";
import { Tutorial } from "../api/Tutorial";

export class DisabledTutorial implements Tutorial {
onState(_callback: (state: TutorialState) => void): Disposable {
return { dispose: () => {} };
}

docsOpened(): void {
// Do nothing
}
scopeTypeVisualized(_scopeType: ScopeType | undefined): void {
// Do nothing
}

start(_id: number | TutorialId): Promise<void> {
throw new Error("Method not implemented.");
}
next(): Promise<void> {
throw new Error("Method not implemented.");
}
previous(): Promise<void> {
throw new Error("Method not implemented.");
}
restart(): Promise<void> {
throw new Error("Method not implemented.");
}
resume(): Promise<void> {
throw new Error("Method not implemented.");
}
list(): Promise<void> {
throw new Error("Method not implemented.");
}

readonly state: TutorialState = { type: "loading" };
}
56 changes: 17 additions & 39 deletions packages/cursorless-engine/src/tutorial/TutorialImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ReadOnlyHatMap,
ScopeType,
TextEditor,
TutorialContentProvider,
TutorialId,
TutorialInfo,
TutorialState,
Expand All @@ -16,22 +17,18 @@ import {
} from "@cursorless/common";
import { produce } from "immer";
import { isEqual } from "lodash-es";
import { readdir } from "node:fs/promises";
import path from "path";
import { CommandRunner } from "../CommandRunner";
import { CommandRunnerDecorator } from "../api/CursorlessEngineApi";
import { Tutorial, TutorialContent, TutorialStep } from "../api/Tutorial";
import { Debouncer } from "../core/Debouncer";
import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl";
import { ide } from "../singletons/ide.singleton";
import { Debouncer } from "../core/Debouncer";
import { TutorialError } from "./TutorialError";
import { TutorialScriptParser } from "./TutorialScriptParser";
import { loadTutorialScript } from "./loadTutorialScript";

const HIGHLIGHT_COLOR = "highlight0";

export class TutorialImpl implements Tutorial, CommandRunnerDecorator {
private tutorialRootDir: string;
private editor?: TextEditor;
private state_: TutorialState = { type: "loading" };
private notifier: Notifier<[TutorialState]> = new Notifier();
Expand All @@ -42,13 +39,12 @@ export class TutorialImpl implements Tutorial, CommandRunnerDecorator {
constructor(
private hatTokenMap: HatTokenMap,
private customSpokenFormGenerator: CustomSpokenFormGeneratorImpl,
private contentProvider: TutorialContentProvider,
) {
this.setupStep = this.setupStep.bind(this);
this.reparseCurrentTutorial = this.reparseCurrentTutorial.bind(this);
const debouncer = new Debouncer(() => this.checkPreconditions(), 100);

this.tutorialRootDir = path.join(ide().assetsRoot, "tutorial");

this.loadTutorialList();

this.disposables.push(
Expand Down Expand Up @@ -85,30 +81,16 @@ export class TutorialImpl implements Tutorial, CommandRunnerDecorator {
}

async loadTutorialList() {
const tutorialDirs = await readdir(this.tutorialRootDir, {
withFileTypes: true,
});

const tutorialProgress = ide().globalState.get("tutorialProgress");

this.tutorials = await Promise.all(
tutorialDirs
.filter((dirent) => dirent.isDirectory())
.map(async (dirent) => {
const tutorialId = dirent.name as TutorialId;
const rawContent = await loadTutorialScript(
this.tutorialRootDir,
tutorialId,
);

return {
id: tutorialId,
title: rawContent.title,
version: rawContent.version,
stepCount: rawContent.steps.length,
currentStep: tutorialProgress[tutorialId]?.currentStep ?? 0,
};
}),
this.tutorials = (await this.contentProvider.getRawTutorials()).map(
(rawContent) => ({
id: rawContent.id,
title: rawContent.title,
version: rawContent.version,
stepCount: rawContent.steps.length,
currentStep: tutorialProgress[rawContent.id]?.currentStep ?? 0,
}),
);

this.setState({
Expand Down Expand Up @@ -163,13 +145,11 @@ export class TutorialImpl implements Tutorial, CommandRunnerDecorator {

const tutorialId = this.state_.id;

const rawContent = await loadTutorialScript(
this.tutorialRootDir,
tutorialId,
);
const rawContent =
await this.contentProvider.loadTutorialScript(tutorialId);

const parser = new TutorialScriptParser(
this.tutorialRootDir,
this.contentProvider,
tutorialId,
this.customSpokenFormGenerator,
);
Expand Down Expand Up @@ -207,13 +187,11 @@ export class TutorialImpl implements Tutorial, CommandRunnerDecorator {
tutorialId = this.tutorials[tutorialId].id;
}

const rawContent = await loadTutorialScript(
this.tutorialRootDir,
tutorialId,
);
const rawContent =
await this.contentProvider.loadTutorialScript(tutorialId);

const parser = new TutorialScriptParser(
this.tutorialRootDir,
this.contentProvider,
tutorialId,
this.customSpokenFormGenerator,
);
Expand Down
18 changes: 11 additions & 7 deletions packages/cursorless-engine/src/tutorial/TutorialScriptParser.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { TutorialId, TutorialStepFragment } from "@cursorless/common";
import {
TutorialContentProvider,
TutorialId,
TutorialStepFragment,
} from "@cursorless/common";
import { TutorialStep } from "../api/Tutorial";
import { parseScopeType } from "../customCommandGrammar/parseCommand";
import { CustomSpokenFormGeneratorImpl } from "../generateSpokenForm/CustomSpokenFormGeneratorImpl";
import { StepComponent } from "./StepComponent";
import { CursorlessCommandComponentParser } from "./stepComponentParsers/CursorlessCommandComponentParser";
import { getScopeTypeSpokenForm } from "./getScopeTypeSpokenForm";
import { specialTerms } from "./specialTerms";
import { ActionComponentParser } from "./stepComponentParsers/ActionComponentParser";
import { parseSpecialComponent } from "./stepComponentParsers/parseSpecialComponent";
import { CursorlessCommandComponentParser } from "./stepComponentParsers/CursorlessCommandComponentParser";
import { GraphemeComponentParser } from "./stepComponentParsers/GraphemeComponentParser";
import { getScopeTypeSpokenForm } from "./getScopeTypeSpokenForm";
import { parseSpecialComponent } from "./stepComponentParsers/parseSpecialComponent";
import { parseVisualizeComponent } from "./stepComponentParsers/parseVisualizeComponent";
import { specialTerms } from "./specialTerms";

/**
* This is trying to catch occurrences of things like `{command:cloneStateInk.yml}`
Expand All @@ -24,14 +28,14 @@ export class TutorialScriptParser {
>;

constructor(
tutorialRootDir: string,
contentProvider: TutorialContentProvider,
tutorialId: TutorialId,
customSpokenFormGenerator: CustomSpokenFormGeneratorImpl,
) {
this.parseTutorialStep = this.parseTutorialStep.bind(this);

const cursorlessCommandHandler = new CursorlessCommandComponentParser(
tutorialRootDir,
contentProvider,
tutorialId,
customSpokenFormGenerator,
);
Expand Down
17 changes: 0 additions & 17 deletions packages/cursorless-engine/src/tutorial/loadTutorialScript.ts

This file was deleted.

Loading

0 comments on commit 6dad7f0

Please sign in to comment.