Skip to content
Merged
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
23 changes: 19 additions & 4 deletions extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
],
"main": "./dist/extension.js",
"contributes": {
"keybindings": [
{
"command": "tutorialkit.delete",
"key": "Shift+Backspace",
"when": "focusedView == tutorialkit-lessons-tree"
}
],
"commands": [
{
"command": "tutorialkit.select-tutorial",
Expand All @@ -37,6 +44,10 @@
"command": "tutorialkit.add-part",
"title": "Add Part"
},
{
"command": "tutorialkit.delete",
"title": "Delete"
},
{
"command": "tutorialkit.refresh",
"title": "Refresh Lessons",
Expand Down Expand Up @@ -100,6 +111,14 @@
{
"command": "tutorialkit.add-chapter",
"when": "view == tutorialkit-lessons-tree && viewItem == part"
},
{
"command": "tutorialkit.add-part",
"when": "view == tutorialkit-lessons-tree && viewItem == tutorial"
},
{
"command": "tutorialkit.delete",
"when": "view == tutorialkit-lessons-tree && (viewItem == chapter || viewItem == part || viewItem == lesson)"
}
]
},
Expand All @@ -119,10 +138,6 @@
]
},
"scripts": {
"__esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node",
"__dev": "pnpm run esbuild-base -- --sourcemap --watch",
"__vscode:prepublish": "pnpm run esbuild-base -- --minify",
"__build": "vsce package",
Comment on lines -122 to -125
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed those because I don't think we should use them? @sulco what do you think?

"dev": "node scripts/build.mjs --watch",
"build": "pnpm run check-types && node scripts/build.mjs",
"check-types": "tsc --noEmit",
Expand Down
13 changes: 10 additions & 3 deletions extensions/vscode/src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as vscode from 'vscode';
import { addChapter, addLesson, addPart } from './tutorialkit.add';
import { deleteNode } from './tutorialkit.delete';
import tutorialkitGoto from './tutorialkit.goto';
import { initialize } from './tutorialkit.initialize';
import { loadTutorial } from './tutorialkit.load-tutorial';
import tutorialkitRefresh from './tutorialkit.refresh';
import { addChapter, addLesson } from './tutorialkit.add';
import { selectTutorial } from './tutorialkit.select-tutorial';
import { loadTutorial } from './tutorialkit.load-tutorial';
import { initialize } from './tutorialkit.initialize';

// no need to use these consts outside of this file, use `cmd[name].command` instead
const CMD = {
Expand All @@ -14,6 +15,8 @@ const CMD = {
GOTO: 'tutorialkit.goto',
ADD_LESSON: 'tutorialkit.add-lesson',
ADD_CHAPTER: 'tutorialkit.add-chapter',
ADD_PART: 'tutorialkit.add-part',
DELETE: 'tutorialkit.delete',
REFRESH: 'tutorialkit.refresh',
} as const;

Expand All @@ -25,6 +28,8 @@ export function useCommands() {
vscode.commands.registerCommand(CMD.GOTO, tutorialkitGoto);
vscode.commands.registerCommand(CMD.ADD_LESSON, addLesson);
vscode.commands.registerCommand(CMD.ADD_CHAPTER, addChapter);
vscode.commands.registerCommand(CMD.ADD_PART, addPart);
vscode.commands.registerCommand(CMD.DELETE, deleteNode);
vscode.commands.registerCommand(CMD.REFRESH, tutorialkitRefresh);
}

Expand All @@ -34,7 +39,9 @@ export const cmd = {
selectTutorial: createExecutor<typeof selectTutorial>(CMD.SELECT_TUTORIAL),
loadTutorial: createExecutor<typeof loadTutorial>(CMD.LOAD_TUTORIAL),
goto: createExecutor<typeof tutorialkitGoto>(CMD.GOTO),
delete: createExecutor<typeof deleteNode>(CMD.DELETE),
addLesson: createExecutor<typeof addLesson>(CMD.ADD_LESSON),
addPart: createExecutor<typeof addPart>(CMD.ADD_PART),
addChapter: createExecutor<typeof addChapter>(CMD.ADD_CHAPTER),
refresh: createExecutor<typeof tutorialkitRefresh>(CMD.REFRESH),
};
Expand Down
64 changes: 36 additions & 28 deletions extensions/vscode/src/commands/tutorialkit.add.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { cmd } from '.';
import { Lesson, LessonType } from '../models/Lesson';
import { Node, NodeType } from '../models/Node';
import * as vscode from 'vscode';
import { FILES_FOLDER, SOLUTION_FOLDER } from '../models/tree/constants';
import { updateNodeMetadataInVFS } from '../models/tree/update';

let kebabCase: (string: string) => string;
let capitalize: (string: string) => string;
Expand All @@ -11,34 +13,34 @@ let capitalize: (string: string) => string;
capitalize = module.capitalCase;
})();

export async function addLesson(parent: Lesson) {
const lessonNumber = parent.children.length + 1;
export async function addLesson(parent: Node) {
const { folderPath, metaFilePath } = await createNodeFolder(parent, 'lesson');

const lessonName = await getUnitName('lesson', lessonNumber);

const lessonFolderPath = await createUnitFolder(parent.path, lessonNumber, lessonName, 'lesson');

await vscode.workspace.fs.createDirectory(vscode.Uri.file(`${lessonFolderPath}/_files`));
await vscode.workspace.fs.createDirectory(vscode.Uri.file(`${lessonFolderPath}/_solution`));
await vscode.workspace.fs.createDirectory(vscode.Uri.joinPath(folderPath, FILES_FOLDER));
await vscode.workspace.fs.createDirectory(vscode.Uri.joinPath(folderPath, SOLUTION_FOLDER));

await cmd.refresh();

return navigateToUnit(lessonFolderPath, 'lesson');
return cmd.goto(metaFilePath);
}

export async function addChapter(parent: Lesson) {
const chapterNumber = parent.children.length + 1;
export async function addChapter(parent: Node) {
const { metaFilePath } = await createNodeFolder(parent, 'chapter');

const chapterName = await getUnitName('chapter', chapterNumber);
await cmd.refresh();

const chapterFolderPath = await createUnitFolder(parent.path, chapterNumber, chapterName, 'chapter');
return cmd.goto(metaFilePath);
}

await navigateToUnit(chapterFolderPath, 'chapter');
export async function addPart(parent: Node) {
const { metaFilePath } = await createNodeFolder(parent, 'part');

await cmd.refresh();

return cmd.goto(metaFilePath);
}

async function getUnitName(unitType: LessonType, unitNumber: number) {
async function getNodeName(unitType: NodeType, unitNumber: number) {
const unitName = await vscode.window.showInputBox({
prompt: `Enter the name of the new ${unitType}`,
value: `${capitalize(unitType)} ${unitNumber}`,
Expand All @@ -52,20 +54,26 @@ async function getUnitName(unitType: LessonType, unitNumber: number) {
return unitName;
}

async function createUnitFolder(parentPath: string, unitNumber: number, unitName: string, unitType: LessonType) {
const unitFolderPath = `${parentPath}/${unitNumber}-${kebabCase(unitName)}`;
const metaFile = unitType === 'lesson' ? 'content.mdx' : 'meta.md';
async function createNodeFolder(parent: Node, nodeType: NodeType) {
const unitNumber = parent.children.length + 1;
const unitName = await getNodeName(nodeType, unitNumber);
const unitFolderPath = parent.order ? kebabCase(unitName) : `${unitNumber}-${kebabCase(unitName)}`;

await vscode.workspace.fs.writeFile(
vscode.Uri.file(`${unitFolderPath}/${metaFile}`),
new TextEncoder().encode(`---\ntype: ${unitType}\ntitle: ${unitName}\n---\n`),
);
const metaFile = nodeType === 'lesson' ? 'content.mdx' : 'meta.md';
const metaFilePath = vscode.Uri.joinPath(parent.path, unitFolderPath, metaFile);

return unitFolderPath;
}
if (parent.order) {
parent.pushChild(unitFolderPath);
await updateNodeMetadataInVFS(parent);
}

async function navigateToUnit(path: string, unitType: LessonType) {
const metaFile = unitType === 'lesson' ? 'content.mdx' : 'meta.md';
await vscode.workspace.fs.writeFile(
metaFilePath,
new TextEncoder().encode(`---\ntype: ${nodeType}\ntitle: ${unitName}\n---\n`),
);

return cmd.goto(`${path}/${metaFile}`);
return {
folderPath: vscode.Uri.joinPath(parent.path, unitFolderPath),
metaFilePath,
};
}
35 changes: 35 additions & 0 deletions extensions/vscode/src/commands/tutorialkit.delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { cmd } from '.';
import * as vscode from 'vscode';
import { Node } from '../models/Node';
import { getLessonsTreeView } from '../global-state';
import { updateNodeMetadataInVFS } from '../models/tree/update';

export async function deleteNode(selectedNode: Node | undefined, selectedNodes: Node[] | undefined) {
let nodes: readonly Node[] = (selectedNodes ? selectedNodes : [selectedNode]).filter((node) => node !== undefined);

if (nodes.length === 0) {
nodes = getLessonsTreeView().selection;
}

const parents = new Set<Node>();

for (const node of nodes) {
if (node.parent) {
parents.add(node.parent);
node.parent.removeChild(node);
}

await vscode.workspace.fs.delete(node.path, { recursive: true });
}

// remove all nodes from parents that that might have been parent of other deleted nodes
for (const node of nodes) {
parents.delete(node);
}

for (const parent of parents) {
await updateNodeMetadataInVFS(parent);
}

return cmd.refresh();
}
20 changes: 18 additions & 2 deletions extensions/vscode/src/commands/tutorialkit.goto.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import * as vscode from 'vscode';

export default async (path: string | undefined) => {
export default async (path: string | vscode.Uri | undefined) => {
if (!path) {
return;
}

const document = await vscode.workspace.openTextDocument(path);
/**
* This cast to 'any' makes no sense because if we narrow the type of path
* there are no type errors. So this code:
*
* ```ts
* typeof path === 'string'
* ? await vscode.workspace.openTextDocument(path)
* : await vscode.workspace.openTextDocument(path)
* ;
* ```
*
* Type check correctly despite being identical to calling the function
* without the branch.
*
* To avoid this TypeScript bug here we just cast to any.
*/
const document = await vscode.workspace.openTextDocument(path as any);

await vscode.window.showTextDocument(document, {
preserveFocus: true,
Expand Down
22 changes: 14 additions & 8 deletions extensions/vscode/src/commands/tutorialkit.load-tutorial.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import * as vscode from 'vscode';
import { extContext } from '../extension';
import { LessonsTreeDataProvider, getLessonsTreeDataProvider, setLessonsTreeDataProvider } from '../views/lessonsTree';
import { LessonsTreeDataProvider } from '../views/lessonsTree';
import { setLessonsTreeDataProvider, setLessonsTreeView } from '../global-state';

export async function loadTutorial(uri: vscode.Uri) {
setLessonsTreeDataProvider(new LessonsTreeDataProvider(uri, extContext));
const treeDataProvider = new LessonsTreeDataProvider(uri, extContext);

extContext.subscriptions.push(
vscode.window.createTreeView('tutorialkit-lessons-tree', {
treeDataProvider: getLessonsTreeDataProvider(),
canSelectMany: true,
}),
);
await treeDataProvider.init();

const treeView = vscode.window.createTreeView('tutorialkit-lessons-tree', {
treeDataProvider,
canSelectMany: true,
});

setLessonsTreeDataProvider(treeDataProvider);
setLessonsTreeView(treeView);

extContext.subscriptions.push(treeView, treeDataProvider);

vscode.commands.executeCommand('setContext', 'tutorialkit:tree', true);
}
2 changes: 1 addition & 1 deletion extensions/vscode/src/commands/tutorialkit.refresh.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getLessonsTreeDataProvider } from '../views/lessonsTree';
import { getLessonsTreeDataProvider } from '../global-state';

export default () => {
getLessonsTreeDataProvider().refresh();
Expand Down
22 changes: 22 additions & 0 deletions extensions/vscode/src/global-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { TreeView } from 'vscode';
import type { LessonsTreeDataProvider } from './views/lessonsTree';
import type { Node } from './models/Node';

let lessonsTreeDataProvider: LessonsTreeDataProvider;
let lessonsTreeView: TreeView<Node>;

export function getLessonsTreeDataProvider() {
return lessonsTreeDataProvider;
}

export function getLessonsTreeView() {
return lessonsTreeView;
}

export function setLessonsTreeDataProvider(provider: LessonsTreeDataProvider) {
lessonsTreeDataProvider = provider;
}

export function setLessonsTreeView(treeView: TreeView<Node>) {
lessonsTreeView = treeView;
}
15 changes: 0 additions & 15 deletions extensions/vscode/src/models/Lesson.ts

This file was deleted.

Loading