diff --git a/custom-editor-sample/README.md b/custom-editor-sample/README.md
index 012e1b3d..d4652559 100644
--- a/custom-editor-sample/README.md
+++ b/custom-editor-sample/README.md
@@ -1,8 +1,11 @@
# Cat Customs - Custom Editor API Samples
-Demonstrates using VS Code's [custom editor API](https://code.visualstudio.com/api/extension-guides/custom-editors):
+
-- Cat Scratch — A text based custom editor for `.cscratch` files (which are just json files)
+Demonstrates VS Code's [custom editor API](https://code.visualstudio.com/api/extension-guides/custom-editors) using two custom editors:
+
+- Cat Scratch — Uses the finalized custom text editor api to provide a custom editor for `.cscratch` files (which are just json files)
+- Paw Draw - Uses the proposed binary custom editor api to provide a custom editor for `.pawdraw` files (which are just jpeg files with a different file extension). Note that this requires VS Code 1.46+
## VS Code API
@@ -10,10 +13,11 @@ Demonstrates using VS Code's [custom editor API](https://code.visualstudio.com/a
- [`window.registerCustomEditorProvider`](https://code.visualstudio.com/api/references/vscode-api#window.registerCustomEditorProvider)
- [`CustomTextEditor`](https://code.visualstudio.com/api/references/vscode-api#CustomTextEditor)
+- [`CustomEditor`](https://code.visualstudio.com/api/references/vscode-api#CustomEditor)
## Running the example
-- Open this example in VS Code 1.44+
+- Open this example in VS Code 1.44+ (note that the custom binary editor requires VS Code 1.45+)
- `npm install`
- `npm run watch` or `npm run compile`
- `F5` to start debugging
diff --git a/custom-editor-sample/documentation/example.png b/custom-editor-sample/documentation/example.png
new file mode 100644
index 00000000..b14743a8
Binary files /dev/null and b/custom-editor-sample/documentation/example.png differ
diff --git a/custom-editor-sample/exampleFiles/example.pawDraw b/custom-editor-sample/exampleFiles/example.pawDraw
new file mode 100644
index 00000000..941ea9c5
Binary files /dev/null and b/custom-editor-sample/exampleFiles/example.pawDraw differ
diff --git a/custom-editor-sample/media/paw-color.svg b/custom-editor-sample/media/paw-color.svg
new file mode 100644
index 00000000..b358cca2
--- /dev/null
+++ b/custom-editor-sample/media/paw-color.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/custom-editor-sample/media/paw-outline.svg b/custom-editor-sample/media/paw-outline.svg
new file mode 100644
index 00000000..0227ff54
--- /dev/null
+++ b/custom-editor-sample/media/paw-outline.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/custom-editor-sample/media/pawDraw.css b/custom-editor-sample/media/pawDraw.css
new file mode 100644
index 00000000..faedcd24
--- /dev/null
+++ b/custom-editor-sample/media/pawDraw.css
@@ -0,0 +1,79 @@
+html, body {
+ height: 100%;
+}
+
+.drawing-canvas {
+ width: 100%;
+ height: 100%;
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-repeat: repeat;
+ flex-direction: column;
+}
+
+.drawing-controls {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+}
+
+.drawing-controls button {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ background: none;
+ border: none;
+ transform: translateY(30%);
+ transition: transform 0.1s linear;
+ outline: none;
+}
+
+.drawing-controls button.active,
+.drawing-controls button:hover {
+ transform: translateY(10%);
+}
+
+.drawing-controls button:before,
+.drawing-controls button:after {
+ display: block;
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.drawing-controls button:before {
+ -webkit-mask: url("./paw-color.svg") no-repeat 50% 50%;
+}
+
+.drawing-controls button:after {
+ background-color: #111;
+ -webkit-mask: url("./paw-outline.svg") no-repeat 50% 50%;
+}
+
+.drawing-controls button.black:before {
+ background-color: #333;
+}
+
+.drawing-controls button.white:before {
+ background-color: white;
+}
+
+.drawing-controls button.red:before {
+ background-color: red;
+}
+
+.drawing-controls button.green:before {
+ background-color: green;
+}
+
+.drawing-controls button.blue:before {
+ background-color: blue;
+}
\ No newline at end of file
diff --git a/custom-editor-sample/media/pawDraw.js b/custom-editor-sample/media/pawDraw.js
new file mode 100644
index 00000000..bfece22f
--- /dev/null
+++ b/custom-editor-sample/media/pawDraw.js
@@ -0,0 +1,231 @@
+// @ts-check
+
+// This script is run within the webview itself
+(function () {
+ // @ts-ignore
+ const vscode = acquireVsCodeApi();
+
+ /**
+ * A drawn line.
+ */
+ class Stroke {
+ constructor(/** @type {string} */ color, /** @type {Array<[number, number]> | undefined} */ stroke) {
+ this.color = color;
+ /** @type {Array<[number, number]>} */
+ this.stroke = stroke || [];
+ }
+
+ addPoint(/** @type {number} */ x, /** @type {number} */ y) {
+ this.stroke.push([x, y])
+ }
+ }
+
+ /**
+ * @param {Uint8Array} initialContent
+ * @return {Promise}
+ */
+ async function loadImageFromData(initialContent) {
+ const blob = new Blob([initialContent], { 'type': 'image/png' });
+ const url = URL.createObjectURL(blob);
+ try {
+ const img = document.createElement('img');
+ img.crossOrigin = 'anonymous';
+ img.src = url;
+ await new Promise((resolve, reject) => {
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ return img;
+ } finally {
+ URL.revokeObjectURL(url);
+ }
+ }
+
+ class PawDrawEditor {
+ constructor( /** @type {HTMLElement} */ parent) {
+ this.ready = false;
+
+ this.drawingColor = 'black';
+
+ /** @type {Array} */
+ this.strokes = [];
+
+ /** @type {Stroke | undefined} */
+ this.currentStroke = undefined;
+
+ this._initElements(parent);
+ }
+
+ addPoint(/** @type {number} */ x, /** @type {number} */ y) {
+ if (this.currentStroke) {
+ this.currentStroke.addPoint(x, y)
+ }
+ }
+
+ beginStoke(/** @type {string} */ color) {
+ this.currentStroke = new Stroke(color);
+ this.strokes.push(this.currentStroke);
+ }
+
+ endStroke() {
+ const previous = this.currentStroke;
+ this.currentStroke = undefined;
+ return previous;
+ }
+
+ _initElements(/** @type {HTMLElement} */ parent) {
+ const colorButtons = /** @type {NodeListOf} */ (document.querySelectorAll('.drawing-controls button'));
+ for (const colorButton of colorButtons) {
+ colorButton.addEventListener('click', e => {
+ e.stopPropagation();
+ colorButtons.forEach(button => button.classList.remove('active'));
+ colorButton.classList.add('active');
+ this.drawingColor = colorButton.dataset['color'];
+ });
+ }
+
+ this.wrapper = document.createElement('div');
+ this.wrapper.style.position = 'relative';
+ parent.append(this.wrapper);
+
+ this.initialCanvas = document.createElement('canvas');
+ this.initialCtx = this.initialCanvas.getContext('2d');
+ this.wrapper.append(this.initialCanvas);
+
+ this.drawingCanvas = document.createElement('canvas');
+ this.drawingCanvas.style.position = 'absolute';
+ this.drawingCanvas.style.top = '0';
+ this.drawingCanvas.style.left = '0';
+ this.drawingCtx = this.drawingCanvas.getContext('2d');
+ this.wrapper.append(this.drawingCanvas);
+
+ let isDrawing = false
+
+ parent.addEventListener('mousedown', () => {
+ if (!this.ready) {
+ return;
+ }
+
+ this.beginStoke(this.drawingColor);
+ this.drawingCtx.strokeStyle = this.drawingColor;
+
+ isDrawing = true;
+ document.body.classList.add('isDrawing');
+ this.drawingCtx.beginPath();
+ });
+
+ document.body.addEventListener('mouseup', async () => {
+ if (!isDrawing || !this.ready) {
+ return;
+ }
+
+ isDrawing = false;
+ document.body.classList.remove('isDrawing');
+ this.drawingCtx.closePath();
+
+ const edit = this.endStroke();
+
+ if (edit.stroke.length) {
+ vscode.postMessage({
+ type: 'stroke',
+ color: edit.color,
+ stroke: edit.stroke,
+ });
+ }
+ });
+
+ parent.addEventListener('mousemove', e => {
+ if (!isDrawing || !this.ready) {
+ return;
+ }
+ const rect = this.wrapper.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+ this.drawingCtx.lineTo(x, y);
+ this.drawingCtx.stroke();
+ this.addPoint(x, y);
+ });
+ }
+
+ _redraw() {
+ this.drawingCtx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
+ for (const stroke of this.strokes) {
+ this.drawingCtx.strokeStyle = stroke.color;
+ this.drawingCtx.beginPath();
+ for (const [x, y] of stroke.stroke) {
+ this.drawingCtx.lineTo(x, y);
+ }
+ this.drawingCtx.stroke();
+ this.drawingCtx.closePath();
+ }
+ }
+
+ /**
+ * @param {Uint8Array | undefined} data
+ * @param {Array | undefined} strokes
+ */
+ async reset(data, strokes = []) {
+ if (data) {
+ const img = await loadImageFromData(data);
+ this.initialCanvas.width = this.drawingCanvas.width = img.naturalWidth;
+ this.initialCanvas.height = this.drawingCanvas.height = img.naturalHeight;
+ this.initialCtx.drawImage(img, 0, 0);
+ this.ready = true;
+ }
+
+ this.strokes = strokes;
+ this._redraw();
+ }
+
+ /** @return {Promise} */
+ async getImageData() {
+ const outCanvas = document.createElement('canvas');
+ outCanvas.width = this.drawingCanvas.width;
+ outCanvas.height = this.drawingCanvas.height;
+
+ const outCtx = outCanvas.getContext('2d');
+ outCtx.drawImage(this.initialCanvas, 0, 0);
+ outCtx.drawImage(this.drawingCanvas, 0, 0);
+
+ const blob = await new Promise(resolve => {
+ outCanvas.toBlob(resolve, 'image/jpeg')
+ });
+
+ return new Uint8Array(await blob.arrayBuffer());
+ }
+ }
+
+ const editor = new PawDrawEditor(document.querySelector('.drawing-canvas'));
+
+ // Handle messages from the extension
+ window.addEventListener('message', async e => {
+ const { type, body, requestId } = e.data;
+ switch (type) {
+ case 'init':
+ {
+ // Load the initial image into the canvas.
+ const data = new Uint8Array(body.value.data);
+ await editor.reset(data);
+ return;
+ }
+ case 'update':
+ {
+ const data = body.content ? new Uint8Array(body.content.data) : undefined;;
+ const strokes = body.edits.map(edit => new Stroke(edit.color, edit.stroke));
+ await editor.reset(data, strokes)
+ return;
+ }
+ case 'getFileData':
+ {
+ // Get the image data for the canvas and post it back to the extension.
+ editor.getImageData().then(data => {
+ vscode.postMessage({ type: 'response', requestId, body: data });
+ });
+ return;
+ }
+ }
+ });
+
+ // Signal to VS Code that the webview is initialized.
+ vscode.postMessage({ type: 'ready' });
+}());
diff --git a/custom-editor-sample/package.json b/custom-editor-sample/package.json
index 804aa8a0..7a97a4ce 100644
--- a/custom-editor-sample/package.json
+++ b/custom-editor-sample/package.json
@@ -12,7 +12,8 @@
"Other"
],
"activationEvents": [
- "onCustomEditor:catCustoms.catScratch"
+ "onCustomEditor:catCustoms.catScratch",
+ "onCustomEditor:catCustoms.pawDraw"
],
"repository": {
"type": "git",
@@ -29,6 +30,15 @@
"filenamePattern": "*.cscratch"
}
]
+ },
+ {
+ "viewType": "catCustoms.pawDraw",
+ "displayName": "Paw Draw",
+ "selector": [
+ {
+ "filenamePattern": "*.pawdraw"
+ }
+ ]
}
]
},
diff --git a/custom-editor-sample/src/dispose.ts b/custom-editor-sample/src/dispose.ts
new file mode 100644
index 00000000..5bf917ae
--- /dev/null
+++ b/custom-editor-sample/src/dispose.ts
@@ -0,0 +1,37 @@
+import * as vscode from 'vscode';
+
+export function disposeAll(disposables: vscode.Disposable[]) {
+ while (disposables.length) {
+ const item = disposables.pop();
+ if (item) {
+ item.dispose();
+ }
+ }
+}
+
+export abstract class Disposable {
+ private _isDisposed = false;
+
+ protected _disposables: vscode.Disposable[] = [];
+
+ public dispose(): any {
+ if (this._isDisposed) {
+ return;
+ }
+ this._isDisposed = true;
+ disposeAll(this._disposables);
+ }
+
+ protected _register(value: T): T {
+ if (this._isDisposed) {
+ value.dispose();
+ } else {
+ this._disposables.push(value);
+ }
+ return value;
+ }
+
+ protected get isDisposed() {
+ return this._isDisposed;
+ }
+}
\ No newline at end of file
diff --git a/custom-editor-sample/src/extension.ts b/custom-editor-sample/src/extension.ts
index d4f5e158..fcd7d831 100644
--- a/custom-editor-sample/src/extension.ts
+++ b/custom-editor-sample/src/extension.ts
@@ -1,7 +1,12 @@
import * as vscode from 'vscode';
import { CatScratchEditorProvider } from './catScratchEditor';
+import { PawDrawEditorProvider } from './pawDrawEditor';
export function activate(context: vscode.ExtensionContext) {
- // Register our custom editor provider
+ // Register our custom editor providers
context.subscriptions.push(CatScratchEditorProvider.register(context));
+
+ if (+vscode.version.match(/1\.(\d+)/)![1] >= 45) {
+ context.subscriptions.push(PawDrawEditorProvider.register(context));
+ }
}
diff --git a/custom-editor-sample/src/pawDrawEditor.ts b/custom-editor-sample/src/pawDrawEditor.ts
new file mode 100644
index 00000000..dcd1b52a
--- /dev/null
+++ b/custom-editor-sample/src/pawDrawEditor.ts
@@ -0,0 +1,416 @@
+import * as path from 'path';
+import * as vscode from 'vscode';
+import { getNonce } from './util';
+import { Disposable, disposeAll } from './dispose';
+
+/**
+ * Define the type of edits used in paw draw files.
+ */
+interface PawDrawEdit {
+ readonly color: string;
+ readonly stroke: ReadonlyArray<[number, number]>;
+}
+
+interface PawDrawDocumentDelegate {
+ getFileData(): Promise;
+}
+
+/**
+ * Define the document (the data model) used for paw draw files.
+ */
+class PawDrawDocument extends Disposable implements vscode.CustomDocument {
+
+ static async create(
+ uri: vscode.Uri,
+ backupId: string | undefined,
+ delegate: PawDrawDocumentDelegate,
+ ): Promise> {
+ // If we have a backup, read that. Otherwise read the resource from the workspace
+ const dataFile = typeof backupId === 'string' ? vscode.Uri.parse(backupId) : uri;
+ const fileData = await vscode.workspace.fs.readFile(dataFile);
+ return new PawDrawDocument(uri, fileData, delegate);
+ }
+
+ private readonly _uri: vscode.Uri;
+
+ private _documentData: Uint8Array;
+ private _edits: Array = [];
+ private _savedEdits: Array = [];
+
+ private readonly _delegate: PawDrawDocumentDelegate;
+
+ private constructor(
+ uri: vscode.Uri,
+ initialContent: Uint8Array,
+ delegate: PawDrawDocumentDelegate
+ ) {
+ super();
+ this._uri = uri;
+ this._documentData = initialContent;
+ this._delegate = delegate;
+ }
+
+ public get uri() { return this._uri; }
+
+ public get documentData(): Uint8Array { return this._documentData; }
+
+ private readonly _onDidDispose = this._register(new vscode.EventEmitter());
+ /**
+ * Fired when the document is disposed of.
+ */
+ public readonly onDidDispose = this._onDidDispose.event;
+
+ private readonly _onDidChangeDocument = this._register(new vscode.EventEmitter<{
+ readonly content?: Uint8Array;
+ readonly edits: readonly PawDrawEdit[];
+ }>());
+ /**
+ * Fired to notify webviews that the document has changed.
+ */
+ public readonly onDidChangeContent = this._onDidChangeDocument.event;
+
+ private readonly _onDidChange = this._register(new vscode.EventEmitter<{
+ readonly label: string,
+ undo(): void,
+ redo(): void,
+ }>());
+ /**
+ * Fired to tell VS Code that an edit has occured in the document.
+ *
+ * This updates the document's dirty indicator.
+ */
+ public readonly onDidChange = this._onDidChange.event;
+
+ /**
+ * Called by VS Code when there are no more references to the document.
+ *
+ * This happens when all editors for it have been closed.
+ */
+ dispose(): void {
+ this._onDidDispose.fire();
+ super.dispose();
+ }
+
+ /**
+ * Called when the user edits the document in a webview.
+ *
+ * This fires an event to notify VS Code that the document has been edited.
+ */
+ makeEdit(edit: PawDrawEdit) {
+ this._edits.push(edit);
+
+ this._onDidChange.fire({
+ label: 'Stroke',
+ undo: async () => {
+ this._edits.pop();
+ this._onDidChangeDocument.fire({
+ edits: this._edits,
+ });
+ },
+ redo: async () => {
+ this._edits.push(edit);
+ this._onDidChangeDocument.fire({
+ edits: this._edits,
+ });
+ }
+ });
+ }
+
+ /**
+ * Called by VS Code when the user saves the document.
+ */
+ async save(cancellation: vscode.CancellationToken): Promise {
+ await this.saveAs(this.uri, cancellation);
+ this._savedEdits = Array.from(this._edits);
+ }
+
+ /**
+ * Called by VS Code when the user saves the document to a new location.
+ */
+ async saveAs(targetResource: vscode.Uri, cancellation: vscode.CancellationToken): Promise {
+ const fileData = await this._delegate.getFileData();
+ if (cancellation.isCancellationRequested) {
+ return;
+ }
+ await vscode.workspace.fs.writeFile(targetResource, fileData);
+ }
+
+ /**
+ * Called by VS Code when the user calls `revert` on a document.
+ */
+ async revert(_cancellation: vscode.CancellationToken): Promise {
+ const diskContent = await vscode.workspace.fs.readFile(this.uri);
+ this._documentData = diskContent;
+ this._edits = this._savedEdits;
+ this._onDidChangeDocument.fire({
+ content: diskContent,
+ edits: this._edits,
+ });
+ }
+
+ /**
+ * Called by VS Code to backup the edited document.
+ *
+ * These backups are used to implement hot exit.
+ */
+ async backup(destination: vscode.Uri, cancellation: vscode.CancellationToken): Promise {
+ await this.saveAs(destination, cancellation);
+
+ return {
+ id: destination.toString(),
+ delete: async () => {
+ try {
+ await vscode.workspace.fs.delete(destination);
+ } catch {
+ // noop
+ }
+ }
+ };
+ }
+}
+
+/**
+ * Provider for paw draw editors.
+ *
+ * Paw draw editors are used for `.pawDraw` files, which are just `.png` files with a different file extension.
+ *
+ * This provider demonstrates:
+ *
+ * - How to implement a custom editor for binary files.
+ * - Setting up the initial webview for a custom editor.
+ * - Loading scripts and styles in a custom editor.
+ * - Communication between VS Code and the custom editor.
+ * - Using CustomDocuments to store information that is shared between multiple custom editors.
+ * - Implementing save, undo, redo, and revert.
+ * - Backing up a custom editor.
+ */
+export class PawDrawEditorProvider implements vscode.CustomEditorProvider {
+
+ public static register(context: vscode.ExtensionContext): vscode.Disposable {
+ return vscode.window.registerCustomEditorProvider2(
+ PawDrawEditorProvider.viewType,
+ new PawDrawEditorProvider(context),
+ {
+ // For this demo extension, we enable `retainContextWhenHidden` which keeps the
+ // webview alive even when it is not visible. You should avoid using this setting
+ // unless is absolutely required as it does have memory overhead.
+ webviewOptions: {
+ retainContextWhenHidden: true,
+ },
+ supportsMultipleEditorsPerDocument: false,
+ });
+ }
+
+ private static readonly viewType = 'catCustoms.pawDraw';
+
+ /**
+ * Tracks all known webviews
+ */
+ private readonly webviews = new WebviewCollection();
+
+ constructor(
+ private readonly _context: vscode.ExtensionContext
+ ) { }
+
+ //#region CustomEditorProvider
+
+ async openCustomDocument(
+ uri: vscode.Uri,
+ openContext: { backupId?: string },
+ _token: vscode.CancellationToken
+ ): Promise {
+ const document = await PawDrawDocument.create(uri, openContext.backupId, {
+ getFileData: async () => {
+ const webviewsForDocument: any = Array.from(this.webviews.get(document.uri));
+ if (!webviewsForDocument.length) {
+ throw new Error('Could not find webview to save for');
+ }
+ const panel = webviewsForDocument[0];
+ const response = await this.postMessageWithResponse<{ data: number[] }>(panel, 'getFileData', {});
+ return new Uint8Array(response.data);
+ }
+ });
+
+ const listeners: vscode.Disposable[] = [];
+
+ listeners.push(document.onDidChange(e => {
+ // Tell VS Code that the document has been edited by the use.
+ this._onDidChangeCustomDocument.fire({
+ document,
+ ...e,
+ });
+ }));
+
+ listeners.push(document.onDidChangeContent(e => {
+ // Update all webviews when the document changes
+ for (const webviewPanel of this.webviews.get(document.uri)) {
+ this.postMessage(webviewPanel, 'update', {
+ edits: e.edits,
+ content: e.content,
+ });
+ }
+ }));
+
+ document.onDidDispose(() => disposeAll(listeners));
+
+ return document;
+ }
+
+ async resolveCustomEditor(
+ document: PawDrawDocument,
+ webviewPanel: vscode.WebviewPanel,
+ _token: vscode.CancellationToken
+ ): Promise {
+ // Add the webview to our internal set of active webviews
+ this.webviews.add(document.uri, webviewPanel);
+
+ // Setup initial content for the webview
+ webviewPanel.webview.options = {
+ enableScripts: true,
+ };
+ webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview);
+
+ webviewPanel.webview.onDidReceiveMessage(e => this.onMessage(document, e));
+
+ // Wait for the webview to be properly ready before we init
+ webviewPanel.webview.onDidReceiveMessage(e => {
+ if (e.type === 'ready') {
+ this.postMessage(webviewPanel, 'init', {
+ value: document.documentData
+ });
+ }
+ });
+ }
+
+ private readonly _onDidChangeCustomDocument = new vscode.EventEmitter>();
+ public readonly onDidChangeCustomDocument = this._onDidChangeCustomDocument.event;
+
+ public saveCustomDocument(document: PawDrawDocument, cancellation: vscode.CancellationToken): Thenable {
+ return document.save(cancellation);
+ }
+
+ public saveCustomDocumentAs(document: PawDrawDocument, destination: vscode.Uri, cancellation: vscode.CancellationToken): Thenable {
+ return document.saveAs(destination, cancellation);
+ }
+
+ public revertCustomDocument(document: PawDrawDocument, cancellation: vscode.CancellationToken): Thenable {
+ return document.revert(cancellation);
+ }
+
+ public backupCustomDocument(document: PawDrawDocument, context: vscode.CustomDocumentBackupContext, cancellation: vscode.CancellationToken): Thenable {
+ return document.backup(context.destination, cancellation);
+ }
+
+ //#endregion
+
+ /**
+ * Get the static HTML used for in our editor's webviews.
+ */
+ private getHtmlForWebview(webview: vscode.Webview): string {
+ // Local path to script and css for the webview
+ const scriptUri = webview.asWebviewUri(vscode.Uri.file(
+ path.join(this._context.extensionPath, 'media', 'pawDraw.js')
+ ));
+ const styleUri = webview.asWebviewUri(vscode.Uri.file(
+ path.join(this._context.extensionPath, 'media', 'pawDraw.css')
+ ));
+
+ // Use a nonce to whitelist which scripts can be run
+ const nonce = getNonce();
+
+ return /* html */`
+
+
+
+
+
+
+
+
+
+
+
+
+ Paw Draw
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ private _requestId = 1;
+ private readonly _callbacks = new Map void>();
+
+ private postMessageWithResponse(panel: vscode.WebviewPanel, type: string, body: any): Promise {
+ const requestId = this._requestId++;
+ const p = new Promise(resolve => this._callbacks.set(requestId, resolve));
+ panel.webview.postMessage({ type, requestId, body });
+ return p;
+ }
+
+ private postMessage(panel: vscode.WebviewPanel, type: string, body: any): void {
+ panel.webview.postMessage({ type, body });
+ }
+
+ private onMessage(document: PawDrawDocument, message: any) {
+ switch (message.type) {
+ case 'stroke':
+ document.makeEdit(message as PawDrawEdit);
+ return;
+
+ case 'response':
+ const callback = this._callbacks.get(message.requestId);
+ callback?.(message.body);
+ return;
+ }
+ }
+}
+
+/**
+ * Tracks all webviews.
+ */
+class WebviewCollection {
+
+ private readonly _webviews = new Set<{
+ readonly resource: string;
+ readonly webviewPanel: vscode.WebviewPanel;
+ }>();
+
+ /**
+ * Get all known webviews for a given uri.
+ */
+ public *get(uri: vscode.Uri): Iterable {
+ const key = uri.toString();
+ for (const entry of this._webviews) {
+ if (entry.resource === key) {
+ yield entry.webviewPanel;
+ }
+ }
+ }
+
+ /**
+ * Add a new webview to the collection.
+ */
+ public add(uri: vscode.Uri, webviewPanel: vscode.WebviewPanel) {
+ const entry = { resource: uri.toString(), webviewPanel };
+ this._webviews.add(entry);
+
+ webviewPanel.onDidDispose(() => {
+ this._webviews.delete(entry);
+ });
+ }
+}
\ No newline at end of file
diff --git a/custom-editor-sample/src/vscode.proposed.d.ts b/custom-editor-sample/src/vscode.proposed.d.ts
new file mode 100644
index 00000000..2c74b800
--- /dev/null
+++ b/custom-editor-sample/src/vscode.proposed.d.ts
@@ -0,0 +1,2097 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * This is the place for API experiments and proposals.
+ * These API are NOT stable and subject to change. They are only available in the Insiders
+ * distribution and CANNOT be used in published extensions.
+ *
+ * To test these API in local environment:
+ * - Use Insiders release of VS Code.
+ * - Add `"enableProposedApi": true` to your package.json.
+ * - Copy this file to your project.
+ */
+
+declare module 'vscode' {
+
+ // #region auth provider: https://github.com/microsoft/vscode/issues/88309
+
+ export interface AuthenticationSession {
+ id: string;
+ getAccessToken(): Thenable;
+ account: {
+ displayName: string;
+ id: string;
+ };
+ scopes: string[]
+ }
+
+ /**
+ * An [event](#Event) which fires when an [AuthenticationProvider](#AuthenticationProvider) is added or removed.
+ */
+ export interface AuthenticationProvidersChangeEvent {
+ /**
+ * The ids of the [authenticationProvider](#AuthenticationProvider)s that have been added.
+ */
+ readonly added: string[];
+
+ /**
+ * The ids of the [authenticationProvider](#AuthenticationProvider)s that have been removed.
+ */
+ readonly removed: string[];
+ }
+
+ /**
+ * An [event](#Event) which fires when an [AuthenticationSession](#AuthenticationSession) is added, removed, or changed.
+ */
+ export interface AuthenticationSessionsChangeEvent {
+ /**
+ * The ids of the [AuthenticationSession](#AuthenticationSession)s that have been added.
+ */
+ readonly added: string[];
+
+ /**
+ * The ids of the [AuthenticationSession](#AuthenticationSession)s that have been removed.
+ */
+ readonly removed: string[];
+
+ /**
+ * The ids of the [AuthenticationSession](#AuthenticationSession)s that have been changed.
+ */
+ readonly changed: string[];
+ }
+
+ /**
+ * **WARNING** When writing an AuthenticationProvider, `id` should be treated as part of your extension's
+ * API, changing it is a breaking change for all extensions relying on the provider. The id is
+ * treated case-sensitively.
+ */
+ export interface AuthenticationProvider {
+ /**
+ * Used as an identifier for extensions trying to work with a particular
+ * provider: 'microsoft', 'github', etc. id must be unique, registering
+ * another provider with the same id will fail.
+ */
+ readonly id: string;
+ readonly displayName: string;
+
+ /**
+ * An [event](#Event) which fires when the array of sessions has changed, or data
+ * within a session has changed.
+ */
+ readonly onDidChangeSessions: Event;
+
+ /**
+ * Returns an array of current sessions.
+ */
+ getSessions(): Thenable>;
+
+ /**
+ * Prompts a user to login.
+ */
+ login(scopes: string[]): Thenable;
+ logout(sessionId: string): Thenable;
+ }
+
+ export namespace authentication {
+ export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;
+
+ /**
+ * Fires with the provider id that was registered or unregistered.
+ */
+ export const onDidChangeAuthenticationProviders: Event;
+
+ /**
+ * An array of the ids of authentication providers that are currently registered.
+ */
+ export const providerIds: string[];
+
+ /**
+ * Get existing authentication sessions. Rejects if a provider with providerId is not
+ * registered, or if the user does not consent to sharing authentication information with
+ * the extension.
+ * @param providerId The id of the provider to use
+ * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication
+ * provider
+ */
+ export function getSessions(providerId: string, scopes: string[]): Thenable;
+
+ /**
+ * Prompt a user to login to create a new authenticaiton session. Rejects if a provider with
+ * providerId is not registered, or if the user does not consent to sharing authentication
+ * information with the extension.
+ * @param providerId The id of the provider to use
+ * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication
+ * provider
+ */
+ export function login(providerId: string, scopes: string[]): Thenable;
+
+ /**
+ * Logout of a specific session.
+ * @param providerId The id of the provider to use
+ * @param sessionId The session id to remove
+ * provider
+ */
+ export function logout(providerId: string, sessionId: string): Thenable;
+
+ /**
+ * An [event](#Event) which fires when the array of sessions has changed, or data
+ * within a session has changed for a provider. Fires with the ids of the providers
+ * that have had session data change.
+ */
+ export const onDidChangeSessions: Event<{ [providerId: string]: AuthenticationSessionsChangeEvent }>;
+ }
+
+ //#endregion
+
+ //#region Alex - resolvers
+
+ export interface RemoteAuthorityResolverContext {
+ resolveAttempt: number;
+ }
+
+ export class ResolvedAuthority {
+ readonly host: string;
+ readonly port: number;
+
+ constructor(host: string, port: number);
+ }
+
+ export interface ResolvedOptions {
+ extensionHostEnv?: { [key: string]: string | null };
+ }
+
+ export interface TunnelOptions {
+ remoteAddress: { port: number, host: string };
+ // The desired local port. If this port can't be used, then another will be chosen.
+ localAddressPort?: number;
+ label?: string;
+ }
+
+ export interface TunnelDescription {
+ remoteAddress: { port: number, host: string };
+ //The complete local address(ex. localhost:1234)
+ localAddress: { port: number, host: string } | string;
+ }
+
+ export interface Tunnel extends TunnelDescription {
+ // Implementers of Tunnel should fire onDidDispose when dispose is called.
+ onDidDispose: Event;
+ dispose(): void;
+ }
+
+ /**
+ * Used as part of the ResolverResult if the extension has any candidate,
+ * published, or forwarded ports.
+ */
+ export interface TunnelInformation {
+ /**
+ * Tunnels that are detected by the extension. The remotePort is used for display purposes.
+ * The localAddress should be the complete local address (ex. localhost:1234) for connecting to the port. Tunnels provided through
+ * detected are read-only from the forwarded ports UI.
+ */
+ environmentTunnels?: TunnelDescription[];
+
+ }
+
+ export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation;
+
+ export class RemoteAuthorityResolverError extends Error {
+ static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError;
+ static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError;
+
+ constructor(message?: string);
+ }
+
+ export interface RemoteAuthorityResolver {
+ resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable;
+ /**
+ * Can be optionally implemented if the extension can forward ports better than the core.
+ * When not implemented, the core will use its default forwarding logic.
+ * When implemented, the core will use this to forward ports.
+ */
+ tunnelFactory?: (tunnelOptions: TunnelOptions) => Thenable | undefined;
+
+ /**
+ * Provides filtering for candidate ports.
+ */
+ showCandidatePort?: (host: string, port: number, detail: string) => Thenable;
+ }
+
+ export namespace workspace {
+ /**
+ * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel.
+ * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips.
+ * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen.
+ */
+ export function openTunnel(tunnelOptions: TunnelOptions): Thenable;
+
+ /**
+ * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user.
+ * Note that these are of type TunnelDescription and cannot be disposed.
+ */
+ export let tunnels: Thenable;
+
+ /**
+ * Fired when the list of tunnels has changed.
+ */
+ export const onDidChangeTunnels: Event;
+ }
+
+ export interface ResourceLabelFormatter {
+ scheme: string;
+ authority?: string;
+ formatting: ResourceLabelFormatting;
+ }
+
+ export interface ResourceLabelFormatting {
+ label: string; // myLabel:/${path}
+ // TODO@isi
+ // eslint-disable-next-line vscode-dts-literal-or-types
+ separator: '/' | '\\' | '';
+ tildify?: boolean;
+ normalizeDriveLetter?: boolean;
+ workspaceSuffix?: string;
+ authorityPrefix?: string;
+ }
+
+ export namespace workspace {
+ export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable;
+ export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable;
+ }
+
+ //#endregion
+
+ //#region editor insets: https://github.com/microsoft/vscode/issues/85682
+
+ export interface WebviewEditorInset {
+ readonly editor: TextEditor;
+ readonly line: number;
+ readonly height: number;
+ readonly webview: Webview;
+ readonly onDidDispose: Event;
+ dispose(): void;
+ }
+
+ export namespace window {
+ export function createWebviewTextEditorInset(editor: TextEditor, line: number, height: number, options?: WebviewOptions): WebviewEditorInset;
+ }
+
+ //#endregion
+
+ //#region read/write in chunks: https://github.com/microsoft/vscode/issues/84515
+
+ export interface FileSystemProvider {
+ open?(resource: Uri, options: { create: boolean }): number | Thenable;
+ close?(fd: number): void | Thenable;
+ read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable;
+ write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable;
+ }
+
+ //#endregion
+
+ //#region TextSearchProvider: https://github.com/microsoft/vscode/issues/59921
+
+ /**
+ * The parameters of a query for text search.
+ */
+ export interface TextSearchQuery {
+ /**
+ * The text pattern to search for.
+ */
+ pattern: string;
+
+ /**
+ * Whether or not `pattern` should match multiple lines of text.
+ */
+ isMultiline?: boolean;
+
+ /**
+ * Whether or not `pattern` should be interpreted as a regular expression.
+ */
+ isRegExp?: boolean;
+
+ /**
+ * Whether or not the search should be case-sensitive.
+ */
+ isCaseSensitive?: boolean;
+
+ /**
+ * Whether or not to search for whole word matches only.
+ */
+ isWordMatch?: boolean;
+ }
+
+ /**
+ * A file glob pattern to match file paths against.
+ * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts.
+ * @see [GlobPattern](#GlobPattern)
+ */
+ export type GlobString = string;
+
+ /**
+ * Options common to file and text search
+ */
+ export interface SearchOptions {
+ /**
+ * The root folder to search within.
+ */
+ folder: Uri;
+
+ /**
+ * Files that match an `includes` glob pattern should be included in the search.
+ */
+ includes: GlobString[];
+
+ /**
+ * Files that match an `excludes` glob pattern should be excluded from the search.
+ */
+ excludes: GlobString[];
+
+ /**
+ * Whether external files that exclude files, like .gitignore, should be respected.
+ * See the vscode setting `"search.useIgnoreFiles"`.
+ */
+ useIgnoreFiles: boolean;
+
+ /**
+ * Whether symlinks should be followed while searching.
+ * See the vscode setting `"search.followSymlinks"`.
+ */
+ followSymlinks: boolean;
+
+ /**
+ * Whether global files that exclude files, like .gitignore, should be respected.
+ * See the vscode setting `"search.useGlobalIgnoreFiles"`.
+ */
+ useGlobalIgnoreFiles: boolean;
+ }
+
+ /**
+ * Options to specify the size of the result text preview.
+ * These options don't affect the size of the match itself, just the amount of preview text.
+ */
+ export interface TextSearchPreviewOptions {
+ /**
+ * The maximum number of lines in the preview.
+ * Only search providers that support multiline search will ever return more than one line in the match.
+ */
+ matchLines: number;
+
+ /**
+ * The maximum number of characters included per line.
+ */
+ charsPerLine: number;
+ }
+
+ /**
+ * Options that apply to text search.
+ */
+ export interface TextSearchOptions extends SearchOptions {
+ /**
+ * The maximum number of results to be returned.
+ */
+ maxResults: number;
+
+ /**
+ * Options to specify the size of the result text preview.
+ */
+ previewOptions?: TextSearchPreviewOptions;
+
+ /**
+ * Exclude files larger than `maxFileSize` in bytes.
+ */
+ maxFileSize?: number;
+
+ /**
+ * Interpret files using this encoding.
+ * See the vscode setting `"files.encoding"`
+ */
+ encoding?: string;
+
+ /**
+ * Number of lines of context to include before each match.
+ */
+ beforeContext?: number;
+
+ /**
+ * Number of lines of context to include after each match.
+ */
+ afterContext?: number;
+ }
+
+ /**
+ * Information collected when text search is complete.
+ */
+ export interface TextSearchComplete {
+ /**
+ * Whether the search hit the limit on the maximum number of search results.
+ * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results.
+ * - If exactly that number of matches exist, this should be false.
+ * - If `maxResults` matches are returned and more exist, this should be true.
+ * - If search hits an internal limit which is less than `maxResults`, this should be true.
+ */
+ limitHit?: boolean;
+ }
+
+ /**
+ * A preview of the text result.
+ */
+ export interface TextSearchMatchPreview {
+ /**
+ * The matching lines of text, or a portion of the matching line that contains the match.
+ */
+ text: string;
+
+ /**
+ * The Range within `text` corresponding to the text of the match.
+ * The number of matches must match the TextSearchMatch's range property.
+ */
+ matches: Range | Range[];
+ }
+
+ /**
+ * A match from a text search
+ */
+ export interface TextSearchMatch {
+ /**
+ * The uri for the matching document.
+ */
+ uri: Uri;
+
+ /**
+ * The range of the match within the document, or multiple ranges for multiple matches.
+ */
+ ranges: Range | Range[];
+
+ /**
+ * A preview of the text match.
+ */
+ preview: TextSearchMatchPreview;
+ }
+
+ /**
+ * A line of context surrounding a TextSearchMatch.
+ */
+ export interface TextSearchContext {
+ /**
+ * The uri for the matching document.
+ */
+ uri: Uri;
+
+ /**
+ * One line of text.
+ * previewOptions.charsPerLine applies to this
+ */
+ text: string;
+
+ /**
+ * The line number of this line of context.
+ */
+ lineNumber: number;
+ }
+
+ export type TextSearchResult = TextSearchMatch | TextSearchContext;
+
+ /**
+ * A TextSearchProvider provides search results for text results inside files in the workspace.
+ */
+ export interface TextSearchProvider {
+ /**
+ * Provide results that match the given text pattern.
+ * @param query The parameters for this query.
+ * @param options A set of options to consider while searching.
+ * @param progress A progress callback that must be invoked for all results.
+ * @param token A cancellation token.
+ */
+ provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult;
+ }
+
+ //#endregion
+
+ //#region FileSearchProvider: https://github.com/microsoft/vscode/issues/73524
+
+ /**
+ * The parameters of a query for file search.
+ */
+ export interface FileSearchQuery {
+ /**
+ * The search pattern to match against file paths.
+ */
+ pattern: string;
+ }
+
+ /**
+ * Options that apply to file search.
+ */
+ export interface FileSearchOptions extends SearchOptions {
+ /**
+ * The maximum number of results to be returned.
+ */
+ maxResults?: number;
+
+ /**
+ * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache,
+ * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared.
+ */
+ session?: CancellationToken;
+ }
+
+ /**
+ * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions.
+ *
+ * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for
+ * all files that match the user's query.
+ *
+ * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string,
+ * and in that case, every file in the folder should be returned.
+ */
+ export interface FileSearchProvider {
+ /**
+ * Provide the set of files that match a certain file path pattern.
+ * @param query The parameters for this query.
+ * @param options A set of options to consider while searching files.
+ * @param token A cancellation token.
+ */
+ provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult;
+ }
+
+ export namespace workspace {
+ /**
+ * Register a search provider.
+ *
+ * Only one provider can be registered per scheme.
+ *
+ * @param scheme The provider will be invoked for workspace folders that have this file scheme.
+ * @param provider The provider.
+ * @return A [disposable](#Disposable) that unregisters this provider when being disposed.
+ */
+ export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable;
+
+ /**
+ * Register a text search provider.
+ *
+ * Only one provider can be registered per scheme.
+ *
+ * @param scheme The provider will be invoked for workspace folders that have this file scheme.
+ * @param provider The provider.
+ * @return A [disposable](#Disposable) that unregisters this provider when being disposed.
+ */
+ export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable;
+ }
+
+ //#endregion
+
+ //#region findTextInFiles: https://github.com/microsoft/vscode/issues/59924
+
+ /**
+ * Options that can be set on a findTextInFiles search.
+ */
+ export interface FindTextInFilesOptions {
+ /**
+ * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern
+ * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern)
+ * to restrict the search results to a [workspace folder](#WorkspaceFolder).
+ */
+ include?: GlobPattern;
+
+ /**
+ * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern
+ * will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default excludes will
+ * apply.
+ */
+ exclude?: GlobPattern;
+
+ /**
+ * Whether to use the default and user-configured excludes. Defaults to true.
+ */
+ useDefaultExcludes?: boolean;
+
+ /**
+ * The maximum number of results to search for
+ */
+ maxResults?: number;
+
+ /**
+ * Whether external files that exclude files, like .gitignore, should be respected.
+ * See the vscode setting `"search.useIgnoreFiles"`.
+ */
+ useIgnoreFiles?: boolean;
+
+ /**
+ * Whether global files that exclude files, like .gitignore, should be respected.
+ * See the vscode setting `"search.useGlobalIgnoreFiles"`.
+ */
+ useGlobalIgnoreFiles?: boolean;
+
+ /**
+ * Whether symlinks should be followed while searching.
+ * See the vscode setting `"search.followSymlinks"`.
+ */
+ followSymlinks?: boolean;
+
+ /**
+ * Interpret files using this encoding.
+ * See the vscode setting `"files.encoding"`
+ */
+ encoding?: string;
+
+ /**
+ * Options to specify the size of the result text preview.
+ */
+ previewOptions?: TextSearchPreviewOptions;
+
+ /**
+ * Number of lines of context to include before each match.
+ */
+ beforeContext?: number;
+
+ /**
+ * Number of lines of context to include after each match.
+ */
+ afterContext?: number;
+ }
+
+ export namespace workspace {
+ /**
+ * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace.
+ * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words.
+ * @param callback A callback, called for each result
+ * @param token A token that can be used to signal cancellation to the underlying search engine.
+ * @return A thenable that resolves when the search is complete.
+ */
+ export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable;
+
+ /**
+ * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace.
+ * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words.
+ * @param options An optional set of query options. Include and exclude patterns, maxResults, etc.
+ * @param callback A callback, called for each result
+ * @param token A token that can be used to signal cancellation to the underlying search engine.
+ * @return A thenable that resolves when the search is complete.
+ */
+ export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable;
+ }
+
+ //#endregion
+
+ //#region diff command: https://github.com/microsoft/vscode/issues/84899
+
+ /**
+ * The contiguous set of modified lines in a diff.
+ */
+ export interface LineChange {
+ readonly originalStartLineNumber: number;
+ readonly originalEndLineNumber: number;
+ readonly modifiedStartLineNumber: number;
+ readonly modifiedEndLineNumber: number;
+ }
+
+ export namespace commands {
+
+ /**
+ * Registers a diff information command that can be invoked via a keyboard shortcut,
+ * a menu item, an action, or directly.
+ *
+ * Diff information commands are different from ordinary [commands](#commands.registerCommand) as
+ * they only execute when there is an active diff editor when the command is called, and the diff
+ * information has been computed. Also, the command handler of an editor command has access to
+ * the diff information.
+ *
+ * @param command A unique identifier for the command.
+ * @param callback A command handler function with access to the [diff information](#LineChange).
+ * @param thisArg The `this` context used when invoking the handler function.
+ * @return Disposable which unregisters this command on disposal.
+ */
+ export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable;
+ }
+
+ //#endregion
+
+ //#region file-decorations: https://github.com/microsoft/vscode/issues/54938
+
+ export class Decoration {
+ letter?: string;
+ title?: string;
+ color?: ThemeColor;
+ priority?: number;
+ bubble?: boolean;
+ }
+
+ export interface DecorationProvider {
+ onDidChangeDecorations: Event;
+ provideDecoration(uri: Uri, token: CancellationToken): ProviderResult;
+ }
+
+ export namespace window {
+ export function registerDecorationProvider(provider: DecorationProvider): Disposable;
+ }
+
+ //#endregion
+
+ //#region debug: https://github.com/microsoft/vscode/issues/88230
+
+ /**
+ * A DebugConfigurationProviderTriggerKind specifies when the `provideDebugConfigurations` method of a `DebugConfigurationProvider` is triggered.
+ * Currently there are two situations: to provide the initial debug configurations for a newly created launch.json or
+ * to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command).
+ * A trigger kind is used when registering a `DebugConfigurationProvider` with #debug.registerDebugConfigurationProvider.
+ */
+ export enum DebugConfigurationProviderTriggerKind {
+ /**
+ * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide the initial debug configurations for a newly created launch.json.
+ */
+ Initial = 1,
+ /**
+ * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command).
+ */
+ Dynamic = 2
+ }
+
+ export namespace debug {
+ /**
+ * Register a [debug configuration provider](#DebugConfigurationProvider) for a specific debug type.
+ * The optional [triggerKind](#DebugConfigurationProviderTriggerKind) can be used to specify when the `provideDebugConfigurations` method of the provider is triggered.
+ * Currently two trigger kinds are possible: with the value `Initial` (or if no trigger kind argument is given) the `provideDebugConfigurations` method is used to provide the initial debug configurations to be copied into a newly created launch.json.
+ * With the trigger kind `Dynamic` the `provideDebugConfigurations` method is used to dynamically determine debug configurations to be presented to the user (in addition to the static configurations from the launch.json).
+ * Please note that the `triggerKind` argument only applies to the `provideDebugConfigurations` method: so the `resolveDebugConfiguration` methods are not affected at all.
+ * Registering a single provider with resolve methods for different trigger kinds, results in the same resolve methods called multiple times.
+ * More than one provider can be registered for the same type.
+ *
+ * @param type The debug type for which the provider is registered.
+ * @param provider The [debug configuration provider](#DebugConfigurationProvider) to register.
+ * @param triggerKind The [trigger](#DebugConfigurationProviderTrigger) for which the 'provideDebugConfiguration' method of the provider is registered.
+ * @return A [disposable](#Disposable) that unregisters this provider when being disposed.
+ */
+ export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable;
+ }
+
+ // deprecated debug API
+
+ export interface DebugConfigurationProvider {
+ /**
+ * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead.
+ * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead
+ */
+ debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult;
+ }
+
+ //#endregion
+
+ //#region LogLevel: https://github.com/microsoft/vscode/issues/85992
+
+ /**
+ * @deprecated DO NOT USE, will be removed
+ */
+ export enum LogLevel {
+ Trace = 1,
+ Debug = 2,
+ Info = 3,
+ Warning = 4,
+ Error = 5,
+ Critical = 6,
+ Off = 7
+ }
+
+ export namespace env {
+ /**
+ * @deprecated DO NOT USE, will be removed
+ */
+ export const logLevel: LogLevel;
+
+ /**
+ * @deprecated DO NOT USE, will be removed
+ */
+ export const onDidChangeLogLevel: Event;
+ }
+
+ //#endregion
+
+ //#region Joao: SCM validation
+
+ /**
+ * Represents the validation type of the Source Control input.
+ */
+ export enum SourceControlInputBoxValidationType {
+
+ /**
+ * Something not allowed by the rules of a language or other means.
+ */
+ Error = 0,
+
+ /**
+ * Something suspicious but allowed.
+ */
+ Warning = 1,
+
+ /**
+ * Something to inform about but not a problem.
+ */
+ Information = 2
+ }
+
+ export interface SourceControlInputBoxValidation {
+
+ /**
+ * The validation message to display.
+ */
+ readonly message: string;
+
+ /**
+ * The validation type.
+ */
+ readonly type: SourceControlInputBoxValidationType;
+ }
+
+ /**
+ * Represents the input box in the Source Control viewlet.
+ */
+ export interface SourceControlInputBox {
+
+ /**
+ * A validation function for the input box. It's possible to change
+ * the validation provider simply by setting this property to a different function.
+ */
+ validateInput?(value: string, cursorPosition: number): ProviderResult;
+ }
+
+ //#endregion
+
+ //#region Joao: SCM selected provider
+
+ export interface SourceControl {
+
+ /**
+ * Whether the source control is selected.
+ */
+ readonly selected: boolean;
+
+ /**
+ * An event signaling when the selection state changes.
+ */
+ readonly onDidChangeSelection: Event;
+ }
+
+ //#endregion
+
+ //#region Terminal data write event https://github.com/microsoft/vscode/issues/78502
+
+ export interface TerminalDataWriteEvent {
+ /**
+ * The [terminal](#Terminal) for which the data was written.
+ */
+ readonly terminal: Terminal;
+ /**
+ * The data being written.
+ */
+ readonly data: string;
+ }
+
+ namespace window {
+ /**
+ * An event which fires when the terminal's pty slave pseudo-device is written to. In other
+ * words, this provides access to the raw data stream from the process running within the
+ * terminal, including VT sequences.
+ */
+ export const onDidWriteTerminalData: Event;
+ }
+
+ //#endregion
+
+ //#region Terminal dimensions property and change event https://github.com/microsoft/vscode/issues/55718
+
+ /**
+ * An [event](#Event) which fires when a [Terminal](#Terminal)'s dimensions change.
+ */
+ export interface TerminalDimensionsChangeEvent {
+ /**
+ * The [terminal](#Terminal) for which the dimensions have changed.
+ */
+ readonly terminal: Terminal;
+ /**
+ * The new value for the [terminal's dimensions](#Terminal.dimensions).
+ */
+ readonly dimensions: TerminalDimensions;
+ }
+
+ export namespace window {
+ /**
+ * An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change.
+ */
+ export const onDidChangeTerminalDimensions: Event;
+ }
+
+ export interface Terminal {
+ /**
+ * The current dimensions of the terminal. This will be `undefined` immediately after the
+ * terminal is created as the dimensions are not known until shortly after the terminal is
+ * created.
+ */
+ readonly dimensions: TerminalDimensions | undefined;
+ }
+
+ //#endregion
+
+
+
+ //#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606
+
+ export namespace window {
+ /**
+ * Register a [TerminalLinkHandler](#TerminalLinkHandler) that can be used to intercept and
+ * handle links that are activated within terminals.
+ * @param handler The link handler being registered.
+ * @return A disposable that unregisters the link handler.
+ */
+ export function registerTerminalLinkHandler(handler: TerminalLinkHandler): Disposable;
+ }
+
+ /**
+ * Describes how to handle terminal links.
+ */
+ export interface TerminalLinkHandler {
+ /**
+ * Handles a link that is activated within the terminal.
+ *
+ * @param terminal The terminal the link was activated on.
+ * @param link The text of the link activated.
+ * @return Whether the link was handled, if the link was handled this link will not be
+ * considered by any other extension or by the default built-in link handler.
+ */
+ handleLink(terminal: Terminal, link: string): ProviderResult;
+ }
+
+ //#endregion
+
+ //#region Contribute to terminal environment https://github.com/microsoft/vscode/issues/46696
+
+ export enum EnvironmentVariableMutatorType {
+ /**
+ * Replace the variable's existing value.
+ */
+ Replace = 1,
+ /**
+ * Append to the end of the variable's existing value.
+ */
+ Append = 2,
+ /**
+ * Prepend to the start of the variable's existing value.
+ */
+ Prepend = 3
+ }
+
+ export interface EnvironmentVariableMutator {
+ /**
+ * The type of mutation that will occur to the variable.
+ */
+ readonly type: EnvironmentVariableMutatorType;
+
+ /**
+ * The value to use for the variable.
+ */
+ readonly value: string;
+ }
+
+ /**
+ * A collection of mutations that an extension can apply to a process environment.
+ */
+ export interface EnvironmentVariableCollection {
+ /**
+ * Whether the collection should be cached for the workspace and applied to the terminal
+ * across window reloads. When true the collection will be active immediately such when the
+ * window reloads. Additionally, this API will return the cached version if it exists. The
+ * collection will be invalidated when the extension is uninstalled or when the collection
+ * is cleared. Defaults to true.
+ */
+ persistent: boolean;
+
+ /**
+ * Replace an environment variable with a value.
+ *
+ * Note that an extension can only make a single change to any one variable, so this will
+ * overwrite any previous calls to replace, append or prepend.
+ */
+ replace(variable: string, value: string): void;
+
+ /**
+ * Append a value to an environment variable.
+ *
+ * Note that an extension can only make a single change to any one variable, so this will
+ * overwrite any previous calls to replace, append or prepend.
+ */
+ append(variable: string, value: string): void;
+
+ /**
+ * Prepend a value to an environment variable.
+ *
+ * Note that an extension can only make a single change to any one variable, so this will
+ * overwrite any previous calls to replace, append or prepend.
+ */
+ prepend(variable: string, value: string): void;
+
+ /**
+ * Gets the mutator that this collection applies to a variable, if any.
+ */
+ get(variable: string): EnvironmentVariableMutator | undefined;
+
+ /**
+ * Iterate over each mutator in this collection.
+ */
+ forEach(callback: (variable: string, mutator: EnvironmentVariableMutator, collection: EnvironmentVariableCollection) => any, thisArg?: any): void;
+
+ /**
+ * Deletes this collection's mutator for a variable.
+ */
+ delete(variable: string): void;
+
+ /**
+ * Clears all mutators from this collection.
+ */
+ clear(): void;
+ }
+
+ export interface ExtensionContext {
+ /**
+ * Gets the extension's environment variable collection for this workspace, enabling changes
+ * to be applied to terminal environment variables.
+ */
+ readonly environmentVariableCollection: EnvironmentVariableCollection;
+ }
+
+ //#endregion
+
+ //#region Joh -> exclusive document filters
+
+ export interface DocumentFilter {
+ exclusive?: boolean;
+ }
+
+ //#endregion
+
+ //#region Alex - OnEnter enhancement
+ export interface OnEnterRule {
+ /**
+ * This rule will only execute if the text above the this line matches this regular expression.
+ */
+ oneLineAboveText?: RegExp;
+ }
+ //#endregion
+
+ //#region Tree View: https://github.com/microsoft/vscode/issues/61313
+ /**
+ * Label describing the [Tree item](#TreeItem)
+ */
+ export interface TreeItemLabel {
+
+ /**
+ * A human-readable string describing the [Tree item](#TreeItem).
+ */
+ label: string;
+
+ /**
+ * Ranges in the label to highlight. A range is defined as a tuple of two number where the
+ * first is the inclusive start index and the second the exclusive end index
+ */
+ highlights?: [number, number][];
+
+ }
+
+ export class TreeItem2 extends TreeItem {
+ /**
+ * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri).
+ */
+ label?: string | TreeItemLabel | /* for compilation */ any;
+
+ /**
+ * @param label Label describing this item
+ * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None)
+ */
+ constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState);
+ }
+ //#endregion
+
+ //#region CustomExecution: https://github.com/microsoft/vscode/issues/81007
+ /**
+ * A task to execute
+ */
+ export class Task2 extends Task {
+ detail?: string;
+ }
+
+ export class CustomExecution2 extends CustomExecution {
+ /**
+ * Constructs a CustomExecution task object. The callback will be executed the task is run, at which point the
+ * extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until
+ * [Pseudoterminal.open](#Pseudoterminal.open) is called. Task cancellation should be handled using
+ * [Pseudoterminal.close](#Pseudoterminal.close). When the task is complete fire
+ * [Pseudoterminal.onDidClose](#Pseudoterminal.onDidClose).
+ * @param callback The callback that will be called when the task is started by a user.
+ */
+ constructor(callback: (resolvedDefinition?: TaskDefinition) => Thenable);
+ }
+ //#endregion
+
+ //#region Task presentation group: https://github.com/microsoft/vscode/issues/47265
+ export interface TaskPresentationOptions {
+ /**
+ * Controls whether the task is executed in a specific terminal group using split panes.
+ */
+ group?: string;
+ }
+ //#endregion
+
+ //#region Status bar item with ID and Name: https://github.com/microsoft/vscode/issues/74972
+
+ export namespace window {
+
+ /**
+ * Options to configure the status bar item.
+ */
+ export interface StatusBarItemOptions {
+
+ /**
+ * A unique identifier of the status bar item. The identifier
+ * is for example used to allow a user to show or hide the
+ * status bar item in the UI.
+ */
+ id: string;
+
+ /**
+ * A human readable name of the status bar item. The name is
+ * for example used as a label in the UI to show or hide the
+ * status bar item.
+ */
+ name: string;
+
+ /**
+ * The alignment of the status bar item.
+ */
+ alignment?: StatusBarAlignment;
+
+ /**
+ * The priority of the status bar item. Higher value means the item should
+ * be shown more to the left.
+ */
+ priority?: number;
+ }
+
+ /**
+ * Creates a status bar [item](#StatusBarItem).
+ *
+ * @param options The options of the item. If not provided, some default values
+ * will be assumed. For example, the `StatusBarItemOptions.id` will be the id
+ * of the extension and the `StatusBarItemOptions.name` will be the extension name.
+ * @return A new status bar item.
+ */
+ export function createStatusBarItem(options?: StatusBarItemOptions): StatusBarItem;
+ }
+
+ //#endregion
+
+ //#region OnTypeRename: https://github.com/microsoft/vscode/issues/88424
+
+ /**
+ * The rename provider interface defines the contract between extensions and
+ * the live-rename feature.
+ */
+ export interface OnTypeRenameProvider {
+ /**
+ * Provide a list of ranges that can be live renamed together.
+ *
+ * @param document The document in which the command was invoked.
+ * @param position The position at which the command was invoked.
+ * @param token A cancellation token.
+ * @return A list of ranges that can be live-renamed togehter. The ranges must have
+ * identical length and contain identical text content. The ranges cannot overlap.
+ */
+ provideOnTypeRenameRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult;
+ }
+
+ namespace languages {
+ /**
+ * Register a rename provider that works on type.
+ *
+ * Multiple providers can be registered for a language. In that case providers are sorted
+ * by their [score](#languages.match) and the best-matching provider is used. Failure
+ * of the selected provider will cause a failure of the whole operation.
+ *
+ * @param selector A selector that defines the documents this provider is applicable to.
+ * @param provider An on type rename provider.
+ * @param stopPattern Stop on type renaming when input text matches the regular expression. Defaults to `^\s`.
+ * @return A [disposable](#Disposable) that unregisters this provider when being disposed.
+ */
+ export function registerOnTypeRenameProvider(selector: DocumentSelector, provider: OnTypeRenameProvider, stopPattern?: RegExp): Disposable;
+ }
+
+ //#endregion
+
+ //#region Custom editor https://github.com/microsoft/vscode/issues/77131
+
+ /**
+ * Represents a custom document used by a [`CustomEditorProvider`](#CustomEditorProvider).
+ *
+ * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is
+ * managed by VS Code. When no more references remain to a `CustomDocument`, it is disposed of.
+ */
+ interface CustomDocument {
+ /**
+ * The associated uri for this document.
+ */
+ readonly uri: Uri;
+
+ /**
+ * Dispose of the custom document.
+ *
+ * This is invoked by VS Code when there are no more references to a given `CustomDocument` (for example when
+ * all editors associated with the document have been closed.)
+ */
+ dispose(): void;
+ }
+
+ /**
+ * Event triggered by extensions to signal to VS Code that an edit has occurred on an [`CustomDocument`](#CustomDocument).
+ *
+ * @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument).
+ */
+ interface CustomDocumentEditEvent {
+
+ /**
+ * The document that the edit is for.
+ */
+ readonly document: T;
+
+ /**
+ * Undo the edit operation.
+ *
+ * This is invoked by VS Code when the user undoes this edit. To implement `undo`, your
+ * extension should restore the document and editor to the state they were in just before this
+ * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
+ */
+ undo(): Thenable | void;
+
+ /**
+ * Redo the edit operation.
+ *
+ * This is invoked by VS Code when the user redoes this edit. To implement `redo`, your
+ * extension should restore the document and editor to the state they were in just after this
+ * edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
+ */
+ redo(): Thenable | void;
+
+ /**
+ * Display name describing the edit.
+ *
+ * This is shown in the UI to users.
+ */
+ readonly label?: string;
+ }
+
+ /**
+ * Event triggered by extensions to signal to VS Code that the content of a [`CustomDocument`](#CustomDocument)
+ * has changed.
+ *
+ * @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument).
+ */
+ interface CustomDocumentContentChangeEvent {
+ /**
+ * The document that the change is for.
+ */
+ readonly document: T;
+ }
+
+ /**
+ * A backup for an [`CustomDocument`](#CustomDocument).
+ */
+ interface CustomDocumentBackup {
+ /**
+ * Unique identifier for the backup.
+ *
+ * This id is passed back to your extension in `openCustomDocument` when opening a custom editor from a backup.
+ */
+ readonly id: string;
+
+ /**
+ * Delete the current backup.
+ *
+ * This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup
+ * is made or when the file is saved.
+ */
+ delete(): void;
+ }
+
+ /**
+ * Additional information used to implement [`CustomEditableDocument.backup`](#CustomEditableDocument.backup).
+ */
+ interface CustomDocumentBackupContext {
+ /**
+ * Suggested file location to write the new backup.
+ *
+ * Note that your extension is free to ignore this and use its own strategy for backup.
+ *
+ * For editors for workspace resource, this destination will be in the workspace storage. The path may not
+ */
+ readonly destination: Uri;
+ }
+
+ /**
+ * Additional information about the opening custom document.
+ */
+ interface CustomDocumentOpenContext {
+ /**
+ * The id of the backup to restore the document from or `undefined` if there is no backup.
+ *
+ * If this is provided, your extension should restore the editor from the backup instead of reading the file
+ * the user's workspace.
+ */
+ readonly backupId?: string;
+ }
+
+ /**
+ * Provider for readonly custom editors that use a custom document model.
+ *
+ * Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
+ *
+ * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
+ * text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
+ *
+ * @param T Type of the custom document returned by this provider.
+ */
+ export interface CustomReadonlyEditorProvider {
+
+ /**
+ * Create a new document for a given resource.
+ *
+ * `openCustomDocument` is called when the first editor for a given resource is opened, and the resolve document
+ * is passed to `resolveCustomEditor`. The resolved `CustomDocument` is re-used for subsequent editor opens.
+ * If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at
+ * this point will trigger another call to `openCustomDocument`.
+ *
+ * @param uri Uri of the document to open.
+ * @param openContext Additional information about the opening custom document.
+ * @param token A cancellation token that indicates the result is no longer needed.
+ *
+ * @return The custom document.
+ */
+ openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable | T;
+
+ /**
+ * Resolve a custom editor for a given resource.
+ *
+ * This is called whenever the user opens a new editor for this `CustomEditorProvider`.
+ *
+ * To resolve a custom editor, the provider must fill in its initial html content and hook up all
+ * the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later,
+ * for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details.
+ *
+ * @param document Document for the resource being resolved.
+ * @param webviewPanel Webview to resolve.
+ * @param token A cancellation token that indicates the result is no longer needed.
+ *
+ * @return Optional thenable indicating that the custom editor has been resolved.
+ */
+ resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void;
+ }
+
+ /**
+ * Provider for editiable custom editors that use a custom document model.
+ *
+ * Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
+ * This gives extensions full control over actions such as edit, save, and backup.
+ *
+ * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
+ * text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
+ *
+ * @param T Type of the custom document returned by this provider.
+ */
+ export interface CustomEditorProvider extends CustomReadonlyEditorProvider {
+ /**
+ * Signal that an edit has occurred inside a custom editor.
+ *
+ * This event must be fired by your extension whenever an edit happens in a custom editor. An edit can be
+ * anything from changing some text, to cropping an image, to reordering a list. Your extension is free to
+ * define what an edit is and what data is stored on each edit.
+ *
+ * Firing `onDidChange` causes VS Code to mark the editors as being dirty. This is cleared when the user either
+ * saves or reverts the file.
+ *
+ * Editors that support undo/redo must fire a `CustomDocumentEditEvent` whenever an edit happens. This allows
+ * users to undo and redo the edit using VS Code's standard VS Code keyboard shortcuts. VS Code will also mark
+ * the editor as no longer being dirty if the user undoes all edits to the last saved state.
+ *
+ * Editors that support editing but cannot use VS Code's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`.
+ * The only way for a user to clear the dirty state of an editor that does not support undo/redo is to either
+ * `save` or `revert` the file.
+ *
+ * An editor should only ever fire `CustomDocumentEditEvent` events, or only ever fire `CustomDocumentContentChangeEvent` events.
+ */
+ readonly onDidChangeCustomDocument: Event> | Event>;
+
+ /**
+ * Save a custom document.
+ *
+ * This method is invoked by VS Code when the user saves a custom editor. This can happen when the user
+ * triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled.
+ *
+ * To implement `save`, the implementer must persist the custom editor. This usually means writing the
+ * file data for the custom document to disk. After `save` completes, any associated editor instances will
+ * no longer be marked as dirty.
+ *
+ * @param document Document to save.
+ * @param cancellation Token that signals the save is no longer required (for example, if another save was triggered).
+ *
+ * @return Thenable signaling that saving has completed.
+ */
+ saveCustomDocument(document: T, cancellation: CancellationToken): Thenable;
+
+ /**
+ * Save a custom document to a different location.
+ *
+ * This method is invoked by VS Code when the user triggers 'save as' on a custom editor. The implementer must
+ * persist the custom editor to `destination`.
+ *
+ * When the user accepts save as, the current editor is be replaced by an non-dirty editor for the newly saved file.
+ *
+ * @param document Document to save.
+ * @param destination Location to save to.
+ * @param cancellation Token that signals the save is no longer required.
+ *
+ * @return Thenable signaling that saving has completed.
+ */
+ saveCustomDocumentAs(document: T, destination: Uri, cancellation: CancellationToken): Thenable;
+
+ /**
+ * Revert a custom document to its last saved state.
+ *
+ * This method is invoked by VS Code when the user triggers `File: Revert File` in a custom editor. (Note that
+ * this is only used using VS Code's `File: Revert File` command and not on a `git revert` of the file).
+ *
+ * To implement `revert`, the implementer must make sure all editor instances (webviews) for `document`
+ * are displaying the document in the same state is saved in. This usually means reloading the file from the
+ * workspace.
+ *
+ * @param document Document to revert.
+ * @param cancellation Token that signals the revert is no longer required.
+ *
+ * @return Thenable signaling that the change has completed.
+ */
+ revertCustomDocument(document: T, cancellation: CancellationToken): Thenable;
+
+ /**
+ * Back up a dirty custom document.
+ *
+ * Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in
+ * its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in
+ * the `ExtensionContext.storagePath`. When VS Code reloads and your custom editor is opened for a resource,
+ * your extension should first check to see if any backups exist for the resource. If there is a backup, your
+ * extension should load the file contents from there instead of from the resource in the workspace.
+ *
+ * `backup` is triggered whenever an edit it made. Calls to `backup` are debounced so that if multiple edits are
+ * made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when
+ * `auto save` is enabled (since auto save already persists resource ).
+ *
+ * @param document Document to backup.
+ * @param context Information that can be used to backup the document.
+ * @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
+ * extension to decided how to respond to cancellation. If for example your extension is backing up a large file
+ * in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
+ * than cancelling it to ensure that VS Code has some valid backup.
+ */
+ backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable;
+ }
+
+ namespace window {
+ /**
+ * Temporary overload for `registerCustomEditorProvider` that takes a `CustomEditorProvider`.
+ */
+ export function registerCustomEditorProvider2(
+ viewType: string,
+ provider: CustomReadonlyEditorProvider | CustomEditorProvider,
+ options?: {
+ readonly webviewOptions?: WebviewPanelOptions;
+
+ /**
+ * Only applies to `CustomReadonlyEditorProvider | CustomEditorProvider`.
+ *
+ * Indicates that the provider allows multiple editor instances to be open at the same time for
+ * the same resource.
+ *
+ * If not set, VS Code only allows one editor instance to be open at a time for each resource. If the
+ * user tries to open a second editor instance for the resource, the first one is instead moved to where
+ * the second one was to be opened.
+ *
+ * When set, users can split and create copies of the custom editor. The custom editor must make sure it
+ * can properly synchronize the states of all editor instances for a resource so that they are consistent.
+ */
+ readonly supportsMultipleEditorsPerDocument?: boolean;
+ }
+ ): Disposable;
+ }
+
+ // #endregion
+
+ //#region Custom editor move https://github.com/microsoft/vscode/issues/86146
+
+ // TODO: Also for custom editor
+
+ export interface CustomTextEditorProvider {
+
+
+ /**
+ * Handle when the underlying resource for a custom editor is renamed.
+ *
+ * This allows the webview for the editor be preserved throughout the rename. If this method is not implemented,
+ * VS Code will destory the previous custom editor and create a replacement one.
+ *
+ * @param newDocument New text document to use for the custom editor.
+ * @param existingWebviewPanel Webview panel for the custom editor.
+ * @param token A cancellation token that indicates the result is no longer needed.
+ *
+ * @return Thenable indicating that the webview editor has been moved.
+ */
+ moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel, token: CancellationToken): Thenable;
+ }
+
+ //#endregion
+
+
+ //#region allow QuickPicks to skip sorting: https://github.com/microsoft/vscode/issues/73904
+
+ export interface QuickPick extends QuickInput {
+ /**
+ * An optional flag to sort the final results by index of first query match in label. Defaults to true.
+ */
+ sortByLabel: boolean;
+ }
+
+ //#endregion
+
+ //#region Allow theme icons in hovers: https://github.com/microsoft/vscode/issues/84695
+
+ export interface MarkdownString {
+
+ /**
+ * Indicates that this markdown string can contain [ThemeIcons](#ThemeIcon), e.g. `$(zap)`.
+ */
+ readonly supportThemeIcons?: boolean;
+ }
+
+ //#endregion
+
+ //#region Peng: Notebook
+
+ export enum CellKind {
+ Markdown = 1,
+ Code = 2
+ }
+
+ export enum CellOutputKind {
+ Text = 1,
+ Error = 2,
+ Rich = 3
+ }
+
+ export interface CellStreamOutput {
+ outputKind: CellOutputKind.Text;
+ text: string;
+ }
+
+ export interface CellErrorOutput {
+ outputKind: CellOutputKind.Error;
+ /**
+ * Exception Name
+ */
+ ename: string;
+ /**
+ * Exception Value
+ */
+ evalue: string;
+ /**
+ * Exception call stack
+ */
+ traceback: string[];
+ }
+
+ export interface CellDisplayOutput {
+ outputKind: CellOutputKind.Rich;
+ /**
+ * { mime_type: value }
+ *
+ * Example:
+ * ```json
+ * {
+ * "outputKind": vscode.CellOutputKind.Rich,
+ * "data": {
+ * "text/html": [
+ * "
Hello
"
+ * ],
+ * "text/plain": [
+ * ""
+ * ]
+ * }
+ * }
+ */
+ data: { [key: string]: any };
+ }
+
+ export type CellOutput = CellStreamOutput | CellErrorOutput | CellDisplayOutput;
+
+ export enum NotebookCellRunState {
+ Running = 1,
+ Idle = 2,
+ Success = 3,
+ Error = 4
+ }
+
+ export interface NotebookCellMetadata {
+ /**
+ * Controls if the content of a cell is editable or not.
+ */
+ editable?: boolean;
+
+ /**
+ * Controls if the cell is executable.
+ * This metadata is ignored for markdown cell.
+ */
+ runnable?: boolean;
+
+ /**
+ * The order in which this cell was executed.
+ */
+ executionOrder?: number;
+
+ /**
+ * A status message to be shown in the cell's status bar
+ */
+ statusMessage?: string;
+
+ /**
+ * The cell's current run state
+ */
+ runState?: NotebookCellRunState;
+ }
+
+ export interface NotebookCell {
+ readonly uri: Uri;
+ readonly cellKind: CellKind;
+ readonly document: TextDocument;
+ // API remove `source` or doc it as shorthand for document.getText()
+ readonly source: string;
+ language: string;
+ outputs: CellOutput[];
+ metadata: NotebookCellMetadata;
+ }
+
+ export interface NotebookDocumentMetadata {
+ /**
+ * Controls if users can add or delete cells
+ * Defaults to true
+ */
+ editable?: boolean;
+
+ /**
+ * Controls whether the full notebook can be run at once.
+ * Defaults to true
+ */
+ runnable?: boolean;
+
+ /**
+ * Default value for [cell editable metadata](#NotebookCellMetadata.editable).
+ * Defaults to true.
+ */
+ cellEditable?: boolean;
+
+ /**
+ * Default value for [cell runnable metadata](#NotebookCellMetadata.runnable).
+ * Defaults to true.
+ */
+ cellRunnable?: boolean;
+
+ /**
+ * Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed.
+ * Defaults to true.
+ */
+ hasExecutionOrder?: boolean;
+ }
+
+ export interface NotebookDocument {
+ readonly uri: Uri;
+ readonly fileName: string;
+ readonly isDirty: boolean;
+ readonly cells: NotebookCell[];
+ languages: string[];
+ displayOrder?: GlobPattern[];
+ metadata: NotebookDocumentMetadata;
+ }
+
+ export interface NotebookConcatTextDocument {
+ isClosed: boolean;
+ dispose(): void;
+ onDidChange: Event;
+ version: number;
+ getText(): string;
+ getText(range: Range): string;
+ offsetAt(position: Position): number;
+ positionAt(offset: number): Position;
+ locationAt(positionOrRange: Position | Range): Location;
+ positionAt(location: Location): Position;
+ }
+
+ export interface NotebookEditorCellEdit {
+ insert(index: number, content: string | string[], language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void;
+ delete(index: number): void;
+ }
+
+ export interface NotebookEditor {
+ /**
+ * The document associated with this notebook editor.
+ */
+ readonly document: NotebookDocument;
+
+ /**
+ * The primary selected cell on this notebook editor.
+ */
+ readonly selection?: NotebookCell;
+ viewColumn?: ViewColumn;
+ /**
+ * Fired when the output hosting webview posts a message.
+ */
+ readonly onDidReceiveMessage: Event;
+ /**
+ * Post a message to the output hosting webview.
+ *
+ * Messages are only delivered if the editor is live.
+ *
+ * @param message Body of the message. This must be a string or other json serilizable object.
+ */
+ postMessage(message: any): Thenable;
+
+ edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable;
+ }
+
+ export interface NotebookProvider {
+ resolveNotebook(editor: NotebookEditor): Promise;
+ executeCell(document: NotebookDocument, cell: NotebookCell | undefined, token: CancellationToken): Promise;
+ save(document: NotebookDocument): Promise;
+ }
+
+ export interface NotebookOutputSelector {
+ type: string;
+ subTypes?: string[];
+ }
+
+ export interface NotebookOutputRenderer {
+ /**
+ *
+ * @returns HTML fragment. We can probably return `CellOutput` instead of string ?
+ *
+ */
+ render(document: NotebookDocument, output: CellDisplayOutput, mimeType: string): string;
+ preloads?: Uri[];
+ }
+
+ export interface NotebookDocumentChangeEvent {
+
+ /**
+ * The affected document.
+ */
+ readonly document: NotebookDocument;
+
+ /**
+ * An array of content changes.
+ */
+ // readonly contentChanges: ReadonlyArray;
+ }
+
+ export namespace notebook {
+ export function registerNotebookProvider(
+ notebookType: string,
+ provider: NotebookProvider
+ ): Disposable;
+
+ export function registerNotebookOutputRenderer(type: string, outputSelector: NotebookOutputSelector, renderer: NotebookOutputRenderer): Disposable;
+
+ // remove activeNotebookDocument, now that there is activeNotebookEditor.document
+ export let activeNotebookDocument: NotebookDocument | undefined;
+
+ export let activeNotebookEditor: NotebookEditor | undefined;
+
+ export const onDidChangeNotebookDocument: Event;
+
+ /**
+ * Create a document that is the concatenation of all notebook cells. By default all code-cells are included
+ * but a selector can be provided to narrow to down the set of cells.
+ *
+ * @param notebook
+ * @param selector
+ */
+ export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument;
+ }
+
+ //#endregion
+
+ //#region https://github.com/microsoft/vscode/issues/39441
+
+ export interface CompletionItem {
+ /**
+ * Will be merged into CompletionItem#label
+ */
+ label2?: CompletionItemLabel;
+ }
+
+ export interface CompletionItemLabel {
+ /**
+ * The function or variable. Rendered leftmost.
+ */
+ name: string;
+
+ /**
+ * The parameters without the return type. Render after `name`.
+ */
+ parameters?: string;
+
+ /**
+ * The fully qualified name, like package name or file path. Rendered after `signature`.
+ */
+ qualifier?: string;
+
+ /**
+ * The return-type of a function or type of a property/variable. Rendered rightmost.
+ */
+ type?: string;
+ }
+
+ //#endregion
+
+
+ //#region eamodio - timeline: https://github.com/microsoft/vscode/issues/84297
+
+ export class TimelineItem {
+ /**
+ * A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred.
+ */
+ timestamp: number;
+
+ /**
+ * A human-readable string describing the timeline item.
+ */
+ label: string;
+
+ /**
+ * Optional id for the timeline item. It must be unique across all the timeline items provided by this source.
+ *
+ * If not provided, an id is generated using the timeline item's timestamp.
+ */
+ id?: string;
+
+ /**
+ * The icon path or [ThemeIcon](#ThemeIcon) for the timeline item.
+ */
+ iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;
+
+ /**
+ * A human readable string describing less prominent details of the timeline item.
+ */
+ description?: string;
+
+ /**
+ * The tooltip text when you hover over the timeline item.
+ */
+ detail?: string;
+
+ /**
+ * The [command](#Command) that should be executed when the timeline item is selected.
+ */
+ command?: Command;
+
+ /**
+ * Context value of the timeline item. This can be used to contribute specific actions to the item.
+ * For example, a timeline item is given a context value as `commit`. When contributing actions to `timeline/item/context`
+ * using `menus` extension point, you can specify context value for key `timelineItem` in `when` expression like `timelineItem == commit`.
+ * ```
+ * "contributes": {
+ * "menus": {
+ * "timeline/item/context": [
+ * {
+ * "command": "extension.copyCommitId",
+ * "when": "timelineItem == commit"
+ * }
+ * ]
+ * }
+ * }
+ * ```
+ * This will show the `extension.copyCommitId` action only for items where `contextValue` is `commit`.
+ */
+ contextValue?: string;
+
+ /**
+ * @param label A human-readable string describing the timeline item
+ * @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred
+ */
+ constructor(label: string, timestamp: number);
+ }
+
+ export interface TimelineChangeEvent {
+ /**
+ * The [uri](#Uri) of the resource for which the timeline changed.
+ */
+ uri: Uri;
+
+ /**
+ * A flag which indicates whether the entire timeline should be reset.
+ */
+ reset?: boolean;
+ }
+
+ export interface Timeline {
+ readonly paging?: {
+ /**
+ * A provider-defined cursor specifying the starting point of timeline items which are after the ones returned.
+ * Use `undefined` to signal that there are no more items to be returned.
+ */
+ readonly cursor: string | undefined;
+ }
+
+ /**
+ * An array of [timeline items](#TimelineItem).
+ */
+ readonly items: readonly TimelineItem[];
+ }
+
+ export interface TimelineOptions {
+ /**
+ * A provider-defined cursor specifying the starting point of the timeline items that should be returned.
+ */
+ cursor?: string;
+
+ /**
+ * An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned.
+ * If `undefined` all timeline items should be returned.
+ */
+ limit?: number | { timestamp: number; id?: string };
+ }
+
+ export interface TimelineProvider {
+ /**
+ * An optional event to signal that the timeline for a source has changed.
+ * To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`.
+ */
+ onDidChange?: Event;
+
+ /**
+ * An identifier of the source of the timeline items. This can be used to filter sources.
+ */
+ readonly id: string;
+
+ /**
+ * A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources.
+ */
+ readonly label: string;
+
+ /**
+ * Provide [timeline items](#TimelineItem) for a [Uri](#Uri).
+ *
+ * @param uri The [uri](#Uri) of the file to provide the timeline for.
+ * @param options A set of options to determine how results should be returned.
+ * @param token A cancellation token.
+ * @return The [timeline result](#TimelineResult) or a thenable that resolves to such. The lack of a result
+ * can be signaled by returning `undefined`, `null`, or an empty array.
+ */
+ provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult;
+ }
+
+ export namespace workspace {
+ /**
+ * Register a timeline provider.
+ *
+ * Multiple providers can be registered. In that case, providers are asked in
+ * parallel and the results are merged. A failing provider (rejected promise or exception) will
+ * not cause a failure of the whole operation.
+ *
+ * @param scheme A scheme or schemes that defines which documents this provider is applicable to. Can be `*` to target all documents.
+ * @param provider A timeline provider.
+ * @return A [disposable](#Disposable) that unregisters this provider when being disposed.
+ */
+ export function registerTimelineProvider(scheme: string | string[], provider: TimelineProvider): Disposable;
+ }
+
+ //#endregion
+
+ //#region https://github.com/microsoft/vscode/issues/86788
+
+ export interface CodeActionProviderMetadata {
+ /**
+ * Static documentation for a class of code actions.
+ *
+ * The documentation is shown in the code actions menu if either:
+ *
+ * - Code actions of `kind` are requested by VS Code. In this case, VS Code will show the documentation that
+ * most closely matches the requested code action kind. For example, if a provider has documentation for
+ * both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`,
+ * VS Code will use the documentation for `RefactorExtract` intead of the documentation for `Refactor`.
+ *
+ * - Any code actions of `kind` are returned by the provider.
+ */
+ readonly documentation?: ReadonlyArray<{ readonly kind: CodeActionKind, readonly command: Command }>;
+ }
+
+ //#endregion
+
+ //#region Dialog title: https://github.com/microsoft/vscode/issues/82871
+
+ /**
+ * Options to configure the behaviour of a file open dialog.
+ *
+ * * Note 1: A dialog can select files, folders, or both. This is not true for Windows
+ * which enforces to open either files or folder, but *not both*.
+ * * Note 2: Explicitly setting `canSelectFiles` and `canSelectFolders` to `false` is futile
+ * and the editor then silently adjusts the options to select files.
+ */
+ export interface OpenDialogOptions {
+ /**
+ * Dialog title.
+ *
+ * Depending on the underlying operating system this parameter might be ignored, since some
+ * systems do not present title on open dialogs.
+ */
+ title?: string;
+ }
+
+ /**
+ * Options to configure the behaviour of a file save dialog.
+ */
+ export interface SaveDialogOptions {
+ /**
+ * Dialog title.
+ *
+ * Depending on the underlying operating system this parameter might be ignored, since some
+ * systems do not present title on save dialogs.
+ */
+ title?: string;
+ }
+
+ //#endregion
+
+ //#region Comment
+ export interface CommentOptions {
+ /**
+ * An optional string to show on the comment input box when it's collapsed.
+ */
+ prompt?: string;
+
+ /**
+ * An optional string to show as placeholder in the comment input box when it's focused.
+ */
+ placeHolder?: string;
+ }
+
+ export interface CommentController {
+ /**
+ * Comment controller options
+ */
+ options?: CommentOptions;
+ }
+
+ //#endregion
+}