From 80f5553e69a48681d256d355e31b81201064efdf Mon Sep 17 00:00:00 2001 From: Jan Dolejsi Date: Fri, 17 May 2019 00:52:52 +0200 Subject: [PATCH] - code documentation, clean-up - sourceControlPane argument in the command handlers - command to open fiddle in the browser --- source-control-sample/README.md | 10 +- source-control-sample/package.json | 8 ++ source-control-sample/src/extension.ts | 44 ++++--- source-control-sample/src/fiddleRepository.ts | 2 +- .../src/fiddleSourceControl.ts | 114 ++++++++++-------- 5 files changed, 110 insertions(+), 68 deletions(-) diff --git a/source-control-sample/README.md b/source-control-sample/README.md index a71c03d9..58ecdedc 100644 --- a/source-control-sample/README.md +++ b/source-control-sample/README.md @@ -56,13 +56,19 @@ The three commands (command, roll-back and refresh) in the title of the source c "command": "extension.source-control.refresh", "group": "navigation", "when": "scmProvider == jsfiddle" + }, + { + "command": "extension.source-control.browse", + "when": "scmProvider == jsfiddle" } ] } }, ``` -It is also worth noting that the sample extension needs to overcome reloading that VS Code triggers when a new workspace folder is added. This is done by writing a memo into the `context.globalState` and reading it upon the next extension activation. +Note that the `extension.source-control.browse` command is intentionally missing the `"group": "navigation"` placement specification, which makes it show up under the [dot dot dot] button. + +It is also worth noting that the sample extension needs to overcome reloading that VS Code triggers when a new workspace folder is added. This could be done either by writing a memo into the `context.globalState`, or a configuration file into the workspace folder and reading it upon the next extension activation. The selected solution is to use `.jsfiddle` configuration file as described below. ## Status bar controls @@ -126,7 +132,7 @@ This extension implements the _cloning_ using the `extension.source-control.open This sample extension stores the source control configuration in the `.jsfiddle` JSON file in the workspace folder root. -Upon extension activation such file is discovered in the workspace folder and source control initialized. +This is done by storing the `.jsfiddle` configuration file into the `WorkspaceFolder` just inserting the workspace folder via the `vscode.workspace.updateWorkspaceFolders(...)` call, which seems to make the extension re-activate. Upon the re-activation, the `initializeFromConfigurationFile()` function scans all `WorkspaceFolder`s for the `.jsfiddle` configuration files and re-creates the `SourceControl` instances. ## Multiple workspace folder support diff --git a/source-control-sample/package.json b/source-control-sample/package.json index 45be42d4..6518c25d 100644 --- a/source-control-sample/package.json +++ b/source-control-sample/package.json @@ -52,6 +52,10 @@ "light": "resources/icons/light/refresh.svg", "dark": "resources/icons/dark/refresh.svg" } + }, + { + "command": "extension.source-control.browse", + "title": "Open current Fiddle in the default browser" } ], "menus": { @@ -70,6 +74,10 @@ "command": "extension.source-control.refresh", "group": "navigation", "when": "scmProvider == jsfiddle" + }, + { + "command": "extension.source-control.browse", + "when": "scmProvider == jsfiddle" } ] } diff --git a/source-control-sample/src/extension.ts b/source-control-sample/src/extension.ts index a9bf1636..9e1753e8 100644 --- a/source-control-sample/src/extension.ts +++ b/source-control-sample/src/extension.ts @@ -30,26 +30,38 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(JSFIDDLE_SCHEME, jsFiddleDocumentContentProvider)); - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.refresh", async () => { - let sourceControl = await pickSourceControl(); - if (sourceControl) { sourceControl.refresh(); } - })); - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.discard", async () => { - let sourceControl = await pickSourceControl(); - if (sourceControl) { sourceControl.resetFilesToCheckedOutVersion(); } - })); - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.commit", async () => { - let sourceControl = await pickSourceControl(); - if (sourceControl) { sourceControl.commitAll(); } - })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.refresh", + async (sourceControlPane: vscode.SourceControl) => { + let sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.refresh(); } + })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.discard", + async (sourceControlPane: vscode.SourceControl) => { + let sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.resetFilesToCheckedOutVersion(); } + })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.commit", + async (sourceControlPane: vscode.SourceControl) => { + let sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.commitAll(); } + })); context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.checkout", async (sourceControl: FiddleSourceControl, newVersion?: number) => { - sourceControl = sourceControl || await pickSourceControl(); + sourceControl = sourceControl || await pickSourceControl(null); if (sourceControl) { sourceControl.tryCheckout(newVersion); } })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.browse", + async (sourceControlPane: vscode.SourceControl) => { + let sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.openInBrowser(); } + })); } -async function pickSourceControl(): Promise { +async function pickSourceControl(sourceControlPane: vscode.SourceControl): Promise { + if (sourceControlPane) { + return fiddleSourceControlRegister.get(sourceControlPane.rootUri); + } + // todo: when/if the SourceControl exposes a 'selected' property, use that instead if (fiddleSourceControlRegister.size === 0) { return undefined; } @@ -131,6 +143,10 @@ function registerFiddleSourceControl(fiddleSourceControl: FiddleSourceControl, c context.subscriptions.push(fiddleSourceControl); } +/** + * When the extension starts up, it must visit all workspace folders to see if any of them are fiddles. + * @param context extension context + */ function initializeFromConfigurationFile(context: vscode.ExtensionContext): void { if (!vscode.workspace.workspaceFolders) { return; } diff --git a/source-control-sample/src/fiddleRepository.ts b/source-control-sample/src/fiddleRepository.ts index 288e5164..b9bec8ef 100644 --- a/source-control-sample/src/fiddleRepository.ts +++ b/source-control-sample/src/fiddleRepository.ts @@ -147,7 +147,7 @@ export async function uploadFiddle(slug: string, version: number, html: string, } } -function toFiddleId(slug: string, version: number | undefined): string { +export function toFiddleId(slug: string, version: number | undefined): string { if (version === undefined) { return slug; } diff --git a/source-control-sample/src/fiddleSourceControl.ts b/source-control-sample/src/fiddleSourceControl.ts index d6a181ee..a6039538 100644 --- a/source-control-sample/src/fiddleSourceControl.ts +++ b/source-control-sample/src/fiddleSourceControl.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { FiddleRepository, toExtension, downloadFiddle, areIdentical, uploadFiddle, Fiddle } from './fiddleRepository'; +import { FiddleRepository, toExtension, downloadFiddle, areIdentical, uploadFiddle, Fiddle, toFiddleId } from './fiddleRepository'; import * as path from 'path'; import { writeFileSync, existsSync, writeFile } from 'fs'; import { FiddleConfiguration, parseFiddleId } from './fiddleConfiguration'; @@ -185,56 +185,6 @@ export class FiddleSourceControl implements vscode.Disposable { }); } - /** - * Refresh is used when the information on the server may have changed. - * For example another user updates the Fiddle online. - */ - async refresh(): Promise { - let latestVersion = this.fiddle.version || 0; - while (true) { - try { - let latestFiddle = await downloadFiddle(this.fiddle.slug, latestVersion); - this.latestFiddleVersion = latestVersion; - latestVersion++; - } catch (ex) { - // typically the ex.statusCode == 404, when there is no further version - break; - } - } - - this.refreshStatusBar(); - } - - /** - * Determines which version was checked out and finds the index of the latest version. - */ - async establishVersion(): Promise { - let version = 0; - let latestVersion = Number.NaN; - let currentFiddle: Fiddle | undefined = undefined; - while (true) { - try { - let latestFiddle = await downloadFiddle(this.fiddle.slug, version); - latestVersion = version; - version++; - if (areIdentical(this.fiddle.data, latestFiddle.data)) { - currentFiddle = latestFiddle; - } - } catch (ex) { - // typically the ex.statusCode == 404, when there is no further version - break; - } - } - - this.latestFiddleVersion = latestVersion; - - // now we know the version of the current fiddle, let's set it - if (currentFiddle) { - this.setFiddle(currentFiddle, false); - } - } - - get onRepositoryChange(): vscode.Event { return this._onRepositoryChange.event; } @@ -253,6 +203,7 @@ export class FiddleSourceControl implements vscode.Disposable { } } + /** This is where the source control determines, which documents were updated, removed, and theoretically added. */ async updateChangedGroup(): Promise { // for simplicity we ignore which document was changed in this event and scan all of them let changedResources: vscode.SourceControlResourceState[] = []; @@ -280,6 +231,9 @@ export class FiddleSourceControl implements vscode.Disposable { } this.changedResources.resourceStates = changedResources; + + // the number of modified resources needs to be assigned to the SourceControl.count filed to let VS Code show the number. + this.jsFiddleScm.count = this.changedResources.resourceStates.length; } /** Determines whether the resource is different, regardless of line endings. */ @@ -315,6 +269,64 @@ export class FiddleSourceControl implements vscode.Disposable { return resourceState; } + /** + * Refresh is used when the information on the server may have changed. + * For example another user updates the Fiddle online. + */ + async refresh(): Promise { + let latestVersion = this.fiddle.version || 0; + while (true) { + try { + let latestFiddle = await downloadFiddle(this.fiddle.slug, latestVersion); + this.latestFiddleVersion = latestVersion; + latestVersion++; + } catch (ex) { + // typically the ex.statusCode == 404, when there is no further version + break; + } + } + + this.refreshStatusBar(); + } + + /** + * Determines which version was checked out and finds the index of the latest version. + * + * When a fiddle is open by the hash code, the latest version is downloaded, + * but extension does not know what version it is. + */ + async establishVersion(): Promise { + let version = 0; + let latestVersion = Number.NaN; + let currentFiddle: Fiddle | undefined = undefined; + while (true) { + try { + let latestFiddle = await downloadFiddle(this.fiddle.slug, version); + latestVersion = version; + version++; + if (areIdentical(this.fiddle.data, latestFiddle.data)) { + currentFiddle = latestFiddle; + } + } catch (ex) { + // typically the ex.statusCode == 404, when there is no further version + break; + } + } + + this.latestFiddleVersion = latestVersion; + + // now we know the version of the current fiddle, let's set it + if (currentFiddle) { + this.setFiddle(currentFiddle, false); + } + } + + /** Opens the fiddle in the default browser. */ + openInBrowser() { + let url = "https://jsfiddle.net/" + toFiddleId(this.fiddle.slug, this.fiddle.version); + vscode.env.openExternal(vscode.Uri.parse(url)); + } + dispose() { this._onRepositoryChange.dispose(); this.jsFiddleScm.dispose();