Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add default title for new step #243

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@
"type": "object",
"title": "CodeTour",
"properties": {
"codetour.customTourDirectory": {
"type": "string",
"default": null,
"description": "Specifies a custom location to use when discovering tours."
},
"codetour.defaultStepTitle": {
"type": "string",
"default": null,
"description": "Specifies a default title when creating a new step."
},
"codetour.promptForWorkspaceTours": {
"type": "boolean",
"default": true,
Expand All @@ -60,11 +70,6 @@
"type": "boolean",
"default": true,
"description": "Specifies whether or not to show tour markers in the editor gutter."
},
"codetour.customTourDirectory": {
"type": "string",
"default": null,
"description": "Specifies a custom location to use when discovering tours."
}
}
},
Expand Down Expand Up @@ -671,4 +676,4 @@
"arrowParens": "avoid",
"trailingComma": "none"
}
}
}
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class URIHandler implements vscode.UriHandler {
}

export async function activate(context: vscode.ExtensionContext) {
registerPlayerModule(context);
await registerPlayerModule(context);
registerRecorderModule();
registerLiveShareModule();

Expand Down
4 changes: 2 additions & 2 deletions src/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,9 @@ async function showDocument(uri: Uri, range: Range, selection?: Selection) {
document.revealRange(range, TextEditorRevealType.InCenter);
}

export function registerPlayerModule(context: ExtensionContext) {
export async function registerPlayerModule(context: ExtensionContext) {
registerPlayerCommands();
registerTreeProvider(context.extensionPath);
await registerTreeProvider(context.extensionPath);
registerFileSystemProvider();
registerTextDocumentContentProvider();
registerStatusBar();
Expand Down
26 changes: 13 additions & 13 deletions src/player/tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ 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";

Expand Down Expand Up @@ -89,9 +89,9 @@ class CodeTourTreeProvider implements TreeDataProvider<TreeItem>, Disposable {

return [item];
} else {
return element.tour.steps.map(
(_, index) => new CodeTourStepNode(element.tour, index)
);
return await Promise.all(element.tour.steps.map(
async (_, index) => await new CodeTourStepNode(element.tour, index).init()
));
}
}
}
Expand Down Expand Up @@ -127,7 +127,7 @@ class CodeTourTreeProvider implements TreeDataProvider<TreeItem>, Disposable {
}
}

export function registerTreeProvider(extensionPath: string) {
export async function registerTreeProvider(extensionPath: string) {
const treeDataProvider = new CodeTourTreeProvider(extensionPath);
const treeView = window.createTreeView(`${EXTENSION_NAME}.tours`, {
showCollapseAll: true,
Expand All @@ -136,17 +136,17 @@ export function registerTreeProvider(extensionPath: string) {
});

let isRevealPending = false;
treeView.onDidChangeVisibility(e => {
treeView.onDidChangeVisibility(async (e) => {
if (e.visible && isRevealPending) {
isRevealPending = false;
revealCurrentStepNode();
await revealCurrentStepNode();
}
});

function revealCurrentStepNode() {
setTimeout(() => {
async function revealCurrentStepNode() {
setTimeout(async () => {
treeView.reveal(
new CodeTourStepNode(store.activeTour!.tour, store.activeTour!.step)
await new CodeTourStepNode(store.activeTour!.tour, store.activeTour!.step).init()
);
}, 300);
}
Expand All @@ -161,7 +161,7 @@ export function registerTreeProvider(extensionPath: string) {
]
: null
],
() => {
async () => {
if (store.activeTour && store.activeTour.step >= 0) {
if (
!treeView.visible ||
Expand All @@ -171,7 +171,7 @@ export function registerTreeProvider(extensionPath: string) {
return;
}

revealCurrentStepNode();
await revealCurrentStepNode();
} else {
// TODO: Once VS Code supports it, we want
// to de-select the step node once the tour ends.
Expand Down
25 changes: 15 additions & 10 deletions src/player/tree/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,23 @@ export class CodeTourNode extends TreeItem {

export class CodeTourStepNode extends TreeItem {
constructor(public tour: CodeTour, public stepNumber: number) {
super(getStepLabel(tour, stepNumber));
super("")
}
async init(): Promise<CodeTourStepNode> {
this.label = await getStepLabel(this.tour, this.stepNumber);

const step = tour.steps[stepNumber];
const step = this.tour.steps[this.stepNumber];

let workspaceRoot, tours;
if (store.activeTour && store.activeTour.tour.id === tour.id) {
if (store.activeTour && store.activeTour.tour.id === this.tour.id) {
workspaceRoot = store.activeTour.workspaceRoot;
tours = store.activeTour.tours;
}

this.command = {
command: `${EXTENSION_NAME}.startTour`,
title: "Start Tour",
arguments: [tour, stepNumber, workspaceRoot, tours]
arguments: [this.tour, this.stepNumber, workspaceRoot, tours]
};

let resourceUri;
Expand All @@ -93,7 +96,7 @@ export class CodeTourStepNode extends TreeItem {
} else if (step.file || step.directory) {
const resourceRoot = workspaceRoot
? workspaceRoot
: getWorkspaceUri(tour);
: getWorkspaceUri(this.tour);

resourceUri = getFileUri(step.directory || step.file!, resourceRoot);
} else {
Expand All @@ -104,12 +107,12 @@ export class CodeTourStepNode extends TreeItem {

const isActive =
store.activeTour &&
tour.id === store.activeTour?.tour.id &&
store.activeTour.step === stepNumber;
this.tour.id === store.activeTour?.tour.id &&
store.activeTour.step === this.stepNumber;

if (isActive) {
this.iconPath = new ThemeIcon("play-circle");
} else if (progress.isComplete(tour, stepNumber)) {
} else if (progress.isComplete(this.tour, this.stepNumber)) {
// @ts-ignore
this.iconPath = completeIcon;
} else if (step.directory) {
Expand All @@ -119,14 +122,16 @@ export class CodeTourStepNode extends TreeItem {
}

const contextValues = ["codetour.tourStep"];
if (stepNumber > 0) {
if (this.stepNumber > 0) {
contextValues.push("hasPrevious");
}

if (stepNumber < tour.steps.length - 1) {
if (this.stepNumber < this.tour.steps.length - 1) {
contextValues.push("hasNext");
}

this.contextValue = contextValues.join(".");

return this;
}
}
11 changes: 9 additions & 2 deletions src/recorder/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,15 @@ export function registerRecorderCommands() {

const workspaceRoot = getActiveWorkspacePath();
const file = getRelativePath(workspaceRoot, thread!.uri.path);
const line = thread.range.start.line + 1;

const customTitle = vscode.workspace
.getConfiguration("codetour")
.get("defaultStepTitle", null);
const title = customTitle || undefined;

const step: CodeTourStep = {
title,
file,
description: reply.text
};
Expand All @@ -404,10 +411,10 @@ export function registerRecorderCommands() {
step.pattern = pattern;
} else {
// TODO: Try to get smarter about how to handle this.
step.line = thread.range.start.line + 1;
step.line = line;
}
} else {
step.line = thread.range.start.line + 1;
step.line = line;
}

store.activeTour!.step++;
Expand Down
57 changes: 55 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ import { Uri, workspace } from "vscode";
import { CONTENT_URI, FS_SCHEME } from "./constants";
import { api } from "./git";
import { CodeTour, CodeTourStep, store } from "./store";
import { formatter } from "./utils/formatter";

interface LabelCache {
contents?: string;
line?: number;
}

const HEADING_PATTERN = /^#+\s*(.*)/;
export function getStepLabel(
export async function getStepLabel(
tour: CodeTour,
stepNumber: number,
includeStepNumber: boolean = true,
Expand All @@ -20,7 +26,19 @@ export function getStepLabel(
const prefix = includeStepNumber ? `#${stepNumber + 1} - ` : "";
let label = "";
if (step.title) {
label = step.title;
const cache: LabelCache = {};

label = await formatter(step.title, {
file: async () => step.file,
line: async () => getStepLine(tour, stepNumber, cache),
text: async () => {
const line = await getStepLine(tour, stepNumber, cache);
const contents = await getStepContents(tour, stepNumber, cache);
const lines = contents.split(/\r?\n/g);

return lines[line - 1].trim();
}
});
} else if (HEADING_PATTERN.test(step.description.trim())) {
label = step.description.trim().match(HEADING_PATTERN)![1];
} else if (step.markerTitle) {
Expand All @@ -34,6 +52,41 @@ export function getStepLabel(
return `${prefix}${label}`;
}

async function getStepLine(tour: CodeTour, stepNumber: number, cache: LabelCache): Promise<number> {
const step = tour.steps[stepNumber];

if (step.line) {
return step.line;
} else if (cache.line) {
return cache.line;
} else if (step.pattern) {
const contents = await getStepContents(tour, stepNumber, cache);
const match = contents.match(new RegExp(step.pattern, "m"));

if (match) {
const lines = contents.substring(0, match.index).split(/\r?\n/g);

cache.line = lines.length;

return cache.line;
}
}

return 1;
}

async function getStepContents(tour: CodeTour, stepNumber: number, cache: LabelCache): Promise<string> {
if (typeof cache.contents === "undefined") {
const step = tour.steps[stepNumber];
const workspaceRoot = getWorkspaceUri(tour);
const stepFileUri = await getStepFileUri(step, workspaceRoot, tour.ref);

cache.contents = await readUriContents(stepFileUri);
}

return cache.contents;
}

export function getTourTitle(tour: CodeTour) {
if (tour.title.match(/^#?\d+\s-/)) {
return tour.title.split("-")[1].trim();
Expand Down
83 changes: 83 additions & 0 deletions src/utils/formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
export async function formatter(format: string, properties: Record<string, () => Promise<unknown>>): Promise<string> {
const regex = /{{([a-z]+)(?:\|([a-z]+)(?::([a-z,]+))?(?::([a-z0-9,-]+))?(?::([a-z0-9,-]+))?)?}}/g;

let match = regex.exec(format);
if (!match) {
return format;
}

let result = "";
let index = 0;

while (match) {
if (match.index > index) {
result += format.slice(index, match.index);
}

const value = await properties[match[1]]();

if (match[2] === "date") {
const date = value as Date;

if (!match[3]) {
result += String(date);
} else if (match[3] === "iso") {
result += date.toISOString();
} else {
const locales = match[4] ? match[4].split(",") : undefined;
const options: {
dateStyle: string | undefined;
timeStyle: string | undefined;
} = {
dateStyle: undefined,
timeStyle: undefined,
};

const styles = match[3].split(",");

if (styles[0]) {
options.dateStyle = styles[0];
}

if (styles[1]) {
options.timeStyle = styles[1];
}

const formatter = new Intl.DateTimeFormat(locales, options as any);

result += formatter.format(date);
}
} else if(match[2] === "string") {
const str = String(value);

if (match[3] === "sub") {
if (!match[4]) {
result += str;
} else {
const offset = Number.parseInt(match[4]);
const begin = offset < 0 ? str.length + offset : offset;

if (!match[5]) {
result += str.substring(begin);
} else {
const length = Number.parseInt(match[5]);
const end = length < 0 ? str.length + length : offset + length;

result += str.substring(begin, end);
}
}
} else {
result += str;
}
} else {
result += String(value);
}

index = match.index + match[0].length;
match = regex.exec(format);
}

result += format.slice(index, format.length);

return result;
}