Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions src/player/tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
TreeItem,
window
} from "vscode";
import { EXTENSION_NAME } from "../../constants";
import { generatePreviewContent } from "..";
import { EXTENSION_NAME } from "../../constants";
import { store } from "../../store";
import { CodeTourNode, CodeTourStepNode } from "./nodes";
import { CodeTourFolderNode, CodeTourNode, CodeTourStepNode } from "./nodes";

class CodeTourTreeProvider implements TreeDataProvider<TreeItem>, Disposable {
private _disposables: Disposable[] = [];
Expand Down Expand Up @@ -58,9 +58,13 @@ class CodeTourTreeProvider implements TreeDataProvider<TreeItem>, Disposable {
if (!store.hasTours && !store.activeTour) {
return undefined;
} else {
const tours = store.tours.map(
tour => new CodeTourNode(tour, this.extensionPath)
);
const tours = store.root.tours.map(tour => {
if ('tours' in tour) {
return new CodeTourFolderNode(tour, this.extensionPath);
} else {
return new CodeTourNode(tour, this.extensionPath);
}
});

if (
store.activeTour &&
Expand All @@ -73,6 +77,16 @@ class CodeTourTreeProvider implements TreeDataProvider<TreeItem>, Disposable {

return tours;
}
} else if (element instanceof CodeTourFolderNode) {
const tours = element.tour.tours.map(tour => {
if ('tours' in tour) {
return new CodeTourFolderNode(tour, this.extensionPath);
} else {
return new CodeTourNode(tour, this.extensionPath);
}
});

return tours;
} else if (element instanceof CodeTourNode) {
if (element.tour.steps.length === 0) {
let item;
Expand Down
15 changes: 14 additions & 1 deletion src/player/tree/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Uri
} from "vscode";
import { CONTENT_URI, EXTENSION_NAME, FS_SCHEME } from "../../constants";
import { CodeTour, store } from "../../store";
import { CodeTour, CodeTourFolder, store } from "../../store";
import { progress } from "../../store/storage";
import { getFileUri, getStepLabel, getWorkspaceUri } from "../../utils";

Expand Down Expand Up @@ -67,6 +67,19 @@ export class CodeTourNode extends TreeItem {
}
}

export class CodeTourFolderNode extends TreeItem {
constructor(public tour: CodeTourFolder, extensionPath: string) {
super(
tour.title,
TreeItemCollapsibleState.Collapsed
);

this.description = `${tour.tours.length} tours`;
this.contextValue = "codetour.folder";
this.iconPath = new ThemeIcon("folder");
}
}

export class CodeTourStepNode extends TreeItem {
constructor(public tour: CodeTour, public stepNumber: number) {
super(getStepLabel(tour, stepNumber));
Expand Down
12 changes: 12 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export interface CodeTour {
when?: string;
}

export interface CodeTourFolder {
title: string;
tours: CodeTourNode[];
}

export type CodeTourNode = CodeTour | CodeTourFolder;

export interface ActiveTour {
tour: CodeTour;
step: number;
Expand All @@ -72,6 +79,7 @@ type CodeTourProgress = [string, number[]];
export type CodeTourStepTuple = [CodeTour, CodeTourStep, number, number?];

export interface Store {
root: CodeTourFolder;
tours: CodeTour[];
activeTour: ActiveTour | null;
activeEditorSteps?: CodeTourStepTuple[];
Expand All @@ -83,6 +91,10 @@ export interface Store {
}

export const store: Store = observable({
root: {
title: "",
tours: []
},
tours: [],
activeTour: null,
isRecording: false,
Expand Down
105 changes: 66 additions & 39 deletions src/store/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as jexl from "jexl";
import { comparer, runInAction, set } from "mobx";
import * as os from "os";
import * as vscode from "vscode";
import { CodeTour, store } from ".";
import { CodeTour, CodeTourFolder, store } from ".";
import { EXTENSION_NAME, VSCODE_DIRECTORY } from "../constants";
import { readUriContents, updateMarkerTitles } from "../utils";
import { endCurrentCodeTour } from "./actions";
Expand Down Expand Up @@ -41,24 +41,28 @@ if (customDirectory) {
}

export async function discoverTours(): Promise<void> {
const tours = await Promise.all(
vscode.workspace.workspaceFolders!.map(async workspaceFolder => {
const mainTours = await discoverMainTours(workspaceFolder.uri);
const tours = await discoverSubTours(workspaceFolder.uri);

if (mainTours) {
tours.push(...mainTours);
}

return tours;
})
);
const root: CodeTourFolder = {
title: "",
tours: []
};

for (const workspaceFolder of vscode.workspace.workspaceFolders!) {
await discoverSubTours(workspaceFolder.uri, root);

const mainTours = await discoverMainTours(workspaceFolder.uri);

if (mainTours) {
root.tours.push(...mainTours);
}
}

runInAction(() => {
store.tours = tours
.flat()
.sort((a, b) => a.title.localeCompare(b.title))
.filter(tour => !tour.when || jexl.evalSync(tour.when, TOUR_CONTEXT));
const tours: CodeTour[] = [];

sortAndFilterFolder(root, tours);

store.root = root;
store.tours = tours;

if (store.activeTour) {
const tour = store.tours.find(
Expand All @@ -85,9 +89,21 @@ export async function discoverTours(): Promise<void> {
updateMarkerTitles();
}

async function discoverMainTours(
workspaceUri: vscode.Uri
): Promise<CodeTour[]> {
function sortAndFilterFolder(folder: CodeTourFolder, tours: CodeTour[]): void {
folder.tours
.sort((a, b) => a.title.localeCompare(b.title))
.filter(tour => 'tours' in tour || !tour.when || jexl.evalSync(tour.when, TOUR_CONTEXT));

for (const tour of folder.tours) {
if ('tours' in tour) {
sortAndFilterFolder(tour, tours)
} else {
tours.push(tour);
}
}
}

async function discoverMainTours(workspaceUri: vscode.Uri): Promise<CodeTour[]> {
const tours = await Promise.all(
MAIN_TOUR_FILES.map(async tourFile => {
try {
Expand All @@ -104,53 +120,64 @@ async function discoverMainTours(
return tours.filter(tour => tour);
}

async function readTourDirectory(uri: vscode.Uri): Promise<CodeTour[]> {
async function readTourDirectory(uri: vscode.Uri, folder: CodeTourFolder): Promise<void> {
try {
const tourFiles = await vscode.workspace.fs.readDirectory(uri);
const tours = await Promise.all(

await Promise.all(
tourFiles.map(async ([file, type]) => {
const fileUri = vscode.Uri.joinPath(uri, file);
if (type === vscode.FileType.File) {
return readTourFile(fileUri);
return readTourFile(fileUri, folder);
} else {
return readTourDirectory(fileUri);
const newFolder = findOrCreateFolder(file, folder);

return readTourDirectory(fileUri, newFolder);
}
})
);

// @ts-ignore
return tours.flat().filter(tour => tour);
} catch {
return [];
}
} catch {}
}

async function readTourFile(
tourUri: vscode.Uri
): Promise<CodeTour | undefined> {
async function readTourFile(tourUri: vscode.Uri, folder: CodeTourFolder): Promise<void> {
try {
const tourContent = await readUriContents(tourUri);
const tour = JSON.parse(tourContent);
tour.id = decodeURIComponent(tourUri.toString());
return tour;
folder.tours.push(tour);
} catch {}
}

async function discoverSubTours(workspaceUri: vscode.Uri): Promise<CodeTour[]> {
const tours = await Promise.all(
async function discoverSubTours(workspaceUri: vscode.Uri, folder: CodeTourFolder): Promise<void> {
await Promise.all(
SUB_TOUR_DIRECTORIES.map(directory => {
const uri = vscode.Uri.joinPath(workspaceUri, directory);
return readTourDirectory(uri);
return readTourDirectory(uri, folder);
})
);
}

function findOrCreateFolder(title: string, folder: CodeTourFolder): CodeTourFolder {
for(const tour of folder.tours) {
if ('tours' in tour && tour.title === title) {
return tour;
}
}

const newFolder = {
title,
tours: []
};

return tours.flat();
folder.tours.push(newFolder);

return newFolder;
}

vscode.workspace.onDidChangeWorkspaceFolders(discoverTours);

const watcher = vscode.workspace.createFileSystemWatcher(
`**/{${SUB_TOUR_DIRECTORIES.join(",")}}/**/*.tour`
`**/{${SUB_TOUR_DIRECTORIES.join(",")}}/**`
);

watcher.onDidChange(discoverTours);
Expand Down