Adding support for VFS with query strings
This commit is contained in:
Родитель
87e8d99e19
Коммит
c55d9e7f91
|
@ -2,6 +2,10 @@
|
|||
|
||||
- Added the following commands to the command link completion list: `Run build task`, `Run task` and `Run test task`.
|
||||
- Fixed a bug where command links didn't work, if the command included multiple "components" to the name (e.g. `workbench.action.tasks.build`).
|
||||
- Fixed a bug where tours weren't being discovered for virtual file systems that include a query string in their workspace path.
|
||||
- Fixed a bug where tours that included content-only steps couldn't be exported.
|
||||
- Fixed the open/export tour commands to correctly look for `*.tour` files.
|
||||
- Fixed a bug where the `CodeTour: Record Tour` command was being displayed without having any workspaces open.
|
||||
|
||||
## v0.0.27 (05/22/2020)
|
||||
|
||||
|
|
|
@ -154,11 +154,6 @@
|
|||
"category": "CodeTour",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "codetour.refreshTours",
|
||||
"title": "Refresh Tours",
|
||||
"category": "CodeTour"
|
||||
},
|
||||
{
|
||||
"command": "codetour.resumeTour",
|
||||
"title": "Resume Tour",
|
||||
|
@ -214,8 +209,8 @@
|
|||
"when": "codetour:inTour && codetour:recording"
|
||||
},
|
||||
{
|
||||
"command": "codetour.refreshTours",
|
||||
"when": "codetour:hasTours"
|
||||
"command": "codetour.recordTour",
|
||||
"when": "workspaceFolderCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "codetour.resumeTour",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as vscode from "vscode";
|
||||
import { CodeTour } from "../store";
|
||||
import { getStepFileUri, getWorkspacePath } from "../utils";
|
||||
import { EXTENSION_NAME, SMALL_ICON_URL } from "../constants";
|
||||
import { CodeTour } from "../store";
|
||||
import { getStepFileUri, getWorkspaceUri } from "../utils";
|
||||
|
||||
class CodeTourNotebookProvider implements vscode.NotebookProvider {
|
||||
async resolveNotebook(editor: vscode.NotebookEditor): Promise<void> {
|
||||
|
@ -11,15 +11,15 @@ class CodeTourNotebookProvider implements vscode.NotebookProvider {
|
|||
cellEditable: false
|
||||
};
|
||||
|
||||
let contents = (
|
||||
let contents = new TextDecoder().decode(
|
||||
await vscode.workspace.fs.readFile(editor.document.uri)
|
||||
).toString();
|
||||
);
|
||||
|
||||
let tour = <CodeTour>JSON.parse(contents);
|
||||
tour.id = editor.document.uri.toString();
|
||||
|
||||
let steps: any[] = [];
|
||||
const workspaceRoot = getWorkspacePath(tour);
|
||||
const workspaceRoot = getWorkspaceUri(tour);
|
||||
|
||||
for (let item of tour.steps) {
|
||||
const uri = await getStepFileUri(item, workspaceRoot, tour.ref);
|
||||
|
|
|
@ -134,7 +134,7 @@ export function registerPlayerCommands() {
|
|||
async () => {
|
||||
const uri = await vscode.window.showOpenDialog({
|
||||
filters: {
|
||||
Tours: ["json"]
|
||||
Tours: ["tour"]
|
||||
},
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
|
@ -146,9 +146,13 @@ export function registerPlayerCommands() {
|
|||
}
|
||||
|
||||
try {
|
||||
const contents = await vscode.workspace.fs.readFile(uri[0]);
|
||||
const tour = JSON.parse(contents.toString());
|
||||
const contents = new TextDecoder().decode(
|
||||
await vscode.workspace.fs.readFile(uri[0])
|
||||
);
|
||||
|
||||
const tour = JSON.parse(contents);
|
||||
tour.id = uri[0].toString();
|
||||
|
||||
startCodeTour(tour);
|
||||
} catch {
|
||||
vscode.window.showErrorMessage(
|
||||
|
@ -186,7 +190,7 @@ export function registerPlayerCommands() {
|
|||
async (node: CodeTourNode) => {
|
||||
const uri = await vscode.window.showSaveDialog({
|
||||
filters: {
|
||||
Tours: ["json"]
|
||||
Tours: ["tour"]
|
||||
},
|
||||
saveLabel: "Export Tour"
|
||||
});
|
||||
|
@ -196,7 +200,8 @@ export function registerPlayerCommands() {
|
|||
}
|
||||
|
||||
const contents = await exportTour(node.tour);
|
||||
vscode.workspace.fs.writeFile(uri, new Buffer(contents));
|
||||
const bytes = new TextEncoder().encode(contents);
|
||||
vscode.workspace.fs.writeFile(uri, bytes);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { reaction } from "mobx";
|
|||
import * as vscode from "vscode";
|
||||
import { FS_SCHEME_CONTENT, ICON_URL } from "../constants";
|
||||
import { CodeTour, CodeTourStep, store } from "../store";
|
||||
import { getStepFileUri, getWorkspacePath } from "../utils";
|
||||
import { getStepFileUri, getWorkspaceUri } from "../utils";
|
||||
|
||||
const DISABLED_SCHEMES = [FS_SCHEME_CONTENT, "comment"];
|
||||
|
||||
|
@ -27,7 +27,7 @@ async function getTourSteps(
|
|||
|
||||
const tourSteps = await Promise.all(
|
||||
steps.map(async ([tour, step, stepNumber]) => {
|
||||
const workspaceRoot = getWorkspacePath(tour);
|
||||
const workspaceRoot = getWorkspaceUri(tour);
|
||||
const uri = await getStepFileUri(step, workspaceRoot);
|
||||
|
||||
if (
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from "vscode";
|
||||
import { SMALL_ICON_URL } from "../constants";
|
||||
import { store } from "../store";
|
||||
import { getActiveWorkspacePath, getFileUri, getStepFileUri } from "../utils";
|
||||
import { getFileUri, getStepFileUri } from "../utils";
|
||||
|
||||
const CONTROLLER_ID = "codetour";
|
||||
const CONTROLLER_LABEL = "CodeTour";
|
||||
|
@ -131,7 +131,7 @@ async function renderCurrentStep() {
|
|||
label += ` (${currentTour.title})`;
|
||||
}
|
||||
|
||||
const workspaceRoot = getActiveWorkspacePath();
|
||||
const workspaceRoot = store.activeTour?.workspaceRoot;
|
||||
const uri = await getStepFileUri(step, workspaceRoot, currentTour.ref);
|
||||
store.activeTour!.thread = controller!.createCommentThread(uri, range, []);
|
||||
|
||||
|
@ -176,7 +176,7 @@ async function renderCurrentStep() {
|
|||
await showDocument(uri, range, selection);
|
||||
|
||||
if (step.directory) {
|
||||
const directoryUri = getFileUri(workspaceRoot, step.directory);
|
||||
const directoryUri = getFileUri(step.directory, workspaceRoot);
|
||||
commands.executeCommand("revealInExplorer", directoryUri);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,14 +96,11 @@ export async function exportTour(tour: CodeTour) {
|
|||
|
||||
newTour.steps = await Promise.all(
|
||||
newTour.steps.map(async step => {
|
||||
if (step.contents && step.uri) {
|
||||
if (step.contents || step.uri || !step.file) {
|
||||
return step;
|
||||
}
|
||||
|
||||
const workspaceRoot = workspace.workspaceFolders
|
||||
? workspace.workspaceFolders[0].uri.toString()
|
||||
: "";
|
||||
|
||||
const workspaceRoot = getWorkspaceUri(tour);
|
||||
const stepFileUri = await getStepFileUri(step, workspaceRoot, tour.ref);
|
||||
|
||||
const stepFileContents = await workspace.fs.readFile(stepFileUri);
|
||||
|
|
|
@ -9,12 +9,17 @@ export interface CodeTourStepPosition {
|
|||
export interface CodeTourStep {
|
||||
title?: string;
|
||||
description: string;
|
||||
|
||||
// If any of the following are set, then only
|
||||
// one of them can be, since these properties
|
||||
// indicate the "type" of step.
|
||||
file?: string;
|
||||
directory?: string;
|
||||
contents?: string;
|
||||
uri?: string;
|
||||
|
||||
line?: number;
|
||||
selection?: { start: CodeTourStepPosition; end: CodeTourStepPosition };
|
||||
contents?: string;
|
||||
}
|
||||
|
||||
export interface CodeTour {
|
||||
|
|
|
@ -5,7 +5,6 @@ import { EXTENSION_NAME, VSCODE_DIRECTORY } from "../constants";
|
|||
import { endCurrentCodeTour } from "./actions";
|
||||
|
||||
const MAIN_TOUR_FILES = [".tour", `${VSCODE_DIRECTORY}/main.tour`];
|
||||
|
||||
const SUB_TOUR_DIRECTORIES = [`${VSCODE_DIRECTORY}/tours`, `.tours`];
|
||||
|
||||
const HAS_TOURS_KEY = `${EXTENSION_NAME}:hasTours`;
|
||||
|
@ -13,9 +12,8 @@ const HAS_TOURS_KEY = `${EXTENSION_NAME}:hasTours`;
|
|||
export async function discoverTours(): Promise<void> {
|
||||
const tours = await Promise.all(
|
||||
vscode.workspace.workspaceFolders!.map(async workspaceFolder => {
|
||||
const workspaceRoot = workspaceFolder.uri.toString();
|
||||
const mainTours = await discoverMainTours(workspaceRoot);
|
||||
const tours = await discoverSubTours(workspaceRoot);
|
||||
const mainTours = await discoverMainTours(workspaceFolder.uri);
|
||||
const tours = await discoverSubTours(workspaceFolder.uri);
|
||||
|
||||
if (mainTours) {
|
||||
tours.push(...mainTours);
|
||||
|
@ -51,14 +49,19 @@ export async function discoverTours(): Promise<void> {
|
|||
vscode.commands.executeCommand("setContext", HAS_TOURS_KEY, store.hasTours);
|
||||
}
|
||||
|
||||
async function discoverMainTours(workspaceRoot: string): Promise<CodeTour[]> {
|
||||
async function discoverMainTours(
|
||||
workspaceUri: vscode.Uri
|
||||
): Promise<CodeTour[]> {
|
||||
const tours = await Promise.all(
|
||||
MAIN_TOUR_FILES.map(async tourFile => {
|
||||
try {
|
||||
const uri = vscode.Uri.parse(`${workspaceRoot}/${tourFile}`);
|
||||
const mainTourContent = (
|
||||
const uri = workspaceUri.with({
|
||||
path: `${workspaceUri.path}/${tourFile}`
|
||||
});
|
||||
|
||||
const mainTourContent = new TextDecoder().decode(
|
||||
await vscode.workspace.fs.readFile(uri)
|
||||
).toString();
|
||||
);
|
||||
const tour = JSON.parse(mainTourContent);
|
||||
tour.id = uri.toString();
|
||||
return tour;
|
||||
|
@ -69,20 +72,23 @@ async function discoverMainTours(workspaceRoot: string): Promise<CodeTour[]> {
|
|||
return tours.filter(tour => tour);
|
||||
}
|
||||
|
||||
async function readTourDirectory(tourDirectory: string): Promise<CodeTour[]> {
|
||||
async function readTourDirectory(uri: vscode.Uri): Promise<CodeTour[]> {
|
||||
try {
|
||||
const uri = vscode.Uri.parse(tourDirectory);
|
||||
const tourFiles = await vscode.workspace.fs.readDirectory(uri);
|
||||
const tours = await Promise.all(
|
||||
tourFiles.map(async ([file, type]) => {
|
||||
const fileUri = uri.with({
|
||||
path: `${uri.path}/${file}`
|
||||
});
|
||||
if (type === vscode.FileType.File) {
|
||||
return readTourFile(tourDirectory, file);
|
||||
return readTourFile(fileUri);
|
||||
} else {
|
||||
return readTourDirectory(`${tourDirectory}/${file}`);
|
||||
return readTourDirectory(fileUri);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
return tours.flat().filter(tour => tour);
|
||||
} catch {
|
||||
return [];
|
||||
|
@ -90,25 +96,27 @@ async function readTourDirectory(tourDirectory: string): Promise<CodeTour[]> {
|
|||
}
|
||||
|
||||
async function readTourFile(
|
||||
directory: string,
|
||||
file: string
|
||||
tourUri: vscode.Uri
|
||||
): Promise<CodeTour | undefined> {
|
||||
try {
|
||||
const tourUri = vscode.Uri.parse(`${directory}/${file}`);
|
||||
const tourContent = (
|
||||
const tourContent = new TextDecoder().decode(
|
||||
await vscode.workspace.fs.readFile(tourUri)
|
||||
).toString();
|
||||
);
|
||||
const tour = JSON.parse(tourContent);
|
||||
tour.id = tourUri.toString();
|
||||
return tour;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function discoverSubTours(workspaceRoot: string): Promise<CodeTour[]> {
|
||||
async function discoverSubTours(workspaceUri: vscode.Uri): Promise<CodeTour[]> {
|
||||
const tours = await Promise.all(
|
||||
SUB_TOUR_DIRECTORIES.map(directory =>
|
||||
readTourDirectory(`${workspaceRoot}/${directory}`)
|
||||
)
|
||||
SUB_TOUR_DIRECTORIES.map(directory => {
|
||||
const uri = workspaceUri.with({
|
||||
path: `${workspaceUri.path}/${directory}`
|
||||
});
|
||||
|
||||
return readTourDirectory(uri);
|
||||
})
|
||||
);
|
||||
|
||||
return tours.flat();
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as path from "path";
|
|||
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
|
||||
import { CONTENT_URI, EXTENSION_NAME, FS_SCHEME } from "../constants";
|
||||
import { CodeTour, store } from "../store";
|
||||
import { getFileUri, getWorkspacePath } from "../utils";
|
||||
import { getFileUri, getWorkspaceUri } from "../utils";
|
||||
|
||||
function isRecording(tour: CodeTour) {
|
||||
return (
|
||||
|
@ -101,10 +101,10 @@ export class CodeTourStepNode extends TreeItem {
|
|||
resourceUri = Uri.parse(`${FS_SCHEME}://current/${step.file}`);
|
||||
} else if (step.file || step.directory) {
|
||||
const resourceRoot = workspaceRoot
|
||||
? workspaceRoot.toString()
|
||||
: getWorkspacePath(tour);
|
||||
? workspaceRoot
|
||||
: getWorkspaceUri(tour);
|
||||
|
||||
resourceUri = getFileUri(resourceRoot, step.directory || step.file!);
|
||||
resourceUri = getFileUri(step.directory || step.file!, resourceRoot);
|
||||
} else {
|
||||
resourceUri = CONTENT_URI;
|
||||
}
|
||||
|
|
38
src/utils.ts
38
src/utils.ts
|
@ -4,28 +4,27 @@ import { CONTENT_URI, FS_SCHEME } from "./constants";
|
|||
import { api } from "./git";
|
||||
import { CodeTour, CodeTourStep, store } from "./store";
|
||||
|
||||
export function getFileUri(workspaceRoot: string, file: string) {
|
||||
const uriPrefix = workspaceRoot.endsWith("/")
|
||||
? workspaceRoot
|
||||
: `${workspaceRoot}/`;
|
||||
|
||||
let uri = Uri.parse(`${uriPrefix}${file}`);
|
||||
|
||||
if (file.startsWith("..")) {
|
||||
// path.join/normalize will resolve relative paths (e.g. replacing
|
||||
// ".." with the actual directories), but it also messes up
|
||||
// non-file based schemes. So we parse the workspace root and
|
||||
// then replace it's path with a "joined" version of _only_ the path.
|
||||
uri = uri.with({
|
||||
path: path.normalize(uri.path)
|
||||
});
|
||||
export function getFileUri(file: string, workspaceRoot?: Uri) {
|
||||
if (!workspaceRoot) {
|
||||
return Uri.parse(file);
|
||||
}
|
||||
return uri;
|
||||
|
||||
const rootPath = workspaceRoot.path.endsWith("/")
|
||||
? workspaceRoot.path
|
||||
: `${workspaceRoot.path}/`;
|
||||
|
||||
// path.join/normalize will resolve relative paths (e.g. replacing
|
||||
// ".." with the actual directories), but it also messes up
|
||||
// non-file based schemes. So we parse the workspace root and
|
||||
// then replace it's path with a normalized version of _only_ the path.
|
||||
return workspaceRoot.with({
|
||||
path: path.normalize(`${rootPath}${file}`)
|
||||
});
|
||||
}
|
||||
|
||||
export async function getStepFileUri(
|
||||
step: CodeTourStep,
|
||||
workspaceRoot: string,
|
||||
workspaceRoot?: Uri,
|
||||
ref?: string
|
||||
): Promise<Uri> {
|
||||
let uri;
|
||||
|
@ -34,7 +33,7 @@ export async function getStepFileUri(
|
|||
} else if (step.uri || step.file) {
|
||||
uri = step.uri
|
||||
? Uri.parse(step.uri)
|
||||
: getFileUri(workspaceRoot, step.file!);
|
||||
: getFileUri(step.file!, workspaceRoot);
|
||||
|
||||
if (api && ref && ref !== "HEAD") {
|
||||
const repo = api.getRepository(uri);
|
||||
|
@ -72,5 +71,6 @@ export function getWorkspacePath(tour: CodeTour) {
|
|||
}
|
||||
|
||||
export function getWorkspaceUri(tour: CodeTour) {
|
||||
return workspace.getWorkspaceFolder(Uri.parse(tour.id))?.uri;
|
||||
const tourUri = Uri.parse(tour.id);
|
||||
return workspace.getWorkspaceFolder(tourUri)?.uri;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": ["ES2019"],
|
||||
"lib": ["ES2019", "DOM"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
|
|
Загрузка…
Ссылка в новой задаче