From 2f83557a56c37a5e48943ea0201e1729708690b6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 4 Nov 2022 14:27:02 -0700 Subject: [PATCH] Run format on repo --- .editorconfig | 3 + .../src/authProvider.ts | 274 +- .../src/extension.ts | 106 +- basic-multi-root-sample/src/extension.ts | 144 +- .../src/FoodPyramidHierarchyProvider.ts | 250 +- call-hierarchy-sample/src/extension.ts | 50 +- call-hierarchy-sample/src/model.ts | 176 +- code-actions-sample/src/diagnostics.ts | 132 +- code-actions-sample/src/extension.ts | 214 +- codelens-sample/src/CodelensProvider.ts | 110 +- codelens-sample/src/extension.ts | 70 +- comment-sample/src/extension.ts | 288 +- completions-sample/src/extension.ts | 148 +- configuration-sample/src/extension.ts | 448 +- contentprovider-sample/src/extension.ts | 62 +- contentprovider-sample/src/provider.ts | 198 +- .../src/referencesDocument.ts | 226 +- custom-editor-sample/src/catScratchEditor.ts | 410 +- custom-editor-sample/src/dispose.ts | 72 +- custom-editor-sample/src/extension.ts | 18 +- custom-editor-sample/src/pawDrawEditor.ts | 912 +- custom-editor-sample/src/util.ts | 16 +- .../src/extension.ts | 66 +- document-editing-sample/src/extension.ts | 46 +- document-paste/src/extension.ts | 114 +- .../src/vscode.proposed.documentPaste.d.ts | 140 +- drop-on-document/src/extension.ts | 210 +- extension-terminal-sample/src/extension.ts | 114 +- fsconsumer-sample/src/extension.ts | 184 +- fsprovider-sample/src/extension.ts | 168 +- fsprovider-sample/src/fileSystemProvider.ts | 454 +- getting-started-sample/src/extension.ts | 60 +- .../src/credentials.ts | 124 +- github-authentication-sample/src/extension.ts | 50 +- helloworld-sample/src/extension.ts | 46 +- helloworld-test-sample/src/extension.ts | 46 +- helloworld-test-sample/src/test/runTest.ts | 46 +- .../src/test/suite/extension.test.ts | 30 +- .../src/test/suite/index.ts | 76 +- helloworld-web-sample/src/web/extension.ts | 54 +- .../src/web/test/suite/extension.test.ts | 30 +- .../src/web/test/suite/index.ts | 60 +- inline-completions/src/extension.ts | 104 +- inline-completions/vscode.d.ts | 33114 ++++++++-------- l10n-sample/src/cli.ts | 20 +- l10n-sample/src/command/sayBye.ts | 20 +- l10n-sample/src/extension.ts | 80 +- nodefs-provider-sample/src/extension.ts | 594 +- .../src/emoji.ts | 45 +- notebook-renderer-sample/src/css.d.ts | 8 +- notebook-renderer-sample/src/index.ts | 86 +- notebook-renderer-sample/src/render.ts | 120 +- progress-sample/src/extension.ts | 84 +- proposed-api-sample/src/extension.ts | 32 +- quickinput-sample/src/basicInput.ts | 68 +- quickinput-sample/src/extension.ts | 60 +- quickinput-sample/src/multiStepInput.ts | 612 +- quickinput-sample/src/quickOpen.ts | 224 +- semantic-tokens-sample/src/extension.ts | 206 +- source-control-sample/src/afs.ts | 126 +- source-control-sample/src/extension.ts | 698 +- .../src/fiddleConfiguration.ts | 24 +- .../src/fiddleDocumentContentProvider.ts | 84 +- source-control-sample/src/fiddleRepository.ts | 328 +- .../src/fiddleSourceControl.ts | 708 +- source-control-sample/src/util.ts | 20 +- statusbar-sample/src/extension.ts | 100 +- .../src/customTaskProvider.ts | 270 +- task-provider-sample/src/extension.ts | 2 +- task-provider-sample/src/rakeTaskProvider.ts | 326 +- terminal-sample/src/extension.ts | 470 +- test-provider-sample/src/extension.ts | 372 +- test-provider-sample/src/parser.ts | 58 +- test-provider-sample/src/testTree.ts | 256 +- tree-view-sample/src/extension.ts | 106 +- tree-view-sample/src/fileExplorer.ts | 614 +- tree-view-sample/src/ftpExplorer.ts | 370 +- tree-view-sample/src/jsftp.d.ts | 92 +- tree-view-sample/src/jsonOutline.ts | 394 +- tree-view-sample/src/nodeDependencies.ts | 208 +- tree-view-sample/src/testViewDragAndDrop.ts | 424 +- uri-handler-sample/src/extension.ts | 70 +- vim-sample/src/common.ts | 102 +- vim-sample/src/controller.ts | 606 +- vim-sample/src/extension.ts | 14 +- vim-sample/src/mappings.ts | 464 +- vim-sample/src/motions.ts | 864 +- vim-sample/src/operators.ts | 686 +- vim-sample/src/words.ts | 208 +- virtual-document-sample/src/cowsay.d.ts | 28 +- virtual-document-sample/src/extension.ts | 100 +- webpack-sample/src/extension.ts | 32 +- webpack-sample/src/math.ts | 24 +- webview-codicons-sample/src/extension.ts | 770 +- webview-sample/src/extension.ts | 444 +- webview-view-sample/src/extension.ts | 248 +- welcome-view-content-sample/src/extension.ts | 28 +- 97 files changed, 26331 insertions(+), 26329 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..9fa985cd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +[*.ts] +indent_style = tab +tab_width = 4 \ No newline at end of file diff --git a/authenticationprovider-sample/src/authProvider.ts b/authenticationprovider-sample/src/authProvider.ts index bf35c6c7..d70f810f 100644 --- a/authenticationprovider-sample/src/authProvider.ts +++ b/authenticationprovider-sample/src/authProvider.ts @@ -1,137 +1,137 @@ -import { - authentication, - AuthenticationProvider, - AuthenticationProviderAuthenticationSessionsChangeEvent, - AuthenticationSession, - Disposable, - Event, - EventEmitter, - SecretStorage, - window, -} from 'vscode'; - -class AzureDevOpsPatSession implements AuthenticationSession { - // We don't know the user's account name, so we'll just use a constant - readonly account = { id: AzureDevOpsAuthenticationProvider.id, label: 'Personal Access Token' }; - // This id isn't used for anything in this example, so we set it to a constant - readonly id = AzureDevOpsAuthenticationProvider.id; - // We don't know what scopes the PAT has, so we have an empty array here. - readonly scopes = []; - - /** - * - * @param accessToken The personal access token to use for authentication - */ - constructor(public readonly accessToken: string) {} -} - -export class AzureDevOpsAuthenticationProvider implements AuthenticationProvider, Disposable { - static id = 'AzureDevOpsPAT'; - private static secretKey = 'AzureDevOpsPAT'; - - // this property is used to determine if the token has been changed in another window of VS Code. - // It is used in the checkForUpdates function. - private currentToken: Promise | undefined; - private initializedDisposable: Disposable | undefined; - - private _onDidChangeSessions = new EventEmitter(); - get onDidChangeSessions(): Event { - return this._onDidChangeSessions.event; - } - - constructor(private readonly secretStorage: SecretStorage) { } - - dispose(): void { - this.initializedDisposable?.dispose(); - } - - private ensureInitialized(): void { - if (this.initializedDisposable === undefined) { - void this.cacheTokenFromStorage(); - - this.initializedDisposable = Disposable.from( - // This onDidChange event happens when the secret storage changes in _any window_ since - // secrets are shared across all open windows. - this.secretStorage.onDidChange(e => { - if (e.key === AzureDevOpsAuthenticationProvider.secretKey) { - void this.checkForUpdates(); - } - }), - // This fires when the user initiates a "silent" auth flow via the Accounts menu. - authentication.onDidChangeSessions(e => { - if (e.provider.id === AzureDevOpsAuthenticationProvider.id) { - void this.checkForUpdates(); - } - }), - ); - } - } - - // This is a crucial function that handles whether or not the token has changed in - // a different window of VS Code and sends the necessary event if it has. - private async checkForUpdates(): Promise { - const added: AuthenticationSession[] = []; - const removed: AuthenticationSession[] = []; - const changed: AuthenticationSession[] = []; - - const previousToken = await this.currentToken; - const session = (await this.getSessions())[0]; - - if (session?.accessToken && !previousToken) { - added.push(session); - } else if (!session?.accessToken && previousToken) { - removed.push(session); - } else if (session?.accessToken !== previousToken) { - changed.push(session); - } else { - return; - } - - void this.cacheTokenFromStorage(); - this._onDidChangeSessions.fire({ added: added, removed: removed, changed: changed }); - } - - private cacheTokenFromStorage() { - this.currentToken = this.secretStorage.get(AzureDevOpsAuthenticationProvider.secretKey) as Promise; - return this.currentToken; - } - - // This function is called first when `vscode.authentication.getSessions` is called. - async getSessions(_scopes?: string[]): Promise { - this.ensureInitialized(); - const token = await this.cacheTokenFromStorage(); - return token ? [new AzureDevOpsPatSession(token)] : []; - } - - // This function is called after `this.getSessions` is called and only when: - // - `this.getSessions` returns nothing but `createIfNone` was set to `true` in `vscode.authentication.getSessions` - // - `vscode.authentication.getSessions` was called with `forceNewSession: true` - // - The end user initiates the "silent" auth flow via the Accounts menu - async createSession(_scopes: string[]): Promise { - this.ensureInitialized(); - - // Prompt for the PAT. - const token = await window.showInputBox({ - ignoreFocusOut: true, - placeHolder: 'Personal access token', - prompt: 'Enter an Azure DevOps Personal Access Token (PAT).', - password: true, - }); - - // Note: this example doesn't do any validation of the token beyond making sure it's not empty. - if (!token) { - throw new Error('PAT is required'); - } - - // Don't set `currentToken` here, since we want to fire the proper events in the `checkForUpdates` call - await this.secretStorage.store(AzureDevOpsAuthenticationProvider.secretKey, token); - console.log('Successfully logged in to Azure DevOps'); - - return new AzureDevOpsPatSession(token); - } - - // This function is called when the end user signs out of the account. - async removeSession(_sessionId: string): Promise { - await this.secretStorage.delete(AzureDevOpsAuthenticationProvider.secretKey); - } -} +import { + authentication, + AuthenticationProvider, + AuthenticationProviderAuthenticationSessionsChangeEvent, + AuthenticationSession, + Disposable, + Event, + EventEmitter, + SecretStorage, + window, +} from 'vscode'; + +class AzureDevOpsPatSession implements AuthenticationSession { + // We don't know the user's account name, so we'll just use a constant + readonly account = { id: AzureDevOpsAuthenticationProvider.id, label: 'Personal Access Token' }; + // This id isn't used for anything in this example, so we set it to a constant + readonly id = AzureDevOpsAuthenticationProvider.id; + // We don't know what scopes the PAT has, so we have an empty array here. + readonly scopes = []; + + /** + * + * @param accessToken The personal access token to use for authentication + */ + constructor(public readonly accessToken: string) { } +} + +export class AzureDevOpsAuthenticationProvider implements AuthenticationProvider, Disposable { + static id = 'AzureDevOpsPAT'; + private static secretKey = 'AzureDevOpsPAT'; + + // this property is used to determine if the token has been changed in another window of VS Code. + // It is used in the checkForUpdates function. + private currentToken: Promise | undefined; + private initializedDisposable: Disposable | undefined; + + private _onDidChangeSessions = new EventEmitter(); + get onDidChangeSessions(): Event { + return this._onDidChangeSessions.event; + } + + constructor(private readonly secretStorage: SecretStorage) { } + + dispose(): void { + this.initializedDisposable?.dispose(); + } + + private ensureInitialized(): void { + if (this.initializedDisposable === undefined) { + void this.cacheTokenFromStorage(); + + this.initializedDisposable = Disposable.from( + // This onDidChange event happens when the secret storage changes in _any window_ since + // secrets are shared across all open windows. + this.secretStorage.onDidChange(e => { + if (e.key === AzureDevOpsAuthenticationProvider.secretKey) { + void this.checkForUpdates(); + } + }), + // This fires when the user initiates a "silent" auth flow via the Accounts menu. + authentication.onDidChangeSessions(e => { + if (e.provider.id === AzureDevOpsAuthenticationProvider.id) { + void this.checkForUpdates(); + } + }), + ); + } + } + + // This is a crucial function that handles whether or not the token has changed in + // a different window of VS Code and sends the necessary event if it has. + private async checkForUpdates(): Promise { + const added: AuthenticationSession[] = []; + const removed: AuthenticationSession[] = []; + const changed: AuthenticationSession[] = []; + + const previousToken = await this.currentToken; + const session = (await this.getSessions())[0]; + + if (session?.accessToken && !previousToken) { + added.push(session); + } else if (!session?.accessToken && previousToken) { + removed.push(session); + } else if (session?.accessToken !== previousToken) { + changed.push(session); + } else { + return; + } + + void this.cacheTokenFromStorage(); + this._onDidChangeSessions.fire({ added: added, removed: removed, changed: changed }); + } + + private cacheTokenFromStorage() { + this.currentToken = this.secretStorage.get(AzureDevOpsAuthenticationProvider.secretKey) as Promise; + return this.currentToken; + } + + // This function is called first when `vscode.authentication.getSessions` is called. + async getSessions(_scopes?: string[]): Promise { + this.ensureInitialized(); + const token = await this.cacheTokenFromStorage(); + return token ? [new AzureDevOpsPatSession(token)] : []; + } + + // This function is called after `this.getSessions` is called and only when: + // - `this.getSessions` returns nothing but `createIfNone` was set to `true` in `vscode.authentication.getSessions` + // - `vscode.authentication.getSessions` was called with `forceNewSession: true` + // - The end user initiates the "silent" auth flow via the Accounts menu + async createSession(_scopes: string[]): Promise { + this.ensureInitialized(); + + // Prompt for the PAT. + const token = await window.showInputBox({ + ignoreFocusOut: true, + placeHolder: 'Personal access token', + prompt: 'Enter an Azure DevOps Personal Access Token (PAT).', + password: true, + }); + + // Note: this example doesn't do any validation of the token beyond making sure it's not empty. + if (!token) { + throw new Error('PAT is required'); + } + + // Don't set `currentToken` here, since we want to fire the proper events in the `checkForUpdates` call + await this.secretStorage.store(AzureDevOpsAuthenticationProvider.secretKey, token); + console.log('Successfully logged in to Azure DevOps'); + + return new AzureDevOpsPatSession(token); + } + + // This function is called when the end user signs out of the account. + async removeSession(_sessionId: string): Promise { + await this.secretStorage.delete(AzureDevOpsAuthenticationProvider.secretKey); + } +} diff --git a/authenticationprovider-sample/src/extension.ts b/authenticationprovider-sample/src/extension.ts index 7f91f9e1..6fb6e879 100644 --- a/authenticationprovider-sample/src/extension.ts +++ b/authenticationprovider-sample/src/extension.ts @@ -1,53 +1,53 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import 'isomorphic-fetch'; -import * as vscode from 'vscode'; -import { AzureDevOpsAuthenticationProvider } from './authProvider'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { - - console.log('Congratulations, your extension "vscode-authenticationprovider-sample" is now active!'); - - // Register our authentication provider. NOTE: this will register the provider globally which means that - // any other extension can use this provider via the `getSession` API. - // NOTE: when implementing an auth provider, don't forget to register an activation event for that provider - // in your package.json file: "onAuthenticationRequest:AzureDevOpsPAT" - context.subscriptions.push(vscode.authentication.registerAuthenticationProvider( - AzureDevOpsAuthenticationProvider.id, - 'Azure Repos', - new AzureDevOpsAuthenticationProvider(context.secrets), - )); - - let disposable = vscode.commands.registerCommand('vscode-authenticationprovider-sample.login', async () => { - // Get our PAT session. - const session = await vscode.authentication.getSession(AzureDevOpsAuthenticationProvider.id, [], { createIfNone: true }); - - try { - // Make a request to the Azure DevOps API. Keep in mind that this particular API only works with PAT's with - // 'all organizations' access. - const req = await fetch('https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=6.0', { - headers: { - authorization: `Basic ${Buffer.from(`:${session.accessToken}`).toString('base64')}`, - 'content-type': 'application/json', - }, - }); - if (!req.ok) { - throw new Error(req.statusText); - } - const res = await req.json() as { displayName: string }; - vscode.window.showInformationMessage(`Hello ${res.displayName}`); - } catch (e: any) { - if (e.message === 'Unauthorized') { - vscode.window.showErrorMessage('Failed to get profile. You need to use a PAT that has access to all organizations. Please sign out and try again.'); - } - throw e; - } - }); - - context.subscriptions.push(disposable); -} - -// this method is called when your extension is deactivated -export function deactivate() {} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import 'isomorphic-fetch'; +import * as vscode from 'vscode'; +import { AzureDevOpsAuthenticationProvider } from './authProvider'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + + console.log('Congratulations, your extension "vscode-authenticationprovider-sample" is now active!'); + + // Register our authentication provider. NOTE: this will register the provider globally which means that + // any other extension can use this provider via the `getSession` API. + // NOTE: when implementing an auth provider, don't forget to register an activation event for that provider + // in your package.json file: "onAuthenticationRequest:AzureDevOpsPAT" + context.subscriptions.push(vscode.authentication.registerAuthenticationProvider( + AzureDevOpsAuthenticationProvider.id, + 'Azure Repos', + new AzureDevOpsAuthenticationProvider(context.secrets), + )); + + let disposable = vscode.commands.registerCommand('vscode-authenticationprovider-sample.login', async () => { + // Get our PAT session. + const session = await vscode.authentication.getSession(AzureDevOpsAuthenticationProvider.id, [], { createIfNone: true }); + + try { + // Make a request to the Azure DevOps API. Keep in mind that this particular API only works with PAT's with + // 'all organizations' access. + const req = await fetch('https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=6.0', { + headers: { + authorization: `Basic ${Buffer.from(`:${session.accessToken}`).toString('base64')}`, + 'content-type': 'application/json', + }, + }); + if (!req.ok) { + throw new Error(req.statusText); + } + const res = await req.json() as { displayName: string }; + vscode.window.showInformationMessage(`Hello ${res.displayName}`); + } catch (e: any) { + if (e.message === 'Unauthorized') { + vscode.window.showErrorMessage('Failed to get profile. You need to use a PAT that has access to all organizations. Please sign out and try again.'); + } + throw e; + } + }); + + context.subscriptions.push(disposable); +} + +// this method is called when your extension is deactivated +export function deactivate() { } diff --git a/basic-multi-root-sample/src/extension.ts b/basic-multi-root-sample/src/extension.ts index 8f7e13a1..84c69ef0 100644 --- a/basic-multi-root-sample/src/extension.ts +++ b/basic-multi-root-sample/src/extension.ts @@ -1,72 +1,72 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands } from 'vscode'; -import { basename } from 'path'; - -export function activate(context: ExtensionContext) { - - // Create a status bar item - const status = window.createStatusBarItem(StatusBarAlignment.Left, 1000000); - context.subscriptions.push(status); - - // Update status bar item based on events for multi root folder changes - context.subscriptions.push(workspace.onDidChangeWorkspaceFolders(e => updateStatus(status))); - - // Update status bar item based on events for configuration - context.subscriptions.push(workspace.onDidChangeConfiguration(e => updateStatus(status))); - - // Update status bar item based on events around the active editor - context.subscriptions.push(window.onDidChangeActiveTextEditor(e => updateStatus(status))); - context.subscriptions.push(window.onDidChangeTextEditorViewColumn(e => updateStatus(status))); - context.subscriptions.push(workspace.onDidOpenTextDocument(e => updateStatus(status))); - context.subscriptions.push(workspace.onDidCloseTextDocument(e => updateStatus(status))); - - updateStatus(status); -} - -function updateStatus(status: StatusBarItem): void { - const info = getEditorInfo(); - status.text = info ? info.text || '' : ''; - status.tooltip = info ? info.tooltip : undefined; - status.color = info ? info.color : undefined; - - if (info) { - status.show(); - } else { - status.hide(); - } -} - -function getEditorInfo(): { text?: string; tooltip?: string; color?: string; } | null { - const editor = window.activeTextEditor; - - // If no workspace is opened or just a single folder, we return without any status label - // because our extension only works when more than one folder is opened in a workspace. - if (!editor || !workspace.workspaceFolders || workspace.workspaceFolders.length < 2) { - return null; - } - - let text: string | undefined; - let tooltip: string | undefined; - let color: string | undefined; - - // If we have a file:// resource we resolve the WorkspaceFolder this file is from and update - // the status accordingly. - const resource = editor.document.uri; - if (resource.scheme === 'file') { - const folder = workspace.getWorkspaceFolder(resource); - if (!folder) { - text = `$(alert) → ${basename(resource.fsPath)}`; - } else { - text = `$(file-submodule) ${basename(folder.uri.fsPath)} (${folder.index + 1} of ${workspace.workspaceFolders.length}) → $(file-code) ${basename(resource.fsPath)}`; - tooltip = resource.fsPath; - - const multiRootConfigForResource = workspace.getConfiguration('multiRootSample', resource); - color = multiRootConfigForResource.get('statusColor'); - } - } - - return { text, tooltip, color }; -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands } from 'vscode'; +import { basename } from 'path'; + +export function activate(context: ExtensionContext) { + + // Create a status bar item + const status = window.createStatusBarItem(StatusBarAlignment.Left, 1000000); + context.subscriptions.push(status); + + // Update status bar item based on events for multi root folder changes + context.subscriptions.push(workspace.onDidChangeWorkspaceFolders(e => updateStatus(status))); + + // Update status bar item based on events for configuration + context.subscriptions.push(workspace.onDidChangeConfiguration(e => updateStatus(status))); + + // Update status bar item based on events around the active editor + context.subscriptions.push(window.onDidChangeActiveTextEditor(e => updateStatus(status))); + context.subscriptions.push(window.onDidChangeTextEditorViewColumn(e => updateStatus(status))); + context.subscriptions.push(workspace.onDidOpenTextDocument(e => updateStatus(status))); + context.subscriptions.push(workspace.onDidCloseTextDocument(e => updateStatus(status))); + + updateStatus(status); +} + +function updateStatus(status: StatusBarItem): void { + const info = getEditorInfo(); + status.text = info ? info.text || '' : ''; + status.tooltip = info ? info.tooltip : undefined; + status.color = info ? info.color : undefined; + + if (info) { + status.show(); + } else { + status.hide(); + } +} + +function getEditorInfo(): { text?: string; tooltip?: string; color?: string; } | null { + const editor = window.activeTextEditor; + + // If no workspace is opened or just a single folder, we return without any status label + // because our extension only works when more than one folder is opened in a workspace. + if (!editor || !workspace.workspaceFolders || workspace.workspaceFolders.length < 2) { + return null; + } + + let text: string | undefined; + let tooltip: string | undefined; + let color: string | undefined; + + // If we have a file:// resource we resolve the WorkspaceFolder this file is from and update + // the status accordingly. + const resource = editor.document.uri; + if (resource.scheme === 'file') { + const folder = workspace.getWorkspaceFolder(resource); + if (!folder) { + text = `$(alert) → ${basename(resource.fsPath)}`; + } else { + text = `$(file-submodule) ${basename(folder.uri.fsPath)} (${folder.index + 1} of ${workspace.workspaceFolders.length}) → $(file-code) ${basename(resource.fsPath)}`; + tooltip = resource.fsPath; + + const multiRootConfigForResource = workspace.getConfiguration('multiRootSample', resource); + color = multiRootConfigForResource.get('statusColor'); + } + } + + return { text, tooltip, color }; +} diff --git a/call-hierarchy-sample/src/FoodPyramidHierarchyProvider.ts b/call-hierarchy-sample/src/FoodPyramidHierarchyProvider.ts index 3b75be80..c022649c 100644 --- a/call-hierarchy-sample/src/FoodPyramidHierarchyProvider.ts +++ b/call-hierarchy-sample/src/FoodPyramidHierarchyProvider.ts @@ -1,125 +1,125 @@ -import * as vscode from 'vscode'; -import { FoodPyramid, FoodRelation } from './model'; - -export class FoodPyramidHierarchyProvider implements vscode.CallHierarchyProvider { - - prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.CallHierarchyItem | undefined { - const range = document.getWordRangeAtPosition(position); - if (range) { - const word = document.getText(range); - return this.createCallHierarchyItem(word, '', document, range); - } else { - return undefined; - } - } - - async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { - const document = await vscode.workspace.openTextDocument(item.uri); - const parser = new FoodPyramidParser(); - parser.parse(document); - const model = parser.getModel(); - const originRelation = model.getRelationAt(item.range); - - const outgoingCallItems: vscode.CallHierarchyOutgoingCall[] = []; - - if (model.isVerb(item.name)) { - const outgoingCalls = model.getVerbRelations(item.name) - .filter(relation => relation.subject === originRelation!.subject); - - outgoingCalls.forEach(relation => { - const outgoingCallRange = relation.getRangeOf(relation.object); - const verbItem = this.createCallHierarchyItem(relation.object, 'noun', document, outgoingCallRange); - const outgoingCallItem = new vscode.CallHierarchyOutgoingCall(verbItem, [outgoingCallRange]); - outgoingCallItems.push(outgoingCallItem); - }); - } - else if (model.isNoun(item.name)) { - const outgoingCallMap = groupBy(model.getSubjectRelations(item.name), relation => relation.verb); - - outgoingCallMap.forEach((relations, verb) => { - const outgoingCallRanges = relations.map(relation => relation.getRangeOf(verb)); - const verbItem = this.createCallHierarchyItem(verb, 'verb', document, outgoingCallRanges[0]); - const outgoingCallItem = new vscode.CallHierarchyOutgoingCall(verbItem, outgoingCallRanges); - outgoingCallItems.push(outgoingCallItem); - }); - } - - return outgoingCallItems; - } - - async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { - const document = await vscode.workspace.openTextDocument(item.uri); - const parser = new FoodPyramidParser(); - parser.parse(document); - const model = parser.getModel(); - const originRelation = model.getRelationAt(item.range); - - const outgoingCallItems: vscode.CallHierarchyIncomingCall[] = []; - - if (model.isVerb(item.name)) { - const outgoingCalls = model.getVerbRelations(item.name) - .filter(relation => relation.object === originRelation!.object); - - outgoingCalls.forEach(relation => { - const outgoingCallRange = relation.getRangeOf(relation.subject); - const verbItem = this.createCallHierarchyItem(relation.subject, 'noun', document, outgoingCallRange); - const outgoingCallItem = new vscode.CallHierarchyIncomingCall(verbItem, [outgoingCallRange]); - outgoingCallItems.push(outgoingCallItem); - }); - } - else if (model.isNoun(item.name)) { - const outgoingCallMap = groupBy(model.getObjectRelations(item.name), relation => relation.verb); - - outgoingCallMap.forEach((relations, verb) => { - const outgoingCallRanges = relations.map(relation => relation.getRangeOf(verb)); - const verbItem = this.createCallHierarchyItem(verb, 'verb-inverted', document, outgoingCallRanges[0]); - const outgoingCallItem = new vscode.CallHierarchyIncomingCall(verbItem, outgoingCallRanges); - outgoingCallItems.push(outgoingCallItem); - }); - } - - return outgoingCallItems; - } - - private createCallHierarchyItem(word: string, type: string, document: vscode.TextDocument, range: vscode.Range): vscode.CallHierarchyItem { - return new vscode.CallHierarchyItem(vscode.SymbolKind.Object, word, `(${type})`, document.uri, range, range); - } - -} - -/** - * Sample parser of the document text into the [FoodPyramid](#FoodPyramid) model. - */ -class FoodPyramidParser { - private _model = new FoodPyramid(); - - getModel(): FoodPyramid { - return this._model; - } - - parse(textDocument: vscode.TextDocument): void { - const pattern = /^(\w+)\s+(\w+)\s+(\w+).$/gm; - let match: RegExpExecArray | null; - while ((match = pattern.exec(textDocument.getText()))) { - const startPosition = textDocument.positionAt(match.index); - const range = new vscode.Range(startPosition, startPosition.translate({ characterDelta: match[0].length })); - this._model.addRelation(new FoodRelation(match[1], match[2], match[3], match[0], range)); - } - } -} - -/** - * Groups array items by a field defined using a key selector. - * @param array array to be grouped - * @param keyGetter grouping key selector - */ -function groupBy(array: Array, keyGetter: (value: V) => K): Map { - const map = new Map(); - array.forEach((item) => { - const key = keyGetter(item); - const groupForKey = map.get(key) || []; - groupForKey.push(item); - map.set(key, groupForKey); - }); - return map; -} +import * as vscode from 'vscode'; +import { FoodPyramid, FoodRelation } from './model'; + +export class FoodPyramidHierarchyProvider implements vscode.CallHierarchyProvider { + + prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.CallHierarchyItem | undefined { + const range = document.getWordRangeAtPosition(position); + if (range) { + const word = document.getText(range); + return this.createCallHierarchyItem(word, '', document, range); + } else { + return undefined; + } + } + + async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const document = await vscode.workspace.openTextDocument(item.uri); + const parser = new FoodPyramidParser(); + parser.parse(document); + const model = parser.getModel(); + const originRelation = model.getRelationAt(item.range); + + const outgoingCallItems: vscode.CallHierarchyOutgoingCall[] = []; + + if (model.isVerb(item.name)) { + const outgoingCalls = model.getVerbRelations(item.name) + .filter(relation => relation.subject === originRelation!.subject); + + outgoingCalls.forEach(relation => { + const outgoingCallRange = relation.getRangeOf(relation.object); + const verbItem = this.createCallHierarchyItem(relation.object, 'noun', document, outgoingCallRange); + const outgoingCallItem = new vscode.CallHierarchyOutgoingCall(verbItem, [outgoingCallRange]); + outgoingCallItems.push(outgoingCallItem); + }); + } + else if (model.isNoun(item.name)) { + const outgoingCallMap = groupBy(model.getSubjectRelations(item.name), relation => relation.verb); + + outgoingCallMap.forEach((relations, verb) => { + const outgoingCallRanges = relations.map(relation => relation.getRangeOf(verb)); + const verbItem = this.createCallHierarchyItem(verb, 'verb', document, outgoingCallRanges[0]); + const outgoingCallItem = new vscode.CallHierarchyOutgoingCall(verbItem, outgoingCallRanges); + outgoingCallItems.push(outgoingCallItem); + }); + } + + return outgoingCallItems; + } + + async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const document = await vscode.workspace.openTextDocument(item.uri); + const parser = new FoodPyramidParser(); + parser.parse(document); + const model = parser.getModel(); + const originRelation = model.getRelationAt(item.range); + + const outgoingCallItems: vscode.CallHierarchyIncomingCall[] = []; + + if (model.isVerb(item.name)) { + const outgoingCalls = model.getVerbRelations(item.name) + .filter(relation => relation.object === originRelation!.object); + + outgoingCalls.forEach(relation => { + const outgoingCallRange = relation.getRangeOf(relation.subject); + const verbItem = this.createCallHierarchyItem(relation.subject, 'noun', document, outgoingCallRange); + const outgoingCallItem = new vscode.CallHierarchyIncomingCall(verbItem, [outgoingCallRange]); + outgoingCallItems.push(outgoingCallItem); + }); + } + else if (model.isNoun(item.name)) { + const outgoingCallMap = groupBy(model.getObjectRelations(item.name), relation => relation.verb); + + outgoingCallMap.forEach((relations, verb) => { + const outgoingCallRanges = relations.map(relation => relation.getRangeOf(verb)); + const verbItem = this.createCallHierarchyItem(verb, 'verb-inverted', document, outgoingCallRanges[0]); + const outgoingCallItem = new vscode.CallHierarchyIncomingCall(verbItem, outgoingCallRanges); + outgoingCallItems.push(outgoingCallItem); + }); + } + + return outgoingCallItems; + } + + private createCallHierarchyItem(word: string, type: string, document: vscode.TextDocument, range: vscode.Range): vscode.CallHierarchyItem { + return new vscode.CallHierarchyItem(vscode.SymbolKind.Object, word, `(${type})`, document.uri, range, range); + } + +} + +/** + * Sample parser of the document text into the [FoodPyramid](#FoodPyramid) model. + */ +class FoodPyramidParser { + private _model = new FoodPyramid(); + + getModel(): FoodPyramid { + return this._model; + } + + parse(textDocument: vscode.TextDocument): void { + const pattern = /^(\w+)\s+(\w+)\s+(\w+).$/gm; + let match: RegExpExecArray | null; + while ((match = pattern.exec(textDocument.getText()))) { + const startPosition = textDocument.positionAt(match.index); + const range = new vscode.Range(startPosition, startPosition.translate({ characterDelta: match[0].length })); + this._model.addRelation(new FoodRelation(match[1], match[2], match[3], match[0], range)); + } + } +} + +/** + * Groups array items by a field defined using a key selector. + * @param array array to be grouped + * @param keyGetter grouping key selector + */ +function groupBy(array: Array, keyGetter: (value: V) => K): Map { + const map = new Map(); + array.forEach((item) => { + const key = keyGetter(item); + const groupForKey = map.get(key) || []; + groupForKey.push(item); + map.set(key, groupForKey); + }); + return map; +} diff --git a/call-hierarchy-sample/src/extension.ts b/call-hierarchy-sample/src/extension.ts index 09d62265..3cc36fbd 100644 --- a/call-hierarchy-sample/src/extension.ts +++ b/call-hierarchy-sample/src/extension.ts @@ -1,26 +1,26 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; -import { FoodPyramidHierarchyProvider } from './FoodPyramidHierarchyProvider'; -import { TextDecoder } from 'util'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log('Congratulations, your extension "call-hierarchy-sample" is now active!'); - - const disposable = vscode.languages.registerCallHierarchyProvider('plaintext', new FoodPyramidHierarchyProvider()); - - context.subscriptions.push(disposable); - - showSampleText(context); -} - -async function showSampleText(context: vscode.ExtensionContext): Promise { - const sampleTextEncoded = await vscode.workspace.fs.readFile(vscode.Uri.file(context.asAbsolutePath('sample.txt'))); - const sampleText = new TextDecoder('utf-8').decode(sampleTextEncoded); - const doc = await vscode.workspace.openTextDocument({ language: 'plaintext', content: sampleText }); - vscode.window.showTextDocument(doc); +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +import { FoodPyramidHierarchyProvider } from './FoodPyramidHierarchyProvider'; +import { TextDecoder } from 'util'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + console.log('Congratulations, your extension "call-hierarchy-sample" is now active!'); + + const disposable = vscode.languages.registerCallHierarchyProvider('plaintext', new FoodPyramidHierarchyProvider()); + + context.subscriptions.push(disposable); + + showSampleText(context); +} + +async function showSampleText(context: vscode.ExtensionContext): Promise { + const sampleTextEncoded = await vscode.workspace.fs.readFile(vscode.Uri.file(context.asAbsolutePath('sample.txt'))); + const sampleText = new TextDecoder('utf-8').decode(sampleTextEncoded); + const doc = await vscode.workspace.openTextDocument({ language: 'plaintext', content: sampleText }); + vscode.window.showTextDocument(doc); } \ No newline at end of file diff --git a/call-hierarchy-sample/src/model.ts b/call-hierarchy-sample/src/model.ts index 143cc08a..20e90e92 100644 --- a/call-hierarchy-sample/src/model.ts +++ b/call-hierarchy-sample/src/model.ts @@ -1,88 +1,88 @@ -import * as vscode from 'vscode'; - -/** - * Sample model of what the text in the document contains. - */ -export class FoodPyramid { - private _relations: FoodRelation[] = []; - private _nouns = new Set(); - private _verbs = new Set(); - - getRelationAt(wordRange: vscode.Range): FoodRelation | undefined { - return this._relations.find(relation => relation.range.contains(wordRange)); - } - - addRelation(relation: FoodRelation): void { - this._relations.push(relation); - this._nouns.add(relation.object).add(relation.subject); - this._verbs.add(relation.verb); - } - - isVerb(name: string): boolean { - return this._verbs.has(name.toLowerCase()); - } - - isNoun(name: string): boolean { - return this._nouns.has(name.toLowerCase()); - } - - getVerbRelations(verb: string): FoodRelation[] { - return this._relations - .filter(relation => relation.verb === verb.toLowerCase()); - } - - getNounRelations(noun: string): FoodRelation[] { - return this._relations - .filter(relation => relation.involves(noun)); - } - - getSubjectRelations(subject: string): FoodRelation[] { - return this._relations - .filter(relation => relation.subject === subject.toLowerCase()); - } - - getObjectRelations(object: string): FoodRelation[] { - return this._relations - .filter(relation => relation.object === object.toLowerCase()); - } -} - -/** - * Model element. - */ -export class FoodRelation { - private _subject: string; - private _verb: string; - private _object: string; - - constructor(subject: string, verb: string, object: string, - private readonly originalText: string, public readonly range: vscode.Range) { - - this._subject = subject.toLowerCase(); - this._verb = verb.toLowerCase(); - this._object = object.toLowerCase(); - } - - get subject(): string { - return this._subject; - } - - get object(): string { - return this._object; - } - - get verb(): string { - return this._verb; - } - - involves(noun: string): boolean { - const needle = noun.toLowerCase(); - return this._subject === needle || this._object === needle; - } - - getRangeOf(word: string): vscode.Range { - const indexOfWord = new RegExp("\\b" + word + "\\b", "i").exec(this.originalText)!.index; - return new vscode.Range(this.range.start.translate({ characterDelta: indexOfWord }), - this.range.start.translate({ characterDelta: indexOfWord + word.length })); - } -} +import * as vscode from 'vscode'; + +/** + * Sample model of what the text in the document contains. + */ +export class FoodPyramid { + private _relations: FoodRelation[] = []; + private _nouns = new Set(); + private _verbs = new Set(); + + getRelationAt(wordRange: vscode.Range): FoodRelation | undefined { + return this._relations.find(relation => relation.range.contains(wordRange)); + } + + addRelation(relation: FoodRelation): void { + this._relations.push(relation); + this._nouns.add(relation.object).add(relation.subject); + this._verbs.add(relation.verb); + } + + isVerb(name: string): boolean { + return this._verbs.has(name.toLowerCase()); + } + + isNoun(name: string): boolean { + return this._nouns.has(name.toLowerCase()); + } + + getVerbRelations(verb: string): FoodRelation[] { + return this._relations + .filter(relation => relation.verb === verb.toLowerCase()); + } + + getNounRelations(noun: string): FoodRelation[] { + return this._relations + .filter(relation => relation.involves(noun)); + } + + getSubjectRelations(subject: string): FoodRelation[] { + return this._relations + .filter(relation => relation.subject === subject.toLowerCase()); + } + + getObjectRelations(object: string): FoodRelation[] { + return this._relations + .filter(relation => relation.object === object.toLowerCase()); + } +} + +/** + * Model element. + */ +export class FoodRelation { + private _subject: string; + private _verb: string; + private _object: string; + + constructor(subject: string, verb: string, object: string, + private readonly originalText: string, public readonly range: vscode.Range) { + + this._subject = subject.toLowerCase(); + this._verb = verb.toLowerCase(); + this._object = object.toLowerCase(); + } + + get subject(): string { + return this._subject; + } + + get object(): string { + return this._object; + } + + get verb(): string { + return this._verb; + } + + involves(noun: string): boolean { + const needle = noun.toLowerCase(); + return this._subject === needle || this._object === needle; + } + + getRangeOf(word: string): vscode.Range { + const indexOfWord = new RegExp("\\b" + word + "\\b", "i").exec(this.originalText)!.index; + return new vscode.Range(this.range.start.translate({ characterDelta: indexOfWord }), + this.range.start.translate({ characterDelta: indexOfWord + word.length })); + } +} diff --git a/code-actions-sample/src/diagnostics.ts b/code-actions-sample/src/diagnostics.ts index 9600bd62..56acc0ce 100644 --- a/code-actions-sample/src/diagnostics.ts +++ b/code-actions-sample/src/diagnostics.ts @@ -1,67 +1,67 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -/** To demonstrate code actions associated with Diagnostics problems, this file provides a mock diagnostics entries. */ - -import * as vscode from 'vscode'; - -/** Code that is used to associate diagnostic entries with code actions. */ -export const EMOJI_MENTION = 'emoji_mention'; - -/** String to detect in the text document. */ -const EMOJI = 'emoji'; - -/** - * Analyzes the text document for problems. - * This demo diagnostic problem provider finds all mentions of 'emoji'. - * @param doc text document to analyze - * @param emojiDiagnostics diagnostic collection - */ -export function refreshDiagnostics(doc: vscode.TextDocument, emojiDiagnostics: vscode.DiagnosticCollection): void { - const diagnostics: vscode.Diagnostic[] = []; - - for (let lineIndex = 0; lineIndex < doc.lineCount; lineIndex++) { - const lineOfText = doc.lineAt(lineIndex); - if (lineOfText.text.includes(EMOJI)) { - diagnostics.push(createDiagnostic(doc, lineOfText, lineIndex)); - } - } - - emojiDiagnostics.set(doc.uri, diagnostics); -} - -function createDiagnostic(doc: vscode.TextDocument, lineOfText: vscode.TextLine, lineIndex: number): vscode.Diagnostic { - // find where in the line of that the 'emoji' is mentioned - const index = lineOfText.text.indexOf(EMOJI); - - // create range that represents, where in the document the word is - const range = new vscode.Range(lineIndex, index, lineIndex, index + EMOJI.length); - - const diagnostic = new vscode.Diagnostic(range, "When you say 'emoji', do you want to find out more?", - vscode.DiagnosticSeverity.Information); - diagnostic.code = EMOJI_MENTION; - return diagnostic; -} - -export function subscribeToDocumentChanges(context: vscode.ExtensionContext, emojiDiagnostics: vscode.DiagnosticCollection): void { - if (vscode.window.activeTextEditor) { - refreshDiagnostics(vscode.window.activeTextEditor.document, emojiDiagnostics); - } - context.subscriptions.push( - vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor) { - refreshDiagnostics(editor.document, emojiDiagnostics); - } - }) - ); - - context.subscriptions.push( - vscode.workspace.onDidChangeTextDocument(e => refreshDiagnostics(e.document, emojiDiagnostics)) - ); - - context.subscriptions.push( - vscode.workspace.onDidCloseTextDocument(doc => emojiDiagnostics.delete(doc.uri)) - ); - +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +/** To demonstrate code actions associated with Diagnostics problems, this file provides a mock diagnostics entries. */ + +import * as vscode from 'vscode'; + +/** Code that is used to associate diagnostic entries with code actions. */ +export const EMOJI_MENTION = 'emoji_mention'; + +/** String to detect in the text document. */ +const EMOJI = 'emoji'; + +/** + * Analyzes the text document for problems. + * This demo diagnostic problem provider finds all mentions of 'emoji'. + * @param doc text document to analyze + * @param emojiDiagnostics diagnostic collection + */ +export function refreshDiagnostics(doc: vscode.TextDocument, emojiDiagnostics: vscode.DiagnosticCollection): void { + const diagnostics: vscode.Diagnostic[] = []; + + for (let lineIndex = 0; lineIndex < doc.lineCount; lineIndex++) { + const lineOfText = doc.lineAt(lineIndex); + if (lineOfText.text.includes(EMOJI)) { + diagnostics.push(createDiagnostic(doc, lineOfText, lineIndex)); + } + } + + emojiDiagnostics.set(doc.uri, diagnostics); +} + +function createDiagnostic(doc: vscode.TextDocument, lineOfText: vscode.TextLine, lineIndex: number): vscode.Diagnostic { + // find where in the line of that the 'emoji' is mentioned + const index = lineOfText.text.indexOf(EMOJI); + + // create range that represents, where in the document the word is + const range = new vscode.Range(lineIndex, index, lineIndex, index + EMOJI.length); + + const diagnostic = new vscode.Diagnostic(range, "When you say 'emoji', do you want to find out more?", + vscode.DiagnosticSeverity.Information); + diagnostic.code = EMOJI_MENTION; + return diagnostic; +} + +export function subscribeToDocumentChanges(context: vscode.ExtensionContext, emojiDiagnostics: vscode.DiagnosticCollection): void { + if (vscode.window.activeTextEditor) { + refreshDiagnostics(vscode.window.activeTextEditor.document, emojiDiagnostics); + } + context.subscriptions.push( + vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor) { + refreshDiagnostics(editor.document, emojiDiagnostics); + } + }) + ); + + context.subscriptions.push( + vscode.workspace.onDidChangeTextDocument(e => refreshDiagnostics(e.document, emojiDiagnostics)) + ); + + context.subscriptions.push( + vscode.workspace.onDidCloseTextDocument(doc => emojiDiagnostics.delete(doc.uri)) + ); + } \ No newline at end of file diff --git a/code-actions-sample/src/extension.ts b/code-actions-sample/src/extension.ts index 23d3037a..e6f624ee 100644 --- a/code-actions-sample/src/extension.ts +++ b/code-actions-sample/src/extension.ts @@ -1,108 +1,108 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { subscribeToDocumentChanges, EMOJI_MENTION } from './diagnostics'; - -const COMMAND = 'code-actions-sample.command'; - -export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push( - vscode.languages.registerCodeActionsProvider('markdown', new Emojizer(), { - providedCodeActionKinds: Emojizer.providedCodeActionKinds - })); - - const emojiDiagnostics = vscode.languages.createDiagnosticCollection("emoji"); - context.subscriptions.push(emojiDiagnostics); - - subscribeToDocumentChanges(context, emojiDiagnostics); - - context.subscriptions.push( - vscode.languages.registerCodeActionsProvider('markdown', new Emojinfo(), { - providedCodeActionKinds: Emojinfo.providedCodeActionKinds - }) - ); - - context.subscriptions.push( - vscode.commands.registerCommand(COMMAND, () => vscode.env.openExternal(vscode.Uri.parse('https://unicode.org/emoji/charts-12.0/full-emoji-list.html'))) - ); -} - -/** - * Provides code actions for converting :) to a smiley emoji. - */ -export class Emojizer implements vscode.CodeActionProvider { - - public static readonly providedCodeActionKinds = [ - vscode.CodeActionKind.QuickFix - ]; - - public provideCodeActions(document: vscode.TextDocument, range: vscode.Range): vscode.CodeAction[] | undefined { - if (!this.isAtStartOfSmiley(document, range)) { - return; - } - - const replaceWithSmileyCatFix = this.createFix(document, range, '😺'); - - const replaceWithSmileyFix = this.createFix(document, range, '😀'); - // Marking a single fix as `preferred` means that users can apply it with a - // single keyboard shortcut using the `Auto Fix` command. - replaceWithSmileyFix.isPreferred = true; - - const replaceWithSmileyHankyFix = this.createFix(document, range, '💩'); - - const commandAction = this.createCommand(); - - return [ - replaceWithSmileyCatFix, - replaceWithSmileyFix, - replaceWithSmileyHankyFix, - commandAction - ]; - } - - private isAtStartOfSmiley(document: vscode.TextDocument, range: vscode.Range) { - const start = range.start; - const line = document.lineAt(start.line); - return line.text[start.character] === ':' && line.text[start.character + 1] === ')'; - } - - private createFix(document: vscode.TextDocument, range: vscode.Range, emoji: string): vscode.CodeAction { - const fix = new vscode.CodeAction(`Convert to ${emoji}`, vscode.CodeActionKind.QuickFix); - fix.edit = new vscode.WorkspaceEdit(); - fix.edit.replace(document.uri, new vscode.Range(range.start, range.start.translate(0, 2)), emoji); - return fix; - } - - private createCommand(): vscode.CodeAction { - const action = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.Empty); - action.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' }; - return action; - } -} - -/** - * Provides code actions corresponding to diagnostic problems. - */ -export class Emojinfo implements vscode.CodeActionProvider { - - public static readonly providedCodeActionKinds = [ - vscode.CodeActionKind.QuickFix - ]; - - provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.CodeAction[] { - // for each diagnostic entry that has the matching `code`, create a code action command - return context.diagnostics - .filter(diagnostic => diagnostic.code === EMOJI_MENTION) - .map(diagnostic => this.createCommandCodeAction(diagnostic)); - } - - private createCommandCodeAction(diagnostic: vscode.Diagnostic): vscode.CodeAction { - const action = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.QuickFix); - action.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' }; - action.diagnostics = [diagnostic]; - action.isPreferred = true; - return action; - } +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { subscribeToDocumentChanges, EMOJI_MENTION } from './diagnostics'; + +const COMMAND = 'code-actions-sample.command'; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.languages.registerCodeActionsProvider('markdown', new Emojizer(), { + providedCodeActionKinds: Emojizer.providedCodeActionKinds + })); + + const emojiDiagnostics = vscode.languages.createDiagnosticCollection("emoji"); + context.subscriptions.push(emojiDiagnostics); + + subscribeToDocumentChanges(context, emojiDiagnostics); + + context.subscriptions.push( + vscode.languages.registerCodeActionsProvider('markdown', new Emojinfo(), { + providedCodeActionKinds: Emojinfo.providedCodeActionKinds + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand(COMMAND, () => vscode.env.openExternal(vscode.Uri.parse('https://unicode.org/emoji/charts-12.0/full-emoji-list.html'))) + ); +} + +/** + * Provides code actions for converting :) to a smiley emoji. + */ +export class Emojizer implements vscode.CodeActionProvider { + + public static readonly providedCodeActionKinds = [ + vscode.CodeActionKind.QuickFix + ]; + + public provideCodeActions(document: vscode.TextDocument, range: vscode.Range): vscode.CodeAction[] | undefined { + if (!this.isAtStartOfSmiley(document, range)) { + return; + } + + const replaceWithSmileyCatFix = this.createFix(document, range, '😺'); + + const replaceWithSmileyFix = this.createFix(document, range, '😀'); + // Marking a single fix as `preferred` means that users can apply it with a + // single keyboard shortcut using the `Auto Fix` command. + replaceWithSmileyFix.isPreferred = true; + + const replaceWithSmileyHankyFix = this.createFix(document, range, '💩'); + + const commandAction = this.createCommand(); + + return [ + replaceWithSmileyCatFix, + replaceWithSmileyFix, + replaceWithSmileyHankyFix, + commandAction + ]; + } + + private isAtStartOfSmiley(document: vscode.TextDocument, range: vscode.Range) { + const start = range.start; + const line = document.lineAt(start.line); + return line.text[start.character] === ':' && line.text[start.character + 1] === ')'; + } + + private createFix(document: vscode.TextDocument, range: vscode.Range, emoji: string): vscode.CodeAction { + const fix = new vscode.CodeAction(`Convert to ${emoji}`, vscode.CodeActionKind.QuickFix); + fix.edit = new vscode.WorkspaceEdit(); + fix.edit.replace(document.uri, new vscode.Range(range.start, range.start.translate(0, 2)), emoji); + return fix; + } + + private createCommand(): vscode.CodeAction { + const action = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.Empty); + action.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' }; + return action; + } +} + +/** + * Provides code actions corresponding to diagnostic problems. + */ +export class Emojinfo implements vscode.CodeActionProvider { + + public static readonly providedCodeActionKinds = [ + vscode.CodeActionKind.QuickFix + ]; + + provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.CodeAction[] { + // for each diagnostic entry that has the matching `code`, create a code action command + return context.diagnostics + .filter(diagnostic => diagnostic.code === EMOJI_MENTION) + .map(diagnostic => this.createCommandCodeAction(diagnostic)); + } + + private createCommandCodeAction(diagnostic: vscode.Diagnostic): vscode.CodeAction { + const action = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.QuickFix); + action.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' }; + action.diagnostics = [diagnostic]; + action.isPreferred = true; + return action; + } } \ No newline at end of file diff --git a/codelens-sample/src/CodelensProvider.ts b/codelens-sample/src/CodelensProvider.ts index bbb023be..bc6f7cc3 100644 --- a/codelens-sample/src/CodelensProvider.ts +++ b/codelens-sample/src/CodelensProvider.ts @@ -1,55 +1,55 @@ -import * as vscode from 'vscode'; - -/** - * CodelensProvider - */ -export class CodelensProvider implements vscode.CodeLensProvider { - - private codeLenses: vscode.CodeLens[] = []; - private regex: RegExp; - private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); - public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event; - - constructor() { - this.regex = /(.+)/g; - - vscode.workspace.onDidChangeConfiguration((_) => { - this._onDidChangeCodeLenses.fire(); - }); - } - - public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable { - - if (vscode.workspace.getConfiguration("codelens-sample").get("enableCodeLens", true)) { - this.codeLenses = []; - const regex = new RegExp(this.regex); - const text = document.getText(); - let matches; - while ((matches = regex.exec(text)) !== null) { - const line = document.lineAt(document.positionAt(matches.index).line); - const indexOf = line.text.indexOf(matches[0]); - const position = new vscode.Position(line.lineNumber, indexOf); - const range = document.getWordRangeAtPosition(position, new RegExp(this.regex)); - if (range) { - this.codeLenses.push(new vscode.CodeLens(range)); - } - } - return this.codeLenses; - } - return []; - } - - public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken) { - if (vscode.workspace.getConfiguration("codelens-sample").get("enableCodeLens", true)) { - codeLens.command = { - title: "Codelens provided by sample extension", - tooltip: "Tooltip provided by sample extension", - command: "codelens-sample.codelensAction", - arguments: ["Argument 1", false] - }; - return codeLens; - } - return null; - } -} - +import * as vscode from 'vscode'; + +/** + * CodelensProvider + */ +export class CodelensProvider implements vscode.CodeLensProvider { + + private codeLenses: vscode.CodeLens[] = []; + private regex: RegExp; + private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); + public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event; + + constructor() { + this.regex = /(.+)/g; + + vscode.workspace.onDidChangeConfiguration((_) => { + this._onDidChangeCodeLenses.fire(); + }); + } + + public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable { + + if (vscode.workspace.getConfiguration("codelens-sample").get("enableCodeLens", true)) { + this.codeLenses = []; + const regex = new RegExp(this.regex); + const text = document.getText(); + let matches; + while ((matches = regex.exec(text)) !== null) { + const line = document.lineAt(document.positionAt(matches.index).line); + const indexOf = line.text.indexOf(matches[0]); + const position = new vscode.Position(line.lineNumber, indexOf); + const range = document.getWordRangeAtPosition(position, new RegExp(this.regex)); + if (range) { + this.codeLenses.push(new vscode.CodeLens(range)); + } + } + return this.codeLenses; + } + return []; + } + + public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken) { + if (vscode.workspace.getConfiguration("codelens-sample").get("enableCodeLens", true)) { + codeLens.command = { + title: "Codelens provided by sample extension", + tooltip: "Tooltip provided by sample extension", + command: "codelens-sample.codelensAction", + arguments: ["Argument 1", false] + }; + return codeLens; + } + return null; + } +} + diff --git a/codelens-sample/src/extension.ts b/codelens-sample/src/extension.ts index 6eae1581..f0c025b5 100644 --- a/codelens-sample/src/extension.ts +++ b/codelens-sample/src/extension.ts @@ -1,35 +1,35 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import { ExtensionContext, languages, commands, Disposable, workspace, window } from 'vscode'; -import { CodelensProvider } from './CodelensProvider'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed - -let disposables: Disposable[] = []; - -export function activate(context: ExtensionContext) { - const codelensProvider = new CodelensProvider(); - - languages.registerCodeLensProvider("*", codelensProvider); - - commands.registerCommand("codelens-sample.enableCodeLens", () => { - workspace.getConfiguration("codelens-sample").update("enableCodeLens", true, true); - }); - - commands.registerCommand("codelens-sample.disableCodeLens", () => { - workspace.getConfiguration("codelens-sample").update("enableCodeLens", false, true); - }); - - commands.registerCommand("codelens-sample.codelensAction", (args: any) => { - window.showInformationMessage(`CodeLens action clicked with args=${args}`); - }); -} - -// this method is called when your extension is deactivated -export function deactivate() { - if (disposables) { - disposables.forEach(item => item.dispose()); - } - disposables = []; -} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import { ExtensionContext, languages, commands, Disposable, workspace, window } from 'vscode'; +import { CodelensProvider } from './CodelensProvider'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed + +let disposables: Disposable[] = []; + +export function activate(context: ExtensionContext) { + const codelensProvider = new CodelensProvider(); + + languages.registerCodeLensProvider("*", codelensProvider); + + commands.registerCommand("codelens-sample.enableCodeLens", () => { + workspace.getConfiguration("codelens-sample").update("enableCodeLens", true, true); + }); + + commands.registerCommand("codelens-sample.disableCodeLens", () => { + workspace.getConfiguration("codelens-sample").update("enableCodeLens", false, true); + }); + + commands.registerCommand("codelens-sample.codelensAction", (args: any) => { + window.showInformationMessage(`CodeLens action clicked with args=${args}`); + }); +} + +// this method is called when your extension is deactivated +export function deactivate() { + if (disposables) { + disposables.forEach(item => item.dispose()); + } + disposables = []; +} diff --git a/comment-sample/src/extension.ts b/comment-sample/src/extension.ts index b238f708..385b7f99 100644 --- a/comment-sample/src/extension.ts +++ b/comment-sample/src/extension.ts @@ -1,144 +1,144 @@ -'use strict'; - -import * as vscode from 'vscode'; - -let commentId = 1; - -class NoteComment implements vscode.Comment { - id: number; - label: string | undefined; - savedBody: string | vscode.MarkdownString; // for the Cancel button - constructor( - public body: string | vscode.MarkdownString, - public mode: vscode.CommentMode, - public author: vscode.CommentAuthorInformation, - public parent?: vscode.CommentThread, - public contextValue?: string - ) { - this.id = ++commentId; - this.savedBody = this.body; - } -} - -export function activate(context: vscode.ExtensionContext) { - // A `CommentController` is able to provide comments for documents. - const commentController = vscode.comments.createCommentController('comment-sample', 'Comment API Sample'); - context.subscriptions.push(commentController); - - // A `CommentingRangeProvider` controls where gutter decorations that allow adding comments are shown - commentController.commentingRangeProvider = { - provideCommentingRanges: (document: vscode.TextDocument, token: vscode.CancellationToken) => { - const lineCount = document.lineCount; - return [new vscode.Range(0, 0, lineCount - 1, 0)]; - } - }; - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.createNote', (reply: vscode.CommentReply) => { - replyNote(reply); - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.replyNote', (reply: vscode.CommentReply) => { - replyNote(reply); - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.startDraft', (reply: vscode.CommentReply) => { - const thread = reply.thread; - thread.contextValue = 'draft'; - const newComment = new NoteComment(reply.text, vscode.CommentMode.Preview, { name: 'vscode' }, thread); - newComment.label = 'pending'; - thread.comments = [...thread.comments, newComment]; - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.finishDraft', (reply: vscode.CommentReply) => { - const thread = reply.thread; - - if (!thread) { - return; - } - - thread.contextValue = undefined; - thread.collapsibleState = vscode.CommentThreadCollapsibleState.Collapsed; - if (reply.text) { - const newComment = new NoteComment(reply.text, vscode.CommentMode.Preview, { name: 'vscode' }, thread); - thread.comments = [...thread.comments, newComment].map(comment => { - comment.label = undefined; - return comment; - }); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.deleteNoteComment', (comment: NoteComment) => { - const thread = comment.parent; - if (!thread) { - return; - } - - thread.comments = thread.comments.filter(cmt => (cmt as NoteComment).id !== comment.id); - - if (thread.comments.length === 0) { - thread.dispose(); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.deleteNote', (thread: vscode.CommentThread) => { - thread.dispose(); - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.cancelsaveNote', (comment: NoteComment) => { - if (!comment.parent) { - return; - } - - comment.parent.comments = comment.parent.comments.map(cmt => { - if ((cmt as NoteComment).id === comment.id) { - cmt.body = (cmt as NoteComment).savedBody; - cmt.mode = vscode.CommentMode.Preview; - } - - return cmt; - }); - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.saveNote', (comment: NoteComment) => { - if (!comment.parent) { - return; - } - - comment.parent.comments = comment.parent.comments.map(cmt => { - if ((cmt as NoteComment).id === comment.id) { - (cmt as NoteComment).savedBody = cmt.body; - cmt.mode = vscode.CommentMode.Preview; - } - - return cmt; - }); - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.editNote', (comment: NoteComment) => { - if (!comment.parent) { - return; - } - - comment.parent.comments = comment.parent.comments.map(cmt => { - if ((cmt as NoteComment).id === comment.id) { - cmt.mode = vscode.CommentMode.Editing; - } - - return cmt; - }); - })); - - context.subscriptions.push(vscode.commands.registerCommand('mywiki.dispose', () => { - commentController.dispose(); - })); - - function replyNote(reply: vscode.CommentReply) { - const thread = reply.thread; - const newComment = new NoteComment(reply.text, vscode.CommentMode.Preview, { name: 'vscode' }, thread, thread.comments.length ? 'canDelete' : undefined); - if (thread.contextValue === 'draft') { - newComment.label = 'pending'; - } - - thread.comments = [...thread.comments, newComment]; - } -} +'use strict'; + +import * as vscode from 'vscode'; + +let commentId = 1; + +class NoteComment implements vscode.Comment { + id: number; + label: string | undefined; + savedBody: string | vscode.MarkdownString; // for the Cancel button + constructor( + public body: string | vscode.MarkdownString, + public mode: vscode.CommentMode, + public author: vscode.CommentAuthorInformation, + public parent?: vscode.CommentThread, + public contextValue?: string + ) { + this.id = ++commentId; + this.savedBody = this.body; + } +} + +export function activate(context: vscode.ExtensionContext) { + // A `CommentController` is able to provide comments for documents. + const commentController = vscode.comments.createCommentController('comment-sample', 'Comment API Sample'); + context.subscriptions.push(commentController); + + // A `CommentingRangeProvider` controls where gutter decorations that allow adding comments are shown + commentController.commentingRangeProvider = { + provideCommentingRanges: (document: vscode.TextDocument, token: vscode.CancellationToken) => { + const lineCount = document.lineCount; + return [new vscode.Range(0, 0, lineCount - 1, 0)]; + } + }; + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.createNote', (reply: vscode.CommentReply) => { + replyNote(reply); + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.replyNote', (reply: vscode.CommentReply) => { + replyNote(reply); + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.startDraft', (reply: vscode.CommentReply) => { + const thread = reply.thread; + thread.contextValue = 'draft'; + const newComment = new NoteComment(reply.text, vscode.CommentMode.Preview, { name: 'vscode' }, thread); + newComment.label = 'pending'; + thread.comments = [...thread.comments, newComment]; + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.finishDraft', (reply: vscode.CommentReply) => { + const thread = reply.thread; + + if (!thread) { + return; + } + + thread.contextValue = undefined; + thread.collapsibleState = vscode.CommentThreadCollapsibleState.Collapsed; + if (reply.text) { + const newComment = new NoteComment(reply.text, vscode.CommentMode.Preview, { name: 'vscode' }, thread); + thread.comments = [...thread.comments, newComment].map(comment => { + comment.label = undefined; + return comment; + }); + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.deleteNoteComment', (comment: NoteComment) => { + const thread = comment.parent; + if (!thread) { + return; + } + + thread.comments = thread.comments.filter(cmt => (cmt as NoteComment).id !== comment.id); + + if (thread.comments.length === 0) { + thread.dispose(); + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.deleteNote', (thread: vscode.CommentThread) => { + thread.dispose(); + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.cancelsaveNote', (comment: NoteComment) => { + if (!comment.parent) { + return; + } + + comment.parent.comments = comment.parent.comments.map(cmt => { + if ((cmt as NoteComment).id === comment.id) { + cmt.body = (cmt as NoteComment).savedBody; + cmt.mode = vscode.CommentMode.Preview; + } + + return cmt; + }); + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.saveNote', (comment: NoteComment) => { + if (!comment.parent) { + return; + } + + comment.parent.comments = comment.parent.comments.map(cmt => { + if ((cmt as NoteComment).id === comment.id) { + (cmt as NoteComment).savedBody = cmt.body; + cmt.mode = vscode.CommentMode.Preview; + } + + return cmt; + }); + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.editNote', (comment: NoteComment) => { + if (!comment.parent) { + return; + } + + comment.parent.comments = comment.parent.comments.map(cmt => { + if ((cmt as NoteComment).id === comment.id) { + cmt.mode = vscode.CommentMode.Editing; + } + + return cmt; + }); + })); + + context.subscriptions.push(vscode.commands.registerCommand('mywiki.dispose', () => { + commentController.dispose(); + })); + + function replyNote(reply: vscode.CommentReply) { + const thread = reply.thread; + const newComment = new NoteComment(reply.text, vscode.CommentMode.Preview, { name: 'vscode' }, thread, thread.comments.length ? 'canDelete' : undefined); + if (thread.contextValue === 'draft') { + newComment.label = 'pending'; + } + + thread.comments = [...thread.comments, newComment]; + } +} diff --git a/completions-sample/src/extension.ts b/completions-sample/src/extension.ts index f2169d7d..1f473099 100644 --- a/completions-sample/src/extension.ts +++ b/completions-sample/src/extension.ts @@ -1,74 +1,74 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - - const provider1 = vscode.languages.registerCompletionItemProvider('plaintext', { - - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) { - - // a simple completion item which inserts `Hello World!` - const simpleCompletion = new vscode.CompletionItem('Hello World!'); - - // a completion item that inserts its text as snippet, - // the `insertText`-property is a `SnippetString` which will be - // honored by the editor. - const snippetCompletion = new vscode.CompletionItem('Good part of the day'); - snippetCompletion.insertText = new vscode.SnippetString('Good ${1|morning,afternoon,evening|}. It is ${1}, right?'); - const docs : any = new vscode.MarkdownString("Inserts a snippet that lets you select [link](x.ts)."); - snippetCompletion.documentation = docs; - docs.baseUri = vscode.Uri.parse('http://example.com/a/b/c/'); - - // a completion item that can be accepted by a commit character, - // the `commitCharacters`-property is set which means that the completion will - // be inserted and then the character will be typed. - const commitCharacterCompletion = new vscode.CompletionItem('console'); - commitCharacterCompletion.commitCharacters = ['.']; - commitCharacterCompletion.documentation = new vscode.MarkdownString('Press `.` to get `console.`'); - - // a completion item that retriggers IntelliSense when being accepted, - // the `command`-property is set which the editor will execute after - // completion has been inserted. Also, the `insertText` is set so that - // a space is inserted after `new` - const commandCompletion = new vscode.CompletionItem('new'); - commandCompletion.kind = vscode.CompletionItemKind.Keyword; - commandCompletion.insertText = 'new '; - commandCompletion.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' }; - - // return all completion items as array - return [ - simpleCompletion, - snippetCompletion, - commitCharacterCompletion, - commandCompletion - ]; - } - }); - - const provider2 = vscode.languages.registerCompletionItemProvider( - 'plaintext', - { - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { - - // get all text until the `position` and check if it reads `console.` - // and if so then complete if `log`, `warn`, and `error` - const linePrefix = document.lineAt(position).text.substr(0, position.character); - if (!linePrefix.endsWith('console.')) { - return undefined; - } - - return [ - new vscode.CompletionItem('log', vscode.CompletionItemKind.Method), - new vscode.CompletionItem('warn', vscode.CompletionItemKind.Method), - new vscode.CompletionItem('error', vscode.CompletionItemKind.Method), - ]; - } - }, - '.' // triggered whenever a '.' is being typed - ); - - context.subscriptions.push(provider1, provider2); -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + + const provider1 = vscode.languages.registerCompletionItemProvider('plaintext', { + + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) { + + // a simple completion item which inserts `Hello World!` + const simpleCompletion = new vscode.CompletionItem('Hello World!'); + + // a completion item that inserts its text as snippet, + // the `insertText`-property is a `SnippetString` which will be + // honored by the editor. + const snippetCompletion = new vscode.CompletionItem('Good part of the day'); + snippetCompletion.insertText = new vscode.SnippetString('Good ${1|morning,afternoon,evening|}. It is ${1}, right?'); + const docs: any = new vscode.MarkdownString("Inserts a snippet that lets you select [link](x.ts)."); + snippetCompletion.documentation = docs; + docs.baseUri = vscode.Uri.parse('http://example.com/a/b/c/'); + + // a completion item that can be accepted by a commit character, + // the `commitCharacters`-property is set which means that the completion will + // be inserted and then the character will be typed. + const commitCharacterCompletion = new vscode.CompletionItem('console'); + commitCharacterCompletion.commitCharacters = ['.']; + commitCharacterCompletion.documentation = new vscode.MarkdownString('Press `.` to get `console.`'); + + // a completion item that retriggers IntelliSense when being accepted, + // the `command`-property is set which the editor will execute after + // completion has been inserted. Also, the `insertText` is set so that + // a space is inserted after `new` + const commandCompletion = new vscode.CompletionItem('new'); + commandCompletion.kind = vscode.CompletionItemKind.Keyword; + commandCompletion.insertText = 'new '; + commandCompletion.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' }; + + // return all completion items as array + return [ + simpleCompletion, + snippetCompletion, + commitCharacterCompletion, + commandCompletion + ]; + } + }); + + const provider2 = vscode.languages.registerCompletionItemProvider( + 'plaintext', + { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { + + // get all text until the `position` and check if it reads `console.` + // and if so then complete if `log`, `warn`, and `error` + const linePrefix = document.lineAt(position).text.substr(0, position.character); + if (!linePrefix.endsWith('console.')) { + return undefined; + } + + return [ + new vscode.CompletionItem('log', vscode.CompletionItemKind.Method), + new vscode.CompletionItem('warn', vscode.CompletionItemKind.Method), + new vscode.CompletionItem('error', vscode.CompletionItemKind.Method), + ]; + } + }, + '.' // triggered whenever a '.' is being typed + ); + + context.subscriptions.push(provider1, provider2); +} diff --git a/configuration-sample/src/extension.ts b/configuration-sample/src/extension.ts index 7322986c..8324def9 100644 --- a/configuration-sample/src/extension.ts +++ b/configuration-sample/src/extension.ts @@ -1,225 +1,225 @@ -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - - // Example: Reading Window scoped configuration - const configuredView = vscode.workspace.getConfiguration().get('conf.view.showOnWindowOpen'); - switch (configuredView) { - case 'explorer': - vscode.commands.executeCommand('workbench.view.explorer'); - break; - case 'search': - vscode.commands.executeCommand('workbench.view.search'); - break; - case 'scm': - vscode.commands.executeCommand('workbench.view.scm'); - break; - case 'debug': - vscode.commands.executeCommand('workbench.view.debug'); - break; - case 'extensions': - vscode.commands.executeCommand('workbench.view.extensions'); - break; - } - - // Example: Updating Window scoped configuration - vscode.commands.registerCommand('config.commands.configureViewOnWindowOpen', async () => { - - // 1) Getting the value - const value = await vscode.window.showQuickPick(['explorer', 'search', 'scm', 'debug', 'extensions'], { placeHolder: 'Select the view to show when opening a window.' }); - - if (vscode.workspace.workspaceFolders) { - - // 2) Getting the Configuration target - const target = await vscode.window.showQuickPick( - [ - { label: 'User', description: 'User Settings', target: vscode.ConfigurationTarget.Global }, - { label: 'Workspace', description: 'Workspace Settings', target: vscode.ConfigurationTarget.Workspace } - ], - { placeHolder: 'Select the view to show when opening a window.' }); - - if (value && target) { - - // 3) Update the configuration value in the target - await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value, target.target); - - /* - // Default is to update in Workspace - await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value); - */ - } - } else { - - // 2) Update the configuration value in User Setting in case of no workspace folders - await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value, vscode.ConfigurationTarget.Global); - } - - - }); - - // Example: Reading Resource scoped configuration for a file - context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => { - - // 1) Get the configured glob pattern value for the current file - const value: any = vscode.workspace.getConfiguration('', e.uri).get('conf.resource.insertEmptyLastLine'); - - // 2) Check if the current resource matches the glob pattern - const matches = value ? value[e.fileName] : undefined; - - // 3) If matches, insert empty last line - if (matches) { - vscode.window.showInformationMessage('An empty line will be added to the document ' + e.fileName); - } - - })); - - // Example: Updating Resource scoped Configuration for current file - vscode.commands.registerCommand('config.commands.configureEmptyLastLineCurrentFile', async () => { - - if (vscode.window.activeTextEditor) { - const currentDocument = vscode.window.activeTextEditor.document; - - // 1) Get the configuration for the current document - const configuration = vscode.workspace.getConfiguration('', currentDocument.uri); - - // 2) Get the configiuration value - const currentValue = configuration.get('conf.resource.insertEmptyLastLine', {}); - - // 3) Choose target to Global when there are no workspace folders - const target = vscode.workspace.workspaceFolders ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Global; - - const value = { ...currentValue, ...{ [currentDocument.fileName]: true } }; - - // 4) Update the configuration - await configuration.update('conf.resource.insertEmptyLastLine', value, target); - } - }); - - // Example: Updating Resource scoped Configuration - vscode.commands.registerCommand('config.commands.configureEmptyLastLineFiles', async () => { - - // 1) Getting the value - const value = await vscode.window.showInputBox({ prompt: 'Provide glob pattern of files to have empty last line.' }); - - if (vscode.workspace.workspaceFolders) { - - // 2) Getting the target - const target = await vscode.window.showQuickPick( - [ - { label: 'Application', description: 'User Settings', target: vscode.ConfigurationTarget.Global }, - { label: 'Workspace', description: 'Workspace Settings', target: vscode.ConfigurationTarget.Workspace }, - { label: 'Workspace Folder', description: 'Workspace Folder Settings', target: vscode.ConfigurationTarget.WorkspaceFolder } - ], - { placeHolder: 'Select the target to which this setting should be applied' }); - - if (value && target) { - - if (target.target === vscode.ConfigurationTarget.WorkspaceFolder) { - - // 3) Getting the workspace folder - const workspaceFolder = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Pick Workspace Folder to which this setting should be applied' }); - if (workspaceFolder) { - - // 4) Get the configuration for the workspace folder - const configuration = vscode.workspace.getConfiguration('', workspaceFolder.uri); - - // 5) Get the current value - const currentValue = configuration.get<{}>('conf.resource.insertEmptyLastLine'); - - const newValue = { ...currentValue, ...{ [value]: true } }; - - // 6) Update the configuration value - await configuration.update('conf.resource.insertEmptyLastLine', newValue, target.target); - } - } else { - - // 3) Get the configuration - const configuration = vscode.workspace.getConfiguration(); - - // 4) Get the current value - const currentValue = configuration.get<{}>('conf.resource.insertEmptyLastLine'); - - const newValue = { ...currentValue, ...(value ? { [value]: true } : {}) }; - - // 3) Update the value in the target - await vscode.workspace.getConfiguration().update('conf.resource.insertEmptyLastLine', newValue, target.target); - } - } - } else { - - // 2) Get the configuration - const configuration = vscode.workspace.getConfiguration(); - - // 3) Get the current value - const currentValue = configuration.get<{}>('conf.resource.insertEmptyLastLine'); - - const newValue = { ...currentValue, ...(value ? { [value]: true } : {}) }; - - // 4) Update the value in the User Settings - await vscode.workspace.getConfiguration().update('conf.resource.insertEmptyLastLine', newValue, vscode.ConfigurationTarget.Global); - } - }); - - let statusSizeDisposable: vscode.Disposable; - // Example: Reading language overridable configuration for a document - context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => { - - if (statusSizeDisposable) { - statusSizeDisposable.dispose(); - } - - // 1) Check if showing size is configured for current file - const showSize: any = vscode.workspace.getConfiguration('', e).get('conf.language.showSize'); - - // 3) If matches, insert empty last line - if (showSize) { - statusSizeDisposable = vscode.window.setStatusBarMessage(`${e.getText().length}`); - } - - })); - - // Example: Overriding configuration value for a language - context.subscriptions.push(vscode.commands.registerCommand('config.commands.overrideLanguageValue', async () => { - - // 1) Getting the languge id - const languageId = await vscode.window.showInputBox({ placeHolder: 'Enter the language id' }); - - // 2) Update - vscode.workspace.getConfiguration('', { languageId: languageId! }).update('conf.language.showSize', true, false, true); - - })); - - // Example: Listening to configuration changes - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { - - if (e.affectsConfiguration('conf.resource.insertEmptyLastLine')) { - if (vscode.window.activeTextEditor) { - - const currentDocument = vscode.window.activeTextEditor.document; - - // 1) Get the configured glob pattern value for the current file - const value: any = vscode.workspace.getConfiguration('', currentDocument.uri).get('conf.resource.insertEmptyLastLine'); - - // 2) Check if the current resource matches the glob pattern - const matches = value[currentDocument.fileName]; - - // 3) If matches, insert empty last line - if (matches) { - vscode.window.showInformationMessage('An empty line will be added to the document ' + currentDocument.fileName); - } - } - } - - // Check if a language configuration is changed for a text document - if (e.affectsConfiguration('conf.language.showSize', vscode.window.activeTextEditor?.document)) { - // noop - } - - // Check if a language configuration is changed for a language - if (e.affectsConfiguration('conf.language.showSize', { languageId: 'typescript' })) { - // noop - } - - })); - +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + + // Example: Reading Window scoped configuration + const configuredView = vscode.workspace.getConfiguration().get('conf.view.showOnWindowOpen'); + switch (configuredView) { + case 'explorer': + vscode.commands.executeCommand('workbench.view.explorer'); + break; + case 'search': + vscode.commands.executeCommand('workbench.view.search'); + break; + case 'scm': + vscode.commands.executeCommand('workbench.view.scm'); + break; + case 'debug': + vscode.commands.executeCommand('workbench.view.debug'); + break; + case 'extensions': + vscode.commands.executeCommand('workbench.view.extensions'); + break; + } + + // Example: Updating Window scoped configuration + vscode.commands.registerCommand('config.commands.configureViewOnWindowOpen', async () => { + + // 1) Getting the value + const value = await vscode.window.showQuickPick(['explorer', 'search', 'scm', 'debug', 'extensions'], { placeHolder: 'Select the view to show when opening a window.' }); + + if (vscode.workspace.workspaceFolders) { + + // 2) Getting the Configuration target + const target = await vscode.window.showQuickPick( + [ + { label: 'User', description: 'User Settings', target: vscode.ConfigurationTarget.Global }, + { label: 'Workspace', description: 'Workspace Settings', target: vscode.ConfigurationTarget.Workspace } + ], + { placeHolder: 'Select the view to show when opening a window.' }); + + if (value && target) { + + // 3) Update the configuration value in the target + await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value, target.target); + + /* + // Default is to update in Workspace + await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value); + */ + } + } else { + + // 2) Update the configuration value in User Setting in case of no workspace folders + await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value, vscode.ConfigurationTarget.Global); + } + + + }); + + // Example: Reading Resource scoped configuration for a file + context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => { + + // 1) Get the configured glob pattern value for the current file + const value: any = vscode.workspace.getConfiguration('', e.uri).get('conf.resource.insertEmptyLastLine'); + + // 2) Check if the current resource matches the glob pattern + const matches = value ? value[e.fileName] : undefined; + + // 3) If matches, insert empty last line + if (matches) { + vscode.window.showInformationMessage('An empty line will be added to the document ' + e.fileName); + } + + })); + + // Example: Updating Resource scoped Configuration for current file + vscode.commands.registerCommand('config.commands.configureEmptyLastLineCurrentFile', async () => { + + if (vscode.window.activeTextEditor) { + const currentDocument = vscode.window.activeTextEditor.document; + + // 1) Get the configuration for the current document + const configuration = vscode.workspace.getConfiguration('', currentDocument.uri); + + // 2) Get the configiuration value + const currentValue = configuration.get('conf.resource.insertEmptyLastLine', {}); + + // 3) Choose target to Global when there are no workspace folders + const target = vscode.workspace.workspaceFolders ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Global; + + const value = { ...currentValue, ...{ [currentDocument.fileName]: true } }; + + // 4) Update the configuration + await configuration.update('conf.resource.insertEmptyLastLine', value, target); + } + }); + + // Example: Updating Resource scoped Configuration + vscode.commands.registerCommand('config.commands.configureEmptyLastLineFiles', async () => { + + // 1) Getting the value + const value = await vscode.window.showInputBox({ prompt: 'Provide glob pattern of files to have empty last line.' }); + + if (vscode.workspace.workspaceFolders) { + + // 2) Getting the target + const target = await vscode.window.showQuickPick( + [ + { label: 'Application', description: 'User Settings', target: vscode.ConfigurationTarget.Global }, + { label: 'Workspace', description: 'Workspace Settings', target: vscode.ConfigurationTarget.Workspace }, + { label: 'Workspace Folder', description: 'Workspace Folder Settings', target: vscode.ConfigurationTarget.WorkspaceFolder } + ], + { placeHolder: 'Select the target to which this setting should be applied' }); + + if (value && target) { + + if (target.target === vscode.ConfigurationTarget.WorkspaceFolder) { + + // 3) Getting the workspace folder + const workspaceFolder = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Pick Workspace Folder to which this setting should be applied' }); + if (workspaceFolder) { + + // 4) Get the configuration for the workspace folder + const configuration = vscode.workspace.getConfiguration('', workspaceFolder.uri); + + // 5) Get the current value + const currentValue = configuration.get<{}>('conf.resource.insertEmptyLastLine'); + + const newValue = { ...currentValue, ...{ [value]: true } }; + + // 6) Update the configuration value + await configuration.update('conf.resource.insertEmptyLastLine', newValue, target.target); + } + } else { + + // 3) Get the configuration + const configuration = vscode.workspace.getConfiguration(); + + // 4) Get the current value + const currentValue = configuration.get<{}>('conf.resource.insertEmptyLastLine'); + + const newValue = { ...currentValue, ...(value ? { [value]: true } : {}) }; + + // 3) Update the value in the target + await vscode.workspace.getConfiguration().update('conf.resource.insertEmptyLastLine', newValue, target.target); + } + } + } else { + + // 2) Get the configuration + const configuration = vscode.workspace.getConfiguration(); + + // 3) Get the current value + const currentValue = configuration.get<{}>('conf.resource.insertEmptyLastLine'); + + const newValue = { ...currentValue, ...(value ? { [value]: true } : {}) }; + + // 4) Update the value in the User Settings + await vscode.workspace.getConfiguration().update('conf.resource.insertEmptyLastLine', newValue, vscode.ConfigurationTarget.Global); + } + }); + + let statusSizeDisposable: vscode.Disposable; + // Example: Reading language overridable configuration for a document + context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => { + + if (statusSizeDisposable) { + statusSizeDisposable.dispose(); + } + + // 1) Check if showing size is configured for current file + const showSize: any = vscode.workspace.getConfiguration('', e).get('conf.language.showSize'); + + // 3) If matches, insert empty last line + if (showSize) { + statusSizeDisposable = vscode.window.setStatusBarMessage(`${e.getText().length}`); + } + + })); + + // Example: Overriding configuration value for a language + context.subscriptions.push(vscode.commands.registerCommand('config.commands.overrideLanguageValue', async () => { + + // 1) Getting the languge id + const languageId = await vscode.window.showInputBox({ placeHolder: 'Enter the language id' }); + + // 2) Update + vscode.workspace.getConfiguration('', { languageId: languageId! }).update('conf.language.showSize', true, false, true); + + })); + + // Example: Listening to configuration changes + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { + + if (e.affectsConfiguration('conf.resource.insertEmptyLastLine')) { + if (vscode.window.activeTextEditor) { + + const currentDocument = vscode.window.activeTextEditor.document; + + // 1) Get the configured glob pattern value for the current file + const value: any = vscode.workspace.getConfiguration('', currentDocument.uri).get('conf.resource.insertEmptyLastLine'); + + // 2) Check if the current resource matches the glob pattern + const matches = value[currentDocument.fileName]; + + // 3) If matches, insert empty last line + if (matches) { + vscode.window.showInformationMessage('An empty line will be added to the document ' + currentDocument.fileName); + } + } + } + + // Check if a language configuration is changed for a text document + if (e.affectsConfiguration('conf.language.showSize', vscode.window.activeTextEditor?.document)) { + // noop + } + + // Check if a language configuration is changed for a language + if (e.affectsConfiguration('conf.language.showSize', { languageId: 'typescript' })) { + // noop + } + + })); + } \ No newline at end of file diff --git a/contentprovider-sample/src/extension.ts b/contentprovider-sample/src/extension.ts index ccffa01b..420004bc 100644 --- a/contentprovider-sample/src/extension.ts +++ b/contentprovider-sample/src/extension.ts @@ -1,31 +1,31 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import { workspace, languages, window, commands, ExtensionContext, Disposable } from 'vscode'; -import ContentProvider, { encodeLocation } from './provider'; - -export function activate(context: ExtensionContext) { - - const provider = new ContentProvider(); - - // register content provider for scheme `references` - // register document link provider for scheme `references` - const providerRegistrations = Disposable.from( - workspace.registerTextDocumentContentProvider(ContentProvider.scheme, provider), - languages.registerDocumentLinkProvider({ scheme: ContentProvider.scheme }, provider) - ); - - // register command that crafts an uri with the `references` scheme, - // open the dynamic document, and shows it in the next editor - const commandRegistration = commands.registerTextEditorCommand('editor.printReferences', editor => { - const uri = encodeLocation(editor.document.uri, editor.selection.active); - return workspace.openTextDocument(uri).then(doc => window.showTextDocument(doc, editor.viewColumn! + 1)); - }); - - context.subscriptions.push( - provider, - commandRegistration, - providerRegistrations - ); -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { workspace, languages, window, commands, ExtensionContext, Disposable } from 'vscode'; +import ContentProvider, { encodeLocation } from './provider'; + +export function activate(context: ExtensionContext) { + + const provider = new ContentProvider(); + + // register content provider for scheme `references` + // register document link provider for scheme `references` + const providerRegistrations = Disposable.from( + workspace.registerTextDocumentContentProvider(ContentProvider.scheme, provider), + languages.registerDocumentLinkProvider({ scheme: ContentProvider.scheme }, provider) + ); + + // register command that crafts an uri with the `references` scheme, + // open the dynamic document, and shows it in the next editor + const commandRegistration = commands.registerTextEditorCommand('editor.printReferences', editor => { + const uri = encodeLocation(editor.document.uri, editor.selection.active); + return workspace.openTextDocument(uri).then(doc => window.showTextDocument(doc, editor.viewColumn! + 1)); + }); + + context.subscriptions.push( + provider, + commandRegistration, + providerRegistrations + ); +} diff --git a/contentprovider-sample/src/provider.ts b/contentprovider-sample/src/provider.ts index ebf82799..c54be91f 100644 --- a/contentprovider-sample/src/provider.ts +++ b/contentprovider-sample/src/provider.ts @@ -1,99 +1,99 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import ReferencesDocument from './referencesDocument'; - -export default class Provider implements vscode.TextDocumentContentProvider, vscode.DocumentLinkProvider { - - static scheme = 'references'; - - private _onDidChange = new vscode.EventEmitter(); - private _documents = new Map(); - private _editorDecoration = vscode.window.createTextEditorDecorationType({ textDecoration: 'underline' }); - private _subscriptions: vscode.Disposable; - - constructor() { - - // Listen to the `closeTextDocument`-event which means we must - // clear the corresponding model object - `ReferencesDocument` - this._subscriptions = vscode.workspace.onDidCloseTextDocument(doc => this._documents.delete(doc.uri.toString())); - } - - dispose() { - this._subscriptions.dispose(); - this._documents.clear(); - this._editorDecoration.dispose(); - this._onDidChange.dispose(); - } - - // Expose an event to signal changes of _virtual_ documents - // to the editor - get onDidChange() { - return this._onDidChange.event; - } - - // Provider method that takes an uri of the `references`-scheme and - // resolves its content by (1) running the reference search command - // and (2) formatting the results - provideTextDocumentContent(uri: vscode.Uri): string | Thenable { - - // already loaded? - const document = this._documents.get(uri.toString()); - if (document) { - return document.value; - } - - // Decode target-uri and target-position from the provided uri and execute the - // `reference provider` command (https://code.visualstudio.com/api/references/commands). - // From the result create a references document which is in charge of loading, - // printing, and formatting references - const [target, pos] = decodeLocation(uri); - return vscode.commands.executeCommand('vscode.executeReferenceProvider', target, pos).then(locations => { - locations = locations || []; - - // sort by locations and shuffle to begin from target resource - let idx = 0; - locations.sort(Provider._compareLocations).find((loc, i) => loc.uri.toString() === target.toString() && !!(idx = i) && true); - locations.push(...locations.splice(0, idx)); - - // create document and return its early state - const document = new ReferencesDocument(uri, locations, this._onDidChange); - this._documents.set(uri.toString(), document); - return document.value; - }); - } - - private static _compareLocations(a: vscode.Location, b: vscode.Location): number { - if (a.uri.toString() < b.uri.toString()) { - return -1; - } else if (a.uri.toString() > b.uri.toString()) { - return 1; - } else { - return a.range.start.compareTo(b.range.start); - } - } - - provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.DocumentLink[] | undefined { - // While building the virtual document we have already created the links. - // Those are composed from the range inside the document and a target uri - // to which they point - const doc = this._documents.get(document.uri.toString()); - if (doc) { - return doc.links; - } - } -} - -let seq = 0; - -export function encodeLocation(uri: vscode.Uri, pos: vscode.Position): vscode.Uri { - const query = JSON.stringify([uri.toString(), pos.line, pos.character]); - return vscode.Uri.parse(`${Provider.scheme}:References.locations?${query}#${seq++}`); -} - -export function decodeLocation(uri: vscode.Uri): [vscode.Uri, vscode.Position] { - const [target, line, character] = <[string, number, number]>JSON.parse(uri.query); - return [vscode.Uri.parse(target), new vscode.Position(line, character)]; -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import ReferencesDocument from './referencesDocument'; + +export default class Provider implements vscode.TextDocumentContentProvider, vscode.DocumentLinkProvider { + + static scheme = 'references'; + + private _onDidChange = new vscode.EventEmitter(); + private _documents = new Map(); + private _editorDecoration = vscode.window.createTextEditorDecorationType({ textDecoration: 'underline' }); + private _subscriptions: vscode.Disposable; + + constructor() { + + // Listen to the `closeTextDocument`-event which means we must + // clear the corresponding model object - `ReferencesDocument` + this._subscriptions = vscode.workspace.onDidCloseTextDocument(doc => this._documents.delete(doc.uri.toString())); + } + + dispose() { + this._subscriptions.dispose(); + this._documents.clear(); + this._editorDecoration.dispose(); + this._onDidChange.dispose(); + } + + // Expose an event to signal changes of _virtual_ documents + // to the editor + get onDidChange() { + return this._onDidChange.event; + } + + // Provider method that takes an uri of the `references`-scheme and + // resolves its content by (1) running the reference search command + // and (2) formatting the results + provideTextDocumentContent(uri: vscode.Uri): string | Thenable { + + // already loaded? + const document = this._documents.get(uri.toString()); + if (document) { + return document.value; + } + + // Decode target-uri and target-position from the provided uri and execute the + // `reference provider` command (https://code.visualstudio.com/api/references/commands). + // From the result create a references document which is in charge of loading, + // printing, and formatting references + const [target, pos] = decodeLocation(uri); + return vscode.commands.executeCommand('vscode.executeReferenceProvider', target, pos).then(locations => { + locations = locations || []; + + // sort by locations and shuffle to begin from target resource + let idx = 0; + locations.sort(Provider._compareLocations).find((loc, i) => loc.uri.toString() === target.toString() && !!(idx = i) && true); + locations.push(...locations.splice(0, idx)); + + // create document and return its early state + const document = new ReferencesDocument(uri, locations, this._onDidChange); + this._documents.set(uri.toString(), document); + return document.value; + }); + } + + private static _compareLocations(a: vscode.Location, b: vscode.Location): number { + if (a.uri.toString() < b.uri.toString()) { + return -1; + } else if (a.uri.toString() > b.uri.toString()) { + return 1; + } else { + return a.range.start.compareTo(b.range.start); + } + } + + provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.DocumentLink[] | undefined { + // While building the virtual document we have already created the links. + // Those are composed from the range inside the document and a target uri + // to which they point + const doc = this._documents.get(document.uri.toString()); + if (doc) { + return doc.links; + } + } +} + +let seq = 0; + +export function encodeLocation(uri: vscode.Uri, pos: vscode.Position): vscode.Uri { + const query = JSON.stringify([uri.toString(), pos.line, pos.character]); + return vscode.Uri.parse(`${Provider.scheme}:References.locations?${query}#${seq++}`); +} + +export function decodeLocation(uri: vscode.Uri): [vscode.Uri, vscode.Position] { + const [target, line, character] = <[string, number, number]>JSON.parse(uri.query); + return [vscode.Uri.parse(target), new vscode.Position(line, character)]; +} diff --git a/contentprovider-sample/src/referencesDocument.ts b/contentprovider-sample/src/referencesDocument.ts index b1455812..b9d337d6 100644 --- a/contentprovider-sample/src/referencesDocument.ts +++ b/contentprovider-sample/src/referencesDocument.ts @@ -1,113 +1,113 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import * as vscode from 'vscode'; - -export default class ReferencesDocument { - - private readonly _uri: vscode.Uri; - private readonly _emitter: vscode.EventEmitter; - private readonly _locations: vscode.Location[]; - - private readonly _lines: string[]; - private readonly _links: vscode.DocumentLink[]; - - constructor(uri: vscode.Uri, locations: vscode.Location[], emitter: vscode.EventEmitter) { - this._uri = uri; - this._locations = locations; - - // The ReferencesDocument has access to the event emitter from - // the containing provider. This allows it to signal changes - this._emitter = emitter; - - // Start with printing a header and start resolving - this._lines = [`Found ${this._locations.length} references`]; - this._links = []; - this._populate(); - } - - get value() { - return this._lines.join('\n'); - } - - get links() { - return this._links; - } - - private async _populate() { - - // group all locations by files containing them - const groups: vscode.Location[][] = []; - let group: vscode.Location[] = []; - for (const loc of this._locations) { - if (group.length === 0 || group[0].uri.toString() !== loc.uri.toString()) { - group = []; - groups.push(group); - } - group.push(loc); - } - - // - for (const group of groups) { - const uri = group[0].uri; - const ranges = group.map(loc => loc.range); - await this._fetchAndFormatLocations(uri, ranges); - this._emitter.fire(this._uri); - } - } - - private async _fetchAndFormatLocations(uri: vscode.Uri, ranges: vscode.Range[]): Promise { - // Fetch the document denoted by the uri and format the matches - // with leading and trailing content form the document. Make sure - // to not duplicate lines - try { - const doc = await vscode.workspace.openTextDocument(uri); - this._lines.push('', uri.toString()); - for (let i = 0; i < ranges.length; i++) { - const { start: { line } } = ranges[i]; - this._appendLeading(doc, line, ranges[i - 1]); - this._appendMatch(doc, line, ranges[i], uri); - this._appendTrailing(doc, line, ranges[i + 1]); - } - } catch (err) { - this._lines.push('', `Failed to load '${uri.toString()}'\n\n${String(err)}`, ''); - } - } - - private _appendLeading(doc: vscode.TextDocument, line: number, previous: vscode.Range): void { - let from = Math.max(0, line - 3, previous && previous.end.line || 0); - while (++from < line) { - const text = doc.lineAt(from).text; - this._lines.push(` ${from + 1}` + (text && ` ${text}`)); - } - } - - private _appendMatch(doc: vscode.TextDocument, line: number, match: vscode.Range, target: vscode.Uri) { - const text = doc.lineAt(line).text; - const preamble = ` ${line + 1}: `; - - // Append line, use new length of lines-array as line number - // for a link that point to the reference - const len = this._lines.push(preamble + text); - - // Create a document link that will reveal the reference - const linkRange = new vscode.Range(len - 1, preamble.length + match.start.character, len - 1, preamble.length + match.end.character); - const linkTarget = target.with({ fragment: String(1 + match.start.line) }); - this._links.push(new vscode.DocumentLink(linkRange, linkTarget)); - } - - private _appendTrailing(doc: vscode.TextDocument, line: number, next: vscode.Range): void { - const to = Math.min(doc.lineCount, line + 3); - if (next && next.start.line - to <= 2) { - return; // next is too close, _appendLeading does the work - } - while (++line < to) { - const text = doc.lineAt(line).text; - this._lines.push(` ${line + 1}` + (text && ` ${text}`)); - } - if (next) { - this._lines.push(` ...`); - } - } -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export default class ReferencesDocument { + + private readonly _uri: vscode.Uri; + private readonly _emitter: vscode.EventEmitter; + private readonly _locations: vscode.Location[]; + + private readonly _lines: string[]; + private readonly _links: vscode.DocumentLink[]; + + constructor(uri: vscode.Uri, locations: vscode.Location[], emitter: vscode.EventEmitter) { + this._uri = uri; + this._locations = locations; + + // The ReferencesDocument has access to the event emitter from + // the containing provider. This allows it to signal changes + this._emitter = emitter; + + // Start with printing a header and start resolving + this._lines = [`Found ${this._locations.length} references`]; + this._links = []; + this._populate(); + } + + get value() { + return this._lines.join('\n'); + } + + get links() { + return this._links; + } + + private async _populate() { + + // group all locations by files containing them + const groups: vscode.Location[][] = []; + let group: vscode.Location[] = []; + for (const loc of this._locations) { + if (group.length === 0 || group[0].uri.toString() !== loc.uri.toString()) { + group = []; + groups.push(group); + } + group.push(loc); + } + + // + for (const group of groups) { + const uri = group[0].uri; + const ranges = group.map(loc => loc.range); + await this._fetchAndFormatLocations(uri, ranges); + this._emitter.fire(this._uri); + } + } + + private async _fetchAndFormatLocations(uri: vscode.Uri, ranges: vscode.Range[]): Promise { + // Fetch the document denoted by the uri and format the matches + // with leading and trailing content form the document. Make sure + // to not duplicate lines + try { + const doc = await vscode.workspace.openTextDocument(uri); + this._lines.push('', uri.toString()); + for (let i = 0; i < ranges.length; i++) { + const { start: { line } } = ranges[i]; + this._appendLeading(doc, line, ranges[i - 1]); + this._appendMatch(doc, line, ranges[i], uri); + this._appendTrailing(doc, line, ranges[i + 1]); + } + } catch (err) { + this._lines.push('', `Failed to load '${uri.toString()}'\n\n${String(err)}`, ''); + } + } + + private _appendLeading(doc: vscode.TextDocument, line: number, previous: vscode.Range): void { + let from = Math.max(0, line - 3, previous && previous.end.line || 0); + while (++from < line) { + const text = doc.lineAt(from).text; + this._lines.push(` ${from + 1}` + (text && ` ${text}`)); + } + } + + private _appendMatch(doc: vscode.TextDocument, line: number, match: vscode.Range, target: vscode.Uri) { + const text = doc.lineAt(line).text; + const preamble = ` ${line + 1}: `; + + // Append line, use new length of lines-array as line number + // for a link that point to the reference + const len = this._lines.push(preamble + text); + + // Create a document link that will reveal the reference + const linkRange = new vscode.Range(len - 1, preamble.length + match.start.character, len - 1, preamble.length + match.end.character); + const linkTarget = target.with({ fragment: String(1 + match.start.line) }); + this._links.push(new vscode.DocumentLink(linkRange, linkTarget)); + } + + private _appendTrailing(doc: vscode.TextDocument, line: number, next: vscode.Range): void { + const to = Math.min(doc.lineCount, line + 3); + if (next && next.start.line - to <= 2) { + return; // next is too close, _appendLeading does the work + } + while (++line < to) { + const text = doc.lineAt(line).text; + this._lines.push(` ${line + 1}` + (text && ` ${text}`)); + } + if (next) { + this._lines.push(` ...`); + } + } +} diff --git a/custom-editor-sample/src/catScratchEditor.ts b/custom-editor-sample/src/catScratchEditor.ts index 4325ca5e..6852e9e1 100644 --- a/custom-editor-sample/src/catScratchEditor.ts +++ b/custom-editor-sample/src/catScratchEditor.ts @@ -1,205 +1,205 @@ -import * as vscode from 'vscode'; -import { getNonce } from './util'; - -/** - * Provider for cat scratch editors. - * - * Cat scratch editors are used for `.cscratch` files, which are just json files. - * To get started, run this extension and open an empty `.cscratch` file in VS Code. - * - * This provider demonstrates: - * - * - Setting up the initial webview for a custom editor. - * - Loading scripts and styles in a custom editor. - * - Synchronizing changes between a text document and a custom editor. - */ -export class CatScratchEditorProvider implements vscode.CustomTextEditorProvider { - - public static register(context: vscode.ExtensionContext): vscode.Disposable { - const provider = new CatScratchEditorProvider(context); - const providerRegistration = vscode.window.registerCustomEditorProvider(CatScratchEditorProvider.viewType, provider); - return providerRegistration; - } - - private static readonly viewType = 'catCustoms.catScratch'; - - private static readonly scratchCharacters = ['😸', '😹', '😺', '😻', '😼', '😽', '😾', '🙀', '😿', '🐱']; - - constructor( - private readonly context: vscode.ExtensionContext - ) { } - - /** - * Called when our custom editor is opened. - * - * - */ - public async resolveCustomTextEditor( - document: vscode.TextDocument, - webviewPanel: vscode.WebviewPanel, - _token: vscode.CancellationToken - ): Promise { - // Setup initial content for the webview - webviewPanel.webview.options = { - enableScripts: true, - }; - webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview); - - function updateWebview() { - webviewPanel.webview.postMessage({ - type: 'update', - text: document.getText(), - }); - } - - // Hook up event handlers so that we can synchronize the webview with the text document. - // - // The text document acts as our model, so we have to sync change in the document to our - // editor and sync changes in the editor back to the document. - // - // Remember that a single text document can also be shared between multiple custom - // editors (this happens for example when you split a custom editor) - - const changeDocumentSubscription = vscode.workspace.onDidChangeTextDocument(e => { - if (e.document.uri.toString() === document.uri.toString()) { - updateWebview(); - } - }); - - // Make sure we get rid of the listener when our editor is closed. - webviewPanel.onDidDispose(() => { - changeDocumentSubscription.dispose(); - }); - - // Receive message from the webview. - webviewPanel.webview.onDidReceiveMessage(e => { - switch (e.type) { - case 'add': - this.addNewScratch(document); - return; - - case 'delete': - this.deleteScratch(document, e.id); - return; - } - }); - - updateWebview(); - } - - /** - * Get the static html used for the editor webviews. - */ - private getHtmlForWebview(webview: vscode.Webview): string { - // Local path to script and css for the webview - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath( - this.context.extensionUri, 'media', 'catScratch.js')); - - const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath( - this.context.extensionUri, 'media', 'reset.css')); - - const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath( - this.context.extensionUri, 'media', 'vscode.css')); - - const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath( - this.context.extensionUri, 'media', 'catScratch.css')); - - // Use a nonce to whitelist which scripts can be run - const nonce = getNonce(); - - return /* html */` - - - - - - - - - - - - - - - Cat Scratch - - -
-
- -
-
- - - - `; - } - - /** - * Add a new scratch to the current document. - */ - private addNewScratch(document: vscode.TextDocument) { - const json = this.getDocumentAsJson(document); - const character = CatScratchEditorProvider.scratchCharacters[Math.floor(Math.random() * CatScratchEditorProvider.scratchCharacters.length)]; - json.scratches = [ - ...(Array.isArray(json.scratches) ? json.scratches : []), - { - id: getNonce(), - text: character, - created: Date.now(), - } - ]; - - return this.updateTextDocument(document, json); - } - - /** - * Delete an existing scratch from a document. - */ - private deleteScratch(document: vscode.TextDocument, id: string) { - const json = this.getDocumentAsJson(document); - if (!Array.isArray(json.scratches)) { - return; - } - - json.scratches = json.scratches.filter((note: any) => note.id !== id); - - return this.updateTextDocument(document, json); - } - - /** - * Try to get a current document as json text. - */ - private getDocumentAsJson(document: vscode.TextDocument): any { - const text = document.getText(); - if (text.trim().length === 0) { - return {}; - } - - try { - return JSON.parse(text); - } catch { - throw new Error('Could not get document as json. Content is not valid json'); - } - } - - /** - * Write out the json to a given document. - */ - private updateTextDocument(document: vscode.TextDocument, json: any) { - const edit = new vscode.WorkspaceEdit(); - - // Just replace the entire document every time for this example extension. - // A more complete extension should compute minimal edits instead. - edit.replace( - document.uri, - new vscode.Range(0, 0, document.lineCount, 0), - JSON.stringify(json, null, 2)); - - return vscode.workspace.applyEdit(edit); - } -} +import * as vscode from 'vscode'; +import { getNonce } from './util'; + +/** + * Provider for cat scratch editors. + * + * Cat scratch editors are used for `.cscratch` files, which are just json files. + * To get started, run this extension and open an empty `.cscratch` file in VS Code. + * + * This provider demonstrates: + * + * - Setting up the initial webview for a custom editor. + * - Loading scripts and styles in a custom editor. + * - Synchronizing changes between a text document and a custom editor. + */ +export class CatScratchEditorProvider implements vscode.CustomTextEditorProvider { + + public static register(context: vscode.ExtensionContext): vscode.Disposable { + const provider = new CatScratchEditorProvider(context); + const providerRegistration = vscode.window.registerCustomEditorProvider(CatScratchEditorProvider.viewType, provider); + return providerRegistration; + } + + private static readonly viewType = 'catCustoms.catScratch'; + + private static readonly scratchCharacters = ['😸', '😹', '😺', '😻', '😼', '😽', '😾', '🙀', '😿', '🐱']; + + constructor( + private readonly context: vscode.ExtensionContext + ) { } + + /** + * Called when our custom editor is opened. + * + * + */ + public async resolveCustomTextEditor( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel, + _token: vscode.CancellationToken + ): Promise { + // Setup initial content for the webview + webviewPanel.webview.options = { + enableScripts: true, + }; + webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview); + + function updateWebview() { + webviewPanel.webview.postMessage({ + type: 'update', + text: document.getText(), + }); + } + + // Hook up event handlers so that we can synchronize the webview with the text document. + // + // The text document acts as our model, so we have to sync change in the document to our + // editor and sync changes in the editor back to the document. + // + // Remember that a single text document can also be shared between multiple custom + // editors (this happens for example when you split a custom editor) + + const changeDocumentSubscription = vscode.workspace.onDidChangeTextDocument(e => { + if (e.document.uri.toString() === document.uri.toString()) { + updateWebview(); + } + }); + + // Make sure we get rid of the listener when our editor is closed. + webviewPanel.onDidDispose(() => { + changeDocumentSubscription.dispose(); + }); + + // Receive message from the webview. + webviewPanel.webview.onDidReceiveMessage(e => { + switch (e.type) { + case 'add': + this.addNewScratch(document); + return; + + case 'delete': + this.deleteScratch(document, e.id); + return; + } + }); + + updateWebview(); + } + + /** + * Get the static html used for the editor webviews. + */ + private getHtmlForWebview(webview: vscode.Webview): string { + // Local path to script and css for the webview + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath( + this.context.extensionUri, 'media', 'catScratch.js')); + + const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath( + this.context.extensionUri, 'media', 'reset.css')); + + const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath( + this.context.extensionUri, 'media', 'vscode.css')); + + const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath( + this.context.extensionUri, 'media', 'catScratch.css')); + + // Use a nonce to whitelist which scripts can be run + const nonce = getNonce(); + + return /* html */` + + + + + + + + + + + + + + + Cat Scratch + + +
+
+ +
+
+ + + + `; + } + + /** + * Add a new scratch to the current document. + */ + private addNewScratch(document: vscode.TextDocument) { + const json = this.getDocumentAsJson(document); + const character = CatScratchEditorProvider.scratchCharacters[Math.floor(Math.random() * CatScratchEditorProvider.scratchCharacters.length)]; + json.scratches = [ + ...(Array.isArray(json.scratches) ? json.scratches : []), + { + id: getNonce(), + text: character, + created: Date.now(), + } + ]; + + return this.updateTextDocument(document, json); + } + + /** + * Delete an existing scratch from a document. + */ + private deleteScratch(document: vscode.TextDocument, id: string) { + const json = this.getDocumentAsJson(document); + if (!Array.isArray(json.scratches)) { + return; + } + + json.scratches = json.scratches.filter((note: any) => note.id !== id); + + return this.updateTextDocument(document, json); + } + + /** + * Try to get a current document as json text. + */ + private getDocumentAsJson(document: vscode.TextDocument): any { + const text = document.getText(); + if (text.trim().length === 0) { + return {}; + } + + try { + return JSON.parse(text); + } catch { + throw new Error('Could not get document as json. Content is not valid json'); + } + } + + /** + * Write out the json to a given document. + */ + private updateTextDocument(document: vscode.TextDocument, json: any) { + const edit = new vscode.WorkspaceEdit(); + + // Just replace the entire document every time for this example extension. + // A more complete extension should compute minimal edits instead. + edit.replace( + document.uri, + new vscode.Range(0, 0, document.lineCount, 0), + JSON.stringify(json, null, 2)); + + return vscode.workspace.applyEdit(edit); + } +} diff --git a/custom-editor-sample/src/dispose.ts b/custom-editor-sample/src/dispose.ts index 93ee9ee5..9e9dedee 100644 --- a/custom-editor-sample/src/dispose.ts +++ b/custom-editor-sample/src/dispose.ts @@ -1,37 +1,37 @@ -import * as vscode from 'vscode'; - -export function disposeAll(disposables: vscode.Disposable[]): void { - 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(): boolean { - return this._isDisposed; - } +import * as vscode from 'vscode'; + +export function disposeAll(disposables: vscode.Disposable[]): void { + 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(): boolean { + 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 5baac788..71466f1d 100644 --- a/custom-editor-sample/src/extension.ts +++ b/custom-editor-sample/src/extension.ts @@ -1,9 +1,9 @@ -import * as vscode from 'vscode'; -import { CatScratchEditorProvider } from './catScratchEditor'; -import { PawDrawEditorProvider } from './pawDrawEditor'; - -export function activate(context: vscode.ExtensionContext) { - // Register our custom editor providers - context.subscriptions.push(CatScratchEditorProvider.register(context)); - context.subscriptions.push(PawDrawEditorProvider.register(context)); -} +import * as vscode from 'vscode'; +import { CatScratchEditorProvider } from './catScratchEditor'; +import { PawDrawEditorProvider } from './pawDrawEditor'; + +export function activate(context: vscode.ExtensionContext) { + // Register our custom editor providers + context.subscriptions.push(CatScratchEditorProvider.register(context)); + context.subscriptions.push(PawDrawEditorProvider.register(context)); +} diff --git a/custom-editor-sample/src/pawDrawEditor.ts b/custom-editor-sample/src/pawDrawEditor.ts index 0f0ab9ef..7c655d60 100644 --- a/custom-editor-sample/src/pawDrawEditor.ts +++ b/custom-editor-sample/src/pawDrawEditor.ts @@ -1,456 +1,456 @@ -import * as vscode from 'vscode'; -import { Disposable, disposeAll } from './dispose'; -import { getNonce } from './util'; - -/** - * 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 PawDrawDocument.readFile(dataFile); - return new PawDrawDocument(uri, fileData, delegate); - } - - private static async readFile(uri: vscode.Uri): Promise { - if (uri.scheme === 'untitled') { - return new Uint8Array(); - } - return new Uint8Array(await vscode.workspace.fs.readFile(uri)); - } - - 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 occurred 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 PawDrawDocument.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 { - - private static newPawDrawFileId = 1; - - public static register(context: vscode.ExtensionContext): vscode.Disposable { - vscode.commands.registerCommand('catCustoms.pawDraw.new', () => { - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders) { - vscode.window.showErrorMessage("Creating new Paw Draw files currently requires opening a workspace"); - return; - } - - const uri = vscode.Uri.joinPath(workspaceFolders[0].uri, `new-${PawDrawEditorProvider.newPawDrawFileId++}.pawdraw`) - .with({ scheme: 'untitled' }); - - vscode.commands.executeCommand('vscode.openWith', uri, PawDrawEditorProvider.viewType); - }); - - return vscode.window.registerCustomEditorProvider( - 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: PawDrawDocument = await PawDrawDocument.create(uri, openContext.backupId, { - getFileData: async () => { - const webviewsForDocument = 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(panel, 'getFileData', {}); - return new Uint8Array(response); - } - }); - - 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') { - if (document.uri.scheme === 'untitled') { - this.postMessage(webviewPanel, 'init', { - untitled: true, - editable: true, - }); - } else { - const editable = vscode.workspace.fs.isWritableFileSystem(document.uri.scheme); - - this.postMessage(webviewPanel, 'init', { - value: document.documentData, - editable, - }); - } - } - }); - } - - 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.joinPath( - this._context.extensionUri, 'media', 'pawDraw.js')); - - const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath( - this._context.extensionUri, 'media', 'reset.css')); - - const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath( - this._context.extensionUri, 'media', 'vscode.css')); - - const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath( - this._context.extensionUri, '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); - }); - } -} +import * as vscode from 'vscode'; +import { Disposable, disposeAll } from './dispose'; +import { getNonce } from './util'; + +/** + * 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 PawDrawDocument.readFile(dataFile); + return new PawDrawDocument(uri, fileData, delegate); + } + + private static async readFile(uri: vscode.Uri): Promise { + if (uri.scheme === 'untitled') { + return new Uint8Array(); + } + return new Uint8Array(await vscode.workspace.fs.readFile(uri)); + } + + 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 occurred 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 PawDrawDocument.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 { + + private static newPawDrawFileId = 1; + + public static register(context: vscode.ExtensionContext): vscode.Disposable { + vscode.commands.registerCommand('catCustoms.pawDraw.new', () => { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + vscode.window.showErrorMessage("Creating new Paw Draw files currently requires opening a workspace"); + return; + } + + const uri = vscode.Uri.joinPath(workspaceFolders[0].uri, `new-${PawDrawEditorProvider.newPawDrawFileId++}.pawdraw`) + .with({ scheme: 'untitled' }); + + vscode.commands.executeCommand('vscode.openWith', uri, PawDrawEditorProvider.viewType); + }); + + return vscode.window.registerCustomEditorProvider( + 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: PawDrawDocument = await PawDrawDocument.create(uri, openContext.backupId, { + getFileData: async () => { + const webviewsForDocument = 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(panel, 'getFileData', {}); + return new Uint8Array(response); + } + }); + + 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') { + if (document.uri.scheme === 'untitled') { + this.postMessage(webviewPanel, 'init', { + untitled: true, + editable: true, + }); + } else { + const editable = vscode.workspace.fs.isWritableFileSystem(document.uri.scheme); + + this.postMessage(webviewPanel, 'init', { + value: document.documentData, + editable, + }); + } + } + }); + } + + 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.joinPath( + this._context.extensionUri, 'media', 'pawDraw.js')); + + const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath( + this._context.extensionUri, 'media', 'reset.css')); + + const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath( + this._context.extensionUri, 'media', 'vscode.css')); + + const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath( + this._context.extensionUri, '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); + }); + } +} diff --git a/custom-editor-sample/src/util.ts b/custom-editor-sample/src/util.ts index 1e54bdff..945f8666 100644 --- a/custom-editor-sample/src/util.ts +++ b/custom-editor-sample/src/util.ts @@ -1,8 +1,8 @@ -export function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 32; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} +export function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/diagnostic-related-information-sample/src/extension.ts b/diagnostic-related-information-sample/src/extension.ts index 0c7480a3..43d61cc7 100644 --- a/diagnostic-related-information-sample/src/extension.ts +++ b/diagnostic-related-information-sample/src/extension.ts @@ -1,33 +1,33 @@ -'use strict'; -import * as vscode from 'vscode'; -import * as path from 'path'; - -export function activate(context: vscode.ExtensionContext) { - - const collection = vscode.languages.createDiagnosticCollection('test'); - if (vscode.window.activeTextEditor) { - updateDiagnostics(vscode.window.activeTextEditor.document, collection); - } - context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor) { - updateDiagnostics(editor.document, collection); - } - })); -} - -function updateDiagnostics(document: vscode.TextDocument, collection: vscode.DiagnosticCollection): void { - if (document && path.basename(document.uri.fsPath) === 'sample-demo.rs') { - collection.set(document.uri, [{ - code: '', - message: 'cannot assign twice to immutable variable `x`', - range: new vscode.Range(new vscode.Position(3, 4), new vscode.Position(3, 10)), - severity: vscode.DiagnosticSeverity.Error, - source: '', - relatedInformation: [ - new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`') - ] - }]); - } else { - collection.clear(); - } -} +'use strict'; +import * as vscode from 'vscode'; +import * as path from 'path'; + +export function activate(context: vscode.ExtensionContext) { + + const collection = vscode.languages.createDiagnosticCollection('test'); + if (vscode.window.activeTextEditor) { + updateDiagnostics(vscode.window.activeTextEditor.document, collection); + } + context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor) { + updateDiagnostics(editor.document, collection); + } + })); +} + +function updateDiagnostics(document: vscode.TextDocument, collection: vscode.DiagnosticCollection): void { + if (document && path.basename(document.uri.fsPath) === 'sample-demo.rs') { + collection.set(document.uri, [{ + code: '', + message: 'cannot assign twice to immutable variable `x`', + range: new vscode.Range(new vscode.Position(3, 4), new vscode.Position(3, 10)), + severity: vscode.DiagnosticSeverity.Error, + source: '', + relatedInformation: [ + new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`') + ] + }]); + } else { + collection.clear(); + } +} diff --git a/document-editing-sample/src/extension.ts b/document-editing-sample/src/extension.ts index 0c860f51..93062ad3 100644 --- a/document-editing-sample/src/extension.ts +++ b/document-editing-sample/src/extension.ts @@ -1,24 +1,24 @@ -'use strict'; - -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - const disposable = vscode.commands.registerCommand('extension.reverseWord', function () { - // Get the active text editor - const editor = vscode.window.activeTextEditor; - - if (editor) { - const document = editor.document; - const selection = editor.selection; - - // Get the word within the selection - const word = document.getText(selection); - const reversed = word.split('').reverse().join(''); - editor.edit(editBuilder => { - editBuilder.replace(selection, reversed); - }); - } - }); - - context.subscriptions.push(disposable); +'use strict'; + +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + const disposable = vscode.commands.registerCommand('extension.reverseWord', function() { + // Get the active text editor + const editor = vscode.window.activeTextEditor; + + if (editor) { + const document = editor.document; + const selection = editor.selection; + + // Get the word within the selection + const word = document.getText(selection); + const reversed = word.split('').reverse().join(''); + editor.edit(editBuilder => { + editBuilder.replace(selection, reversed); + }); + } + }); + + context.subscriptions.push(disposable); } \ No newline at end of file diff --git a/document-paste/src/extension.ts b/document-paste/src/extension.ts index 2ee9726b..409ec0bb 100644 --- a/document-paste/src/extension.ts +++ b/document-paste/src/extension.ts @@ -1,57 +1,57 @@ -import * as vscode from 'vscode'; - -/** - * Provider that maintains a count of the number of times it has copied text. - */ -class CopyCountPasteEditProvider implements vscode.DocumentPasteEditProvider { - - private readonly countMimeTypes = 'application/vnd.code.copydemo-copy-count'; - - private count = 0; - - prepareDocumentPaste?( - _document: vscode.TextDocument, - _ranges: readonly vscode.Range[], - dataTransfer: vscode.DataTransfer, - _token: vscode.CancellationToken - ): void | Thenable { - dataTransfer.set(this.countMimeTypes, new vscode.DataTransferItem(this.count++)); - dataTransfer.set('text/plain', new vscode.DataTransferItem(this.count++)); - } - - async provideDocumentPasteEdits( - _document: vscode.TextDocument, - _ranges: readonly vscode.Range[], - dataTransfer: vscode.DataTransfer, - _token: vscode.CancellationToken - ): Promise { - const countDataTransferItem = dataTransfer.get(this.countMimeTypes); - if (!countDataTransferItem) { - return undefined; - } - - const textDataTransferItem = dataTransfer.get('text') ?? dataTransfer.get('text/plain'); - if (!textDataTransferItem) { - return undefined; - } - - const count = await countDataTransferItem.asString(); - const text = await textDataTransferItem.asString(); - - // Build a snippet to insert - const snippet = new vscode.SnippetString(); - snippet.appendText(`(copy #${count}) ${text}`); - - return { insertText: snippet }; - } -} - -export function activate(context: vscode.ExtensionContext) { - // Enable our provider in plaintext files - const selector: vscode.DocumentSelector = { language: 'plaintext' }; - - // Register our provider - context.subscriptions.push(vscode.languages.registerDocumentPasteEditProvider(selector, new CopyCountPasteEditProvider(), { - pasteMimeTypes: ['text/plain'], - })); -} +import * as vscode from 'vscode'; + +/** + * Provider that maintains a count of the number of times it has copied text. + */ +class CopyCountPasteEditProvider implements vscode.DocumentPasteEditProvider { + + private readonly countMimeTypes = 'application/vnd.code.copydemo-copy-count'; + + private count = 0; + + prepareDocumentPaste?( + _document: vscode.TextDocument, + _ranges: readonly vscode.Range[], + dataTransfer: vscode.DataTransfer, + _token: vscode.CancellationToken + ): void | Thenable { + dataTransfer.set(this.countMimeTypes, new vscode.DataTransferItem(this.count++)); + dataTransfer.set('text/plain', new vscode.DataTransferItem(this.count++)); + } + + async provideDocumentPasteEdits( + _document: vscode.TextDocument, + _ranges: readonly vscode.Range[], + dataTransfer: vscode.DataTransfer, + _token: vscode.CancellationToken + ): Promise { + const countDataTransferItem = dataTransfer.get(this.countMimeTypes); + if (!countDataTransferItem) { + return undefined; + } + + const textDataTransferItem = dataTransfer.get('text') ?? dataTransfer.get('text/plain'); + if (!textDataTransferItem) { + return undefined; + } + + const count = await countDataTransferItem.asString(); + const text = await textDataTransferItem.asString(); + + // Build a snippet to insert + const snippet = new vscode.SnippetString(); + snippet.appendText(`(copy #${count}) ${text}`); + + return { insertText: snippet }; + } +} + +export function activate(context: vscode.ExtensionContext) { + // Enable our provider in plaintext files + const selector: vscode.DocumentSelector = { language: 'plaintext' }; + + // Register our provider + context.subscriptions.push(vscode.languages.registerDocumentPasteEditProvider(selector, new CopyCountPasteEditProvider(), { + pasteMimeTypes: ['text/plain'], + })); +} diff --git a/document-paste/src/vscode.proposed.documentPaste.d.ts b/document-paste/src/vscode.proposed.documentPaste.d.ts index 29335e1d..accae646 100644 --- a/document-paste/src/vscode.proposed.documentPaste.d.ts +++ b/document-paste/src/vscode.proposed.documentPaste.d.ts @@ -1,70 +1,70 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/30066/ - - /** - * Provider invoked when the user copies and pastes code. - */ - interface DocumentPasteEditProvider { - - /** - * Optional method invoked after the user copies text in a file. - * - * During {@link prepareDocumentPaste}, an extension can compute metadata that is attached to - * a {@link DataTransfer} and is passed back to the provider in {@link provideDocumentPasteEdits}. - * - * @param document Document where the copy took place. - * @param ranges Ranges being copied in the `document`. - * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@link provideDocumentPasteEdits}. - * @param token A cancellation token. - */ - prepareDocumentPaste?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): void | Thenable; - - /** - * Invoked before the user pastes into a document. - * - * In this method, extensions can return a workspace edit that replaces the standard pasting behavior. - * - * @param document Document being pasted into - * @param ranges Currently selected ranges in the document. - * @param dataTransfer The data transfer associated with the paste. - * @param token A cancellation token. - * - * @return Optional workspace edit that applies the paste. Return undefined to use standard pasting. - */ - provideDocumentPasteEdits(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; - } - - /** - * An operation applied on paste - */ - interface DocumentPasteEdit { - /** - * The text or snippet to insert at the pasted locations. - */ - readonly insertText: string | SnippetString; - - /** - * An optional additional edit to apply on paste. - */ - readonly additionalEdit?: WorkspaceEdit; - } - - interface DocumentPasteProviderMetadata { - /** - * Mime types that `provideDocumentPasteEdits` should be invoked for. - * - * Use the special `files` mimetype to indicate the provider should be invoked if any files are present in the `DataTransfer`. - */ - readonly pasteMimeTypes: readonly string[]; - } - - namespace languages { - export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable; - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/30066/ + + /** + * Provider invoked when the user copies and pastes code. + */ + interface DocumentPasteEditProvider { + + /** + * Optional method invoked after the user copies text in a file. + * + * During {@link prepareDocumentPaste}, an extension can compute metadata that is attached to + * a {@link DataTransfer} and is passed back to the provider in {@link provideDocumentPasteEdits}. + * + * @param document Document where the copy took place. + * @param ranges Ranges being copied in the `document`. + * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@link provideDocumentPasteEdits}. + * @param token A cancellation token. + */ + prepareDocumentPaste?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): void | Thenable; + + /** + * Invoked before the user pastes into a document. + * + * In this method, extensions can return a workspace edit that replaces the standard pasting behavior. + * + * @param document Document being pasted into + * @param ranges Currently selected ranges in the document. + * @param dataTransfer The data transfer associated with the paste. + * @param token A cancellation token. + * + * @return Optional workspace edit that applies the paste. Return undefined to use standard pasting. + */ + provideDocumentPasteEdits(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; + } + + /** + * An operation applied on paste + */ + interface DocumentPasteEdit { + /** + * The text or snippet to insert at the pasted locations. + */ + readonly insertText: string | SnippetString; + + /** + * An optional additional edit to apply on paste. + */ + readonly additionalEdit?: WorkspaceEdit; + } + + interface DocumentPasteProviderMetadata { + /** + * Mime types that `provideDocumentPasteEdits` should be invoked for. + * + * Use the special `files` mimetype to indicate the provider should be invoked if any files are present in the `DataTransfer`. + */ + readonly pasteMimeTypes: readonly string[]; + } + + namespace languages { + export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable; + } +} diff --git a/drop-on-document/src/extension.ts b/drop-on-document/src/extension.ts index e5948a67..cf99675a 100644 --- a/drop-on-document/src/extension.ts +++ b/drop-on-document/src/extension.ts @@ -1,105 +1,105 @@ -import * as vscode from 'vscode'; -import * as path from 'path'; - -const uriListMime = 'text/uri-list'; - -/** - * Provider that reverses dropped text. - * - * Note this does not apply to text that is drag and dropped with-in the current editor, - * only for text dropped from external apps. - */ -class ReverseTextOnDropProvider implements vscode.DocumentDropEditProvider { - async provideDocumentDropEdits( - _document: vscode.TextDocument, - position: vscode.Position, - dataTransfer: vscode.DataTransfer, - token: vscode.CancellationToken - ): Promise { - // Check the data transfer to see if we have some kind of text data - const dataTransferItem = dataTransfer.get('text') ?? dataTransfer.get('text/plain'); - if (!dataTransferItem) { - return undefined; - } - - const text = await dataTransferItem.asString(); - if (token.isCancellationRequested) { - return undefined; - } - - // Build a snippet to insert - const snippet = new vscode.SnippetString(); - // Adding the reversed text - snippet.appendText([...text].reverse().join('')); - - return { insertText: snippet }; - } -} - -/** - * Provider that inserts a numbered list of the names of dropped files. - * - * Try dropping one or more files from: - * - * - VS Code's explorer - * - The operating system - * - The open editors view - */ -class FileNameListOnDropProvider implements vscode.DocumentDropEditProvider { - async provideDocumentDropEdits( - _document: vscode.TextDocument, - position: vscode.Position, - dataTransfer: vscode.DataTransfer, - token: vscode.CancellationToken - ): Promise { - // Check the data transfer to see if we have dropped a list of uris - const dataTransferItem = dataTransfer.get(uriListMime); - if (!dataTransferItem) { - return undefined; - } - - // 'text/uri-list' contains a list of uris separated by new lines. - // Parse this to an array of uris. - const urlList = await dataTransferItem.asString(); - if (token.isCancellationRequested) { - return undefined; - } - - const uris: vscode.Uri[] = []; - for (const resource of urlList.split('\n')) { - try { - uris.push(vscode.Uri.parse(resource)); - } catch { - // noop - } - } - - if (!uris.length) { - return undefined; - } - - // Build a snippet to insert - const snippet = new vscode.SnippetString(); - uris.forEach((uri, index) => { - const name = path.basename(uri.path); - snippet.appendText(`${index + 1}. ${name}`); - snippet.appendTabstop(); - - if (index <= uris.length - 1 && uris.length > 1) { - snippet.appendText('\n'); - } - }); - - return { insertText: snippet }; - } -} - - -export function activate(context: vscode.ExtensionContext) { - // Enable our providers in plaintext files - const selector: vscode.DocumentSelector = { language: 'plaintext' }; - - // Register our providers - context.subscriptions.push(vscode.languages.registerDocumentDropEditProvider(selector, new ReverseTextOnDropProvider())); - context.subscriptions.push(vscode.languages.registerDocumentDropEditProvider(selector, new FileNameListOnDropProvider())); -} +import * as vscode from 'vscode'; +import * as path from 'path'; + +const uriListMime = 'text/uri-list'; + +/** + * Provider that reverses dropped text. + * + * Note this does not apply to text that is drag and dropped with-in the current editor, + * only for text dropped from external apps. + */ +class ReverseTextOnDropProvider implements vscode.DocumentDropEditProvider { + async provideDocumentDropEdits( + _document: vscode.TextDocument, + position: vscode.Position, + dataTransfer: vscode.DataTransfer, + token: vscode.CancellationToken + ): Promise { + // Check the data transfer to see if we have some kind of text data + const dataTransferItem = dataTransfer.get('text') ?? dataTransfer.get('text/plain'); + if (!dataTransferItem) { + return undefined; + } + + const text = await dataTransferItem.asString(); + if (token.isCancellationRequested) { + return undefined; + } + + // Build a snippet to insert + const snippet = new vscode.SnippetString(); + // Adding the reversed text + snippet.appendText([...text].reverse().join('')); + + return { insertText: snippet }; + } +} + +/** + * Provider that inserts a numbered list of the names of dropped files. + * + * Try dropping one or more files from: + * + * - VS Code's explorer + * - The operating system + * - The open editors view + */ +class FileNameListOnDropProvider implements vscode.DocumentDropEditProvider { + async provideDocumentDropEdits( + _document: vscode.TextDocument, + position: vscode.Position, + dataTransfer: vscode.DataTransfer, + token: vscode.CancellationToken + ): Promise { + // Check the data transfer to see if we have dropped a list of uris + const dataTransferItem = dataTransfer.get(uriListMime); + if (!dataTransferItem) { + return undefined; + } + + // 'text/uri-list' contains a list of uris separated by new lines. + // Parse this to an array of uris. + const urlList = await dataTransferItem.asString(); + if (token.isCancellationRequested) { + return undefined; + } + + const uris: vscode.Uri[] = []; + for (const resource of urlList.split('\n')) { + try { + uris.push(vscode.Uri.parse(resource)); + } catch { + // noop + } + } + + if (!uris.length) { + return undefined; + } + + // Build a snippet to insert + const snippet = new vscode.SnippetString(); + uris.forEach((uri, index) => { + const name = path.basename(uri.path); + snippet.appendText(`${index + 1}. ${name}`); + snippet.appendTabstop(); + + if (index <= uris.length - 1 && uris.length > 1) { + snippet.appendText('\n'); + } + }); + + return { insertText: snippet }; + } +} + + +export function activate(context: vscode.ExtensionContext) { + // Enable our providers in plaintext files + const selector: vscode.DocumentSelector = { language: 'plaintext' }; + + // Register our providers + context.subscriptions.push(vscode.languages.registerDocumentDropEditProvider(selector, new ReverseTextOnDropProvider())); + context.subscriptions.push(vscode.languages.registerDocumentDropEditProvider(selector, new FileNameListOnDropProvider())); +} diff --git a/extension-terminal-sample/src/extension.ts b/extension-terminal-sample/src/extension.ts index 028199c1..5351b614 100644 --- a/extension-terminal-sample/src/extension.ts +++ b/extension-terminal-sample/src/extension.ts @@ -1,58 +1,58 @@ -'use strict'; - -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - const writeEmitter = new vscode.EventEmitter(); - context.subscriptions.push(vscode.commands.registerCommand('extensionTerminalSample.create', () => { - let line = ''; - const pty = { - onDidWrite: writeEmitter.event, - open: () => writeEmitter.fire('Type and press enter to echo the text\r\n\r\n'), - close: () => { /* noop*/ }, - handleInput: (data: string) => { - if (data === '\r') { // Enter - writeEmitter.fire(`\r\necho: "${colorText(line)}"\r\n\n`); - line = ''; - return; - } - if (data === '\x7f') { // Backspace - if (line.length === 0) { - return; - } - line = line.substr(0, line.length - 1); - // Move cursor backward - writeEmitter.fire('\x1b[D'); - // Delete character - writeEmitter.fire('\x1b[P'); - return; - } - line += data; - writeEmitter.fire(data); - } - }; - const terminal = vscode.window.createTerminal({ name: `My Extension REPL`, pty }); - terminal.show(); - })); - - context.subscriptions.push(vscode.commands.registerCommand('extensionTerminalSample.clear', () => { - writeEmitter.fire('\x1b[2J\x1b[3J\x1b[;H'); - })); -} - -function colorText(text: string): string { - let output = ''; - let colorIndex = 1; - for (let i = 0; i < text.length; i++) { - const char = text.charAt(i); - if (char === ' ' || char === '\r' || char === '\n') { - output += char; - } else { - output += `\x1b[3${colorIndex++}m${text.charAt(i)}\x1b[0m`; - if (colorIndex > 6) { - colorIndex = 1; - } - } - } - return output; +'use strict'; + +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + const writeEmitter = new vscode.EventEmitter(); + context.subscriptions.push(vscode.commands.registerCommand('extensionTerminalSample.create', () => { + let line = ''; + const pty = { + onDidWrite: writeEmitter.event, + open: () => writeEmitter.fire('Type and press enter to echo the text\r\n\r\n'), + close: () => { /* noop*/ }, + handleInput: (data: string) => { + if (data === '\r') { // Enter + writeEmitter.fire(`\r\necho: "${colorText(line)}"\r\n\n`); + line = ''; + return; + } + if (data === '\x7f') { // Backspace + if (line.length === 0) { + return; + } + line = line.substr(0, line.length - 1); + // Move cursor backward + writeEmitter.fire('\x1b[D'); + // Delete character + writeEmitter.fire('\x1b[P'); + return; + } + line += data; + writeEmitter.fire(data); + } + }; + const terminal = vscode.window.createTerminal({ name: `My Extension REPL`, pty }); + terminal.show(); + })); + + context.subscriptions.push(vscode.commands.registerCommand('extensionTerminalSample.clear', () => { + writeEmitter.fire('\x1b[2J\x1b[3J\x1b[;H'); + })); +} + +function colorText(text: string): string { + let output = ''; + let colorIndex = 1; + for (let i = 0; i < text.length; i++) { + const char = text.charAt(i); + if (char === ' ' || char === '\r' || char === '\n') { + output += char; + } else { + output += `\x1b[3${colorIndex++}m${text.charAt(i)}\x1b[0m`; + if (colorIndex > 6) { + colorIndex = 1; + } + } + } + return output; } \ No newline at end of file diff --git a/fsconsumer-sample/src/extension.ts b/fsconsumer-sample/src/extension.ts index bf27281c..4ae6af39 100644 --- a/fsconsumer-sample/src/extension.ts +++ b/fsconsumer-sample/src/extension.ts @@ -1,92 +1,92 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -'use strict'; - -import * as vscode from 'vscode'; -import { posix } from 'path'; - -export function activate(context: vscode.ExtensionContext) { - - // Command #1 - Check and show a JavaScript-file for a TypeScript-file - // * shows how to derive a new uri from an existing uri - // * shows how to check for existence of a file - vscode.commands.registerCommand('fs/openJS', async function () { - if ( - !vscode.window.activeTextEditor - || posix.extname(vscode.window.activeTextEditor.document.uri.path) !== '.ts' - ) { - return vscode.window.showInformationMessage('Open a TypeScript file first'); - } - - const tsUri = vscode.window.activeTextEditor.document.uri; - const jsPath = posix.join(tsUri.path, '..', posix.basename(tsUri.path, '.ts') + '.js'); - const jsUri = tsUri.with({ path: jsPath }); - - try { - await vscode.workspace.fs.stat(jsUri); - vscode.window.showTextDocument(jsUri, { viewColumn: vscode.ViewColumn.Beside }); - } catch { - vscode.window.showInformationMessage(`${jsUri.toString(true)} file does *not* exist`); - } - }); - - // Command #2 - Compute total size of files in a folder - // * shows how to read a directory - // * shows how retrieve metadata for a file - // * create an untitled document that shows count and total of files - vscode.commands.registerCommand('fs/sumSizes', async function () { - - async function countAndTotalOfFilesInFolder(folder: vscode.Uri): Promise<{ total: number, count: number }> { - let total = 0; - let count = 0; - for (const [name, type] of await vscode.workspace.fs.readDirectory(folder)) { - if (type === vscode.FileType.File) { - const filePath = posix.join(folder.path, name); - const stat = await vscode.workspace.fs.stat(folder.with({ path: filePath })); - total += stat.size; - count += 1; - } - } - return { total, count }; - } - - if (!vscode.window.activeTextEditor) { - return vscode.window.showInformationMessage('Open a file first'); - } - - const fileUri = vscode.window.activeTextEditor.document.uri; - const folderPath = posix.dirname(fileUri.path); - const folderUri = fileUri.with({ path: folderPath }); - - const info = await countAndTotalOfFilesInFolder(folderUri); - const doc = await vscode.workspace.openTextDocument({ content: `${info.count} files in ${folderUri.toString(true)} with a total of ${info.total} bytes` }); - vscode.window.showTextDocument(doc, { viewColumn: vscode.ViewColumn.Beside }); - }); - - // Command #3 - Write and read a file - // * shows how to derive a new file-uri from a folder-uri - // * shows how to convert a string into a typed array and back - vscode.commands.registerCommand('fs/readWriteFile', async function () { - - if (!vscode.workspace.workspaceFolders) { - return vscode.window.showInformationMessage('No folder or workspace opened'); - } - - const writeStr = '1€ is 1.12$ is 0.9£'; - const writeData = Buffer.from(writeStr, 'utf8'); - - const folderUri = vscode.workspace.workspaceFolders[0].uri; - const fileUri = folderUri.with({ path: posix.join(folderUri.path, 'test.txt') }); - - await vscode.workspace.fs.writeFile(fileUri, writeData); - - const readData = await vscode.workspace.fs.readFile(fileUri); - const readStr = Buffer.from(readData).toString('utf8'); - - vscode.window.showInformationMessage(readStr); - vscode.window.showTextDocument(fileUri); - }); - -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; +import { posix } from 'path'; + +export function activate(context: vscode.ExtensionContext) { + + // Command #1 - Check and show a JavaScript-file for a TypeScript-file + // * shows how to derive a new uri from an existing uri + // * shows how to check for existence of a file + vscode.commands.registerCommand('fs/openJS', async function() { + if ( + !vscode.window.activeTextEditor + || posix.extname(vscode.window.activeTextEditor.document.uri.path) !== '.ts' + ) { + return vscode.window.showInformationMessage('Open a TypeScript file first'); + } + + const tsUri = vscode.window.activeTextEditor.document.uri; + const jsPath = posix.join(tsUri.path, '..', posix.basename(tsUri.path, '.ts') + '.js'); + const jsUri = tsUri.with({ path: jsPath }); + + try { + await vscode.workspace.fs.stat(jsUri); + vscode.window.showTextDocument(jsUri, { viewColumn: vscode.ViewColumn.Beside }); + } catch { + vscode.window.showInformationMessage(`${jsUri.toString(true)} file does *not* exist`); + } + }); + + // Command #2 - Compute total size of files in a folder + // * shows how to read a directory + // * shows how retrieve metadata for a file + // * create an untitled document that shows count and total of files + vscode.commands.registerCommand('fs/sumSizes', async function() { + + async function countAndTotalOfFilesInFolder(folder: vscode.Uri): Promise<{ total: number, count: number }> { + let total = 0; + let count = 0; + for (const [name, type] of await vscode.workspace.fs.readDirectory(folder)) { + if (type === vscode.FileType.File) { + const filePath = posix.join(folder.path, name); + const stat = await vscode.workspace.fs.stat(folder.with({ path: filePath })); + total += stat.size; + count += 1; + } + } + return { total, count }; + } + + if (!vscode.window.activeTextEditor) { + return vscode.window.showInformationMessage('Open a file first'); + } + + const fileUri = vscode.window.activeTextEditor.document.uri; + const folderPath = posix.dirname(fileUri.path); + const folderUri = fileUri.with({ path: folderPath }); + + const info = await countAndTotalOfFilesInFolder(folderUri); + const doc = await vscode.workspace.openTextDocument({ content: `${info.count} files in ${folderUri.toString(true)} with a total of ${info.total} bytes` }); + vscode.window.showTextDocument(doc, { viewColumn: vscode.ViewColumn.Beside }); + }); + + // Command #3 - Write and read a file + // * shows how to derive a new file-uri from a folder-uri + // * shows how to convert a string into a typed array and back + vscode.commands.registerCommand('fs/readWriteFile', async function() { + + if (!vscode.workspace.workspaceFolders) { + return vscode.window.showInformationMessage('No folder or workspace opened'); + } + + const writeStr = '1€ is 1.12$ is 0.9£'; + const writeData = Buffer.from(writeStr, 'utf8'); + + const folderUri = vscode.workspace.workspaceFolders[0].uri; + const fileUri = folderUri.with({ path: posix.join(folderUri.path, 'test.txt') }); + + await vscode.workspace.fs.writeFile(fileUri, writeData); + + const readData = await vscode.workspace.fs.readFile(fileUri); + const readStr = Buffer.from(readData).toString('utf8'); + + vscode.window.showInformationMessage(readStr); + vscode.window.showTextDocument(fileUri); + }); + +} diff --git a/fsprovider-sample/src/extension.ts b/fsprovider-sample/src/extension.ts index 07f3a7c7..3a44e3b4 100644 --- a/fsprovider-sample/src/extension.ts +++ b/fsprovider-sample/src/extension.ts @@ -1,84 +1,84 @@ -'use strict'; - -import * as vscode from 'vscode'; -import { MemFS } from './fileSystemProvider'; - -export function activate(context: vscode.ExtensionContext) { - - console.log('MemFS says "Hello"'); - - const memFs = new MemFS(); - context.subscriptions.push(vscode.workspace.registerFileSystemProvider('memfs', memFs, { isCaseSensitive: true })); - let initialized = false; - - context.subscriptions.push(vscode.commands.registerCommand('memfs.reset', _ => { - for (const [name] of memFs.readDirectory(vscode.Uri.parse('memfs:/'))) { - memFs.delete(vscode.Uri.parse(`memfs:/${name}`)); - } - initialized = false; - })); - - context.subscriptions.push(vscode.commands.registerCommand('memfs.addFile', _ => { - if (initialized) { - memFs.writeFile(vscode.Uri.parse(`memfs:/file.txt`), Buffer.from('foo'), { create: true, overwrite: true }); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('memfs.deleteFile', _ => { - if (initialized) { - memFs.delete(vscode.Uri.parse('memfs:/file.txt')); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('memfs.init', _ => { - if (initialized) { - return; - } - initialized = true; - - // most common files types - memFs.writeFile(vscode.Uri.parse(`memfs:/file.txt`), Buffer.from('foo'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.html`), Buffer.from('

Hello

'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.js`), Buffer.from('console.log("JavaScript")'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.json`), Buffer.from('{ "json": true }'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.ts`), Buffer.from('console.log("TypeScript")'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.css`), Buffer.from('* { color: green; }'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.md`), Buffer.from('Hello _World_'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.xml`), Buffer.from(''), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.py`), Buffer.from('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.php`), Buffer.from('&1\'); ?>'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/file.yaml`), Buffer.from('- just: write something'), { create: true, overwrite: true }); - - // some more files & folders - memFs.createDirectory(vscode.Uri.parse(`memfs:/folder/`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/large/`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/abc`)); - memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/def`)); - - memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/folder/file.ts`), Buffer.from('let a:number = true; console.log(a);'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/large/rnd.foo`), randomData(50000), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/UPPER.txt`), Buffer.from('UPPER'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/upper.txt`), Buffer.from('upper'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.md`), Buffer.from('*MemFS*'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.bin`), Buffer.from([0, 0, 0, 1, 7, 0, 0, 1, 1]), { create: true, overwrite: true }); - })); - - context.subscriptions.push(vscode.commands.registerCommand('memfs.workspaceInit', _ => { - vscode.workspace.updateWorkspaceFolders(0, 0, { uri: vscode.Uri.parse('memfs:/'), name: "MemFS - Sample" }); - })); -} - -function randomData(lineCnt: number, lineLen = 155): Buffer { - const lines: string[] = []; - for (let i = 0; i < lineCnt; i++) { - let line = ''; - while (line.length < lineLen) { - line += Math.random().toString(2 + (i % 34)).substr(2); - } - lines.push(line.substr(0, lineLen)); - } - return Buffer.from(lines.join('\n'), 'utf8'); -} +'use strict'; + +import * as vscode from 'vscode'; +import { MemFS } from './fileSystemProvider'; + +export function activate(context: vscode.ExtensionContext) { + + console.log('MemFS says "Hello"'); + + const memFs = new MemFS(); + context.subscriptions.push(vscode.workspace.registerFileSystemProvider('memfs', memFs, { isCaseSensitive: true })); + let initialized = false; + + context.subscriptions.push(vscode.commands.registerCommand('memfs.reset', _ => { + for (const [name] of memFs.readDirectory(vscode.Uri.parse('memfs:/'))) { + memFs.delete(vscode.Uri.parse(`memfs:/${name}`)); + } + initialized = false; + })); + + context.subscriptions.push(vscode.commands.registerCommand('memfs.addFile', _ => { + if (initialized) { + memFs.writeFile(vscode.Uri.parse(`memfs:/file.txt`), Buffer.from('foo'), { create: true, overwrite: true }); + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('memfs.deleteFile', _ => { + if (initialized) { + memFs.delete(vscode.Uri.parse('memfs:/file.txt')); + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('memfs.init', _ => { + if (initialized) { + return; + } + initialized = true; + + // most common files types + memFs.writeFile(vscode.Uri.parse(`memfs:/file.txt`), Buffer.from('foo'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.html`), Buffer.from('

Hello

'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.js`), Buffer.from('console.log("JavaScript")'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.json`), Buffer.from('{ "json": true }'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.ts`), Buffer.from('console.log("TypeScript")'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.css`), Buffer.from('* { color: green; }'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.md`), Buffer.from('Hello _World_'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.xml`), Buffer.from(''), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.py`), Buffer.from('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.php`), Buffer.from('&1\'); ?>'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/file.yaml`), Buffer.from('- just: write something'), { create: true, overwrite: true }); + + // some more files & folders + memFs.createDirectory(vscode.Uri.parse(`memfs:/folder/`)); + memFs.createDirectory(vscode.Uri.parse(`memfs:/large/`)); + memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/`)); + memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/abc`)); + memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/def`)); + + memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/folder/file.ts`), Buffer.from('let a:number = true; console.log(a);'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/large/rnd.foo`), randomData(50000), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/UPPER.txt`), Buffer.from('UPPER'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/upper.txt`), Buffer.from('upper'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.md`), Buffer.from('*MemFS*'), { create: true, overwrite: true }); + memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.bin`), Buffer.from([0, 0, 0, 1, 7, 0, 0, 1, 1]), { create: true, overwrite: true }); + })); + + context.subscriptions.push(vscode.commands.registerCommand('memfs.workspaceInit', _ => { + vscode.workspace.updateWorkspaceFolders(0, 0, { uri: vscode.Uri.parse('memfs:/'), name: "MemFS - Sample" }); + })); +} + +function randomData(lineCnt: number, lineLen = 155): Buffer { + const lines: string[] = []; + for (let i = 0; i < lineCnt; i++) { + let line = ''; + while (line.length < lineLen) { + line += Math.random().toString(2 + (i % 34)).substr(2); + } + lines.push(line.substr(0, lineLen)); + } + return Buffer.from(lines.join('\n'), 'utf8'); +} diff --git a/fsprovider-sample/src/fileSystemProvider.ts b/fsprovider-sample/src/fileSystemProvider.ts index 973d4b3a..f2bdf3e9 100644 --- a/fsprovider-sample/src/fileSystemProvider.ts +++ b/fsprovider-sample/src/fileSystemProvider.ts @@ -1,227 +1,227 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -import * as path from 'path'; -import * as vscode from 'vscode'; - -export class File implements vscode.FileStat { - - type: vscode.FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - data?: Uint8Array; - - constructor(name: string) { - this.type = vscode.FileType.File; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - } -} - -export class Directory implements vscode.FileStat { - - type: vscode.FileType; - ctime: number; - mtime: number; - size: number; - - name: string; - entries: Map; - - constructor(name: string) { - this.type = vscode.FileType.Directory; - this.ctime = Date.now(); - this.mtime = Date.now(); - this.size = 0; - this.name = name; - this.entries = new Map(); - } -} - -export type Entry = File | Directory; - -export class MemFS implements vscode.FileSystemProvider { - - root = new Directory(''); - - // --- manage file metadata - - stat(uri: vscode.Uri): vscode.FileStat { - return this._lookup(uri, false); - } - - readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { - const entry = this._lookupAsDirectory(uri, false); - const result: [string, vscode.FileType][] = []; - for (const [name, child] of entry.entries) { - result.push([name, child.type]); - } - return result; - } - - // --- manage file contents - - readFile(uri: vscode.Uri): Uint8Array { - const data = this._lookupAsFile(uri, false).data; - if (data) { - return data; - } - throw vscode.FileSystemError.FileNotFound(); - } - - writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { - const basename = path.posix.basename(uri.path); - const parent = this._lookupParentDirectory(uri); - let entry = parent.entries.get(basename); - if (entry instanceof Directory) { - throw vscode.FileSystemError.FileIsADirectory(uri); - } - if (!entry && !options.create) { - throw vscode.FileSystemError.FileNotFound(uri); - } - if (entry && options.create && !options.overwrite) { - throw vscode.FileSystemError.FileExists(uri); - } - if (!entry) { - entry = new File(basename); - parent.entries.set(basename, entry); - this._fireSoon({ type: vscode.FileChangeType.Created, uri }); - } - entry.mtime = Date.now(); - entry.size = content.byteLength; - entry.data = content; - - this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); - } - - // --- manage files/folders - - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void { - - if (!options.overwrite && this._lookup(newUri, true)) { - throw vscode.FileSystemError.FileExists(newUri); - } - - const entry = this._lookup(oldUri, false); - const oldParent = this._lookupParentDirectory(oldUri); - - const newParent = this._lookupParentDirectory(newUri); - const newName = path.posix.basename(newUri.path); - - oldParent.entries.delete(entry.name); - entry.name = newName; - newParent.entries.set(newName, entry); - - this._fireSoon( - { type: vscode.FileChangeType.Deleted, uri: oldUri }, - { type: vscode.FileChangeType.Created, uri: newUri } - ); - } - - delete(uri: vscode.Uri): void { - const dirname = uri.with({ path: path.posix.dirname(uri.path) }); - const basename = path.posix.basename(uri.path); - const parent = this._lookupAsDirectory(dirname, false); - if (!parent.entries.has(basename)) { - throw vscode.FileSystemError.FileNotFound(uri); - } - parent.entries.delete(basename); - parent.mtime = Date.now(); - parent.size -= 1; - this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted }); - } - - createDirectory(uri: vscode.Uri): void { - const basename = path.posix.basename(uri.path); - const dirname = uri.with({ path: path.posix.dirname(uri.path) }); - const parent = this._lookupAsDirectory(dirname, false); - - const entry = new Directory(basename); - parent.entries.set(entry.name, entry); - parent.mtime = Date.now(); - parent.size += 1; - this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri }); - } - - // --- lookup - - private _lookup(uri: vscode.Uri, silent: false): Entry; - private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined; - private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined { - const parts = uri.path.split('/'); - let entry: Entry = this.root; - for (const part of parts) { - if (!part) { - continue; - } - let child: Entry | undefined; - if (entry instanceof Directory) { - child = entry.entries.get(part); - } - if (!child) { - if (!silent) { - throw vscode.FileSystemError.FileNotFound(uri); - } else { - return undefined; - } - } - entry = child; - } - return entry; - } - - private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory { - const entry = this._lookup(uri, silent); - if (entry instanceof Directory) { - return entry; - } - throw vscode.FileSystemError.FileNotADirectory(uri); - } - - private _lookupAsFile(uri: vscode.Uri, silent: boolean): File { - const entry = this._lookup(uri, silent); - if (entry instanceof File) { - return entry; - } - throw vscode.FileSystemError.FileIsADirectory(uri); - } - - private _lookupParentDirectory(uri: vscode.Uri): Directory { - const dirname = uri.with({ path: path.posix.dirname(uri.path) }); - return this._lookupAsDirectory(dirname, false); - } - - // --- manage file events - - private _emitter = new vscode.EventEmitter(); - private _bufferedEvents: vscode.FileChangeEvent[] = []; - private _fireSoonHandle?: NodeJS.Timer; - - readonly onDidChangeFile: vscode.Event = this._emitter.event; - - watch(_resource: vscode.Uri): vscode.Disposable { - // ignore, fires for all changes... - return new vscode.Disposable(() => { }); - } - - private _fireSoon(...events: vscode.FileChangeEvent[]): void { - this._bufferedEvents.push(...events); - - if (this._fireSoonHandle) { - clearTimeout(this._fireSoonHandle); - } - - this._fireSoonHandle = setTimeout(() => { - this._emitter.fire(this._bufferedEvents); - this._bufferedEvents.length = 0; - }, 5); - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import * as path from 'path'; +import * as vscode from 'vscode'; + +export class File implements vscode.FileStat { + + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + data?: Uint8Array; + + constructor(name: string) { + this.type = vscode.FileType.File; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + } +} + +export class Directory implements vscode.FileStat { + + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + entries: Map; + + constructor(name: string) { + this.type = vscode.FileType.Directory; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + this.entries = new Map(); + } +} + +export type Entry = File | Directory; + +export class MemFS implements vscode.FileSystemProvider { + + root = new Directory(''); + + // --- manage file metadata + + stat(uri: vscode.Uri): vscode.FileStat { + return this._lookup(uri, false); + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { + const entry = this._lookupAsDirectory(uri, false); + const result: [string, vscode.FileType][] = []; + for (const [name, child] of entry.entries) { + result.push([name, child.type]); + } + return result; + } + + // --- manage file contents + + readFile(uri: vscode.Uri): Uint8Array { + const data = this._lookupAsFile(uri, false).data; + if (data) { + return data; + } + throw vscode.FileSystemError.FileNotFound(); + } + + writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void { + const basename = path.posix.basename(uri.path); + const parent = this._lookupParentDirectory(uri); + let entry = parent.entries.get(basename); + if (entry instanceof Directory) { + throw vscode.FileSystemError.FileIsADirectory(uri); + } + if (!entry && !options.create) { + throw vscode.FileSystemError.FileNotFound(uri); + } + if (entry && options.create && !options.overwrite) { + throw vscode.FileSystemError.FileExists(uri); + } + if (!entry) { + entry = new File(basename); + parent.entries.set(basename, entry); + this._fireSoon({ type: vscode.FileChangeType.Created, uri }); + } + entry.mtime = Date.now(); + entry.size = content.byteLength; + entry.data = content; + + this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); + } + + // --- manage files/folders + + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void { + + if (!options.overwrite && this._lookup(newUri, true)) { + throw vscode.FileSystemError.FileExists(newUri); + } + + const entry = this._lookup(oldUri, false); + const oldParent = this._lookupParentDirectory(oldUri); + + const newParent = this._lookupParentDirectory(newUri); + const newName = path.posix.basename(newUri.path); + + oldParent.entries.delete(entry.name); + entry.name = newName; + newParent.entries.set(newName, entry); + + this._fireSoon( + { type: vscode.FileChangeType.Deleted, uri: oldUri }, + { type: vscode.FileChangeType.Created, uri: newUri } + ); + } + + delete(uri: vscode.Uri): void { + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + const basename = path.posix.basename(uri.path); + const parent = this._lookupAsDirectory(dirname, false); + if (!parent.entries.has(basename)) { + throw vscode.FileSystemError.FileNotFound(uri); + } + parent.entries.delete(basename); + parent.mtime = Date.now(); + parent.size -= 1; + this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted }); + } + + createDirectory(uri: vscode.Uri): void { + const basename = path.posix.basename(uri.path); + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + const parent = this._lookupAsDirectory(dirname, false); + + const entry = new Directory(basename); + parent.entries.set(entry.name, entry); + parent.mtime = Date.now(); + parent.size += 1; + this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri }); + } + + // --- lookup + + private _lookup(uri: vscode.Uri, silent: false): Entry; + private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined; + private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined { + const parts = uri.path.split('/'); + let entry: Entry = this.root; + for (const part of parts) { + if (!part) { + continue; + } + let child: Entry | undefined; + if (entry instanceof Directory) { + child = entry.entries.get(part); + } + if (!child) { + if (!silent) { + throw vscode.FileSystemError.FileNotFound(uri); + } else { + return undefined; + } + } + entry = child; + } + return entry; + } + + private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory { + const entry = this._lookup(uri, silent); + if (entry instanceof Directory) { + return entry; + } + throw vscode.FileSystemError.FileNotADirectory(uri); + } + + private _lookupAsFile(uri: vscode.Uri, silent: boolean): File { + const entry = this._lookup(uri, silent); + if (entry instanceof File) { + return entry; + } + throw vscode.FileSystemError.FileIsADirectory(uri); + } + + private _lookupParentDirectory(uri: vscode.Uri): Directory { + const dirname = uri.with({ path: path.posix.dirname(uri.path) }); + return this._lookupAsDirectory(dirname, false); + } + + // --- manage file events + + private _emitter = new vscode.EventEmitter(); + private _bufferedEvents: vscode.FileChangeEvent[] = []; + private _fireSoonHandle?: NodeJS.Timer; + + readonly onDidChangeFile: vscode.Event = this._emitter.event; + + watch(_resource: vscode.Uri): vscode.Disposable { + // ignore, fires for all changes... + return new vscode.Disposable(() => { }); + } + + private _fireSoon(...events: vscode.FileChangeEvent[]): void { + this._bufferedEvents.push(...events); + + if (this._fireSoonHandle) { + clearTimeout(this._fireSoonHandle); + } + + this._fireSoonHandle = setTimeout(() => { + this._emitter.fire(this._bufferedEvents); + this._bufferedEvents.length = 0; + }, 5); + } +} diff --git a/getting-started-sample/src/extension.ts b/getting-started-sample/src/extension.ts index 4ef98532..cd766a8d 100644 --- a/getting-started-sample/src/extension.ts +++ b/getting-started-sample/src/extension.ts @@ -1,31 +1,31 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; - - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext): void { - context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.runCommand', async () => { - await new Promise(resolve => setTimeout(resolve, 1000)); - vscode.commands.executeCommand('getting-started-sample.sayHello', vscode.Uri.joinPath(context.extensionUri, 'sample-folder')); - })); - - context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.changeSetting', async () => { - await new Promise(resolve => setTimeout(resolve, 1000)); - vscode.workspace.getConfiguration('getting-started-sample').update('sampleSetting', true); - })); - - context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.setContext', async () => { - await new Promise(resolve => setTimeout(resolve, 1000)); - vscode.commands.executeCommand('setContext', 'gettingStartedContextKey', true); - })); - - context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.sayHello', () => { - vscode.window.showInformationMessage('Hello'); - })); - - context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.viewSources', () => { - return { openFolder: vscode.Uri.joinPath(context.extensionUri, 'src') }; - })); +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; + + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext): void { + context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.runCommand', async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); + vscode.commands.executeCommand('getting-started-sample.sayHello', vscode.Uri.joinPath(context.extensionUri, 'sample-folder')); + })); + + context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.changeSetting', async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); + vscode.workspace.getConfiguration('getting-started-sample').update('sampleSetting', true); + })); + + context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.setContext', async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); + vscode.commands.executeCommand('setContext', 'gettingStartedContextKey', true); + })); + + context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.sayHello', () => { + vscode.window.showInformationMessage('Hello'); + })); + + context.subscriptions.push(vscode.commands.registerCommand('getting-started-sample.viewSources', () => { + return { openFolder: vscode.Uri.joinPath(context.extensionUri, 'src') }; + })); } \ No newline at end of file diff --git a/github-authentication-sample/src/credentials.ts b/github-authentication-sample/src/credentials.ts index 2a539f98..1866f545 100644 --- a/github-authentication-sample/src/credentials.ts +++ b/github-authentication-sample/src/credentials.ts @@ -1,63 +1,63 @@ -import * as vscode from 'vscode'; -import * as Octokit from '@octokit/rest'; - -const GITHUB_AUTH_PROVIDER_ID = 'github'; -// The GitHub Authentication Provider accepts the scopes described here: -// https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ -const SCOPES = ['user:email']; - -export class Credentials { - private octokit: Octokit.Octokit | undefined; - - async initialize(context: vscode.ExtensionContext): Promise { - this.registerListeners(context); - this.setOctokit(); - } - - private async setOctokit() { - /** - * By passing the `createIfNone` flag, a numbered badge will show up on the accounts activity bar icon. - * An entry for the sample extension will be added under the menu to sign in. This allows quietly - * prompting the user to sign in. - * */ - const session = await vscode.authentication.getSession(GITHUB_AUTH_PROVIDER_ID, SCOPES, { createIfNone: false }); - - if (session) { - this.octokit = new Octokit.Octokit({ - auth: session.accessToken - }); - - return; - } - - this.octokit = undefined; - } - - registerListeners(context: vscode.ExtensionContext): void { - /** - * Sessions are changed when a user logs in or logs out. - */ - context.subscriptions.push(vscode.authentication.onDidChangeSessions(async e => { - if (e.provider.id === GITHUB_AUTH_PROVIDER_ID) { - await this.setOctokit(); - } - })); - } - - async getOctokit(): Promise { - if (this.octokit) { - return this.octokit; - } - - /** - * When the `createIfNone` flag is passed, a modal dialog will be shown asking the user to sign in. - * Note that this can throw if the user clicks cancel. - */ - const session = await vscode.authentication.getSession(GITHUB_AUTH_PROVIDER_ID, SCOPES, { createIfNone: true }); - this.octokit = new Octokit.Octokit({ - auth: session.accessToken - }); - - return this.octokit; - } +import * as vscode from 'vscode'; +import * as Octokit from '@octokit/rest'; + +const GITHUB_AUTH_PROVIDER_ID = 'github'; +// The GitHub Authentication Provider accepts the scopes described here: +// https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ +const SCOPES = ['user:email']; + +export class Credentials { + private octokit: Octokit.Octokit | undefined; + + async initialize(context: vscode.ExtensionContext): Promise { + this.registerListeners(context); + this.setOctokit(); + } + + private async setOctokit() { + /** + * By passing the `createIfNone` flag, a numbered badge will show up on the accounts activity bar icon. + * An entry for the sample extension will be added under the menu to sign in. This allows quietly + * prompting the user to sign in. + * */ + const session = await vscode.authentication.getSession(GITHUB_AUTH_PROVIDER_ID, SCOPES, { createIfNone: false }); + + if (session) { + this.octokit = new Octokit.Octokit({ + auth: session.accessToken + }); + + return; + } + + this.octokit = undefined; + } + + registerListeners(context: vscode.ExtensionContext): void { + /** + * Sessions are changed when a user logs in or logs out. + */ + context.subscriptions.push(vscode.authentication.onDidChangeSessions(async e => { + if (e.provider.id === GITHUB_AUTH_PROVIDER_ID) { + await this.setOctokit(); + } + })); + } + + async getOctokit(): Promise { + if (this.octokit) { + return this.octokit; + } + + /** + * When the `createIfNone` flag is passed, a modal dialog will be shown asking the user to sign in. + * Note that this can throw if the user clicks cancel. + */ + const session = await vscode.authentication.getSession(GITHUB_AUTH_PROVIDER_ID, SCOPES, { createIfNone: true }); + this.octokit = new Octokit.Octokit({ + auth: session.accessToken + }); + + return this.octokit; + } } \ No newline at end of file diff --git a/github-authentication-sample/src/extension.ts b/github-authentication-sample/src/extension.ts index c0fab399..a9b31d1d 100644 --- a/github-authentication-sample/src/extension.ts +++ b/github-authentication-sample/src/extension.ts @@ -1,25 +1,25 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; -import { Credentials } from './credentials'; - - -export async function activate(context: vscode.ExtensionContext) { - const credentials = new Credentials(); - await credentials.initialize(context); - - const disposable = vscode.commands.registerCommand('extension.getGitHubUser', async () => { - /** - * Octokit (https://github.com/octokit/rest.js#readme) is a library for making REST API - * calls to GitHub. It provides convenient typings that can be helpful for using the API. - * - * Documentation on GitHub's REST API can be found here: https://docs.github.com/en/rest - */ - const octokit = await credentials.getOctokit(); - const userInfo = await octokit.users.getAuthenticated(); - - vscode.window.showInformationMessage(`Logged into GitHub as ${userInfo.data.login}`); - }); - - context.subscriptions.push(disposable); -} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +import { Credentials } from './credentials'; + + +export async function activate(context: vscode.ExtensionContext) { + const credentials = new Credentials(); + await credentials.initialize(context); + + const disposable = vscode.commands.registerCommand('extension.getGitHubUser', async () => { + /** + * Octokit (https://github.com/octokit/rest.js#readme) is a library for making REST API + * calls to GitHub. It provides convenient typings that can be helpful for using the API. + * + * Documentation on GitHub's REST API can be found here: https://docs.github.com/en/rest + */ + const octokit = await credentials.getOctokit(); + const userInfo = await octokit.users.getAuthenticated(); + + vscode.window.showInformationMessage(`Logged into GitHub as ${userInfo.data.login}`); + }); + + context.subscriptions.push(disposable); +} diff --git a/helloworld-sample/src/extension.ts b/helloworld-sample/src/extension.ts index e93d7dd0..7fa9da09 100644 --- a/helloworld-sample/src/extension.ts +++ b/helloworld-sample/src/extension.ts @@ -1,23 +1,23 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log('Congratulations, your extension "helloworld-sample" is now active!'); - - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { - // The code you place here will be executed every time your command is executed - - // Display a message box to the user - vscode.window.showInformationMessage('Hello World!'); - }); - - context.subscriptions.push(disposable); -} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + console.log('Congratulations, your extension "helloworld-sample" is now active!'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { + // The code you place here will be executed every time your command is executed + + // Display a message box to the user + vscode.window.showInformationMessage('Hello World!'); + }); + + context.subscriptions.push(disposable); +} diff --git a/helloworld-test-sample/src/extension.ts b/helloworld-test-sample/src/extension.ts index e93d7dd0..7fa9da09 100644 --- a/helloworld-test-sample/src/extension.ts +++ b/helloworld-test-sample/src/extension.ts @@ -1,23 +1,23 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log('Congratulations, your extension "helloworld-sample" is now active!'); - - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { - // The code you place here will be executed every time your command is executed - - // Display a message box to the user - vscode.window.showInformationMessage('Hello World!'); - }); - - context.subscriptions.push(disposable); -} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + console.log('Congratulations, your extension "helloworld-sample" is now active!'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { + // The code you place here will be executed every time your command is executed + + // Display a message box to the user + vscode.window.showInformationMessage('Hello World!'); + }); + + context.subscriptions.push(disposable); +} diff --git a/helloworld-test-sample/src/test/runTest.ts b/helloworld-test-sample/src/test/runTest.ts index ff292ae0..b09e0d5a 100644 --- a/helloworld-test-sample/src/test/runTest.ts +++ b/helloworld-test-sample/src/test/runTest.ts @@ -1,23 +1,23 @@ -import * as path from 'path'; - -import { runTests } from '@vscode/test-electron'; - -async function main() { - try { - // The folder containing the Extension Manifest package.json - // Passed to `--extensionDevelopmentPath` - const extensionDevelopmentPath = path.resolve(__dirname, '../../'); - - // The path to the extension test script - // Passed to --extensionTestsPath - const extensionTestsPath = path.resolve(__dirname, './suite/index'); - - // Download VS Code, unzip it and run the integration test - await runTests({ extensionDevelopmentPath, extensionTestsPath }); - } catch (err) { - console.error('Failed to run tests'); - process.exit(1); - } -} - -main(); +import * as path from 'path'; + +import { runTests } from '@vscode/test-electron'; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + + // The path to the extension test script + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './suite/index'); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error('Failed to run tests'); + process.exit(1); + } +} + +main(); diff --git a/helloworld-test-sample/src/test/suite/extension.test.ts b/helloworld-test-sample/src/test/suite/extension.test.ts index 9bca13ad..10c3c5f5 100644 --- a/helloworld-test-sample/src/test/suite/extension.test.ts +++ b/helloworld-test-sample/src/test/suite/extension.test.ts @@ -1,15 +1,15 @@ -import * as assert from 'assert'; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it -import * as vscode from 'vscode'; -// import * as myExtension from '../../extension'; - -suite('Extension Test Suite', () => { - vscode.window.showInformationMessage('Start all tests.'); - - test('Sample test', () => { - assert.strictEqual([1, 2, 3].indexOf(5), -1); - assert.strictEqual([1, 2, 3].indexOf(0), -1); - }); -}); +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +// import * as myExtension from '../../extension'; + +suite('Extension Test Suite', () => { + vscode.window.showInformationMessage('Start all tests.'); + + test('Sample test', () => { + assert.strictEqual([1, 2, 3].indexOf(5), -1); + assert.strictEqual([1, 2, 3].indexOf(0), -1); + }); +}); diff --git a/helloworld-test-sample/src/test/suite/index.ts b/helloworld-test-sample/src/test/suite/index.ts index 5ca2e56e..945136c0 100644 --- a/helloworld-test-sample/src/test/suite/index.ts +++ b/helloworld-test-sample/src/test/suite/index.ts @@ -1,38 +1,38 @@ -import * as path from 'path'; -import * as Mocha from 'mocha'; -import * as glob from 'glob'; - -export function run(): Promise { - // Create the mocha test - const mocha = new Mocha({ - ui: 'tdd' - }); - mocha.useColors(true); - - const testsRoot = path.resolve(__dirname, '..'); - - return new Promise((c, e) => { - glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { - if (err) { - return e(err); - } - - // Add files to the test suite - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); - - try { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } else { - c(); - } - }); - } catch (err) { - console.error(err); - e(err); - } - }); - }); -} +import * as path from 'path'; +import * as Mocha from 'mocha'; +import * as glob from 'glob'; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: 'tdd' + }); + mocha.useColors(true); + + const testsRoot = path.resolve(__dirname, '..'); + + return new Promise((c, e) => { + glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err); + } + + // Add files to the test suite + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }); + }); +} diff --git a/helloworld-web-sample/src/web/extension.ts b/helloworld-web-sample/src/web/extension.ts index 176a2dd2..7348e2e3 100644 --- a/helloworld-web-sample/src/web/extension.ts +++ b/helloworld-web-sample/src/web/extension.ts @@ -1,27 +1,27 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { - - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log('Congratulations, your extension "helloworld-web-sample" is now active in the web extension host!'); - - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - let disposable = vscode.commands.registerCommand('helloworld-web-sample.helloWorld', () => { - // The code you place here will be executed every time your command is executed - - // Display a message box to the user - vscode.window.showInformationMessage('Hello World from helloworld-web-sample in a web extension host!'); - }); - - context.subscriptions.push(disposable); -} - -// this method is called when your extension is deactivated -export function deactivate() {} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + console.log('Congratulations, your extension "helloworld-web-sample" is now active in the web extension host!'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + let disposable = vscode.commands.registerCommand('helloworld-web-sample.helloWorld', () => { + // The code you place here will be executed every time your command is executed + + // Display a message box to the user + vscode.window.showInformationMessage('Hello World from helloworld-web-sample in a web extension host!'); + }); + + context.subscriptions.push(disposable); +} + +// this method is called when your extension is deactivated +export function deactivate() { } diff --git a/helloworld-web-sample/src/web/test/suite/extension.test.ts b/helloworld-web-sample/src/web/test/suite/extension.test.ts index 7e022bc4..3b4b1eed 100644 --- a/helloworld-web-sample/src/web/test/suite/extension.test.ts +++ b/helloworld-web-sample/src/web/test/suite/extension.test.ts @@ -1,15 +1,15 @@ -import * as assert from 'assert'; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it -import * as vscode from 'vscode'; -// import * as myExtension from '../../extension'; - -suite('Web Extension Test Suite', () => { - vscode.window.showInformationMessage('Start all tests.'); - - test('Sample test', () => { - assert.strictEqual(-1, [1, 2, 3].indexOf(5)); - assert.strictEqual(-1, [1, 2, 3].indexOf(0)); - }); -}); +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +// import * as myExtension from '../../extension'; + +suite('Web Extension Test Suite', () => { + vscode.window.showInformationMessage('Start all tests.'); + + test('Sample test', () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/helloworld-web-sample/src/web/test/suite/index.ts b/helloworld-web-sample/src/web/test/suite/index.ts index 87abf7ff..bbcab725 100644 --- a/helloworld-web-sample/src/web/test/suite/index.ts +++ b/helloworld-web-sample/src/web/test/suite/index.ts @@ -1,30 +1,30 @@ -// imports mocha for the browser, defining the `mocha` global. -require('mocha/mocha'); - -export function run(): Promise { - - return new Promise((c, e) => { - mocha.setup({ - ui: 'tdd', - reporter: undefined - }); - - // bundles all files in the current directory matching `*.test` - const importAll = (r: __WebpackModuleApi.RequireContext) => r.keys().forEach(r); - importAll(require.context('.', true, /\.test$/)); - - try { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } else { - c(); - } - }); - } catch (err) { - console.error(err); - e(err); - } - }); -} +// imports mocha for the browser, defining the `mocha` global. +require('mocha/mocha'); + +export function run(): Promise { + + return new Promise((c, e) => { + mocha.setup({ + ui: 'tdd', + reporter: undefined + }); + + // bundles all files in the current directory matching `*.test` + const importAll = (r: __WebpackModuleApi.RequireContext) => r.keys().forEach(r); + importAll(require.context('.', true, /\.test$/)); + + try { + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }); +} diff --git a/inline-completions/src/extension.ts b/inline-completions/src/extension.ts index 17357e01..b847761c 100644 --- a/inline-completions/src/extension.ts +++ b/inline-completions/src/extension.ts @@ -1,52 +1,52 @@ -import * as vscode from 'vscode'; - -// Try it out in `playground.js` - -export function activate(context: vscode.ExtensionContext) { - const disposable = vscode.commands.registerCommand( - 'extension.inline-completion-settings', - () => { - vscode.window.showInformationMessage('Show settings'); - } - ); - - context.subscriptions.push(disposable); - let someTrackingIdCounter = 0; - - const provider: vscode.InlineCompletionItemProvider = { - provideInlineCompletionItems: async (document, position, context, token) => { - console.log('provideInlineCompletionItems triggered'); - - const regexp = /\/\/ \[(.+),(.+)\):(.*)/; - if (position.line <= 0) { - return; - } - - const lineBefore = document.lineAt(position.line - 1).text; - const matches = lineBefore.match(regexp); - if (matches) { - const start = matches[1]; - const startInt = parseInt(start, 10); - const end = matches[2]; - const endInt = - end === '*' ? document.lineAt(position.line).text.length : parseInt(end, 10); - const insertText = matches[3].replace(/\\n/g, '\n'); - - return [ - { - insertText, - range: new vscode.Range(position.line, startInt, position.line, endInt), - someTrackingId: someTrackingIdCounter++, - }, - ] as MyInlineCompletionItem[]; - } - }, - }; - - vscode.languages.registerInlineCompletionItemProvider({ pattern: '**' }, provider); - -} - -interface MyInlineCompletionItem extends vscode.InlineCompletionItem { - someTrackingId: number; -} +import * as vscode from 'vscode'; + +// Try it out in `playground.js` + +export function activate(context: vscode.ExtensionContext) { + const disposable = vscode.commands.registerCommand( + 'extension.inline-completion-settings', + () => { + vscode.window.showInformationMessage('Show settings'); + } + ); + + context.subscriptions.push(disposable); + let someTrackingIdCounter = 0; + + const provider: vscode.InlineCompletionItemProvider = { + provideInlineCompletionItems: async (document, position, context, token) => { + console.log('provideInlineCompletionItems triggered'); + + const regexp = /\/\/ \[(.+),(.+)\):(.*)/; + if (position.line <= 0) { + return; + } + + const lineBefore = document.lineAt(position.line - 1).text; + const matches = lineBefore.match(regexp); + if (matches) { + const start = matches[1]; + const startInt = parseInt(start, 10); + const end = matches[2]; + const endInt = + end === '*' ? document.lineAt(position.line).text.length : parseInt(end, 10); + const insertText = matches[3].replace(/\\n/g, '\n'); + + return [ + { + insertText, + range: new vscode.Range(position.line, startInt, position.line, endInt), + someTrackingId: someTrackingIdCounter++, + }, + ] as MyInlineCompletionItem[]; + } + }, + }; + + vscode.languages.registerInlineCompletionItemProvider({ pattern: '**' }, provider); + +} + +interface MyInlineCompletionItem extends vscode.InlineCompletionItem { + someTrackingId: number; +} diff --git a/inline-completions/vscode.d.ts b/inline-completions/vscode.d.ts index 82d8ea46..ebda4ed0 100644 --- a/inline-completions/vscode.d.ts +++ b/inline-completions/vscode.d.ts @@ -1,16557 +1,16557 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - /** - * The version of the editor. - */ - export const version: string; - - /** - * Represents a reference to a command. Provides a title which - * will be used to represent a command in the UI and, optionally, - * an array of arguments which will be passed to the command handler - * function when invoked. - */ - export interface Command { - /** - * Title of the command, like `save`. - */ - title: string; - - /** - * The identifier of the actual command handler. - * @see {@link commands.registerCommand} - */ - command: string; - - /** - * A tooltip for the command, when represented in the UI. - */ - tooltip?: string; - - /** - * Arguments that the command handler should be - * invoked with. - */ - arguments?: any[]; - } - - /** - * Represents a line of text, such as a line of source code. - * - * TextLine objects are __immutable__. When a {@link TextDocument document} changes, - * previously retrieved lines will not represent the latest state. - */ - export interface TextLine { - - /** - * The zero-based line number. - */ - readonly lineNumber: number; - - /** - * The text of this line without the line separator characters. - */ - readonly text: string; - - /** - * The range this line covers without the line separator characters. - */ - readonly range: Range; - - /** - * The range this line covers with the line separator characters. - */ - readonly rangeIncludingLineBreak: Range; - - /** - * The offset of the first character which is not a whitespace character as defined - * by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned. - */ - readonly firstNonWhitespaceCharacterIndex: number; - - /** - * Whether this line is whitespace only, shorthand - * for {@link TextLine.firstNonWhitespaceCharacterIndex} === {@link TextLine.text TextLine.text.length}. - */ - readonly isEmptyOrWhitespace: boolean; - } - - /** - * Represents a text document, such as a source file. Text documents have - * {@link TextLine lines} and knowledge about an underlying resource like a file. - */ - export interface TextDocument { - - /** - * The associated uri for this document. - * - * *Note* that most documents use the `file`-scheme, which means they are files on disk. However, **not** all documents are - * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. - * - * @see {@link FileSystemProvider} - * @see {@link TextDocumentContentProvider} - */ - readonly uri: Uri; - - /** - * The file system path of the associated resource. Shorthand - * notation for {@link TextDocument.uri TextDocument.uri.fsPath}. Independent of the uri scheme. - */ - readonly fileName: string; - - /** - * Is this document representing an untitled file which has never been saved yet. *Note* that - * this does not mean the document will be saved to disk, use {@linkcode Uri.scheme} - * to figure out where a document will be {@link FileSystemProvider saved}, e.g. `file`, `ftp` etc. - */ - readonly isUntitled: boolean; - - /** - * The identifier of the language associated with this document. - */ - readonly languageId: string; - - /** - * The version number of this document (it will strictly increase after each - * change, including undo/redo). - */ - readonly version: number; - - /** - * `true` if there are unpersisted changes. - */ - readonly isDirty: boolean; - - /** - * `true` if the document has been closed. A closed document isn't synchronized anymore - * and won't be re-used when the same resource is opened again. - */ - readonly isClosed: boolean; - - /** - * Save the underlying file. - * - * @return A promise that will resolve to `true` when the file - * has been saved. If the save failed, will return `false`. - */ - save(): Thenable; - - /** - * The {@link EndOfLine end of line} sequence that is predominately - * used in this document. - */ - readonly eol: EndOfLine; - - /** - * The number of lines in this document. - */ - readonly lineCount: number; - - /** - * Returns a text line denoted by the line number. Note - * that the returned object is *not* live and changes to the - * document are not reflected. - * - * @param line A line number in [0, lineCount). - * @return A {@link TextLine line}. - */ - lineAt(line: number): TextLine; - - /** - * Returns a text line denoted by the position. Note - * that the returned object is *not* live and changes to the - * document are not reflected. - * - * The position will be {@link TextDocument.validatePosition adjusted}. - * - * @see {@link TextDocument.lineAt} - * - * @param position A position. - * @return A {@link TextLine line}. - */ - lineAt(position: Position): TextLine; - - /** - * Converts the position to a zero-based offset. - * - * The position will be {@link TextDocument.validatePosition adjusted}. - * - * @param position A position. - * @return A valid zero-based offset. - */ - offsetAt(position: Position): number; - - /** - * Converts a zero-based offset to a position. - * - * @param offset A zero-based offset. - * @return A valid {@link Position}. - */ - positionAt(offset: number): Position; - - /** - * Get the text of this document. A substring can be retrieved by providing - * a range. The range will be {@link TextDocument.validateRange adjusted}. - * - * @param range Include only the text included by the range. - * @return The text inside the provided range or the entire text. - */ - getText(range?: Range): string; - - /** - * Get a word-range at the given position. By default words are defined by - * common separators, like space, -, _, etc. In addition, per language custom - * [word definitions} can be defined. It - * is also possible to provide a custom regular expression. - * - * * *Note 1:* A custom regular expression must not match the empty string and - * if it does, it will be ignored. - * * *Note 2:* A custom regular expression will fail to match multiline strings - * and in the name of speed regular expressions should not match words with - * spaces. Use {@linkcode TextLine.text} for more complex, non-wordy, scenarios. - * - * The position will be {@link TextDocument.validatePosition adjusted}. - * - * @param position A position. - * @param regex Optional regular expression that describes what a word is. - * @return A range spanning a word, or `undefined`. - */ - getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined; - - /** - * Ensure a range is completely contained in this document. - * - * @param range A range. - * @return The given range or a new, adjusted range. - */ - validateRange(range: Range): Range; - - /** - * Ensure a position is contained in the range of this document. - * - * @param position A position. - * @return The given position or a new, adjusted position. - */ - validatePosition(position: Position): Position; - } - - /** - * Represents a line and character position, such as - * the position of the cursor. - * - * Position objects are __immutable__. Use the {@link Position.with with} or - * {@link Position.translate translate} methods to derive new positions - * from an existing position. - */ - export class Position { - - /** - * The zero-based line value. - */ - readonly line: number; - - /** - * The zero-based character value. - */ - readonly character: number; - - /** - * @param line A zero-based line value. - * @param character A zero-based character value. - */ - constructor(line: number, character: number); - - /** - * Check if this position is before `other`. - * - * @param other A position. - * @return `true` if position is on a smaller line - * or on the same line on a smaller character. - */ - isBefore(other: Position): boolean; - - /** - * Check if this position is before or equal to `other`. - * - * @param other A position. - * @return `true` if position is on a smaller line - * or on the same line on a smaller or equal character. - */ - isBeforeOrEqual(other: Position): boolean; - - /** - * Check if this position is after `other`. - * - * @param other A position. - * @return `true` if position is on a greater line - * or on the same line on a greater character. - */ - isAfter(other: Position): boolean; - - /** - * Check if this position is after or equal to `other`. - * - * @param other A position. - * @return `true` if position is on a greater line - * or on the same line on a greater or equal character. - */ - isAfterOrEqual(other: Position): boolean; - - /** - * Check if this position is equal to `other`. - * - * @param other A position. - * @return `true` if the line and character of the given position are equal to - * the line and character of this position. - */ - isEqual(other: Position): boolean; - - /** - * Compare this to `other`. - * - * @param other A position. - * @return A number smaller than zero if this position is before the given position, - * a number greater than zero if this position is after the given position, or zero when - * this and the given position are equal. - */ - compareTo(other: Position): number; - - /** - * Create a new position relative to this position. - * - * @param lineDelta Delta value for the line value, default is `0`. - * @param characterDelta Delta value for the character value, default is `0`. - * @return A position which line and character is the sum of the current line and - * character and the corresponding deltas. - */ - translate(lineDelta?: number, characterDelta?: number): Position; - - /** - * Derived a new position relative to this position. - * - * @param change An object that describes a delta to this position. - * @return A position that reflects the given delta. Will return `this` position if the change - * is not changing anything. - */ - translate(change: { lineDelta?: number; characterDelta?: number }): Position; - - /** - * Create a new position derived from this position. - * - * @param line Value that should be used as line value, default is the {@link Position.line existing value} - * @param character Value that should be used as character value, default is the {@link Position.character existing value} - * @return A position where line and character are replaced by the given values. - */ - with(line?: number, character?: number): Position; - - /** - * Derived a new position from this position. - * - * @param change An object that describes a change to this position. - * @return A position that reflects the given change. Will return `this` position if the change - * is not changing anything. - */ - with(change: { line?: number; character?: number }): Position; - } - - /** - * A range represents an ordered pair of two positions. - * It is guaranteed that {@link Range.start start}.isBeforeOrEqual({@link Range.end end}) - * - * Range objects are __immutable__. Use the {@link Range.with with}, - * {@link Range.intersection intersection}, or {@link Range.union union} methods - * to derive new ranges from an existing range. - */ - export class Range { - - /** - * The start position. It is before or equal to {@link Range.end end}. - */ - readonly start: Position; - - /** - * The end position. It is after or equal to {@link Range.start start}. - */ - readonly end: Position; - - /** - * Create a new range from two positions. If `start` is not - * before or equal to `end`, the values will be swapped. - * - * @param start A position. - * @param end A position. - */ - constructor(start: Position, end: Position); - - /** - * Create a new range from number coordinates. It is a shorter equivalent of - * using `new Range(new Position(startLine, startCharacter), new Position(endLine, endCharacter))` - * - * @param startLine A zero-based line value. - * @param startCharacter A zero-based character value. - * @param endLine A zero-based line value. - * @param endCharacter A zero-based character value. - */ - constructor(startLine: number, startCharacter: number, endLine: number, endCharacter: number); - - /** - * `true` if `start` and `end` are equal. - */ - isEmpty: boolean; - - /** - * `true` if `start.line` and `end.line` are equal. - */ - isSingleLine: boolean; - - /** - * Check if a position or a range is contained in this range. - * - * @param positionOrRange A position or a range. - * @return `true` if the position or range is inside or equal - * to this range. - */ - contains(positionOrRange: Position | Range): boolean; - - /** - * Check if `other` equals this range. - * - * @param other A range. - * @return `true` when start and end are {@link Position.isEqual equal} to - * start and end of this range. - */ - isEqual(other: Range): boolean; - - /** - * Intersect `range` with this range and returns a new range or `undefined` - * if the ranges have no overlap. - * - * @param range A range. - * @return A range of the greater start and smaller end positions. Will - * return undefined when there is no overlap. - */ - intersection(range: Range): Range | undefined; - - /** - * Compute the union of `other` with this range. - * - * @param other A range. - * @return A range of smaller start position and the greater end position. - */ - union(other: Range): Range; - - /** - * Derived a new range from this range. - * - * @param start A position that should be used as start. The default value is the {@link Range.start current start}. - * @param end A position that should be used as end. The default value is the {@link Range.end current end}. - * @return A range derived from this range with the given start and end position. - * If start and end are not different `this` range will be returned. - */ - with(start?: Position, end?: Position): Range; - - /** - * Derived a new range from this range. - * - * @param change An object that describes a change to this range. - * @return A range that reflects the given change. Will return `this` range if the change - * is not changing anything. - */ - with(change: { start?: Position; end?: Position }): Range; - } - - /** - * Represents a text selection in an editor. - */ - export class Selection extends Range { - - /** - * The position at which the selection starts. - * This position might be before or after {@link Selection.active active}. - */ - anchor: Position; - - /** - * The position of the cursor. - * This position might be before or after {@link Selection.anchor anchor}. - */ - active: Position; - - /** - * Create a selection from two positions. - * - * @param anchor A position. - * @param active A position. - */ - constructor(anchor: Position, active: Position); - - /** - * Create a selection from four coordinates. - * - * @param anchorLine A zero-based line value. - * @param anchorCharacter A zero-based character value. - * @param activeLine A zero-based line value. - * @param activeCharacter A zero-based character value. - */ - constructor(anchorLine: number, anchorCharacter: number, activeLine: number, activeCharacter: number); - - /** - * A selection is reversed if its {@link Selection.anchor anchor} is the {@link Selection.end end} position. - */ - isReversed: boolean; - } - - /** - * Represents sources that can cause {@link window.onDidChangeTextEditorSelection selection change events}. - */ - export enum TextEditorSelectionChangeKind { - /** - * Selection changed due to typing in the editor. - */ - Keyboard = 1, - /** - * Selection change due to clicking in the editor. - */ - Mouse = 2, - /** - * Selection changed because a command ran. - */ - Command = 3 - } - - /** - * Represents an event describing the change in a {@link TextEditor.selections text editor's selections}. - */ - export interface TextEditorSelectionChangeEvent { - /** - * The {@link TextEditor text editor} for which the selections have changed. - */ - readonly textEditor: TextEditor; - /** - * The new value for the {@link TextEditor.selections text editor's selections}. - */ - readonly selections: readonly Selection[]; - /** - * The {@link TextEditorSelectionChangeKind change kind} which has triggered this - * event. Can be `undefined`. - */ - readonly kind: TextEditorSelectionChangeKind | undefined; - } - - /** - * Represents an event describing the change in a {@link TextEditor.visibleRanges text editor's visible ranges}. - */ - export interface TextEditorVisibleRangesChangeEvent { - /** - * The {@link TextEditor text editor} for which the visible ranges have changed. - */ - readonly textEditor: TextEditor; - /** - * The new value for the {@link TextEditor.visibleRanges text editor's visible ranges}. - */ - readonly visibleRanges: readonly Range[]; - } - - /** - * Represents an event describing the change in a {@link TextEditor.options text editor's options}. - */ - export interface TextEditorOptionsChangeEvent { - /** - * The {@link TextEditor text editor} for which the options have changed. - */ - readonly textEditor: TextEditor; - /** - * The new value for the {@link TextEditor.options text editor's options}. - */ - readonly options: TextEditorOptions; - } - - /** - * Represents an event describing the change of a {@link TextEditor.viewColumn text editor's view column}. - */ - export interface TextEditorViewColumnChangeEvent { - /** - * The {@link TextEditor text editor} for which the view column has changed. - */ - readonly textEditor: TextEditor; - /** - * The new value for the {@link TextEditor.viewColumn text editor's view column}. - */ - readonly viewColumn: ViewColumn; - } - - /** - * Rendering style of the cursor. - */ - export enum TextEditorCursorStyle { - /** - * Render the cursor as a vertical thick line. - */ - Line = 1, - /** - * Render the cursor as a block filled. - */ - Block = 2, - /** - * Render the cursor as a thick horizontal line. - */ - Underline = 3, - /** - * Render the cursor as a vertical thin line. - */ - LineThin = 4, - /** - * Render the cursor as a block outlined. - */ - BlockOutline = 5, - /** - * Render the cursor as a thin horizontal line. - */ - UnderlineThin = 6 - } - - /** - * Rendering style of the line numbers. - */ - export enum TextEditorLineNumbersStyle { - /** - * Do not render the line numbers. - */ - Off = 0, - /** - * Render the line numbers. - */ - On = 1, - /** - * Render the line numbers with values relative to the primary cursor location. - */ - Relative = 2 - } - - /** - * Represents a {@link TextEditor text editor}'s {@link TextEditor.options options}. - */ - export interface TextEditorOptions { - - /** - * The size in spaces a tab takes. This is used for two purposes: - * - the rendering width of a tab character; - * - the number of spaces to insert when {@link TextEditorOptions.insertSpaces insertSpaces} is true. - * - * When getting a text editor's options, this property will always be a number (resolved). - * When setting a text editor's options, this property is optional and it can be a number or `"auto"`. - */ - tabSize?: number | string; - - /** - * When pressing Tab insert {@link TextEditorOptions.tabSize n} spaces. - * When getting a text editor's options, this property will always be a boolean (resolved). - * When setting a text editor's options, this property is optional and it can be a boolean or `"auto"`. - */ - insertSpaces?: boolean | string; - - /** - * The rendering style of the cursor in this editor. - * When getting a text editor's options, this property will always be present. - * When setting a text editor's options, this property is optional. - */ - cursorStyle?: TextEditorCursorStyle; - - /** - * Render relative line numbers w.r.t. the current line number. - * When getting a text editor's options, this property will always be present. - * When setting a text editor's options, this property is optional. - */ - lineNumbers?: TextEditorLineNumbersStyle; - } - - /** - * Represents a handle to a set of decorations - * sharing the same {@link DecorationRenderOptions styling options} in a {@link TextEditor text editor}. - * - * To get an instance of a `TextEditorDecorationType` use - * {@link window.createTextEditorDecorationType createTextEditorDecorationType}. - */ - export interface TextEditorDecorationType { - - /** - * Internal representation of the handle. - */ - readonly key: string; - - /** - * Remove this decoration type and all decorations on all text editors using it. - */ - dispose(): void; - } - - /** - * Represents different {@link TextEditor.revealRange reveal} strategies in a text editor. - */ - export enum TextEditorRevealType { - /** - * The range will be revealed with as little scrolling as possible. - */ - Default = 0, - /** - * The range will always be revealed in the center of the viewport. - */ - InCenter = 1, - /** - * If the range is outside the viewport, it will be revealed in the center of the viewport. - * Otherwise, it will be revealed with as little scrolling as possible. - */ - InCenterIfOutsideViewport = 2, - /** - * The range will always be revealed at the top of the viewport. - */ - AtTop = 3 - } - - /** - * Represents different positions for rendering a decoration in an {@link DecorationRenderOptions.overviewRulerLane overview ruler}. - * The overview ruler supports three lanes. - */ - export enum OverviewRulerLane { - Left = 1, - Center = 2, - Right = 4, - Full = 7 - } - - /** - * Describes the behavior of decorations when typing/editing at their edges. - */ - export enum DecorationRangeBehavior { - /** - * The decoration's range will widen when edits occur at the start or end. - */ - OpenOpen = 0, - /** - * The decoration's range will not widen when edits occur at the start of end. - */ - ClosedClosed = 1, - /** - * The decoration's range will widen when edits occur at the start, but not at the end. - */ - OpenClosed = 2, - /** - * The decoration's range will widen when edits occur at the end, but not at the start. - */ - ClosedOpen = 3 - } - - /** - * Represents options to configure the behavior of showing a {@link TextDocument document} in an {@link TextEditor editor}. - */ - export interface TextDocumentShowOptions { - /** - * An optional view column in which the {@link TextEditor editor} should be shown. - * The default is the {@link ViewColumn.Active active}. Columns that do not exist - * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. - * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently - * active one. - */ - viewColumn?: ViewColumn; - - /** - * An optional flag that when `true` will stop the {@link TextEditor editor} from taking focus. - */ - preserveFocus?: boolean; - - /** - * An optional flag that controls if an {@link TextEditor editor}-tab shows as preview. Preview tabs will - * be replaced and reused until set to stay - either explicitly or through editing. - * - * *Note* that the flag is ignored if a user has disabled preview editors in settings. - */ - preview?: boolean; - - /** - * An optional selection to apply for the document in the {@link TextEditor editor}. - */ - selection?: Range; - } - - /** - * Represents an event describing the change in a {@link NotebookEditor.selections notebook editor's selections}. - */ - export interface NotebookEditorSelectionChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the selections have changed. - */ - readonly notebookEditor: NotebookEditor; - - /** - * The new value for the {@link NotebookEditor.selections notebook editor's selections}. - */ - readonly selections: readonly NotebookRange[]; - } - - /** - * Represents an event describing the change in a {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. - */ - export interface NotebookEditorVisibleRangesChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. - */ - readonly notebookEditor: NotebookEditor; - - /** - * The new value for the {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. - */ - readonly visibleRanges: readonly NotebookRange[]; - } - - /** - * Represents options to configure the behavior of showing a {@link NotebookDocument notebook document} in an {@link NotebookEditor notebook editor}. - */ - export interface NotebookDocumentShowOptions { - /** - * An optional view column in which the {@link NotebookEditor notebook editor} should be shown. - * The default is the {@link ViewColumn.Active active}. Columns that do not exist - * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. - * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently - * active one. - */ - readonly viewColumn?: ViewColumn; - - /** - * An optional flag that when `true` will stop the {@link NotebookEditor notebook editor} from taking focus. - */ - readonly preserveFocus?: boolean; - - /** - * An optional flag that controls if an {@link NotebookEditor notebook editor}-tab shows as preview. Preview tabs will - * be replaced and reused until set to stay - either explicitly or through editing. The default behaviour depends - * on the `workbench.editor.enablePreview`-setting. - */ - readonly preview?: boolean; - - /** - * An optional selection to apply for the document in the {@link NotebookEditor notebook editor}. - */ - readonly selections?: readonly NotebookRange[]; - } - - /** - * A reference to one of the workbench colors as defined in https://code.visualstudio.com/docs/getstarted/theme-color-reference. - * Using a theme color is preferred over a custom color as it gives theme authors and users the possibility to change the color. - */ - export class ThemeColor { - - /** - * Creates a reference to a theme color. - * @param id of the color. The available colors are listed in https://code.visualstudio.com/docs/getstarted/theme-color-reference. - */ - constructor(id: string); - } - - /** - * A reference to a named icon. Currently, {@link ThemeIcon.File File}, {@link ThemeIcon.Folder Folder}, - * and [ThemeIcon ids](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing) are supported. - * Using a theme icon is preferred over a custom icon as it gives product theme authors the possibility to change the icons. - * - * *Note* that theme icons can also be rendered inside labels and descriptions. Places that support theme icons spell this out - * and they use the `$()`-syntax, for instance `quickPick.label = "Hello World $(globe)"`. - */ - export class ThemeIcon { - /** - * Reference to an icon representing a file. The icon is taken from the current file icon theme or a placeholder icon is used. - */ - static readonly File: ThemeIcon; - - /** - * Reference to an icon representing a folder. The icon is taken from the current file icon theme or a placeholder icon is used. - */ - static readonly Folder: ThemeIcon; - - /** - * The id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. - */ - readonly id: string; - - /** - * The optional ThemeColor of the icon. The color is currently only used in {@link TreeItem}. - */ - readonly color?: ThemeColor | undefined; - - /** - * Creates a reference to a theme icon. - * @param id id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. - * @param color optional `ThemeColor` for the icon. The color is currently only used in {@link TreeItem}. - */ - constructor(id: string, color?: ThemeColor); - } - - /** - * Represents theme specific rendering styles for a {@link TextEditorDecorationType text editor decoration}. - */ - export interface ThemableDecorationRenderOptions { - /** - * Background color of the decoration. Use rgba() and define transparent background colors to play well with other decorations. - * Alternatively a color from the color registry can be {@link ThemeColor referenced}. - */ - backgroundColor?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - outline?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'outline' for setting one or more of the individual outline properties. - */ - outlineColor?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'outline' for setting one or more of the individual outline properties. - */ - outlineStyle?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'outline' for setting one or more of the individual outline properties. - */ - outlineWidth?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - border?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderColor?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderRadius?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderSpacing?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderStyle?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - * Better use 'border' for setting one or more of the individual border properties. - */ - borderWidth?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - fontStyle?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - fontWeight?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - textDecoration?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - cursor?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - color?: string | ThemeColor; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - opacity?: string; - - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - letterSpacing?: string; - - /** - * An **absolute path** or an URI to an image to be rendered in the gutter. - */ - gutterIconPath?: string | Uri; - - /** - * Specifies the size of the gutter icon. - * Available values are 'auto', 'contain', 'cover' and any percentage value. - * For further information: https://msdn.microsoft.com/en-us/library/jj127316(v=vs.85).aspx - */ - gutterIconSize?: string; - - /** - * The color of the decoration in the overview ruler. Use rgba() and define transparent colors to play well with other decorations. - */ - overviewRulerColor?: string | ThemeColor; - - /** - * Defines the rendering options of the attachment that is inserted before the decorated text. - */ - before?: ThemableDecorationAttachmentRenderOptions; - - /** - * Defines the rendering options of the attachment that is inserted after the decorated text. - */ - after?: ThemableDecorationAttachmentRenderOptions; - } - - export interface ThemableDecorationAttachmentRenderOptions { - /** - * Defines a text content that is shown in the attachment. Either an icon or a text can be shown, but not both. - */ - contentText?: string; - /** - * An **absolute path** or an URI to an image to be rendered in the attachment. Either an icon - * or a text can be shown, but not both. - */ - contentIconPath?: string | Uri; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - border?: string; - /** - * CSS styling property that will be applied to text enclosed by a decoration. - */ - borderColor?: string | ThemeColor; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontStyle?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - fontWeight?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - textDecoration?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - color?: string | ThemeColor; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - backgroundColor?: string | ThemeColor; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - margin?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - width?: string; - /** - * CSS styling property that will be applied to the decoration attachment. - */ - height?: string; - } - - /** - * Represents rendering styles for a {@link TextEditorDecorationType text editor decoration}. - */ - export interface DecorationRenderOptions extends ThemableDecorationRenderOptions { - /** - * Should the decoration be rendered also on the whitespace after the line text. - * Defaults to `false`. - */ - isWholeLine?: boolean; - - /** - * Customize the growing behavior of the decoration when edits occur at the edges of the decoration's range. - * Defaults to `DecorationRangeBehavior.OpenOpen`. - */ - rangeBehavior?: DecorationRangeBehavior; - - /** - * The position in the overview ruler where the decoration should be rendered. - */ - overviewRulerLane?: OverviewRulerLane; - - /** - * Overwrite options for light themes. - */ - light?: ThemableDecorationRenderOptions; - - /** - * Overwrite options for dark themes. - */ - dark?: ThemableDecorationRenderOptions; - } - - /** - * Represents options for a specific decoration in a {@link TextEditorDecorationType decoration set}. - */ - export interface DecorationOptions { - - /** - * Range to which this decoration is applied. The range must not be empty. - */ - range: Range; - - /** - * A message that should be rendered when hovering over the decoration. - */ - hoverMessage?: MarkdownString | MarkedString | Array; - - /** - * Render options applied to the current decoration. For performance reasons, keep the - * number of decoration specific options small, and use decoration types wherever possible. - */ - renderOptions?: DecorationInstanceRenderOptions; - } - - export interface ThemableDecorationInstanceRenderOptions { - /** - * Defines the rendering options of the attachment that is inserted before the decorated text. - */ - before?: ThemableDecorationAttachmentRenderOptions; - - /** - * Defines the rendering options of the attachment that is inserted after the decorated text. - */ - after?: ThemableDecorationAttachmentRenderOptions; - } - - export interface DecorationInstanceRenderOptions extends ThemableDecorationInstanceRenderOptions { - /** - * Overwrite options for light themes. - */ - light?: ThemableDecorationInstanceRenderOptions; - - /** - * Overwrite options for dark themes. - */ - dark?: ThemableDecorationInstanceRenderOptions; - } - - /** - * Represents an editor that is attached to a {@link TextDocument document}. - */ - export interface TextEditor { - - /** - * The document associated with this text editor. The document will be the same for the entire lifetime of this text editor. - */ - readonly document: TextDocument; - - /** - * The primary selection on this text editor. Shorthand for `TextEditor.selections[0]`. - */ - selection: Selection; - - /** - * The selections in this text editor. The primary selection is always at index 0. - */ - selections: readonly Selection[]; - - /** - * The current visible ranges in the editor (vertically). - * This accounts only for vertical scrolling, and not for horizontal scrolling. - */ - readonly visibleRanges: readonly Range[]; - - /** - * Text editor options. - */ - options: TextEditorOptions; - - /** - * The column in which this editor shows. Will be `undefined` in case this - * isn't one of the main editors, e.g. an embedded editor, or when the editor - * column is larger than three. - */ - readonly viewColumn: ViewColumn | undefined; - - /** - * Perform an edit on the document associated with this text editor. - * - * The given callback-function is invoked with an {@link TextEditorEdit edit-builder} which must - * be used to make edits. Note that the edit-builder is only valid while the - * callback executes. - * - * @param callback A function which can create edits using an {@link TextEditorEdit edit-builder}. - * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. - * @return A promise that resolves with a value indicating if the edits could be applied. - */ - edit(callback: (editBuilder: TextEditorEdit) => void, options?: { readonly undoStopBefore: boolean; readonly undoStopAfter: boolean }): Thenable; - - /** - * Insert a {@link SnippetString snippet} and put the editor into snippet mode. "Snippet mode" - * means the editor adds placeholders and additional cursors so that the user can complete - * or accept the snippet. - * - * @param snippet The snippet to insert in this edit. - * @param location Position or range at which to insert the snippet, defaults to the current editor selection or selections. - * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. - * @return A promise that resolves with a value indicating if the snippet could be inserted. Note that the promise does not signal - * that the snippet is completely filled-in or accepted. - */ - insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[], options?: { readonly undoStopBefore: boolean; readonly undoStopAfter: boolean }): Thenable; - - /** - * Adds a set of decorations to the text editor. If a set of decorations already exists with - * the given {@link TextEditorDecorationType decoration type}, they will be replaced. If - * `rangesOrOptions` is empty, the existing decorations with the given {@link TextEditorDecorationType decoration type} - * will be removed. - * - * @see {@link window.createTextEditorDecorationType createTextEditorDecorationType}. - * - * @param decorationType A decoration type. - * @param rangesOrOptions Either {@link Range ranges} or more detailed {@link DecorationOptions options}. - */ - setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: readonly Range[] | readonly DecorationOptions[]): void; - - /** - * Scroll as indicated by `revealType` in order to reveal the given range. - * - * @param range A range. - * @param revealType The scrolling strategy for revealing `range`. - */ - revealRange(range: Range, revealType?: TextEditorRevealType): void; - - /** - * Show the text editor. - * - * @deprecated Use {@link window.showTextDocument} instead. - * - * @param column The {@link ViewColumn column} in which to show this editor. - * This method shows unexpected behavior and will be removed in the next major update. - */ - show(column?: ViewColumn): void; - - /** - * Hide the text editor. - * - * @deprecated Use the command `workbench.action.closeActiveEditor` instead. - * This method shows unexpected behavior and will be removed in the next major update. - */ - hide(): void; - } - - /** - * Represents an end of line character sequence in a {@link TextDocument document}. - */ - export enum EndOfLine { - /** - * The line feed `\n` character. - */ - LF = 1, - /** - * The carriage return line feed `\r\n` sequence. - */ - CRLF = 2 - } - - /** - * A complex edit that will be applied in one transaction on a TextEditor. - * This holds a description of the edits and if the edits are valid (i.e. no overlapping regions, document was not changed in the meantime, etc.) - * they can be applied on a {@link TextDocument document} associated with a {@link TextEditor text editor}. - */ - export interface TextEditorEdit { - /** - * Replace a certain text region with a new value. - * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. - * - * @param location The range this operation should remove. - * @param value The new text this operation should insert after removing `location`. - */ - replace(location: Position | Range | Selection, value: string): void; - - /** - * Insert text at a location. - * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. - * Although the equivalent text edit can be made with {@link TextEditorEdit.replace replace}, `insert` will produce a different resulting selection (it will get moved). - * - * @param location The position where the new text should be inserted. - * @param value The new text this operation should insert. - */ - insert(location: Position, value: string): void; - - /** - * Delete a certain text region. - * - * @param location The range this operation should remove. - */ - delete(location: Range | Selection): void; - - /** - * Set the end of line sequence. - * - * @param endOfLine The new end of line for the {@link TextDocument document}. - */ - setEndOfLine(endOfLine: EndOfLine): void; - } - - /** - * A universal resource identifier representing either a file on disk - * or another resource, like untitled resources. - */ - export class Uri { - - /** - * Create an URI from a string, e.g. `http://www.example.com/some/path`, - * `file:///usr/home`, or `scheme:with/path`. - * - * *Note* that for a while uris without a `scheme` were accepted. That is not correct - * as all uris should have a scheme. To avoid breakage of existing code the optional - * `strict`-argument has been added. We *strongly* advise to use it, e.g. `Uri.parse('my:uri', true)` - * - * @see {@link Uri.toString} - * @param value The string value of an Uri. - * @param strict Throw an error when `value` is empty or when no `scheme` can be parsed. - * @return A new Uri instance. - */ - static parse(value: string, strict?: boolean): Uri; - - /** - * Create an URI from a file system path. The {@link Uri.scheme scheme} - * will be `file`. - * - * The *difference* between {@link Uri.parse} and {@link Uri.file} is that the latter treats the argument - * as path, not as stringified-uri. E.g. `Uri.file(path)` is *not* the same as - * `Uri.parse('file://' + path)` because the path might contain characters that are - * interpreted (# and ?). See the following sample: - * ```ts - * const good = URI.file('/coding/c#/project1'); - * good.scheme === 'file'; - * good.path === '/coding/c#/project1'; - * good.fragment === ''; - * - * const bad = URI.parse('file://' + '/coding/c#/project1'); - * bad.scheme === 'file'; - * bad.path === '/coding/c'; // path is now broken - * bad.fragment === '/project1'; - * ``` - * - * @param path A file system or UNC path. - * @return A new Uri instance. - */ - static file(path: string): Uri; - - /** - * Create a new uri which path is the result of joining - * the path of the base uri with the provided path segments. - * - * - Note 1: `joinPath` only affects the path component - * and all other components (scheme, authority, query, and fragment) are - * left as they are. - * - Note 2: The base uri must have a path; an error is thrown otherwise. - * - * The path segments are normalized in the following ways: - * - sequences of path separators (`/` or `\`) are replaced with a single separator - * - for `file`-uris on windows, the backslash-character (`\`) is considered a path-separator - * - the `..`-segment denotes the parent segment, the `.` denotes the current segment - * - paths have a root which always remains, for instance on windows drive-letters are roots - * so that is true: `joinPath(Uri.file('file:///c:/root'), '../../other').fsPath === 'c:/other'` - * - * @param base An uri. Must have a path. - * @param pathSegments One more more path fragments - * @returns A new uri which path is joined with the given fragments - */ - static joinPath(base: Uri, ...pathSegments: string[]): Uri; - - /** - * Create an URI from its component parts - * - * @see {@link Uri.toString} - * @param components The component parts of an Uri. - * @return A new Uri instance. - */ - static from(components: { readonly scheme: string; readonly authority?: string; readonly path?: string; readonly query?: string; readonly fragment?: string }): Uri; - - /** - * Use the `file` and `parse` factory functions to create new `Uri` objects. - */ - private constructor(scheme: string, authority: string, path: string, query: string, fragment: string); - - /** - * Scheme is the `http` part of `http://www.example.com/some/path?query#fragment`. - * The part before the first colon. - */ - readonly scheme: string; - - /** - * Authority is the `www.example.com` part of `http://www.example.com/some/path?query#fragment`. - * The part between the first double slashes and the next slash. - */ - readonly authority: string; - - /** - * Path is the `/some/path` part of `http://www.example.com/some/path?query#fragment`. - */ - readonly path: string; - - /** - * Query is the `query` part of `http://www.example.com/some/path?query#fragment`. - */ - readonly query: string; - - /** - * Fragment is the `fragment` part of `http://www.example.com/some/path?query#fragment`. - */ - readonly fragment: string; - - /** - * The string representing the corresponding file system path of this Uri. - * - * Will handle UNC paths and normalize windows drive letters to lower-case. Also - * uses the platform specific path separator. - * - * * Will *not* validate the path for invalid characters and semantics. - * * Will *not* look at the scheme of this Uri. - * * The resulting string shall *not* be used for display purposes but - * for disk operations, like `readFile` et al. - * - * The *difference* to the {@linkcode Uri.path path}-property is the use of the platform specific - * path separator and the handling of UNC paths. The sample below outlines the difference: - * ```ts - * const u = URI.parse('file://server/c$/folder/file.txt') - * u.authority === 'server' - * u.path === '/shares/c$/file.txt' - * u.fsPath === '\\server\c$\folder\file.txt' - * ``` - */ - readonly fsPath: string; - - /** - * Derive a new Uri from this Uri. - * - * ```ts - * let file = Uri.parse('before:some/file/path'); - * let other = file.with({ scheme: 'after' }); - * assert.ok(other.toString() === 'after:some/file/path'); - * ``` - * - * @param change An object that describes a change to this Uri. To unset components use `null` or - * the empty string. - * @return A new Uri that reflects the given change. Will return `this` Uri if the change - * is not changing anything. - */ - with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri; - - /** - * Returns a string representation of this Uri. The representation and normalization - * of a URI depends on the scheme. - * - * * The resulting string can be safely used with {@link Uri.parse}. - * * The resulting string shall *not* be used for display purposes. - * - * *Note* that the implementation will encode _aggressive_ which often leads to unexpected, - * but not incorrect, results. For instance, colons are encoded to `%3A` which might be unexpected - * in file-uri. Also `&` and `=` will be encoded which might be unexpected for http-uris. For stability - * reasons this cannot be changed anymore. If you suffer from too aggressive encoding you should use - * the `skipEncoding`-argument: `uri.toString(true)`. - * - * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that - * the `#` and `?` characters occurring in the path will always be encoded. - * @returns A string representation of this Uri. - */ - toString(skipEncoding?: boolean): string; - - /** - * Returns a JSON representation of this Uri. - * - * @return An object. - */ - toJSON(): any; - } - - /** - * A cancellation token is passed to an asynchronous or long running - * operation to request cancellation, like cancelling a request - * for completion items because the user continued to type. - * - * To get an instance of a `CancellationToken` use a - * {@link CancellationTokenSource}. - */ - export interface CancellationToken { - - /** - * Is `true` when the token has been cancelled, `false` otherwise. - */ - isCancellationRequested: boolean; - - /** - * An {@link Event} which fires upon cancellation. - */ - onCancellationRequested: Event; - } - - /** - * A cancellation source creates and controls a {@link CancellationToken cancellation token}. - */ - export class CancellationTokenSource { - - /** - * The cancellation token of this source. - */ - token: CancellationToken; - - /** - * Signal cancellation on the token. - */ - cancel(): void; - - /** - * Dispose object and free resources. - */ - dispose(): void; - } - - /** - * An error type that should be used to signal cancellation of an operation. - * - * This type can be used in response to a {@link CancellationToken cancellation token} - * being cancelled or when an operation is being cancelled by the - * executor of that operation. - */ - export class CancellationError extends Error { - - /** - * Creates a new cancellation error. - */ - constructor(); - } - - /** - * Represents a type which can release resources, such - * as event listening or a timer. - */ - export class Disposable { - - /** - * Combine many disposable-likes into one. You can use this method when having objects with - * a dispose function which aren't instances of `Disposable`. - * - * @param disposableLikes Objects that have at least a `dispose`-function member. Note that asynchronous - * dispose-functions aren't awaited. - * @return Returns a new disposable which, upon dispose, will - * dispose all provided disposables. - */ - static from(...disposableLikes: { dispose: () => any }[]): Disposable; - - /** - * Creates a new disposable that calls the provided function - * on dispose. - * - * *Note* that an asynchronous function is not awaited. - * - * @param callOnDispose Function that disposes something. - */ - constructor(callOnDispose: () => any); - - /** - * Dispose this object. - */ - dispose(): any; - } - - /** - * Represents a typed event. - * - * A function that represents an event to which you subscribe by calling it with - * a listener function as argument. - * - * @example - * item.onDidChange(function(event) { console.log("Event happened: " + event); }); - */ - export interface Event { - - /** - * A function that represents an event to which you subscribe by calling it with - * a listener function as argument. - * - * @param listener The listener function will be called when the event happens. - * @param thisArgs The `this`-argument which will be used when calling the event listener. - * @param disposables An array to which a {@link Disposable} will be added. - * @return A disposable which unsubscribes the event listener. - */ - (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable; - } - - /** - * An event emitter can be used to create and manage an {@link Event} for others - * to subscribe to. One emitter always owns one event. - * - * Use this class if you want to provide event from within your extension, for instance - * inside a {@link TextDocumentContentProvider} or when providing - * API to other extensions. - */ - export class EventEmitter { - - /** - * The event listeners can subscribe to. - */ - event: Event; - - /** - * Notify all subscribers of the {@link EventEmitter.event event}. Failure - * of one or more listener will not fail this function call. - * - * @param data The event object. - */ - fire(data: T): void; - - /** - * Dispose this object and free resources. - */ - dispose(): void; - } - - /** - * A file system watcher notifies about changes to files and folders - * on disk or from other {@link FileSystemProvider FileSystemProviders}. - * - * To get an instance of a `FileSystemWatcher` use - * {@link workspace.createFileSystemWatcher createFileSystemWatcher}. - */ - export interface FileSystemWatcher extends Disposable { - - /** - * true if this file system watcher has been created such that - * it ignores creation file system events. - */ - readonly ignoreCreateEvents: boolean; - - /** - * true if this file system watcher has been created such that - * it ignores change file system events. - */ - readonly ignoreChangeEvents: boolean; - - /** - * true if this file system watcher has been created such that - * it ignores delete file system events. - */ - readonly ignoreDeleteEvents: boolean; - - /** - * An event which fires on file/folder creation. - */ - readonly onDidCreate: Event; - - /** - * An event which fires on file/folder change. - */ - readonly onDidChange: Event; - - /** - * An event which fires on file/folder deletion. - */ - readonly onDidDelete: Event; - } - - /** - * A text document content provider allows to add readonly documents - * to the editor, such as source from a dll or generated html from md. - * - * Content providers are {@link workspace.registerTextDocumentContentProvider registered} - * for a {@link Uri.scheme uri-scheme}. When a uri with that scheme is to - * be {@link workspace.openTextDocument loaded} the content provider is - * asked. - */ - export interface TextDocumentContentProvider { - - /** - * An event to signal a resource has changed. - */ - onDidChange?: Event; - - /** - * Provide textual content for a given uri. - * - * The editor will use the returned string-content to create a readonly - * {@link TextDocument document}. Resources allocated should be released when - * the corresponding document has been {@link workspace.onDidCloseTextDocument closed}. - * - * **Note**: The contents of the created {@link TextDocument document} might not be - * identical to the provided text due to end-of-line-sequence normalization. - * - * @param uri An uri which scheme matches the scheme this provider was {@link workspace.registerTextDocumentContentProvider registered} for. - * @param token A cancellation token. - * @return A string or a thenable that resolves to such. - */ - provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult; - } - - /** - * The kind of {@link QuickPickItem quick pick item}. - */ - export enum QuickPickItemKind { - /** - * When a {@link QuickPickItem} has a kind of {@link Separator}, the item is just a visual separator and does not represent a real item. - * The only property that applies is {@link QuickPickItem.label label }. All other properties on {@link QuickPickItem} will be ignored and have no effect. - */ - Separator = -1, - /** - * The default {@link QuickPickItem.kind} is an item that can be selected in the quick pick. - */ - Default = 0, - } - - /** - * Represents an item that can be selected from - * a list of items. - */ - export interface QuickPickItem { - - /** - * A human-readable string which is rendered prominent. Supports rendering of {@link ThemeIcon theme icons} via - * the `$()`-syntax. - */ - label: string; - - /** - * The kind of QuickPickItem that will determine how this item is rendered in the quick pick. When not specified, - * the default is {@link QuickPickItemKind.Default}. - */ - kind?: QuickPickItemKind; - - /** - * A human-readable string which is rendered less prominent in the same line. Supports rendering of - * {@link ThemeIcon theme icons} via the `$()`-syntax. - * - * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} - */ - description?: string; - - /** - * A human-readable string which is rendered less prominent in a separate line. Supports rendering of - * {@link ThemeIcon theme icons} via the `$()`-syntax. - * - * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} - */ - detail?: string; - - /** - * Optional flag indicating if this item is picked initially. This is only honored when using - * the {@link window.showQuickPick showQuickPick()} API. To do the same thing with - * the {@link window.createQuickPick createQuickPick()} API, simply set the {@link QuickPick.selectedItems} - * to the items you want picked initially. - * (*Note:* This is only honored when the picker allows multiple selections.) - * - * @see {@link QuickPickOptions.canPickMany} - * - * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} - */ - picked?: boolean; - - /** - * Always show this item. - * - * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} - */ - alwaysShow?: boolean; - - /** - * Optional buttons that will be rendered on this particular item. These buttons will trigger - * an {@link QuickPickItemButtonEvent} when clicked. Buttons are only rendered when using a quickpick - * created by the {@link window.createQuickPick createQuickPick()} API. Buttons are not rendered when using - * the {@link window.showQuickPick showQuickPick()} API. - * - * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} - */ - buttons?: readonly QuickInputButton[]; - } - - /** - * Options to configure the behavior of the quick pick UI. - */ - export interface QuickPickOptions { - - /** - * An optional string that represents the title of the quick pick. - */ - title?: string; - - /** - * An optional flag to include the description when filtering the picks. - */ - matchOnDescription?: boolean; - - /** - * An optional flag to include the detail when filtering the picks. - */ - matchOnDetail?: boolean; - - /** - * An optional string to show as placeholder in the input box to guide the user what to pick on. - */ - placeHolder?: string; - - /** - * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. - * This setting is ignored on iPad and is always false. - */ - ignoreFocusOut?: boolean; - - /** - * An optional flag to make the picker accept multiple selections, if true the result is an array of picks. - */ - canPickMany?: boolean; - - /** - * An optional function that is invoked whenever an item is selected. - */ - onDidSelectItem?(item: QuickPickItem | string): any; - } - - /** - * Options to configure the behaviour of the {@link WorkspaceFolder workspace folder} pick UI. - */ - export interface WorkspaceFolderPickOptions { - - /** - * An optional string to show as placeholder in the input box to guide the user what to pick on. - */ - placeHolder?: string; - - /** - * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. - * This setting is ignored on iPad and is always false. - */ - ignoreFocusOut?: boolean; - } - - /** - * Options to configure the behaviour of a file open dialog. - * - * * Note 1: On Windows and Linux, a file dialog cannot be both a file selector and a folder selector, so if you - * set both `canSelectFiles` and `canSelectFolders` to `true` on these platforms, a folder selector will be shown. - * * 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 { - /** - * The resource the dialog shows when opened. - */ - defaultUri?: Uri; - - /** - * A human-readable string for the open button. - */ - openLabel?: string; - - /** - * Allow to select files, defaults to `true`. - */ - canSelectFiles?: boolean; - - /** - * Allow to select folders, defaults to `false`. - */ - canSelectFolders?: boolean; - - /** - * Allow to select many files or folders. - */ - canSelectMany?: boolean; - - /** - * A set of file filters that are used by the dialog. Each entry is a human-readable label, - * like "TypeScript", and an array of extensions, e.g. - * ```ts - * { - * 'Images': ['png', 'jpg'] - * 'TypeScript': ['ts', 'tsx'] - * } - * ``` - */ - filters?: { [name: string]: string[] }; - - /** - * Dialog title. - * - * This parameter might be ignored, as not all operating systems display a title on open dialogs - * (for example, macOS). - */ - title?: string; - } - - /** - * Options to configure the behaviour of a file save dialog. - */ - export interface SaveDialogOptions { - /** - * The resource the dialog shows when opened. - */ - defaultUri?: Uri; - - /** - * A human-readable string for the save button. - */ - saveLabel?: string; - - /** - * A set of file filters that are used by the dialog. Each entry is a human-readable label, - * like "TypeScript", and an array of extensions, e.g. - * ```ts - * { - * 'Images': ['png', 'jpg'] - * 'TypeScript': ['ts', 'tsx'] - * } - * ``` - */ - filters?: { [name: string]: string[] }; - - /** - * Dialog title. - * - * This parameter might be ignored, as not all operating systems display a title on save dialogs - * (for example, macOS). - */ - title?: string; - } - - /** - * Represents an action that is shown with an information, warning, or - * error message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * @see {@link window.showWarningMessage showWarningMessage} - * @see {@link window.showErrorMessage showErrorMessage} - */ - export interface MessageItem { - - /** - * A short title like 'Retry', 'Open Log' etc. - */ - title: string; - - /** - * A hint for modal dialogs that the item should be triggered - * when the user cancels the dialog (e.g. by pressing the ESC - * key). - * - * Note: this option is ignored for non-modal messages. - */ - isCloseAffordance?: boolean; - } - - /** - * Options to configure the behavior of the message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * @see {@link window.showWarningMessage showWarningMessage} - * @see {@link window.showErrorMessage showErrorMessage} - */ - export interface MessageOptions { - - /** - * Indicates that this message should be modal. - */ - modal?: boolean; - - /** - * Human-readable detail message that is rendered less prominent. _Note_ that detail - * is only shown for {@link MessageOptions.modal modal} messages. - */ - detail?: string; - } - - /** - * Impacts the behavior and appearance of the validation message. - */ - export enum InputBoxValidationSeverity { - Info = 1, - Warning = 2, - Error = 3 - } - - /** - * Object to configure the behavior of the validation message. - */ - export interface InputBoxValidationMessage { - /** - * The validation message to display. - */ - readonly message: string; - - /** - * The severity of the validation message. - * NOTE: When using `InputBoxValidationSeverity.Error`, the user will not be allowed to accept (hit ENTER) the input. - * `Info` and `Warning` will still allow the InputBox to accept the input. - */ - readonly severity: InputBoxValidationSeverity; - } - - /** - * Options to configure the behavior of the input box UI. - */ - export interface InputBoxOptions { - - /** - * An optional string that represents the title of the input box. - */ - title?: string; - - /** - * The value to pre-fill in the input box. - */ - value?: string; - - /** - * Selection of the pre-filled {@linkcode InputBoxOptions.value value}. Defined as tuple of two number where the - * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole - * pre-filled value will be selected, when empty (start equals end) only the cursor will be set, - * otherwise the defined range will be selected. - */ - valueSelection?: [number, number]; - - /** - * The text to display underneath the input box. - */ - prompt?: string; - - /** - * An optional string to show as placeholder in the input box to guide the user what to type. - */ - placeHolder?: string; - - /** - * Controls if a password input is shown. Password input hides the typed text. - */ - password?: boolean; - - /** - * Set to `true` to keep the input box open when focus moves to another part of the editor or to another window. - * This setting is ignored on iPad and is always false. - */ - ignoreFocusOut?: boolean; - - /** - * An optional function that will be called to validate input and to give a hint - * to the user. - * - * @param value The current value of the input box. - * @return Either a human-readable string which is presented as an error message or an {@link InputBoxValidationMessage} - * which can provide a specific message severity. Return `undefined`, `null`, or the empty string when 'value' is valid. - */ - validateInput?(value: string): string | InputBoxValidationMessage | undefined | null | - Thenable; - } - - /** - * A relative pattern is a helper to construct glob patterns that are matched - * relatively to a base file path. The base path can either be an absolute file - * path as string or uri or a {@link WorkspaceFolder workspace folder}, which is the - * preferred way of creating the relative pattern. - */ - export class RelativePattern { - - /** - * A base file path to which this pattern will be matched against relatively. - */ - baseUri: Uri; - - /** - * A base file path to which this pattern will be matched against relatively. - * - * This matches the `fsPath` value of {@link RelativePattern.baseUri}. - * - * *Note:* updating this value will update {@link RelativePattern.baseUri} to - * be a uri with `file` scheme. - * - * @deprecated This property is deprecated, please use {@link RelativePattern.baseUri} instead. - */ - base: string; - - /** - * A file glob pattern like `*.{ts,js}` that will be matched on file paths - * relative to the base path. - * - * Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`, - * the file glob pattern will match on `index.js`. - */ - pattern: string; - - /** - * Creates a new relative pattern object with a base file path and pattern to match. This pattern - * will be matched on file paths relative to the base. - * - * Example: - * ```ts - * const folder = vscode.workspace.workspaceFolders?.[0]; - * if (folder) { - * - * // Match any TypeScript file in the root of this workspace folder - * const pattern1 = new vscode.RelativePattern(folder, '*.ts'); - * - * // Match any TypeScript file in `someFolder` inside this workspace folder - * const pattern2 = new vscode.RelativePattern(folder, 'someFolder/*.ts'); - * } - * ``` - * - * @param base A base to which this pattern will be matched against relatively. It is recommended - * to pass in a {@link WorkspaceFolder workspace folder} if the pattern should match inside the workspace. - * Otherwise, a uri or string should only be used if the pattern is for a file path outside the workspace. - * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on paths relative to the base. - */ - constructor(base: WorkspaceFolder | Uri | string, pattern: string); - } - - /** - * A file glob pattern to match file paths against. This can either be a glob pattern string - * (like `**​/*.{ts,js}` or `*.{ts,js}`) or a {@link RelativePattern relative pattern}. - * - * Glob patterns can have the following syntax: - * * `*` to match zero or more characters in a path segment - * * `?` to match on one character in a path segment - * * `**` to match any number of path segments, including none - * * `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) - * * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - * * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) - * - * Note: a backslash (`\`) is not valid within a glob pattern. If you have an existing file - * path to match against, consider to use the {@link RelativePattern relative pattern} support - * that takes care of converting any backslash into slash. Otherwise, make sure to convert - * any backslash to slash when creating the glob pattern. - */ - export type GlobPattern = string | RelativePattern; - - /** - * A document filter denotes a document by different properties like - * the {@link TextDocument.languageId language}, the {@link Uri.scheme scheme} of - * its resource, or a glob-pattern that is applied to the {@link TextDocument.fileName path}. - * - * @example A language filter that applies to typescript files on disk - * { language: 'typescript', scheme: 'file' } - * - * @example A language filter that applies to all package.json paths - * { language: 'json', pattern: '**​/package.json' } - */ - export interface DocumentFilter { - - /** - * A language id, like `typescript`. - */ - readonly language?: string; - - /** - * The {@link NotebookDocument.notebookType type} of a notebook, like `jupyter-notebook`. This allows - * to narrow down on the type of a notebook that a {@link NotebookCell.document cell document} belongs to. - * - * *Note* that setting the `notebookType`-property changes how `scheme` and `pattern` are interpreted. When set - * they are evaluated against the {@link NotebookDocument.uri notebook uri}, not the document uri. - * - * @example Match python document inside jupyter notebook that aren't stored yet (`untitled`) - * { language: 'python', notebookType: 'jupyter-notebook', scheme: 'untitled' } - */ - readonly notebookType?: string; - - /** - * A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. - */ - readonly scheme?: string; - - /** - * A {@link GlobPattern glob pattern} that is matched on the absolute path of the document. Use a {@link RelativePattern relative pattern} - * to filter documents to a {@link WorkspaceFolder workspace folder}. - */ - readonly pattern?: GlobPattern; - } - - /** - * A language selector is the combination of one or many language identifiers - * and {@link DocumentFilter language filters}. - * - * *Note* that a document selector that is just a language identifier selects *all* - * documents, even those that are not saved on disk. Only use such selectors when - * a feature works without further context, e.g. without the need to resolve related - * 'files'. - * - * @example - * let sel:DocumentSelector = { scheme: 'file', language: 'typescript' }; - */ - export type DocumentSelector = DocumentFilter | string | ReadonlyArray; - - /** - * A provider result represents the values a provider, like the {@linkcode HoverProvider}, - * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves - * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a - * thenable. - * - * The snippets below are all valid implementations of the {@linkcode HoverProvider}: - * - * ```ts - * let a: HoverProvider = { - * provideHover(doc, pos, token): ProviderResult { - * return new Hover('Hello World'); - * } - * } - * - * let b: HoverProvider = { - * provideHover(doc, pos, token): ProviderResult { - * return new Promise(resolve => { - * resolve(new Hover('Hello World')); - * }); - * } - * } - * - * let c: HoverProvider = { - * provideHover(doc, pos, token): ProviderResult { - * return; // undefined - * } - * } - * ``` - */ - export type ProviderResult = T | undefined | null | Thenable; - - /** - * Kind of a code action. - * - * Kinds are a hierarchical list of identifiers separated by `.`, e.g. `"refactor.extract.function"`. - * - * Code action kinds are used by the editor for UI elements such as the refactoring context menu. Users - * can also trigger code actions with a specific kind with the `editor.action.codeAction` command. - */ - export class CodeActionKind { - /** - * Empty kind. - */ - static readonly Empty: CodeActionKind; - - /** - * Base kind for quickfix actions: `quickfix`. - * - * Quick fix actions address a problem in the code and are shown in the normal code action context menu. - */ - static readonly QuickFix: CodeActionKind; - - /** - * Base kind for refactoring actions: `refactor` - * - * Refactoring actions are shown in the refactoring context menu. - */ - static readonly Refactor: CodeActionKind; - - /** - * Base kind for refactoring extraction actions: `refactor.extract` - * - * Example extract actions: - * - * - Extract method - * - Extract function - * - Extract variable - * - Extract interface from class - * - ... - */ - static readonly RefactorExtract: CodeActionKind; - - /** - * Base kind for refactoring inline actions: `refactor.inline` - * - * Example inline actions: - * - * - Inline function - * - Inline variable - * - Inline constant - * - ... - */ - static readonly RefactorInline: CodeActionKind; - - /** - * Base kind for refactoring move actions: `refactor.move` - * - * Example move actions: - * - * - Move a function to a new file - * - Move a property between classes - * - Move method to base class - * - ... - */ - static readonly RefactorMove: CodeActionKind; - - /** - * Base kind for refactoring rewrite actions: `refactor.rewrite` - * - * Example rewrite actions: - * - * - Convert JavaScript function to class - * - Add or remove parameter - * - Encapsulate field - * - Make method static - * - ... - */ - static readonly RefactorRewrite: CodeActionKind; - - /** - * Base kind for source actions: `source` - * - * Source code actions apply to the entire file. They must be explicitly requested and will not show in the - * normal [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions - * can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu. - */ - static readonly Source: CodeActionKind; - - /** - * Base kind for an organize imports source action: `source.organizeImports`. - */ - static readonly SourceOrganizeImports: CodeActionKind; - - /** - * Base kind for auto-fix source actions: `source.fixAll`. - * - * Fix all actions automatically fix errors that have a clear fix that do not require user input. - * They should not suppress errors or perform unsafe fixes such as generating new types or classes. - */ - static readonly SourceFixAll: CodeActionKind; - - private constructor(value: string); - - /** - * String value of the kind, e.g. `"refactor.extract.function"`. - */ - readonly value: string; - - /** - * Create a new kind by appending a more specific selector to the current kind. - * - * Does not modify the current kind. - */ - append(parts: string): CodeActionKind; - - /** - * Checks if this code action kind intersects `other`. - * - * The kind `"refactor.extract"` for example intersects `refactor`, `"refactor.extract"` and ``"refactor.extract.function"`, - * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"`. - * - * @param other Kind to check. - */ - intersects(other: CodeActionKind): boolean; - - /** - * Checks if `other` is a sub-kind of this `CodeActionKind`. - * - * The kind `"refactor.extract"` for example contains `"refactor.extract"` and ``"refactor.extract.function"`, - * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"` or `refactor`. - * - * @param other Kind to check. - */ - contains(other: CodeActionKind): boolean; - } - - /** - * The reason why code actions were requested. - */ - export enum CodeActionTriggerKind { - /** - * Code actions were explicitly requested by the user or by an extension. - */ - Invoke = 1, - - /** - * Code actions were requested automatically. - * - * This typically happens when current selection in a file changes, but can - * also be triggered when file content changes. - */ - Automatic = 2, - } - - /** - * Contains additional diagnostic information about the context in which - * a {@link CodeActionProvider.provideCodeActions code action} is run. - */ - export interface CodeActionContext { - /** - * The reason why code actions were requested. - */ - readonly triggerKind: CodeActionTriggerKind; - - /** - * An array of diagnostics. - */ - readonly diagnostics: readonly Diagnostic[]; - - /** - * Requested kind of actions to return. - * - * Actions not of this kind are filtered out before being shown by the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action). - */ - readonly only: CodeActionKind | undefined; - } - - /** - * A code action represents a change that can be performed in code, e.g. to fix a problem or - * to refactor code. - * - * A CodeAction must set either {@linkcode CodeAction.edit edit} and/or a {@linkcode CodeAction.command command}. If both are supplied, the `edit` is applied first, then the command is executed. - */ - export class CodeAction { - - /** - * A short, human-readable, title for this code action. - */ - title: string; - - /** - * A {@link WorkspaceEdit workspace edit} this code action performs. - */ - edit?: WorkspaceEdit; - - /** - * {@link Diagnostic Diagnostics} that this code action resolves. - */ - diagnostics?: Diagnostic[]; - - /** - * A {@link Command} this code action executes. - * - * If this command throws an exception, the editor displays the exception message to users in the editor at the - * current cursor position. - */ - command?: Command; - - /** - * {@link CodeActionKind Kind} of the code action. - * - * Used to filter code actions. - */ - kind?: CodeActionKind; - - /** - * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted - * by keybindings. - * - * A quick fix should be marked preferred if it properly addresses the underlying error. - * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. - */ - isPreferred?: boolean; - - /** - * Marks that the code action cannot currently be applied. - * - * - Disabled code actions are not shown in automatic [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) - * code action menu. - * - * - Disabled actions are shown as faded out in the code action menu when the user request a more specific type - * of code action, such as refactorings. - * - * - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions) - * that auto applies a code action and only a disabled code actions are returned, the editor will show the user an - * error message with `reason` in the editor. - */ - disabled?: { - /** - * Human readable description of why the code action is currently disabled. - * - * This is displayed in the code actions UI. - */ - readonly reason: string; - }; - - /** - * Creates a new code action. - * - * A code action must have at least a {@link CodeAction.title title} and {@link CodeAction.edit edits} - * and/or a {@link CodeAction.command command}. - * - * @param title The title of the code action. - * @param kind The kind of the code action. - */ - constructor(title: string, kind?: CodeActionKind); - } - - /** - * The code action interface defines the contract between extensions and - * the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. - * - * A code action can be any command that is {@link commands.getCommands known} to the system. - */ - export interface CodeActionProvider { - /** - * Provide commands for the given document and range. - * - * @param document The document in which the command was invoked. - * @param range The selector or range for which the command was invoked. This will always be a selection if - * there is a currently active editor. - * @param context Context carrying additional information. - * @param token A cancellation token. - * - * @return An array of code actions, such as quick fixes or refactorings. The lack of a result can be signaled - * by returning `undefined`, `null`, or an empty array. - * - * We also support returning `Command` for legacy reasons, however all new extensions should return - * `CodeAction` object instead. - */ - provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | T)[]>; - - /** - * Given a code action fill in its {@linkcode CodeAction.edit edit}-property. Changes to - * all other properties, like title, are ignored. A code action that has an edit - * will not be resolved. - * - * *Note* that a code action provider that returns commands, not code actions, cannot successfully - * implement this function. Returning commands is deprecated and instead code actions should be - * returned. - * - * @param codeAction A code action. - * @param token A cancellation token. - * @return The resolved code action or a thenable that resolves to such. It is OK to return the given - * `item`. When no result is returned, the given `item` will be used. - */ - resolveCodeAction?(codeAction: T, token: CancellationToken): ProviderResult; - } - - /** - * Metadata about the type of code actions that a {@link CodeActionProvider} provides. - */ - export interface CodeActionProviderMetadata { - /** - * List of {@link CodeActionKind CodeActionKinds} that a {@link CodeActionProvider} may return. - * - * This list is used to determine if a given `CodeActionProvider` should be invoked or not. - * To avoid unnecessary computation, every `CodeActionProvider` should list use `providedCodeActionKinds`. The - * list of kinds may either be generic, such as `[CodeActionKind.Refactor]`, or list out every kind provided, - * such as `[CodeActionKind.Refactor.Extract.append('function'), CodeActionKind.Refactor.Extract.append('constant'), ...]`. - */ - readonly providedCodeActionKinds?: readonly CodeActionKind[]; - - /** - * Static documentation for a class of code actions. - * - * Documentation from the provider is shown in the code actions menu if either: - * - * - Code actions of `kind` are requested by the editor. In this case, the editor 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`, - * the editor will use the documentation for `RefactorExtract` instead of the documentation for `Refactor`. - * - * - Any code actions of `kind` are returned by the provider. - * - * At most one documentation entry will be shown per provider. - */ - readonly documentation?: ReadonlyArray<{ - /** - * The kind of the code action being documented. - * - * If the kind is generic, such as `CodeActionKind.Refactor`, the documentation will be shown whenever any - * refactorings are returned. If the kind if more specific, such as `CodeActionKind.RefactorExtract`, the - * documentation will only be shown when extract refactoring code actions are returned. - */ - readonly kind: CodeActionKind; - - /** - * Command that displays the documentation to the user. - * - * This can display the documentation directly in the editor or open a website using {@linkcode env.openExternal}; - * - * The title of this documentation code action is taken from {@linkcode Command.title} - */ - readonly command: Command; - }>; - } - - /** - * A code lens represents a {@link Command} that should be shown along with - * source text, like the number of references, a way to run tests, etc. - * - * A code lens is _unresolved_ when no command is associated to it. For performance - * reasons the creation of a code lens and resolving should be done to two stages. - * - * @see {@link CodeLensProvider.provideCodeLenses} - * @see {@link CodeLensProvider.resolveCodeLens} - */ - export class CodeLens { - - /** - * The range in which this code lens is valid. Should only span a single line. - */ - range: Range; - - /** - * The command this code lens represents. - */ - command?: Command; - - /** - * `true` when there is a command associated. - */ - readonly isResolved: boolean; - - /** - * Creates a new code lens object. - * - * @param range The range to which this code lens applies. - * @param command The command associated to this code lens. - */ - constructor(range: Range, command?: Command); - } - - /** - * A code lens provider adds {@link Command commands} to source text. The commands will be shown - * as dedicated horizontal lines in between the source text. - */ - export interface CodeLensProvider { - - /** - * An optional event to signal that the code lenses from this provider have changed. - */ - onDidChangeCodeLenses?: Event; - - /** - * Compute a list of {@link CodeLens lenses}. This call should return as fast as possible and if - * computing the commands is expensive implementors should only return code lens objects with the - * range set and implement {@link CodeLensProvider.resolveCodeLens resolve}. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of code lenses or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult; - - /** - * This function will be called for each visible code lens, usually when scrolling and after - * calls to {@link CodeLensProvider.provideCodeLenses compute}-lenses. - * - * @param codeLens Code lens that must be resolved. - * @param token A cancellation token. - * @return The given, resolved code lens or thenable that resolves to such. - */ - resolveCodeLens?(codeLens: T, token: CancellationToken): ProviderResult; - } - - /** - * Information about where a symbol is defined. - * - * Provides additional metadata over normal {@link Location} definitions, including the range of - * the defining symbol - */ - export type DefinitionLink = LocationLink; - - /** - * The definition of a symbol represented as one or many {@link Location locations}. - * For most programming languages there is only one location at which a symbol is - * defined. - */ - export type Definition = Location | Location[]; - - /** - * The definition provider interface defines the contract between extensions and - * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) - * and peek definition features. - */ - export interface DefinitionProvider { - - /** - * Provide the definition of the symbol at the given position and document. - * - * @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 definition or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * The implementation provider interface defines the contract between extensions and - * the go to implementation feature. - */ - export interface ImplementationProvider { - - /** - * Provide the implementations of the symbol at the given position and document. - * - * @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 definition or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * The type definition provider defines the contract between extensions and - * the go to type definition feature. - */ - export interface TypeDefinitionProvider { - - /** - * Provide the type definition of the symbol at the given position and document. - * - * @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 definition or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * The declaration of a symbol representation as one or many {@link Location locations} - * or {@link LocationLink location links}. - */ - export type Declaration = Location | Location[] | LocationLink[]; - - /** - * The declaration provider interface defines the contract between extensions and - * the go to declaration feature. - */ - export interface DeclarationProvider { - - /** - * Provide the declaration of the symbol at the given position and document. - * - * @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 declaration or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideDeclaration(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * Human-readable text that supports formatting via the [markdown syntax](https://commonmark.org). - * - * Rendering of {@link ThemeIcon theme icons} via the `$()`-syntax is supported - * when the {@linkcode supportThemeIcons} is set to `true`. - * - * Rendering of embedded html is supported when {@linkcode supportHtml} is set to `true`. - */ - export class MarkdownString { - - /** - * The markdown string. - */ - value: string; - - /** - * Indicates that this markdown string is from a trusted source. Only *trusted* - * markdown supports links that execute commands, e.g. `[Run it](command:myCommandId)`. - * - * Defaults to `false` (commands are disabled). - * - * If this is an object, only the set of commands listed in `enabledCommands` are allowed. - */ - isTrusted?: boolean | { readonly enabledCommands: readonly string[] }; - - /** - * Indicates that this markdown string can contain {@link ThemeIcon ThemeIcons}, e.g. `$(zap)`. - */ - supportThemeIcons?: boolean; - - /** - * Indicates that this markdown string can contain raw html tags. Defaults to `false`. - * - * When `supportHtml` is false, the markdown renderer will strip out any raw html tags - * that appear in the markdown text. This means you can only use markdown syntax for rendering. - * - * When `supportHtml` is true, the markdown render will also allow a safe subset of html tags - * and attributes to be rendered. See https://github.com/microsoft/vscode/blob/6d2920473c6f13759c978dd89104c4270a83422d/src/vs/base/browser/markdownRenderer.ts#L296 - * for a list of all supported tags and attributes. - */ - supportHtml?: boolean; - - /** - * Uri that relative paths are resolved relative to. - * - * If the `baseUri` ends with `/`, it is considered a directory and relative paths in the markdown are resolved relative to that directory: - * - * ```ts - * const md = new vscode.MarkdownString(`[link](./file.js)`); - * md.baseUri = vscode.Uri.file('/path/to/dir/'); - * // Here 'link' in the rendered markdown resolves to '/path/to/dir/file.js' - * ``` - * - * If the `baseUri` is a file, relative paths in the markdown are resolved relative to the parent dir of that file: - * - * ```ts - * const md = new vscode.MarkdownString(`[link](./file.js)`); - * md.baseUri = vscode.Uri.file('/path/to/otherFile.js'); - * // Here 'link' in the rendered markdown resolves to '/path/to/file.js' - * ``` - */ - baseUri?: Uri; - - /** - * Creates a new markdown string with the given value. - * - * @param value Optional, initial value. - * @param supportThemeIcons Optional, Specifies whether {@link ThemeIcon ThemeIcons} are supported within the {@linkcode MarkdownString}. - */ - constructor(value?: string, supportThemeIcons?: boolean); - - /** - * Appends and escapes the given string to this markdown string. - * @param value Plain text. - */ - appendText(value: string): MarkdownString; - - /** - * Appends the given string 'as is' to this markdown string. When {@linkcode MarkdownString.supportThemeIcons supportThemeIcons} is `true`, {@link ThemeIcon ThemeIcons} in the `value` will be iconified. - * @param value Markdown string. - */ - appendMarkdown(value: string): MarkdownString; - - /** - * Appends the given string as codeblock using the provided language. - * @param value A code snippet. - * @param language An optional {@link languages.getLanguages language identifier}. - */ - appendCodeblock(value: string, language?: string): MarkdownString; - } - - /** - * MarkedString can be used to render human-readable text. It is either a markdown string - * or a code-block that provides a language and a code snippet. Note that - * markdown strings will be sanitized - that means html will be escaped. - * - * @deprecated This type is deprecated, please use {@linkcode MarkdownString} instead. - */ - export type MarkedString = string | { language: string; value: string }; - - /** - * A hover represents additional information for a symbol or word. Hovers are - * rendered in a tooltip-like widget. - */ - export class Hover { - - /** - * The contents of this hover. - */ - contents: Array; - - /** - * The range to which this hover applies. When missing, the - * editor will use the range at the current position or the - * current position itself. - */ - range?: Range; - - /** - * Creates a new hover object. - * - * @param contents The contents of the hover. - * @param range The range to which the hover applies. - */ - constructor(contents: MarkdownString | MarkedString | Array, range?: Range); - } - - /** - * The hover provider interface defines the contract between extensions and - * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ - export interface HoverProvider { - - /** - * Provide a hover for the given position and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - * - * @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 hover or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * An EvaluatableExpression represents an expression in a document that can be evaluated by an active debugger or runtime. - * The result of this evaluation is shown in a tooltip-like widget. - * If only a range is specified, the expression will be extracted from the underlying document. - * An optional expression can be used to override the extracted expression. - * In this case the range is still used to highlight the range in the document. - */ - export class EvaluatableExpression { - - /* - * The range is used to extract the evaluatable expression from the underlying document and to highlight it. - */ - readonly range: Range; - - /* - * If specified the expression overrides the extracted expression. - */ - readonly expression?: string | undefined; - - /** - * Creates a new evaluatable expression object. - * - * @param range The range in the underlying document from which the evaluatable expression is extracted. - * @param expression If specified overrides the extracted expression. - */ - constructor(range: Range, expression?: string); - } - - /** - * The evaluatable expression provider interface defines the contract between extensions and - * the debug hover. In this contract the provider returns an evaluatable expression for a given position - * in a document and the editor evaluates this expression in the active debug session and shows the result in a debug hover. - */ - export interface EvaluatableExpressionProvider { - - /** - * Provide an evaluatable expression for the given document and position. - * The editor will evaluate this expression in the active debug session and will show the result in the debug hover. - * The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression. - * - * @param document The document for which the debug hover is about to appear. - * @param position The line and character position in the document where the debug hover is about to appear. - * @param token A cancellation token. - * @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * Provide inline value as text. - */ - export class InlineValueText { - /** - * The document range for which the inline value applies. - */ - readonly range: Range; - /** - * The text of the inline value. - */ - readonly text: string; - /** - * Creates a new InlineValueText object. - * - * @param range The document line where to show the inline value. - * @param text The value to be shown for the line. - */ - constructor(range: Range, text: string); - } - - /** - * Provide inline value through a variable lookup. - * If only a range is specified, the variable name will be extracted from the underlying document. - * An optional variable name can be used to override the extracted name. - */ - export class InlineValueVariableLookup { - /** - * The document range for which the inline value applies. - * The range is used to extract the variable name from the underlying document. - */ - readonly range: Range; - /** - * If specified the name of the variable to look up. - */ - readonly variableName?: string | undefined; - /** - * How to perform the lookup. - */ - readonly caseSensitiveLookup: boolean; - /** - * Creates a new InlineValueVariableLookup object. - * - * @param range The document line where to show the inline value. - * @param variableName The name of the variable to look up. - * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. - */ - constructor(range: Range, variableName?: string, caseSensitiveLookup?: boolean); - } - - /** - * Provide an inline value through an expression evaluation. - * If only a range is specified, the expression will be extracted from the underlying document. - * An optional expression can be used to override the extracted expression. - */ - export class InlineValueEvaluatableExpression { - /** - * The document range for which the inline value applies. - * The range is used to extract the evaluatable expression from the underlying document. - */ - readonly range: Range; - /** - * If specified the expression overrides the extracted expression. - */ - readonly expression?: string | undefined; - /** - * Creates a new InlineValueEvaluatableExpression object. - * - * @param range The range in the underlying document from which the evaluatable expression is extracted. - * @param expression If specified overrides the extracted expression. - */ - constructor(range: Range, expression?: string); - } - - /** - * Inline value information can be provided by different means: - * - directly as a text value (class InlineValueText). - * - as a name to use for a variable lookup (class InlineValueVariableLookup) - * - as an evaluatable expression (class InlineValueEvaluatableExpression) - * The InlineValue types combines all inline value types into one type. - */ - export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; - - /** - * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. - */ - export interface InlineValueContext { - - /** - * The stack frame (as a DAP Id) where the execution has stopped. - */ - readonly frameId: number; - - /** - * The document range where execution has stopped. - * Typically the end position of the range denotes the line where the inline values are shown. - */ - readonly stoppedLocation: Range; - } - - /** - * The inline values provider interface defines the contract between extensions and the editor's debugger inline values feature. - * In this contract the provider returns inline value information for a given document range - * and the editor shows this information in the editor at the end of lines. - */ - export interface InlineValuesProvider { - - /** - * An optional event to signal that inline values have changed. - * @see {@link EventEmitter} - */ - onDidChangeInlineValues?: Event | undefined; - - /** - * Provide "inline value" information for a given document and range. - * The editor calls this method whenever debugging stops in the given document. - * The returned inline values information is rendered in the editor at the end of lines. - * - * @param document The document for which the inline values information is needed. - * @param viewPort The visible document range for which inline values should be computed. - * @param context A bag containing contextual information like the current location. - * @param token A cancellation token. - * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; - } - - /** - * A document highlight kind. - */ - export enum DocumentHighlightKind { - - /** - * A textual occurrence. - */ - Text = 0, - - /** - * Read-access of a symbol, like reading a variable. - */ - Read = 1, - - /** - * Write-access of a symbol, like writing to a variable. - */ - Write = 2 - } - - /** - * A document highlight is a range inside a text document which deserves - * special attention. Usually a document highlight is visualized by changing - * the background color of its range. - */ - export class DocumentHighlight { - - /** - * The range this highlight applies to. - */ - range: Range; - - /** - * The highlight kind, default is {@link DocumentHighlightKind.Text text}. - */ - kind?: DocumentHighlightKind; - - /** - * Creates a new document highlight object. - * - * @param range The range the highlight applies to. - * @param kind The highlight kind, default is {@link DocumentHighlightKind.Text text}. - */ - constructor(range: Range, kind?: DocumentHighlightKind); - } - - /** - * The document highlight provider interface defines the contract between extensions and - * the word-highlight-feature. - */ - export interface DocumentHighlightProvider { - - /** - * Provide a set of document highlights, like all occurrences of a variable or - * all exit-points of a function. - * - * @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 An array of document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentHighlights(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * A symbol kind. - */ - export enum SymbolKind { - File = 0, - Module = 1, - Namespace = 2, - Package = 3, - Class = 4, - Method = 5, - Property = 6, - Field = 7, - Constructor = 8, - Enum = 9, - Interface = 10, - Function = 11, - Variable = 12, - Constant = 13, - String = 14, - Number = 15, - Boolean = 16, - Array = 17, - Object = 18, - Key = 19, - Null = 20, - EnumMember = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25 - } - - /** - * Symbol tags are extra annotations that tweak the rendering of a symbol. - */ - export enum SymbolTag { - - /** - * Render a symbol as obsolete, usually using a strike-out. - */ - Deprecated = 1 - } - - /** - * Represents information about programming constructs like variables, classes, - * interfaces etc. - */ - export class SymbolInformation { - - /** - * The name of this symbol. - */ - name: string; - - /** - * The name of the symbol containing this symbol. - */ - containerName: string; - - /** - * The kind of this symbol. - */ - kind: SymbolKind; - - /** - * Tags for this symbol. - */ - tags?: readonly SymbolTag[]; - - /** - * The location of this symbol. - */ - location: Location; - - /** - * Creates a new symbol information object. - * - * @param name The name of the symbol. - * @param kind The kind of the symbol. - * @param containerName The name of the symbol containing the symbol. - * @param location The location of the symbol. - */ - constructor(name: string, kind: SymbolKind, containerName: string, location: Location); - - /** - * Creates a new symbol information object. - * - * @deprecated Please use the constructor taking a {@link Location} object. - * - * @param name The name of the symbol. - * @param kind The kind of the symbol. - * @param range The range of the location of the symbol. - * @param uri The resource of the location of symbol, defaults to the current document. - * @param containerName The name of the symbol containing the symbol. - */ - constructor(name: string, kind: SymbolKind, range: Range, uri?: Uri, containerName?: string); - } - - /** - * Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document - * symbols can be hierarchical and they have two ranges: one that encloses its definition and one that points to - * its most interesting range, e.g. the range of an identifier. - */ - export class DocumentSymbol { - - /** - * The name of this symbol. - */ - name: string; - - /** - * More detail for this symbol, e.g. the signature of a function. - */ - detail: string; - - /** - * The kind of this symbol. - */ - kind: SymbolKind; - - /** - * Tags for this symbol. - */ - tags?: readonly SymbolTag[]; - - /** - * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. - */ - range: Range; - - /** - * The range that should be selected and reveal when this symbol is being picked, e.g. the name of a function. - * Must be contained by the {@linkcode DocumentSymbol.range range}. - */ - selectionRange: Range; - - /** - * Children of this symbol, e.g. properties of a class. - */ - children: DocumentSymbol[]; - - /** - * Creates a new document symbol. - * - * @param name The name of the symbol. - * @param detail Details for the symbol. - * @param kind The kind of the symbol. - * @param range The full range of the symbol. - * @param selectionRange The range that should be reveal. - */ - constructor(name: string, detail: string, kind: SymbolKind, range: Range, selectionRange: Range); - } - - /** - * The document symbol provider interface defines the contract between extensions and - * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature. - */ - export interface DocumentSymbolProvider { - - /** - * Provide symbol information for the given document. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult; - } - - /** - * Metadata about a document symbol provider. - */ - export interface DocumentSymbolProviderMetadata { - /** - * A human-readable string that is shown when multiple outlines trees show for one document. - */ - label?: string; - } - - /** - * The workspace symbol provider interface defines the contract between extensions and - * the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature. - */ - export interface WorkspaceSymbolProvider { - - /** - * Project-wide search for a symbol matching the given query string. - * - * The `query`-parameter should be interpreted in a *relaxed way* as the editor will apply its own highlighting - * and scoring on the results. A good rule of thumb is to match case-insensitive and to simply check that the - * characters of *query* appear in their order in a candidate symbol. Don't use prefix, substring, or similar - * strict matching. - * - * To improve performance implementors can implement `resolveWorkspaceSymbol` and then provide symbols with partial - * {@link SymbolInformation.location location}-objects, without a `range` defined. The editor will then call - * `resolveWorkspaceSymbol` for selected symbols only, e.g. when opening a workspace symbol. - * - * @param query A query string, can be the empty string in which case all symbols should be returned. - * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult; - - /** - * Given a symbol fill in its {@link SymbolInformation.location location}. This method is called whenever a symbol - * is selected in the UI. Providers can implement this method and return incomplete symbols from - * {@linkcode WorkspaceSymbolProvider.provideWorkspaceSymbols provideWorkspaceSymbols} which often helps to improve - * performance. - * - * @param symbol The symbol that is to be resolved. Guaranteed to be an instance of an object returned from an - * earlier call to `provideWorkspaceSymbols`. - * @param token A cancellation token. - * @return The resolved symbol or a thenable that resolves to that. When no result is returned, - * the given `symbol` is used. - */ - resolveWorkspaceSymbol?(symbol: T, token: CancellationToken): ProviderResult; - } - - /** - * Value-object that contains additional information when - * requesting references. - */ - export interface ReferenceContext { - - /** - * Include the declaration of the current symbol. - */ - readonly includeDeclaration: boolean; - } - - /** - * The reference provider interface defines the contract between extensions and - * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. - */ - export interface ReferenceProvider { - - /** - * Provide a set of project-wide references for the given position and document. - * - * @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 An array of locations or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; - } - - /** - * A text edit represents edits that should be applied - * to a document. - */ - export class TextEdit { - - /** - * Utility to create a replace edit. - * - * @param range A range. - * @param newText A string. - * @return A new text edit object. - */ - static replace(range: Range, newText: string): TextEdit; - - /** - * Utility to create an insert edit. - * - * @param position A position, will become an empty range. - * @param newText A string. - * @return A new text edit object. - */ - static insert(position: Position, newText: string): TextEdit; - - /** - * Utility to create a delete edit. - * - * @param range A range. - * @return A new text edit object. - */ - static delete(range: Range): TextEdit; - - /** - * Utility to create an eol-edit. - * - * @param eol An eol-sequence - * @return A new text edit object. - */ - static setEndOfLine(eol: EndOfLine): TextEdit; - - /** - * The range this edit applies to. - */ - range: Range; - - /** - * The string this edit will insert. - */ - newText: string; - - /** - * The eol-sequence used in the document. - * - * *Note* that the eol-sequence will be applied to the - * whole document. - */ - newEol?: EndOfLine; - - /** - * Create a new TextEdit. - * - * @param range A range. - * @param newText A string. - */ - constructor(range: Range, newText: string); - } - - /** - * A snippet edit represents an interactive edit that is performed by - * the editor. - * - * *Note* that a snippet edit can always be performed as a normal {@link TextEdit text edit}. - * This will happen when no matching editor is open or when a {@link WorkspaceEdit workspace edit} - * contains snippet edits for multiple files. In that case only those that match the active editor - * will be performed as snippet edits and the others as normal text edits. - */ - export class SnippetTextEdit { - - /** - * Utility to create a replace snippet edit. - * - * @param range A range. - * @param snippet A snippet string. - * @return A new snippet edit object. - */ - static replace(range: Range, snippet: SnippetString): SnippetTextEdit; - - /** - * Utility to create an insert snippet edit. - * - * @param position A position, will become an empty range. - * @param snippet A snippet string. - * @return A new snippet edit object. - */ - static insert(position: Position, snippet: SnippetString): SnippetTextEdit; - - /** - * The range this edit applies to. - */ - range: Range; - - /** - * The {@link SnippetString snippet} this edit will perform. - */ - snippet: SnippetString; - - /** - * Create a new snippet edit. - * - * @param range A range. - * @param snippet A snippet string. - */ - constructor(range: Range, snippet: SnippetString); - } - - /** - * A notebook edit represents edits that should be applied to the contents of a notebook. - */ - export class NotebookEdit { - - /** - * Utility to create a edit that replaces cells in a notebook. - * - * @param range The range of cells to replace - * @param newCells The new notebook cells. - */ - static replaceCells(range: NotebookRange, newCells: NotebookCellData[]): NotebookEdit; - - /** - * Utility to create an edit that replaces cells in a notebook. - * - * @param index The index to insert cells at. - * @param newCells The new notebook cells. - */ - static insertCells(index: number, newCells: NotebookCellData[]): NotebookEdit; - - /** - * Utility to create an edit that deletes cells in a notebook. - * - * @param range The range of cells to delete. - */ - static deleteCells(range: NotebookRange): NotebookEdit; - - /** - * Utility to create an edit that update a cell's metadata. - * - * @param index The index of the cell to update. - * @param newCellMetadata The new metadata for the cell. - */ - static updateCellMetadata(index: number, newCellMetadata: { [key: string]: any }): NotebookEdit; - - /** - * Utility to create an edit that updates the notebook's metadata. - * - * @param newNotebookMetadata The new metadata for the notebook. - */ - static updateNotebookMetadata(newNotebookMetadata: { [key: string]: any }): NotebookEdit; - - /** - * Range of the cells being edited. May be empty. - */ - range: NotebookRange; - - /** - * New cells being inserted. May be empty. - */ - newCells: NotebookCellData[]; - - /** - * Optional new metadata for the cells. - */ - newCellMetadata?: { [key: string]: any }; - - /** - * Optional new metadata for the notebook. - */ - newNotebookMetadata?: { [key: string]: any }; - - constructor(range: NotebookRange, newCells: NotebookCellData[]); - } - - /** - * Additional data for entries of a workspace edit. Supports to label entries and marks entries - * as needing confirmation by the user. The editor groups edits with equal labels into tree nodes, - * for instance all edits labelled with "Changes in Strings" would be a tree node. - */ - export interface WorkspaceEditEntryMetadata { - - /** - * A flag which indicates that user confirmation is needed. - */ - needsConfirmation: boolean; - - /** - * A human-readable string which is rendered prominent. - */ - label: string; - - /** - * A human-readable string which is rendered less prominent on the same line. - */ - description?: string; - - /** - * The icon path or {@link ThemeIcon} for the edit. - */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; - } - - /** - * Additional data about a workspace edit. - */ - export interface WorkspaceEditMetadata { - /** - * Signal to the editor that this edit is a refactoring. - */ - isRefactoring?: boolean; - } - - /** - * A workspace edit is a collection of textual and files changes for - * multiple resources and documents. - * - * Use the {@link workspace.applyEdit applyEdit}-function to apply a workspace edit. - */ - export class WorkspaceEdit { - - /** - * The number of affected resources of textual or resource changes. - */ - readonly size: number; - - /** - * Replace the given range with given text for the given resource. - * - * @param uri A resource identifier. - * @param range A range. - * @param newText A string. - * @param metadata Optional metadata for the entry. - */ - replace(uri: Uri, range: Range, newText: string, metadata?: WorkspaceEditEntryMetadata): void; - - /** - * Insert the given text at the given position. - * - * @param uri A resource identifier. - * @param position A position. - * @param newText A string. - * @param metadata Optional metadata for the entry. - */ - insert(uri: Uri, position: Position, newText: string, metadata?: WorkspaceEditEntryMetadata): void; - - /** - * Delete the text at the given range. - * - * @param uri A resource identifier. - * @param range A range. - * @param metadata Optional metadata for the entry. - */ - delete(uri: Uri, range: Range, metadata?: WorkspaceEditEntryMetadata): void; - - /** - * Check if a text edit for a resource exists. - * - * @param uri A resource identifier. - * @return `true` if the given resource will be touched by this edit. - */ - has(uri: Uri): boolean; - - /** - * Set (and replace) notebook edits for a resource. - * - * @param uri A resource identifier. - * @param edits An array of edits. - */ - set(uri: Uri, edits: readonly NotebookEdit[]): void; - - /** - * Set (and replace) notebook edits with metadata for a resource. - * - * @param uri A resource identifier. - * @param edits An array of edits. - */ - set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata]>): void; - - /** - * Set (and replace) text edits or snippet edits for a resource. - * - * @param uri A resource identifier. - * @param edits An array of edits. - */ - set(uri: Uri, edits: ReadonlyArray): void; - - /** - * Set (and replace) text edits or snippet edits with metadata for a resource. - * - * @param uri A resource identifier. - * @param edits An array of edits. - */ - set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata]>): void; - - /** - * Get the text edits for a resource. - * - * @param uri A resource identifier. - * @return An array of text edits. - */ - get(uri: Uri): TextEdit[]; - - /** - * Create a regular file. - * - * @param uri Uri of the new file. - * @param options Defines if an existing file should be overwritten or be - * ignored. When `overwrite` and `ignoreIfExists` are both set `overwrite` wins. - * When both are unset and when the file already exists then the edit cannot - * be applied successfully. The `content`-property allows to set the initial contents - * the file is being created with. - * @param metadata Optional metadata for the entry. - */ - createFile(uri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean; readonly contents?: Uint8Array }, metadata?: WorkspaceEditEntryMetadata): void; - - /** - * Delete a file or folder. - * - * @param uri The uri of the file that is to be deleted. - * @param metadata Optional metadata for the entry. - */ - deleteFile(uri: Uri, options?: { readonly recursive?: boolean; readonly ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; - - /** - * Rename a file or folder. - * - * @param oldUri The existing file. - * @param newUri The new location. - * @param options Defines if existing files should be overwritten or be - * ignored. When overwrite and ignoreIfExists are both set overwrite wins. - * @param metadata Optional metadata for the entry. - */ - renameFile(oldUri: Uri, newUri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; - - /** - * Get all text edits grouped by resource. - * - * @return A shallow copy of `[Uri, TextEdit[]]`-tuples. - */ - entries(): [Uri, TextEdit[]][]; - } - - /** - * A snippet string is a template which allows to insert text - * and to control the editor cursor when insertion happens. - * - * A snippet can define tab stops and placeholders with `$1`, `$2` - * and `${3:foo}`. `$0` defines the final tab stop, it defaults to - * the end of the snippet. Variables are defined with `$name` and - * `${name:default value}`. Also see - * [the full snippet syntax](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_creating-your-own-snippets). - */ - export class SnippetString { - - /** - * The snippet string. - */ - value: string; - - constructor(value?: string); - - /** - * Builder-function that appends the given string to - * the {@linkcode SnippetString.value value} of this snippet string. - * - * @param string A value to append 'as given'. The string will be escaped. - * @return This snippet string. - */ - appendText(string: string): SnippetString; - - /** - * Builder-function that appends a tabstop (`$1`, `$2` etc) to - * the {@linkcode SnippetString.value value} of this snippet string. - * - * @param number The number of this tabstop, defaults to an auto-increment - * value starting at 1. - * @return This snippet string. - */ - appendTabstop(number?: number): SnippetString; - - /** - * Builder-function that appends a placeholder (`${1:value}`) to - * the {@linkcode SnippetString.value value} of this snippet string. - * - * @param value The value of this placeholder - either a string or a function - * with which a nested snippet can be created. - * @param number The number of this tabstop, defaults to an auto-increment - * value starting at 1. - * @return This snippet string. - */ - appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString; - - /** - * Builder-function that appends a choice (`${1|a,b,c|}`) to - * the {@linkcode SnippetString.value value} of this snippet string. - * - * @param values The values for choices - the array of strings - * @param number The number of this tabstop, defaults to an auto-increment - * value starting at 1. - * @return This snippet string. - */ - appendChoice(values: readonly string[], number?: number): SnippetString; - - /** - * Builder-function that appends a variable (`${VAR}`) to - * the {@linkcode SnippetString.value value} of this snippet string. - * - * @param name The name of the variable - excluding the `$`. - * @param defaultValue The default value which is used when the variable name cannot - * be resolved - either a string or a function with which a nested snippet can be created. - * @return This snippet string. - */ - appendVariable(name: string, defaultValue: string | ((snippet: SnippetString) => any)): SnippetString; - } - - /** - * The rename provider interface defines the contract between extensions and - * the [rename](https://code.visualstudio.com/docs/editor/editingevolved#_rename-symbol)-feature. - */ - export interface RenameProvider { - - /** - * Provide an edit that describes changes that have to be made to one - * or many resources to rename a symbol to a different name. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param newName The new name of the symbol. If the given name is not valid, the provider must return a rejected promise. - * @param token A cancellation token. - * @return A workspace edit or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult; - - /** - * Optional function for resolving and validating a position *before* running rename. The result can - * be a range or a range and a placeholder text. The placeholder text should be the identifier of the symbol - * which is being renamed - when omitted the text in the returned range is used. - * - * *Note:* This function should throw an error or return a rejected thenable when the provided location - * doesn't allow for a rename. - * - * @param document The document in which rename will be invoked. - * @param position The position at which rename will be invoked. - * @param token A cancellation token. - * @return The range or range and placeholder text of the identifier that is to be renamed. The lack of a result can signaled by returning `undefined` or `null`. - */ - prepareRename?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * A semantic tokens legend contains the needed information to decipher - * the integer encoded representation of semantic tokens. - */ - export class SemanticTokensLegend { - /** - * The possible token types. - */ - readonly tokenTypes: string[]; - /** - * The possible token modifiers. - */ - readonly tokenModifiers: string[]; - - constructor(tokenTypes: string[], tokenModifiers?: string[]); - } - - /** - * A semantic tokens builder can help with creating a `SemanticTokens` instance - * which contains delta encoded semantic tokens. - */ - export class SemanticTokensBuilder { - - constructor(legend?: SemanticTokensLegend); - - /** - * Add another token. - * - * @param line The token start line number (absolute value). - * @param char The token start character (absolute value). - * @param length The token length in characters. - * @param tokenType The encoded token type. - * @param tokenModifiers The encoded token modifiers. - */ - push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void; - - /** - * Add another token. Use only when providing a legend. - * - * @param range The range of the token. Must be single-line. - * @param tokenType The token type. - * @param tokenModifiers The token modifiers. - */ - push(range: Range, tokenType: string, tokenModifiers?: readonly string[]): void; - - /** - * Finish and create a `SemanticTokens` instance. - */ - build(resultId?: string): SemanticTokens; - } - - /** - * Represents semantic tokens, either in a range or in an entire document. - * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format. - * @see {@link SemanticTokensBuilder} for a helper to create an instance. - */ - export class SemanticTokens { - /** - * The result id of the tokens. - * - * This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented). - */ - readonly resultId: string | undefined; - /** - * The actual tokens data. - * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format. - */ - readonly data: Uint32Array; - - constructor(data: Uint32Array, resultId?: string); - } - - /** - * Represents edits to semantic tokens. - * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits provideDocumentSemanticTokensEdits} for an explanation of the format. - */ - export class SemanticTokensEdits { - /** - * The result id of the tokens. - * - * This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented). - */ - readonly resultId: string | undefined; - /** - * The edits to the tokens data. - * All edits refer to the initial data state. - */ - readonly edits: SemanticTokensEdit[]; - - constructor(edits: SemanticTokensEdit[], resultId?: string); - } - - /** - * Represents an edit to semantic tokens. - * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits provideDocumentSemanticTokensEdits} for an explanation of the format. - */ - export class SemanticTokensEdit { - /** - * The start offset of the edit. - */ - readonly start: number; - /** - * The count of elements to remove. - */ - readonly deleteCount: number; - /** - * The elements to insert. - */ - readonly data: Uint32Array | undefined; - - constructor(start: number, deleteCount: number, data?: Uint32Array); - } - - /** - * The document semantic tokens provider interface defines the contract between extensions and - * semantic tokens. - */ - export interface DocumentSemanticTokensProvider { - /** - * An optional event to signal that the semantic tokens from this provider have changed. - */ - onDidChangeSemanticTokens?: Event; - - /** - * Tokens in a file are represented as an array of integers. The position of each token is expressed relative to - * the token before it, because most tokens remain stable relative to each other when edits are made in a file. - * - * --- - * In short, each token takes 5 integers to represent, so a specific token `i` in the file consists of the following array indices: - * - at index `5*i` - `deltaLine`: token line number, relative to the previous token - * - at index `5*i+1` - `deltaStart`: token start character, relative to the previous token (relative to 0 or the previous token's start if they are on the same line) - * - at index `5*i+2` - `length`: the length of the token. A token cannot be multiline. - * - at index `5*i+3` - `tokenType`: will be looked up in `SemanticTokensLegend.tokenTypes`. We currently ask that `tokenType` < 65536. - * - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticTokensLegend.tokenModifiers` - * - * --- - * ### How to encode tokens - * - * Here is an example for encoding a file with 3 tokens in a uint32 array: - * ``` - * { line: 2, startChar: 5, length: 3, tokenType: "property", tokenModifiers: ["private", "static"] }, - * { line: 2, startChar: 10, length: 4, tokenType: "type", tokenModifiers: [] }, - * { line: 5, startChar: 2, length: 7, tokenType: "class", tokenModifiers: [] } - * ``` - * - * 1. First of all, a legend must be devised. This legend must be provided up-front and capture all possible token types. - * For this example, we will choose the following legend which must be passed in when registering the provider: - * ``` - * tokenTypes: ['property', 'type', 'class'], - * tokenModifiers: ['private', 'static'] - * ``` - * - * 2. The first transformation step is to encode `tokenType` and `tokenModifiers` as integers using the legend. Token types are looked - * up by index, so a `tokenType` value of `1` means `tokenTypes[1]`. Multiple token modifiers can be set by using bit flags, - * so a `tokenModifier` value of `3` is first viewed as binary `0b00000011`, which means `[tokenModifiers[0], tokenModifiers[1]]` because - * bits 0 and 1 are set. Using this legend, the tokens now are: - * ``` - * { line: 2, startChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, - * { line: 2, startChar: 10, length: 4, tokenType: 1, tokenModifiers: 0 }, - * { line: 5, startChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } - * ``` - * - * 3. The next step is to represent each token relative to the previous token in the file. In this case, the second token - * is on the same line as the first token, so the `startChar` of the second token is made relative to the `startChar` - * of the first token, so it will be `10 - 5`. The third token is on a different line than the second token, so the - * `startChar` of the third token will not be altered: - * ``` - * { deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, - * { deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 1, tokenModifiers: 0 }, - * { deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } - * ``` - * - * 4. Finally, the last step is to inline each of the 5 fields for a token in a single array, which is a memory friendly representation: - * ``` - * // 1st token, 2nd token, 3rd token - * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] - * ``` - * - * @see {@link SemanticTokensBuilder} for a helper to encode tokens as integers. - * *NOTE*: When doing edits, it is possible that multiple edits occur until the editor decides to invoke the semantic tokens provider. - * *NOTE*: If the provider cannot temporarily compute semantic tokens, it can indicate this by throwing an error with the message 'Busy'. - */ - provideDocumentSemanticTokens(document: TextDocument, token: CancellationToken): ProviderResult; - - /** - * Instead of always returning all the tokens in a file, it is possible for a `DocumentSemanticTokensProvider` to implement - * this method (`provideDocumentSemanticTokensEdits`) and then return incremental updates to the previously provided semantic tokens. - * - * --- - * ### How tokens change when the document changes - * - * Suppose that `provideDocumentSemanticTokens` has previously returned the following semantic tokens: - * ``` - * // 1st token, 2nd token, 3rd token - * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] - * ``` - * - * Also suppose that after some edits, the new semantic tokens in a file are: - * ``` - * // 1st token, 2nd token, 3rd token - * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] - * ``` - * It is possible to express these new tokens in terms of an edit applied to the previous tokens: - * ``` - * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // old tokens - * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // new tokens - * - * edit: { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3 - * ``` - * - * *NOTE*: If the provider cannot compute `SemanticTokensEdits`, it can "give up" and return all the tokens in the document again. - * *NOTE*: All edits in `SemanticTokensEdits` contain indices in the old integers array, so they all refer to the previous result state. - */ - provideDocumentSemanticTokensEdits?(document: TextDocument, previousResultId: string, token: CancellationToken): ProviderResult; - } - - /** - * The document range semantic tokens provider interface defines the contract between extensions and - * semantic tokens. - */ - export interface DocumentRangeSemanticTokensProvider { - /** - * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens}. - */ - provideDocumentRangeSemanticTokens(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; - } - - /** - * Value-object describing what options formatting should use. - */ - export interface FormattingOptions { - - /** - * Size of a tab in spaces. - */ - tabSize: number; - - /** - * Prefer spaces over tabs. - */ - insertSpaces: boolean; - - /** - * Signature for further properties. - */ - [key: string]: boolean | number | string; - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ - export interface DocumentFormattingEditProvider { - - /** - * Provide formatting edits for a whole document. - * - * @param document The document in which the command was invoked. - * @param options Options controlling formatting. - * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult; - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ - export interface DocumentRangeFormattingEditProvider { - - /** - * Provide formatting edits for a range in a document. - * - * The given range is a hint and providers can decide to format a smaller - * or larger range. Often this is done by adjusting the start and end - * of the range to full syntax nodes. - * - * @param document The document in which the command was invoked. - * @param range The range which should be formatted. - * @param options Options controlling formatting. - * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; - } - - /** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ - export interface OnTypeFormattingEditProvider { - - /** - * Provide formatting edits after a character has been typed. - * - * The given position and character should hint to the provider - * what range the position to expand to, like find the matching `{` - * when `}` has been entered. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param ch The character that has been typed. - * @param options Options controlling formatting. - * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; - } - - /** - * Represents a parameter of a callable-signature. A parameter can - * have a label and a doc-comment. - */ - export class ParameterInformation { - - /** - * The label of this signature. - * - * Either a string or inclusive start and exclusive end offsets within its containing - * {@link SignatureInformation.label signature label}. *Note*: A label of type string must be - * a substring of its containing signature information's {@link SignatureInformation.label label}. - */ - label: string | [number, number]; - - /** - * The human-readable doc-comment of this signature. Will be shown - * in the UI but can be omitted. - */ - documentation?: string | MarkdownString; - - /** - * Creates a new parameter information object. - * - * @param label A label string or inclusive start and exclusive end offsets within its containing signature label. - * @param documentation A doc string. - */ - constructor(label: string | [number, number], documentation?: string | MarkdownString); - } - - /** - * Represents the signature of something callable. A signature - * can have a label, like a function-name, a doc-comment, and - * a set of parameters. - */ - export class SignatureInformation { - - /** - * The label of this signature. Will be shown in - * the UI. - */ - label: string; - - /** - * The human-readable doc-comment of this signature. Will be shown - * in the UI but can be omitted. - */ - documentation?: string | MarkdownString; - - /** - * The parameters of this signature. - */ - parameters: ParameterInformation[]; - - /** - * The index of the active parameter. - * - * If provided, this is used in place of {@linkcode SignatureHelp.activeParameter}. - */ - activeParameter?: number; - - /** - * Creates a new signature information object. - * - * @param label A label string. - * @param documentation A doc string. - */ - constructor(label: string, documentation?: string | MarkdownString); - } - - /** - * Signature help represents the signature of something - * callable. There can be multiple signatures but only one - * active and only one active parameter. - */ - export class SignatureHelp { - - /** - * One or more signatures. - */ - signatures: SignatureInformation[]; - - /** - * The active signature. - */ - activeSignature: number; - - /** - * The active parameter of the active signature. - */ - activeParameter: number; - } - - /** - * How a {@linkcode SignatureHelpProvider} was triggered. - */ - export enum SignatureHelpTriggerKind { - /** - * Signature help was invoked manually by the user or by a command. - */ - Invoke = 1, - - /** - * Signature help was triggered by a trigger character. - */ - TriggerCharacter = 2, - - /** - * Signature help was triggered by the cursor moving or by the document content changing. - */ - ContentChange = 3, - } - - /** - * Additional information about the context in which a - * {@linkcode SignatureHelpProvider.provideSignatureHelp SignatureHelpProvider} was triggered. - */ - export interface SignatureHelpContext { - /** - * Action that caused signature help to be triggered. - */ - readonly triggerKind: SignatureHelpTriggerKind; - - /** - * Character that caused signature help to be triggered. - * - * This is `undefined` when signature help is not triggered by typing, such as when manually invoking - * signature help or when moving the cursor. - */ - readonly triggerCharacter: string | undefined; - - /** - * `true` if signature help was already showing when it was triggered. - * - * Retriggers occur when the signature help is already active and can be caused by actions such as - * typing a trigger character, a cursor move, or document content changes. - */ - readonly isRetrigger: boolean; - - /** - * The currently active {@linkcode SignatureHelp}. - * - * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on - * the user arrowing through available signatures. - */ - readonly activeSignatureHelp: SignatureHelp | undefined; - } - - /** - * The signature help provider interface defines the contract between extensions and - * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ - export interface SignatureHelpProvider { - - /** - * Provide help for the signature at the given position and document. - * - * @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. - * @param context Information about how signature help was triggered. - * - * @return Signature help or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; - } - - /** - * Metadata about a registered {@linkcode SignatureHelpProvider}. - */ - export interface SignatureHelpProviderMetadata { - /** - * List of characters that trigger signature help. - */ - readonly triggerCharacters: readonly string[]; - - /** - * List of characters that re-trigger signature help. - * - * These trigger characters are only active when signature help is already showing. All trigger characters - * are also counted as re-trigger characters. - */ - readonly retriggerCharacters: readonly string[]; - } - - /** - * A structured label for a {@link CompletionItem completion item}. - */ - export interface CompletionItemLabel { - - /** - * The label of this completion item. - * - * By default this is also the text that is inserted when this completion is selected. - */ - label: string; - - /** - * An optional string which is rendered less prominently directly after {@link CompletionItemLabel.label label}, - * without any spacing. Should be used for function signatures or type annotations. - */ - detail?: string; - - /** - * An optional string which is rendered less prominently after {@link CompletionItemLabel.detail}. Should be used - * for fully qualified names or file path. - */ - description?: string; - } - - /** - * Completion item kinds. - */ - export enum CompletionItemKind { - Text = 0, - Method = 1, - Function = 2, - Constructor = 3, - Field = 4, - Variable = 5, - Class = 6, - Interface = 7, - Module = 8, - Property = 9, - Unit = 10, - Value = 11, - Enum = 12, - Keyword = 13, - Snippet = 14, - Color = 15, - Reference = 17, - File = 16, - Folder = 18, - EnumMember = 19, - Constant = 20, - Struct = 21, - Event = 22, - Operator = 23, - TypeParameter = 24, - User = 25, - Issue = 26, - } - - /** - * Completion item tags are extra annotations that tweak the rendering of a completion - * item. - */ - export enum CompletionItemTag { - /** - * Render a completion as obsolete, usually using a strike-out. - */ - Deprecated = 1 - } - - /** - * A completion item represents a text snippet that is proposed to complete text that is being typed. - * - * It is sufficient to create a completion item from just a {@link CompletionItem.label label}. In that - * case the completion item will replace the {@link TextDocument.getWordRangeAtPosition word} - * until the cursor with the given label or {@link CompletionItem.insertText insertText}. Otherwise the - * given {@link CompletionItem.textEdit edit} is used. - * - * When selecting a completion item in the editor its defined or synthesized text edit will be applied - * to *all* cursors/selections whereas {@link CompletionItem.additionalTextEdits additionalTextEdits} will be - * applied as provided. - * - * @see {@link CompletionItemProvider.provideCompletionItems} - * @see {@link CompletionItemProvider.resolveCompletionItem} - */ - export class CompletionItem { - - /** - * The label of this completion item. By default - * this is also the text that is inserted when selecting - * this completion. - */ - label: string | CompletionItemLabel; - - /** - * The kind of this completion item. Based on the kind - * an icon is chosen by the editor. - */ - kind?: CompletionItemKind; - - /** - * Tags for this completion item. - */ - tags?: readonly CompletionItemTag[]; - - /** - * A human-readable string with additional information - * about this item, like type or symbol information. - */ - detail?: string; - - /** - * A human-readable string that represents a doc-comment. - */ - documentation?: string | MarkdownString; - - /** - * A string that should be used when comparing this item - * with other items. When `falsy` the {@link CompletionItem.label label} - * is used. - * - * Note that `sortText` is only used for the initial ordering of completion - * items. When having a leading word (prefix) ordering is based on how - * well completions match that prefix and the initial ordering is only used - * when completions match equally well. The prefix is defined by the - * {@linkcode CompletionItem.range range}-property and can therefore be different - * for each completion. - */ - sortText?: string; - - /** - * A string that should be used when filtering a set of - * completion items. When `falsy` the {@link CompletionItem.label label} - * is used. - * - * Note that the filter text is matched against the leading word (prefix) which is defined - * by the {@linkcode CompletionItem.range range}-property. - */ - filterText?: string; - - /** - * Select this item when showing. *Note* that only one completion item can be selected and - * that the editor decides which item that is. The rule is that the *first* item of those - * that match best is selected. - */ - preselect?: boolean; - - /** - * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the {@link CompletionItem.label label} - * is used. - */ - insertText?: string | SnippetString; - - /** - * A range or a insert and replace range selecting the text that should be replaced by this completion item. - * - * When omitted, the range of the {@link TextDocument.getWordRangeAtPosition current word} is used as replace-range - * and as insert-range the start of the {@link TextDocument.getWordRangeAtPosition current word} to the - * current position is used. - * - * *Note 1:* A range must be a {@link Range.isSingleLine single line} and it must - * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. - * *Note 2:* A insert range must be a prefix of a replace range, that means it must be contained and starting at the same position. - */ - range?: Range | { inserting: Range; replacing: Range }; - - /** - * An optional set of characters that when pressed while this completion is active will accept it first and - * then type that character. *Note* that all commit characters should have `length=1` and that superfluous - * characters will be ignored. - */ - commitCharacters?: string[]; - - /** - * Keep whitespace of the {@link CompletionItem.insertText insertText} as is. By default, the editor adjusts leading - * whitespace of new lines so that they match the indentation of the line for which the item is accepted - setting - * this to `true` will prevent that. - */ - keepWhitespace?: boolean; - - /** - * @deprecated Use `CompletionItem.insertText` and `CompletionItem.range` instead. - * - * An {@link TextEdit edit} which is applied to a document when selecting - * this completion. When an edit is provided the value of - * {@link CompletionItem.insertText insertText} is ignored. - * - * The {@link Range} of the edit must be single-line and on the same - * line completions were {@link CompletionItemProvider.provideCompletionItems requested} at. - */ - textEdit?: TextEdit; - - /** - * An optional array of additional {@link TextEdit text edits} that are applied when - * selecting this completion. Edits must not overlap with the main {@link CompletionItem.textEdit edit} - * nor with themselves. - */ - additionalTextEdits?: TextEdit[]; - - /** - * An optional {@link Command} that is executed *after* inserting this completion. *Note* that - * additional modifications to the current document should be described with the - * {@link CompletionItem.additionalTextEdits additionalTextEdits}-property. - */ - command?: Command; - - /** - * Creates a new completion item. - * - * Completion items must have at least a {@link CompletionItem.label label} which then - * will be used as insert text as well as for sorting and filtering. - * - * @param label The label of the completion. - * @param kind The {@link CompletionItemKind kind} of the completion. - */ - constructor(label: string | CompletionItemLabel, kind?: CompletionItemKind); - } - - /** - * Represents a collection of {@link CompletionItem completion items} to be presented - * in the editor. - */ - export class CompletionList { - - /** - * This list is not complete. Further typing should result in recomputing - * this list. - */ - isIncomplete?: boolean; - - /** - * The completion items. - */ - items: T[]; - - /** - * Creates a new completion list. - * - * @param items The completion items. - * @param isIncomplete The list is not complete. - */ - constructor(items?: T[], isIncomplete?: boolean); - } - - /** - * How a {@link CompletionItemProvider completion provider} was triggered - */ - export enum CompletionTriggerKind { - /** - * Completion was triggered normally. - */ - Invoke = 0, - /** - * Completion was triggered by a trigger character. - */ - TriggerCharacter = 1, - /** - * Completion was re-triggered as current completion list is incomplete - */ - TriggerForIncompleteCompletions = 2 - } - - /** - * Contains additional information about the context in which - * {@link CompletionItemProvider.provideCompletionItems completion provider} is triggered. - */ - export interface CompletionContext { - /** - * How the completion was triggered. - */ - readonly triggerKind: CompletionTriggerKind; - - /** - * Character that triggered the completion item provider. - * - * `undefined` if the provider was not triggered by a character. - * - * The trigger character is already in the document when the completion provider is triggered. - */ - readonly triggerCharacter: string | undefined; - } - - /** - * The completion item provider interface defines the contract between extensions and - * [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). - * - * Providers can delay the computation of the {@linkcode CompletionItem.detail detail} - * and {@linkcode CompletionItem.documentation documentation} properties by implementing the - * {@linkcode CompletionItemProvider.resolveCompletionItem resolveCompletionItem}-function. However, properties that - * are needed for the initial sorting and filtering, like `sortText`, `filterText`, `insertText`, and `range`, must - * not be changed during resolve. - * - * Providers are asked for completions either explicitly by a user gesture or -depending on the configuration- - * implicitly when typing words or trigger characters. - */ - export interface CompletionItemProvider { - - /** - * Provide completion items for the given position and document. - * - * @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. - * @param context How the completion was triggered. - * - * @return An array of completions, a {@link CompletionList completion list}, or a thenable that resolves to either. - * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult>; - - /** - * Given a completion item fill in more data, like {@link CompletionItem.documentation doc-comment} - * or {@link CompletionItem.detail details}. - * - * The editor will only resolve a completion item once. - * - * *Note* that this function is called when completion items are already showing in the UI or when an item has been - * selected for insertion. Because of that, no property that changes the presentation (label, sorting, filtering etc) - * or the (primary) insert behaviour ({@link CompletionItem.insertText insertText}) can be changed. - * - * This function may fill in {@link CompletionItem.additionalTextEdits additionalTextEdits}. However, that means an item might be - * inserted *before* resolving is done and in that case the editor will do a best effort to still apply those additional - * text edits. - * - * @param item A completion item currently active in the UI. - * @param token A cancellation token. - * @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given - * `item`. When no result is returned, the given `item` will be used. - */ - resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult; - } - - - /** - * The inline completion item provider interface defines the contract between extensions and - * the inline completion feature. - * - * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. - */ - export interface InlineCompletionItemProvider { - - /** - * Provides inline completion items for the given position and document. - * If inline completions are enabled, this method will be called whenever the user stopped typing. - * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. - * In that case, all available inline completions should be returned. - * `context.triggerKind` can be used to distinguish between these scenarios. - * - * @param document The document inline completions are requested for. - * @param position The position inline completions are requested for. - * @param context A context object with additional information. - * @param token A cancellation token. - * @return An array of completion items or a thenable that resolves to an array of completion items. - */ - provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; - } - - /** - * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented - * in the editor. - */ - export class InlineCompletionList { - /** - * The inline completion items. - */ - items: InlineCompletionItem[]; - - /** - * Creates a new list of inline completion items. - */ - constructor(items: InlineCompletionItem[]); - } - - /** - * Provides information about the context in which an inline completion was requested. - */ - export interface InlineCompletionContext { - /** - * Describes how the inline completion was triggered. - */ - readonly triggerKind: InlineCompletionTriggerKind; - - /** - * Provides information about the currently selected item in the autocomplete widget if it is visible. - * - * If set, provided inline completions must extend the text of the selected item - * and use the same range, otherwise they are not shown as preview. - * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, - * the inline completion must also replace `.` and start with `.log`, for example `.log()`. - * - * Inline completion providers are requested again whenever the selected item changes. - */ - readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; - } - - /** - * Describes the currently selected completion item. - */ - export interface SelectedCompletionInfo { - /** - * The range that will be replaced if this completion item is accepted. - */ - readonly range: Range; - - /** - * The text the range will be replaced with if this completion is accepted. - */ - readonly text: string; - } - - /** - * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. - */ - export enum InlineCompletionTriggerKind { - /** - * Completion was triggered explicitly by a user gesture. - * Return multiple completion items to enable cycling through them. - */ - Invoke = 0, - - /** - * Completion was triggered automatically while editing. - * It is sufficient to return a single completion item in this case. - */ - Automatic = 1, - } - - /** - * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. - * - * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} - */ - export class InlineCompletionItem { - /** - * The text to replace the range with. Must be set. - * Is used both for the preview and the accept operation. - */ - insertText: string | SnippetString; - - /** - * A text that is used to decide if this inline completion should be shown. When `falsy` - * the {@link InlineCompletionItem.insertText} is used. - * - * An inline completion is shown if the text to replace is a prefix of the filter text. - */ - filterText?: string; - - /** - * The range to replace. - * Must begin and end on the same line. - * - * Prefer replacements over insertions to provide a better experience when the user deletes typed text. - */ - range?: Range; - - /** - * An optional {@link Command} that is executed *after* inserting this completion. - */ - command?: Command; - - /** - * Creates a new inline completion item. - * - * @param insertText The text to replace the range with. - * @param range The range to replace. If not set, the word at the requested position will be used. - * @param command An optional {@link Command} that is executed *after* inserting this completion. - */ - constructor(insertText: string | SnippetString, range?: Range, command?: Command); - } - - /** - * A document link is a range in a text document that links to an internal or external resource, like another - * text document or a web site. - */ - export class DocumentLink { - - /** - * The range this link applies to. - */ - range: Range; - - /** - * The uri this link points to. - */ - target?: Uri; - - /** - * The tooltip text when you hover over this link. - * - * If a tooltip is provided, is will be displayed in a string that includes instructions on how to - * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, - * user settings, and localization. - */ - tooltip?: string; - - /** - * Creates a new document link. - * - * @param range The range the document link applies to. Must not be empty. - * @param target The uri the document link points to. - */ - constructor(range: Range, target?: Uri); - } - - /** - * The document link provider defines the contract between extensions and feature of showing - * links in the editor. - */ - export interface DocumentLinkProvider { - - /** - * Provide links for the given document. Note that the editor ships with a default provider that detects - * `http(s)` and `file` links. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of {@link DocumentLink document links} or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult; - - /** - * Given a link fill in its {@link DocumentLink.target target}. This method is called when an incomplete - * link is selected in the UI. Providers can implement this method and return incomplete links - * (without target) from the {@linkcode DocumentLinkProvider.provideDocumentLinks provideDocumentLinks} method which - * often helps to improve performance. - * - * @param link The link that is to be resolved. - * @param token A cancellation token. - */ - resolveDocumentLink?(link: T, token: CancellationToken): ProviderResult; - } - - /** - * Represents a color in RGBA space. - */ - export class Color { - - /** - * The red component of this color in the range [0-1]. - */ - readonly red: number; - - /** - * The green component of this color in the range [0-1]. - */ - readonly green: number; - - /** - * The blue component of this color in the range [0-1]. - */ - readonly blue: number; - - /** - * The alpha component of this color in the range [0-1]. - */ - readonly alpha: number; - - /** - * Creates a new color instance. - * - * @param red The red component. - * @param green The green component. - * @param blue The blue component. - * @param alpha The alpha component. - */ - constructor(red: number, green: number, blue: number, alpha: number); - } - - /** - * Represents a color range from a document. - */ - export class ColorInformation { - - /** - * The range in the document where this color appears. - */ - range: Range; - - /** - * The actual color value for this color range. - */ - color: Color; - - /** - * Creates a new color range. - * - * @param range The range the color appears in. Must not be empty. - * @param color The value of the color. - */ - constructor(range: Range, color: Color); - } - - /** - * A color presentation object describes how a {@linkcode Color} should be represented as text and what - * edits are required to refer to it from source code. - * - * For some languages one color can have multiple presentations, e.g. css can represent the color red with - * the constant `Red`, the hex-value `#ff0000`, or in rgba and hsla forms. In csharp other representations - * apply, e.g. `System.Drawing.Color.Red`. - */ - export class ColorPresentation { - - /** - * The label of this color presentation. It will be shown on the color - * picker header. By default this is also the text that is inserted when selecting - * this color presentation. - */ - label: string; - - /** - * An {@link TextEdit edit} which is applied to a document when selecting - * this presentation for the color. When `falsy` the {@link ColorPresentation.label label} - * is used. - */ - textEdit?: TextEdit; - - /** - * An optional array of additional {@link TextEdit text edits} that are applied when - * selecting this color presentation. Edits must not overlap with the main {@link ColorPresentation.textEdit edit} nor with themselves. - */ - additionalTextEdits?: TextEdit[]; - - /** - * Creates a new color presentation. - * - * @param label The label of this color presentation. - */ - constructor(label: string); - } - - /** - * The document color provider defines the contract between extensions and feature of - * picking and modifying colors in the editor. - */ - export interface DocumentColorProvider { - - /** - * Provide colors for the given document. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of {@link ColorInformation color information} or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; - - /** - * Provide {@link ColorPresentation representations} for a color. - * - * @param color The color to show and insert. - * @param context A context object with additional information - * @param token A cancellation token. - * @return An array of color presentations or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideColorPresentations(color: Color, context: { readonly document: TextDocument; readonly range: Range }, token: CancellationToken): ProviderResult; - } - - /** - * Inlay hint kinds. - * - * The kind of an inline hint defines its appearance, e.g the corresponding foreground and background colors are being - * used. - */ - export enum InlayHintKind { - /** - * An inlay hint that for a type annotation. - */ - Type = 1, - /** - * An inlay hint that is for a parameter. - */ - Parameter = 2, - } - - /** - * An inlay hint label part allows for interactive and composite labels of inlay hints. - */ - export class InlayHintLabelPart { - - /** - * The value of this label part. - */ - value: string; - - /** - * The tooltip text when you hover over this label part. - * - * *Note* that this property can be set late during - * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. - */ - tooltip?: string | MarkdownString | undefined; - - /** - * An optional {@link Location source code location} that represents this label - * part. - * - * The editor will use this location for the hover and for code navigation features: This - * part will become a clickable link that resolves to the definition of the symbol at the - * given location (not necessarily the location itself), it shows the hover that shows at - * the given location, and it shows a context menu with further code navigation commands. - * - * *Note* that this property can be set late during - * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. - */ - location?: Location | undefined; - - /** - * An optional command for this label part. - * - * The editor renders parts with commands as clickable links. The command is added to the context menu - * when a label part defines {@link InlayHintLabelPart.location location} and {@link InlayHintLabelPart.command command} . - * - * *Note* that this property can be set late during - * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. - */ - command?: Command | undefined; - - /** - * Creates a new inlay hint label part. - * - * @param value The value of the part. - */ - constructor(value: string); - } - - /** - * Inlay hint information. - */ - export class InlayHint { - - /** - * The position of this hint. - */ - position: Position; - - /** - * The label of this hint. A human readable string or an array of {@link InlayHintLabelPart label parts}. - * - * *Note* that neither the string nor the label part can be empty. - */ - label: string | InlayHintLabelPart[]; - - /** - * The tooltip text when you hover over this item. - * - * *Note* that this property can be set late during - * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. - */ - tooltip?: string | MarkdownString | undefined; - - /** - * The kind of this hint. The inlay hint kind defines the appearance of this inlay hint. - */ - kind?: InlayHintKind; - - /** - * Optional {@link TextEdit text edits} that are performed when accepting this inlay hint. The default - * gesture for accepting an inlay hint is the double click. - * - * *Note* that edits are expected to change the document so that the inlay hint (or its nearest variant) is - * now part of the document and the inlay hint itself is now obsolete. - * - * *Note* that this property can be set late during - * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. - */ - textEdits?: TextEdit[]; - - /** - * Render padding before the hint. Padding will use the editor's background color, - * not the background color of the hint itself. That means padding can be used to visually - * align/separate an inlay hint. - */ - paddingLeft?: boolean; - - /** - * Render padding after the hint. Padding will use the editor's background color, - * not the background color of the hint itself. That means padding can be used to visually - * align/separate an inlay hint. - */ - paddingRight?: boolean; - - /** - * Creates a new inlay hint. - * - * @param position The position of the hint. - * @param label The label of the hint. - * @param kind The {@link InlayHintKind kind} of the hint. - */ - constructor(position: Position, label: string | InlayHintLabelPart[], kind?: InlayHintKind); - } - - /** - * The inlay hints provider interface defines the contract between extensions and - * the inlay hints feature. - */ - export interface InlayHintsProvider { - - /** - * An optional event to signal that inlay hints from this provider have changed. - */ - onDidChangeInlayHints?: Event; - - /** - * Provide inlay hints for the given range and document. - * - * *Note* that inlay hints that are not {@link Range.contains contained} by the given range are ignored. - * - * @param document The document in which the command was invoked. - * @param range The range for which inlay hints should be computed. - * @param token A cancellation token. - * @return An array of inlay hints or a thenable that resolves to such. - */ - provideInlayHints(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; - - /** - * Given an inlay hint fill in {@link InlayHint.tooltip tooltip}, {@link InlayHint.textEdits text edits}, - * or complete label {@link InlayHintLabelPart parts}. - * - * *Note* that the editor will resolve an inlay hint at most once. - * - * @param hint An inlay hint. - * @param token A cancellation token. - * @return The resolved inlay hint or a thenable that resolves to such. It is OK to return the given `item`. When no result is returned, the given `item` will be used. - */ - resolveInlayHint?(hint: T, token: CancellationToken): ProviderResult; - } - - /** - * A line based folding range. To be valid, start and end line must be bigger than zero and smaller than the number of lines in the document. - * Invalid ranges will be ignored. - */ - export class FoldingRange { - - /** - * The zero-based start line of the range to fold. The folded area starts after the line's last character. - * To be valid, the end must be zero or larger and smaller than the number of lines in the document. - */ - start: number; - - /** - * The zero-based end line of the range to fold. The folded area ends with the line's last character. - * To be valid, the end must be zero or larger and smaller than the number of lines in the document. - */ - end: number; - - /** - * Describes the {@link FoldingRangeKind Kind} of the folding range such as {@link FoldingRangeKind.Comment Comment} or - * {@link FoldingRangeKind.Region Region}. The kind is used to categorize folding ranges and used by commands - * like 'Fold all comments'. See - * {@link FoldingRangeKind} for an enumeration of all kinds. - * If not set, the range is originated from a syntax element. - */ - kind?: FoldingRangeKind; - - /** - * Creates a new folding range. - * - * @param start The start line of the folded range. - * @param end The end line of the folded range. - * @param kind The kind of the folding range. - */ - constructor(start: number, end: number, kind?: FoldingRangeKind); - } - - /** - * An enumeration of specific folding range kinds. The kind is an optional field of a {@link FoldingRange} - * and is used to distinguish specific folding ranges such as ranges originated from comments. The kind is used by commands like - * `Fold all comments` or `Fold all regions`. - * If the kind is not set on the range, the range originated from a syntax element other than comments, imports or region markers. - */ - export enum FoldingRangeKind { - /** - * Kind for folding range representing a comment. - */ - Comment = 1, - /** - * Kind for folding range representing a import. - */ - Imports = 2, - /** - * Kind for folding range representing regions originating from folding markers like `#region` and `#endregion`. - */ - Region = 3 - } - - /** - * Folding context (for future use) - */ - export interface FoldingContext { - } - - /** - * The folding range provider interface defines the contract between extensions and - * [Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) in the editor. - */ - export interface FoldingRangeProvider { - - /** - * An optional event to signal that the folding ranges from this provider have changed. - */ - onDidChangeFoldingRanges?: Event; - - /** - * Returns a list of folding ranges or null and undefined if the provider - * does not want to participate or was cancelled. - * @param document The document in which the command was invoked. - * @param context Additional context information (for future use) - * @param token A cancellation token. - */ - provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken): ProviderResult; - } - - /** - * A selection range represents a part of a selection hierarchy. A selection range - * may have a parent selection range that contains it. - */ - export class SelectionRange { - - /** - * The {@link Range} of this selection range. - */ - range: Range; - - /** - * The parent selection range containing this range. - */ - parent?: SelectionRange; - - /** - * Creates a new selection range. - * - * @param range The range of the selection range. - * @param parent The parent of the selection range. - */ - constructor(range: Range, parent?: SelectionRange); - } - - export interface SelectionRangeProvider { - /** - * Provide selection ranges for the given positions. - * - * Selection ranges should be computed individually and independent for each position. The editor will merge - * and deduplicate ranges but providers must return hierarchies of selection ranges so that a range - * is {@link Range.contains contained} by its parent. - * - * @param document The document in which the command was invoked. - * @param positions The positions at which the command was invoked. - * @param token A cancellation token. - * @return Selection ranges or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideSelectionRanges(document: TextDocument, positions: readonly Position[], token: CancellationToken): ProviderResult; - } - - /** - * Represents programming constructs like functions or constructors in the context - * of call hierarchy. - */ - export class CallHierarchyItem { - /** - * The name of this item. - */ - name: string; - - /** - * The kind of this item. - */ - kind: SymbolKind; - - /** - * Tags for this item. - */ - tags?: readonly SymbolTag[]; - - /** - * More detail for this item, e.g. the signature of a function. - */ - detail?: string; - - /** - * The resource identifier of this item. - */ - uri: Uri; - - /** - * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. - */ - range: Range; - - /** - * The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function. - * Must be contained by the {@linkcode CallHierarchyItem.range range}. - */ - selectionRange: Range; - - /** - * Creates a new call hierarchy item. - */ - constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); - } - - /** - * Represents an incoming call, e.g. a caller of a method or constructor. - */ - export class CallHierarchyIncomingCall { - - /** - * The item that makes the call. - */ - from: CallHierarchyItem; - - /** - * The range at which at which the calls appears. This is relative to the caller - * denoted by {@linkcode CallHierarchyIncomingCall.from this.from}. - */ - fromRanges: Range[]; - - /** - * Create a new call object. - * - * @param item The item making the call. - * @param fromRanges The ranges at which the calls appear. - */ - constructor(item: CallHierarchyItem, fromRanges: Range[]); - } - - /** - * Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. - */ - export class CallHierarchyOutgoingCall { - - /** - * The item that is called. - */ - to: CallHierarchyItem; - - /** - * The range at which this item is called. This is the range relative to the caller, e.g the item - * passed to {@linkcode CallHierarchyProvider.provideCallHierarchyOutgoingCalls provideCallHierarchyOutgoingCalls} - * and not {@linkcode CallHierarchyOutgoingCall.to this.to}. - */ - fromRanges: Range[]; - - /** - * Create a new call object. - * - * @param item The item being called - * @param fromRanges The ranges at which the calls appear. - */ - constructor(item: CallHierarchyItem, fromRanges: Range[]); - } - - /** - * The call hierarchy provider interface describes the contract between extensions - * and the call hierarchy feature which allows to browse calls and caller of function, - * methods, constructor etc. - */ - export interface CallHierarchyProvider { - - /** - * Bootstraps call hierarchy by returning the item that is denoted by the given document - * and position. This item will be used as entry into the call graph. Providers should - * return `undefined` or `null` when there is no item at the given location. - * - * @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. - * @returns One or multiple call hierarchy items or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - prepareCallHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - - /** - * Provide all incoming calls for an item, e.g all callers for a method. In graph terms this describes directed - * and annotated edges inside the call graph, e.g the given item is the starting node and the result is the nodes - * that can be reached. - * - * @param item The hierarchy item for which incoming calls should be computed. - * @param token A cancellation token. - * @returns A set of incoming calls or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideCallHierarchyIncomingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; - - /** - * Provide all outgoing calls for an item, e.g call calls to functions, methods, or constructors from the given item. In - * graph terms this describes directed and annotated edges inside the call graph, e.g the given item is the starting - * node and the result is the nodes that can be reached. - * - * @param item The hierarchy item for which outgoing calls should be computed. - * @param token A cancellation token. - * @returns A set of outgoing calls or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; - } - - /** - * Represents an item of a type hierarchy, like a class or an interface. - */ - export class TypeHierarchyItem { - /** - * The name of this item. - */ - name: string; - - /** - * The kind of this item. - */ - kind: SymbolKind; - - /** - * Tags for this item. - */ - tags?: ReadonlyArray; - - /** - * More detail for this item, e.g. the signature of a function. - */ - detail?: string; - - /** - * The resource identifier of this item. - */ - uri: Uri; - - /** - * The range enclosing this symbol not including leading/trailing whitespace - * but everything else, e.g. comments and code. - */ - range: Range; - - /** - * The range that should be selected and revealed when this symbol is being - * picked, e.g. the name of a class. Must be contained by the {@link TypeHierarchyItem.range range}-property. - */ - selectionRange: Range; - - /** - * Creates a new type hierarchy item. - * - * @param kind The kind of the item. - * @param name The name of the item. - * @param detail The details of the item. - * @param uri The Uri of the item. - * @param range The whole range of the item. - * @param selectionRange The selection range of the item. - */ - constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); - } - - /** - * The type hierarchy provider interface describes the contract between extensions - * and the type hierarchy feature. - */ - export interface TypeHierarchyProvider { - - /** - * Bootstraps type hierarchy by returning the item that is denoted by the given document - * and position. This item will be used as entry into the type graph. Providers should - * return `undefined` or `null` when there is no item at the given location. - * - * @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. - * @returns One or multiple type hierarchy items or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty array. - */ - prepareTypeHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - - /** - * Provide all supertypes for an item, e.g all types from which a type is derived/inherited. In graph terms this describes directed - * and annotated edges inside the type graph, e.g the given item is the starting node and the result is the nodes - * that can be reached. - * - * @param item The hierarchy item for which super types should be computed. - * @param token A cancellation token. - * @returns A set of direct supertypes or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideTypeHierarchySupertypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult; - - /** - * Provide all subtypes for an item, e.g all types which are derived/inherited from the given item. In - * graph terms this describes directed and annotated edges inside the type graph, e.g the given item is the starting - * node and the result is the nodes that can be reached. - * - * @param item The hierarchy item for which subtypes should be computed. - * @param token A cancellation token. - * @returns A set of direct subtypes or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideTypeHierarchySubtypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult; - } - - /** - * Represents a list of ranges that can be edited together along with a word pattern to describe valid range contents. - */ - export class LinkedEditingRanges { - /** - * Create a new linked editing ranges object. - * - * @param ranges A list of ranges that can be edited together - * @param wordPattern An optional word pattern that describes valid contents for the given ranges - */ - constructor(ranges: Range[], wordPattern?: RegExp); - - /** - * A list of ranges that can be edited together. The ranges must have - * identical length and text content. The ranges cannot overlap. - */ - readonly ranges: Range[]; - - /** - * An optional word pattern that describes valid contents for the given ranges. - * If no pattern is provided, the language configuration's word pattern will be used. - */ - readonly wordPattern: RegExp | undefined; - } - - /** - * The linked editing range provider interface defines the contract between extensions and - * the linked editing feature. - */ - export interface LinkedEditingRangeProvider { - /** - * For a given position in a document, returns the range of the symbol at the position and all ranges - * that have the same content. A change to one of the ranges can be applied to all other ranges if the new content - * is valid. An optional word pattern can be returned with the result to describe valid contents. - * If no result-specific word pattern is provided, the word pattern from the language configuration is used. - * - * @param document The document in which the provider was invoked. - * @param position The position at which the provider was invoked. - * @param token A cancellation token. - * @return A list of ranges that can be edited together - */ - provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - /** - * An edit operation applied {@link DocumentDropEditProvider on drop}. - */ - export class DocumentDropEdit { - /** - * The text or snippet to insert at the drop location. - */ - insertText: string | SnippetString; - - /** - * An optional additional edit to apply on drop. - */ - additionalEdit?: WorkspaceEdit; - - /** - * @param insertText The text or snippet to insert at the drop location. - */ - constructor(insertText: string | SnippetString); - } - - /** - * Provider which handles dropping of resources into a text editor. - * - * This allows users to drag and drop resources (including resources from external apps) into the editor. While dragging - * and dropping files, users can hold down `shift` to drop the file into the editor instead of opening it. - * Requires `editor.dropIntoEditor.enabled` to be on. - */ - export interface DocumentDropEditProvider { - /** - * Provide edits which inserts the content being dragged and dropped into the document. - * - * @param document The document in which the drop occurred. - * @param position The position in the document where the drop occurred. - * @param dataTransfer A {@link DataTransfer} object that holds data about what is being dragged and dropped. - * @param token A cancellation token. - * - * @return A {@link DocumentDropEdit} or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideDocumentDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; - } - - /** - * A tuple of two characters, like a pair of - * opening and closing brackets. - */ - export type CharacterPair = [string, string]; - - /** - * Describes how comments for a language work. - */ - export interface CommentRule { - - /** - * The line comment token, like `// this is a comment` - */ - lineComment?: string; - - /** - * The block comment character pair, like `/* block comment */` - */ - blockComment?: CharacterPair; - } - - /** - * Describes indentation rules for a language. - */ - export interface IndentationRule { - /** - * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). - */ - decreaseIndentPattern: RegExp; - /** - * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). - */ - increaseIndentPattern: RegExp; - /** - * If a line matches this pattern, then **only the next line** after it should be indented once. - */ - indentNextLinePattern?: RegExp; - /** - * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. - */ - unIndentedLinePattern?: RegExp; - } - - /** - * Describes what to do with the indentation when pressing Enter. - */ - export enum IndentAction { - /** - * Insert new line and copy the previous line's indentation. - */ - None = 0, - /** - * Insert new line and indent once (relative to the previous line's indentation). - */ - Indent = 1, - /** - * Insert two new lines: - * - the first one indented which will hold the cursor - * - the second one at the same indentation level - */ - IndentOutdent = 2, - /** - * Insert new line and outdent once (relative to the previous line's indentation). - */ - Outdent = 3 - } - - /** - * Describes what to do when pressing Enter. - */ - export interface EnterAction { - /** - * Describe what to do with the indentation. - */ - indentAction: IndentAction; - /** - * Describes text to be appended after the new line and after the indentation. - */ - appendText?: string; - /** - * Describes the number of characters to remove from the new line's indentation. - */ - removeText?: number; - } - - /** - * Describes a rule to be evaluated when pressing Enter. - */ - export interface OnEnterRule { - /** - * This rule will only execute if the text before the cursor matches this regular expression. - */ - beforeText: RegExp; - /** - * This rule will only execute if the text after the cursor matches this regular expression. - */ - afterText?: RegExp; - /** - * This rule will only execute if the text above the current line matches this regular expression. - */ - previousLineText?: RegExp; - /** - * The action to execute. - */ - action: EnterAction; - } - - /** - * The language configuration interfaces defines the contract between extensions - * and various editor features, like automatic bracket insertion, automatic indentation etc. - */ - export interface LanguageConfiguration { - /** - * The language's comment settings. - */ - comments?: CommentRule; - /** - * The language's brackets. - * This configuration implicitly affects pressing Enter around these brackets. - */ - brackets?: CharacterPair[]; - /** - * The language's word definition. - * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable - * to provide a word definition that uses exclusion of known separators. - * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): - * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g - */ - wordPattern?: RegExp; - /** - * The language's indentation settings. - */ - indentationRules?: IndentationRule; - /** - * The language's rules to be evaluated when pressing Enter. - */ - onEnterRules?: OnEnterRule[]; - - /** - * **Deprecated** Do not use. - * - * @deprecated Will be replaced by a better API soon. - */ - __electricCharacterSupport?: { - /** - * This property is deprecated and will be **ignored** from - * the editor. - * @deprecated - */ - brackets?: any; - /** - * This property is deprecated and not fully supported anymore by - * the editor (scope and lineStart are ignored). - * Use the autoClosingPairs property in the language configuration file instead. - * @deprecated - */ - docComment?: { - scope: string; - open: string; - lineStart: string; - close?: string; - }; - }; - - /** - * **Deprecated** Do not use. - * - * @deprecated * Use the autoClosingPairs property in the language configuration file instead. - */ - __characterPairSupport?: { - autoClosingPairs: { - open: string; - close: string; - notIn?: string[]; - }[]; - }; - } - - /** - * The configuration target - */ - export enum ConfigurationTarget { - /** - * Global configuration - */ - Global = 1, - - /** - * Workspace configuration - */ - Workspace = 2, - - /** - * Workspace folder configuration - */ - WorkspaceFolder = 3 - } - - /** - * Represents the configuration. It is a merged view of - * - * - *Default Settings* - * - *Global (User) Settings* - * - *Workspace settings* - * - *Workspace Folder settings* - From one of the {@link workspace.workspaceFolders Workspace Folders} under which requested resource belongs to. - * - *Language settings* - Settings defined under requested language. - * - * The *effective* value (returned by {@linkcode WorkspaceConfiguration.get get}) is computed by overriding or merging the values in the following order: - * - * 1. `defaultValue` (if defined in `package.json` otherwise derived from the value's type) - * 1. `globalValue` (if defined) - * 1. `workspaceValue` (if defined) - * 1. `workspaceFolderValue` (if defined) - * 1. `defaultLanguageValue` (if defined) - * 1. `globalLanguageValue` (if defined) - * 1. `workspaceLanguageValue` (if defined) - * 1. `workspaceFolderLanguageValue` (if defined) - * - * **Note:** Only `object` value types are merged and all other value types are overridden. - * - * Example 1: Overriding - * - * ```ts - * defaultValue = 'on'; - * globalValue = 'relative' - * workspaceFolderValue = 'off' - * value = 'off' - * ``` - * - * Example 2: Language Values - * - * ```ts - * defaultValue = 'on'; - * globalValue = 'relative' - * workspaceFolderValue = 'off' - * globalLanguageValue = 'on' - * value = 'on' - * ``` - * - * Example 3: Object Values - * - * ```ts - * defaultValue = { "a": 1, "b": 2 }; - * globalValue = { "b": 3, "c": 4 }; - * value = { "a": 1, "b": 3, "c": 4 }; - * ``` - * - * *Note:* Workspace and Workspace Folder configurations contains `launch` and `tasks` settings. Their basename will be - * part of the section identifier. The following snippets shows how to retrieve all configurations - * from `launch.json`: - * - * ```ts - * // launch.json configuration - * const config = workspace.getConfiguration('launch', vscode.workspace.workspaceFolders[0].uri); - * - * // retrieve values - * const values = config.get('configurations'); - * ``` - * - * Refer to [Settings](https://code.visualstudio.com/docs/getstarted/settings) for more information. - */ - export interface WorkspaceConfiguration { - - /** - * Return a value from this configuration. - * - * @param section Configuration name, supports _dotted_ names. - * @return The value `section` denotes or `undefined`. - */ - get(section: string): T | undefined; - - /** - * Return a value from this configuration. - * - * @param section Configuration name, supports _dotted_ names. - * @param defaultValue A value should be returned when no value could be found, is `undefined`. - * @return The value `section` denotes or the default. - */ - get(section: string, defaultValue: T): T; - - /** - * Check if this configuration has a certain value. - * - * @param section Configuration name, supports _dotted_ names. - * @return `true` if the section doesn't resolve to `undefined`. - */ - has(section: string): boolean; - - /** - * Retrieve all information about a configuration setting. A configuration value - * often consists of a *default* value, a global or installation-wide value, - * a workspace-specific value, folder-specific value - * and language-specific values (if {@link WorkspaceConfiguration} is scoped to a language). - * - * Also provides all language ids under which the given configuration setting is defined. - * - * *Note:* The configuration name must denote a leaf in the configuration tree - * (`editor.fontSize` vs `editor`) otherwise no result is returned. - * - * @param section Configuration name, supports _dotted_ names. - * @return Information about a configuration setting or `undefined`. - */ - inspect(section: string): { - key: string; - - defaultValue?: T; - globalValue?: T; - workspaceValue?: T; - workspaceFolderValue?: T; - - defaultLanguageValue?: T; - globalLanguageValue?: T; - workspaceLanguageValue?: T; - workspaceFolderLanguageValue?: T; - - languageIds?: string[]; - - } | undefined; - - /** - * Update a configuration value. The updated configuration values are persisted. - * - * A value can be changed in - * - * - {@link ConfigurationTarget.Global Global settings}: Changes the value for all instances of the editor. - * - {@link ConfigurationTarget.Workspace Workspace settings}: Changes the value for current workspace, if available. - * - {@link ConfigurationTarget.WorkspaceFolder Workspace folder settings}: Changes the value for settings from one of the {@link workspace.workspaceFolders Workspace Folders} under which the requested resource belongs to. - * - Language settings: Changes the value for the requested languageId. - * - * *Note:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` - * - * @param section Configuration name, supports _dotted_ names. - * @param value The new value. - * @param configurationTarget The {@link ConfigurationTarget configuration target} or a boolean value. - * - If `true` updates {@link ConfigurationTarget.Global Global settings}. - * - If `false` updates {@link ConfigurationTarget.Workspace Workspace settings}. - * - If `undefined` or `null` updates to {@link ConfigurationTarget.WorkspaceFolder Workspace folder settings} if configuration is resource specific, - * otherwise to {@link ConfigurationTarget.Workspace Workspace settings}. - * @param overrideInLanguage Whether to update the value in the scope of requested languageId or not. - * - If `true` updates the value under the requested languageId. - * - If `undefined` updates the value under the requested languageId only if the configuration is defined for the language. - * @throws error while updating - * - configuration which is not registered. - * - window configuration to workspace folder - * - configuration to workspace or workspace folder when no workspace is opened. - * - configuration to workspace folder when there is no workspace folder settings. - * - configuration to workspace folder when {@link WorkspaceConfiguration} is not scoped to a resource. - */ - update(section: string, value: any, configurationTarget?: ConfigurationTarget | boolean | null, overrideInLanguage?: boolean): Thenable; - - /** - * Readable dictionary that backs this configuration. - */ - readonly [key: string]: any; - } - - /** - * Represents a location inside a resource, such as a line - * inside a text file. - */ - export class Location { - - /** - * The resource identifier of this location. - */ - uri: Uri; - - /** - * The document range of this location. - */ - range: Range; - - /** - * Creates a new location object. - * - * @param uri The resource identifier. - * @param rangeOrPosition The range or position. Positions will be converted to an empty range. - */ - constructor(uri: Uri, rangeOrPosition: Range | Position); - } - - /** - * Represents the connection of two locations. Provides additional metadata over normal {@link Location locations}, - * including an origin range. - */ - export interface LocationLink { - /** - * Span of the origin of this link. - * - * Used as the underlined span for mouse definition hover. Defaults to the word range at - * the definition position. - */ - originSelectionRange?: Range; - - /** - * The target resource identifier of this link. - */ - targetUri: Uri; - - /** - * The full target range of this link. - */ - targetRange: Range; - - /** - * The span of this link. - */ - targetSelectionRange?: Range; - } - - /** - * The event that is fired when diagnostics change. - */ - export interface DiagnosticChangeEvent { - - /** - * An array of resources for which diagnostics have changed. - */ - readonly uris: readonly Uri[]; - } - - /** - * Represents the severity of diagnostics. - */ - export enum DiagnosticSeverity { - - /** - * 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, - - /** - * Something to hint to a better way of doing it, like proposing - * a refactoring. - */ - Hint = 3 - } - - /** - * Represents a related message and source code location for a diagnostic. This should be - * used to point to code locations that cause or related to a diagnostics, e.g. when duplicating - * a symbol in a scope. - */ - export class DiagnosticRelatedInformation { - - /** - * The location of this related diagnostic information. - */ - location: Location; - - /** - * The message of this related diagnostic information. - */ - message: string; - - /** - * Creates a new related diagnostic information object. - * - * @param location The location. - * @param message The message. - */ - constructor(location: Location, message: string); - } - - /** - * Additional metadata about the type of a diagnostic. - */ - export enum DiagnosticTag { - /** - * Unused or unnecessary code. - * - * Diagnostics with this tag are rendered faded out. The amount of fading - * is controlled by the `"editorUnnecessaryCode.opacity"` theme color. For - * example, `"editorUnnecessaryCode.opacity": "#000000c0"` will render the - * code with 75% opacity. For high contrast themes, use the - * `"editorUnnecessaryCode.border"` theme color to underline unnecessary code - * instead of fading it out. - */ - Unnecessary = 1, - - /** - * Deprecated or obsolete code. - * - * Diagnostics with this tag are rendered with a strike through. - */ - Deprecated = 2, - } - - /** - * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects - * are only valid in the scope of a file. - */ - export class Diagnostic { - - /** - * The range to which this diagnostic applies. - */ - range: Range; - - /** - * The human-readable message. - */ - message: string; - - /** - * The severity, default is {@link DiagnosticSeverity.Error error}. - */ - severity: DiagnosticSeverity; - - /** - * A human-readable string describing the source of this - * diagnostic, e.g. 'typescript' or 'super lint'. - */ - source?: string; - - /** - * A code or identifier for this diagnostic. - * Should be used for later processing, e.g. when providing {@link CodeActionContext code actions}. - */ - code?: string | number | { - /** - * A code or identifier for this diagnostic. - * Should be used for later processing, e.g. when providing {@link CodeActionContext code actions}. - */ - value: string | number; - - /** - * A target URI to open with more information about the diagnostic error. - */ - target: Uri; - }; - - /** - * An array of related diagnostic information, e.g. when symbol-names within - * a scope collide all definitions can be marked via this property. - */ - relatedInformation?: DiagnosticRelatedInformation[]; - - /** - * Additional metadata about the diagnostic. - */ - tags?: DiagnosticTag[]; - - /** - * Creates a new diagnostic object. - * - * @param range The range to which this diagnostic applies. - * @param message The human-readable message. - * @param severity The severity, default is {@link DiagnosticSeverity.Error error}. - */ - constructor(range: Range, message: string, severity?: DiagnosticSeverity); - } - - /** - * A diagnostics collection is a container that manages a set of - * {@link Diagnostic diagnostics}. Diagnostics are always scopes to a - * diagnostics collection and a resource. - * - * To get an instance of a `DiagnosticCollection` use - * {@link languages.createDiagnosticCollection createDiagnosticCollection}. - */ - export interface DiagnosticCollection extends Iterable<[uri: Uri, diagnostics: readonly Diagnostic[]]> { - - /** - * The name of this diagnostic collection, for instance `typescript`. Every diagnostic - * from this collection will be associated with this name. Also, the task framework uses this - * name when defining [problem matchers](https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher). - */ - readonly name: string; - - /** - * Assign diagnostics for given resource. Will replace - * existing diagnostics for that resource. - * - * @param uri A resource identifier. - * @param diagnostics Array of diagnostics or `undefined` - */ - set(uri: Uri, diagnostics: readonly Diagnostic[] | undefined): void; - - /** - * Replace diagnostics for multiple resources in this collection. - * - * _Note_ that multiple tuples of the same uri will be merged, e.g - * `[[file1, [d1]], [file1, [d2]]]` is equivalent to `[[file1, [d1, d2]]]`. - * If a diagnostics item is `undefined` as in `[file1, undefined]` - * all previous but not subsequent diagnostics are removed. - * - * @param entries An array of tuples, like `[[file1, [d1, d2]], [file2, [d3, d4, d5]]]`, or `undefined`. - */ - set(entries: ReadonlyArray<[Uri, readonly Diagnostic[] | undefined]>): void; - - /** - * Remove all diagnostics from this collection that belong - * to the provided `uri`. The same as `#set(uri, undefined)`. - * - * @param uri A resource identifier. - */ - delete(uri: Uri): void; - - /** - * Remove all diagnostics from this collection. The same - * as calling `#set(undefined)`; - */ - clear(): void; - - /** - * Iterate over each entry in this collection. - * - * @param callback Function to execute for each entry. - * @param thisArg The `this` context used when invoking the handler function. - */ - forEach(callback: (uri: Uri, diagnostics: readonly Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void; - - /** - * Get the diagnostics for a given resource. *Note* that you cannot - * modify the diagnostics-array returned from this call. - * - * @param uri A resource identifier. - * @returns An immutable array of {@link Diagnostic diagnostics} or `undefined`. - */ - get(uri: Uri): readonly Diagnostic[] | undefined; - - /** - * Check if this collection contains diagnostics for a - * given resource. - * - * @param uri A resource identifier. - * @returns `true` if this collection has diagnostic for the given resource. - */ - has(uri: Uri): boolean; - - /** - * Dispose and free associated resources. Calls - * {@link DiagnosticCollection.clear clear}. - */ - dispose(): void; - } - - /** - * Represents the severity of a language status item. - */ - export enum LanguageStatusSeverity { - Information = 0, - Warning = 1, - Error = 2 - } - - /** - * A language status item is the preferred way to present language status reports for the active text editors, - * such as selected linter or notifying about a configuration problem. - */ - export interface LanguageStatusItem { - - /** - * The identifier of this item. - */ - readonly id: string; - - /** - * The short name of this item, like 'Java Language Status', etc. - */ - name: string | undefined; - - /** - * A {@link DocumentSelector selector} that defines for what editors - * this item shows. - */ - selector: DocumentSelector; - - /** - * The severity of this item. - * - * Defaults to {@link LanguageStatusSeverity.Information information}. You can use this property to - * signal to users that there is a problem that needs attention, like a missing executable or an - * invalid configuration. - */ - severity: LanguageStatusSeverity; - - /** - * The text to show for the entry. You can embed icons in the text by leveraging the syntax: - * - * `My text $(icon-name) contains icons like $(icon-name) this one.` - * - * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. - * `light-bulb`, `thumbsup`, `zap` etc. - */ - text: string; - - /** - * Optional, human-readable details for this item. - */ - detail?: string; - - /** - * Controls whether the item is shown as "busy". Defaults to `false`. - */ - busy: boolean; - - /** - * A {@linkcode Command command} for this item. - */ - command: Command | undefined; - - /** - * Accessibility information used when a screen reader interacts with this item - */ - accessibilityInformation?: AccessibilityInformation; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * Denotes a location of an editor in the window. Editors can be arranged in a grid - * and each column represents one editor location in that grid by counting the editors - * in order of their appearance. - */ - export enum ViewColumn { - /** - * A *symbolic* editor column representing the currently active column. This value - * can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value - * of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Active`. - */ - Active = -1, - /** - * A *symbolic* editor column representing the column to the side of the active one. This value - * can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value - * of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Beside`. - */ - Beside = -2, - /** - * The first editor column. - */ - One = 1, - /** - * The second editor column. - */ - Two = 2, - /** - * The third editor column. - */ - Three = 3, - /** - * The fourth editor column. - */ - Four = 4, - /** - * The fifth editor column. - */ - Five = 5, - /** - * The sixth editor column. - */ - Six = 6, - /** - * The seventh editor column. - */ - Seven = 7, - /** - * The eighth editor column. - */ - Eight = 8, - /** - * The ninth editor column. - */ - Nine = 9 - } - - /** - * An output channel is a container for readonly textual information. - * - * To get an instance of an `OutputChannel` use - * {@link window.createOutputChannel createOutputChannel}. - */ - export interface OutputChannel { - - /** - * The human-readable name of this output channel. - */ - readonly name: string; - - /** - * Append the given value to the channel. - * - * @param value A string, falsy values will not be printed. - */ - append(value: string): void; - - /** - * Append the given value and a line feed character - * to the channel. - * - * @param value A string, falsy values will be printed. - */ - appendLine(value: string): void; - - /** - * Replaces all output from the channel with the given value. - * - * @param value A string, falsy values will not be printed. - */ - replace(value: string): void; - - /** - * Removes all output from the channel. - */ - clear(): void; - - /** - * Reveal this channel in the UI. - * - * @param preserveFocus When `true` the channel will not take focus. - */ - show(preserveFocus?: boolean): void; - - /** - * Reveal this channel in the UI. - * - * @deprecated Use the overload with just one parameter (`show(preserveFocus?: boolean): void`). - * - * @param column This argument is **deprecated** and will be ignored. - * @param preserveFocus When `true` the channel will not take focus. - */ - show(column?: ViewColumn, preserveFocus?: boolean): void; - - /** - * Hide this channel from the UI. - */ - hide(): void; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * Accessibility information which controls screen reader behavior. - */ - export interface AccessibilityInformation { - /** - * Label to be read out by a screen reader once the item has focus. - */ - readonly label: string; - - /** - * Role of the widget which defines how a screen reader interacts with it. - * The role should be set in special cases when for example a tree-like element behaves like a checkbox. - * If role is not specified the editor will pick the appropriate role automatically. - * More about aria roles can be found here https://w3c.github.io/aria/#widget_roles - */ - readonly role?: string; - } - - /** - * Represents the alignment of status bar items. - */ - export enum StatusBarAlignment { - - /** - * Aligned to the left side. - */ - Left = 1, - - /** - * Aligned to the right side. - */ - Right = 2 - } - - /** - * A status bar item is a status bar contribution that can - * show text and icons and run a command on click. - */ - export interface StatusBarItem { - - /** - * The identifier of this item. - * - * *Note*: if no identifier was provided by the {@linkcode window.createStatusBarItem} - * method, the identifier will match the {@link Extension.id extension identifier}. - */ - readonly id: string; - - /** - * The alignment of this item. - */ - readonly alignment: StatusBarAlignment; - - /** - * The priority of this item. Higher value means the item should - * be shown more to the left. - */ - readonly priority: number | undefined; - - /** - * The name of the entry, like 'Python Language Indicator', 'Git Status' etc. - * Try to keep the length of the name short, yet descriptive enough that - * users can understand what the status bar item is about. - */ - name: string | undefined; - - /** - * The text to show for the entry. You can embed icons in the text by leveraging the syntax: - * - * `My text $(icon-name) contains icons like $(icon-name) this one.` - * - * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. - * `light-bulb`, `thumbsup`, `zap` etc. - */ - text: string; - - /** - * The tooltip text when you hover over this entry. - */ - tooltip: string | MarkdownString | undefined; - - /** - * The foreground color for this entry. - */ - color: string | ThemeColor | undefined; - - /** - * The background color for this entry. - * - * *Note*: only the following colors are supported: - * * `new ThemeColor('statusBarItem.errorBackground')` - * * `new ThemeColor('statusBarItem.warningBackground')` - * - * More background colors may be supported in the future. - * - * *Note*: when a background color is set, the statusbar may override - * the `color` choice to ensure the entry is readable in all themes. - */ - backgroundColor: ThemeColor | undefined; - - /** - * {@linkcode Command} or identifier of a command to run on click. - * - * The command must be {@link commands.getCommands known}. - * - * Note that if this is a {@linkcode Command} object, only the {@linkcode Command.command command} and {@linkcode Command.arguments arguments} - * are used by the editor. - */ - command: string | Command | undefined; - - /** - * Accessibility information used when a screen reader interacts with this StatusBar item - */ - accessibilityInformation: AccessibilityInformation | undefined; - - /** - * Shows the entry in the status bar. - */ - show(): void; - - /** - * Hide the entry in the status bar. - */ - hide(): void; - - /** - * Dispose and free associated resources. Call - * {@link StatusBarItem.hide hide}. - */ - dispose(): void; - } - - /** - * Defines a generalized way of reporting progress updates. - */ - export interface Progress { - - /** - * Report a progress update. - * @param value A progress item, like a message and/or an - * report on how much work finished - */ - report(value: T): void; - } - - /** - * An individual terminal instance within the integrated terminal. - */ - export interface Terminal { - - /** - * The name of the terminal. - */ - readonly name: string; - - /** - * The process ID of the shell process. - */ - readonly processId: Thenable; - - /** - * The object used to initialize the terminal, this is useful for example to detecting the - * shell type of when the terminal was not launched by this extension or for detecting what - * folder the shell was launched in. - */ - readonly creationOptions: Readonly; - - /** - * The exit status of the terminal, this will be undefined while the terminal is active. - * - * **Example:** Show a notification with the exit code when the terminal exits with a - * non-zero exit code. - * ```typescript - * window.onDidCloseTerminal(t => { - * if (t.exitStatus && t.exitStatus.code) { - * vscode.window.showInformationMessage(`Exit code: ${t.exitStatus.code}`); - * } - * }); - * ``` - */ - readonly exitStatus: TerminalExitStatus | undefined; - - /** - * The current state of the {@link Terminal}. - */ - readonly state: TerminalState; - - /** - * Send text to the terminal. The text is written to the stdin of the underlying pty process - * (shell) of the terminal. - * - * @param text The text to send. - * @param addNewLine Whether to add a new line to the text being sent, this is normally - * required to run a command in the terminal. The character(s) added are \n or \r\n - * depending on the platform. This defaults to `true`. - */ - sendText(text: string, addNewLine?: boolean): void; - - /** - * Show the terminal panel and reveal this terminal in the UI. - * - * @param preserveFocus When `true` the terminal will not take focus. - */ - show(preserveFocus?: boolean): void; - - /** - * Hide the terminal panel if this terminal is currently showing. - */ - hide(): void; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * The location of the terminal. - */ - export enum TerminalLocation { - /** - * In the terminal view - */ - Panel = 1, - /** - * In the editor area - */ - Editor = 2, - } - - /** - * Assumes a {@link TerminalLocation} of editor and allows specifying a {@link ViewColumn} and - * {@link TerminalEditorLocationOptions.preserveFocus preserveFocus } property - */ - export interface TerminalEditorLocationOptions { - /** - * A view column in which the {@link Terminal terminal} should be shown in the editor area. - * The default is the {@link ViewColumn.Active active}. Columns that do not exist - * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. - * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently - * active one. - */ - viewColumn: ViewColumn; - /** - * An optional flag that when `true` will stop the {@link Terminal} from taking focus. - */ - preserveFocus?: boolean; - } - - /** - * Uses the parent {@link Terminal}'s location for the terminal - */ - export interface TerminalSplitLocationOptions { - /** - * The parent terminal to split this terminal beside. This works whether the parent terminal - * is in the panel or the editor area. - */ - parentTerminal: Terminal; - } - - /** - * Represents the state of a {@link Terminal}. - */ - export interface TerminalState { - /** - * Whether the {@link Terminal} has been interacted with. Interaction means that the - * terminal has sent data to the process which depending on the terminal's _mode_. By - * default input is sent when a key is pressed or when a command or extension sends text, - * but based on the terminal's mode it can also happen on: - * - * - a pointer click event - * - a pointer scroll event - * - a pointer move event - * - terminal focus in/out - * - * For more information on events that can send data see "DEC Private Mode Set (DECSET)" on - * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html - */ - readonly isInteractedWith: boolean; - } - - /** - * Provides information on a line in a terminal in order to provide links for it. - */ - export interface TerminalLinkContext { - /** - * This is the text from the unwrapped line in the terminal. - */ - line: string; - - /** - * The terminal the link belongs to. - */ - terminal: Terminal; - } - - /** - * A provider that enables detection and handling of links within terminals. - */ - export interface TerminalLinkProvider { - /** - * Provide terminal links for the given context. Note that this can be called multiple times - * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) - * that could have problems when asynchronous usage may overlap. - * @param context Information about what links are being provided for. - * @param token A cancellation token. - * @return A list of terminal links for the given line. - */ - provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult; - - /** - * Handle an activated terminal link. - * @param link The link to handle. - */ - handleTerminalLink(link: T): ProviderResult; - } - - /** - * A link on a terminal line. - */ - export class TerminalLink { - /** - * The start index of the link on {@link TerminalLinkContext.line}. - */ - startIndex: number; - - /** - * The length of the link on {@link TerminalLinkContext.line}. - */ - length: number; - - /** - * The tooltip text when you hover over this link. - * - * If a tooltip is provided, is will be displayed in a string that includes instructions on - * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary - * depending on OS, user settings, and localization. - */ - tooltip?: string; - - /** - * Creates a new terminal link. - * @param startIndex The start index of the link on {@link TerminalLinkContext.line}. - * @param length The length of the link on {@link TerminalLinkContext.line}. - * @param tooltip The tooltip text when you hover over this link. - * - * If a tooltip is provided, is will be displayed in a string that includes instructions on - * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary - * depending on OS, user settings, and localization. - */ - constructor(startIndex: number, length: number, tooltip?: string); - } - - /** - * Provides a terminal profile for the contributed terminal profile when launched via the UI or - * command. - */ - export interface TerminalProfileProvider { - /** - * Provide the terminal profile. - * @param token A cancellation token that indicates the result is no longer needed. - * @returns The terminal profile. - */ - provideTerminalProfile(token: CancellationToken): ProviderResult; - } - - /** - * A terminal profile defines how a terminal will be launched. - */ - export class TerminalProfile { - /** - * The options that the terminal will launch with. - */ - options: TerminalOptions | ExtensionTerminalOptions; - - /** - * Creates a new terminal profile. - * @param options The options that the terminal will launch with. - */ - constructor(options: TerminalOptions | ExtensionTerminalOptions); - } - - /** - * A file decoration represents metadata that can be rendered with a file. - */ - export class FileDecoration { - - /** - * A very short string that represents this decoration. - */ - badge?: string; - - /** - * A human-readable tooltip for this decoration. - */ - tooltip?: string; - - /** - * The color of this decoration. - */ - color?: ThemeColor; - - /** - * A flag expressing that this decoration should be - * propagated to its parents. - */ - propagate?: boolean; - - /** - * Creates a new decoration. - * - * @param badge A letter that represents the decoration. - * @param tooltip The tooltip of the decoration. - * @param color The color of the decoration. - */ - constructor(badge?: string, tooltip?: string, color?: ThemeColor); - } - - /** - * The decoration provider interfaces defines the contract between extensions and - * file decorations. - */ - export interface FileDecorationProvider { - - /** - * An optional event to signal that decorations for one or many files have changed. - * - * *Note* that this event should be used to propagate information about children. - * - * @see {@link EventEmitter} - */ - onDidChangeFileDecorations?: Event; - - /** - * Provide decorations for a given uri. - * - * *Note* that this function is only called when a file gets rendered in the UI. - * This means a decoration from a descendent that propagates upwards must be signaled - * to the editor via the {@link FileDecorationProvider.onDidChangeFileDecorations onDidChangeFileDecorations}-event. - * - * @param uri The uri of the file to provide a decoration for. - * @param token A cancellation token. - * @returns A decoration or a thenable that resolves to such. - */ - provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; - } - - - /** - * In a remote window the extension kind describes if an extension - * runs where the UI (window) runs or if an extension runs remotely. - */ - export enum ExtensionKind { - - /** - * Extension runs where the UI runs. - */ - UI = 1, - - /** - * Extension runs where the remote extension host runs. - */ - Workspace = 2 - } - - /** - * Represents an extension. - * - * To get an instance of an `Extension` use {@link extensions.getExtension getExtension}. - */ - export interface Extension { - - /** - * The canonical extension identifier in the form of: `publisher.name`. - */ - readonly id: string; - - /** - * The uri of the directory containing the extension. - */ - readonly extensionUri: Uri; - - /** - * The absolute file path of the directory containing this extension. Shorthand - * notation for {@link Extension.extensionUri Extension.extensionUri.fsPath} (independent of the uri scheme). - */ - readonly extensionPath: string; - - /** - * `true` if the extension has been activated. - */ - readonly isActive: boolean; - - /** - * The parsed contents of the extension's package.json. - */ - readonly packageJSON: any; - - /** - * The extension kind describes if an extension runs where the UI runs - * or if an extension runs where the remote extension host runs. The extension kind - * is defined in the `package.json`-file of extensions but can also be refined - * via the `remote.extensionKind`-setting. When no remote extension host exists, - * the value is {@linkcode ExtensionKind.UI}. - */ - extensionKind: ExtensionKind; - - /** - * The public API exported by this extension (return value of `activate`). - * It is an invalid action to access this field before this extension has been activated. - */ - readonly exports: T; - - /** - * Activates this extension and returns its public API. - * - * @return A promise that will resolve when this extension has been activated. - */ - activate(): Thenable; - } - - /** - * The ExtensionMode is provided on the `ExtensionContext` and indicates the - * mode the specific extension is running in. - */ - export enum ExtensionMode { - /** - * The extension is installed normally (for example, from the marketplace - * or VSIX) in the editor. - */ - Production = 1, - - /** - * The extension is running from an `--extensionDevelopmentPath` provided - * when launching the editor. - */ - Development = 2, - - /** - * The extension is running from an `--extensionTestsPath` and - * the extension host is running unit tests. - */ - Test = 3, - } - - /** - * An extension context is a collection of utilities private to an - * extension. - * - * An instance of an `ExtensionContext` is provided as the first - * parameter to the `activate`-call of an extension. - */ - export interface ExtensionContext { - - /** - * An array to which disposables can be added. When this - * extension is deactivated the disposables will be disposed. - * - * *Note* that asynchronous dispose-functions aren't awaited. - */ - readonly subscriptions: { dispose(): any }[]; - - /** - * A memento object that stores state in the context - * of the currently opened {@link workspace.workspaceFolders workspace}. - */ - readonly workspaceState: Memento; - - /** - * A memento object that stores state independent - * of the current opened {@link workspace.workspaceFolders workspace}. - */ - readonly globalState: Memento & { - /** - * Set the keys whose values should be synchronized across devices when synchronizing user-data - * like configuration, extensions, and mementos. - * - * Note that this function defines the whole set of keys whose values are synchronized: - * - calling it with an empty array stops synchronization for this memento - * - calling it with a non-empty array replaces all keys whose values are synchronized - * - * For any given set of keys this function needs to be called only once but there is no harm in - * repeatedly calling it. - * - * @param keys The set of keys whose values are synced. - */ - setKeysForSync(keys: readonly string[]): void; - }; - - /** - * A storage utility for secrets. Secrets are persisted across reloads and are independent of the - * current opened {@link workspace.workspaceFolders workspace}. - */ - readonly secrets: SecretStorage; - - /** - * The uri of the directory containing the extension. - */ - readonly extensionUri: Uri; - - /** - * The absolute file path of the directory containing the extension. Shorthand - * notation for {@link TextDocument.uri ExtensionContext.extensionUri.fsPath} (independent of the uri scheme). - */ - readonly extensionPath: string; - - /** - * Gets the extension's environment variable collection for this workspace, enabling changes - * to be applied to terminal environment variables. - */ - readonly environmentVariableCollection: EnvironmentVariableCollection; - - /** - * Get the absolute path of a resource contained in the extension. - * - * *Note* that an absolute uri can be constructed via {@linkcode Uri.joinPath} and - * {@linkcode ExtensionContext.extensionUri extensionUri}, e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` - * - * @param relativePath A relative path to a resource contained in the extension. - * @return The absolute path of the resource. - */ - asAbsolutePath(relativePath: string): string; - - /** - * The uri of a workspace specific directory in which the extension - * can store private state. The directory might not exist and creation is - * up to the extension. However, the parent directory is guaranteed to be existent. - * The value is `undefined` when no workspace nor folder has been opened. - * - * Use {@linkcode ExtensionContext.workspaceState workspaceState} or - * {@linkcode ExtensionContext.globalState globalState} to store key value data. - * - * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from - * an uri. - */ - readonly storageUri: Uri | undefined; - - /** - * An absolute file path of a workspace specific directory in which the extension - * can store private state. The directory might not exist on disk and creation is - * up to the extension. However, the parent directory is guaranteed to be existent. - * - * Use {@linkcode ExtensionContext.workspaceState workspaceState} or - * {@linkcode ExtensionContext.globalState globalState} to store key value data. - * - * @deprecated Use {@link ExtensionContext.storageUri storageUri} instead. - */ - readonly storagePath: string | undefined; - - /** - * The uri of a directory in which the extension can store global state. - * The directory might not exist on disk and creation is - * up to the extension. However, the parent directory is guaranteed to be existent. - * - * Use {@linkcode ExtensionContext.globalState globalState} to store key value data. - * - * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from - * an uri. - */ - readonly globalStorageUri: Uri; - - /** - * An absolute file path in which the extension can store global state. - * The directory might not exist on disk and creation is - * up to the extension. However, the parent directory is guaranteed to be existent. - * - * Use {@linkcode ExtensionContext.globalState globalState} to store key value data. - * - * @deprecated Use {@link ExtensionContext.globalStorageUri globalStorageUri} instead. - */ - readonly globalStoragePath: string; - - /** - * The uri of a directory in which the extension can create log files. - * The directory might not exist on disk and creation is up to the extension. However, - * the parent directory is guaranteed to be existent. - * - * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from - * an uri. - */ - readonly logUri: Uri; - - /** - * An absolute file path of a directory in which the extension can create log files. - * The directory might not exist on disk and creation is up to the extension. However, - * the parent directory is guaranteed to be existent. - * - * @deprecated Use {@link ExtensionContext.logUri logUri} instead. - */ - readonly logPath: string; - - /** - * The mode the extension is running in. This is specific to the current - * extension. One extension may be in `ExtensionMode.Development` while - * other extensions in the host run in `ExtensionMode.Release`. - */ - readonly extensionMode: ExtensionMode; - - /** - * The current `Extension` instance. - */ - readonly extension: Extension; - } - - /** - * A memento represents a storage utility. It can store and retrieve - * values. - */ - export interface Memento { - - /** - * Returns the stored keys. - * - * @return The stored keys. - */ - keys(): readonly string[]; - - /** - * Return a value. - * - * @param key A string. - * @return The stored value or `undefined`. - */ - get(key: string): T | undefined; - - /** - * Return a value. - * - * @param key A string. - * @param defaultValue A value that should be returned when there is no - * value (`undefined`) with the given key. - * @return The stored value or the defaultValue. - */ - get(key: string, defaultValue: T): T; - - /** - * Store a value. The value must be JSON-stringifyable. - * - * *Note* that using `undefined` as value removes the key from the underlying - * storage. - * - * @param key A string. - * @param value A value. MUST not contain cyclic references. - */ - update(key: string, value: any): Thenable; - } - - /** - * The event data that is fired when a secret is added or removed. - */ - export interface SecretStorageChangeEvent { - /** - * The key of the secret that has changed. - */ - readonly key: string; - } - - /** - * Represents a storage utility for secrets, information that is - * sensitive. - */ - export interface SecretStorage { - /** - * Retrieve a secret that was stored with key. Returns undefined if there - * is no password matching that key. - * @param key The key the secret was stored under. - * @returns The stored value or `undefined`. - */ - get(key: string): Thenable; - - /** - * Store a secret under a given key. - * @param key The key to store the secret under. - * @param value The secret. - */ - store(key: string, value: string): Thenable; - - /** - * Remove a secret from storage. - * @param key The key the secret was stored under. - */ - delete(key: string): Thenable; - - /** - * Fires when a secret is stored or deleted. - */ - onDidChange: Event; - } - - /** - * Represents a color theme kind. - */ - export enum ColorThemeKind { - Light = 1, - Dark = 2, - HighContrast = 3, - HighContrastLight = 4 - } - - /** - * Represents a color theme. - */ - export interface ColorTheme { - - /** - * The kind of this color theme: light, dark, high contrast dark and high contrast light. - */ - readonly kind: ColorThemeKind; - } - - /** - * Controls the behaviour of the terminal's visibility. - */ - export enum TaskRevealKind { - /** - * Always brings the terminal to front if the task is executed. - */ - Always = 1, - - /** - * Only brings the terminal to front if a problem is detected executing the task - * (e.g. the task couldn't be started because). - */ - Silent = 2, - - /** - * The terminal never comes to front when the task is executed. - */ - Never = 3 - } - - /** - * Controls how the task channel is used between tasks - */ - export enum TaskPanelKind { - - /** - * Shares a panel with other tasks. This is the default. - */ - Shared = 1, - - /** - * Uses a dedicated panel for this tasks. The panel is not - * shared with other tasks. - */ - Dedicated = 2, - - /** - * Creates a new panel whenever this task is executed. - */ - New = 3 - } - - /** - * Controls how the task is presented in the UI. - */ - export interface TaskPresentationOptions { - /** - * Controls whether the task output is reveal in the user interface. - * Defaults to `RevealKind.Always`. - */ - reveal?: TaskRevealKind; - - /** - * Controls whether the command associated with the task is echoed - * in the user interface. - */ - echo?: boolean; - - /** - * Controls whether the panel showing the task output is taking focus. - */ - focus?: boolean; - - /** - * Controls if the task panel is used for this task only (dedicated), - * shared between tasks (shared) or if a new panel is created on - * every task execution (new). Defaults to `TaskInstanceKind.Shared` - */ - panel?: TaskPanelKind; - - /** - * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message. - */ - showReuseMessage?: boolean; - - /** - * Controls whether the terminal is cleared before executing the task. - */ - clear?: boolean; - } - - /** - * A grouping for tasks. The editor by default supports the - * 'Clean', 'Build', 'RebuildAll' and 'Test' group. - */ - export class TaskGroup { - - /** - * The clean task group; - */ - static Clean: TaskGroup; - - /** - * The build task group; - */ - static Build: TaskGroup; - - /** - * The rebuild all task group; - */ - static Rebuild: TaskGroup; - - /** - * The test all task group; - */ - static Test: TaskGroup; - - /** - * Whether the task that is part of this group is the default for the group. - * This property cannot be set through API, and is controlled by a user's task configurations. - */ - readonly isDefault: boolean | undefined; - - /** - * The ID of the task group. Is one of TaskGroup.Clean.id, TaskGroup.Build.id, TaskGroup.Rebuild.id, or TaskGroup.Test.id. - */ - readonly id: string; - - private constructor(id: string, label: string); - } - - /** - * A structure that defines a task kind in the system. - * The value must be JSON-stringifyable. - */ - export interface TaskDefinition { - /** - * The task definition describing the task provided by an extension. - * Usually a task provider defines more properties to identify - * a task. They need to be defined in the package.json of the - * extension under the 'taskDefinitions' extension point. The npm - * task definition for example looks like this - * ```typescript - * interface NpmTaskDefinition extends TaskDefinition { - * script: string; - * } - * ``` - * - * Note that type identifier starting with a '$' are reserved for internal - * usages and shouldn't be used by extensions. - */ - readonly type: string; - - /** - * Additional attributes of a concrete task definition. - */ - [name: string]: any; - } - - /** - * Options for a process execution - */ - export interface ProcessExecutionOptions { - /** - * The current working directory of the executed program or shell. - * If omitted the tools current workspace root is used. - */ - cwd?: string; - - /** - * The additional environment of the executed program or shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env?: { [key: string]: string }; - } - - /** - * The execution of a task happens as an external process - * without shell interaction. - */ - export class ProcessExecution { - - /** - * Creates a process execution. - * - * @param process The process to start. - * @param options Optional options for the started process. - */ - constructor(process: string, options?: ProcessExecutionOptions); - - /** - * Creates a process execution. - * - * @param process The process to start. - * @param args Arguments to be passed to the process. - * @param options Optional options for the started process. - */ - constructor(process: string, args: string[], options?: ProcessExecutionOptions); - - /** - * The process to be executed. - */ - process: string; - - /** - * The arguments passed to the process. Defaults to an empty array. - */ - args: string[]; - - /** - * The process options used when the process is executed. - * Defaults to undefined. - */ - options?: ProcessExecutionOptions; - } - - /** - * The shell quoting options. - */ - export interface ShellQuotingOptions { - - /** - * The character used to do character escaping. If a string is provided only spaces - * are escaped. If a `{ escapeChar, charsToEscape }` literal is provide all characters - * in `charsToEscape` are escaped using the `escapeChar`. - */ - escape?: string | { - /** - * The escape character. - */ - escapeChar: string; - /** - * The characters to escape. - */ - charsToEscape: string; - }; - - /** - * The character used for strong quoting. The string's length must be 1. - */ - strong?: string; - - /** - * The character used for weak quoting. The string's length must be 1. - */ - weak?: string; - } - - /** - * Options for a shell execution - */ - export interface ShellExecutionOptions { - /** - * The shell executable. - */ - executable?: string; - - /** - * The arguments to be passed to the shell executable used to run the task. Most shells - * require special arguments to execute a command. For example `bash` requires the `-c` - * argument to execute a command, `PowerShell` requires `-Command` and `cmd` requires both - * `/d` and `/c`. - */ - shellArgs?: string[]; - - /** - * The shell quotes supported by this shell. - */ - shellQuoting?: ShellQuotingOptions; - - /** - * The current working directory of the executed shell. - * If omitted the tools current workspace root is used. - */ - cwd?: string; - - /** - * The additional environment of the executed shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env?: { [key: string]: string }; - } - - /** - * Defines how an argument should be quoted if it contains - * spaces or unsupported characters. - */ - export enum ShellQuoting { - - /** - * Character escaping should be used. This for example - * uses \ on bash and ` on PowerShell. - */ - Escape = 1, - - /** - * Strong string quoting should be used. This for example - * uses " for Windows cmd and ' for bash and PowerShell. - * Strong quoting treats arguments as literal strings. - * Under PowerShell echo 'The value is $(2 * 3)' will - * print `The value is $(2 * 3)` - */ - Strong = 2, - - /** - * Weak string quoting should be used. This for example - * uses " for Windows cmd, bash and PowerShell. Weak quoting - * still performs some kind of evaluation inside the quoted - * string. Under PowerShell echo "The value is $(2 * 3)" - * will print `The value is 6` - */ - Weak = 3 - } - - /** - * A string that will be quoted depending on the used shell. - */ - export interface ShellQuotedString { - /** - * The actual string value. - */ - value: string; - - /** - * The quoting style to use. - */ - quoting: ShellQuoting; - } - - export class ShellExecution { - /** - * Creates a shell execution with a full command line. - * - * @param commandLine The command line to execute. - * @param options Optional options for the started the shell. - */ - constructor(commandLine: string, options?: ShellExecutionOptions); - - /** - * Creates a shell execution with a command and arguments. For the real execution the editor will - * construct a command line from the command and the arguments. This is subject to interpretation - * especially when it comes to quoting. If full control over the command line is needed please - * use the constructor that creates a `ShellExecution` with the full command line. - * - * @param command The command to execute. - * @param args The command arguments. - * @param options Optional options for the started the shell. - */ - constructor(command: string | ShellQuotedString, args: (string | ShellQuotedString)[], options?: ShellExecutionOptions); - - /** - * The shell command line. Is `undefined` if created with a command and arguments. - */ - commandLine: string | undefined; - - /** - * The shell command. Is `undefined` if created with a full command line. - */ - command: string | ShellQuotedString; - - /** - * The shell args. Is `undefined` if created with a full command line. - */ - args: (string | ShellQuotedString)[]; - - /** - * The shell options used when the command line is executed in a shell. - * Defaults to undefined. - */ - options?: ShellExecutionOptions; - } - - /** - * Class used to execute an extension callback as a task. - */ - export class CustomExecution { - /** - * Constructs a CustomExecution task object. The callback will be executed when 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 - * {@link Pseudoterminal.open} is called. Task cancellation should be handled using - * {@link Pseudoterminal.close}. When the task is complete fire - * {@link Pseudoterminal.onDidClose}. - * @param callback The callback that will be called when the task is started by a user. Any ${} style variables that - * were in the task definition will be resolved and passed into the callback as `resolvedDefinition`. - */ - constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); - } - - /** - * The scope of a task. - */ - export enum TaskScope { - /** - * The task is a global task. Global tasks are currently not supported. - */ - Global = 1, - - /** - * The task is a workspace task - */ - Workspace = 2 - } - - /** - * Run options for a task. - */ - export interface RunOptions { - /** - * Controls whether task variables are re-evaluated on rerun. - */ - reevaluateOnRerun?: boolean; - } - - /** - * A task to execute - */ - export class Task { - - /** - * Creates a new task. - * - * @param taskDefinition The task definition as defined in the taskDefinitions extension point. - * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. Global tasks are currently not supported. - * @param name The task's name. Is presented in the user interface. - * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. - * @param execution The process or shell execution. - * @param problemMatchers the names of problem matchers to use, like '$tsc' - * or '$eslint'. Problem matchers can be contributed by an extension using - * the `problemMatchers` extension point. - */ - constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); - - /** - * Creates a new task. - * - * @deprecated Use the new constructors that allow specifying a scope for the task. - * - * @param taskDefinition The task definition as defined in the taskDefinitions extension point. - * @param name The task's name. Is presented in the user interface. - * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. - * @param execution The process or shell execution. - * @param problemMatchers the names of problem matchers to use, like '$tsc' - * or '$eslint'. Problem matchers can be contributed by an extension using - * the `problemMatchers` extension point. - */ - constructor(taskDefinition: TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); - - /** - * The task's definition. - */ - definition: TaskDefinition; - - /** - * The task's scope. - */ - readonly scope: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder | undefined; - - /** - * The task's name - */ - name: string; - - /** - * A human-readable string which is rendered less prominently on a separate line in places - * where the task's name is displayed. Supports rendering of {@link ThemeIcon theme icons} - * via the `$()`-syntax. - */ - detail?: string; - - /** - * The task's execution engine - */ - execution?: ProcessExecution | ShellExecution | CustomExecution; - - /** - * Whether the task is a background task or not. - */ - isBackground: boolean; - - /** - * A human-readable string describing the source of this shell task, e.g. 'gulp' - * or 'npm'. Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax. - */ - source: string; - - /** - * The task group this tasks belongs to. See TaskGroup - * for a predefined set of available groups. - * Defaults to undefined meaning that the task doesn't - * belong to any special group. - */ - group?: TaskGroup; - - /** - * The presentation options. Defaults to an empty literal. - */ - presentationOptions: TaskPresentationOptions; - - /** - * The problem matchers attached to the task. Defaults to an empty - * array. - */ - problemMatchers: string[]; - - /** - * Run options for the task - */ - runOptions: RunOptions; - } - - /** - * A task provider allows to add tasks to the task service. - * A task provider is registered via {@link tasks.registerTaskProvider}. - */ - export interface TaskProvider { - /** - * Provides tasks. - * @param token A cancellation token. - * @return an array of tasks - */ - provideTasks(token: CancellationToken): ProviderResult; - - /** - * Resolves a task that has no {@linkcode Task.execution execution} set. Tasks are - * often created from information found in the `tasks.json`-file. Such tasks miss - * the information on how to execute them and a task provider must fill in - * the missing information in the `resolveTask`-method. This method will not be - * called for tasks returned from the above `provideTasks` method since those - * tasks are always fully resolved. A valid default implementation for the - * `resolveTask` method is to return `undefined`. - * - * Note that when filling in the properties of `task`, you _must_ be sure to - * use the exact same `TaskDefinition` and not create a new one. Other properties - * may be changed. - * - * @param task The task to resolve. - * @param token A cancellation token. - * @return The resolved task - */ - resolveTask(task: T, token: CancellationToken): ProviderResult; - } - - /** - * An object representing an executed Task. It can be used - * to terminate a task. - * - * This interface is not intended to be implemented. - */ - export interface TaskExecution { - /** - * The task that got started. - */ - task: Task; - - /** - * Terminates the task execution. - */ - terminate(): void; - } - - /** - * An event signaling the start of a task execution. - * - * This interface is not intended to be implemented. - */ - interface TaskStartEvent { - /** - * The task item representing the task that got started. - */ - readonly execution: TaskExecution; - } - - /** - * An event signaling the end of an executed task. - * - * This interface is not intended to be implemented. - */ - interface TaskEndEvent { - /** - * The task item representing the task that finished. - */ - readonly execution: TaskExecution; - } - - /** - * An event signaling the start of a process execution - * triggered through a task - */ - export interface TaskProcessStartEvent { - - /** - * The task execution for which the process got started. - */ - readonly execution: TaskExecution; - - /** - * The underlying process id. - */ - readonly processId: number; - } - - /** - * An event signaling the end of a process execution - * triggered through a task - */ - export interface TaskProcessEndEvent { - - /** - * The task execution for which the process got started. - */ - readonly execution: TaskExecution; - - /** - * The process's exit code. Will be `undefined` when the task is terminated. - */ - readonly exitCode: number | undefined; - } - - export interface TaskFilter { - /** - * The task version as used in the tasks.json file. - * The string support the package.json semver notation. - */ - version?: string; - - /** - * The task type to return; - */ - type?: string; - } - - /** - * Namespace for tasks functionality. - */ - export namespace tasks { - - /** - * Register a task provider. - * - * @param type The task kind type this provider is registered for. - * @param provider A task provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; - - /** - * Fetches all tasks available in the systems. This includes tasks - * from `tasks.json` files as well as tasks from task providers - * contributed through extensions. - * - * @param filter Optional filter to select tasks of a certain type or version. - */ - export function fetchTasks(filter?: TaskFilter): Thenable; - - /** - * Executes a task that is managed by the editor. The returned - * task execution can be used to terminate the task. - * - * @throws When running a ShellExecution or a ProcessExecution - * task in an environment where a new process cannot be started. - * In such an environment, only CustomExecution tasks can be run. - * - * @param task the task to execute - */ - export function executeTask(task: Task): Thenable; - - /** - * The currently active task executions or an empty array. - */ - export const taskExecutions: readonly TaskExecution[]; - - /** - * Fires when a task starts. - */ - export const onDidStartTask: Event; - - /** - * Fires when a task ends. - */ - export const onDidEndTask: Event; - - /** - * Fires when the underlying process has been started. - * This event will not fire for tasks that don't - * execute an underlying process. - */ - export const onDidStartTaskProcess: Event; - - /** - * Fires when the underlying process has ended. - * This event will not fire for tasks that don't - * execute an underlying process. - */ - export const onDidEndTaskProcess: Event; - } - - /** - * Enumeration of file types. The types `File` and `Directory` can also be - * a symbolic links, in that case use `FileType.File | FileType.SymbolicLink` and - * `FileType.Directory | FileType.SymbolicLink`. - */ - export enum FileType { - /** - * The file type is unknown. - */ - Unknown = 0, - /** - * A regular file. - */ - File = 1, - /** - * A directory. - */ - Directory = 2, - /** - * A symbolic link to a file. - */ - SymbolicLink = 64 - } - - export enum FilePermission { - /** - * The file is readonly. - * - * *Note:* All `FileStat` from a `FileSystemProvider` that is registered with - * the option `isReadonly: true` will be implicitly handled as if `FilePermission.Readonly` - * is set. As a consequence, it is not possible to have a readonly file system provider - * registered where some `FileStat` are not readonly. - */ - Readonly = 1 - } - - /** - * The `FileStat`-type represents metadata about a file - */ - export interface FileStat { - /** - * The type of the file, e.g. is a regular file, a directory, or symbolic link - * to a file. - * - * *Note:* This value might be a bitmask, e.g. `FileType.File | FileType.SymbolicLink`. - */ - type: FileType; - /** - * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. - */ - ctime: number; - /** - * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. - * - * *Note:* If the file changed, it is important to provide an updated `mtime` that advanced - * from the previous value. Otherwise there may be optimizations in place that will not show - * the updated file contents in an editor for example. - */ - mtime: number; - /** - * The size in bytes. - * - * *Note:* If the file changed, it is important to provide an updated `size`. Otherwise there - * may be optimizations in place that will not show the updated file contents in an editor for - * example. - */ - size: number; - /** - * The permissions of the file, e.g. whether the file is readonly. - * - * *Note:* This value might be a bitmask, e.g. `FilePermission.Readonly | FilePermission.Other`. - */ - permissions?: FilePermission; - } - - /** - * A type that filesystem providers should use to signal errors. - * - * This class has factory methods for common error-cases, like `FileNotFound` when - * a file or folder doesn't exist, use them like so: `throw vscode.FileSystemError.FileNotFound(someUri);` - */ - export class FileSystemError extends Error { - - /** - * Create an error to signal that a file or folder wasn't found. - * @param messageOrUri Message or uri. - */ - static FileNotFound(messageOrUri?: string | Uri): FileSystemError; - - /** - * Create an error to signal that a file or folder already exists, e.g. when - * creating but not overwriting a file. - * @param messageOrUri Message or uri. - */ - static FileExists(messageOrUri?: string | Uri): FileSystemError; - - /** - * Create an error to signal that a file is not a folder. - * @param messageOrUri Message or uri. - */ - static FileNotADirectory(messageOrUri?: string | Uri): FileSystemError; - - /** - * Create an error to signal that a file is a folder. - * @param messageOrUri Message or uri. - */ - static FileIsADirectory(messageOrUri?: string | Uri): FileSystemError; - - /** - * Create an error to signal that an operation lacks required permissions. - * @param messageOrUri Message or uri. - */ - static NoPermissions(messageOrUri?: string | Uri): FileSystemError; - - /** - * Create an error to signal that the file system is unavailable or too busy to - * complete a request. - * @param messageOrUri Message or uri. - */ - static Unavailable(messageOrUri?: string | Uri): FileSystemError; - - /** - * Creates a new filesystem error. - * - * @param messageOrUri Message or uri. - */ - constructor(messageOrUri?: string | Uri); - - /** - * A code that identifies this error. - * - * Possible values are names of errors, like {@linkcode FileSystemError.FileNotFound FileNotFound}, - * or `Unknown` for unspecified errors. - */ - readonly code: string; - } - - /** - * Enumeration of file change types. - */ - export enum FileChangeType { - - /** - * The contents or metadata of a file have changed. - */ - Changed = 1, - - /** - * A file has been created. - */ - Created = 2, - - /** - * A file has been deleted. - */ - Deleted = 3, - } - - /** - * The event filesystem providers must use to signal a file change. - */ - export interface FileChangeEvent { - - /** - * The type of change. - */ - readonly type: FileChangeType; - - /** - * The uri of the file that has changed. - */ - readonly uri: Uri; - } - - /** - * The filesystem provider defines what the editor needs to read, write, discover, - * and to manage files and folders. It allows extensions to serve files from remote places, - * like ftp-servers, and to seamlessly integrate those into the editor. - * - * * *Note 1:* The filesystem provider API works with {@link Uri uris} and assumes hierarchical - * paths, e.g. `foo:/my/path` is a child of `foo:/my/` and a parent of `foo:/my/path/deeper`. - * * *Note 2:* There is an activation event `onFileSystem:` that fires when a file - * or folder is being accessed. - * * *Note 3:* The word 'file' is often used to denote all {@link FileType kinds} of files, e.g. - * folders, symbolic links, and regular files. - */ - export interface FileSystemProvider { - - /** - * An event to signal that a resource has been created, changed, or deleted. This - * event should fire for resources that are being {@link FileSystemProvider.watch watched} - * by clients of this provider. - * - * *Note:* It is important that the metadata of the file that changed provides an - * updated `mtime` that advanced from the previous value in the {@link FileStat stat} and a - * correct `size` value. Otherwise there may be optimizations in place that will not show - * the change in an editor for example. - */ - readonly onDidChangeFile: Event; - - /** - * Subscribes to file change events in the file or folder denoted by `uri`. For folders, - * the option `recursive` indicates whether subfolders, sub-subfolders, etc. should - * be watched for file changes as well. With `recursive: false`, only changes to the - * files that are direct children of the folder should trigger an event. - * - * The `excludes` array is used to indicate paths that should be excluded from file - * watching. It is typically derived from the `files.watcherExclude` setting that - * is configurable by the user. Each entry can be be: - * - the absolute path to exclude - * - a relative path to exclude (for example `build/output`) - * - a simple glob pattern (for example `**​/build`, `output/**`) - * - * It is the file system provider's job to call {@linkcode FileSystemProvider.onDidChangeFile onDidChangeFile} - * for every change given these rules. No event should be emitted for files that match any of the provided - * excludes. - * - * @param uri The uri of the file or folder to be watched. - * @param options Configures the watch. - * @returns A disposable that tells the provider to stop watching the `uri`. - */ - watch(uri: Uri, options: { readonly recursive: boolean; readonly excludes: readonly string[] }): Disposable; - - /** - * Retrieve metadata about a file. - * - * Note that the metadata for symbolic links should be the metadata of the file they refer to. - * Still, the {@link FileType.SymbolicLink SymbolicLink}-type must be used in addition to the actual type, e.g. - * `FileType.SymbolicLink | FileType.Directory`. - * - * @param uri The uri of the file to retrieve metadata about. - * @return The file metadata about the file. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. - */ - stat(uri: Uri): FileStat | Thenable; - - /** - * Retrieve all entries of a {@link FileType.Directory directory}. - * - * @param uri The uri of the folder. - * @return An array of name/type-tuples or a thenable that resolves to such. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. - */ - readDirectory(uri: Uri): [string, FileType][] | Thenable<[string, FileType][]>; - - /** - * Create a new directory (Note, that new files are created via `write`-calls). - * - * @param uri The uri of the new folder. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when the parent of `uri` doesn't exist, e.g. no mkdirp-logic required. - * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists. - * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. - */ - createDirectory(uri: Uri): void | Thenable; - - /** - * Read the entire contents of a file. - * - * @param uri The uri of the file. - * @return An array of bytes or a thenable that resolves to such. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. - */ - readFile(uri: Uri): Uint8Array | Thenable; - - /** - * Write data to a file, replacing its entire contents. - * - * @param uri The uri of the file. - * @param content The new content of the file. - * @param options Defines if missing files should or must be created. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist and `create` is not set. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when the parent of `uri` doesn't exist and `create` is set, e.g. no mkdirp-logic required. - * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists, `create` is set but `overwrite` is not set. - * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. - */ - writeFile(uri: Uri, content: Uint8Array, options: { readonly create: boolean; readonly overwrite: boolean }): void | Thenable; - - /** - * Delete a file. - * - * @param uri The resource that is to be deleted. - * @param options Defines if deletion of folders is recursive. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. - * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. - */ - delete(uri: Uri, options: { readonly recursive: boolean }): void | Thenable; - - /** - * Rename a file or folder. - * - * @param oldUri The existing file. - * @param newUri The new location. - * @param options Defines if existing files should be overwritten. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `oldUri` doesn't exist. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when parent of `newUri` doesn't exist, e.g. no mkdirp-logic required. - * @throws {@linkcode FileSystemError.FileExists FileExists} when `newUri` exists and when the `overwrite` option is not `true`. - * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. - */ - rename(oldUri: Uri, newUri: Uri, options: { readonly overwrite: boolean }): void | Thenable; - - /** - * Copy files or folders. Implementing this function is optional but it will speedup - * the copy operation. - * - * @param source The existing file. - * @param destination The destination location. - * @param options Defines if existing files should be overwritten. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `source` doesn't exist. - * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when parent of `destination` doesn't exist, e.g. no mkdirp-logic required. - * @throws {@linkcode FileSystemError.FileExists FileExists} when `destination` exists and when the `overwrite` option is not `true`. - * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. - */ - copy?(source: Uri, destination: Uri, options: { readonly overwrite: boolean }): void | Thenable; - } - - /** - * The file system interface exposes the editor's built-in and contributed - * {@link FileSystemProvider file system providers}. It allows extensions to work - * with files from the local disk as well as files from remote places, like the - * remote extension host or ftp-servers. - * - * *Note* that an instance of this interface is available as {@linkcode workspace.fs}. - */ - export interface FileSystem { - - /** - * Retrieve metadata about a file. - * - * @param uri The uri of the file to retrieve metadata about. - * @return The file metadata about the file. - */ - stat(uri: Uri): Thenable; - - /** - * Retrieve all entries of a {@link FileType.Directory directory}. - * - * @param uri The uri of the folder. - * @return An array of name/type-tuples or a thenable that resolves to such. - */ - readDirectory(uri: Uri): Thenable<[string, FileType][]>; - - /** - * Create a new directory (Note, that new files are created via `write`-calls). - * - * *Note* that missing directories are created automatically, e.g this call has - * `mkdirp` semantics. - * - * @param uri The uri of the new folder. - */ - createDirectory(uri: Uri): Thenable; - - /** - * Read the entire contents of a file. - * - * @param uri The uri of the file. - * @return An array of bytes or a thenable that resolves to such. - */ - readFile(uri: Uri): Thenable; - - /** - * Write data to a file, replacing its entire contents. - * - * @param uri The uri of the file. - * @param content The new content of the file. - */ - writeFile(uri: Uri, content: Uint8Array): Thenable; - - /** - * Delete a file. - * - * @param uri The resource that is to be deleted. - * @param options Defines if trash can should be used and if deletion of folders is recursive - */ - delete(uri: Uri, options?: { recursive?: boolean; useTrash?: boolean }): Thenable; - - /** - * Rename a file or folder. - * - * @param source The existing file. - * @param target The new location. - * @param options Defines if existing files should be overwritten. - */ - rename(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; - - /** - * Copy files or folders. - * - * @param source The existing file. - * @param target The destination location. - * @param options Defines if existing files should be overwritten. - */ - copy(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; - - /** - * Check if a given file system supports writing files. - * - * Keep in mind that just because a file system supports writing, that does - * not mean that writes will always succeed. There may be permissions issues - * or other errors that prevent writing a file. - * - * @param scheme The scheme of the filesystem, for example `file` or `git`. - * - * @return `true` if the file system supports writing, `false` if it does not - * support writing (i.e. it is readonly), and `undefined` if the editor does not - * know about the filesystem. - */ - isWritableFileSystem(scheme: string): boolean | undefined; - } - - /** - * Defines a port mapping used for localhost inside the webview. - */ - export interface WebviewPortMapping { - /** - * Localhost port to remap inside the webview. - */ - readonly webviewPort: number; - - /** - * Destination port. The `webviewPort` is resolved to this port. - */ - readonly extensionHostPort: number; - } - - /** - * Content settings for a webview. - */ - export interface WebviewOptions { - /** - * Controls whether scripts are enabled in the webview content or not. - * - * Defaults to false (scripts-disabled). - */ - readonly enableScripts?: boolean; - - /** - * Controls whether forms are enabled in the webview content or not. - * - * Defaults to true if {@link WebviewOptions.enableScripts scripts are enabled}. Otherwise defaults to false. - * Explicitly setting this property to either true or false overrides the default. - */ - readonly enableForms?: boolean; - - /** - * Controls whether command uris are enabled in webview content or not. - * - * Defaults to `false` (command uris are disabled). - * - * If you pass in an array, only the commands in the array are allowed. - */ - readonly enableCommandUris?: boolean | readonly string[]; - - /** - * Root paths from which the webview can load local (filesystem) resources using uris from `asWebviewUri` - * - * Default to the root folders of the current workspace plus the extension's install directory. - * - * Pass in an empty array to disallow access to any local resources. - */ - readonly localResourceRoots?: readonly Uri[]; - - /** - * Mappings of localhost ports used inside the webview. - * - * Port mapping allow webviews to transparently define how localhost ports are resolved. This can be used - * to allow using a static localhost port inside the webview that is resolved to random port that a service is - * running on. - * - * If a webview accesses localhost content, we recommend that you specify port mappings even if - * the `webviewPort` and `extensionHostPort` ports are the same. - * - * *Note* that port mappings only work for `http` or `https` urls. Websocket urls (e.g. `ws://localhost:3000`) - * cannot be mapped to another port. - */ - readonly portMapping?: readonly WebviewPortMapping[]; - } - - /** - * Displays html content, similarly to an iframe. - */ - export interface Webview { - /** - * Content settings for the webview. - */ - options: WebviewOptions; - - /** - * HTML contents of the webview. - * - * This should be a complete, valid html document. Changing this property causes the webview to be reloaded. - * - * Webviews are sandboxed from normal extension process, so all communication with the webview must use - * message passing. To send a message from the extension to the webview, use {@linkcode Webview.postMessage postMessage}. - * To send message from the webview back to an extension, use the `acquireVsCodeApi` function inside the webview - * to get a handle to the editor's api and then call `.postMessage()`: - * - * ```html - * - * ``` - * - * To load a resources from the workspace inside a webview, use the {@linkcode Webview.asWebviewUri asWebviewUri} method - * and ensure the resource's directory is listed in {@linkcode WebviewOptions.localResourceRoots}. - * - * Keep in mind that even though webviews are sandboxed, they still allow running scripts and loading arbitrary content, - * so extensions must follow all standard web security best practices when working with webviews. This includes - * properly sanitizing all untrusted input (including content from the workspace) and - * setting a [content security policy](https://aka.ms/vscode-api-webview-csp). - */ - html: string; - - /** - * Fired when the webview content posts a message. - * - * Webview content can post strings or json serializable objects back to an extension. They cannot - * post `Blob`, `File`, `ImageData` and other DOM specific objects since the extension that receives the - * message does not run in a browser environment. - */ - readonly onDidReceiveMessage: Event; - - /** - * Post a message to the webview content. - * - * Messages are only delivered if the webview is live (either visible or in the - * background with `retainContextWhenHidden`). - * - * @param message Body of the message. This must be a string or other json serializable object. - * - * For older versions of vscode, if an `ArrayBuffer` is included in `message`, - * it will not be serialized properly and will not be received by the webview. - * Similarly any TypedArrays, such as a `Uint8Array`, will be very inefficiently - * serialized and will also not be recreated as a typed array inside the webview. - * - * However if your extension targets vscode 1.57+ in the `engines` field of its - * `package.json`, any `ArrayBuffer` values that appear in `message` will be more - * efficiently transferred to the webview and will also be correctly recreated inside - * of the webview. - * - * @return A promise that resolves when the message is posted to a webview or when it is - * dropped because the message was not deliverable. - * - * Returns `true` if the message was posted to the webview. Messages can only be posted to - * live webviews (i.e. either visible webviews or hidden webviews that set `retainContextWhenHidden`). - * - * A response of `true` does not mean that the message was actually received by the webview. - * For example, no message listeners may be have been hooked up inside the webview or the webview may - * have been destroyed after the message was posted but before it was received. - * - * If you want confirm that a message as actually received, you can try having your webview posting a - * confirmation message back to your extension. - */ - postMessage(message: any): Thenable; - - /** - * Convert a uri for the local file system to one that can be used inside webviews. - * - * Webviews cannot directly load resources from the workspace or local file system using `file:` uris. The - * `asWebviewUri` function takes a local `file:` uri and converts it into a uri that can be used inside of - * a webview to load the same resource: - * - * ```ts - * webview.html = `` - * ``` - */ - asWebviewUri(localResource: Uri): Uri; - - /** - * Content security policy source for webview resources. - * - * This is the origin that should be used in a content security policy rule: - * - * ```ts - * `img-src https: ${webview.cspSource} ...;` - * ``` - */ - readonly cspSource: string; - } - - /** - * Content settings for a webview panel. - */ - export interface WebviewPanelOptions { - /** - * Controls if the find widget is enabled in the panel. - * - * Defaults to `false`. - */ - readonly enableFindWidget?: boolean; - - /** - * Controls if the webview panel's content (iframe) is kept around even when the panel - * is no longer visible. - * - * Normally the webview panel's html context is created when the panel becomes visible - * and destroyed when it is hidden. Extensions that have complex state - * or UI can set the `retainContextWhenHidden` to make the editor keep the webview - * context around, even when the webview moves to a background tab. When a webview using - * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. - * When the panel becomes visible again, the context is automatically restored - * in the exact same state it was in originally. You cannot send messages to a - * hidden webview, even with `retainContextWhenHidden` enabled. - * - * `retainContextWhenHidden` has a high memory overhead and should only be used if - * your panel's context cannot be quickly saved and restored. - */ - readonly retainContextWhenHidden?: boolean; - } - - /** - * A panel that contains a webview. - */ - interface WebviewPanel { - /** - * Identifies the type of the webview panel, such as `'markdown.preview'`. - */ - readonly viewType: string; - - /** - * Title of the panel shown in UI. - */ - title: string; - - /** - * Icon for the panel shown in UI. - */ - iconPath?: Uri | { readonly light: Uri; readonly dark: Uri }; - - /** - * {@linkcode Webview} belonging to the panel. - */ - readonly webview: Webview; - - /** - * Content settings for the webview panel. - */ - readonly options: WebviewPanelOptions; - - /** - * Editor position of the panel. This property is only set if the webview is in - * one of the editor view columns. - */ - readonly viewColumn: ViewColumn | undefined; - - /** - * Whether the panel is active (focused by the user). - */ - readonly active: boolean; - - /** - * Whether the panel is visible. - */ - readonly visible: boolean; - - /** - * Fired when the panel's view state changes. - */ - readonly onDidChangeViewState: Event; - - /** - * Fired when the panel is disposed. - * - * This may be because the user closed the panel or because `.dispose()` was - * called on it. - * - * Trying to use the panel after it has been disposed throws an exception. - */ - readonly onDidDispose: Event; - - /** - * Show the webview panel in a given column. - * - * A webview panel may only show in a single column at a time. If it is already showing, this - * method moves it to a new column. - * - * @param viewColumn View column to show the panel in. Shows in the current `viewColumn` if undefined. - * @param preserveFocus When `true`, the webview will not take focus. - */ - reveal(viewColumn?: ViewColumn, preserveFocus?: boolean): void; - - /** - * Dispose of the webview panel. - * - * This closes the panel if it showing and disposes of the resources owned by the webview. - * Webview panels are also disposed when the user closes the webview panel. Both cases - * fire the `onDispose` event. - */ - dispose(): any; - } - - /** - * Event fired when a webview panel's view state changes. - */ - export interface WebviewPanelOnDidChangeViewStateEvent { - /** - * Webview panel whose view state changed. - */ - readonly webviewPanel: WebviewPanel; - } - - /** - * Restore webview panels that have been persisted when vscode shuts down. - * - * There are two types of webview persistence: - * - * - Persistence within a session. - * - Persistence across sessions (across restarts of the editor). - * - * A `WebviewPanelSerializer` is only required for the second case: persisting a webview across sessions. - * - * Persistence within a session allows a webview to save its state when it becomes hidden - * and restore its content from this state when it becomes visible again. It is powered entirely - * by the webview content itself. To save off a persisted state, call `acquireVsCodeApi().setState()` with - * any json serializable object. To restore the state again, call `getState()` - * - * ```js - * // Within the webview - * const vscode = acquireVsCodeApi(); - * - * // Get existing state - * const oldState = vscode.getState() || { value: 0 }; - * - * // Update state - * setState({ value: oldState.value + 1 }) - * ``` - * - * A `WebviewPanelSerializer` extends this persistence across restarts of the editor. When the editor is shutdown, - * it will save off the state from `setState` of all webviews that have a serializer. When the - * webview first becomes visible after the restart, this state is passed to `deserializeWebviewPanel`. - * The extension can then restore the old `WebviewPanel` from this state. - * - * @param T Type of the webview's state. - */ - interface WebviewPanelSerializer { - /** - * Restore a webview panel from its serialized `state`. - * - * Called when a serialized webview first becomes visible. - * - * @param webviewPanel Webview panel to restore. The serializer should take ownership of this panel. The - * serializer must restore the webview's `.html` and hook up all webview events. - * @param state Persisted state from the webview content. - * - * @return Thenable indicating that the webview has been fully restored. - */ - deserializeWebviewPanel(webviewPanel: WebviewPanel, state: T): Thenable; - } - - /** - * A webview based view. - */ - export interface WebviewView { - /** - * Identifies the type of the webview view, such as `'hexEditor.dataView'`. - */ - readonly viewType: string; - - /** - * The underlying webview for the view. - */ - readonly webview: Webview; - - /** - * View title displayed in the UI. - * - * The view title is initially taken from the extension `package.json` contribution. - */ - title?: string; - - /** - * Human-readable string which is rendered less prominently in the title. - */ - description?: string; - - /** - * The badge to display for this webview view. - * To remove the badge, set to undefined. - */ - badge?: ViewBadge | undefined; - - /** - * Event fired when the view is disposed. - * - * Views are disposed when they are explicitly hidden by a user (this happens when a user - * right clicks in a view and unchecks the webview view). - * - * Trying to use the view after it has been disposed throws an exception. - */ - readonly onDidDispose: Event; - - /** - * Tracks if the webview is currently visible. - * - * Views are visible when they are on the screen and expanded. - */ - readonly visible: boolean; - - /** - * Event fired when the visibility of the view changes. - * - * Actions that trigger a visibility change: - * - * - The view is collapsed or expanded. - * - The user switches to a different view group in the sidebar or panel. - * - * Note that hiding a view using the context menu instead disposes of the view and fires `onDidDispose`. - */ - readonly onDidChangeVisibility: Event; - - /** - * Reveal the view in the UI. - * - * If the view is collapsed, this will expand it. - * - * @param preserveFocus When `true` the view will not take focus. - */ - show(preserveFocus?: boolean): void; - } - - /** - * Additional information the webview view being resolved. - * - * @param T Type of the webview's state. - */ - interface WebviewViewResolveContext { - /** - * Persisted state from the webview content. - * - * To save resources, the editor normally deallocates webview documents (the iframe content) that are not visible. - * For example, when the user collapse a view or switches to another top level activity in the sidebar, the - * `WebviewView` itself is kept alive but the webview's underlying document is deallocated. It is recreated when - * the view becomes visible again. - * - * You can prevent this behavior by setting `retainContextWhenHidden` in the `WebviewOptions`. However this - * increases resource usage and should be avoided wherever possible. Instead, you can use persisted state to - * save off a webview's state so that it can be quickly recreated as needed. - * - * To save off a persisted state, inside the webview call `acquireVsCodeApi().setState()` with - * any json serializable object. To restore the state again, call `getState()`. For example: - * - * ```js - * // Within the webview - * const vscode = acquireVsCodeApi(); - * - * // Get existing state - * const oldState = vscode.getState() || { value: 0 }; - * - * // Update state - * setState({ value: oldState.value + 1 }) - * ``` - * - * The editor ensures that the persisted state is saved correctly when a webview is hidden and across - * editor restarts. - */ - readonly state: T | undefined; - } - - /** - * Provider for creating `WebviewView` elements. - */ - export interface WebviewViewProvider { - /** - * Revolves a webview view. - * - * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is - * first loaded or when the user hides and then shows a view again. - * - * @param webviewView Webview view to restore. The provider should take ownership of this view. The - * provider must set the webview's `.html` and hook up all webview events it is interested in. - * @param context Additional metadata about the view being resolved. - * @param token Cancellation token indicating that the view being provided is no longer needed. - * - * @return Optional thenable indicating that the view has been fully resolved. - */ - resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Thenable | void; - } - - /** - * Provider for text based custom editors. - * - * Text based custom editors use a {@linkcode TextDocument} as their data model. This considerably simplifies - * implementing a custom editor as it allows the editor to handle many common operations such as - * undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`. - */ - export interface CustomTextEditorProvider { - - /** - * Resolve a custom editor for a given text resource. - * - * This is called when a user first opens a resource for a `CustomTextEditorProvider`, or if they reopen an - * existing editor using this `CustomTextEditorProvider`. - * - * - * @param document Document for the resource to resolve. - * - * @param webviewPanel The webview panel used to display the editor UI for this resource. - * - * During resolve, the provider must fill in the initial html for the content webview panel and hook up all - * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to - * use later for example in a command. See {@linkcode WebviewPanel} for additional details. - * - * @param token A cancellation token that indicates the result is no longer needed. - * - * @return Thenable indicating that the custom editor has been resolved. - */ - resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; - } - - /** - * Represents a custom document used by a {@linkcode CustomEditorProvider}. - * - * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is - * managed by the editor. 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 the editor 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 the editor that an edit has occurred on an {@linkcode CustomDocument}. - * - * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. - */ - interface CustomDocumentEditEvent { - - /** - * The document that the edit is for. - */ - readonly document: T; - - /** - * Undo the edit operation. - * - * This is invoked by the editor 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 the editor's internal edit stack by `onDidChangeCustomDocument`. - */ - undo(): Thenable | void; - - /** - * Redo the edit operation. - * - * This is invoked by the editor 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 the editor's internal edit stack by `onDidChangeCustomDocument`. - */ - redo(): Thenable | void; - - /** - * Display name describing the edit. - * - * This will be shown to users in the UI for undo/redo operations. - */ - readonly label?: string; - } - - /** - * Event triggered by extensions to signal to the editor that the content of a {@linkcode CustomDocument} - * has changed. - * - * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. - */ - interface CustomDocumentContentChangeEvent { - /** - * The document that the change is for. - */ - readonly document: T; - } - - /** - * A backup for an {@linkcode 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 the editor 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 {@linkcode 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. - * - * If the editor is for a resource from the current workspace, `destination` will point to a file inside - * `ExtensionContext.storagePath`. The parent folder of `destination` may not exist, so make sure to created it - * before writing the backup to this location. - */ - 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 - * from the user's workspace. - */ - readonly backupId: string | undefined; - - /** - * If the URI is an untitled file, this will be populated with the byte data of that file - * - * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in - */ - readonly untitledDocumentData: Uint8Array | undefined; - } - - /** - * Provider for readonly custom editors that use a custom document model. - * - * Custom editors use {@linkcode CustomDocument} as their document model instead of a {@linkcode TextDocument}. - * - * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple - * text based documents, use {@linkcode 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 time an editor for a given resource is opened. The opened - * document is then passed to `resolveCustomEditor` so that the editor can be shown to the user. - * - * Already opened `CustomDocument` are re-used if the user opened additional editors. When 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`. - * - * @param document Document for the resource being resolved. - * - * @param webviewPanel The webview panel used to display the editor UI for this resource. - * - * During resolve, the provider must fill in the initial html for the content webview panel and hook up all - * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to - * use later for example in a command. See {@linkcode WebviewPanel} for additional details. - * - * @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 editable custom editors that use a custom document model. - * - * Custom editors use {@linkcode CustomDocument} as their document model instead of a {@linkcode 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 {@linkcode 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 the editors to be marked 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 the editor's standard keyboard shortcuts. The editor 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 the editor'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 the editor 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 the editor 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 the editor when the user triggers `File: Revert File` in a custom editor. (Note that - * this is only used using the editor'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 the editor 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 approximately one second after the user stops editing the document. If the user - * rapidly edits the document, `backup` will not be invoked until the editing stops. - * - * `backup` is not invoked when `auto save` is enabled (since auto save already persists the 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 the editor has some valid backup. - */ - backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable; - } - - /** - * The clipboard provides read and write access to the system's clipboard. - */ - export interface Clipboard { - - /** - * Read the current clipboard contents as text. - * @returns A thenable that resolves to a string. - */ - readText(): Thenable; - - /** - * Writes text into the clipboard. - * @returns A thenable that resolves when writing happened. - */ - writeText(value: string): Thenable; - } - - /** - * Possible kinds of UI that can use extensions. - */ - export enum UIKind { - - /** - * Extensions are accessed from a desktop application. - */ - Desktop = 1, - - /** - * Extensions are accessed from a web browser. - */ - Web = 2 - } - - /** - * Namespace describing the environment the editor runs in. - */ - export namespace env { - - /** - * The application name of the editor, like 'VS Code'. - */ - export const appName: string; - - /** - * The application root folder from which the editor is running. - * - * *Note* that the value is the empty string when running in an - * environment that has no representation of an application root folder. - */ - export const appRoot: string; - - /** - * The hosted location of the application - * On desktop this is 'desktop' - * In the web this is the specified embedder i.e. 'github.dev', 'codespaces', or 'web' if the embedder - * does not provide that information - */ - export const appHost: string; - - /** - * The custom uri scheme the editor registers to in the operating system. - */ - export const uriScheme: string; - - /** - * Represents the preferred user-language, like `de-CH`, `fr`, or `en-US`. - */ - export const language: string; - - /** - * The system clipboard. - */ - export const clipboard: Clipboard; - - /** - * A unique identifier for the computer. - */ - export const machineId: string; - - /** - * A unique identifier for the current session. - * Changes each time the editor is started. - */ - export const sessionId: string; - - /** - * Indicates that this is a fresh install of the application. - * `true` if within the first day of installation otherwise `false`. - */ - export const isNewAppInstall: boolean; - - /** - * Indicates whether the users has telemetry enabled. - * Can be observed to determine if the extension should send telemetry. - */ - export const isTelemetryEnabled: boolean; - - /** - * An {@link Event} which fires when the user enabled or disables telemetry. - * `true` if the user has enabled telemetry or `false` if the user has disabled telemetry. - */ - export const onDidChangeTelemetryEnabled: Event; - - /** - * The name of a remote. Defined by extensions, popular samples are `wsl` for the Windows - * Subsystem for Linux or `ssh-remote` for remotes using a secure shell. - * - * *Note* that the value is `undefined` when there is no remote extension host but that the - * value is defined in all extension hosts (local and remote) in case a remote extension host - * exists. Use {@link Extension.extensionKind} to know if - * a specific extension runs remote or not. - */ - export const remoteName: string | undefined; - - /** - * The detected default shell for the extension host, this is overridden by the - * `terminal.integrated.defaultProfile` setting for the extension host's platform. Note that in - * environments that do not support a shell the value is the empty string. - */ - export const shell: string; - - /** - * The UI kind property indicates from which UI extensions - * are accessed from. For example, extensions could be accessed - * from a desktop application or a web browser. - */ - export const uiKind: UIKind; - - /** - * Opens a link externally using the default application. Depending on the - * used scheme this can be: - * * a browser (`http:`, `https:`) - * * a mail client (`mailto:`) - * * VSCode itself (`vscode:` from `vscode.env.uriScheme`) - * - * *Note* that {@linkcode window.showTextDocument showTextDocument} is the right - * way to open a text document inside the editor, not this function. - * - * @param target The uri that should be opened. - * @returns A promise indicating if open was successful. - */ - export function openExternal(target: Uri): Thenable; - - /** - * Resolves a uri to a form that is accessible externally. - * - * #### `http:` or `https:` scheme - * - * Resolves an *external* uri, such as a `http:` or `https:` link, from where the extension is running to a - * uri to the same resource on the client machine. - * - * This is a no-op if the extension is running on the client machine. - * - * If the extension is running remotely, this function automatically establishes a port forwarding tunnel - * from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of - * the port forwarding tunnel is managed by the editor and the tunnel can be closed by the user. - * - * *Note* that uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` on them. - * - * #### `vscode.env.uriScheme` - * - * Creates a uri that - if opened in a browser (e.g. via `openExternal`) - will result in a registered {@link UriHandler} - * to trigger. - * - * Extensions should not make any assumptions about the resulting uri and should not alter it in any way. - * Rather, extensions can e.g. use this uri in an authentication flow, by adding the uri as callback query - * argument to the server to authenticate to. - * - * *Note* that if the server decides to add additional query parameters to the uri (e.g. a token or secret), it - * will appear in the uri that is passed to the {@link UriHandler}. - * - * **Example** of an authentication flow: - * ```typescript - * vscode.window.registerUriHandler({ - * handleUri(uri: vscode.Uri): vscode.ProviderResult { - * if (uri.path === '/did-authenticate') { - * console.log(uri.toString()); - * } - * } - * }); - * - * const callableUri = await vscode.env.asExternalUri(vscode.Uri.parse(vscode.env.uriScheme + '://my.extension/did-authenticate')); - * await vscode.env.openExternal(callableUri); - * ``` - * - * *Note* that extensions should not cache the result of `asExternalUri` as the resolved uri may become invalid due to - * a system or user action — for example, in remote cases, a user may close a port forwarding tunnel that was opened by - * `asExternalUri`. - * - * #### Any other scheme - * - * Any other scheme will be handled as if the provided URI is a workspace URI. In that case, the method will return - * a URI which, when handled, will make the editor open the workspace. - * - * @return A uri that can be used on the client machine. - */ - export function asExternalUri(target: Uri): Thenable; - } - - /** - * Namespace for dealing with commands. In short, a command is a function with a - * unique identifier. The function is sometimes also called _command handler_. - * - * Commands can be added to the editor using the {@link commands.registerCommand registerCommand} - * and {@link commands.registerTextEditorCommand registerTextEditorCommand} functions. Commands - * can be executed {@link commands.executeCommand manually} or from a UI gesture. Those are: - * - * * palette - Use the `commands`-section in `package.json` to make a command show in - * the [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette). - * * keybinding - Use the `keybindings`-section in `package.json` to enable - * [keybindings](https://code.visualstudio.com/docs/getstarted/keybindings#_customizing-shortcuts) - * for your extension. - * - * Commands from other extensions and from the editor itself are accessible to an extension. However, - * when invoking an editor command not all argument types are supported. - * - * This is a sample that registers a command handler and adds an entry for that command to the palette. First - * register a command handler with the identifier `extension.sayHello`. - * ```javascript - * commands.registerCommand('extension.sayHello', () => { - * window.showInformationMessage('Hello World!'); - * }); - * ``` - * Second, bind the command identifier to a title under which it will show in the palette (`package.json`). - * ```json - * { - * "contributes": { - * "commands": [{ - * "command": "extension.sayHello", - * "title": "Hello World" - * }] - * } - * } - * ``` - */ - export namespace commands { - - /** - * Registers a command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Registering a command with an existing command identifier twice - * will cause an error. - * - * @param command A unique identifier for the command. - * @param callback A command handler function. - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): Disposable; - - /** - * Registers a text editor command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Text editor commands are different from ordinary {@link commands.registerCommand commands} as - * they only execute when there is an active editor when the command is called. Also, the - * command handler of an editor command has access to the active editor and to an - * {@link TextEditorEdit edit}-builder. Note that the edit-builder is only valid while the - * callback executes. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to an {@link TextEditor editor} and an {@link TextEditorEdit edit}. - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerTextEditorCommand(command: string, callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, thisArg?: any): Disposable; - - /** - * Executes the command denoted by the given command identifier. - * - * * *Note 1:* When executing an editor command not all types are allowed to - * be passed as arguments. Allowed are the primitive types `string`, `boolean`, - * `number`, `undefined`, and `null`, as well as {@linkcode Position}, {@linkcode Range}, {@linkcode Uri} and {@linkcode Location}. - * * *Note 2:* There are no restrictions when executing commands that have been contributed - * by extensions. - * - * @param command Identifier of the command to execute. - * @param rest Parameters passed to the command function. - * @return A thenable that resolves to the returned value of the given command. Returns `undefined` when - * the command handler function doesn't return anything. - */ - export function executeCommand(command: string, ...rest: any[]): Thenable; - - /** - * Retrieve the list of all available commands. Commands starting with an underscore are - * treated as internal commands. - * - * @param filterInternal Set `true` to not see internal commands (starting with an underscore) - * @return Thenable that resolves to a list of command ids. - */ - export function getCommands(filterInternal?: boolean): Thenable; - } - - /** - * Represents the state of a window. - */ - export interface WindowState { - - /** - * Whether the current window is focused. - */ - readonly focused: boolean; - } - - /** - * A uri handler is responsible for handling system-wide {@link Uri uris}. - * - * @see {@link window.registerUriHandler}. - */ - export interface UriHandler { - - /** - * Handle the provided system-wide {@link Uri}. - * - * @see {@link window.registerUriHandler}. - */ - handleUri(uri: Uri): ProviderResult; - } - - /** - * Namespace for dealing with the current window of the editor. That is visible - * and active editors, as well as, UI elements to show messages, selections, and - * asking for user input. - */ - export namespace window { - - /** - * Represents the grid widget within the main editor area - */ - export const tabGroups: TabGroups; - - /** - * The currently active editor or `undefined`. The active editor is the one - * that currently has focus or, when none has focus, the one that has changed - * input most recently. - */ - export let activeTextEditor: TextEditor | undefined; - - /** - * The currently visible editors or an empty array. - */ - export let visibleTextEditors: readonly TextEditor[]; - - /** - * An {@link Event} which fires when the {@link window.activeTextEditor active editor} - * has changed. *Note* that the event also fires when the active editor changes - * to `undefined`. - */ - export const onDidChangeActiveTextEditor: Event; - - /** - * An {@link Event} which fires when the array of {@link window.visibleTextEditors visible editors} - * has changed. - */ - export const onDidChangeVisibleTextEditors: Event; - - /** - * An {@link Event} which fires when the selection in an editor has changed. - */ - export const onDidChangeTextEditorSelection: Event; - - /** - * An {@link Event} which fires when the visible ranges of an editor has changed. - */ - export const onDidChangeTextEditorVisibleRanges: Event; - - /** - * An {@link Event} which fires when the options of an editor have changed. - */ - export const onDidChangeTextEditorOptions: Event; - - /** - * An {@link Event} which fires when the view column of an editor has changed. - */ - export const onDidChangeTextEditorViewColumn: Event; - - /** - * The currently visible {@link NotebookEditor notebook editors} or an empty array. - */ - export const visibleNotebookEditors: readonly NotebookEditor[]; - - /** - * An {@link Event} which fires when the {@link window.visibleNotebookEditors visible notebook editors} - * has changed. - */ - export const onDidChangeVisibleNotebookEditors: Event; - - /** - * The currently active {@link NotebookEditor notebook editor} or `undefined`. The active editor is the one - * that currently has focus or, when none has focus, the one that has changed - * input most recently. - */ - export const activeNotebookEditor: NotebookEditor | undefined; - - /** - * An {@link Event} which fires when the {@link window.activeNotebookEditor active notebook editor} - * has changed. *Note* that the event also fires when the active editor changes - * to `undefined`. - */ - export const onDidChangeActiveNotebookEditor: Event; - - /** - * An {@link Event} which fires when the {@link NotebookEditor.selections notebook editor selections} - * have changed. - */ - export const onDidChangeNotebookEditorSelection: Event; - - /** - * An {@link Event} which fires when the {@link NotebookEditor.visibleRanges notebook editor visible ranges} - * have changed. - */ - export const onDidChangeNotebookEditorVisibleRanges: Event; - - /** - * The currently opened terminals or an empty array. - */ - export const terminals: readonly Terminal[]; - - /** - * The currently active terminal or `undefined`. The active terminal is the one that - * currently has focus or most recently had focus. - */ - export const activeTerminal: Terminal | undefined; - - /** - * An {@link Event} which fires when the {@link window.activeTerminal active terminal} - * has changed. *Note* that the event also fires when the active terminal changes - * to `undefined`. - */ - export const onDidChangeActiveTerminal: Event; - - /** - * An {@link Event} which fires when a terminal has been created, either through the - * {@link window.createTerminal createTerminal} API or commands. - */ - export const onDidOpenTerminal: Event; - - /** - * An {@link Event} which fires when a terminal is disposed. - */ - export const onDidCloseTerminal: Event; - - /** - * An {@link Event} which fires when a {@link Terminal.state terminal's state} has changed. - */ - export const onDidChangeTerminalState: Event; - - /** - * Represents the current window's state. - */ - export const state: WindowState; - - /** - * An {@link Event} which fires when the focus state of the current window - * changes. The value of the event represents whether the window is focused. - */ - export const onDidChangeWindowState: Event; - - /** - * Show the given document in a text editor. A {@link ViewColumn column} can be provided - * to control where the editor is being shown. Might change the {@link window.activeTextEditor active editor}. - * - * @param document A text document to be shown. - * @param column A view column in which the {@link TextEditor editor} should be shown. The default is the {@link ViewColumn.Active active}. - * Columns that do not exist will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. Use {@linkcode ViewColumn.Beside} - * to open the editor to the side of the currently active one. - * @param preserveFocus When `true` the editor will not take focus. - * @return A promise that resolves to an {@link TextEditor editor}. - */ - export function showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; - - /** - * Show the given document in a text editor. {@link TextDocumentShowOptions Options} can be provided - * to control options of the editor is being shown. Might change the {@link window.activeTextEditor active editor}. - * - * @param document A text document to be shown. - * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. - * @return A promise that resolves to an {@link TextEditor editor}. - */ - export function showTextDocument(document: TextDocument, options?: TextDocumentShowOptions): Thenable; - - /** - * A short-hand for `openTextDocument(uri).then(document => showTextDocument(document, options))`. - * - * @see {@link workspace.openTextDocument} - * - * @param uri A resource identifier. - * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. - * @return A promise that resolves to an {@link TextEditor editor}. - */ - export function showTextDocument(uri: Uri, options?: TextDocumentShowOptions): Thenable; - - /** - * Show the given {@link NotebookDocument} in a {@link NotebookEditor notebook editor}. - * - * @param document A text document to be shown. - * @param options {@link NotebookDocumentShowOptions Editor options} to configure the behavior of showing the {@link NotebookEditor notebook editor}. - * - * @return A promise that resolves to an {@link NotebookEditor notebook editor}. - */ - export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; - - /** - * Create a TextEditorDecorationType that can be used to add decorations to text editors. - * - * @param options Rendering options for the decoration type. - * @return A new decoration type instance. - */ - export function createTextEditorDecorationType(options: DecorationRenderOptions): TextEditorDecorationType; - - /** - * Show an information message to users. Optionally provide an array of items which will be presented as - * clickable buttons. - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, ...items: T[]): Thenable; - - /** - * Show an information message to users. Optionally provide an array of items which will be presented as - * clickable buttons. - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show an information message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, ...items: T[]): Thenable; - - /** - * Show an information message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show a warning message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, ...items: T[]): Thenable; - - /** - * Show a warning message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show a warning message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, ...items: T[]): Thenable; - - /** - * Show a warning message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show an error message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, ...items: T[]): Thenable; - - /** - * Show an error message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Show an error message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, ...items: T[]): Thenable; - - /** - * Show an error message. - * - * @see {@link window.showInformationMessage showInformationMessage} - * - * @param message The message to show. - * @param options Configures the behaviour of the message. - * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. - */ - export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; - - /** - * Shows a selection list allowing multiple selections. - * - * @param items An array of strings, or a promise that resolves to an array of strings. - * @param options Configures the behavior of the selection list. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected items or `undefined`. - */ - export function showQuickPick(items: readonly string[] | Thenable, options: QuickPickOptions & { canPickMany: true }, token?: CancellationToken): Thenable; - - /** - * Shows a selection list. - * - * @param items An array of strings, or a promise that resolves to an array of strings. - * @param options Configures the behavior of the selection list. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selection or `undefined`. - */ - export function showQuickPick(items: readonly string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; - - /** - * Shows a selection list allowing multiple selections. - * - * @param items An array of items, or a promise that resolves to an array of items. - * @param options Configures the behavior of the selection list. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected items or `undefined`. - */ - export function showQuickPick(items: readonly T[] | Thenable, options: QuickPickOptions & { canPickMany: true }, token?: CancellationToken): Thenable; - - /** - * Shows a selection list. - * - * @param items An array of items, or a promise that resolves to an array of items. - * @param options Configures the behavior of the selection list. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected item or `undefined`. - */ - export function showQuickPick(items: readonly T[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; - - /** - * Shows a selection list of {@link workspace.workspaceFolders workspace folders} to pick from. - * Returns `undefined` if no folder is open. - * - * @param options Configures the behavior of the workspace folder list. - * @return A promise that resolves to the workspace folder or `undefined`. - */ - export function showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable; - - /** - * Shows a file open dialog to the user which allows to select a file - * for opening-purposes. - * - * @param options Options that control the dialog. - * @returns A promise that resolves to the selected resources or `undefined`. - */ - export function showOpenDialog(options?: OpenDialogOptions): Thenable; - - /** - * Shows a file save dialog to the user which allows to select a file - * for saving-purposes. - * - * @param options Options that control the dialog. - * @returns A promise that resolves to the selected resource or `undefined`. - */ - export function showSaveDialog(options?: SaveDialogOptions): Thenable; - - /** - * Opens an input box to ask the user for input. - * - * The returned value will be `undefined` if the input box was canceled (e.g. pressing ESC). Otherwise the - * returned value will be the string typed by the user or an empty string if the user did not type - * anything but dismissed the input box with OK. - * - * @param options Configures the behavior of the input box. - * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to a string the user provided or to `undefined` in case of dismissal. - */ - export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; - - /** - * Creates a {@link QuickPick} to let the user pick an item from a list - * of items of type T. - * - * Note that in many cases the more convenient {@link window.showQuickPick} - * is easier to use. {@link window.createQuickPick} should be used - * when {@link window.showQuickPick} does not offer the required flexibility. - * - * @return A new {@link QuickPick}. - */ - export function createQuickPick(): QuickPick; - - /** - * Creates a {@link InputBox} to let the user enter some text input. - * - * Note that in many cases the more convenient {@link window.showInputBox} - * is easier to use. {@link window.createInputBox} should be used - * when {@link window.showInputBox} does not offer the required flexibility. - * - * @return A new {@link InputBox}. - */ - export function createInputBox(): InputBox; - - /** - * Creates a new {@link OutputChannel output channel} with the given name and language id - * If language id is not provided, then **Log** is used as default language id. - * - * You can access the visible or active output channel as a {@link TextDocument text document} from {@link window.visibleTextEditors visible editors} or {@link window.activeTextEditor active editor} - * and use the language id to contribute language features like syntax coloring, code lens etc., - * - * @param name Human-readable string which will be used to represent the channel in the UI. - * @param languageId The identifier of the language associated with the channel. - */ - export function createOutputChannel(name: string, languageId?: string): OutputChannel; - - /** - * Create and show a new webview panel. - * - * @param viewType Identifies the type of the webview panel. - * @param title Title of the panel. - * @param showOptions Where to show the webview in the editor. If preserveFocus is set, the new webview will not take focus. - * @param options Settings for the new panel. - * - * @return New webview panel. - */ - export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { readonly viewColumn: ViewColumn; readonly preserveFocus?: boolean }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; - - /** - * Set a message to the status bar. This is a short hand for the more powerful - * status bar {@link window.createStatusBarItem items}. - * - * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. - * @param hideAfterTimeout Timeout in milliseconds after which the message will be disposed. - * @return A disposable which hides the status bar message. - */ - export function setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; - - /** - * Set a message to the status bar. This is a short hand for the more powerful - * status bar {@link window.createStatusBarItem items}. - * - * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. - * @param hideWhenDone Thenable on which completion (resolve or reject) the message will be disposed. - * @return A disposable which hides the status bar message. - */ - export function setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; - - /** - * Set a message to the status bar. This is a short hand for the more powerful - * status bar {@link window.createStatusBarItem items}. - * - * *Note* that status bar messages stack and that they must be disposed when no - * longer used. - * - * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. - * @return A disposable which hides the status bar message. - */ - export function setStatusBarMessage(text: string): Disposable; - - /** - * Show progress in the Source Control viewlet while running the given callback and while - * its returned promise isn't resolve or rejected. - * - * @deprecated Use `withProgress` instead. - * - * @param task A callback returning a promise. Progress increments can be reported with - * the provided {@link Progress}-object. - * @return The thenable the task did return. - */ - export function withScmProgress(task: (progress: Progress) => Thenable): Thenable; - - /** - * Show progress in the editor. Progress is shown while running the given callback - * and while the promise it returned isn't resolved nor rejected. The location at which - * progress should show (and other details) is defined via the passed {@linkcode ProgressOptions}. - * - * @param task A callback returning a promise. Progress state can be reported with - * the provided {@link Progress}-object. - * - * To report discrete progress, use `increment` to indicate how much work has been completed. Each call with - * a `increment` value will be summed up and reflected as overall progress until 100% is reached (a value of - * e.g. `10` accounts for `10%` of work done). - * Note that currently only `ProgressLocation.Notification` is capable of showing discrete progress. - * - * To monitor if the operation has been cancelled by the user, use the provided {@linkcode CancellationToken}. - * Note that currently only `ProgressLocation.Notification` is supporting to show a cancel button to cancel the - * long running operation. - * - * @return The thenable the task-callback returned. - */ - export function withProgress(options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable): Thenable; - - /** - * Creates a status bar {@link StatusBarItem item}. - * - * @param alignment The alignment of the item. - * @param priority The priority of the item. Higher values mean the item should be shown more to the left. - * @return A new status bar item. - */ - export function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem; - - /** - * Creates a status bar {@link StatusBarItem item}. - * - * @param id The unique identifier of the item. - * @param alignment The alignment of the item. - * @param priority The priority of the item. Higher values mean the item should be shown more to the left. - * @return A new status bar item. - */ - export function createStatusBarItem(id: string, alignment?: StatusBarAlignment, priority?: number): StatusBarItem; - - /** - * Creates a {@link Terminal} with a backing shell process. The cwd of the terminal will be the workspace - * directory if it exists. - * - * @param name Optional human-readable string which will be used to represent the terminal in the UI. - * @param shellPath Optional path to a custom shell executable to be used in the terminal. - * @param shellArgs Optional args for the custom shell executable. A string can be used on Windows only which - * allows specifying shell args in - * [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). - * @return A new Terminal. - * @throws When running in an environment where a new process cannot be started. - */ - export function createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): Terminal; - - /** - * Creates a {@link Terminal} with a backing shell process. - * - * @param options A TerminalOptions object describing the characteristics of the new terminal. - * @return A new Terminal. - * @throws When running in an environment where a new process cannot be started. - */ - export function createTerminal(options: TerminalOptions): Terminal; - - /** - * Creates a {@link Terminal} where an extension controls its input and output. - * - * @param options An {@link ExtensionTerminalOptions} object describing - * the characteristics of the new terminal. - * @return A new Terminal. - */ - export function createTerminal(options: ExtensionTerminalOptions): Terminal; - - /** - * Register a {@link TreeDataProvider} for the view contributed using the extension point `views`. - * This will allow you to contribute data to the {@link TreeView} and update if the data changes. - * - * **Note:** To get access to the {@link TreeView} and perform operations on it, use {@link window.createTreeView createTreeView}. - * - * @param viewId Id of the view contributed using the extension point `views`. - * @param treeDataProvider A {@link TreeDataProvider} that provides tree data for the view - */ - export function registerTreeDataProvider(viewId: string, treeDataProvider: TreeDataProvider): Disposable; - - /** - * Create a {@link TreeView} for the view contributed using the extension point `views`. - * @param viewId Id of the view contributed using the extension point `views`. - * @param options Options for creating the {@link TreeView} - * @returns a {@link TreeView}. - */ - export function createTreeView(viewId: string, options: TreeViewOptions): TreeView; - - /** - * Registers a {@link UriHandler uri handler} capable of handling system-wide {@link Uri uris}. - * In case there are multiple windows open, the topmost window will handle the uri. - * A uri handler is scoped to the extension it is contributed from; it will only - * be able to handle uris which are directed to the extension itself. A uri must respect - * the following rules: - * - * - The uri-scheme must be `vscode.env.uriScheme`; - * - The uri-authority must be the extension id (e.g. `my.extension`); - * - The uri-path, -query and -fragment parts are arbitrary. - * - * For example, if the `my.extension` extension registers a uri handler, it will only - * be allowed to handle uris with the prefix `product-name://my.extension`. - * - * An extension can only register a single uri handler in its entire activation lifetime. - * - * * *Note:* There is an activation event `onUri` that fires when a uri directed for - * the current extension is about to be handled. - * - * @param handler The uri handler to register for this extension. - */ - export function registerUriHandler(handler: UriHandler): Disposable; - - /** - * Registers a webview panel serializer. - * - * Extensions that support reviving should have an `"onWebviewPanel:viewType"` activation event and - * make sure that `registerWebviewPanelSerializer` is called during activation. - * - * Only a single serializer may be registered at a time for a given `viewType`. - * - * @param viewType Type of the webview panel that can be serialized. - * @param serializer Webview serializer. - */ - export function registerWebviewPanelSerializer(viewType: string, serializer: WebviewPanelSerializer): Disposable; - - /** - * Register a new provider for webview views. - * - * @param viewId Unique id of the view. This should match the `id` from the - * `views` contribution in the package.json. - * @param provider Provider for the webview views. - * - * @return Disposable that unregisters the provider. - */ - export function registerWebviewViewProvider(viewId: string, provider: WebviewViewProvider, options?: { - /** - * Content settings for the webview created for this view. - */ - readonly webviewOptions?: { - /** - * Controls if the webview element itself (iframe) is kept around even when the view - * is no longer visible. - * - * Normally the webview's html context is created when the view becomes visible - * and destroyed when it is hidden. Extensions that have complex state - * or UI can set the `retainContextWhenHidden` to make the editor keep the webview - * context around, even when the webview moves to a background tab. When a webview using - * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. - * When the view becomes visible again, the context is automatically restored - * in the exact same state it was in originally. You cannot send messages to a - * hidden webview, even with `retainContextWhenHidden` enabled. - * - * `retainContextWhenHidden` has a high memory overhead and should only be used if - * your view's context cannot be quickly saved and restored. - */ - readonly retainContextWhenHidden?: boolean; - }; - }): Disposable; - - /** - * Register a provider for custom editors for the `viewType` contributed by the `customEditors` extension point. - * - * When a custom editor is opened, an `onCustomEditor:viewType` activation event is fired. Your extension - * must register a {@linkcode CustomTextEditorProvider}, {@linkcode CustomReadonlyEditorProvider}, - * {@linkcode CustomEditorProvider}for `viewType` as part of activation. - * - * @param viewType Unique identifier for the custom editor provider. This should match the `viewType` from the - * `customEditors` contribution point. - * @param provider Provider that resolves custom editors. - * @param options Options for the provider. - * - * @return Disposable that unregisters the provider. - */ - export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider | CustomReadonlyEditorProvider | CustomEditorProvider, options?: { - /** - * Content settings for the webview panels created for this custom editor. - */ - 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. - * - * By default, the editor 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 `supportsMultipleEditorsPerDocument` is enabled, users can split and create copies of the custom - * editor. In this case, 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; - - /** - * Register provider that enables the detection and handling of links within the terminal. - * @param provider The provider that provides the terminal links. - * @return Disposable that unregisters the provider. - */ - export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; - - /** - * Registers a provider for a contributed terminal profile. - * @param id The ID of the contributed terminal profile. - * @param provider The terminal profile provider. - */ - export function registerTerminalProfileProvider(id: string, provider: TerminalProfileProvider): Disposable; - /** - * Register a file decoration provider. - * - * @param provider A {@link FileDecorationProvider}. - * @return A {@link Disposable} that unregisters the provider. - */ - export function registerFileDecorationProvider(provider: FileDecorationProvider): Disposable; - - /** - * The currently active color theme as configured in the settings. The active - * theme can be changed via the `workbench.colorTheme` setting. - */ - export let activeColorTheme: ColorTheme; - - /** - * An {@link Event} which fires when the active color theme is changed or has changes. - */ - export const onDidChangeActiveColorTheme: Event; - } - - /** - * Options for creating a {@link TreeView} - */ - export interface TreeViewOptions { - - /** - * A data provider that provides tree data. - */ - treeDataProvider: TreeDataProvider; - - /** - * Whether to show collapse all action or not. - */ - showCollapseAll?: boolean; - - /** - * Whether the tree supports multi-select. When the tree supports multi-select and a command is executed from the tree, - * the first argument to the command is the tree item that the command was executed on and the second argument is an - * array containing all selected tree items. - */ - canSelectMany?: boolean; - - /** - * An optional interface to implement drag and drop in the tree view. - */ - dragAndDropController?: TreeDragAndDropController; - } - - /** - * The event that is fired when an element in the {@link TreeView} is expanded or collapsed - */ - export interface TreeViewExpansionEvent { - - /** - * Element that is expanded or collapsed. - */ - readonly element: T; - - } - - /** - * The event that is fired when there is a change in {@link TreeView.selection tree view's selection} - */ - export interface TreeViewSelectionChangeEvent { - - /** - * Selected elements. - */ - readonly selection: readonly T[]; - - } - - /** - * The event that is fired when there is a change in {@link TreeView.visible tree view's visibility} - */ - export interface TreeViewVisibilityChangeEvent { - - /** - * `true` if the {@link TreeView tree view} is visible otherwise `false`. - */ - readonly visible: boolean; - } - - /** - * A file associated with a {@linkcode DataTransferItem}. - */ - export interface DataTransferFile { - /** - * The name of the file. - */ - readonly name: string; - - /** - * The full file path of the file. - * - * May be `undefined` on web. - */ - readonly uri?: Uri; - - /** - * The full file contents of the file. - */ - data(): Thenable; - } - - /** - * Encapsulates data transferred during drag and drop operations. - */ - export class DataTransferItem { - /** - * Get a string representation of this item. - * - * If {@linkcode DataTransferItem.value} is an object, this returns the result of json stringifying {@linkcode DataTransferItem.value} value. - */ - asString(): Thenable; - - /** - * Try getting the {@link DataTransferFile file} associated with this data transfer item. - * - * Note that the file object is only valid for the scope of the drag and drop operation. - * - * @returns The file for the data transfer or `undefined` if the item is either not a file or the - * file data cannot be accessed. - */ - asFile(): DataTransferFile | undefined; - - /** - * Custom data stored on this item. - * - * You can use `value` to share data across operations. The original object can be retrieved so long as the extension that - * created the `DataTransferItem` runs in the same extension host. - */ - readonly value: any; - - /** - * @param value Custom data stored on this item. Can be retrieved using {@linkcode DataTransferItem.value}. - */ - constructor(value: any); - } - - /** - * A map containing a mapping of the mime type of the corresponding transferred data. - * - * Drag and drop controllers that implement {@link TreeDragAndDropController.handleDrag `handleDrag`} can add additional mime types to the - * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from - * an element in the same drag and drop controller. - */ - export class DataTransfer implements Iterable<[mimeType: string, item: DataTransferItem]> { - /** - * Retrieves the data transfer item for a given mime type. - * - * @param mimeType The mime type to get the data transfer item for, such as `text/plain` or `image/png`. - * - * Special mime types: - * - `text/uri-list` — A string with `toString()`ed Uris separated by `\r\n`. To specify a cursor position in the file, - * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. - */ - get(mimeType: string): DataTransferItem | undefined; - - /** - * Sets a mime type to data transfer item mapping. - * @param mimeType The mime type to set the data for. - * @param value The data transfer item for the given mime type. - */ - set(mimeType: string, value: DataTransferItem): void; - - /** - * Allows iteration through the data transfer items. - * - * @param callbackfn Callback for iteration through the data transfer items. - * @param thisArg The `this` context used when invoking the handler function. - */ - forEach(callbackfn: (item: DataTransferItem, mimeType: string, dataTransfer: DataTransfer) => void, thisArg?: any): void; - - /** - * Get a new iterator with the `[mime, item]` pairs for each element in this data transfer. - */ - [Symbol.iterator](): IterableIterator<[mimeType: string, item: DataTransferItem]>; - } - - /** - * Provides support for drag and drop in `TreeView`. - */ - export interface TreeDragAndDropController { - - /** - * The mime types that the {@link TreeDragAndDropController.handleDrop `handleDrop`} method of this `DragAndDropController` supports. - * This could be well-defined, existing, mime types, and also mime types defined by the extension. - * - * To support drops from trees, you will need to add the mime type of that tree. - * This includes drops from within the same tree. - * The mime type of a tree is recommended to be of the format `application/vnd.code.tree.`. - * - * Use the special `files` mime type to support all types of dropped files {@link DataTransferFile files}, regardless of the file's actual mime type. - * - * To learn the mime type of a dragged item: - * 1. Set up your `DragAndDropController` - * 2. Use the Developer: Set Log Level... command to set the level to "Debug" - * 3. Open the developer tools and drag the item with unknown mime type over your tree. The mime types will be logged to the developer console - * - * Note that mime types that cannot be sent to the extension will be omitted. - */ - readonly dropMimeTypes: readonly string[]; - - /** - * The mime types that the {@link TreeDragAndDropController.handleDrag `handleDrag`} method of this `TreeDragAndDropController` may add to the tree data transfer. - * This could be well-defined, existing, mime types, and also mime types defined by the extension. - * - * The recommended mime type of the tree (`application/vnd.code.tree.`) will be automatically added. - */ - readonly dragMimeTypes: readonly string[]; - - /** - * When the user starts dragging items from this `DragAndDropController`, `handleDrag` will be called. - * Extensions can use `handleDrag` to add their {@link DataTransferItem `DataTransferItem`} items to the drag and drop. - * - * When the items are dropped on **another tree item** in **the same tree**, your `DataTransferItem` objects - * will be preserved. Use the recommended mime type for the tree (`application/vnd.code.tree.`) to add - * tree objects in a data transfer. See the documentation for `DataTransferItem` for how best to take advantage of this. - * - * To add a data transfer item that can be dragged into the editor, use the application specific mime type "text/uri-list". - * The data for "text/uri-list" should be a string with `toString()`ed Uris separated by newlines. To specify a cursor position in the file, - * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. - * - * @param source The source items for the drag and drop operation. - * @param dataTransfer The data transfer associated with this drag. - * @param token A cancellation token indicating that drag has been cancelled. - */ - handleDrag?(source: readonly T[], dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; - - /** - * Called when a drag and drop action results in a drop on the tree that this `DragAndDropController` belongs to. - * - * Extensions should fire {@link TreeDataProvider.onDidChangeTreeData onDidChangeTreeData} for any elements that need to be refreshed. - * - * @param dataTransfer The data transfer items of the source of the drag. - * @param target The target tree element that the drop is occurring on. When undefined, the target is the root. - * @param token A cancellation token indicating that the drop has been cancelled. - */ - handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; - } - - /** - * A badge presenting a value for a view - */ - export interface ViewBadge { - - /** - * A label to present in tooltip for the badge. - */ - readonly tooltip: string; - - /** - * The value to present in the badge. - */ - readonly value: number; - } - - /** - * Represents a Tree view - */ - export interface TreeView extends Disposable { - - /** - * Event that is fired when an element is expanded - */ - readonly onDidExpandElement: Event>; - - /** - * Event that is fired when an element is collapsed - */ - readonly onDidCollapseElement: Event>; - - /** - * Currently selected elements. - */ - readonly selection: readonly T[]; - - /** - * Event that is fired when the {@link TreeView.selection selection} has changed - */ - readonly onDidChangeSelection: Event>; - - /** - * `true` if the {@link TreeView tree view} is visible otherwise `false`. - */ - readonly visible: boolean; - - /** - * Event that is fired when {@link TreeView.visible visibility} has changed - */ - readonly onDidChangeVisibility: Event; - - /** - * An optional human-readable message that will be rendered in the view. - * Setting the message to null, undefined, or empty string will remove the message from the view. - */ - message?: string; - - /** - * The tree view title is initially taken from the extension package.json - * Changes to the title property will be properly reflected in the UI in the title of the view. - */ - title?: string; - - /** - * An optional human-readable description which is rendered less prominently in the title of the view. - * Setting the title description to null, undefined, or empty string will remove the description from the view. - */ - description?: string; - - /** - * The badge to display for this TreeView. - * To remove the badge, set to undefined. - */ - badge?: ViewBadge | undefined; - - /** - * Reveals the given element in the tree view. - * If the tree view is not visible then the tree view is shown and element is revealed. - * - * By default revealed element is selected. - * In order to not to select, set the option `select` to `false`. - * In order to focus, set the option `focus` to `true`. - * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. - * **NOTE:** You can expand only to 3 levels maximum. - * - * **NOTE:** The {@link TreeDataProvider} that the `TreeView` {@link window.createTreeView is registered with} with must implement {@link TreeDataProvider.getParent getParent} method to access this API. - */ - reveal(element: T, options?: { select?: boolean; focus?: boolean; expand?: boolean | number }): Thenable; - } - - /** - * A data provider that provides tree data - */ - export interface TreeDataProvider { - /** - * An optional event to signal that an element or root has changed. - * This will trigger the view to update the changed element/root and its children recursively (if shown). - * To signal that root has changed, do not pass any argument or pass `undefined` or `null`. - */ - onDidChangeTreeData?: Event; - - /** - * Get {@link TreeItem} representation of the `element` - * - * @param element The element for which {@link TreeItem} representation is asked for. - * @return TreeItem representation of the element. - */ - getTreeItem(element: T): TreeItem | Thenable; - - /** - * Get the children of `element` or root if no element is passed. - * - * @param element The element from which the provider gets children. Can be `undefined`. - * @return Children of `element` or root if no element is passed. - */ - getChildren(element?: T): ProviderResult; - - /** - * Optional method to return the parent of `element`. - * Return `null` or `undefined` if `element` is a child of root. - * - * **NOTE:** This method should be implemented in order to access {@link TreeView.reveal reveal} API. - * - * @param element The element for which the parent has to be returned. - * @return Parent of `element`. - */ - getParent?(element: T): ProviderResult; - - /** - * Called on hover to resolve the {@link TreeItem.tooltip TreeItem} property if it is undefined. - * Called on tree item click/open to resolve the {@link TreeItem.command TreeItem} property if it is undefined. - * Only properties that were undefined can be resolved in `resolveTreeItem`. - * Functionality may be expanded later to include being called to resolve other missing - * properties on selection and/or on open. - * - * Will only ever be called once per TreeItem. - * - * onDidChangeTreeData should not be triggered from within resolveTreeItem. - * - * *Note* that this function is called when tree items are already showing in the UI. - * Because of that, no property that changes the presentation (label, description, etc.) - * can be changed. - * - * @param item Undefined properties of `item` should be set then `item` should be returned. - * @param element The object associated with the TreeItem. - * @param token A cancellation token. - * @return The resolved tree item or a thenable that resolves to such. It is OK to return the given - * `item`. When no result is returned, the given `item` will be used. - */ - resolveTreeItem?(item: TreeItem, element: T, token: CancellationToken): ProviderResult; - } - - export class TreeItem { - /** - * A human-readable string describing this item. When `falsy`, it is derived from {@link TreeItem.resourceUri resourceUri}. - */ - label?: string | TreeItemLabel; - - /** - * Optional id for the tree item that has to be unique across tree. The id is used to preserve the selection and expansion state of the tree item. - * - * If not provided, an id is generated using the tree item's label. **Note** that when labels change, ids will change and that selection and expansion state cannot be kept stable anymore. - */ - id?: string; - - /** - * The icon path or {@link ThemeIcon} for the tree item. - * When `falsy`, {@link ThemeIcon.Folder Folder Theme Icon} is assigned, if item is collapsible otherwise {@link ThemeIcon.File File Theme Icon}. - * When a file or folder {@link ThemeIcon} is specified, icon is derived from the current file icon theme for the specified theme icon using {@link TreeItem.resourceUri resourceUri} (if provided). - */ - iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; - - /** - * A human-readable string which is rendered less prominent. - * When `true`, it is derived from {@link TreeItem.resourceUri resourceUri} and when `falsy`, it is not shown. - */ - description?: string | boolean; - - /** - * The {@link Uri} of the resource representing this item. - * - * Will be used to derive the {@link TreeItem.label label}, when it is not provided. - * Will be used to derive the icon from current file icon theme, when {@link TreeItem.iconPath iconPath} has {@link ThemeIcon} value. - */ - resourceUri?: Uri; - - /** - * The tooltip text when you hover over this item. - */ - tooltip?: string | MarkdownString | undefined; - - /** - * The {@link Command} that should be executed when the tree item is selected. - * - * Please use `vscode.open` or `vscode.diff` as command IDs when the tree item is opening - * something in the editor. Using these commands ensures that the resulting editor will - * appear consistent with how other built-in trees open editors. - */ - command?: Command; - - /** - * {@link TreeItemCollapsibleState} of the tree item. - */ - collapsibleState?: TreeItemCollapsibleState; - - /** - * Context value of the tree item. This can be used to contribute item specific actions in the tree. - * For example, a tree item is given a context value as `folder`. When contributing actions to `view/item/context` - * using `menus` extension point, you can specify context value for key `viewItem` in `when` expression like `viewItem == folder`. - * ```json - * "contributes": { - * "menus": { - * "view/item/context": [ - * { - * "command": "extension.deleteFolder", - * "when": "viewItem == folder" - * } - * ] - * } - * } - * ``` - * This will show action `extension.deleteFolder` only for items with `contextValue` is `folder`. - */ - contextValue?: string; - - /** - * Accessibility information used when screen reader interacts with this tree item. - * Generally, a TreeItem has no need to set the `role` of the accessibilityInformation; - * however, there are cases where a TreeItem is not displayed in a tree-like way where setting the `role` may make sense. - */ - accessibilityInformation?: AccessibilityInformation; - - /** - * @param label A human-readable string describing this item - * @param collapsibleState {@link TreeItemCollapsibleState} of the tree item. Default is {@link TreeItemCollapsibleState.None} - */ - constructor(label: string | TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); - - /** - * @param resourceUri The {@link Uri} of the resource representing this item. - * @param collapsibleState {@link TreeItemCollapsibleState} of the tree item. Default is {@link TreeItemCollapsibleState.None} - */ - constructor(resourceUri: Uri, collapsibleState?: TreeItemCollapsibleState); - } - - /** - * Collapsible state of the tree item - */ - export enum TreeItemCollapsibleState { - /** - * Determines an item can be neither collapsed nor expanded. Implies it has no children. - */ - None = 0, - /** - * Determines an item is collapsed - */ - Collapsed = 1, - /** - * Determines an item is expanded - */ - Expanded = 2 - } - - /** - * Label describing the {@link TreeItem Tree item} - */ - export interface TreeItemLabel { - - /** - * A human-readable string describing the {@link TreeItem Tree item}. - */ - 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][]; - } - - /** - * Value-object describing what options a terminal should use. - */ - export interface TerminalOptions { - /** - * A human-readable string which will be used to represent the terminal in the UI. - */ - name?: string; - - /** - * A path to a custom shell executable to be used in the terminal. - */ - shellPath?: string; - - /** - * Args for the custom shell executable. A string can be used on Windows only which allows - * specifying shell args in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). - */ - shellArgs?: string[] | string; - - /** - * A path or Uri for the current working directory to be used for the terminal. - */ - cwd?: string | Uri; - - /** - * Object with environment variables that will be added to the editor process. - */ - env?: { [key: string]: string | null | undefined }; - - /** - * Whether the terminal process environment should be exactly as provided in - * `TerminalOptions.env`. When this is false (default), the environment will be based on the - * window's environment and also apply configured platform settings like - * `terminal.integrated.env.windows` on top. When this is true, the complete environment - * must be provided as nothing will be inherited from the process or any configuration. - */ - strictEnv?: boolean; - - /** - * When enabled the terminal will run the process as normal but not be surfaced to the user - * until `Terminal.show` is called. The typical usage for this is when you need to run - * something that may need interactivity but only want to tell the user about it when - * interaction is needed. Note that the terminals will still be exposed to all extensions - * as normal. - */ - hideFromUser?: boolean; - - /** - * A message to write to the terminal on first launch, note that this is not sent to the - * process but, rather written directly to the terminal. This supports escape sequences such - * a setting text style. - */ - message?: string; - - /** - * The icon path or {@link ThemeIcon} for the terminal. - */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; - - /** - * The icon {@link ThemeColor} for the terminal. - * The `terminal.ansi*` theme keys are - * recommended for the best contrast and consistency across themes. - */ - color?: ThemeColor; - - /** - * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. - */ - location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; - - /** - * Opt-out of the default terminal persistence on restart and reload. - * This will only take effect when `terminal.integrated.enablePersistentSessions` is enabled. - */ - isTransient?: boolean; - } - - /** - * Value-object describing what options a virtual process terminal should use. - */ - export interface ExtensionTerminalOptions { - /** - * A human-readable string which will be used to represent the terminal in the UI. - */ - name: string; - - /** - * An implementation of {@link Pseudoterminal} that allows an extension to - * control a terminal. - */ - pty: Pseudoterminal; - - /** - * The icon path or {@link ThemeIcon} for the terminal. - */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; - - /** - * The icon {@link ThemeColor} for the terminal. - * The standard `terminal.ansi*` theme keys are - * recommended for the best contrast and consistency across themes. - */ - color?: ThemeColor; - - /** - * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. - */ - location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; - - /** - * Opt-out of the default terminal persistence on restart and reload. - * This will only take effect when `terminal.integrated.enablePersistentSessions` is enabled. - */ - isTransient?: boolean; - } - - /** - * Defines the interface of a terminal pty, enabling extensions to control a terminal. - */ - interface Pseudoterminal { - /** - * An event that when fired will write data to the terminal. Unlike - * {@link Terminal.sendText} which sends text to the underlying child - * pseudo-device (the child), this will write the text to parent pseudo-device (the - * _terminal_ itself). - * - * Note writing `\n` will just move the cursor down 1 row, you need to write `\r` as well - * to move the cursor to the left-most cell. - * - * Events fired before {@link Pseudoterminal.open} is called will be be ignored. - * - * **Example:** Write red text to the terminal - * ```typescript - * const writeEmitter = new vscode.EventEmitter(); - * const pty: vscode.Pseudoterminal = { - * onDidWrite: writeEmitter.event, - * open: () => writeEmitter.fire('\x1b[31mHello world\x1b[0m'), - * close: () => {} - * }; - * vscode.window.createTerminal({ name: 'My terminal', pty }); - * ``` - * - * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk - * ```typescript - * writeEmitter.fire('\x1b[10;20H*'); - * ``` - */ - onDidWrite: Event; - - /** - * An event that when fired allows overriding the {@link Pseudoterminal.setDimensions dimensions} of the - * terminal. Note that when set, the overridden dimensions will only take effect when they - * are lower than the actual dimensions of the terminal (ie. there will never be a scroll - * bar). Set to `undefined` for the terminal to go back to the regular dimensions (fit to - * the size of the panel). - * - * Events fired before {@link Pseudoterminal.open} is called will be be ignored. - * - * **Example:** Override the dimensions of a terminal to 20 columns and 10 rows - * ```typescript - * const dimensionsEmitter = new vscode.EventEmitter(); - * const pty: vscode.Pseudoterminal = { - * onDidWrite: writeEmitter.event, - * onDidOverrideDimensions: dimensionsEmitter.event, - * open: () => { - * dimensionsEmitter.fire({ - * columns: 20, - * rows: 10 - * }); - * }, - * close: () => {} - * }; - * vscode.window.createTerminal({ name: 'My terminal', pty }); - * ``` - */ - onDidOverrideDimensions?: Event; - - /** - * An event that when fired will signal that the pty is closed and dispose of the terminal. - * - * Events fired before {@link Pseudoterminal.open} is called will be be ignored. - * - * A number can be used to provide an exit code for the terminal. Exit codes must be - * positive and a non-zero exit codes signals failure which shows a notification for a - * regular terminal and allows dependent tasks to proceed when used with the - * `CustomExecution` API. - * - * **Example:** Exit the terminal when "y" is pressed, otherwise show a notification. - * ```typescript - * const writeEmitter = new vscode.EventEmitter(); - * const closeEmitter = new vscode.EventEmitter(); - * const pty: vscode.Pseudoterminal = { - * onDidWrite: writeEmitter.event, - * onDidClose: closeEmitter.event, - * open: () => writeEmitter.fire('Press y to exit successfully'), - * close: () => {}, - * handleInput: data => { - * if (data !== 'y') { - * vscode.window.showInformationMessage('Something went wrong'); - * } - * closeEmitter.fire(); - * } - * }; - * vscode.window.createTerminal({ name: 'Exit example', pty }); - * ``` - */ - onDidClose?: Event; - - /** - * An event that when fired allows changing the name of the terminal. - * - * Events fired before {@link Pseudoterminal.open} is called will be be ignored. - * - * **Example:** Change the terminal name to "My new terminal". - * ```typescript - * const writeEmitter = new vscode.EventEmitter(); - * const changeNameEmitter = new vscode.EventEmitter(); - * const pty: vscode.Pseudoterminal = { - * onDidWrite: writeEmitter.event, - * onDidChangeName: changeNameEmitter.event, - * open: () => changeNameEmitter.fire('My new terminal'), - * close: () => {} - * }; - * vscode.window.createTerminal({ name: 'My terminal', pty }); - * ``` - */ - onDidChangeName?: Event; - - /** - * Implement to handle when the pty is open and ready to start firing events. - * - * @param initialDimensions The dimensions of the terminal, this will be undefined if the - * terminal panel has not been opened before this is called. - */ - open(initialDimensions: TerminalDimensions | undefined): void; - - /** - * Implement to handle when the terminal is closed by an act of the user. - */ - close(): void; - - /** - * Implement to handle incoming keystrokes in the terminal or when an extension calls - * {@link Terminal.sendText}. `data` contains the keystrokes/text serialized into - * their corresponding VT sequence representation. - * - * @param data The incoming data. - * - * **Example:** Echo input in the terminal. The sequence for enter (`\r`) is translated to - * CRLF to go to a new line and move the cursor to the start of the line. - * ```typescript - * const writeEmitter = new vscode.EventEmitter(); - * const pty: vscode.Pseudoterminal = { - * onDidWrite: writeEmitter.event, - * open: () => {}, - * close: () => {}, - * handleInput: data => writeEmitter.fire(data === '\r' ? '\r\n' : data) - * }; - * vscode.window.createTerminal({ name: 'Local echo', pty }); - * ``` - */ - handleInput?(data: string): void; - - /** - * Implement to handle when the number of rows and columns that fit into the terminal panel - * changes, for example when font size changes or when the panel is resized. The initial - * state of a terminal's dimensions should be treated as `undefined` until this is triggered - * as the size of a terminal isn't known until it shows up in the user interface. - * - * When dimensions are overridden by - * {@link Pseudoterminal.onDidOverrideDimensions onDidOverrideDimensions}, `setDimensions` will - * continue to be called with the regular panel dimensions, allowing the extension continue - * to react dimension changes. - * - * @param dimensions The new dimensions. - */ - setDimensions?(dimensions: TerminalDimensions): void; - } - - /** - * Represents the dimensions of a terminal. - */ - export interface TerminalDimensions { - /** - * The number of columns in the terminal. - */ - readonly columns: number; - - /** - * The number of rows in the terminal. - */ - readonly rows: number; - } - - /** - * Represents how a terminal exited. - */ - export interface TerminalExitStatus { - /** - * The exit code that a terminal exited with, it can have the following values: - * - Zero: the terminal process or custom execution succeeded. - * - Non-zero: the terminal process or custom execution failed. - * - `undefined`: the user forcibly closed the terminal or a custom execution exited - * without providing an exit code. - */ - readonly code: number | undefined; - - /** - * The reason that triggered the exit of a terminal. - */ - readonly reason: TerminalExitReason; - } - - /** - * Terminal exit reason kind. - */ - export enum TerminalExitReason { - /** - * Unknown reason. - */ - Unknown = 0, - - /** - * The window closed/reloaded. - */ - Shutdown = 1, - - /** - * The shell process exited. - */ - Process = 2, - - /** - * The user closed the terminal. - */ - User = 3, - - /** - * An extension disposed the terminal. - */ - Extension = 4, - } - - /** - * A type of mutation that can be applied to an environment variable. - */ - 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 - } - - /** - * A type of mutation and its value to be applied to an environment variable. - */ - 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 extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { - /** - * 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. - * - * @param variable The variable to replace. - * @param value The value to replace the variable with. - */ - 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. - * - * @param variable The variable to append to. - * @param value The value to append to the variable. - */ - 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. - * - * @param variable The variable to prepend. - * @param value The value to prepend to the variable. - */ - prepend(variable: string, value: string): void; - - /** - * Gets the mutator that this collection applies to a variable, if any. - * - * @param variable The variable to get the mutator for. - */ - get(variable: string): EnvironmentVariableMutator | undefined; - - /** - * Iterate over each mutator in this collection. - * - * @param callback Function to execute for each entry. - * @param thisArg The `this` context used when invoking the handler function. - */ - forEach(callback: (variable: string, mutator: EnvironmentVariableMutator, collection: EnvironmentVariableCollection) => any, thisArg?: any): void; - - /** - * Deletes this collection's mutator for a variable. - * - * @param variable The variable to delete the mutator for. - */ - delete(variable: string): void; - - /** - * Clears all mutators from this collection. - */ - clear(): void; - } - - /** - * A location in the editor at which progress information can be shown. It depends on the - * location how progress is visually represented. - */ - export enum ProgressLocation { - - /** - * Show progress for the source control viewlet, as overlay for the icon and as progress bar - * inside the viewlet (when visible). Neither supports cancellation nor discrete progress nor - * a label to describe the operation. - */ - SourceControl = 1, - - /** - * Show progress in the status bar of the editor. Neither supports cancellation nor discrete progress. - * Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax in the progress label. - */ - Window = 10, - - /** - * Show progress as notification with an optional cancel button. Supports to show infinite and discrete - * progress but does not support rendering of icons. - */ - Notification = 15 - } - - /** - * Value-object describing where and how progress should show. - */ - export interface ProgressOptions { - - /** - * The location at which progress should show. - */ - location: ProgressLocation | { viewId: string }; - - /** - * A human-readable string which will be used to describe the - * operation. - */ - title?: string; - - /** - * Controls if a cancel button should show to allow the user to - * cancel the long running operation. Note that currently only - * `ProgressLocation.Notification` is supporting to show a cancel - * button. - */ - cancellable?: boolean; - } - - /** - * A light-weight user input UI that is initially not visible. After - * configuring it through its properties the extension can make it - * visible by calling {@link QuickInput.show}. - * - * There are several reasons why this UI might have to be hidden and - * the extension will be notified through {@link QuickInput.onDidHide}. - * (Examples include: an explicit call to {@link QuickInput.hide}, - * the user pressing Esc, some other input UI opening, etc.) - * - * A user pressing Enter or some other gesture implying acceptance - * of the current state does not automatically hide this UI component. - * It is up to the extension to decide whether to accept the user's input - * and if the UI should indeed be hidden through a call to {@link QuickInput.hide}. - * - * When the extension no longer needs this input UI, it should - * {@link QuickInput.dispose} it to allow for freeing up - * any resources associated with it. - * - * See {@link QuickPick} and {@link InputBox} for concrete UIs. - */ - export interface QuickInput { - - /** - * An optional title. - */ - title: string | undefined; - - /** - * An optional current step count. - */ - step: number | undefined; - - /** - * An optional total step count. - */ - totalSteps: number | undefined; - - /** - * If the UI should allow for user input. Defaults to true. - * - * Change this to false, e.g., while validating user input or - * loading data for the next step in user input. - */ - enabled: boolean; - - /** - * If the UI should show a progress indicator. Defaults to false. - * - * Change this to true, e.g., while loading more data or validating - * user input. - */ - busy: boolean; - - /** - * If the UI should stay open even when loosing UI focus. Defaults to false. - * This setting is ignored on iPad and is always false. - */ - ignoreFocusOut: boolean; - - /** - * Makes the input UI visible in its current configuration. Any other input - * UI will first fire an {@link QuickInput.onDidHide} event. - */ - show(): void; - - /** - * Hides this input UI. This will also fire an {@link QuickInput.onDidHide} - * event. - */ - hide(): void; - - /** - * An event signaling when this input UI is hidden. - * - * There are several reasons why this UI might have to be hidden and - * the extension will be notified through {@link QuickInput.onDidHide}. - * (Examples include: an explicit call to {@link QuickInput.hide}, - * the user pressing Esc, some other input UI opening, etc.) - */ - onDidHide: Event; - - /** - * Dispose of this input UI and any associated resources. If it is still - * visible, it is first hidden. After this call the input UI is no longer - * functional and no additional methods or properties on it should be - * accessed. Instead a new input UI should be created. - */ - dispose(): void; - } - - /** - * A concrete {@link QuickInput} to let the user pick an item from a - * list of items of type T. The items can be filtered through a filter text field and - * there is an option {@link QuickPick.canSelectMany canSelectMany} to allow for - * selecting multiple items. - * - * Note that in many cases the more convenient {@link window.showQuickPick} - * is easier to use. {@link window.createQuickPick} should be used - * when {@link window.showQuickPick} does not offer the required flexibility. - */ - export interface QuickPick extends QuickInput { - - /** - * Current value of the filter text. - */ - value: string; - - /** - * Optional placeholder shown in the filter textbox when no filter has been entered. - */ - placeholder: string | undefined; - - /** - * An event signaling when the value of the filter text has changed. - */ - readonly onDidChangeValue: Event; - - /** - * An event signaling when the user indicated acceptance of the selected item(s). - */ - readonly onDidAccept: Event; - - /** - * Buttons for actions in the UI. - */ - buttons: readonly QuickInputButton[]; - - /** - * An event signaling when a button in the title bar was triggered. - * This event does not fire for buttons on a {@link QuickPickItem}. - */ - readonly onDidTriggerButton: Event; - - /** - * An event signaling when a button in a particular {@link QuickPickItem} was triggered. - * This event does not fire for buttons in the title bar. - */ - readonly onDidTriggerItemButton: Event>; - - /** - * Items to pick from. This can be read and updated by the extension. - */ - items: readonly T[]; - - /** - * If multiple items can be selected at the same time. Defaults to false. - */ - canSelectMany: boolean; - - /** - * If the filter text should also be matched against the description of the items. Defaults to false. - */ - matchOnDescription: boolean; - - /** - * If the filter text should also be matched against the detail of the items. Defaults to false. - */ - matchOnDetail: boolean; - - /* - * An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false. - */ - keepScrollPosition?: boolean; - - /** - * Active items. This can be read and updated by the extension. - */ - activeItems: readonly T[]; - - /** - * An event signaling when the active items have changed. - */ - readonly onDidChangeActive: Event; - - /** - * Selected items. This can be read and updated by the extension. - */ - selectedItems: readonly T[]; - - /** - * An event signaling when the selected items have changed. - */ - readonly onDidChangeSelection: Event; - } - - /** - * A concrete {@link QuickInput} to let the user input a text value. - * - * Note that in many cases the more convenient {@link window.showInputBox} - * is easier to use. {@link window.createInputBox} should be used - * when {@link window.showInputBox} does not offer the required flexibility. - */ - export interface InputBox extends QuickInput { - - /** - * Current input value. - */ - value: string; - - /** - * Selection range in the input value. Defined as tuple of two number where the - * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole - * pre-filled value will be selected, when empty (start equals end) only the cursor will be set, - * otherwise the defined range will be selected. - * - * This property does not get updated when the user types or makes a selection, - * but it can be updated by the extension. - */ - valueSelection: readonly [number, number] | undefined; - - /** - * Optional placeholder shown when no value has been input. - */ - placeholder: string | undefined; - - /** - * If the input value should be hidden. Defaults to false. - */ - password: boolean; - - /** - * An event signaling when the value has changed. - */ - readonly onDidChangeValue: Event; - - /** - * An event signaling when the user indicated acceptance of the input value. - */ - readonly onDidAccept: Event; - - /** - * Buttons for actions in the UI. - */ - buttons: readonly QuickInputButton[]; - - /** - * An event signaling when a button was triggered. - */ - readonly onDidTriggerButton: Event; - - /** - * An optional prompt text providing some ask or explanation to the user. - */ - prompt: string | undefined; - - /** - * An optional validation message indicating a problem with the current input value. - * By returning a string, the InputBox will use a default {@link InputBoxValidationSeverity} of Error. - * Returning undefined clears the validation message. - */ - validationMessage: string | InputBoxValidationMessage | undefined; - } - - /** - * Button for an action in a {@link QuickPick} or {@link InputBox}. - */ - export interface QuickInputButton { - - /** - * Icon for the button. - */ - readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; - - /** - * An optional tooltip. - */ - readonly tooltip?: string | undefined; - } - - /** - * Predefined buttons for {@link QuickPick} and {@link InputBox}. - */ - export class QuickInputButtons { - - /** - * A back button for {@link QuickPick} and {@link InputBox}. - * - * When a navigation 'back' button is needed this one should be used for consistency. - * It comes with a predefined icon, tooltip and location. - */ - static readonly Back: QuickInputButton; - - /** - * @hidden - */ - private constructor(); - } - - /** - * An event signaling when a button in a particular {@link QuickPickItem} was triggered. - * This event does not fire for buttons in the title bar. - */ - export interface QuickPickItemButtonEvent { - /** - * The button that was clicked. - */ - readonly button: QuickInputButton; - /** - * The item that the button belongs to. - */ - readonly item: T; - } - - /** - * An event describing an individual change in the text of a {@link TextDocument document}. - */ - export interface TextDocumentContentChangeEvent { - /** - * The range that got replaced. - */ - readonly range: Range; - /** - * The offset of the range that got replaced. - */ - readonly rangeOffset: number; - /** - * The length of the range that got replaced. - */ - readonly rangeLength: number; - /** - * The new text for the range. - */ - readonly text: string; - } - - export enum TextDocumentChangeReason { - /** The text change is caused by an undo operation. */ - Undo = 1, - - /** The text change is caused by an redo operation. */ - Redo = 2, - } - - /** - * An event describing a transactional {@link TextDocument document} change. - */ - export interface TextDocumentChangeEvent { - - /** - * The affected document. - */ - readonly document: TextDocument; - - /** - * An array of content changes. - */ - readonly contentChanges: readonly TextDocumentContentChangeEvent[]; - - /** - * The reason why the document was changed. - * Is `undefined` if the reason is not known. - */ - readonly reason: TextDocumentChangeReason | undefined; - } - - /** - * Represents reasons why a text document is saved. - */ - export enum TextDocumentSaveReason { - - /** - * Manually triggered, e.g. by the user pressing save, by starting debugging, - * or by an API call. - */ - Manual = 1, - - /** - * Automatic after a delay. - */ - AfterDelay = 2, - - /** - * When the editor lost focus. - */ - FocusOut = 3 - } - - /** - * An event that is fired when a {@link TextDocument document} will be saved. - * - * To make modifications to the document before it is being saved, call the - * {@linkcode TextDocumentWillSaveEvent.waitUntil waitUntil}-function with a thenable - * that resolves to an array of {@link TextEdit text edits}. - */ - export interface TextDocumentWillSaveEvent { - - /** - * The document that will be saved. - */ - readonly document: TextDocument; - - /** - * The reason why save was triggered. - */ - readonly reason: TextDocumentSaveReason; - - /** - * Allows to pause the event loop and to apply {@link TextEdit pre-save-edits}. - * Edits of subsequent calls to this function will be applied in order. The - * edits will be *ignored* if concurrent modifications of the document happened. - * - * *Note:* This function can only be called during event dispatch and not - * in an asynchronous manner: - * - * ```ts - * workspace.onWillSaveTextDocument(event => { - * // async, will *throw* an error - * setTimeout(() => event.waitUntil(promise)); - * - * // sync, OK - * event.waitUntil(promise); - * }) - * ``` - * - * @param thenable A thenable that resolves to {@link TextEdit pre-save-edits}. - */ - waitUntil(thenable: Thenable): void; - - /** - * Allows to pause the event loop until the provided thenable resolved. - * - * *Note:* This function can only be called during event dispatch. - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - } - - /** - * An event that is fired when files are going to be created. - * - * To make modifications to the workspace before the files are created, - * call the {@linkcode FileWillCreateEvent.waitUntil waitUntil}-function with a - * thenable that resolves to a {@link WorkspaceEdit workspace edit}. - */ - export interface FileWillCreateEvent { - - /** - * A cancellation token. - */ - readonly token: CancellationToken; - - /** - * The files that are going to be created. - */ - readonly files: readonly Uri[]; - - /** - * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. - * - * *Note:* This function can only be called during event dispatch and not - * in an asynchronous manner: - * - * ```ts - * workspace.onWillCreateFiles(event => { - * // async, will *throw* an error - * setTimeout(() => event.waitUntil(promise)); - * - * // sync, OK - * event.waitUntil(promise); - * }) - * ``` - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - - /** - * Allows to pause the event until the provided thenable resolves. - * - * *Note:* This function can only be called during event dispatch. - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - } - - /** - * An event that is fired after files are created. - */ - export interface FileCreateEvent { - - /** - * The files that got created. - */ - readonly files: readonly Uri[]; - } - - /** - * An event that is fired when files are going to be deleted. - * - * To make modifications to the workspace before the files are deleted, - * call the {@link FileWillCreateEvent.waitUntil `waitUntil}-function with a - * thenable that resolves to a {@link WorkspaceEdit workspace edit}. - */ - export interface FileWillDeleteEvent { - - /** - * A cancellation token. - */ - readonly token: CancellationToken; - - /** - * The files that are going to be deleted. - */ - readonly files: readonly Uri[]; - - /** - * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. - * - * *Note:* This function can only be called during event dispatch and not - * in an asynchronous manner: - * - * ```ts - * workspace.onWillCreateFiles(event => { - * // async, will *throw* an error - * setTimeout(() => event.waitUntil(promise)); - * - * // sync, OK - * event.waitUntil(promise); - * }) - * ``` - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - - /** - * Allows to pause the event until the provided thenable resolves. - * - * *Note:* This function can only be called during event dispatch. - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - } - - /** - * An event that is fired after files are deleted. - */ - export interface FileDeleteEvent { - - /** - * The files that got deleted. - */ - readonly files: readonly Uri[]; - } - - /** - * An event that is fired when files are going to be renamed. - * - * To make modifications to the workspace before the files are renamed, - * call the {@link FileWillCreateEvent.waitUntil `waitUntil}-function with a - * thenable that resolves to a {@link WorkspaceEdit workspace edit}. - */ - export interface FileWillRenameEvent { - - /** - * A cancellation token. - */ - readonly token: CancellationToken; - - /** - * The files that are going to be renamed. - */ - readonly files: ReadonlyArray<{ readonly oldUri: Uri; readonly newUri: Uri }>; - - /** - * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. - * - * *Note:* This function can only be called during event dispatch and not - * in an asynchronous manner: - * - * ```ts - * workspace.onWillCreateFiles(event => { - * // async, will *throw* an error - * setTimeout(() => event.waitUntil(promise)); - * - * // sync, OK - * event.waitUntil(promise); - * }) - * ``` - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - - /** - * Allows to pause the event until the provided thenable resolves. - * - * *Note:* This function can only be called during event dispatch. - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - } - - /** - * An event that is fired after files are renamed. - */ - export interface FileRenameEvent { - - /** - * The files that got renamed. - */ - readonly files: ReadonlyArray<{ readonly oldUri: Uri; readonly newUri: Uri }>; - } - - /** - * An event describing a change to the set of {@link workspace.workspaceFolders workspace folders}. - */ - export interface WorkspaceFoldersChangeEvent { - /** - * Added workspace folders. - */ - readonly added: readonly WorkspaceFolder[]; - - /** - * Removed workspace folders. - */ - readonly removed: readonly WorkspaceFolder[]; - } - - /** - * A workspace folder is one of potentially many roots opened by the editor. All workspace folders - * are equal which means there is no notion of an active or primary workspace folder. - */ - export interface WorkspaceFolder { - - /** - * The associated uri for this workspace folder. - * - * *Note:* The {@link Uri}-type was intentionally chosen such that future releases of the editor can support - * workspace folders that are not stored on the local disk, e.g. `ftp://server/workspaces/foo`. - */ - readonly uri: Uri; - - /** - * The name of this workspace folder. Defaults to - * the basename of its {@link Uri.path uri-path} - */ - readonly name: string; - - /** - * The ordinal number of this workspace folder. - */ - readonly index: number; - } - - /** - * Namespace for dealing with the current workspace. A workspace is the collection of one - * or more folders that are opened in an editor window (instance). - * - * It is also possible to open an editor without a workspace. For example, when you open a - * new editor window by selecting a file from your platform's File menu, you will not be - * inside a workspace. In this mode, some of the editor's capabilities are reduced but you can - * still open text files and edit them. - * - * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on - * the concept of workspaces. - * - * The workspace offers support for {@link workspace.createFileSystemWatcher listening} to fs - * events and for {@link workspace.findFiles finding} files. Both perform well and run _outside_ - * the editor-process so that they should be always used instead of nodejs-equivalents. - */ - export namespace workspace { - - /** - * A {@link FileSystem file system} instance that allows to interact with local and remote - * files, e.g. `vscode.workspace.fs.readDirectory(someUri)` allows to retrieve all entries - * of a directory or `vscode.workspace.fs.stat(anotherUri)` returns the meta data for a - * file. - */ - export const fs: FileSystem; - - /** - * The uri of the first entry of {@linkcode workspace.workspaceFolders workspaceFolders} - * as `string`. `undefined` if there is no first entry. - * - * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information - * on workspaces. - * - * @deprecated Use {@linkcode workspace.workspaceFolders workspaceFolders} instead. - */ - export const rootPath: string | undefined; - - /** - * List of workspace folders (0-N) that are open in the editor. `undefined` when no workspace - * has been opened. - * - * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information - * on workspaces. - */ - export const workspaceFolders: readonly WorkspaceFolder[] | undefined; - - /** - * The name of the workspace. `undefined` when no workspace - * has been opened. - * - * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on - * the concept of workspaces. - */ - export const name: string | undefined; - - /** - * The location of the workspace file, for example: - * - * `file:///Users/name/Development/myProject.code-workspace` - * - * or - * - * `untitled:1555503116870` - * - * for a workspace that is untitled and not yet saved. - * - * Depending on the workspace that is opened, the value will be: - * * `undefined` when no workspace is opened - * * the path of the workspace file as `Uri` otherwise. if the workspace - * is untitled, the returned URI will use the `untitled:` scheme - * - * The location can e.g. be used with the `vscode.openFolder` command to - * open the workspace again after it has been closed. - * - * **Example:** - * ```typescript - * vscode.commands.executeCommand('vscode.openFolder', uriOfWorkspace); - * ``` - * - * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on - * the concept of workspaces. - * - * **Note:** it is not advised to use `workspace.workspaceFile` to write - * configuration data into the file. You can use `workspace.getConfiguration().update()` - * for that purpose which will work both when a single folder is opened as - * well as an untitled or saved workspace. - */ - export const workspaceFile: Uri | undefined; - - /** - * An event that is emitted when a workspace folder is added or removed. - * - * **Note:** this event will not fire if the first workspace folder is added, removed or changed, - * because in that case the currently executing extensions (including the one that listens to this - * event) will be terminated and restarted so that the (deprecated) `rootPath` property is updated - * to point to the first workspace folder. - */ - export const onDidChangeWorkspaceFolders: Event; - - /** - * Returns the {@link WorkspaceFolder workspace folder} that contains a given uri. - * * returns `undefined` when the given uri doesn't match any workspace folder - * * returns the *input* when the given uri is a workspace folder itself - * - * @param uri An uri. - * @return A workspace folder or `undefined` - */ - export function getWorkspaceFolder(uri: Uri): WorkspaceFolder | undefined; - - /** - * Returns a path that is relative to the workspace folder or folders. - * - * When there are no {@link workspace.workspaceFolders workspace folders} or when the path - * is not contained in them, the input is returned. - * - * @param pathOrUri A path or uri. When a uri is given its {@link Uri.fsPath fsPath} is used. - * @param includeWorkspaceFolder When `true` and when the given path is contained inside a - * workspace folder the name of the workspace is prepended. Defaults to `true` when there are - * multiple workspace folders and `false` otherwise. - * @return A path relative to the root or the input. - */ - export function asRelativePath(pathOrUri: string | Uri, includeWorkspaceFolder?: boolean): string; - - /** - * This method replaces `deleteCount` {@link workspace.workspaceFolders workspace folders} starting at index `start` - * by an optional set of `workspaceFoldersToAdd` on the `vscode.workspace.workspaceFolders` array. This "splice" - * behavior can be used to add, remove and change workspace folders in a single operation. - * - * If the first workspace folder is added, removed or changed, the currently executing extensions (including the - * one that called this method) will be terminated and restarted so that the (deprecated) `rootPath` property is - * updated to point to the first workspace folder. - * - * Use the {@linkcode onDidChangeWorkspaceFolders onDidChangeWorkspaceFolders()} event to get notified when the - * workspace folders have been updated. - * - * **Example:** adding a new workspace folder at the end of workspace folders - * ```typescript - * workspace.updateWorkspaceFolders(workspace.workspaceFolders ? workspace.workspaceFolders.length : 0, null, { uri: ...}); - * ``` - * - * **Example:** removing the first workspace folder - * ```typescript - * workspace.updateWorkspaceFolders(0, 1); - * ``` - * - * **Example:** replacing an existing workspace folder with a new one - * ```typescript - * workspace.updateWorkspaceFolders(0, 1, { uri: ...}); - * ``` - * - * It is valid to remove an existing workspace folder and add it again with a different name - * to rename that folder. - * - * **Note:** it is not valid to call {@link updateWorkspaceFolders updateWorkspaceFolders()} multiple times - * without waiting for the {@linkcode onDidChangeWorkspaceFolders onDidChangeWorkspaceFolders()} to fire. - * - * @param start the zero-based location in the list of currently opened {@link WorkspaceFolder workspace folders} - * from which to start deleting workspace folders. - * @param deleteCount the optional number of workspace folders to remove. - * @param workspaceFoldersToAdd the optional variable set of workspace folders to add in place of the deleted ones. - * Each workspace is identified with a mandatory URI and an optional name. - * @return true if the operation was successfully started and false otherwise if arguments were used that would result - * in invalid workspace folder state (e.g. 2 folders with the same URI). - */ - export function updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, ...workspaceFoldersToAdd: { readonly uri: Uri; readonly name?: string }[]): boolean; - - /** - * Creates a file system watcher that is notified on file events (create, change, delete) - * depending on the parameters provided. - * - * By default, all opened {@link workspace.workspaceFolders workspace folders} will be watched - * for file changes recursively. - * - * Additional paths can be added for file watching by providing a {@link RelativePattern} with - * a `base` path to watch. If the `pattern` is complex (e.g. contains `**` or path segments), - * the path will be watched recursively and otherwise will be watched non-recursively (i.e. only - * changes to the first level of the path will be reported). - * - * *Note* that requests for recursive file watchers for a `base` path that is inside the opened - * workspace are ignored given all opened {@link workspace.workspaceFolders workspace folders} are - * watched for file changes recursively by default. Non-recursive file watchers however are always - * supported, even inside the opened workspace because they allow to bypass the configured settings - * for excludes (`files.watcherExclude`). If you need to watch in a location that is typically - * excluded (for example `node_modules` or `.git` folder), then you can use a non-recursive watcher - * in the workspace for this purpose. - * - * If possible, keep the use of recursive watchers to a minimum because recursive file watching - * is quite resource intense. - * - * Providing a `string` as `globPattern` acts as convenience method for watching file events in - * all opened workspace folders. It cannot be used to add more folders for file watching, nor will - * it report any file events from folders that are not part of the opened workspace folders. - * - * Optionally, flags to ignore certain kinds of events can be provided. - * - * To stop listening to events the watcher must be disposed. - * - * *Note* that file events from recursive file watchers may be excluded based on user configuration. - * The setting `files.watcherExclude` helps to reduce the overhead of file events from folders - * that are known to produce many file changes at once (such as `node_modules` folders). As such, - * it is highly recommended to watch with simple patterns that do not require recursive watchers - * where the exclude settings are ignored and you have full control over the events. - * - * *Note* that symbolic links are not automatically followed for file watching unless the path to - * watch itself is a symbolic link. - * - * *Note* that file changes for the path to be watched may not be delivered when the path itself - * changes. For example, when watching a path `/Users/somename/Desktop` and the path itself is - * being deleted, the watcher may not report an event and may not work anymore from that moment on. - * The underlying behaviour depends on the path that is provided for watching: - * * if the path is within any of the workspace folders, deletions are tracked and reported unless - * excluded via `files.watcherExclude` setting - * * if the path is equal to any of the workspace folders, deletions are not tracked - * * if the path is outside of any of the workspace folders, deletions are not tracked - * - * If you are interested in being notified when the watched path itself is being deleted, you have - * to watch it's parent folder. Make sure to use a simple `pattern` (such as putting the name of the - * folder) to not accidentally watch all sibling folders recursively. - * - * *Note* that the file paths that are reported for having changed may have a different path casing - * compared to the actual casing on disk on case-insensitive platforms (typically macOS and Windows - * but not Linux). We allow a user to open a workspace folder with any desired path casing and try - * to preserve that. This means: - * * if the path is within any of the workspace folders, the path will match the casing of the - * workspace folder up to that portion of the path and match the casing on disk for children - * * if the path is outside of any of the workspace folders, the casing will match the case of the - * path that was provided for watching - * In the same way, symbolic links are preserved, i.e. the file event will report the path of the - * symbolic link as it was provided for watching and not the target. - * - * ### Examples - * - * The basic anatomy of a file watcher is as follows: - * - * ```ts - * const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(, )); - * - * watcher.onDidChange(uri => { ... }); // listen to files being changed - * watcher.onDidCreate(uri => { ... }); // listen to files/folders being created - * watcher.onDidDelete(uri => { ... }); // listen to files/folders getting deleted - * - * watcher.dispose(); // dispose after usage - * ``` - * - * #### Workspace file watching - * - * If you only care about file events in a specific workspace folder: - * - * ```ts - * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], '**​/*.js')); - * ``` - * - * If you want to monitor file events across all opened workspace folders: - * - * ```ts - * vscode.workspace.createFileSystemWatcher('**​/*.js')); - * ``` - * - * *Note:* the array of workspace folders can be empty if no workspace is opened (empty window). - * - * #### Out of workspace file watching - * - * To watch a folder for changes to *.js files outside the workspace (non recursively), pass in a `Uri` to such - * a folder: - * - * ```ts - * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.file(), '*.js')); - * ``` - * - * And use a complex glob pattern to watch recursively: - * - * ```ts - * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.file(), '**​/*.js')); - * ``` - * - * Here is an example for watching the active editor for file changes: - * - * ```ts - * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.window.activeTextEditor.document.uri, '*')); - * ``` - * - * @param globPattern A {@link GlobPattern glob pattern} that controls which file events the watcher should report. - * @param ignoreCreateEvents Ignore when files have been created. - * @param ignoreChangeEvents Ignore when files have been changed. - * @param ignoreDeleteEvents Ignore when files have been deleted. - * @return A new file system watcher instance. Must be disposed when no longer needed. - */ - export function createFileSystemWatcher(globPattern: GlobPattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; - - /** - * Find files across all {@link workspace.workspaceFolders workspace folders} in the workspace. - * - * @example - * findFiles('**​/*.js', '**​/node_modules/**', 10) - * - * @param include A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern - * will be matched against the file paths of resulting matches relative to their workspace. Use a {@link RelativePattern relative pattern} - * to restrict the search results to a {@link WorkspaceFolder workspace folder}. - * @param exclude A {@link GlobPattern glob pattern} 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 file-excludes (e.g. the `files.exclude`-setting - * but not `search.exclude`) will apply. When `null`, no excludes will apply. - * @param maxResults An upper-bound for the result. - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves to an array of resource identifiers. Will return no results if no - * {@link workspace.workspaceFolders workspace folders} are opened. - */ - export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable; - - /** - * Save all dirty files. - * - * @param includeUntitled Also save files that have been created during this session. - * @return A thenable that resolves when the files have been saved. Will return `false` - * for any file that failed to save. - */ - export function saveAll(includeUntitled?: boolean): Thenable; - - /** - * Make changes to one or many resources or create, delete, and rename resources as defined by the given - * {@link WorkspaceEdit workspace edit}. - * - * All changes of a workspace edit are applied in the same order in which they have been added. If - * multiple textual inserts are made at the same position, these strings appear in the resulting text - * in the order the 'inserts' were made, unless that are interleaved with resource edits. Invalid sequences - * like 'delete file a' -> 'insert text in file a' cause failure of the operation. - * - * When applying a workspace edit that consists only of text edits an 'all-or-nothing'-strategy is used. - * A workspace edit with resource creations or deletions aborts the operation, e.g. consecutive edits will - * not be attempted, when a single edit fails. - * - * @param edit A workspace edit. - * @param metadata Optional {@link WorkspaceEditMetadata metadata} for the edit. - * @return A thenable that resolves when the edit could be applied. - */ - export function applyEdit(edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata): Thenable; - - /** - * All text documents currently known to the editor. - */ - export const textDocuments: readonly TextDocument[]; - - /** - * Opens a document. Will return early if this document is already open. Otherwise - * the document is loaded and the {@link workspace.onDidOpenTextDocument didOpen}-event fires. - * - * The document is denoted by an {@link Uri}. Depending on the {@link Uri.scheme scheme} the - * following rules apply: - * * `file`-scheme: Open a file on disk (`openTextDocument(Uri.file(path))`). Will be rejected if the file - * does not exist or cannot be loaded. - * * `untitled`-scheme: Open a blank untitled file with associated path (`openTextDocument(Uri.file(path).with({ scheme: 'untitled' }))`). - * The language will be derived from the file name. - * * For all other schemes contributed {@link TextDocumentContentProvider text document content providers} and - * {@link FileSystemProvider file system providers} are consulted. - * - * *Note* that the lifecycle of the returned document is owned by the editor and not by the extension. That means an - * {@linkcode workspace.onDidCloseTextDocument onDidClose}-event can occur at any time after opening it. - * - * @param uri Identifies the resource to open. - * @return A promise that resolves to a {@link TextDocument document}. - */ - export function openTextDocument(uri: Uri): Thenable; - - /** - * A short-hand for `openTextDocument(Uri.file(fileName))`. - * - * @see {@link workspace.openTextDocument} - * @param fileName A name of a file on disk. - * @return A promise that resolves to a {@link TextDocument document}. - */ - export function openTextDocument(fileName: string): Thenable; - - /** - * Opens an untitled text document. The editor will prompt the user for a file - * path when the document is to be saved. The `options` parameter allows to - * specify the *language* and/or the *content* of the document. - * - * @param options Options to control how the document will be created. - * @return A promise that resolves to a {@link TextDocument document}. - */ - export function openTextDocument(options?: { language?: string; content?: string }): Thenable; - - /** - * Register a text document content provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The uri-scheme to register for. - * @param provider A content provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTextDocumentContentProvider(scheme: string, provider: TextDocumentContentProvider): Disposable; - - /** - * An event that is emitted when a {@link TextDocument text document} is opened or when the language id - * of a text document {@link languages.setTextDocumentLanguage has been changed}. - * - * To add an event listener when a visible text document is opened, use the {@link TextEditor} events in the - * {@link window} namespace. Note that: - * - * - The event is emitted before the {@link TextDocument document} is updated in the - * {@link window.activeTextEditor active text editor} - * - When a {@link TextDocument text document} is already open (e.g.: open in another {@link window.visibleTextEditors visible text editor}) this event is not emitted - * - */ - export const onDidOpenTextDocument: Event; - - /** - * An event that is emitted when a {@link TextDocument text document} is disposed or when the language id - * of a text document {@link languages.setTextDocumentLanguage has been changed}. - * - * *Note 1:* There is no guarantee that this event fires when an editor tab is closed, use the - * {@linkcode window.onDidChangeVisibleTextEditors onDidChangeVisibleTextEditors}-event to know when editors change. - * - * *Note 2:* A document can be open but not shown in an editor which means this event can fire - * for a document that has not been shown in an editor. - */ - export const onDidCloseTextDocument: Event; - - /** - * An event that is emitted when a {@link TextDocument text document} is changed. This usually happens - * when the {@link TextDocument.getText contents} changes but also when other things like the - * {@link TextDocument.isDirty dirty}-state changes. - */ - export const onDidChangeTextDocument: Event; - - /** - * An event that is emitted when a {@link TextDocument text document} will be saved to disk. - * - * *Note 1:* Subscribers can delay saving by registering asynchronous work. For the sake of data integrity the editor - * might save without firing this event. For instance when shutting down with dirty files. - * - * *Note 2:* Subscribers are called sequentially and they can {@link TextDocumentWillSaveEvent.waitUntil delay} saving - * by registering asynchronous work. Protection against misbehaving listeners is implemented as such: - * * there is an overall time budget that all listeners share and if that is exhausted no further listener is called - * * listeners that take a long time or produce errors frequently will not be called anymore - * - * The current thresholds are 1.5 seconds as overall time budget and a listener can misbehave 3 times before being ignored. - */ - export const onWillSaveTextDocument: Event; - - /** - * An event that is emitted when a {@link TextDocument text document} is saved to disk. - */ - export const onDidSaveTextDocument: Event; - - /** - * All notebook documents currently known to the editor. - */ - export const notebookDocuments: readonly NotebookDocument[]; - - /** - * Open a notebook. Will return early if this notebook is already {@link notebook.notebookDocuments loaded}. Otherwise - * the notebook is loaded and the {@linkcode notebook.onDidOpenNotebookDocument onDidOpenNotebookDocument}-event fires. - * - * *Note* that the lifecycle of the returned notebook is owned by the editor and not by the extension. That means an - * {@linkcode notebook.onDidCloseNotebookDocument onDidCloseNotebookDocument}-event can occur at any time after. - * - * *Note* that opening a notebook does not show a notebook editor. This function only returns a notebook document which - * can be shown in a notebook editor but it can also be used for other things. - * - * @param uri The resource to open. - * @returns A promise that resolves to a {@link NotebookDocument notebook} - */ - export function openNotebookDocument(uri: Uri): Thenable; - - /** - * Open an untitled notebook. The editor will prompt the user for a file - * path when the document is to be saved. - * - * @see {@link workspace.openNotebookDocument} - * @param notebookType The notebook type that should be used. - * @param content The initial contents of the notebook. - * @returns A promise that resolves to a {@link NotebookDocument notebook}. - */ - export function openNotebookDocument(notebookType: string, content?: NotebookData): Thenable; - - /** - * An event that is emitted when a {@link NotebookDocument notebook} has changed. - */ - export const onDidChangeNotebookDocument: Event; - - /** - * An event that is emitted when a {@link NotebookDocument notebook} is saved. - */ - export const onDidSaveNotebookDocument: Event; - - /** - * Register a {@link NotebookSerializer notebook serializer}. - * - * A notebook serializer must be contributed through the `notebooks` extension point. When opening a notebook file, the editor will send - * the `onNotebook:` activation event, and extensions must register their serializer in return. - * - * @param notebookType A notebook. - * @param serializer A notebook serializer. - * @param options Optional context options that define what parts of a notebook should be persisted - * @return A {@link Disposable} that unregisters this serializer when being disposed. - */ - export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions): Disposable; - - /** - * An event that is emitted when a {@link NotebookDocument notebook} is opened. - */ - export const onDidOpenNotebookDocument: Event; - - /** - * An event that is emitted when a {@link NotebookDocument notebook} is disposed. - * - * *Note 1:* There is no guarantee that this event fires when an editor tab is closed. - * - * *Note 2:* A notebook can be open but not shown in an editor which means this event can fire - * for a notebook that has not been shown in an editor. - */ - export const onDidCloseNotebookDocument: Event; - - /** - * An event that is emitted when files are being created. - * - * *Note 1:* This event is triggered by user gestures, like creating a file from the - * explorer, or from the {@linkcode workspace.applyEdit}-api. This event is *not* fired when - * files change on disk, e.g triggered by another application, or when using the - * {@linkcode FileSystem workspace.fs}-api. - * - * *Note 2:* When this event is fired, edits to files that are are being created cannot be applied. - */ - export const onWillCreateFiles: Event; - - /** - * An event that is emitted when files have been created. - * - * *Note:* This event is triggered by user gestures, like creating a file from the - * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when - * files change on disk, e.g triggered by another application, or when using the - * {@linkcode FileSystem workspace.fs}-api. - */ - export const onDidCreateFiles: Event; - - /** - * An event that is emitted when files are being deleted. - * - * *Note 1:* This event is triggered by user gestures, like deleting a file from the - * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when - * files change on disk, e.g triggered by another application, or when using the - * {@linkcode FileSystem workspace.fs}-api. - * - * *Note 2:* When deleting a folder with children only one event is fired. - */ - export const onWillDeleteFiles: Event; - - /** - * An event that is emitted when files have been deleted. - * - * *Note 1:* This event is triggered by user gestures, like deleting a file from the - * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when - * files change on disk, e.g triggered by another application, or when using the - * {@linkcode FileSystem workspace.fs}-api. - * - * *Note 2:* When deleting a folder with children only one event is fired. - */ - export const onDidDeleteFiles: Event; - - /** - * An event that is emitted when files are being renamed. - * - * *Note 1:* This event is triggered by user gestures, like renaming a file from the - * explorer, and from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when - * files change on disk, e.g triggered by another application, or when using the - * {@linkcode FileSystem workspace.fs}-api. - * - * *Note 2:* When renaming a folder with children only one event is fired. - */ - export const onWillRenameFiles: Event; - - /** - * An event that is emitted when files have been renamed. - * - * *Note 1:* This event is triggered by user gestures, like renaming a file from the - * explorer, and from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when - * files change on disk, e.g triggered by another application, or when using the - * {@linkcode FileSystem workspace.fs}-api. - * - * *Note 2:* When renaming a folder with children only one event is fired. - */ - export const onDidRenameFiles: Event; - - /** - * Get a workspace configuration object. - * - * When a section-identifier is provided only that part of the configuration - * is returned. Dots in the section-identifier are interpreted as child-access, - * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. - * - * When a scope is provided configuration confined to that scope is returned. Scope can be a resource or a language identifier or both. - * - * @param section A dot-separated identifier. - * @param scope A scope for which the configuration is asked for. - * @return The full configuration or a subset. - */ - export function getConfiguration(section?: string, scope?: ConfigurationScope | null): WorkspaceConfiguration; - - /** - * An event that is emitted when the {@link WorkspaceConfiguration configuration} changed. - */ - export const onDidChangeConfiguration: Event; - - /** - * Register a task provider. - * - * @deprecated Use the corresponding function on the `tasks` namespace instead - * - * @param type The task kind type this provider is registered for. - * @param provider A task provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; - - /** - * Register a filesystem provider for a given scheme, e.g. `ftp`. - * - * There can only be one provider per scheme and an error is being thrown when a scheme - * has been claimed by another provider or when it is reserved. - * - * @param scheme The uri-{@link Uri.scheme scheme} the provider registers for. - * @param provider The filesystem provider. - * @param options Immutable metadata about the provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { readonly isCaseSensitive?: boolean; readonly isReadonly?: boolean }): Disposable; - - /** - * When true, the user has explicitly trusted the contents of the workspace. - */ - export const isTrusted: boolean; - - /** - * Event that fires when the current workspace has been trusted. - */ - export const onDidGrantWorkspaceTrust: Event; - } - - /** - * The configuration scope which can be a - * a 'resource' or a languageId or both or - * a '{@link TextDocument}' or - * a '{@link WorkspaceFolder}' - */ - export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { uri?: Uri; languageId: string }; - - /** - * An event describing the change in Configuration - */ - export interface ConfigurationChangeEvent { - - /** - * Checks if the given section has changed. - * If scope is provided, checks if the section has changed for resources under the given scope. - * - * @param section Configuration name, supports _dotted_ names. - * @param scope A scope in which to check. - * @return `true` if the given section has changed. - */ - affectsConfiguration(section: string, scope?: ConfigurationScope): boolean; - } - - /** - * Namespace for participating in language-specific editor [features](https://code.visualstudio.com/docs/editor/editingevolved), - * like IntelliSense, code actions, diagnostics etc. - * - * Many programming languages exist and there is huge variety in syntaxes, semantics, and paradigms. Despite that, features - * like automatic word-completion, code navigation, or code checking have become popular across different tools for different - * programming languages. - * - * The editor provides an API that makes it simple to provide such common features by having all UI and actions already in place and - * by allowing you to participate by providing data only. For instance, to contribute a hover all you have to do is provide a function - * that can be called with a {@link TextDocument} and a {@link Position} returning hover info. The rest, like tracking the - * mouse, positioning the hover, keeping the hover stable etc. is taken care of by the editor. - * - * ```javascript - * languages.registerHoverProvider('javascript', { - * provideHover(document, position, token) { - * return new Hover('I am a hover!'); - * } - * }); - * ``` - * - * Registration is done using a {@link DocumentSelector document selector} which is either a language id, like `javascript` or - * a more complex {@link DocumentFilter filter} like `{ language: 'typescript', scheme: 'file' }`. Matching a document against such - * a selector will result in a {@link languages.match score} that is used to determine if and how a provider shall be used. When - * scores are equal the provider that came last wins. For features that allow full arity, like {@link languages.registerHoverProvider hover}, - * the score is only checked to be `>0`, for other features, like {@link languages.registerCompletionItemProvider IntelliSense} the - * score is used for determining the order in which providers are asked to participate. - */ - export namespace languages { - - /** - * Return the identifiers of all known languages. - * @return Promise resolving to an array of identifier strings. - */ - export function getLanguages(): Thenable; - - /** - * Set (and change) the {@link TextDocument.languageId language} that is associated - * with the given document. - * - * *Note* that calling this function will trigger the {@linkcode workspace.onDidCloseTextDocument onDidCloseTextDocument} event - * followed by the {@linkcode workspace.onDidOpenTextDocument onDidOpenTextDocument} event. - * - * @param document The document which language is to be changed - * @param languageId The new language identifier. - * @returns A thenable that resolves with the updated document. - */ - export function setTextDocumentLanguage(document: TextDocument, languageId: string): Thenable; - - /** - * Compute the match between a document {@link DocumentSelector selector} and a document. Values - * greater than zero mean the selector matches the document. - * - * A match is computed according to these rules: - * 1. When {@linkcode DocumentSelector} is an array, compute the match for each contained `DocumentFilter` or language identifier and take the maximum value. - * 2. A string will be desugared to become the `language`-part of a {@linkcode DocumentFilter}, so `"fooLang"` is like `{ language: "fooLang" }`. - * 3. A {@linkcode DocumentFilter} will be matched against the document by comparing its parts with the document. The following rules apply: - * 1. When the `DocumentFilter` is empty (`{}`) the result is `0` - * 2. When `scheme`, `language`, `pattern`, or `notebook` are defined but one doesn't match, the result is `0` - * 3. Matching against `*` gives a score of `5`, matching via equality or via a glob-pattern gives a score of `10` - * 4. The result is the maximum value of each match - * - * Samples: - * ```js - * // default document from disk (file-scheme) - * doc.uri; //'file:///my/file.js' - * doc.languageId; // 'javascript' - * match('javascript', doc); // 10; - * match({ language: 'javascript' }, doc); // 10; - * match({ language: 'javascript', scheme: 'file' }, doc); // 10; - * match('*', doc); // 5 - * match('fooLang', doc); // 0 - * match(['fooLang', '*'], doc); // 5 - * - * // virtual document, e.g. from git-index - * doc.uri; // 'git:/my/file.js' - * doc.languageId; // 'javascript' - * match('javascript', doc); // 10; - * match({ language: 'javascript', scheme: 'git' }, doc); // 10; - * match('*', doc); // 5 - * - * // notebook cell document - * doc.uri; // `vscode-notebook-cell:///my/notebook.ipynb#gl65s2pmha`; - * doc.languageId; // 'python' - * match({ notebookType: 'jupyter-notebook' }, doc) // 10 - * match({ notebookType: 'fooNotebook', language: 'python' }, doc) // 0 - * match({ language: 'python' }, doc) // 10 - * match({ notebookType: '*' }, doc) // 5 - * ``` - * - * @param selector A document selector. - * @param document A text document. - * @return A number `>0` when the selector matches and `0` when the selector does not match. - */ - export function match(selector: DocumentSelector, document: TextDocument): number; - - /** - * An {@link Event} which fires when the global set of diagnostics changes. This is - * newly added and removed diagnostics. - */ - export const onDidChangeDiagnostics: Event; - - /** - * Get all diagnostics for a given resource. - * - * @param resource A resource - * @returns An array of {@link Diagnostic diagnostics} objects or an empty array. - */ - export function getDiagnostics(resource: Uri): Diagnostic[]; - - /** - * Get all diagnostics. - * - * @returns An array of uri-diagnostics tuples or an empty array. - */ - export function getDiagnostics(): [Uri, Diagnostic[]][]; - - /** - * Create a diagnostics collection. - * - * @param name The {@link DiagnosticCollection.name name} of the collection. - * @return A new diagnostic collection. - */ - export function createDiagnosticCollection(name?: string): DiagnosticCollection; - - /** - * Creates a new {@link LanguageStatusItem language status item}. - * - * @param id The identifier of the item. - * @param selector The document selector that defines for what editors the item shows. - */ - export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem; - - /** - * Register a completion provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} and groups of equal score are sequentially asked for - * completion items. The process stops when one or many providers of a group return a - * result. A failing provider (rejected promise or exception) will not fail the whole - * operation. - * - * A completion item provider can be associated with a set of `triggerCharacters`. When trigger - * characters are being typed, completions are requested but only from providers that registered - * the typed character. Because of that trigger characters should be different than {@link LanguageConfiguration.wordPattern word characters}, - * a common trigger character is `.` to trigger member completions. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A completion provider. - * @param triggerCharacters Trigger completion when the user types one of the characters. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable; - - /** - * Registers an inline completion provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider An inline completion provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; - - /** - * Register a code action provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A code action provider. - * @param metadata Metadata about the kind of code actions the provider provides. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable; - - /** - * Register a code lens provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A code lens provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; - - /** - * Register a definition provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A definition provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable; - - /** - * Register an implementation provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider An implementation provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerImplementationProvider(selector: DocumentSelector, provider: ImplementationProvider): Disposable; - - /** - * Register a type definition provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A type definition provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; - - /** - * Register a declaration provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A declaration provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDeclarationProvider(selector: DocumentSelector, provider: DeclarationProvider): Disposable; - - /** - * Register a hover provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A hover provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable; - - /** - * Register a provider that locates evaluatable expressions in text documents. - * The editor will evaluate the expression in the active debug session and will show the result in the debug hover. - * - * If multiple providers are registered for a language an arbitrary provider will be used. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An evaluatable expression provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable; - - /** - * Register a provider that returns data for the debugger's 'inline value' feature. - * Whenever the generic debugger has stopped in a source file, providers registered for the language of the file - * are called to return textual data that will be shown in the editor at the end of lines. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider An inline values provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; - - /** - * Register a document highlight provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} and groups sequentially asked for document highlights. - * The process stops when a provider returns a `non-falsy` or `non-failure` result. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A document highlight provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentHighlightProvider(selector: DocumentSelector, provider: DocumentHighlightProvider): Disposable; - - /** - * Register a document symbol provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A document symbol provider. - * @param metaData metadata about the provider - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider, metaData?: DocumentSymbolProviderMetadata): Disposable; - - /** - * Register a workspace symbol 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 provider A workspace symbol provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerWorkspaceSymbolProvider(provider: WorkspaceSymbolProvider): Disposable; - - /** - * Register a reference provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A reference provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerReferenceProvider(selector: DocumentSelector, provider: ReferenceProvider): Disposable; - - /** - * Register a rename provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} and asked in sequence. The first provider producing a result - * defines the result of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A rename provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerRenameProvider(selector: DocumentSelector, provider: RenameProvider): Disposable; - - /** - * Register a semantic tokens provider for a whole document. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} 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 A document semantic tokens provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentSemanticTokensProvider(selector: DocumentSelector, provider: DocumentSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; - - /** - * Register a semantic tokens provider for a document range. - * - * *Note:* If a document has both a `DocumentSemanticTokensProvider` and a `DocumentRangeSemanticTokensProvider`, - * the range provider will be invoked only initially, for the time in which the full document provider takes - * to resolve the first request. Once the full document provider resolves the first request, the semantic tokens - * provided via the range provider will be discarded and from that point forward, only the document provider - * will be used. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} 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 A document range semantic tokens provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentRangeSemanticTokensProvider(selector: DocumentSelector, provider: DocumentRangeSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; - - /** - * Register a formatting provider for a document. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} 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 A document formatting edit provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentFormattingEditProvider(selector: DocumentSelector, provider: DocumentFormattingEditProvider): Disposable; - - /** - * Register a formatting provider for a document range. - * - * *Note:* A document range provider is also a {@link DocumentFormattingEditProvider document formatter} - * which means there is no need to {@link languages.registerDocumentFormattingEditProvider register} a document - * formatter when also registering a range provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} 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 A document range formatting edit provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentRangeFormattingEditProvider(selector: DocumentSelector, provider: DocumentRangeFormattingEditProvider): Disposable; - - /** - * Register a formatting provider that works on type. The provider is active when the user enables the setting `editor.formatOnType`. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} 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 formatting edit provider. - * @param firstTriggerCharacter A character on which formatting should be triggered, like `}`. - * @param moreTriggerCharacter More trigger characters. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerOnTypeFormattingEditProvider(selector: DocumentSelector, provider: OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacter: string[]): Disposable; - - /** - * Register a signature help provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} and called sequentially until a provider returns a - * valid result. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A signature help provider. - * @param triggerCharacters Trigger signature help when the user types one of the characters, like `,` or `(`. - * @param metadata Information about the provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; - export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; - - /** - * Register a document link provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A document link provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable; - - /** - * Register a color provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A color provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; - - /** - * Register a inlay hints provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider An inlay hints provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable; - - /** - * Register a folding range provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. - * If multiple folding ranges start at the same position, only the range of the first registered provider is used. - * If a folding range overlaps with an other range that has a smaller position, it is also ignored. - * - * A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A folding range provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerFoldingRangeProvider(selector: DocumentSelector, provider: FoldingRangeProvider): Disposable; - - /** - * Register a selection range provider. - * - * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. - * @param provider A selection range provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; - - /** - * Register a call hierarchy provider. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A call hierarchy provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable; - - /** - * Register a type hierarchy provider. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A type hierarchy provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTypeHierarchyProvider(selector: DocumentSelector, provider: TypeHierarchyProvider): Disposable; - - /** - * Register a linked editing range provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} and the best-matching provider that has a result 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 A linked editing range provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerLinkedEditingRangeProvider(selector: DocumentSelector, provider: LinkedEditingRangeProvider): Disposable; - - /** - * Registers a new {@link DocumentDropEditProvider}. - * - * @param selector A selector that defines the documents this provider applies to. - * @param provider A drop provider. - * - * @return A {@link Disposable} that unregisters this provider when disposed of. - */ - export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider): Disposable; - - /** - * Set a {@link LanguageConfiguration language configuration} for a language. - * - * @param language A language identifier like `typescript`. - * @param configuration Language configuration. - * @return A {@link Disposable} that unsets this configuration. - */ - export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; - } - - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - */ - export enum NotebookEditorRevealType { - /** - * The range will be revealed with as little scrolling as possible. - */ - Default = 0, - - /** - * The range will always be revealed in the center of the viewport. - */ - InCenter = 1, - - /** - * If the range is outside the viewport, it will be revealed in the center of the viewport. - * Otherwise, it will be revealed with as little scrolling as possible. - */ - InCenterIfOutsideViewport = 2, - - /** - * The range will always be revealed at the top of the viewport. - */ - AtTop = 3 - } - - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - * Additional properties of the NotebookEditor are available in the proposed - * API, which will be finalized later. - */ - export interface NotebookEditor { - - /** - * The {@link NotebookDocument notebook document} associated with this notebook editor. - */ - readonly notebook: NotebookDocument; - - /** - * The primary selection in this notebook editor. - */ - selection: NotebookRange; - - /** - * All selections in this notebook editor. - * - * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; - */ - selections: readonly NotebookRange[]; - - /** - * The current visible ranges in the editor (vertically). - */ - readonly visibleRanges: readonly NotebookRange[]; - - /** - * The column in which this editor shows. - */ - readonly viewColumn?: ViewColumn; - - /** - * Scroll as indicated by `revealType` in order to reveal the given range. - * - * @param range A range. - * @param revealType The scrolling strategy for revealing `range`. - */ - revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; - } - - /** - * Renderer messaging is used to communicate with a single renderer. It's returned from {@link notebooks.createRendererMessaging}. - */ - export interface NotebookRendererMessaging { - /** - * An event that fires when a message is received from a renderer. - */ - readonly onDidReceiveMessage: Event<{ - readonly editor: NotebookEditor; - readonly message: any; - }>; - - /** - * Send a message to one or all renderer. - * - * @param message Message to send - * @param editor Editor to target with the message. If not provided, the - * message is sent to all renderers. - * @returns a boolean indicating whether the message was successfully - * delivered to any renderer. - */ - postMessage(message: any, editor?: NotebookEditor): Thenable; - } - - /** - * A notebook cell kind. - */ - export enum NotebookCellKind { - - /** - * A markup-cell is formatted source that is used for display. - */ - Markup = 1, - - /** - * A code-cell is source that can be {@link NotebookController executed} and that - * produces {@link NotebookCellOutput output}. - */ - Code = 2 - } - - /** - * Represents a cell of a {@link NotebookDocument notebook}, either a {@link NotebookCellKind.Code code}-cell - * or {@link NotebookCellKind.Markup markup}-cell. - * - * NotebookCell instances are immutable and are kept in sync for as long as they are part of their notebook. - */ - export interface NotebookCell { - - /** - * The index of this cell in its {@link NotebookDocument.cellAt containing notebook}. The - * index is updated when a cell is moved within its notebook. The index is `-1` - * when the cell has been removed from its notebook. - */ - readonly index: number; - - /** - * The {@link NotebookDocument notebook} that contains this cell. - */ - readonly notebook: NotebookDocument; - - /** - * The kind of this cell. - */ - readonly kind: NotebookCellKind; - - /** - * The {@link TextDocument text} of this cell, represented as text document. - */ - readonly document: TextDocument; - - /** - * The metadata of this cell. Can be anything but must be JSON-stringifyable. - */ - readonly metadata: { readonly [key: string]: any }; - - /** - * The outputs of this cell. - */ - readonly outputs: readonly NotebookCellOutput[]; - - /** - * The most recent {@link NotebookCellExecutionSummary execution summary} for this cell. - */ - readonly executionSummary: NotebookCellExecutionSummary | undefined; - } - - /** - * Represents a notebook which itself is a sequence of {@link NotebookCell code or markup cells}. Notebook documents are - * created from {@link NotebookData notebook data}. - */ - export interface NotebookDocument { - - /** - * The associated uri for this notebook. - * - * *Note* that most notebooks use the `file`-scheme, which means they are files on disk. However, **not** all notebooks are - * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. - * - * @see {@link FileSystemProvider} - */ - readonly uri: Uri; - - /** - * The type of notebook. - */ - readonly notebookType: string; - - /** - * The version number of this notebook (it will strictly increase after each - * change, including undo/redo). - */ - readonly version: number; - - /** - * `true` if there are unpersisted changes. - */ - readonly isDirty: boolean; - - /** - * Is this notebook representing an untitled file which has not been saved yet. - */ - readonly isUntitled: boolean; - - /** - * `true` if the notebook has been closed. A closed notebook isn't synchronized anymore - * and won't be re-used when the same resource is opened again. - */ - readonly isClosed: boolean; - - /** - * Arbitrary metadata for this notebook. Can be anything but must be JSON-stringifyable. - */ - readonly metadata: { [key: string]: any }; - - /** - * The number of cells in the notebook. - */ - readonly cellCount: number; - - /** - * Return the cell at the specified index. The index will be adjusted to the notebook. - * - * @param index - The index of the cell to retrieve. - * @return A {@link NotebookCell cell}. - */ - cellAt(index: number): NotebookCell; - - /** - * Get the cells of this notebook. A subset can be retrieved by providing - * a range. The range will be adjusted to the notebook. - * - * @param range A notebook range. - * @returns The cells contained by the range or all cells. - */ - getCells(range?: NotebookRange): NotebookCell[]; - - /** - * Save the document. The saving will be handled by the corresponding {@link NotebookSerializer serializer}. - * - * @return A promise that will resolve to true when the document - * has been saved. Will return false if the file was not dirty or when save failed. - */ - save(): Thenable; - } - - /** - * Describes a change to a notebook cell. - * - * @see {@link NotebookDocumentChangeEvent} - */ - export interface NotebookDocumentCellChange { - - /** - * The affected cell. - */ - readonly cell: NotebookCell; - - /** - * The document of the cell or `undefined` when it did not change. - * - * *Note* that you should use the {@link workspace.onDidChangeTextDocument onDidChangeTextDocument}-event - * for detailed change information, like what edits have been performed. - */ - readonly document: TextDocument | undefined; - - /** - * The new metadata of the cell or `undefined` when it did not change. - */ - readonly metadata: { [key: string]: any } | undefined; - - /** - * The new outputs of the cell or `undefined` when they did not change. - */ - readonly outputs: readonly NotebookCellOutput[] | undefined; - - /** - * The new execution summary of the cell or `undefined` when it did not change. - */ - readonly executionSummary: NotebookCellExecutionSummary | undefined; - } - - /** - * Describes a structural change to a notebook document, e.g newly added and removed cells. - * - * @see {@link NotebookDocumentChangeEvent} - */ - export interface NotebookDocumentContentChange { - - /** - * The range at which cells have been either added or removed. - * - * Note that no cells have been {@link NotebookDocumentContentChange.removedCells removed} - * when this range is {@link NotebookRange.isEmpty empty}. - */ - readonly range: NotebookRange; - - /** - * Cells that have been added to the document. - */ - readonly addedCells: readonly NotebookCell[]; - - /** - * Cells that have been removed from the document. - */ - readonly removedCells: readonly NotebookCell[]; - } - - /** - * An event describing a transactional {@link NotebookDocument notebook} change. - */ - export interface NotebookDocumentChangeEvent { - - /** - * The affected notebook. - */ - readonly notebook: NotebookDocument; - - /** - * The new metadata of the notebook or `undefined` when it did not change. - */ - readonly metadata: { [key: string]: any } | undefined; - - /** - * An array of content changes describing added or removed {@link NotebookCell cells}. - */ - readonly contentChanges: readonly NotebookDocumentContentChange[]; - - /** - * An array of {@link NotebookDocumentCellChange cell changes}. - */ - readonly cellChanges: readonly NotebookDocumentCellChange[]; - } - - /** - * The summary of a notebook cell execution. - */ - export interface NotebookCellExecutionSummary { - - /** - * The order in which the execution happened. - */ - readonly executionOrder?: number; - - /** - * If the execution finished successfully. - */ - readonly success?: boolean; - - /** - * The times at which execution started and ended, as unix timestamps - */ - readonly timing?: { readonly startTime: number; readonly endTime: number }; - } - - /** - * A notebook range represents an ordered pair of two cell indices. - * It is guaranteed that start is less than or equal to end. - */ - export class NotebookRange { - - /** - * The zero-based start index of this range. - */ - readonly start: number; - - /** - * The exclusive end index of this range (zero-based). - */ - readonly end: number; - - /** - * `true` if `start` and `end` are equal. - */ - readonly isEmpty: boolean; - - /** - * Create a new notebook range. If `start` is not - * before or equal to `end`, the values will be swapped. - * - * @param start start index - * @param end end index. - */ - constructor(start: number, end: number); - - /** - * Derive a new range for this range. - * - * @param change An object that describes a change to this range. - * @return A range that reflects the given change. Will return `this` range if the change - * is not changing anything. - */ - with(change: { start?: number; end?: number }): NotebookRange; - } - - /** - * One representation of a {@link NotebookCellOutput notebook output}, defined by MIME type and data. - */ - export class NotebookCellOutputItem { - - /** - * Factory function to create a `NotebookCellOutputItem` from a string. - * - * *Note* that an UTF-8 encoder is used to create bytes for the string. - * - * @param value A string. - * @param mime Optional MIME type, defaults to `text/plain`. - * @returns A new output item object. - */ - static text(value: string, mime?: string): NotebookCellOutputItem; - - /** - * Factory function to create a `NotebookCellOutputItem` from - * a JSON object. - * - * *Note* that this function is not expecting "stringified JSON" but - * an object that can be stringified. This function will throw an error - * when the passed value cannot be JSON-stringified. - * - * @param value A JSON-stringifyable value. - * @param mime Optional MIME type, defaults to `application/json` - * @returns A new output item object. - */ - static json(value: any, mime?: string): NotebookCellOutputItem; - - /** - * Factory function to create a `NotebookCellOutputItem` that uses - * uses the `application/vnd.code.notebook.stdout` mime type. - * - * @param value A string. - * @returns A new output item object. - */ - static stdout(value: string): NotebookCellOutputItem; - - /** - * Factory function to create a `NotebookCellOutputItem` that uses - * uses the `application/vnd.code.notebook.stderr` mime type. - * - * @param value A string. - * @returns A new output item object. - */ - static stderr(value: string): NotebookCellOutputItem; - - /** - * Factory function to create a `NotebookCellOutputItem` that uses - * uses the `application/vnd.code.notebook.error` mime type. - * - * @param value An error object. - * @returns A new output item object. - */ - static error(value: Error): NotebookCellOutputItem; - - /** - * The mime type which determines how the {@linkcode NotebookCellOutputItem.data data}-property - * is interpreted. - * - * Notebooks have built-in support for certain mime-types, extensions can add support for new - * types and override existing types. - */ - mime: string; - - /** - * The data of this output item. Must always be an array of unsigned 8-bit integers. - */ - data: Uint8Array; - - /** - * Create a new notebook cell output item. - * - * @param data The value of the output item. - * @param mime The mime type of the output item. - */ - constructor(data: Uint8Array, mime: string); - } - - /** - * Notebook cell output represents a result of executing a cell. It is a container type for multiple - * {@link NotebookCellOutputItem output items} where contained items represent the same result but - * use different MIME types. - */ - export class NotebookCellOutput { - - /** - * The output items of this output. Each item must represent the same result. _Note_ that repeated - * MIME types per output is invalid and that the editor will just pick one of them. - * - * ```ts - * new vscode.NotebookCellOutput([ - * vscode.NotebookCellOutputItem.text('Hello', 'text/plain'), - * vscode.NotebookCellOutputItem.text('Hello', 'text/html'), - * vscode.NotebookCellOutputItem.text('_Hello_', 'text/markdown'), - * vscode.NotebookCellOutputItem.text('Hey', 'text/plain'), // INVALID: repeated type, editor will pick just one - * ]) - * ``` - */ - items: NotebookCellOutputItem[]; - - /** - * Arbitrary metadata for this cell output. Can be anything but must be JSON-stringifyable. - */ - metadata?: { [key: string]: any }; - - /** - * Create new notebook output. - * - * @param items Notebook output items. - * @param metadata Optional metadata. - */ - constructor(items: NotebookCellOutputItem[], metadata?: { [key: string]: any }); - } - - /** - * NotebookCellData is the raw representation of notebook cells. Its is part of {@linkcode NotebookData}. - */ - export class NotebookCellData { - - /** - * The {@link NotebookCellKind kind} of this cell data. - */ - kind: NotebookCellKind; - - /** - * The source value of this cell data - either source code or formatted text. - */ - value: string; - - /** - * The language identifier of the source value of this cell data. Any value from - * {@linkcode languages.getLanguages getLanguages} is possible. - */ - languageId: string; - - /** - * The outputs of this cell data. - */ - outputs?: NotebookCellOutput[]; - - /** - * Arbitrary metadata of this cell data. Can be anything but must be JSON-stringifyable. - */ - metadata?: { [key: string]: any }; - - /** - * The execution summary of this cell data. - */ - executionSummary?: NotebookCellExecutionSummary; - - /** - * Create new cell data. Minimal cell data specifies its kind, its source value, and the - * language identifier of its source. - * - * @param kind The kind. - * @param value The source value. - * @param languageId The language identifier of the source value. - */ - constructor(kind: NotebookCellKind, value: string, languageId: string); - } - - /** - * Raw representation of a notebook. - * - * Extensions are responsible for creating {@linkcode NotebookData} so that the editor - * can create a {@linkcode NotebookDocument}. - * - * @see {@link NotebookSerializer} - */ - export class NotebookData { - /** - * The cell data of this notebook data. - */ - cells: NotebookCellData[]; - - /** - * Arbitrary metadata of notebook data. - */ - metadata?: { [key: string]: any }; - - /** - * Create new notebook data. - * - * @param cells An array of cell data. - */ - constructor(cells: NotebookCellData[]); - } - - /** - * The notebook serializer enables the editor to open notebook files. - * - * At its core the editor only knows a {@link NotebookData notebook data structure} but not - * how that data structure is written to a file, nor how it is read from a file. The - * notebook serializer bridges this gap by deserializing bytes into notebook data and - * vice versa. - */ - export interface NotebookSerializer { - - /** - * Deserialize contents of a notebook file into the notebook data structure. - * - * @param content Contents of a notebook file. - * @param token A cancellation token. - * @return Notebook data or a thenable that resolves to such. - */ - deserializeNotebook(content: Uint8Array, token: CancellationToken): NotebookData | Thenable; - - /** - * Serialize notebook data into file contents. - * - * @param data A notebook data structure. - * @param token A cancellation token. - * @returns An array of bytes or a thenable that resolves to such. - */ - serializeNotebook(data: NotebookData, token: CancellationToken): Uint8Array | Thenable; - } - - /** - * Notebook content options define what parts of a notebook are persisted. Note - * - * For instance, a notebook serializer can opt-out of saving outputs and in that case the editor doesn't mark a - * notebooks as {@link NotebookDocument.isDirty dirty} when its output has changed. - */ - export interface NotebookDocumentContentOptions { - /** - * Controls if output change events will trigger notebook document content change events and - * if it will be used in the diff editor, defaults to false. If the content provider doesn't - * persist the outputs in the file document, this should be set to true. - */ - transientOutputs?: boolean; - - /** - * Controls if a cell metadata property change event will trigger notebook document content - * change events and if it will be used in the diff editor, defaults to false. If the - * content provider doesn't persist a metadata property in the file document, it should be - * set to true. - */ - transientCellMetadata?: { [key: string]: boolean | undefined }; - - /** - * Controls if a document metadata property change event will trigger notebook document - * content change event and if it will be used in the diff editor, defaults to false. If the - * content provider doesn't persist a metadata property in the file document, it should be - * set to true. - */ - transientDocumentMetadata?: { [key: string]: boolean | undefined }; - } - - /** - * Notebook controller affinity for notebook documents. - * - * @see {@link NotebookController.updateNotebookAffinity} - */ - export enum NotebookControllerAffinity { - /** - * Default affinity. - */ - Default = 1, - /** - * A controller is preferred for a notebook. - */ - Preferred = 2 - } - - /** - * A notebook controller represents an entity that can execute notebook cells. This is often referred to as a kernel. - * - * There can be multiple controllers and the editor will let users choose which controller to use for a certain notebook. The - * {@linkcode NotebookController.notebookType notebookType}-property defines for what kind of notebooks a controller is for and - * the {@linkcode NotebookController.updateNotebookAffinity updateNotebookAffinity}-function allows controllers to set a preference - * for specific notebook documents. When a controller has been selected its - * {@link NotebookController.onDidChangeSelectedNotebooks onDidChangeSelectedNotebooks}-event fires. - * - * When a cell is being run the editor will invoke the {@linkcode NotebookController.executeHandler executeHandler} and a controller - * is expected to create and finalize a {@link NotebookCellExecution notebook cell execution}. However, controllers are also free - * to create executions by themselves. - */ - export interface NotebookController { - - /** - * The identifier of this notebook controller. - * - * _Note_ that controllers are remembered by their identifier and that extensions should use - * stable identifiers across sessions. - */ - readonly id: string; - - /** - * The notebook type this controller is for. - */ - readonly notebookType: string; - - /** - * An array of language identifiers that are supported by this - * controller. Any language identifier from {@linkcode languages.getLanguages getLanguages} - * is possible. When falsy all languages are supported. - * - * Samples: - * ```js - * // support JavaScript and TypeScript - * myController.supportedLanguages = ['javascript', 'typescript'] - * - * // support all languages - * myController.supportedLanguages = undefined; // falsy - * myController.supportedLanguages = []; // falsy - * ``` - */ - supportedLanguages?: string[]; - - /** - * The human-readable label of this notebook controller. - */ - label: string; - - /** - * The human-readable description which is rendered less prominent. - */ - description?: string; - - /** - * The human-readable detail which is rendered less prominent. - */ - detail?: string; - - /** - * Whether this controller supports execution order so that the - * editor can render placeholders for them. - */ - supportsExecutionOrder?: boolean; - - /** - * Create a cell execution task. - * - * _Note_ that there can only be one execution per cell at a time and that an error is thrown if - * a cell execution is created while another is still active. - * - * This should be used in response to the {@link NotebookController.executeHandler execution handler} - * being called or when cell execution has been started else, e.g when a cell was already - * executing or when cell execution was triggered from another source. - * - * @param cell The notebook cell for which to create the execution. - * @returns A notebook cell execution. - */ - createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution; - - /** - * The execute handler is invoked when the run gestures in the UI are selected, e.g Run Cell, Run All, - * Run Selection etc. The execute handler is responsible for creating and managing {@link NotebookCellExecution execution}-objects. - */ - executeHandler: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable; - - /** - * Optional interrupt handler. - * - * By default cell execution is canceled via {@link NotebookCellExecution.token tokens}. Cancellation - * tokens require that a controller can keep track of its execution so that it can cancel a specific execution at a later - * point. Not all scenarios allow for that, eg. REPL-style controllers often work by interrupting whatever is currently - * running. For those cases the interrupt handler exists - it can be thought of as the equivalent of `SIGINT` - * or `Control+C` in terminals. - * - * _Note_ that supporting {@link NotebookCellExecution.token cancellation tokens} is preferred and that interrupt handlers should - * only be used when tokens cannot be supported. - */ - interruptHandler?: (notebook: NotebookDocument) => void | Thenable; - - /** - * An event that fires whenever a controller has been selected or un-selected for a notebook document. - * - * There can be multiple controllers for a notebook and in that case a controllers needs to be _selected_. This is a user gesture - * and happens either explicitly or implicitly when interacting with a notebook for which a controller was _suggested_. When possible, - * the editor _suggests_ a controller that is most likely to be _selected_. - * - * _Note_ that controller selection is persisted (by the controllers {@link NotebookController.id id}) and restored as soon as a - * controller is re-created or as a notebook is {@link workspace.onDidOpenNotebookDocument opened}. - */ - readonly onDidChangeSelectedNotebooks: Event<{ readonly notebook: NotebookDocument; readonly selected: boolean }>; - - /** - * A controller can set affinities for specific notebook documents. This allows a controller - * to be presented more prominent for some notebooks. - * - * @param notebook The notebook for which a priority is set. - * @param affinity A controller affinity - */ - updateNotebookAffinity(notebook: NotebookDocument, affinity: NotebookControllerAffinity): void; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * A NotebookCellExecution is how {@link NotebookController notebook controller} modify a notebook cell as - * it is executing. - * - * When a cell execution object is created, the cell enters the {@linkcode NotebookCellExecutionState.Pending Pending} state. - * When {@linkcode NotebookCellExecution.start start(...)} is called on the execution task, it enters the {@linkcode NotebookCellExecutionState.Executing Executing} state. When - * {@linkcode NotebookCellExecution.end end(...)} is called, it enters the {@linkcode NotebookCellExecutionState.Idle Idle} state. - */ - export interface NotebookCellExecution { - - /** - * The {@link NotebookCell cell} for which this execution has been created. - */ - readonly cell: NotebookCell; - - /** - * A cancellation token which will be triggered when the cell execution is canceled - * from the UI. - * - * _Note_ that the cancellation token will not be triggered when the {@link NotebookController controller} - * that created this execution uses an {@link NotebookController.interruptHandler interrupt-handler}. - */ - readonly token: CancellationToken; - - /** - * Set and unset the order of this cell execution. - */ - executionOrder: number | undefined; - - /** - * Signal that the execution has begun. - * - * @param startTime The time that execution began, in milliseconds in the Unix epoch. Used to drive the clock - * that shows for how long a cell has been running. If not given, the clock won't be shown. - */ - start(startTime?: number): void; - - /** - * Signal that execution has ended. - * - * @param success If true, a green check is shown on the cell status bar. - * If false, a red X is shown. - * If undefined, no check or X icon is shown. - * @param endTime The time that execution finished, in milliseconds in the Unix epoch. - */ - end(success: boolean | undefined, endTime?: number): void; - - /** - * Clears the output of the cell that is executing or of another cell that is affected by this execution. - * - * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of - * this execution. - * @return A thenable that resolves when the operation finished. - */ - clearOutput(cell?: NotebookCell): Thenable; - - /** - * Replace the output of the cell that is executing or of another cell that is affected by this execution. - * - * @param out Output that replaces the current output. - * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of - * this execution. - * @return A thenable that resolves when the operation finished. - */ - replaceOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; - - /** - * Append to the output of the cell that is executing or to another cell that is affected by this execution. - * - * @param out Output that is appended to the current output. - * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of - * this execution. - * @return A thenable that resolves when the operation finished. - */ - appendOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; - - /** - * Replace all output items of existing cell output. - * - * @param items Output items that replace the items of existing output. - * @param output Output object that already exists. - * @return A thenable that resolves when the operation finished. - */ - replaceOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; - - /** - * Append output items to existing cell output. - * - * @param items Output items that are append to existing output. - * @param output Output object that already exists. - * @return A thenable that resolves when the operation finished. - */ - appendOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; - } - - /** - * Represents the alignment of status bar items. - */ - export enum NotebookCellStatusBarAlignment { - - /** - * Aligned to the left side. - */ - Left = 1, - - /** - * Aligned to the right side. - */ - Right = 2 - } - - /** - * A contribution to a cell's status bar - */ - export class NotebookCellStatusBarItem { - /** - * The text to show for the item. - */ - text: string; - - /** - * Whether the item is aligned to the left or right. - */ - alignment: NotebookCellStatusBarAlignment; - - /** - * An optional {@linkcode Command} or identifier of a command to run on click. - * - * The command must be {@link commands.getCommands known}. - * - * Note that if this is a {@linkcode Command} object, only the {@linkcode Command.command command} and {@linkcode Command.arguments arguments} - * are used by the editor. - */ - command?: string | Command; - - /** - * A tooltip to show when the item is hovered. - */ - tooltip?: string; - - /** - * The priority of the item. A higher value item will be shown more to the left. - */ - priority?: number; - - /** - * Accessibility information used when a screen reader interacts with this item. - */ - accessibilityInformation?: AccessibilityInformation; - - /** - * Creates a new NotebookCellStatusBarItem. - * @param text The text to show for the item. - * @param alignment Whether the item is aligned to the left or right. - */ - constructor(text: string, alignment: NotebookCellStatusBarAlignment); - } - - /** - * A provider that can contribute items to the status bar that appears below a cell's editor. - */ - export interface NotebookCellStatusBarItemProvider { - /** - * An optional event to signal that statusbar items have changed. The provide method will be called again. - */ - onDidChangeCellStatusBarItems?: Event; - - /** - * The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state. - * @param cell The cell for which to return items. - * @param token A token triggered if this request should be cancelled. - * @return One or more {@link NotebookCellStatusBarItem cell statusbar items} - */ - provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult; - } - - /** - * Namespace for notebooks. - * - * The notebooks functionality is composed of three loosely coupled components: - * - * 1. {@link NotebookSerializer} enable the editor to open, show, and save notebooks - * 2. {@link NotebookController} own the execution of notebooks, e.g they create output from code cells. - * 3. NotebookRenderer present notebook output in the editor. They run in a separate context. - */ - export namespace notebooks { - - /** - * Creates a new notebook controller. - * - * @param id Identifier of the controller. Must be unique per extension. - * @param notebookType A notebook type for which this controller is for. - * @param label The label of the controller. - * @param handler The execute-handler of the controller. - */ - export function createNotebookController(id: string, notebookType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable): NotebookController; - - /** - * Register a {@link NotebookCellStatusBarItemProvider cell statusbar item provider} for the given notebook type. - * - * @param notebookType The notebook type to register for. - * @param provider A cell status bar provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable; - - /** - * Creates a new messaging instance used to communicate with a specific renderer. - * - * * *Note 1:* Extensions can only create renderer that they have defined in their `package.json`-file - * * *Note 2:* A renderer only has access to messaging if `requiresMessaging` is set to `always` or `optional` in - * its `notebookRenderer` contribution. - * - * @param rendererId The renderer ID to communicate with - * @returns A new notebook renderer messaging object. - */ - export function createRendererMessaging(rendererId: string): NotebookRendererMessaging; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Setter and getter for the contents of the input box. - */ - value: string; - - /** - * A string to show as placeholder in the input box to guide the user. - */ - placeholder: string; - - /** - * Controls whether the input box is enabled (default is `true`). - */ - enabled: boolean; - - /** - * Controls whether the input box is visible (default is `true`). - */ - visible: boolean; - } - - interface QuickDiffProvider { - - /** - * Provide a {@link Uri} to the original resource of any given resource uri. - * - * @param uri The uri of the resource open in a text editor. - * @param token A cancellation token. - * @return A thenable that resolves to uri of the matching original resource. - */ - provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; - } - - /** - * The theme-aware decorations for a - * {@link SourceControlResourceState source control resource state}. - */ - export interface SourceControlResourceThemableDecorations { - - /** - * The icon path for a specific - * {@link SourceControlResourceState source control resource state}. - */ - readonly iconPath?: string | Uri | ThemeIcon; - } - - /** - * The decorations for a {@link SourceControlResourceState source control resource state}. - * Can be independently specified for light and dark themes. - */ - export interface SourceControlResourceDecorations extends SourceControlResourceThemableDecorations { - - /** - * Whether the {@link SourceControlResourceState source control resource state} should - * be striked-through in the UI. - */ - readonly strikeThrough?: boolean; - - /** - * Whether the {@link SourceControlResourceState source control resource state} should - * be faded in the UI. - */ - readonly faded?: boolean; - - /** - * The title for a specific - * {@link SourceControlResourceState source control resource state}. - */ - readonly tooltip?: string; - - /** - * The light theme decorations. - */ - readonly light?: SourceControlResourceThemableDecorations; - - /** - * The dark theme decorations. - */ - readonly dark?: SourceControlResourceThemableDecorations; - } - - /** - * An source control resource state represents the state of an underlying workspace - * resource within a certain {@link SourceControlResourceGroup source control group}. - */ - export interface SourceControlResourceState { - - /** - * The {@link Uri} of the underlying resource inside the workspace. - */ - readonly resourceUri: Uri; - - /** - * The {@link Command} which should be run when the resource - * state is open in the Source Control viewlet. - */ - readonly command?: Command; - - /** - * The {@link SourceControlResourceDecorations decorations} for this source control - * resource state. - */ - readonly decorations?: SourceControlResourceDecorations; - - /** - * Context value of the resource state. This can be used to contribute resource specific actions. - * For example, if a resource is given a context value as `diffable`. When contributing actions to `scm/resourceState/context` - * using `menus` extension point, you can specify context value for key `scmResourceState` in `when` expressions, like `scmResourceState == diffable`. - * ```json - * "contributes": { - * "menus": { - * "scm/resourceState/context": [ - * { - * "command": "extension.diff", - * "when": "scmResourceState == diffable" - * } - * ] - * } - * } - * ``` - * This will show action `extension.diff` only for resources with `contextValue` is `diffable`. - */ - readonly contextValue?: string; - } - - /** - * A source control resource group is a collection of - * {@link SourceControlResourceState source control resource states}. - */ - export interface SourceControlResourceGroup { - - /** - * The id of this source control resource group. - */ - readonly id: string; - - /** - * The label of this source control resource group. - */ - label: string; - - /** - * Whether this source control resource group is hidden when it contains - * no {@link SourceControlResourceState source control resource states}. - */ - hideWhenEmpty?: boolean; - - /** - * This group's collection of - * {@link SourceControlResourceState source control resource states}. - */ - resourceStates: SourceControlResourceState[]; - - /** - * Dispose this source control resource group. - */ - dispose(): void; - } - - /** - * An source control is able to provide {@link SourceControlResourceState resource states} - * to the editor and interact with the editor in several source control related ways. - */ - export interface SourceControl { - - /** - * The id of this source control. - */ - readonly id: string; - - /** - * The human-readable label of this source control. - */ - readonly label: string; - - /** - * The (optional) Uri of the root of this source control. - */ - readonly rootUri: Uri | undefined; - - /** - * The {@link SourceControlInputBox input box} for this source control. - */ - readonly inputBox: SourceControlInputBox; - - /** - * The UI-visible count of {@link SourceControlResourceState resource states} of - * this source control. - * - * If undefined, this source control will - * - display its UI-visible count as zero, and - * - contribute the count of its {@link SourceControlResourceState resource states} to the UI-visible aggregated count for all source controls - */ - count?: number; - - /** - * An optional {@link QuickDiffProvider quick diff provider}. - */ - quickDiffProvider?: QuickDiffProvider; - - /** - * Optional commit template string. - * - * The Source Control viewlet will populate the Source Control - * input with this value when appropriate. - */ - commitTemplate?: string; - - /** - * Optional accept input command. - * - * This command will be invoked when the user accepts the value - * in the Source Control input. - */ - acceptInputCommand?: Command; - - /** - * Optional status bar commands. - * - * These commands will be displayed in the editor's status bar. - */ - statusBarCommands?: Command[]; - - /** - * Create a new {@link SourceControlResourceGroup resource group}. - */ - createResourceGroup(id: string, label: string): SourceControlResourceGroup; - - /** - * Dispose this source control. - */ - dispose(): void; - } - - export namespace scm { - - /** - * The {@link SourceControlInputBox input box} for the last source control - * created by the extension. - * - * @deprecated Use SourceControl.inputBox instead - */ - export const inputBox: SourceControlInputBox; - - /** - * Creates a new {@link SourceControl source control} instance. - * - * @param id An `id` for the source control. Something short, e.g.: `git`. - * @param label A human-readable string for the source control. E.g.: `Git`. - * @param rootUri An optional Uri of the root of the source control. E.g.: `Uri.parse(workspaceRoot)`. - * @return An instance of {@link SourceControl source control}. - */ - export function createSourceControl(id: string, label: string, rootUri?: Uri): SourceControl; - } - - /** - * A DebugProtocolMessage is an opaque stand-in type for the [ProtocolMessage](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage) type defined in the Debug Adapter Protocol. - */ - export interface DebugProtocolMessage { - // Properties: see [ProtocolMessage details](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage). - } - - /** - * A DebugProtocolSource is an opaque stand-in type for the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. - */ - export interface DebugProtocolSource { - // Properties: see [Source details](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source). - } - - /** - * A DebugProtocolBreakpoint is an opaque stand-in type for the [Breakpoint](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint) type defined in the Debug Adapter Protocol. - */ - export interface DebugProtocolBreakpoint { - // Properties: see [Breakpoint details](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint). - } - - /** - * Configuration for a debug session. - */ - export interface DebugConfiguration { - /** - * The type of the debug session. - */ - type: string; - - /** - * The name of the debug session. - */ - name: string; - - /** - * The request type of the debug session. - */ - request: string; - - /** - * Additional debug type specific properties. - */ - [key: string]: any; - } - - /** - * A debug session. - */ - export interface DebugSession { - - /** - * The unique ID of this debug session. - */ - readonly id: string; - - /** - * The debug session's type from the {@link DebugConfiguration debug configuration}. - */ - readonly type: string; - - /** - * The parent session of this debug session, if it was created as a child. - * @see DebugSessionOptions.parentSession - */ - readonly parentSession?: DebugSession; - - /** - * The debug session's name is initially taken from the {@link DebugConfiguration debug configuration}. - * Any changes will be properly reflected in the UI. - */ - name: string; - - /** - * The workspace folder of this session or `undefined` for a folderless setup. - */ - readonly workspaceFolder: WorkspaceFolder | undefined; - - /** - * The "resolved" {@link DebugConfiguration debug configuration} of this session. - * "Resolved" means that - * - all variables have been substituted and - * - platform specific attribute sections have been "flattened" for the matching platform and removed for non-matching platforms. - */ - readonly configuration: DebugConfiguration; - - /** - * Send a custom request to the debug adapter. - */ - customRequest(command: string, args?: any): Thenable; - - /** - * Maps a breakpoint in the editor to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session. - * If no DAP breakpoint exists (either because the editor breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned. - * - * @param breakpoint A {@link Breakpoint} in the editor. - * @return A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. - */ - getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable; - } - - /** - * A custom Debug Adapter Protocol event received from a {@link DebugSession debug session}. - */ - export interface DebugSessionCustomEvent { - /** - * The {@link DebugSession debug session} for which the custom event was received. - */ - readonly session: DebugSession; - - /** - * Type of event. - */ - readonly event: string; - - /** - * Event specific information. - */ - readonly body: any; - } - - /** - * A debug configuration provider allows to add debug configurations to the debug service - * and to resolve launch configurations before they are used to start a debug session. - * A debug configuration provider is registered via {@link debug.registerDebugConfigurationProvider}. - */ - export interface DebugConfigurationProvider { - /** - * Provides {@link DebugConfiguration debug configuration} to the debug service. If more than one debug configuration provider is - * registered for the same type, debug configurations are concatenated in arbitrary order. - * - * @param folder The workspace folder for which the configurations are used or `undefined` for a folderless setup. - * @param token A cancellation token. - * @return An array of {@link DebugConfiguration debug configurations}. - */ - provideDebugConfigurations?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; - - /** - * Resolves a {@link DebugConfiguration debug configuration} by filling in missing values or by adding/changing/removing attributes. - * If more than one debug configuration provider is registered for the same type, the resolveDebugConfiguration calls are chained - * in arbitrary order and the initial debug configuration is piped through the chain. - * Returning the value 'undefined' prevents the debug session from starting. - * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. - * - * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. - * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. - * @param token A cancellation token. - * @return The resolved debug configuration or undefined or null. - */ - resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; - - /** - * This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted. - * It can be used to resolve or verify a {@link DebugConfiguration debug configuration} by filling in missing values or by adding/changing/removing attributes. - * If more than one debug configuration provider is registered for the same type, the 'resolveDebugConfigurationWithSubstitutedVariables' calls are chained - * in arbitrary order and the initial debug configuration is piped through the chain. - * Returning the value 'undefined' prevents the debug session from starting. - * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. - * - * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. - * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. - * @param token A cancellation token. - * @return The resolved debug configuration or undefined or null. - */ - resolveDebugConfigurationWithSubstitutedVariables?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; - } - - /** - * Represents a debug adapter executable and optional arguments and runtime options passed to it. - */ - export class DebugAdapterExecutable { - - /** - * Creates a description for a debug adapter based on an executable program. - * - * @param command The command or executable path that implements the debug adapter. - * @param args Optional arguments to be passed to the command or executable. - * @param options Optional options to be used when starting the command or executable. - */ - constructor(command: string, args?: string[], options?: DebugAdapterExecutableOptions); - - /** - * The command or path of the debug adapter executable. - * A command must be either an absolute path of an executable or the name of an command to be looked up via the PATH environment variable. - * The special value 'node' will be mapped to the editor's built-in Node.js runtime. - */ - readonly command: string; - - /** - * The arguments passed to the debug adapter executable. Defaults to an empty array. - */ - readonly args: string[]; - - /** - * Optional options to be used when the debug adapter is started. - * Defaults to undefined. - */ - readonly options?: DebugAdapterExecutableOptions; - } - - /** - * Options for a debug adapter executable. - */ - export interface DebugAdapterExecutableOptions { - - /** - * The additional environment of the executed program or shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env?: { [key: string]: string }; - - /** - * The current working directory for the executed debug adapter. - */ - cwd?: string; - } - - /** - * Represents a debug adapter running as a socket based server. - */ - export class DebugAdapterServer { - - /** - * The port. - */ - readonly port: number; - - /** - * The host. - */ - readonly host?: string | undefined; - - /** - * Create a description for a debug adapter running as a socket based server. - */ - constructor(port: number, host?: string); - } - - /** - * Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. - */ - export class DebugAdapterNamedPipeServer { - /** - * The path to the NamedPipe/UNIX Domain Socket. - */ - readonly path: string; - - /** - * Create a description for a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. - */ - constructor(path: string); - } - - /** - * A debug adapter that implements the Debug Adapter Protocol can be registered with the editor if it implements the DebugAdapter interface. - */ - export interface DebugAdapter extends Disposable { - - /** - * An event which fires after the debug adapter has sent a Debug Adapter Protocol message to the editor. - * Messages can be requests, responses, or events. - */ - readonly onDidSendMessage: Event; - - /** - * Handle a Debug Adapter Protocol message. - * Messages can be requests, responses, or events. - * Results or errors are returned via onSendMessage events. - * @param message A Debug Adapter Protocol message - */ - handleMessage(message: DebugProtocolMessage): void; - } - - /** - * A debug adapter descriptor for an inline implementation. - */ - export class DebugAdapterInlineImplementation { - - /** - * Create a descriptor for an inline implementation of a debug adapter. - */ - constructor(implementation: DebugAdapter); - } - - export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation; - - export interface DebugAdapterDescriptorFactory { - /** - * 'createDebugAdapterDescriptor' is called at the start of a debug session to provide details about the debug adapter to use. - * These details must be returned as objects of type {@link DebugAdapterDescriptor}. - * Currently two types of debug adapters are supported: - * - a debug adapter executable is specified as a command path and arguments (see {@link DebugAdapterExecutable}), - * - a debug adapter server reachable via a communication port (see {@link DebugAdapterServer}). - * If the method is not implemented the default behavior is this: - * createDebugAdapter(session: DebugSession, executable: DebugAdapterExecutable) { - * if (typeof session.configuration.debugServer === 'number') { - * return new DebugAdapterServer(session.configuration.debugServer); - * } - * return executable; - * } - * @param session The {@link DebugSession debug session} for which the debug adapter will be used. - * @param executable The debug adapter's executable information as specified in the package.json (or undefined if no such information exists). - * @return a {@link DebugAdapterDescriptor debug adapter descriptor} or undefined. - */ - createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): ProviderResult; - } - - /** - * A Debug Adapter Tracker is a means to track the communication between the editor and a Debug Adapter. - */ - export interface DebugAdapterTracker { - /** - * A session with the debug adapter is about to be started. - */ - onWillStartSession?(): void; - /** - * The debug adapter is about to receive a Debug Adapter Protocol message from the editor. - */ - onWillReceiveMessage?(message: any): void; - /** - * The debug adapter has sent a Debug Adapter Protocol message to the editor. - */ - onDidSendMessage?(message: any): void; - /** - * The debug adapter session is about to be stopped. - */ - onWillStopSession?(): void; - /** - * An error with the debug adapter has occurred. - */ - onError?(error: Error): void; - /** - * The debug adapter has exited with the given exit code or signal. - */ - onExit?(code: number | undefined, signal: string | undefined): void; - } - - export interface DebugAdapterTrackerFactory { - /** - * The method 'createDebugAdapterTracker' is called at the start of a debug session in order - * to return a "tracker" object that provides read-access to the communication between the editor and a debug adapter. - * - * @param session The {@link DebugSession debug session} for which the debug adapter tracker will be used. - * @return A {@link DebugAdapterTracker debug adapter tracker} or undefined. - */ - createDebugAdapterTracker(session: DebugSession): ProviderResult; - } - - /** - * Represents the debug console. - */ - export interface DebugConsole { - /** - * Append the given value to the debug console. - * - * @param value A string, falsy values will not be printed. - */ - append(value: string): void; - - /** - * Append the given value and a line feed character - * to the debug console. - * - * @param value A string, falsy values will be printed. - */ - appendLine(value: string): void; - } - - /** - * An event describing the changes to the set of {@link Breakpoint breakpoints}. - */ - export interface BreakpointsChangeEvent { - /** - * Added breakpoints. - */ - readonly added: readonly Breakpoint[]; - - /** - * Removed breakpoints. - */ - readonly removed: readonly Breakpoint[]; - - /** - * Changed breakpoints. - */ - readonly changed: readonly Breakpoint[]; - } - - /** - * The base class of all breakpoint types. - */ - export class Breakpoint { - /** - * The unique ID of the breakpoint. - */ - readonly id: string; - /** - * Is breakpoint enabled. - */ - readonly enabled: boolean; - /** - * An optional expression for conditional breakpoints. - */ - readonly condition?: string | undefined; - /** - * An optional expression that controls how many hits of the breakpoint are ignored. - */ - readonly hitCondition?: string | undefined; - /** - * An optional message that gets logged when this breakpoint is hit. Embedded expressions within {} are interpolated by the debug adapter. - */ - readonly logMessage?: string | undefined; - - protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); - } - - /** - * A breakpoint specified by a source location. - */ - export class SourceBreakpoint extends Breakpoint { - /** - * The source and line position of this breakpoint. - */ - readonly location: Location; - - /** - * Create a new breakpoint for a source location. - */ - constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); - } - - /** - * A breakpoint specified by a function name. - */ - export class FunctionBreakpoint extends Breakpoint { - /** - * The name of the function to which this breakpoint is attached. - */ - readonly functionName: string; - - /** - * Create a new function breakpoint. - */ - constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); - } - - /** - * Debug console mode used by debug session, see {@link DebugSessionOptions options}. - */ - export enum DebugConsoleMode { - /** - * Debug session should have a separate debug console. - */ - Separate = 0, - - /** - * Debug session should share debug console with its parent session. - * This value has no effect for sessions which do not have a parent session. - */ - MergeWithParent = 1 - } - - /** - * Options for {@link debug.startDebugging starting a debug session}. - */ - export interface DebugSessionOptions { - - /** - * When specified the newly created debug session is registered as a "child" session of this - * "parent" debug session. - */ - parentSession?: DebugSession; - - /** - * Controls whether lifecycle requests like 'restart' are sent to the newly created session or its parent session. - * By default (if the property is false or missing), lifecycle requests are sent to the new session. - * This property is ignored if the session has no parent session. - */ - lifecycleManagedByParent?: boolean; - - /** - * Controls whether this session should have a separate debug console or share it - * with the parent session. Has no effect for sessions which do not have a parent session. - * Defaults to Separate. - */ - consoleMode?: DebugConsoleMode; - - /** - * Controls whether this session should run without debugging, thus ignoring breakpoints. - * When this property is not specified, the value from the parent session (if there is one) is used. - */ - noDebug?: boolean; - - /** - * Controls if the debug session's parent session is shown in the CALL STACK view even if it has only a single child. - * By default, the debug session will never hide its parent. - * If compact is true, debug sessions with a single child are hidden in the CALL STACK view to make the tree more compact. - */ - compact?: boolean; - - /** - * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. - */ - suppressSaveBeforeStart?: boolean; - - /** - * When true, the debug toolbar will not be shown for this session. - */ - suppressDebugToolbar?: boolean; - - /** - * When true, the window statusbar color will not be changed for this session. - */ - suppressDebugStatusbar?: boolean; - - /** - * When true, the debug viewlet will not be automatically revealed for this session. - */ - suppressDebugView?: boolean; - } - - /** - * 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 {@link 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 - } - - /** - * Namespace for debug functionality. - */ - export namespace debug { - - /** - * The currently active {@link DebugSession debug session} or `undefined`. The active debug session is the one - * represented by the debug action floating window or the one currently shown in the drop down menu of the debug action floating window. - * If no debug session is active, the value is `undefined`. - */ - export let activeDebugSession: DebugSession | undefined; - - /** - * The currently active {@link DebugConsole debug console}. - * If no debug session is active, output sent to the debug console is not shown. - */ - export let activeDebugConsole: DebugConsole; - - /** - * List of breakpoints. - */ - export let breakpoints: readonly Breakpoint[]; - - /** - * An {@link Event} which fires when the {@link debug.activeDebugSession active debug session} - * has changed. *Note* that the event also fires when the active debug session changes - * to `undefined`. - */ - export const onDidChangeActiveDebugSession: Event; - - /** - * An {@link Event} which fires when a new {@link DebugSession debug session} has been started. - */ - export const onDidStartDebugSession: Event; - - /** - * An {@link Event} which fires when a custom DAP event is received from the {@link DebugSession debug session}. - */ - export const onDidReceiveDebugSessionCustomEvent: Event; - - /** - * An {@link Event} which fires when a {@link DebugSession debug session} has terminated. - */ - export const onDidTerminateDebugSession: Event; - - /** - * An {@link Event} that is emitted when the set of breakpoints is added, removed, or changed. - */ - export const onDidChangeBreakpoints: Event; - - /** - * Register a {@link DebugConfigurationProvider debug configuration provider} for a specific debug type. - * The optional {@link DebugConfigurationProviderTriggerKind triggerKind} 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 debugType The debug type for which the provider is registered. - * @param provider The {@link DebugConfigurationProvider debug configuration provider} to register. - * @param triggerKind The {@link DebugConfigurationProviderTrigger trigger} for which the 'provideDebugConfiguration' method of the provider is registered. If `triggerKind` is missing, the value `DebugConfigurationProviderTriggerKind.Initial` is assumed. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable; - - /** - * Register a {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} for a specific debug type. - * An extension is only allowed to register a DebugAdapterDescriptorFactory for the debug type(s) defined by the extension. Otherwise an error is thrown. - * Registering more than one DebugAdapterDescriptorFactory for a debug type results in an error. - * - * @param debugType The debug type for which the factory is registered. - * @param factory The {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} to register. - * @return A {@link Disposable} that unregisters this factory when being disposed. - */ - export function registerDebugAdapterDescriptorFactory(debugType: string, factory: DebugAdapterDescriptorFactory): Disposable; - - /** - * Register a debug adapter tracker factory for the given debug type. - * - * @param debugType The debug type for which the factory is registered or '*' for matching all debug types. - * @param factory The {@link DebugAdapterTrackerFactory debug adapter tracker factory} to register. - * @return A {@link Disposable} that unregisters this factory when being disposed. - */ - export function registerDebugAdapterTrackerFactory(debugType: string, factory: DebugAdapterTrackerFactory): Disposable; - - /** - * Start debugging by using either a named launch or named compound configuration, - * or by directly passing a {@link DebugConfiguration}. - * The named configurations are looked up in '.vscode/launch.json' found in the given folder. - * Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date. - * Folder specific variables used in the configuration (e.g. '${workspaceFolder}') are resolved against the given folder. - * @param folder The {@link WorkspaceFolder workspace folder} for looking up named configurations and resolving variables or `undefined` for a non-folder setup. - * @param nameOrConfiguration Either the name of a debug or compound configuration or a {@link DebugConfiguration} object. - * @param parentSessionOrOptions Debug session options. When passed a parent {@link DebugSession debug session}, assumes options with just this parent session. - * @return A thenable that resolves when debugging could be successfully started. - */ - export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable; - - /** - * Stop the given debug session or stop all debug sessions if session is omitted. - * @param session The {@link DebugSession debug session} to stop; if omitted all sessions are stopped. - */ - export function stopDebugging(session?: DebugSession): Thenable; - - /** - * Add breakpoints. - * @param breakpoints The breakpoints to add. - */ - export function addBreakpoints(breakpoints: readonly Breakpoint[]): void; - - /** - * Remove breakpoints. - * @param breakpoints The breakpoints to remove. - */ - export function removeBreakpoints(breakpoints: readonly Breakpoint[]): void; - - /** - * Converts a "Source" descriptor object received via the Debug Adapter Protocol into a Uri that can be used to load its contents. - * If the source descriptor is based on a path, a file Uri is returned. - * If the source descriptor uses a reference number, a specific debug Uri (scheme 'debug') is constructed that requires a corresponding ContentProvider and a running debug session - * - * If the "Source" descriptor has insufficient information for creating the Uri, an error is thrown. - * - * @param source An object conforming to the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. - * @param session An optional debug session that will be used when the source descriptor uses a reference number to load the contents from an active debug session. - * @return A uri that can be used to load the contents of the source. - */ - export function asDebugSourceUri(source: DebugProtocolSource, session?: DebugSession): Uri; - } - - /** - * Namespace for dealing with installed extensions. Extensions are represented - * by an {@link Extension}-interface which enables reflection on them. - * - * Extension writers can provide APIs to other extensions by returning their API public - * surface from the `activate`-call. - * - * ```javascript - * export function activate(context: vscode.ExtensionContext) { - * let api = { - * sum(a, b) { - * return a + b; - * }, - * mul(a, b) { - * return a * b; - * } - * }; - * // 'export' public api-surface - * return api; - * } - * ``` - * When depending on the API of another extension add an `extensionDependencies`-entry - * to `package.json`, and use the {@link extensions.getExtension getExtension}-function - * and the {@link Extension.exports exports}-property, like below: - * - * ```javascript - * let mathExt = extensions.getExtension('genius.math'); - * let importedApi = mathExt.exports; - * - * console.log(importedApi.mul(42, 1)); - * ``` - */ - export namespace extensions { - - /** - * Get an extension by its full identifier in the form of: `publisher.name`. - * - * @param extensionId An extension identifier. - * @return An extension or `undefined`. - */ - export function getExtension(extensionId: string): Extension | undefined; - - /** - * All extensions currently known to the system. - */ - export const all: readonly Extension[]; - - /** - * An event which fires when `extensions.all` changes. This can happen when extensions are - * installed, uninstalled, enabled or disabled. - */ - export const onDidChange: Event; - } - - /** - * Collapsible state of a {@link CommentThread comment thread} - */ - export enum CommentThreadCollapsibleState { - /** - * Determines an item is collapsed - */ - Collapsed = 0, - - /** - * Determines an item is expanded - */ - Expanded = 1 - } - - /** - * Comment mode of a {@link Comment} - */ - export enum CommentMode { - /** - * Displays the comment editor - */ - Editing = 0, - - /** - * Displays the preview of the comment - */ - Preview = 1 - } - - /** - * A collection of {@link Comment comments} representing a conversation at a particular range in a document. - */ - export interface CommentThread { - /** - * The uri of the document the thread has been created on. - */ - readonly uri: Uri; - - /** - * The range the comment thread is located within the document. The thread icon will be shown - * at the last line of the range. - */ - range: Range; - - /** - * The ordered comments of the thread. - */ - comments: readonly Comment[]; - - /** - * Whether the thread should be collapsed or expanded when opening the document. - * Defaults to Collapsed. - */ - collapsibleState: CommentThreadCollapsibleState; - - /** - * Whether the thread supports reply. - * Defaults to true. - */ - canReply: boolean; - - /** - * Context value of the comment thread. This can be used to contribute thread specific actions. - * For example, a comment thread is given a context value as `editable`. When contributing actions to `comments/commentThread/title` - * using `menus` extension point, you can specify context value for key `commentThread` in `when` expression like `commentThread == editable`. - * ```json - * "contributes": { - * "menus": { - * "comments/commentThread/title": [ - * { - * "command": "extension.deleteCommentThread", - * "when": "commentThread == editable" - * } - * ] - * } - * } - * ``` - * This will show action `extension.deleteCommentThread` only for comment threads with `contextValue` is `editable`. - */ - contextValue?: string; - - /** - * The optional human-readable label describing the {@link CommentThread Comment Thread} - */ - label?: string; - - /** - * Dispose this comment thread. - * - * Once disposed, this comment thread will be removed from visible editors and Comment Panel when appropriate. - */ - dispose(): void; - } - - /** - * Author information of a {@link Comment} - */ - export interface CommentAuthorInformation { - /** - * The display name of the author of the comment - */ - name: string; - - /** - * The optional icon path for the author - */ - iconPath?: Uri; - } - - /** - * Reactions of a {@link Comment} - */ - export interface CommentReaction { - /** - * The human-readable label for the reaction - */ - readonly label: string; - - /** - * Icon for the reaction shown in UI. - */ - readonly iconPath: string | Uri; - - /** - * The number of users who have reacted to this reaction - */ - readonly count: number; - - /** - * Whether the {@link CommentAuthorInformation author} of the comment has reacted to this reaction - */ - readonly authorHasReacted: boolean; - } - - /** - * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. - */ - export interface Comment { - /** - * The human-readable comment body - */ - body: string | MarkdownString; - - /** - * {@link CommentMode Comment mode} of the comment - */ - mode: CommentMode; - - /** - * The {@link CommentAuthorInformation author information} of the comment - */ - author: CommentAuthorInformation; - - /** - * Context value of the comment. This can be used to contribute comment specific actions. - * For example, a comment is given a context value as `editable`. When contributing actions to `comments/comment/title` - * using `menus` extension point, you can specify context value for key `comment` in `when` expression like `comment == editable`. - * ```json - * "contributes": { - * "menus": { - * "comments/comment/title": [ - * { - * "command": "extension.deleteComment", - * "when": "comment == editable" - * } - * ] - * } - * } - * ``` - * This will show action `extension.deleteComment` only for comments with `contextValue` is `editable`. - */ - contextValue?: string; - - /** - * Optional reactions of the {@link Comment} - */ - reactions?: CommentReaction[]; - - /** - * Optional label describing the {@link Comment} - * Label will be rendered next to authorName if exists. - */ - label?: string; - - /** - * Optional timestamp that will be displayed in comments. - * The date will be formatted according to the user's locale and settings. - */ - timestamp?: Date; - } - - /** - * Command argument for actions registered in `comments/commentThread/context`. - */ - export interface CommentReply { - /** - * The active {@link CommentThread comment thread} - */ - thread: CommentThread; - - /** - * The value in the comment editor - */ - text: string; - } - - /** - * Commenting range provider for a {@link CommentController comment controller}. - */ - export interface CommentingRangeProvider { - /** - * Provide a list of ranges which allow new comment threads creation or null for a given document - */ - provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; - } - - /** - * Represents a {@link CommentController comment controller}'s {@link CommentController.options options}. - */ - 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; - } - - /** - * A comment controller is able to provide {@link CommentThread comments} support to the editor and - * provide users various ways to interact with comments. - */ - export interface CommentController { - /** - * The id of this comment controller. - */ - readonly id: string; - - /** - * The human-readable label of this comment controller. - */ - readonly label: string; - - /** - * Comment controller options - */ - options?: CommentOptions; - - /** - * Optional commenting range provider. Provide a list {@link Range ranges} which support commenting to any given resource uri. - * - * If not provided, users cannot leave any comments. - */ - commentingRangeProvider?: CommentingRangeProvider; - - /** - * Create a {@link CommentThread comment thread}. The comment thread will be displayed in visible text editors (if the resource matches) - * and Comments Panel once created. - * - * @param uri The uri of the document the thread has been created on. - * @param range The range the comment thread is located within the document. - * @param comments The ordered comments of the thread. - */ - createCommentThread(uri: Uri, range: Range, comments: readonly Comment[]): CommentThread; - - /** - * Optional reaction handler for creating and deleting reactions on a {@link Comment}. - */ - reactionHandler?: (comment: Comment, reaction: CommentReaction) => Thenable; - - /** - * Dispose this comment controller. - * - * Once disposed, all {@link CommentThread comment threads} created by this comment controller will also be removed from the editor - * and Comments Panel. - */ - dispose(): void; - } - - namespace comments { - /** - * Creates a new {@link CommentController comment controller} instance. - * - * @param id An `id` for the comment controller. - * @param label A human-readable string for the comment controller. - * @return An instance of {@link CommentController comment controller}. - */ - export function createCommentController(id: string, label: string): CommentController; - } - - /** - * Represents a session of a currently logged in user. - */ - export interface AuthenticationSession { - /** - * The identifier of the authentication session. - */ - readonly id: string; - - /** - * The access token. - */ - readonly accessToken: string; - - /** - * The account associated with the session. - */ - readonly account: AuthenticationSessionAccountInformation; - - /** - * The permissions granted by the session's access token. Available scopes - * are defined by the {@link AuthenticationProvider}. - */ - readonly scopes: readonly string[]; - } - - /** - * The information of an account associated with an {@link AuthenticationSession}. - */ - export interface AuthenticationSessionAccountInformation { - /** - * The unique identifier of the account. - */ - readonly id: string; - - /** - * The human-readable name of the account. - */ - readonly label: string; - } - - - /** - * Options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. - */ - export interface AuthenticationGetSessionOptions { - /** - * Whether the existing user session preference should be cleared. - * - * For authentication providers that support being signed into multiple accounts at once, the user will be - * prompted to select an account to use when {@link authentication.getSession getSession} is called. This preference - * is remembered until {@link authentication.getSession getSession} is called with this flag. - * - * Defaults to false. - */ - clearSessionPreference?: boolean; - - /** - * Whether login should be performed if there is no matching session. - * - * If true, a modal dialog will be shown asking the user to sign in. If false, a numbered badge will be shown - * on the accounts activity bar icon. An entry for the extension will be added under the menu to sign in. This - * allows quietly prompting the user to sign in. - * - * If there is a matching session but the extension has not been granted access to it, setting this to true - * will also result in an immediate modal dialog, and false will add a numbered badge to the accounts icon. - * - * Defaults to false. - * - * Note: you cannot use this option with {@link AuthenticationGetSessionOptions.silent silent}. - */ - createIfNone?: boolean; - - /** - * Whether we should attempt to reauthenticate even if there is already a session available. - * - * If true, a modal dialog will be shown asking the user to sign in again. This is mostly used for scenarios - * where the token needs to be re minted because it has lost some authorization. - * - * If there are no existing sessions and forceNewSession is true, it will behave identically to - * {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. - * - * This defaults to false. - */ - forceNewSession?: boolean | { detail: string }; - - /** - * Whether we should show the indication to sign in in the Accounts menu. - * - * If false, the user will be shown a badge on the Accounts menu with an option to sign in for the extension. - * If true, no indication will be shown. - * - * Defaults to false. - * - * Note: you cannot use this option with any other options that prompt the user like {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. - */ - silent?: boolean; - } - - /** - * Basic information about an {@link AuthenticationProvider} - */ - export interface AuthenticationProviderInformation { - /** - * The unique identifier of the authentication provider. - */ - readonly id: string; - - /** - * The human-readable name of the authentication provider. - */ - readonly label: string; - } - - /** - * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. - */ - export interface AuthenticationSessionsChangeEvent { - /** - * The {@link AuthenticationProvider} that has had its sessions change. - */ - readonly provider: AuthenticationProviderInformation; - } - - /** - * Options for creating an {@link AuthenticationProvider}. - */ - export interface AuthenticationProviderOptions { - /** - * Whether it is possible to be signed into multiple accounts at once with this provider. - * If not specified, will default to false. - */ - readonly supportsMultipleAccounts?: boolean; - } - - /** - * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. - */ - export interface AuthenticationProviderAuthenticationSessionsChangeEvent { - /** - * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been added. - */ - readonly added: readonly AuthenticationSession[] | undefined; - - /** - * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been removed. - */ - readonly removed: readonly AuthenticationSession[] | undefined; - - /** - * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been changed. - * A session changes when its data excluding the id are updated. An example of this is a session refresh that results in a new - * access token being set for the session. - */ - readonly changed: readonly AuthenticationSession[] | undefined; - } - - /** - * A provider for performing authentication to a service. - */ - export interface AuthenticationProvider { - /** - * An {@link Event} which fires when the array of sessions has changed, or data - * within a session has changed. - */ - readonly onDidChangeSessions: Event; - - /** - * Get a list of sessions. - * @param scopes An optional list of scopes. If provided, the sessions returned should match - * these permissions, otherwise all sessions should be returned. - * @returns A promise that resolves to an array of authentication sessions. - */ - getSessions(scopes?: readonly string[]): Thenable; - - /** - * Prompts a user to login. - * - * If login is successful, the onDidChangeSessions event should be fired. - * - * If login fails, a rejected promise should be returned. - * - * If the provider has specified that it does not support multiple accounts, - * then this should never be called if there is already an existing session matching these - * scopes. - * @param scopes A list of scopes, permissions, that the new session should be created with. - * @returns A promise that resolves to an authentication session. - */ - createSession(scopes: readonly string[]): Thenable; - - /** - * Removes the session corresponding to session id. - * - * If the removal is successful, the onDidChangeSessions event should be fired. - * - * If a session cannot be removed, the provider should reject with an error message. - * @param sessionId The id of the session to remove. - */ - removeSession(sessionId: string): Thenable; - } - - - /** - * Namespace for authentication. - */ - export namespace authentication { - /** - * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not - * registered, or if the user does not consent to sharing authentication information with - * the extension. If there are multiple sessions with the same scopes, the user will be shown a - * quickpick to select which account they would like to use. - * - * Currently, there are only two authentication providers that are contributed from built in extensions - * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. - * @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 - * @param options The {@link AuthenticationGetSessionOptions} to use - * @returns A thenable that resolves to an authentication session - */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { createIfNone: true }): Thenable; - - /** - * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not - * registered, or if the user does not consent to sharing authentication information with - * the extension. If there are multiple sessions with the same scopes, the user will be shown a - * quickpick to select which account they would like to use. - * - * Currently, there are only two authentication providers that are contributed from built in extensions - * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. - * @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 - * @param options The {@link AuthenticationGetSessionOptions} to use - * @returns A thenable that resolves to an authentication session - */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | { detail: string } }): Thenable; - - /** - * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not - * registered, or if the user does not consent to sharing authentication information with - * the extension. If there are multiple sessions with the same scopes, the user will be shown a - * quickpick to select which account they would like to use. - * - * Currently, there are only two authentication providers that are contributed from built in extensions - * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. - * @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 - * @param options The {@link AuthenticationGetSessionOptions} to use - * @returns A thenable that resolves to an authentication session if available, or undefined if there are no sessions - */ - export function getSession(providerId: string, scopes: readonly string[], options?: AuthenticationGetSessionOptions): Thenable; - - /** - * An {@link Event} which fires when the authentication sessions of an authentication provider have - * been added, removed, or changed. - */ - export const onDidChangeSessions: Event; - - /** - * Register an authentication provider. - * - * There can only be one provider per id and an error is being thrown when an id - * has already been used by another provider. Ids are case-sensitive. - * - * @param id The unique identifier of the provider. - * @param label The human-readable name of the provider. - * @param provider The authentication provider provider. - * @params options Additional options for the provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerAuthenticationProvider(id: string, label: string, provider: AuthenticationProvider, options?: AuthenticationProviderOptions): Disposable; - } - - /** - * Namespace for localization-related functionality in the extension API. To use this properly, - * you must have `l10n` defined in your extension manifest and have bundle.l10n..json files. - * For more information on how to generate bundle.l10n..json files, check out the - * [vscode-l10n repo](https://github.com/microsoft/vscode-l10n). - * - * Note: Built-in extensions (for example, Git, TypeScript Language Features, GitHub Authentication) - * are excluded from the `l10n` property requirement. In other words, they do not need to specify - * a `l10n` in the extension manifest because their translated strings come from Language Packs. - */ - export namespace l10n { - /** - * Marks a string for localization. If a localized bundle is available for the language specified by - * {@link env.language} and the bundle has a localized value for this message, then that localized - * value will be returned (with injected {@link args} values for any templated values). - * @param message - The message to localize. Supports index templating where strings like `{0}` and `{1}` are - * replaced by the item at that index in the {@link args} array. - * @param args - The arguments to be used in the localized string. The index of the argument is used to - * match the template placeholder in the localized string. - * @returns localized string with injected arguments. - * @example `l10n.t('Hello {0}!', 'World');` - */ - export function t(message: string, ...args: Array): string; - - /** - * Marks a string for localization. If a localized bundle is available for the language specified by - * {@link env.language} and the bundle has a localized value for this message, then that localized - * value will be returned (with injected {@link args} values for any templated values). - * @param message The message to localize. Supports named templating where strings like `{foo}` and `{bar}` are - * replaced by the value in the Record for that key (foo, bar, etc). - * @param args The arguments to be used in the localized string. The name of the key in the record is used to - * match the template placeholder in the localized string. - * @returns localized string with injected arguments. - * @example `l10n.t('Hello {name}', { name: 'Erich' });` - */ - export function t(message: string, args: Record): string; - /** - * Marks a string for localization. If a localized bundle is available for the language specified by - * {@link env.language} and the bundle has a localized value for this message, then that localized - * value will be returned (with injected args values for any templated values). - * @param options The options to use when localizing the message. - * @returns localized string with injected arguments. - */ - export function t(options: { - /** - * The message to localize. If {@link args} is an array, this message supports index templating where strings like - * `{0}` and `{1}` are replaced by the item at that index in the {@link args} array. If `args` is a `Record`, - * this supports named templating where strings like `{foo}` and `{bar}` are replaced by the value in - * the Record for that key (foo, bar, etc). - */ - message: string; - /** - * The arguments to be used in the localized string. As an array, the index of the argument is used to - * match the template placeholder in the localized string. As a Record, the key is used to match the template - * placeholder in the localized string. - */ - args?: Array | Record; - /** - * A comment to help translators understand the context of the message. - */ - comment: string | string[]; - }): string; - /** - * The bundle of localized strings that have been loaded for the extension. - * It's undefined if no bundle has been loaded. The bundle is typically not loaded if - * there was no bundle found or when we are running with the default language. - */ - export const bundle: { [key: string]: string } | undefined; - /** - * The URI of the localization bundle that has been loaded for the extension. - * It's undefined if no bundle has been loaded. The bundle is typically not loaded if - * there was no bundle found or when we are running with the default language. - */ - export const uri: Uri | undefined; - } - - /** - * Namespace for testing functionality. Tests are published by registering - * {@link TestController} instances, then adding {@link TestItem TestItems}. - * Controllers may also describe how to run tests by creating one or more - * {@link TestRunProfile} instances. - */ - export namespace tests { - /** - * Creates a new test controller. - * - * @param id Identifier for the controller, must be globally unique. - * @param label A human-readable label for the controller. - * @returns An instance of the {@link TestController}. - */ - export function createTestController(id: string, label: string): TestController; - } - - /** - * The kind of executions that {@link TestRunProfile TestRunProfiles} control. - */ - export enum TestRunProfileKind { - Run = 1, - Debug = 2, - Coverage = 3, - } - - /** - * Tags can be associated with {@link TestItem TestItems} and - * {@link TestRunProfile TestRunProfiles}. A profile with a tag can only - * execute tests that include that tag in their {@link TestItem.tags} array. - */ - export class TestTag { - /** - * ID of the test tag. `TestTag` instances with the same ID are considered - * to be identical. - */ - readonly id: string; - - /** - * Creates a new TestTag instance. - * @param id ID of the test tag. - */ - constructor(id: string); - } - - /** - * A TestRunProfile describes one way to execute tests in a {@link TestController}. - */ - export interface TestRunProfile { - /** - * Label shown to the user in the UI. - * - * Note that the label has some significance if the user requests that - * tests be re-run in a certain way. For example, if tests were run - * normally and the user requests to re-run them in debug mode, the editor - * will attempt use a configuration with the same label of the `Debug` - * kind. If there is no such configuration, the default will be used. - */ - label: string; - - /** - * Configures what kind of execution this profile controls. If there - * are no profiles for a kind, it will not be available in the UI. - */ - readonly kind: TestRunProfileKind; - - /** - * Controls whether this profile is the default action that will - * be taken when its kind is actioned. For example, if the user clicks - * the generic "run all" button, then the default profile for - * {@link TestRunProfileKind.Run} will be executed, although the - * user can configure this. - */ - isDefault: boolean; - - /** - * Associated tag for the profile. If this is set, only {@link TestItem} - * instances with the same tag will be eligible to execute in this profile. - */ - tag: TestTag | undefined; - - /** - * If this method is present, a configuration gear will be present in the - * UI, and this method will be invoked when it's clicked. When called, - * you can take other editor actions, such as showing a quick pick or - * opening a configuration file. - */ - configureHandler: (() => void) | undefined; - - /** - * Handler called to start a test run. When invoked, the function should call - * {@link TestController.createTestRun} at least once, and all test runs - * associated with the request should be created before the function returns - * or the returned promise is resolved. - * - * @param request Request information for the test run. - * @param cancellationToken Token that signals the used asked to abort the - * test run. If cancellation is requested on this token, all {@link TestRun} - * instances associated with the request will be - * automatically cancelled as well. - */ - runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void; - - /** - * Deletes the run profile. - */ - dispose(): void; - } - - /** - * Entry point to discover and execute tests. It contains {@link TestController.items} which - * are used to populate the editor UI, and is associated with - * {@link TestController.createRunProfile run profiles} to allow - * for tests to be executed. - */ - export interface TestController { - /** - * The id of the controller passed in {@link vscode.tests.createTestController}. - * This must be globally unique. - */ - readonly id: string; - - /** - * Human-readable label for the test controller. - */ - label: string; - - /** - * A collection of "top-level" {@link TestItem} instances, which can in - * turn have their own {@link TestItem.children children} to form the - * "test tree." - * - * The extension controls when to add tests. For example, extensions should - * add tests for a file when {@link vscode.workspace.onDidOpenTextDocument} - * fires in order for decorations for tests within a file to be visible. - * - * However, the editor may sometimes explicitly request children using the - * {@link resolveHandler} See the documentation on that method for more details. - */ - readonly items: TestItemCollection; - - /** - * Creates a profile used for running tests. Extensions must create - * at least one profile in order for tests to be run. - * @param label A human-readable label for this profile. - * @param kind Configures what kind of execution this profile manages. - * @param runHandler Function called to start a test run. - * @param isDefault Whether this is the default action for its kind. - * @param tag Profile test tag. - * @returns An instance of a {@link TestRunProfile}, which is automatically - * associated with this controller. - */ - createRunProfile(label: string, kind: TestRunProfileKind, runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void, isDefault?: boolean, tag?: TestTag): TestRunProfile; - - /** - * A function provided by the extension that the editor may call to request - * children of a test item, if the {@link TestItem.canResolveChildren} is - * `true`. When called, the item should discover children and call - * {@link vscode.tests.createTestItem} as children are discovered. - * - * Generally the extension manages the lifecycle of test items, but under - * certain conditions the editor may request the children of a specific - * item to be loaded. For example, if the user requests to re-run tests - * after reloading the editor, the editor may need to call this method - * to resolve the previously-run tests. - * - * The item in the explorer will automatically be marked as "busy" until - * the function returns or the returned thenable resolves. - * - * @param item An unresolved test item for which children are being - * requested, or `undefined` to resolve the controller's initial {@link TestController.items items}. - */ - resolveHandler?: (item: TestItem | undefined) => Thenable | void; - - /** - * If this method is present, a refresh button will be present in the - * UI, and this method will be invoked when it's clicked. When called, - * the extension should scan the workspace for any new, changed, or - * removed tests. - * - * It's recommended that extensions try to update tests in realtime, using - * a {@link FileSystemWatcher} for example, and use this method as a fallback. - * - * @returns A thenable that resolves when tests have been refreshed. - */ - refreshHandler: ((token: CancellationToken) => Thenable | void) | undefined; - - /** - * Creates a {@link TestRun}. This should be called by the - * {@link TestRunProfile} when a request is made to execute tests, and may - * also be called if a test run is detected externally. Once created, tests - * that are included in the request will be moved into the queued state. - * - * All runs created using the same `request` instance will be grouped - * together. This is useful if, for example, a single suite of tests is - * run on multiple platforms. - * - * @param request Test run request. Only tests inside the `include` may be - * modified, and tests in its `exclude` are ignored. - * @param name The human-readable name of the run. This can be used to - * disambiguate multiple sets of results in a test run. It is useful if - * tests are run across multiple platforms, for example. - * @param persist Whether the results created by the run should be - * persisted in the editor. This may be false if the results are coming from - * a file already saved externally, such as a coverage information file. - * @returns An instance of the {@link TestRun}. It will be considered "running" - * from the moment this method is invoked until {@link TestRun.end} is called. - */ - createTestRun(request: TestRunRequest, name?: string, persist?: boolean): TestRun; - - /** - * Creates a new managed {@link TestItem} instance. It can be added into - * the {@link TestItem.children} of an existing item, or into the - * {@link TestController.items}. - * - * @param id Identifier for the TestItem. The test item's ID must be unique - * in the {@link TestItemCollection} it's added to. - * @param label Human-readable label of the test item. - * @param uri URI this TestItem is associated with. May be a file or directory. - */ - createTestItem(id: string, label: string, uri?: Uri): TestItem; - - /** - * Unregisters the test controller, disposing of its associated tests - * and unpersisted results. - */ - dispose(): void; - } - - /** - * A TestRunRequest is a precursor to a {@link TestRun}, which in turn is - * created by passing a request to {@link TestController.createTestRun}. The - * TestRunRequest contains information about which tests should be run, which - * should not be run, and how they are run (via the {@link TestRunRequest.profile profile}). - * - * In general, TestRunRequests are created by the editor and pass to - * {@link TestRunProfile.runHandler}, however you can also create test - * requests and runs outside of the `runHandler`. - */ - export class TestRunRequest { - /** - * A filter for specific tests to run. If given, the extension should run - * all of the included tests and all their children, excluding any tests - * that appear in {@link TestRunRequest.exclude}. If this property is - * undefined, then the extension should simply run all tests. - * - * The process of running tests should resolve the children of any test - * items who have not yet been resolved. - */ - readonly include: readonly TestItem[] | undefined; - - /** - * An array of tests the user has marked as excluded from the test included - * in this run; exclusions should apply after inclusions. - * - * May be omitted if no exclusions were requested. Test controllers should - * not run excluded tests or any children of excluded tests. - */ - readonly exclude: readonly TestItem[] | undefined; - - /** - * The profile used for this request. This will always be defined - * for requests issued from the editor UI, though extensions may - * programmatically create requests not associated with any profile. - */ - readonly profile: TestRunProfile | undefined; - - /** - * @param include Array of specific tests to run, or undefined to run all tests - * @param exclude An array of tests to exclude from the run. - * @param profile The run profile used for this request. - */ - constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile); - } - - /** - * A TestRun represents an in-progress or completed test run and - * provides methods to report the state of individual tests in the run. - */ - export interface TestRun { - /** - * The human-readable name of the run. This can be used to - * disambiguate multiple sets of results in a test run. It is useful if - * tests are run across multiple platforms, for example. - */ - readonly name: string | undefined; - - /** - * A cancellation token which will be triggered when the test run is - * canceled from the UI. - */ - readonly token: CancellationToken; - - /** - * Whether the test run will be persisted across reloads by the editor. - */ - readonly isPersisted: boolean; - - /** - * Indicates a test is queued for later execution. - * @param test Test item to update. - */ - enqueued(test: TestItem): void; - - /** - * Indicates a test has started running. - * @param test Test item to update. - */ - started(test: TestItem): void; - - /** - * Indicates a test has been skipped. - * @param test Test item to update. - */ - skipped(test: TestItem): void; - - /** - * Indicates a test has failed. You should pass one or more - * {@link TestMessage TestMessages} to describe the failure. - * @param test Test item to update. - * @param message Messages associated with the test failure. - * @param duration How long the test took to execute, in milliseconds. - */ - failed(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void; - - /** - * Indicates a test has errored. You should pass one or more - * {@link TestMessage TestMessages} to describe the failure. This differs - * from the "failed" state in that it indicates a test that couldn't be - * executed at all, from a compilation error for example. - * @param test Test item to update. - * @param message Messages associated with the test failure. - * @param duration How long the test took to execute, in milliseconds. - */ - errored(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void; - - /** - * Indicates a test has passed. - * @param test Test item to update. - * @param duration How long the test took to execute, in milliseconds. - */ - passed(test: TestItem, duration?: number): void; - - /** - * Appends raw output from the test runner. On the user's request, the - * output will be displayed in a terminal. ANSI escape sequences, - * such as colors and text styles, are supported. - * - * @param output Output text to append. - * @param location Indicate that the output was logged at the given - * location. - * @param test Test item to associate the output with. - */ - appendOutput(output: string, location?: Location, test?: TestItem): void; - - /** - * Signals that the end of the test run. Any tests included in the run whose - * states have not been updated will have their state reset. - */ - end(): void; - } - - /** - * Collection of test items, found in {@link TestItem.children} and - * {@link TestController.items}. - */ - export interface TestItemCollection extends Iterable<[id: string, testItem: TestItem]> { - /** - * Gets the number of items in the collection. - */ - readonly size: number; - - /** - * Replaces the items stored by the collection. - * @param items Items to store. - */ - replace(items: readonly TestItem[]): void; - - /** - * Iterate over each entry in this collection. - * - * @param callback Function to execute for each entry. - * @param thisArg The `this` context used when invoking the handler function. - */ - forEach(callback: (item: TestItem, collection: TestItemCollection) => unknown, thisArg?: any): void; - - /** - * Adds the test item to the children. If an item with the same ID already - * exists, it'll be replaced. - * @param item Item to add. - */ - add(item: TestItem): void; - - /** - * Removes a single test item from the collection. - * @param itemId Item ID to delete. - */ - delete(itemId: string): void; - - /** - * Efficiently gets a test item by ID, if it exists, in the children. - * @param itemId Item ID to get. - * @returns The found item or undefined if it does not exist. - */ - get(itemId: string): TestItem | undefined; - } - - /** - * An item shown in the "test explorer" view. - * - * A `TestItem` can represent either a test suite or a test itself, since - * they both have similar capabilities. - */ - export interface TestItem { - /** - * Identifier for the `TestItem`. This is used to correlate - * test results and tests in the document with those in the workspace - * (test explorer). This cannot change for the lifetime of the `TestItem`, - * and must be unique among its parent's direct children. - */ - readonly id: string; - - /** - * URI this `TestItem` is associated with. May be a file or directory. - */ - readonly uri: Uri | undefined; - - /** - * The children of this test item. For a test suite, this may contain the - * individual test cases or nested suites. - */ - readonly children: TestItemCollection; - - /** - * The parent of this item. It's set automatically, and is undefined - * top-level items in the {@link TestController.items} and for items that - * aren't yet included in another item's {@link TestItem.children children}. - */ - readonly parent: TestItem | undefined; - - /** - * Tags associated with this test item. May be used in combination with - * {@link TestRunProfile.tags}, or simply as an organizational feature. - */ - tags: readonly TestTag[]; - - /** - * Indicates whether this test item may have children discovered by resolving. - * - * If true, this item is shown as expandable in the Test Explorer view and - * expanding the item will cause {@link TestController.resolveHandler} - * to be invoked with the item. - * - * Default to `false`. - */ - canResolveChildren: boolean; - - /** - * Controls whether the item is shown as "busy" in the Test Explorer view. - * This is useful for showing status while discovering children. - * - * Defaults to `false`. - */ - busy: boolean; - - /** - * Display name describing the test case. - */ - label: string; - - /** - * Optional description that appears next to the label. - */ - description?: string; - - /** - * A string that should be used when comparing this item - * with other items. When `falsy` the {@link TestItem.label label} - * is used. - */ - sortText?: string | undefined; - - /** - * Location of the test item in its {@link TestItem.uri uri}. - * - * This is only meaningful if the `uri` points to a file. - */ - range: Range | undefined; - - /** - * Optional error encountered while loading the test. - * - * Note that this is not a test result and should only be used to represent errors in - * test discovery, such as syntax errors. - */ - error: string | MarkdownString | undefined; - } - - /** - * Message associated with the test state. Can be linked to a specific - * source range -- useful for assertion failures, for example. - */ - export class TestMessage { - /** - * Human-readable message text to display. - */ - message: string | MarkdownString; - - /** - * Expected test output. If given with {@link TestMessage.actualOutput actualOutput }, a diff view will be shown. - */ - expectedOutput?: string; - - /** - * Actual test output. If given with {@link TestMessage.expectedOutput expectedOutput }, a diff view will be shown. - */ - actualOutput?: string; - - /** - * Associated file location. - */ - location?: Location; - - /** - * Creates a new TestMessage that will present as a diff in the editor. - * @param message Message to display to the user. - * @param expected Expected output. - * @param actual Actual output. - */ - static diff(message: string | MarkdownString, expected: string, actual: string): TestMessage; - - /** - * Creates a new TestMessage instance. - * @param message The message to show to the user. - */ - constructor(message: string | MarkdownString); - } - - /** - * The tab represents a single text based resource. - */ - export class TabInputText { - /** - * The uri represented by the tab. - */ - readonly uri: Uri; - /** - * Constructs a text tab input with the given URI. - * @param uri The URI of the tab. - */ - constructor(uri: Uri); - } - - /** - * The tab represents two text based resources - * being rendered as a diff. - */ - export class TabInputTextDiff { - /** - * The uri of the original text resource. - */ - readonly original: Uri; - /** - * The uri of the modified text resource. - */ - readonly modified: Uri; - /** - * Constructs a new text diff tab input with the given URIs. - * @param original The uri of the original text resource. - * @param modified The uri of the modified text resource. - */ - constructor(original: Uri, modified: Uri); - } - - /** - * The tab represents a custom editor. - */ - export class TabInputCustom { - /** - * The uri that the tab is representing. - */ - readonly uri: Uri; - /** - * The type of custom editor. - */ - readonly viewType: string; - /** - * Constructs a custom editor tab input. - * @param uri The uri of the tab. - * @param viewType The viewtype of the custom editor. - */ - constructor(uri: Uri, viewType: string); - } - - /** - * The tab represents a webview. - */ - export class TabInputWebview { - /** - * The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} - */ - readonly viewType: string; - /** - * Constructs a webview tab input with the given view type. - * @param viewType The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} - */ - constructor(viewType: string); - } - - /** - * The tab represents a notebook. - */ - export class TabInputNotebook { - /** - * The uri that the tab is representing. - */ - readonly uri: Uri; - /** - * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} - */ - readonly notebookType: string; - /** - * Constructs a new tab input for a notebook. - * @param uri The uri of the notebook. - * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} - */ - constructor(uri: Uri, notebookType: string); - } - - /** - * The tabs represents two notebooks in a diff configuration. - */ - export class TabInputNotebookDiff { - /** - * The uri of the original notebook. - */ - readonly original: Uri; - /** - * The uri of the modified notebook. - */ - readonly modified: Uri; - /** - * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} - */ - readonly notebookType: string; - /** - * Constructs a notebook diff tab input. - * @param original The uri of the original unmodified notebook. - * @param modified The uri of the modified notebook. - * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} - */ - constructor(original: Uri, modified: Uri, notebookType: string); - } - - /** - * The tab represents a terminal in the editor area. - */ - export class TabInputTerminal { - /** - * Constructs a terminal tab input. - */ - constructor(); - } - - /** - * Represents a tab within a {@link TabGroup group of tabs}. - * Tabs are merely the graphical representation within the editor area. - * A backing editor is not a guarantee. - */ - export interface Tab { - - /** - * The text displayed on the tab. - */ - readonly label: string; - - /** - * The group which the tab belongs to. - */ - readonly group: TabGroup; - - /** - * Defines the structure of the tab i.e. text, notebook, custom, etc. - * Resource and other useful properties are defined on the tab kind. - */ - readonly input: TabInputText | TabInputTextDiff | TabInputCustom | TabInputWebview | TabInputNotebook | TabInputNotebookDiff | TabInputTerminal | unknown; - - /** - * Whether or not the tab is currently active. - * This is dictated by being the selected tab in the group. - */ - readonly isActive: boolean; - - /** - * Whether or not the dirty indicator is present on the tab. - */ - readonly isDirty: boolean; - - /** - * Whether or not the tab is pinned (pin icon is present). - */ - readonly isPinned: boolean; - - /** - * Whether or not the tab is in preview mode. - */ - readonly isPreview: boolean; - } - - /** - * An event describing change to tabs. - */ - export interface TabChangeEvent { - /** - * The tabs that have been opened. - */ - readonly opened: readonly Tab[]; - /** - * The tabs that have been closed. - */ - readonly closed: readonly Tab[]; - /** - * Tabs that have changed, e.g have changed - * their {@link Tab.isActive active} state. - */ - readonly changed: readonly Tab[]; - } - - /** - * An event describing changes to tab groups. - */ - export interface TabGroupChangeEvent { - /** - * Tab groups that have been opened. - */ - readonly opened: readonly TabGroup[]; - /** - * Tab groups that have been closed. - */ - readonly closed: readonly TabGroup[]; - /** - * Tab groups that have changed, e.g have changed - * their {@link TabGroup.isActive active} state. - */ - readonly changed: readonly TabGroup[]; - } - - /** - * Represents a group of tabs. A tab group itself consists of multiple tabs. - */ - export interface TabGroup { - /** - * Whether or not the group is currently active. - * - * *Note* that only one tab group is active at a time, but that multiple tab - * groups can have an {@link TabGroup.aciveTab active tab}. - * - * @see {@link Tab.isActive} - */ - readonly isActive: boolean; - - /** - * The view column of the group. - */ - readonly viewColumn: ViewColumn; - - /** - * The active {@link Tab tab} in the group. This is the tab whose contents are currently - * being rendered. - * - * *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}. - */ - readonly activeTab: Tab | undefined; - - /** - * The list of tabs contained within the group. - * This can be empty if the group has no tabs open. - */ - readonly tabs: readonly Tab[]; - } - - /** - * Represents the main editor area which consists of multple groups which contain tabs. - */ - export interface TabGroups { - /** - * All the groups within the group container. - */ - readonly all: readonly TabGroup[]; - - /** - * The currently active group. - */ - readonly activeTabGroup: TabGroup; - - /** - * An {@link Event event} which fires when {@link TabGroup tab groups} have changed. - */ - readonly onDidChangeTabGroups: Event; - - /** - * An {@link Event event} which fires when {@link Tab tabs} have changed. - */ - readonly onDidChangeTabs: Event; - - /** - * Closes the tab. This makes the tab object invalid and the tab - * should no longer be used for further actions. - * Note: In the case of a dirty tab, a confirmation dialog will be shown which may be cancelled. If cancelled the tab is still valid - * - * @param tab The tab to close. - * @param preserveFocus When `true` focus will remain in its current position. If `false` it will jump to the next tab. - * @returns A promise that resolves to `true` when all tabs have been closed. - */ - close(tab: Tab | readonly Tab[], preserveFocus?: boolean): Thenable; - - /** - * Closes the tab group. This makes the tab group object invalid and the tab group - * should no longer be used for further actions. - * @param tabGroup The tab group to close. - * @param preserveFocus When `true` focus will remain in its current position. - * @returns A promise that resolves to `true` when all tab groups have been closed. - */ - close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; - } -} - -/** - * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, - * and others. This API makes no assumption about what promise library is being used which - * enables reusing existing code without migrating to a specific promise implementation. Still, - * we recommend the use of native promises which are available in this editor. - */ -interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + /** + * The version of the editor. + */ + export const version: string; + + /** + * Represents a reference to a command. Provides a title which + * will be used to represent a command in the UI and, optionally, + * an array of arguments which will be passed to the command handler + * function when invoked. + */ + export interface Command { + /** + * Title of the command, like `save`. + */ + title: string; + + /** + * The identifier of the actual command handler. + * @see {@link commands.registerCommand} + */ + command: string; + + /** + * A tooltip for the command, when represented in the UI. + */ + tooltip?: string; + + /** + * Arguments that the command handler should be + * invoked with. + */ + arguments?: any[]; + } + + /** + * Represents a line of text, such as a line of source code. + * + * TextLine objects are __immutable__. When a {@link TextDocument document} changes, + * previously retrieved lines will not represent the latest state. + */ + export interface TextLine { + + /** + * The zero-based line number. + */ + readonly lineNumber: number; + + /** + * The text of this line without the line separator characters. + */ + readonly text: string; + + /** + * The range this line covers without the line separator characters. + */ + readonly range: Range; + + /** + * The range this line covers with the line separator characters. + */ + readonly rangeIncludingLineBreak: Range; + + /** + * The offset of the first character which is not a whitespace character as defined + * by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned. + */ + readonly firstNonWhitespaceCharacterIndex: number; + + /** + * Whether this line is whitespace only, shorthand + * for {@link TextLine.firstNonWhitespaceCharacterIndex} === {@link TextLine.text TextLine.text.length}. + */ + readonly isEmptyOrWhitespace: boolean; + } + + /** + * Represents a text document, such as a source file. Text documents have + * {@link TextLine lines} and knowledge about an underlying resource like a file. + */ + export interface TextDocument { + + /** + * The associated uri for this document. + * + * *Note* that most documents use the `file`-scheme, which means they are files on disk. However, **not** all documents are + * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. + * + * @see {@link FileSystemProvider} + * @see {@link TextDocumentContentProvider} + */ + readonly uri: Uri; + + /** + * The file system path of the associated resource. Shorthand + * notation for {@link TextDocument.uri TextDocument.uri.fsPath}. Independent of the uri scheme. + */ + readonly fileName: string; + + /** + * Is this document representing an untitled file which has never been saved yet. *Note* that + * this does not mean the document will be saved to disk, use {@linkcode Uri.scheme} + * to figure out where a document will be {@link FileSystemProvider saved}, e.g. `file`, `ftp` etc. + */ + readonly isUntitled: boolean; + + /** + * The identifier of the language associated with this document. + */ + readonly languageId: string; + + /** + * The version number of this document (it will strictly increase after each + * change, including undo/redo). + */ + readonly version: number; + + /** + * `true` if there are unpersisted changes. + */ + readonly isDirty: boolean; + + /** + * `true` if the document has been closed. A closed document isn't synchronized anymore + * and won't be re-used when the same resource is opened again. + */ + readonly isClosed: boolean; + + /** + * Save the underlying file. + * + * @return A promise that will resolve to `true` when the file + * has been saved. If the save failed, will return `false`. + */ + save(): Thenable; + + /** + * The {@link EndOfLine end of line} sequence that is predominately + * used in this document. + */ + readonly eol: EndOfLine; + + /** + * The number of lines in this document. + */ + readonly lineCount: number; + + /** + * Returns a text line denoted by the line number. Note + * that the returned object is *not* live and changes to the + * document are not reflected. + * + * @param line A line number in [0, lineCount). + * @return A {@link TextLine line}. + */ + lineAt(line: number): TextLine; + + /** + * Returns a text line denoted by the position. Note + * that the returned object is *not* live and changes to the + * document are not reflected. + * + * The position will be {@link TextDocument.validatePosition adjusted}. + * + * @see {@link TextDocument.lineAt} + * + * @param position A position. + * @return A {@link TextLine line}. + */ + lineAt(position: Position): TextLine; + + /** + * Converts the position to a zero-based offset. + * + * The position will be {@link TextDocument.validatePosition adjusted}. + * + * @param position A position. + * @return A valid zero-based offset. + */ + offsetAt(position: Position): number; + + /** + * Converts a zero-based offset to a position. + * + * @param offset A zero-based offset. + * @return A valid {@link Position}. + */ + positionAt(offset: number): Position; + + /** + * Get the text of this document. A substring can be retrieved by providing + * a range. The range will be {@link TextDocument.validateRange adjusted}. + * + * @param range Include only the text included by the range. + * @return The text inside the provided range or the entire text. + */ + getText(range?: Range): string; + + /** + * Get a word-range at the given position. By default words are defined by + * common separators, like space, -, _, etc. In addition, per language custom + * [word definitions} can be defined. It + * is also possible to provide a custom regular expression. + * + * * *Note 1:* A custom regular expression must not match the empty string and + * if it does, it will be ignored. + * * *Note 2:* A custom regular expression will fail to match multiline strings + * and in the name of speed regular expressions should not match words with + * spaces. Use {@linkcode TextLine.text} for more complex, non-wordy, scenarios. + * + * The position will be {@link TextDocument.validatePosition adjusted}. + * + * @param position A position. + * @param regex Optional regular expression that describes what a word is. + * @return A range spanning a word, or `undefined`. + */ + getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined; + + /** + * Ensure a range is completely contained in this document. + * + * @param range A range. + * @return The given range or a new, adjusted range. + */ + validateRange(range: Range): Range; + + /** + * Ensure a position is contained in the range of this document. + * + * @param position A position. + * @return The given position or a new, adjusted position. + */ + validatePosition(position: Position): Position; + } + + /** + * Represents a line and character position, such as + * the position of the cursor. + * + * Position objects are __immutable__. Use the {@link Position.with with} or + * {@link Position.translate translate} methods to derive new positions + * from an existing position. + */ + export class Position { + + /** + * The zero-based line value. + */ + readonly line: number; + + /** + * The zero-based character value. + */ + readonly character: number; + + /** + * @param line A zero-based line value. + * @param character A zero-based character value. + */ + constructor(line: number, character: number); + + /** + * Check if this position is before `other`. + * + * @param other A position. + * @return `true` if position is on a smaller line + * or on the same line on a smaller character. + */ + isBefore(other: Position): boolean; + + /** + * Check if this position is before or equal to `other`. + * + * @param other A position. + * @return `true` if position is on a smaller line + * or on the same line on a smaller or equal character. + */ + isBeforeOrEqual(other: Position): boolean; + + /** + * Check if this position is after `other`. + * + * @param other A position. + * @return `true` if position is on a greater line + * or on the same line on a greater character. + */ + isAfter(other: Position): boolean; + + /** + * Check if this position is after or equal to `other`. + * + * @param other A position. + * @return `true` if position is on a greater line + * or on the same line on a greater or equal character. + */ + isAfterOrEqual(other: Position): boolean; + + /** + * Check if this position is equal to `other`. + * + * @param other A position. + * @return `true` if the line and character of the given position are equal to + * the line and character of this position. + */ + isEqual(other: Position): boolean; + + /** + * Compare this to `other`. + * + * @param other A position. + * @return A number smaller than zero if this position is before the given position, + * a number greater than zero if this position is after the given position, or zero when + * this and the given position are equal. + */ + compareTo(other: Position): number; + + /** + * Create a new position relative to this position. + * + * @param lineDelta Delta value for the line value, default is `0`. + * @param characterDelta Delta value for the character value, default is `0`. + * @return A position which line and character is the sum of the current line and + * character and the corresponding deltas. + */ + translate(lineDelta?: number, characterDelta?: number): Position; + + /** + * Derived a new position relative to this position. + * + * @param change An object that describes a delta to this position. + * @return A position that reflects the given delta. Will return `this` position if the change + * is not changing anything. + */ + translate(change: { lineDelta?: number; characterDelta?: number }): Position; + + /** + * Create a new position derived from this position. + * + * @param line Value that should be used as line value, default is the {@link Position.line existing value} + * @param character Value that should be used as character value, default is the {@link Position.character existing value} + * @return A position where line and character are replaced by the given values. + */ + with(line?: number, character?: number): Position; + + /** + * Derived a new position from this position. + * + * @param change An object that describes a change to this position. + * @return A position that reflects the given change. Will return `this` position if the change + * is not changing anything. + */ + with(change: { line?: number; character?: number }): Position; + } + + /** + * A range represents an ordered pair of two positions. + * It is guaranteed that {@link Range.start start}.isBeforeOrEqual({@link Range.end end}) + * + * Range objects are __immutable__. Use the {@link Range.with with}, + * {@link Range.intersection intersection}, or {@link Range.union union} methods + * to derive new ranges from an existing range. + */ + export class Range { + + /** + * The start position. It is before or equal to {@link Range.end end}. + */ + readonly start: Position; + + /** + * The end position. It is after or equal to {@link Range.start start}. + */ + readonly end: Position; + + /** + * Create a new range from two positions. If `start` is not + * before or equal to `end`, the values will be swapped. + * + * @param start A position. + * @param end A position. + */ + constructor(start: Position, end: Position); + + /** + * Create a new range from number coordinates. It is a shorter equivalent of + * using `new Range(new Position(startLine, startCharacter), new Position(endLine, endCharacter))` + * + * @param startLine A zero-based line value. + * @param startCharacter A zero-based character value. + * @param endLine A zero-based line value. + * @param endCharacter A zero-based character value. + */ + constructor(startLine: number, startCharacter: number, endLine: number, endCharacter: number); + + /** + * `true` if `start` and `end` are equal. + */ + isEmpty: boolean; + + /** + * `true` if `start.line` and `end.line` are equal. + */ + isSingleLine: boolean; + + /** + * Check if a position or a range is contained in this range. + * + * @param positionOrRange A position or a range. + * @return `true` if the position or range is inside or equal + * to this range. + */ + contains(positionOrRange: Position | Range): boolean; + + /** + * Check if `other` equals this range. + * + * @param other A range. + * @return `true` when start and end are {@link Position.isEqual equal} to + * start and end of this range. + */ + isEqual(other: Range): boolean; + + /** + * Intersect `range` with this range and returns a new range or `undefined` + * if the ranges have no overlap. + * + * @param range A range. + * @return A range of the greater start and smaller end positions. Will + * return undefined when there is no overlap. + */ + intersection(range: Range): Range | undefined; + + /** + * Compute the union of `other` with this range. + * + * @param other A range. + * @return A range of smaller start position and the greater end position. + */ + union(other: Range): Range; + + /** + * Derived a new range from this range. + * + * @param start A position that should be used as start. The default value is the {@link Range.start current start}. + * @param end A position that should be used as end. The default value is the {@link Range.end current end}. + * @return A range derived from this range with the given start and end position. + * If start and end are not different `this` range will be returned. + */ + with(start?: Position, end?: Position): Range; + + /** + * Derived a new range from this range. + * + * @param change An object that describes a change to this range. + * @return A range that reflects the given change. Will return `this` range if the change + * is not changing anything. + */ + with(change: { start?: Position; end?: Position }): Range; + } + + /** + * Represents a text selection in an editor. + */ + export class Selection extends Range { + + /** + * The position at which the selection starts. + * This position might be before or after {@link Selection.active active}. + */ + anchor: Position; + + /** + * The position of the cursor. + * This position might be before or after {@link Selection.anchor anchor}. + */ + active: Position; + + /** + * Create a selection from two positions. + * + * @param anchor A position. + * @param active A position. + */ + constructor(anchor: Position, active: Position); + + /** + * Create a selection from four coordinates. + * + * @param anchorLine A zero-based line value. + * @param anchorCharacter A zero-based character value. + * @param activeLine A zero-based line value. + * @param activeCharacter A zero-based character value. + */ + constructor(anchorLine: number, anchorCharacter: number, activeLine: number, activeCharacter: number); + + /** + * A selection is reversed if its {@link Selection.anchor anchor} is the {@link Selection.end end} position. + */ + isReversed: boolean; + } + + /** + * Represents sources that can cause {@link window.onDidChangeTextEditorSelection selection change events}. + */ + export enum TextEditorSelectionChangeKind { + /** + * Selection changed due to typing in the editor. + */ + Keyboard = 1, + /** + * Selection change due to clicking in the editor. + */ + Mouse = 2, + /** + * Selection changed because a command ran. + */ + Command = 3 + } + + /** + * Represents an event describing the change in a {@link TextEditor.selections text editor's selections}. + */ + export interface TextEditorSelectionChangeEvent { + /** + * The {@link TextEditor text editor} for which the selections have changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.selections text editor's selections}. + */ + readonly selections: readonly Selection[]; + /** + * The {@link TextEditorSelectionChangeKind change kind} which has triggered this + * event. Can be `undefined`. + */ + readonly kind: TextEditorSelectionChangeKind | undefined; + } + + /** + * Represents an event describing the change in a {@link TextEditor.visibleRanges text editor's visible ranges}. + */ + export interface TextEditorVisibleRangesChangeEvent { + /** + * The {@link TextEditor text editor} for which the visible ranges have changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.visibleRanges text editor's visible ranges}. + */ + readonly visibleRanges: readonly Range[]; + } + + /** + * Represents an event describing the change in a {@link TextEditor.options text editor's options}. + */ + export interface TextEditorOptionsChangeEvent { + /** + * The {@link TextEditor text editor} for which the options have changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.options text editor's options}. + */ + readonly options: TextEditorOptions; + } + + /** + * Represents an event describing the change of a {@link TextEditor.viewColumn text editor's view column}. + */ + export interface TextEditorViewColumnChangeEvent { + /** + * The {@link TextEditor text editor} for which the view column has changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.viewColumn text editor's view column}. + */ + readonly viewColumn: ViewColumn; + } + + /** + * Rendering style of the cursor. + */ + export enum TextEditorCursorStyle { + /** + * Render the cursor as a vertical thick line. + */ + Line = 1, + /** + * Render the cursor as a block filled. + */ + Block = 2, + /** + * Render the cursor as a thick horizontal line. + */ + Underline = 3, + /** + * Render the cursor as a vertical thin line. + */ + LineThin = 4, + /** + * Render the cursor as a block outlined. + */ + BlockOutline = 5, + /** + * Render the cursor as a thin horizontal line. + */ + UnderlineThin = 6 + } + + /** + * Rendering style of the line numbers. + */ + export enum TextEditorLineNumbersStyle { + /** + * Do not render the line numbers. + */ + Off = 0, + /** + * Render the line numbers. + */ + On = 1, + /** + * Render the line numbers with values relative to the primary cursor location. + */ + Relative = 2 + } + + /** + * Represents a {@link TextEditor text editor}'s {@link TextEditor.options options}. + */ + export interface TextEditorOptions { + + /** + * The size in spaces a tab takes. This is used for two purposes: + * - the rendering width of a tab character; + * - the number of spaces to insert when {@link TextEditorOptions.insertSpaces insertSpaces} is true. + * + * When getting a text editor's options, this property will always be a number (resolved). + * When setting a text editor's options, this property is optional and it can be a number or `"auto"`. + */ + tabSize?: number | string; + + /** + * When pressing Tab insert {@link TextEditorOptions.tabSize n} spaces. + * When getting a text editor's options, this property will always be a boolean (resolved). + * When setting a text editor's options, this property is optional and it can be a boolean or `"auto"`. + */ + insertSpaces?: boolean | string; + + /** + * The rendering style of the cursor in this editor. + * When getting a text editor's options, this property will always be present. + * When setting a text editor's options, this property is optional. + */ + cursorStyle?: TextEditorCursorStyle; + + /** + * Render relative line numbers w.r.t. the current line number. + * When getting a text editor's options, this property will always be present. + * When setting a text editor's options, this property is optional. + */ + lineNumbers?: TextEditorLineNumbersStyle; + } + + /** + * Represents a handle to a set of decorations + * sharing the same {@link DecorationRenderOptions styling options} in a {@link TextEditor text editor}. + * + * To get an instance of a `TextEditorDecorationType` use + * {@link window.createTextEditorDecorationType createTextEditorDecorationType}. + */ + export interface TextEditorDecorationType { + + /** + * Internal representation of the handle. + */ + readonly key: string; + + /** + * Remove this decoration type and all decorations on all text editors using it. + */ + dispose(): void; + } + + /** + * Represents different {@link TextEditor.revealRange reveal} strategies in a text editor. + */ + export enum TextEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 + } + + /** + * Represents different positions for rendering a decoration in an {@link DecorationRenderOptions.overviewRulerLane overview ruler}. + * The overview ruler supports three lanes. + */ + export enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 + } + + /** + * Describes the behavior of decorations when typing/editing at their edges. + */ + export enum DecorationRangeBehavior { + /** + * The decoration's range will widen when edits occur at the start or end. + */ + OpenOpen = 0, + /** + * The decoration's range will not widen when edits occur at the start of end. + */ + ClosedClosed = 1, + /** + * The decoration's range will widen when edits occur at the start, but not at the end. + */ + OpenClosed = 2, + /** + * The decoration's range will widen when edits occur at the end, but not at the start. + */ + ClosedOpen = 3 + } + + /** + * Represents options to configure the behavior of showing a {@link TextDocument document} in an {@link TextEditor editor}. + */ + export interface TextDocumentShowOptions { + /** + * An optional view column in which the {@link TextEditor editor} should be shown. + * The default is the {@link ViewColumn.Active active}. Columns that do not exist + * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. + * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently + * active one. + */ + viewColumn?: ViewColumn; + + /** + * An optional flag that when `true` will stop the {@link TextEditor editor} from taking focus. + */ + preserveFocus?: boolean; + + /** + * An optional flag that controls if an {@link TextEditor editor}-tab shows as preview. Preview tabs will + * be replaced and reused until set to stay - either explicitly or through editing. + * + * *Note* that the flag is ignored if a user has disabled preview editors in settings. + */ + preview?: boolean; + + /** + * An optional selection to apply for the document in the {@link TextEditor editor}. + */ + selection?: Range; + } + + /** + * Represents an event describing the change in a {@link NotebookEditor.selections notebook editor's selections}. + */ + export interface NotebookEditorSelectionChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the selections have changed. + */ + readonly notebookEditor: NotebookEditor; + + /** + * The new value for the {@link NotebookEditor.selections notebook editor's selections}. + */ + readonly selections: readonly NotebookRange[]; + } + + /** + * Represents an event describing the change in a {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. + */ + export interface NotebookEditorVisibleRangesChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. + */ + readonly notebookEditor: NotebookEditor; + + /** + * The new value for the {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. + */ + readonly visibleRanges: readonly NotebookRange[]; + } + + /** + * Represents options to configure the behavior of showing a {@link NotebookDocument notebook document} in an {@link NotebookEditor notebook editor}. + */ + export interface NotebookDocumentShowOptions { + /** + * An optional view column in which the {@link NotebookEditor notebook editor} should be shown. + * The default is the {@link ViewColumn.Active active}. Columns that do not exist + * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. + * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently + * active one. + */ + readonly viewColumn?: ViewColumn; + + /** + * An optional flag that when `true` will stop the {@link NotebookEditor notebook editor} from taking focus. + */ + readonly preserveFocus?: boolean; + + /** + * An optional flag that controls if an {@link NotebookEditor notebook editor}-tab shows as preview. Preview tabs will + * be replaced and reused until set to stay - either explicitly or through editing. The default behaviour depends + * on the `workbench.editor.enablePreview`-setting. + */ + readonly preview?: boolean; + + /** + * An optional selection to apply for the document in the {@link NotebookEditor notebook editor}. + */ + readonly selections?: readonly NotebookRange[]; + } + + /** + * A reference to one of the workbench colors as defined in https://code.visualstudio.com/docs/getstarted/theme-color-reference. + * Using a theme color is preferred over a custom color as it gives theme authors and users the possibility to change the color. + */ + export class ThemeColor { + + /** + * Creates a reference to a theme color. + * @param id of the color. The available colors are listed in https://code.visualstudio.com/docs/getstarted/theme-color-reference. + */ + constructor(id: string); + } + + /** + * A reference to a named icon. Currently, {@link ThemeIcon.File File}, {@link ThemeIcon.Folder Folder}, + * and [ThemeIcon ids](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing) are supported. + * Using a theme icon is preferred over a custom icon as it gives product theme authors the possibility to change the icons. + * + * *Note* that theme icons can also be rendered inside labels and descriptions. Places that support theme icons spell this out + * and they use the `$()`-syntax, for instance `quickPick.label = "Hello World $(globe)"`. + */ + export class ThemeIcon { + /** + * Reference to an icon representing a file. The icon is taken from the current file icon theme or a placeholder icon is used. + */ + static readonly File: ThemeIcon; + + /** + * Reference to an icon representing a folder. The icon is taken from the current file icon theme or a placeholder icon is used. + */ + static readonly Folder: ThemeIcon; + + /** + * The id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. + */ + readonly id: string; + + /** + * The optional ThemeColor of the icon. The color is currently only used in {@link TreeItem}. + */ + readonly color?: ThemeColor | undefined; + + /** + * Creates a reference to a theme icon. + * @param id id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. + * @param color optional `ThemeColor` for the icon. The color is currently only used in {@link TreeItem}. + */ + constructor(id: string, color?: ThemeColor); + } + + /** + * Represents theme specific rendering styles for a {@link TextEditorDecorationType text editor decoration}. + */ + export interface ThemableDecorationRenderOptions { + /** + * Background color of the decoration. Use rgba() and define transparent background colors to play well with other decorations. + * Alternatively a color from the color registry can be {@link ThemeColor referenced}. + */ + backgroundColor?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + outline?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'outline' for setting one or more of the individual outline properties. + */ + outlineColor?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'outline' for setting one or more of the individual outline properties. + */ + outlineStyle?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'outline' for setting one or more of the individual outline properties. + */ + outlineWidth?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + border?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderColor?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderRadius?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderSpacing?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderStyle?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderWidth?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + fontStyle?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + fontWeight?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + textDecoration?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + cursor?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + color?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + opacity?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + letterSpacing?: string; + + /** + * An **absolute path** or an URI to an image to be rendered in the gutter. + */ + gutterIconPath?: string | Uri; + + /** + * Specifies the size of the gutter icon. + * Available values are 'auto', 'contain', 'cover' and any percentage value. + * For further information: https://msdn.microsoft.com/en-us/library/jj127316(v=vs.85).aspx + */ + gutterIconSize?: string; + + /** + * The color of the decoration in the overview ruler. Use rgba() and define transparent colors to play well with other decorations. + */ + overviewRulerColor?: string | ThemeColor; + + /** + * Defines the rendering options of the attachment that is inserted before the decorated text. + */ + before?: ThemableDecorationAttachmentRenderOptions; + + /** + * Defines the rendering options of the attachment that is inserted after the decorated text. + */ + after?: ThemableDecorationAttachmentRenderOptions; + } + + export interface ThemableDecorationAttachmentRenderOptions { + /** + * Defines a text content that is shown in the attachment. Either an icon or a text can be shown, but not both. + */ + contentText?: string; + /** + * An **absolute path** or an URI to an image to be rendered in the attachment. Either an icon + * or a text can be shown, but not both. + */ + contentIconPath?: string | Uri; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + border?: string; + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + borderColor?: string | ThemeColor; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontStyle?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontWeight?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + textDecoration?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + color?: string | ThemeColor; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + backgroundColor?: string | ThemeColor; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + margin?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + width?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + height?: string; + } + + /** + * Represents rendering styles for a {@link TextEditorDecorationType text editor decoration}. + */ + export interface DecorationRenderOptions extends ThemableDecorationRenderOptions { + /** + * Should the decoration be rendered also on the whitespace after the line text. + * Defaults to `false`. + */ + isWholeLine?: boolean; + + /** + * Customize the growing behavior of the decoration when edits occur at the edges of the decoration's range. + * Defaults to `DecorationRangeBehavior.OpenOpen`. + */ + rangeBehavior?: DecorationRangeBehavior; + + /** + * The position in the overview ruler where the decoration should be rendered. + */ + overviewRulerLane?: OverviewRulerLane; + + /** + * Overwrite options for light themes. + */ + light?: ThemableDecorationRenderOptions; + + /** + * Overwrite options for dark themes. + */ + dark?: ThemableDecorationRenderOptions; + } + + /** + * Represents options for a specific decoration in a {@link TextEditorDecorationType decoration set}. + */ + export interface DecorationOptions { + + /** + * Range to which this decoration is applied. The range must not be empty. + */ + range: Range; + + /** + * A message that should be rendered when hovering over the decoration. + */ + hoverMessage?: MarkdownString | MarkedString | Array; + + /** + * Render options applied to the current decoration. For performance reasons, keep the + * number of decoration specific options small, and use decoration types wherever possible. + */ + renderOptions?: DecorationInstanceRenderOptions; + } + + export interface ThemableDecorationInstanceRenderOptions { + /** + * Defines the rendering options of the attachment that is inserted before the decorated text. + */ + before?: ThemableDecorationAttachmentRenderOptions; + + /** + * Defines the rendering options of the attachment that is inserted after the decorated text. + */ + after?: ThemableDecorationAttachmentRenderOptions; + } + + export interface DecorationInstanceRenderOptions extends ThemableDecorationInstanceRenderOptions { + /** + * Overwrite options for light themes. + */ + light?: ThemableDecorationInstanceRenderOptions; + + /** + * Overwrite options for dark themes. + */ + dark?: ThemableDecorationInstanceRenderOptions; + } + + /** + * Represents an editor that is attached to a {@link TextDocument document}. + */ + export interface TextEditor { + + /** + * The document associated with this text editor. The document will be the same for the entire lifetime of this text editor. + */ + readonly document: TextDocument; + + /** + * The primary selection on this text editor. Shorthand for `TextEditor.selections[0]`. + */ + selection: Selection; + + /** + * The selections in this text editor. The primary selection is always at index 0. + */ + selections: readonly Selection[]; + + /** + * The current visible ranges in the editor (vertically). + * This accounts only for vertical scrolling, and not for horizontal scrolling. + */ + readonly visibleRanges: readonly Range[]; + + /** + * Text editor options. + */ + options: TextEditorOptions; + + /** + * The column in which this editor shows. Will be `undefined` in case this + * isn't one of the main editors, e.g. an embedded editor, or when the editor + * column is larger than three. + */ + readonly viewColumn: ViewColumn | undefined; + + /** + * Perform an edit on the document associated with this text editor. + * + * The given callback-function is invoked with an {@link TextEditorEdit edit-builder} which must + * be used to make edits. Note that the edit-builder is only valid while the + * callback executes. + * + * @param callback A function which can create edits using an {@link TextEditorEdit edit-builder}. + * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. + * @return A promise that resolves with a value indicating if the edits could be applied. + */ + edit(callback: (editBuilder: TextEditorEdit) => void, options?: { readonly undoStopBefore: boolean; readonly undoStopAfter: boolean }): Thenable; + + /** + * Insert a {@link SnippetString snippet} and put the editor into snippet mode. "Snippet mode" + * means the editor adds placeholders and additional cursors so that the user can complete + * or accept the snippet. + * + * @param snippet The snippet to insert in this edit. + * @param location Position or range at which to insert the snippet, defaults to the current editor selection or selections. + * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. + * @return A promise that resolves with a value indicating if the snippet could be inserted. Note that the promise does not signal + * that the snippet is completely filled-in or accepted. + */ + insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[], options?: { readonly undoStopBefore: boolean; readonly undoStopAfter: boolean }): Thenable; + + /** + * Adds a set of decorations to the text editor. If a set of decorations already exists with + * the given {@link TextEditorDecorationType decoration type}, they will be replaced. If + * `rangesOrOptions` is empty, the existing decorations with the given {@link TextEditorDecorationType decoration type} + * will be removed. + * + * @see {@link window.createTextEditorDecorationType createTextEditorDecorationType}. + * + * @param decorationType A decoration type. + * @param rangesOrOptions Either {@link Range ranges} or more detailed {@link DecorationOptions options}. + */ + setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: readonly Range[] | readonly DecorationOptions[]): void; + + /** + * Scroll as indicated by `revealType` in order to reveal the given range. + * + * @param range A range. + * @param revealType The scrolling strategy for revealing `range`. + */ + revealRange(range: Range, revealType?: TextEditorRevealType): void; + + /** + * Show the text editor. + * + * @deprecated Use {@link window.showTextDocument} instead. + * + * @param column The {@link ViewColumn column} in which to show this editor. + * This method shows unexpected behavior and will be removed in the next major update. + */ + show(column?: ViewColumn): void; + + /** + * Hide the text editor. + * + * @deprecated Use the command `workbench.action.closeActiveEditor` instead. + * This method shows unexpected behavior and will be removed in the next major update. + */ + hide(): void; + } + + /** + * Represents an end of line character sequence in a {@link TextDocument document}. + */ + export enum EndOfLine { + /** + * The line feed `\n` character. + */ + LF = 1, + /** + * The carriage return line feed `\r\n` sequence. + */ + CRLF = 2 + } + + /** + * A complex edit that will be applied in one transaction on a TextEditor. + * This holds a description of the edits and if the edits are valid (i.e. no overlapping regions, document was not changed in the meantime, etc.) + * they can be applied on a {@link TextDocument document} associated with a {@link TextEditor text editor}. + */ + export interface TextEditorEdit { + /** + * Replace a certain text region with a new value. + * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. + * + * @param location The range this operation should remove. + * @param value The new text this operation should insert after removing `location`. + */ + replace(location: Position | Range | Selection, value: string): void; + + /** + * Insert text at a location. + * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. + * Although the equivalent text edit can be made with {@link TextEditorEdit.replace replace}, `insert` will produce a different resulting selection (it will get moved). + * + * @param location The position where the new text should be inserted. + * @param value The new text this operation should insert. + */ + insert(location: Position, value: string): void; + + /** + * Delete a certain text region. + * + * @param location The range this operation should remove. + */ + delete(location: Range | Selection): void; + + /** + * Set the end of line sequence. + * + * @param endOfLine The new end of line for the {@link TextDocument document}. + */ + setEndOfLine(endOfLine: EndOfLine): void; + } + + /** + * A universal resource identifier representing either a file on disk + * or another resource, like untitled resources. + */ + export class Uri { + + /** + * Create an URI from a string, e.g. `http://www.example.com/some/path`, + * `file:///usr/home`, or `scheme:with/path`. + * + * *Note* that for a while uris without a `scheme` were accepted. That is not correct + * as all uris should have a scheme. To avoid breakage of existing code the optional + * `strict`-argument has been added. We *strongly* advise to use it, e.g. `Uri.parse('my:uri', true)` + * + * @see {@link Uri.toString} + * @param value The string value of an Uri. + * @param strict Throw an error when `value` is empty or when no `scheme` can be parsed. + * @return A new Uri instance. + */ + static parse(value: string, strict?: boolean): Uri; + + /** + * Create an URI from a file system path. The {@link Uri.scheme scheme} + * will be `file`. + * + * The *difference* between {@link Uri.parse} and {@link Uri.file} is that the latter treats the argument + * as path, not as stringified-uri. E.g. `Uri.file(path)` is *not* the same as + * `Uri.parse('file://' + path)` because the path might contain characters that are + * interpreted (# and ?). See the following sample: + * ```ts + * const good = URI.file('/coding/c#/project1'); + * good.scheme === 'file'; + * good.path === '/coding/c#/project1'; + * good.fragment === ''; + * + * const bad = URI.parse('file://' + '/coding/c#/project1'); + * bad.scheme === 'file'; + * bad.path === '/coding/c'; // path is now broken + * bad.fragment === '/project1'; + * ``` + * + * @param path A file system or UNC path. + * @return A new Uri instance. + */ + static file(path: string): Uri; + + /** + * Create a new uri which path is the result of joining + * the path of the base uri with the provided path segments. + * + * - Note 1: `joinPath` only affects the path component + * and all other components (scheme, authority, query, and fragment) are + * left as they are. + * - Note 2: The base uri must have a path; an error is thrown otherwise. + * + * The path segments are normalized in the following ways: + * - sequences of path separators (`/` or `\`) are replaced with a single separator + * - for `file`-uris on windows, the backslash-character (`\`) is considered a path-separator + * - the `..`-segment denotes the parent segment, the `.` denotes the current segment + * - paths have a root which always remains, for instance on windows drive-letters are roots + * so that is true: `joinPath(Uri.file('file:///c:/root'), '../../other').fsPath === 'c:/other'` + * + * @param base An uri. Must have a path. + * @param pathSegments One more more path fragments + * @returns A new uri which path is joined with the given fragments + */ + static joinPath(base: Uri, ...pathSegments: string[]): Uri; + + /** + * Create an URI from its component parts + * + * @see {@link Uri.toString} + * @param components The component parts of an Uri. + * @return A new Uri instance. + */ + static from(components: { readonly scheme: string; readonly authority?: string; readonly path?: string; readonly query?: string; readonly fragment?: string }): Uri; + + /** + * Use the `file` and `parse` factory functions to create new `Uri` objects. + */ + private constructor(scheme: string, authority: string, path: string, query: string, fragment: string); + + /** + * Scheme is the `http` part of `http://www.example.com/some/path?query#fragment`. + * The part before the first colon. + */ + readonly scheme: string; + + /** + * Authority is the `www.example.com` part of `http://www.example.com/some/path?query#fragment`. + * The part between the first double slashes and the next slash. + */ + readonly authority: string; + + /** + * Path is the `/some/path` part of `http://www.example.com/some/path?query#fragment`. + */ + readonly path: string; + + /** + * Query is the `query` part of `http://www.example.com/some/path?query#fragment`. + */ + readonly query: string; + + /** + * Fragment is the `fragment` part of `http://www.example.com/some/path?query#fragment`. + */ + readonly fragment: string; + + /** + * The string representing the corresponding file system path of this Uri. + * + * Will handle UNC paths and normalize windows drive letters to lower-case. Also + * uses the platform specific path separator. + * + * * Will *not* validate the path for invalid characters and semantics. + * * Will *not* look at the scheme of this Uri. + * * The resulting string shall *not* be used for display purposes but + * for disk operations, like `readFile` et al. + * + * The *difference* to the {@linkcode Uri.path path}-property is the use of the platform specific + * path separator and the handling of UNC paths. The sample below outlines the difference: + * ```ts + * const u = URI.parse('file://server/c$/folder/file.txt') + * u.authority === 'server' + * u.path === '/shares/c$/file.txt' + * u.fsPath === '\\server\c$\folder\file.txt' + * ``` + */ + readonly fsPath: string; + + /** + * Derive a new Uri from this Uri. + * + * ```ts + * let file = Uri.parse('before:some/file/path'); + * let other = file.with({ scheme: 'after' }); + * assert.ok(other.toString() === 'after:some/file/path'); + * ``` + * + * @param change An object that describes a change to this Uri. To unset components use `null` or + * the empty string. + * @return A new Uri that reflects the given change. Will return `this` Uri if the change + * is not changing anything. + */ + with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri; + + /** + * Returns a string representation of this Uri. The representation and normalization + * of a URI depends on the scheme. + * + * * The resulting string can be safely used with {@link Uri.parse}. + * * The resulting string shall *not* be used for display purposes. + * + * *Note* that the implementation will encode _aggressive_ which often leads to unexpected, + * but not incorrect, results. For instance, colons are encoded to `%3A` which might be unexpected + * in file-uri. Also `&` and `=` will be encoded which might be unexpected for http-uris. For stability + * reasons this cannot be changed anymore. If you suffer from too aggressive encoding you should use + * the `skipEncoding`-argument: `uri.toString(true)`. + * + * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that + * the `#` and `?` characters occurring in the path will always be encoded. + * @returns A string representation of this Uri. + */ + toString(skipEncoding?: boolean): string; + + /** + * Returns a JSON representation of this Uri. + * + * @return An object. + */ + toJSON(): any; + } + + /** + * A cancellation token is passed to an asynchronous or long running + * operation to request cancellation, like cancelling a request + * for completion items because the user continued to type. + * + * To get an instance of a `CancellationToken` use a + * {@link CancellationTokenSource}. + */ + export interface CancellationToken { + + /** + * Is `true` when the token has been cancelled, `false` otherwise. + */ + isCancellationRequested: boolean; + + /** + * An {@link Event} which fires upon cancellation. + */ + onCancellationRequested: Event; + } + + /** + * A cancellation source creates and controls a {@link CancellationToken cancellation token}. + */ + export class CancellationTokenSource { + + /** + * The cancellation token of this source. + */ + token: CancellationToken; + + /** + * Signal cancellation on the token. + */ + cancel(): void; + + /** + * Dispose object and free resources. + */ + dispose(): void; + } + + /** + * An error type that should be used to signal cancellation of an operation. + * + * This type can be used in response to a {@link CancellationToken cancellation token} + * being cancelled or when an operation is being cancelled by the + * executor of that operation. + */ + export class CancellationError extends Error { + + /** + * Creates a new cancellation error. + */ + constructor(); + } + + /** + * Represents a type which can release resources, such + * as event listening or a timer. + */ + export class Disposable { + + /** + * Combine many disposable-likes into one. You can use this method when having objects with + * a dispose function which aren't instances of `Disposable`. + * + * @param disposableLikes Objects that have at least a `dispose`-function member. Note that asynchronous + * dispose-functions aren't awaited. + * @return Returns a new disposable which, upon dispose, will + * dispose all provided disposables. + */ + static from(...disposableLikes: { dispose: () => any }[]): Disposable; + + /** + * Creates a new disposable that calls the provided function + * on dispose. + * + * *Note* that an asynchronous function is not awaited. + * + * @param callOnDispose Function that disposes something. + */ + constructor(callOnDispose: () => any); + + /** + * Dispose this object. + */ + dispose(): any; + } + + /** + * Represents a typed event. + * + * A function that represents an event to which you subscribe by calling it with + * a listener function as argument. + * + * @example + * item.onDidChange(function(event) { console.log("Event happened: " + event); }); + */ + export interface Event { + + /** + * A function that represents an event to which you subscribe by calling it with + * a listener function as argument. + * + * @param listener The listener function will be called when the event happens. + * @param thisArgs The `this`-argument which will be used when calling the event listener. + * @param disposables An array to which a {@link Disposable} will be added. + * @return A disposable which unsubscribes the event listener. + */ + (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable; + } + + /** + * An event emitter can be used to create and manage an {@link Event} for others + * to subscribe to. One emitter always owns one event. + * + * Use this class if you want to provide event from within your extension, for instance + * inside a {@link TextDocumentContentProvider} or when providing + * API to other extensions. + */ + export class EventEmitter { + + /** + * The event listeners can subscribe to. + */ + event: Event; + + /** + * Notify all subscribers of the {@link EventEmitter.event event}. Failure + * of one or more listener will not fail this function call. + * + * @param data The event object. + */ + fire(data: T): void; + + /** + * Dispose this object and free resources. + */ + dispose(): void; + } + + /** + * A file system watcher notifies about changes to files and folders + * on disk or from other {@link FileSystemProvider FileSystemProviders}. + * + * To get an instance of a `FileSystemWatcher` use + * {@link workspace.createFileSystemWatcher createFileSystemWatcher}. + */ + export interface FileSystemWatcher extends Disposable { + + /** + * true if this file system watcher has been created such that + * it ignores creation file system events. + */ + readonly ignoreCreateEvents: boolean; + + /** + * true if this file system watcher has been created such that + * it ignores change file system events. + */ + readonly ignoreChangeEvents: boolean; + + /** + * true if this file system watcher has been created such that + * it ignores delete file system events. + */ + readonly ignoreDeleteEvents: boolean; + + /** + * An event which fires on file/folder creation. + */ + readonly onDidCreate: Event; + + /** + * An event which fires on file/folder change. + */ + readonly onDidChange: Event; + + /** + * An event which fires on file/folder deletion. + */ + readonly onDidDelete: Event; + } + + /** + * A text document content provider allows to add readonly documents + * to the editor, such as source from a dll or generated html from md. + * + * Content providers are {@link workspace.registerTextDocumentContentProvider registered} + * for a {@link Uri.scheme uri-scheme}. When a uri with that scheme is to + * be {@link workspace.openTextDocument loaded} the content provider is + * asked. + */ + export interface TextDocumentContentProvider { + + /** + * An event to signal a resource has changed. + */ + onDidChange?: Event; + + /** + * Provide textual content for a given uri. + * + * The editor will use the returned string-content to create a readonly + * {@link TextDocument document}. Resources allocated should be released when + * the corresponding document has been {@link workspace.onDidCloseTextDocument closed}. + * + * **Note**: The contents of the created {@link TextDocument document} might not be + * identical to the provided text due to end-of-line-sequence normalization. + * + * @param uri An uri which scheme matches the scheme this provider was {@link workspace.registerTextDocumentContentProvider registered} for. + * @param token A cancellation token. + * @return A string or a thenable that resolves to such. + */ + provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult; + } + + /** + * The kind of {@link QuickPickItem quick pick item}. + */ + export enum QuickPickItemKind { + /** + * When a {@link QuickPickItem} has a kind of {@link Separator}, the item is just a visual separator and does not represent a real item. + * The only property that applies is {@link QuickPickItem.label label }. All other properties on {@link QuickPickItem} will be ignored and have no effect. + */ + Separator = -1, + /** + * The default {@link QuickPickItem.kind} is an item that can be selected in the quick pick. + */ + Default = 0, + } + + /** + * Represents an item that can be selected from + * a list of items. + */ + export interface QuickPickItem { + + /** + * A human-readable string which is rendered prominent. Supports rendering of {@link ThemeIcon theme icons} via + * the `$()`-syntax. + */ + label: string; + + /** + * The kind of QuickPickItem that will determine how this item is rendered in the quick pick. When not specified, + * the default is {@link QuickPickItemKind.Default}. + */ + kind?: QuickPickItemKind; + + /** + * A human-readable string which is rendered less prominent in the same line. Supports rendering of + * {@link ThemeIcon theme icons} via the `$()`-syntax. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + description?: string; + + /** + * A human-readable string which is rendered less prominent in a separate line. Supports rendering of + * {@link ThemeIcon theme icons} via the `$()`-syntax. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + detail?: string; + + /** + * Optional flag indicating if this item is picked initially. This is only honored when using + * the {@link window.showQuickPick showQuickPick()} API. To do the same thing with + * the {@link window.createQuickPick createQuickPick()} API, simply set the {@link QuickPick.selectedItems} + * to the items you want picked initially. + * (*Note:* This is only honored when the picker allows multiple selections.) + * + * @see {@link QuickPickOptions.canPickMany} + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + picked?: boolean; + + /** + * Always show this item. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + alwaysShow?: boolean; + + /** + * Optional buttons that will be rendered on this particular item. These buttons will trigger + * an {@link QuickPickItemButtonEvent} when clicked. Buttons are only rendered when using a quickpick + * created by the {@link window.createQuickPick createQuickPick()} API. Buttons are not rendered when using + * the {@link window.showQuickPick showQuickPick()} API. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + buttons?: readonly QuickInputButton[]; + } + + /** + * Options to configure the behavior of the quick pick UI. + */ + export interface QuickPickOptions { + + /** + * An optional string that represents the title of the quick pick. + */ + title?: string; + + /** + * An optional flag to include the description when filtering the picks. + */ + matchOnDescription?: boolean; + + /** + * An optional flag to include the detail when filtering the picks. + */ + matchOnDetail?: boolean; + + /** + * An optional string to show as placeholder in the input box to guide the user what to pick on. + */ + placeHolder?: string; + + /** + * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut?: boolean; + + /** + * An optional flag to make the picker accept multiple selections, if true the result is an array of picks. + */ + canPickMany?: boolean; + + /** + * An optional function that is invoked whenever an item is selected. + */ + onDidSelectItem?(item: QuickPickItem | string): any; + } + + /** + * Options to configure the behaviour of the {@link WorkspaceFolder workspace folder} pick UI. + */ + export interface WorkspaceFolderPickOptions { + + /** + * An optional string to show as placeholder in the input box to guide the user what to pick on. + */ + placeHolder?: string; + + /** + * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut?: boolean; + } + + /** + * Options to configure the behaviour of a file open dialog. + * + * * Note 1: On Windows and Linux, a file dialog cannot be both a file selector and a folder selector, so if you + * set both `canSelectFiles` and `canSelectFolders` to `true` on these platforms, a folder selector will be shown. + * * 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 { + /** + * The resource the dialog shows when opened. + */ + defaultUri?: Uri; + + /** + * A human-readable string for the open button. + */ + openLabel?: string; + + /** + * Allow to select files, defaults to `true`. + */ + canSelectFiles?: boolean; + + /** + * Allow to select folders, defaults to `false`. + */ + canSelectFolders?: boolean; + + /** + * Allow to select many files or folders. + */ + canSelectMany?: boolean; + + /** + * A set of file filters that are used by the dialog. Each entry is a human-readable label, + * like "TypeScript", and an array of extensions, e.g. + * ```ts + * { + * 'Images': ['png', 'jpg'] + * 'TypeScript': ['ts', 'tsx'] + * } + * ``` + */ + filters?: { [name: string]: string[] }; + + /** + * Dialog title. + * + * This parameter might be ignored, as not all operating systems display a title on open dialogs + * (for example, macOS). + */ + title?: string; + } + + /** + * Options to configure the behaviour of a file save dialog. + */ + export interface SaveDialogOptions { + /** + * The resource the dialog shows when opened. + */ + defaultUri?: Uri; + + /** + * A human-readable string for the save button. + */ + saveLabel?: string; + + /** + * A set of file filters that are used by the dialog. Each entry is a human-readable label, + * like "TypeScript", and an array of extensions, e.g. + * ```ts + * { + * 'Images': ['png', 'jpg'] + * 'TypeScript': ['ts', 'tsx'] + * } + * ``` + */ + filters?: { [name: string]: string[] }; + + /** + * Dialog title. + * + * This parameter might be ignored, as not all operating systems display a title on save dialogs + * (for example, macOS). + */ + title?: string; + } + + /** + * Represents an action that is shown with an information, warning, or + * error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * @see {@link window.showWarningMessage showWarningMessage} + * @see {@link window.showErrorMessage showErrorMessage} + */ + export interface MessageItem { + + /** + * A short title like 'Retry', 'Open Log' etc. + */ + title: string; + + /** + * A hint for modal dialogs that the item should be triggered + * when the user cancels the dialog (e.g. by pressing the ESC + * key). + * + * Note: this option is ignored for non-modal messages. + */ + isCloseAffordance?: boolean; + } + + /** + * Options to configure the behavior of the message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * @see {@link window.showWarningMessage showWarningMessage} + * @see {@link window.showErrorMessage showErrorMessage} + */ + export interface MessageOptions { + + /** + * Indicates that this message should be modal. + */ + modal?: boolean; + + /** + * Human-readable detail message that is rendered less prominent. _Note_ that detail + * is only shown for {@link MessageOptions.modal modal} messages. + */ + detail?: string; + } + + /** + * Impacts the behavior and appearance of the validation message. + */ + export enum InputBoxValidationSeverity { + Info = 1, + Warning = 2, + Error = 3 + } + + /** + * Object to configure the behavior of the validation message. + */ + export interface InputBoxValidationMessage { + /** + * The validation message to display. + */ + readonly message: string; + + /** + * The severity of the validation message. + * NOTE: When using `InputBoxValidationSeverity.Error`, the user will not be allowed to accept (hit ENTER) the input. + * `Info` and `Warning` will still allow the InputBox to accept the input. + */ + readonly severity: InputBoxValidationSeverity; + } + + /** + * Options to configure the behavior of the input box UI. + */ + export interface InputBoxOptions { + + /** + * An optional string that represents the title of the input box. + */ + title?: string; + + /** + * The value to pre-fill in the input box. + */ + value?: string; + + /** + * Selection of the pre-filled {@linkcode InputBoxOptions.value value}. Defined as tuple of two number where the + * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole + * pre-filled value will be selected, when empty (start equals end) only the cursor will be set, + * otherwise the defined range will be selected. + */ + valueSelection?: [number, number]; + + /** + * The text to display underneath the input box. + */ + prompt?: string; + + /** + * An optional string to show as placeholder in the input box to guide the user what to type. + */ + placeHolder?: string; + + /** + * Controls if a password input is shown. Password input hides the typed text. + */ + password?: boolean; + + /** + * Set to `true` to keep the input box open when focus moves to another part of the editor or to another window. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut?: boolean; + + /** + * An optional function that will be called to validate input and to give a hint + * to the user. + * + * @param value The current value of the input box. + * @return Either a human-readable string which is presented as an error message or an {@link InputBoxValidationMessage} + * which can provide a specific message severity. Return `undefined`, `null`, or the empty string when 'value' is valid. + */ + validateInput?(value: string): string | InputBoxValidationMessage | undefined | null | + Thenable; + } + + /** + * A relative pattern is a helper to construct glob patterns that are matched + * relatively to a base file path. The base path can either be an absolute file + * path as string or uri or a {@link WorkspaceFolder workspace folder}, which is the + * preferred way of creating the relative pattern. + */ + export class RelativePattern { + + /** + * A base file path to which this pattern will be matched against relatively. + */ + baseUri: Uri; + + /** + * A base file path to which this pattern will be matched against relatively. + * + * This matches the `fsPath` value of {@link RelativePattern.baseUri}. + * + * *Note:* updating this value will update {@link RelativePattern.baseUri} to + * be a uri with `file` scheme. + * + * @deprecated This property is deprecated, please use {@link RelativePattern.baseUri} instead. + */ + base: string; + + /** + * A file glob pattern like `*.{ts,js}` that will be matched on file paths + * relative to the base path. + * + * Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`, + * the file glob pattern will match on `index.js`. + */ + pattern: string; + + /** + * Creates a new relative pattern object with a base file path and pattern to match. This pattern + * will be matched on file paths relative to the base. + * + * Example: + * ```ts + * const folder = vscode.workspace.workspaceFolders?.[0]; + * if (folder) { + * + * // Match any TypeScript file in the root of this workspace folder + * const pattern1 = new vscode.RelativePattern(folder, '*.ts'); + * + * // Match any TypeScript file in `someFolder` inside this workspace folder + * const pattern2 = new vscode.RelativePattern(folder, 'someFolder/*.ts'); + * } + * ``` + * + * @param base A base to which this pattern will be matched against relatively. It is recommended + * to pass in a {@link WorkspaceFolder workspace folder} if the pattern should match inside the workspace. + * Otherwise, a uri or string should only be used if the pattern is for a file path outside the workspace. + * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on paths relative to the base. + */ + constructor(base: WorkspaceFolder | Uri | string, pattern: string); + } + + /** + * A file glob pattern to match file paths against. This can either be a glob pattern string + * (like `**​/*.{ts,js}` or `*.{ts,js}`) or a {@link RelativePattern relative pattern}. + * + * Glob patterns can have the following syntax: + * * `*` to match zero or more characters in a path segment + * * `?` to match on one character in a path segment + * * `**` to match any number of path segments, including none + * * `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * Note: a backslash (`\`) is not valid within a glob pattern. If you have an existing file + * path to match against, consider to use the {@link RelativePattern relative pattern} support + * that takes care of converting any backslash into slash. Otherwise, make sure to convert + * any backslash to slash when creating the glob pattern. + */ + export type GlobPattern = string | RelativePattern; + + /** + * A document filter denotes a document by different properties like + * the {@link TextDocument.languageId language}, the {@link Uri.scheme scheme} of + * its resource, or a glob-pattern that is applied to the {@link TextDocument.fileName path}. + * + * @example A language filter that applies to typescript files on disk + * { language: 'typescript', scheme: 'file' } + * + * @example A language filter that applies to all package.json paths + * { language: 'json', pattern: '**​/package.json' } + */ + export interface DocumentFilter { + + /** + * A language id, like `typescript`. + */ + readonly language?: string; + + /** + * The {@link NotebookDocument.notebookType type} of a notebook, like `jupyter-notebook`. This allows + * to narrow down on the type of a notebook that a {@link NotebookCell.document cell document} belongs to. + * + * *Note* that setting the `notebookType`-property changes how `scheme` and `pattern` are interpreted. When set + * they are evaluated against the {@link NotebookDocument.uri notebook uri}, not the document uri. + * + * @example Match python document inside jupyter notebook that aren't stored yet (`untitled`) + * { language: 'python', notebookType: 'jupyter-notebook', scheme: 'untitled' } + */ + readonly notebookType?: string; + + /** + * A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. + */ + readonly scheme?: string; + + /** + * A {@link GlobPattern glob pattern} that is matched on the absolute path of the document. Use a {@link RelativePattern relative pattern} + * to filter documents to a {@link WorkspaceFolder workspace folder}. + */ + readonly pattern?: GlobPattern; + } + + /** + * A language selector is the combination of one or many language identifiers + * and {@link DocumentFilter language filters}. + * + * *Note* that a document selector that is just a language identifier selects *all* + * documents, even those that are not saved on disk. Only use such selectors when + * a feature works without further context, e.g. without the need to resolve related + * 'files'. + * + * @example + * let sel:DocumentSelector = { scheme: 'file', language: 'typescript' }; + */ + export type DocumentSelector = DocumentFilter | string | ReadonlyArray; + + /** + * A provider result represents the values a provider, like the {@linkcode HoverProvider}, + * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves + * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a + * thenable. + * + * The snippets below are all valid implementations of the {@linkcode HoverProvider}: + * + * ```ts + * let a: HoverProvider = { + * provideHover(doc, pos, token): ProviderResult { + * return new Hover('Hello World'); + * } + * } + * + * let b: HoverProvider = { + * provideHover(doc, pos, token): ProviderResult { + * return new Promise(resolve => { + * resolve(new Hover('Hello World')); + * }); + * } + * } + * + * let c: HoverProvider = { + * provideHover(doc, pos, token): ProviderResult { + * return; // undefined + * } + * } + * ``` + */ + export type ProviderResult = T | undefined | null | Thenable; + + /** + * Kind of a code action. + * + * Kinds are a hierarchical list of identifiers separated by `.`, e.g. `"refactor.extract.function"`. + * + * Code action kinds are used by the editor for UI elements such as the refactoring context menu. Users + * can also trigger code actions with a specific kind with the `editor.action.codeAction` command. + */ + export class CodeActionKind { + /** + * Empty kind. + */ + static readonly Empty: CodeActionKind; + + /** + * Base kind for quickfix actions: `quickfix`. + * + * Quick fix actions address a problem in the code and are shown in the normal code action context menu. + */ + static readonly QuickFix: CodeActionKind; + + /** + * Base kind for refactoring actions: `refactor` + * + * Refactoring actions are shown in the refactoring context menu. + */ + static readonly Refactor: CodeActionKind; + + /** + * Base kind for refactoring extraction actions: `refactor.extract` + * + * Example extract actions: + * + * - Extract method + * - Extract function + * - Extract variable + * - Extract interface from class + * - ... + */ + static readonly RefactorExtract: CodeActionKind; + + /** + * Base kind for refactoring inline actions: `refactor.inline` + * + * Example inline actions: + * + * - Inline function + * - Inline variable + * - Inline constant + * - ... + */ + static readonly RefactorInline: CodeActionKind; + + /** + * Base kind for refactoring move actions: `refactor.move` + * + * Example move actions: + * + * - Move a function to a new file + * - Move a property between classes + * - Move method to base class + * - ... + */ + static readonly RefactorMove: CodeActionKind; + + /** + * Base kind for refactoring rewrite actions: `refactor.rewrite` + * + * Example rewrite actions: + * + * - Convert JavaScript function to class + * - Add or remove parameter + * - Encapsulate field + * - Make method static + * - ... + */ + static readonly RefactorRewrite: CodeActionKind; + + /** + * Base kind for source actions: `source` + * + * Source code actions apply to the entire file. They must be explicitly requested and will not show in the + * normal [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions + * can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu. + */ + static readonly Source: CodeActionKind; + + /** + * Base kind for an organize imports source action: `source.organizeImports`. + */ + static readonly SourceOrganizeImports: CodeActionKind; + + /** + * Base kind for auto-fix source actions: `source.fixAll`. + * + * Fix all actions automatically fix errors that have a clear fix that do not require user input. + * They should not suppress errors or perform unsafe fixes such as generating new types or classes. + */ + static readonly SourceFixAll: CodeActionKind; + + private constructor(value: string); + + /** + * String value of the kind, e.g. `"refactor.extract.function"`. + */ + readonly value: string; + + /** + * Create a new kind by appending a more specific selector to the current kind. + * + * Does not modify the current kind. + */ + append(parts: string): CodeActionKind; + + /** + * Checks if this code action kind intersects `other`. + * + * The kind `"refactor.extract"` for example intersects `refactor`, `"refactor.extract"` and ``"refactor.extract.function"`, + * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"`. + * + * @param other Kind to check. + */ + intersects(other: CodeActionKind): boolean; + + /** + * Checks if `other` is a sub-kind of this `CodeActionKind`. + * + * The kind `"refactor.extract"` for example contains `"refactor.extract"` and ``"refactor.extract.function"`, + * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"` or `refactor`. + * + * @param other Kind to check. + */ + contains(other: CodeActionKind): boolean; + } + + /** + * The reason why code actions were requested. + */ + export enum CodeActionTriggerKind { + /** + * Code actions were explicitly requested by the user or by an extension. + */ + Invoke = 1, + + /** + * Code actions were requested automatically. + * + * This typically happens when current selection in a file changes, but can + * also be triggered when file content changes. + */ + Automatic = 2, + } + + /** + * Contains additional diagnostic information about the context in which + * a {@link CodeActionProvider.provideCodeActions code action} is run. + */ + export interface CodeActionContext { + /** + * The reason why code actions were requested. + */ + readonly triggerKind: CodeActionTriggerKind; + + /** + * An array of diagnostics. + */ + readonly diagnostics: readonly Diagnostic[]; + + /** + * Requested kind of actions to return. + * + * Actions not of this kind are filtered out before being shown by the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action). + */ + readonly only: CodeActionKind | undefined; + } + + /** + * A code action represents a change that can be performed in code, e.g. to fix a problem or + * to refactor code. + * + * A CodeAction must set either {@linkcode CodeAction.edit edit} and/or a {@linkcode CodeAction.command command}. If both are supplied, the `edit` is applied first, then the command is executed. + */ + export class CodeAction { + + /** + * A short, human-readable, title for this code action. + */ + title: string; + + /** + * A {@link WorkspaceEdit workspace edit} this code action performs. + */ + edit?: WorkspaceEdit; + + /** + * {@link Diagnostic Diagnostics} that this code action resolves. + */ + diagnostics?: Diagnostic[]; + + /** + * A {@link Command} this code action executes. + * + * If this command throws an exception, the editor displays the exception message to users in the editor at the + * current cursor position. + */ + command?: Command; + + /** + * {@link CodeActionKind Kind} of the code action. + * + * Used to filter code actions. + */ + kind?: CodeActionKind; + + /** + * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted + * by keybindings. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + */ + isPreferred?: boolean; + + /** + * Marks that the code action cannot currently be applied. + * + * - Disabled code actions are not shown in automatic [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) + * code action menu. + * + * - Disabled actions are shown as faded out in the code action menu when the user request a more specific type + * of code action, such as refactorings. + * + * - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions) + * that auto applies a code action and only a disabled code actions are returned, the editor will show the user an + * error message with `reason` in the editor. + */ + disabled?: { + /** + * Human readable description of why the code action is currently disabled. + * + * This is displayed in the code actions UI. + */ + readonly reason: string; + }; + + /** + * Creates a new code action. + * + * A code action must have at least a {@link CodeAction.title title} and {@link CodeAction.edit edits} + * and/or a {@link CodeAction.command command}. + * + * @param title The title of the code action. + * @param kind The kind of the code action. + */ + constructor(title: string, kind?: CodeActionKind); + } + + /** + * The code action interface defines the contract between extensions and + * the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. + * + * A code action can be any command that is {@link commands.getCommands known} to the system. + */ + export interface CodeActionProvider { + /** + * Provide commands for the given document and range. + * + * @param document The document in which the command was invoked. + * @param range The selector or range for which the command was invoked. This will always be a selection if + * there is a currently active editor. + * @param context Context carrying additional information. + * @param token A cancellation token. + * + * @return An array of code actions, such as quick fixes or refactorings. The lack of a result can be signaled + * by returning `undefined`, `null`, or an empty array. + * + * We also support returning `Command` for legacy reasons, however all new extensions should return + * `CodeAction` object instead. + */ + provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | T)[]>; + + /** + * Given a code action fill in its {@linkcode CodeAction.edit edit}-property. Changes to + * all other properties, like title, are ignored. A code action that has an edit + * will not be resolved. + * + * *Note* that a code action provider that returns commands, not code actions, cannot successfully + * implement this function. Returning commands is deprecated and instead code actions should be + * returned. + * + * @param codeAction A code action. + * @param token A cancellation token. + * @return The resolved code action or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveCodeAction?(codeAction: T, token: CancellationToken): ProviderResult; + } + + /** + * Metadata about the type of code actions that a {@link CodeActionProvider} provides. + */ + export interface CodeActionProviderMetadata { + /** + * List of {@link CodeActionKind CodeActionKinds} that a {@link CodeActionProvider} may return. + * + * This list is used to determine if a given `CodeActionProvider` should be invoked or not. + * To avoid unnecessary computation, every `CodeActionProvider` should list use `providedCodeActionKinds`. The + * list of kinds may either be generic, such as `[CodeActionKind.Refactor]`, or list out every kind provided, + * such as `[CodeActionKind.Refactor.Extract.append('function'), CodeActionKind.Refactor.Extract.append('constant'), ...]`. + */ + readonly providedCodeActionKinds?: readonly CodeActionKind[]; + + /** + * Static documentation for a class of code actions. + * + * Documentation from the provider is shown in the code actions menu if either: + * + * - Code actions of `kind` are requested by the editor. In this case, the editor 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`, + * the editor will use the documentation for `RefactorExtract` instead of the documentation for `Refactor`. + * + * - Any code actions of `kind` are returned by the provider. + * + * At most one documentation entry will be shown per provider. + */ + readonly documentation?: ReadonlyArray<{ + /** + * The kind of the code action being documented. + * + * If the kind is generic, such as `CodeActionKind.Refactor`, the documentation will be shown whenever any + * refactorings are returned. If the kind if more specific, such as `CodeActionKind.RefactorExtract`, the + * documentation will only be shown when extract refactoring code actions are returned. + */ + readonly kind: CodeActionKind; + + /** + * Command that displays the documentation to the user. + * + * This can display the documentation directly in the editor or open a website using {@linkcode env.openExternal}; + * + * The title of this documentation code action is taken from {@linkcode Command.title} + */ + readonly command: Command; + }>; + } + + /** + * A code lens represents a {@link Command} that should be shown along with + * source text, like the number of references, a way to run tests, etc. + * + * A code lens is _unresolved_ when no command is associated to it. For performance + * reasons the creation of a code lens and resolving should be done to two stages. + * + * @see {@link CodeLensProvider.provideCodeLenses} + * @see {@link CodeLensProvider.resolveCodeLens} + */ + export class CodeLens { + + /** + * The range in which this code lens is valid. Should only span a single line. + */ + range: Range; + + /** + * The command this code lens represents. + */ + command?: Command; + + /** + * `true` when there is a command associated. + */ + readonly isResolved: boolean; + + /** + * Creates a new code lens object. + * + * @param range The range to which this code lens applies. + * @param command The command associated to this code lens. + */ + constructor(range: Range, command?: Command); + } + + /** + * A code lens provider adds {@link Command commands} to source text. The commands will be shown + * as dedicated horizontal lines in between the source text. + */ + export interface CodeLensProvider { + + /** + * An optional event to signal that the code lenses from this provider have changed. + */ + onDidChangeCodeLenses?: Event; + + /** + * Compute a list of {@link CodeLens lenses}. This call should return as fast as possible and if + * computing the commands is expensive implementors should only return code lens objects with the + * range set and implement {@link CodeLensProvider.resolveCodeLens resolve}. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @return An array of code lenses or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * This function will be called for each visible code lens, usually when scrolling and after + * calls to {@link CodeLensProvider.provideCodeLenses compute}-lenses. + * + * @param codeLens Code lens that must be resolved. + * @param token A cancellation token. + * @return The given, resolved code lens or thenable that resolves to such. + */ + resolveCodeLens?(codeLens: T, token: CancellationToken): ProviderResult; + } + + /** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal {@link Location} definitions, including the range of + * the defining symbol + */ + export type DefinitionLink = LocationLink; + + /** + * The definition of a symbol represented as one or many {@link Location locations}. + * For most programming languages there is only one location at which a symbol is + * defined. + */ + export type Definition = Location | Location[]; + + /** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ + export interface DefinitionProvider { + + /** + * Provide the definition of the symbol at the given position and document. + * + * @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 definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * The implementation provider interface defines the contract between extensions and + * the go to implementation feature. + */ + export interface ImplementationProvider { + + /** + * Provide the implementations of the symbol at the given position and document. + * + * @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 definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * The type definition provider defines the contract between extensions and + * the go to type definition feature. + */ + export interface TypeDefinitionProvider { + + /** + * Provide the type definition of the symbol at the given position and document. + * + * @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 definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * The declaration of a symbol representation as one or many {@link Location locations} + * or {@link LocationLink location links}. + */ + export type Declaration = Location | Location[] | LocationLink[]; + + /** + * The declaration provider interface defines the contract between extensions and + * the go to declaration feature. + */ + export interface DeclarationProvider { + + /** + * Provide the declaration of the symbol at the given position and document. + * + * @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 declaration or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDeclaration(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * Human-readable text that supports formatting via the [markdown syntax](https://commonmark.org). + * + * Rendering of {@link ThemeIcon theme icons} via the `$()`-syntax is supported + * when the {@linkcode supportThemeIcons} is set to `true`. + * + * Rendering of embedded html is supported when {@linkcode supportHtml} is set to `true`. + */ + export class MarkdownString { + + /** + * The markdown string. + */ + value: string; + + /** + * Indicates that this markdown string is from a trusted source. Only *trusted* + * markdown supports links that execute commands, e.g. `[Run it](command:myCommandId)`. + * + * Defaults to `false` (commands are disabled). + * + * If this is an object, only the set of commands listed in `enabledCommands` are allowed. + */ + isTrusted?: boolean | { readonly enabledCommands: readonly string[] }; + + /** + * Indicates that this markdown string can contain {@link ThemeIcon ThemeIcons}, e.g. `$(zap)`. + */ + supportThemeIcons?: boolean; + + /** + * Indicates that this markdown string can contain raw html tags. Defaults to `false`. + * + * When `supportHtml` is false, the markdown renderer will strip out any raw html tags + * that appear in the markdown text. This means you can only use markdown syntax for rendering. + * + * When `supportHtml` is true, the markdown render will also allow a safe subset of html tags + * and attributes to be rendered. See https://github.com/microsoft/vscode/blob/6d2920473c6f13759c978dd89104c4270a83422d/src/vs/base/browser/markdownRenderer.ts#L296 + * for a list of all supported tags and attributes. + */ + supportHtml?: boolean; + + /** + * Uri that relative paths are resolved relative to. + * + * If the `baseUri` ends with `/`, it is considered a directory and relative paths in the markdown are resolved relative to that directory: + * + * ```ts + * const md = new vscode.MarkdownString(`[link](./file.js)`); + * md.baseUri = vscode.Uri.file('/path/to/dir/'); + * // Here 'link' in the rendered markdown resolves to '/path/to/dir/file.js' + * ``` + * + * If the `baseUri` is a file, relative paths in the markdown are resolved relative to the parent dir of that file: + * + * ```ts + * const md = new vscode.MarkdownString(`[link](./file.js)`); + * md.baseUri = vscode.Uri.file('/path/to/otherFile.js'); + * // Here 'link' in the rendered markdown resolves to '/path/to/file.js' + * ``` + */ + baseUri?: Uri; + + /** + * Creates a new markdown string with the given value. + * + * @param value Optional, initial value. + * @param supportThemeIcons Optional, Specifies whether {@link ThemeIcon ThemeIcons} are supported within the {@linkcode MarkdownString}. + */ + constructor(value?: string, supportThemeIcons?: boolean); + + /** + * Appends and escapes the given string to this markdown string. + * @param value Plain text. + */ + appendText(value: string): MarkdownString; + + /** + * Appends the given string 'as is' to this markdown string. When {@linkcode MarkdownString.supportThemeIcons supportThemeIcons} is `true`, {@link ThemeIcon ThemeIcons} in the `value` will be iconified. + * @param value Markdown string. + */ + appendMarkdown(value: string): MarkdownString; + + /** + * Appends the given string as codeblock using the provided language. + * @param value A code snippet. + * @param language An optional {@link languages.getLanguages language identifier}. + */ + appendCodeblock(value: string, language?: string): MarkdownString; + } + + /** + * MarkedString can be used to render human-readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. Note that + * markdown strings will be sanitized - that means html will be escaped. + * + * @deprecated This type is deprecated, please use {@linkcode MarkdownString} instead. + */ + export type MarkedString = string | { language: string; value: string }; + + /** + * A hover represents additional information for a symbol or word. Hovers are + * rendered in a tooltip-like widget. + */ + export class Hover { + + /** + * The contents of this hover. + */ + contents: Array; + + /** + * The range to which this hover applies. When missing, the + * editor will use the range at the current position or the + * current position itself. + */ + range?: Range; + + /** + * Creates a new hover object. + * + * @param contents The contents of the hover. + * @param range The range to which the hover applies. + */ + constructor(contents: MarkdownString | MarkedString | Array, range?: Range); + } + + /** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ + export interface HoverProvider { + + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + * + * @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 hover or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * An EvaluatableExpression represents an expression in a document that can be evaluated by an active debugger or runtime. + * The result of this evaluation is shown in a tooltip-like widget. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + * In this case the range is still used to highlight the range in the document. + */ + export class EvaluatableExpression { + + /* + * The range is used to extract the evaluatable expression from the underlying document and to highlight it. + */ + readonly range: Range; + + /* + * If specified the expression overrides the extracted expression. + */ + readonly expression?: string | undefined; + + /** + * Creates a new evaluatable expression object. + * + * @param range The range in the underlying document from which the evaluatable expression is extracted. + * @param expression If specified overrides the extracted expression. + */ + constructor(range: Range, expression?: string); + } + + /** + * The evaluatable expression provider interface defines the contract between extensions and + * the debug hover. In this contract the provider returns an evaluatable expression for a given position + * in a document and the editor evaluates this expression in the active debug session and shows the result in a debug hover. + */ + export interface EvaluatableExpressionProvider { + + /** + * Provide an evaluatable expression for the given document and position. + * The editor will evaluate this expression in the active debug session and will show the result in the debug hover. + * The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression. + * + * @param document The document for which the debug hover is about to appear. + * @param position The line and character position in the document where the debug hover is about to appear. + * @param token A cancellation token. + * @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * Provide inline value as text. + */ + export class InlineValueText { + /** + * The document range for which the inline value applies. + */ + readonly range: Range; + /** + * The text of the inline value. + */ + readonly text: string; + /** + * Creates a new InlineValueText object. + * + * @param range The document line where to show the inline value. + * @param text The value to be shown for the line. + */ + constructor(range: Range, text: string); + } + + /** + * Provide inline value through a variable lookup. + * If only a range is specified, the variable name will be extracted from the underlying document. + * An optional variable name can be used to override the extracted name. + */ + export class InlineValueVariableLookup { + /** + * The document range for which the inline value applies. + * The range is used to extract the variable name from the underlying document. + */ + readonly range: Range; + /** + * If specified the name of the variable to look up. + */ + readonly variableName?: string | undefined; + /** + * How to perform the lookup. + */ + readonly caseSensitiveLookup: boolean; + /** + * Creates a new InlineValueVariableLookup object. + * + * @param range The document line where to show the inline value. + * @param variableName The name of the variable to look up. + * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. + */ + constructor(range: Range, variableName?: string, caseSensitiveLookup?: boolean); + } + + /** + * Provide an inline value through an expression evaluation. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + */ + export class InlineValueEvaluatableExpression { + /** + * The document range for which the inline value applies. + * The range is used to extract the evaluatable expression from the underlying document. + */ + readonly range: Range; + /** + * If specified the expression overrides the extracted expression. + */ + readonly expression?: string | undefined; + /** + * Creates a new InlineValueEvaluatableExpression object. + * + * @param range The range in the underlying document from which the evaluatable expression is extracted. + * @param expression If specified overrides the extracted expression. + */ + constructor(range: Range, expression?: string); + } + + /** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + */ + export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; + + /** + * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. + */ + export interface InlineValueContext { + + /** + * The stack frame (as a DAP Id) where the execution has stopped. + */ + readonly frameId: number; + + /** + * The document range where execution has stopped. + * Typically the end position of the range denotes the line where the inline values are shown. + */ + readonly stoppedLocation: Range; + } + + /** + * The inline values provider interface defines the contract between extensions and the editor's debugger inline values feature. + * In this contract the provider returns inline value information for a given document range + * and the editor shows this information in the editor at the end of lines. + */ + export interface InlineValuesProvider { + + /** + * An optional event to signal that inline values have changed. + * @see {@link EventEmitter} + */ + onDidChangeInlineValues?: Event | undefined; + + /** + * Provide "inline value" information for a given document and range. + * The editor calls this method whenever debugging stops in the given document. + * The returned inline values information is rendered in the editor at the end of lines. + * + * @param document The document for which the inline values information is needed. + * @param viewPort The visible document range for which inline values should be computed. + * @param context A bag containing contextual information like the current location. + * @param token A cancellation token. + * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; + } + + /** + * A document highlight kind. + */ + export enum DocumentHighlightKind { + + /** + * A textual occurrence. + */ + Text = 0, + + /** + * Read-access of a symbol, like reading a variable. + */ + Read = 1, + + /** + * Write-access of a symbol, like writing to a variable. + */ + Write = 2 + } + + /** + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ + export class DocumentHighlight { + + /** + * The range this highlight applies to. + */ + range: Range; + + /** + * The highlight kind, default is {@link DocumentHighlightKind.Text text}. + */ + kind?: DocumentHighlightKind; + + /** + * Creates a new document highlight object. + * + * @param range The range the highlight applies to. + * @param kind The highlight kind, default is {@link DocumentHighlightKind.Text text}. + */ + constructor(range: Range, kind?: DocumentHighlightKind); + } + + /** + * The document highlight provider interface defines the contract between extensions and + * the word-highlight-feature. + */ + export interface DocumentHighlightProvider { + + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + * + * @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 An array of document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentHighlights(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * A symbol kind. + */ + export enum SymbolKind { + File = 0, + Module = 1, + Namespace = 2, + Package = 3, + Class = 4, + Method = 5, + Property = 6, + Field = 7, + Constructor = 8, + Enum = 9, + Interface = 10, + Function = 11, + Variable = 12, + Constant = 13, + String = 14, + Number = 15, + Boolean = 16, + Array = 17, + Object = 18, + Key = 19, + Null = 20, + EnumMember = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25 + } + + /** + * Symbol tags are extra annotations that tweak the rendering of a symbol. + */ + export enum SymbolTag { + + /** + * Render a symbol as obsolete, usually using a strike-out. + */ + Deprecated = 1 + } + + /** + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ + export class SymbolInformation { + + /** + * The name of this symbol. + */ + name: string; + + /** + * The name of the symbol containing this symbol. + */ + containerName: string; + + /** + * The kind of this symbol. + */ + kind: SymbolKind; + + /** + * Tags for this symbol. + */ + tags?: readonly SymbolTag[]; + + /** + * The location of this symbol. + */ + location: Location; + + /** + * Creates a new symbol information object. + * + * @param name The name of the symbol. + * @param kind The kind of the symbol. + * @param containerName The name of the symbol containing the symbol. + * @param location The location of the symbol. + */ + constructor(name: string, kind: SymbolKind, containerName: string, location: Location); + + /** + * Creates a new symbol information object. + * + * @deprecated Please use the constructor taking a {@link Location} object. + * + * @param name The name of the symbol. + * @param kind The kind of the symbol. + * @param range The range of the location of the symbol. + * @param uri The resource of the location of symbol, defaults to the current document. + * @param containerName The name of the symbol containing the symbol. + */ + constructor(name: string, kind: SymbolKind, range: Range, uri?: Uri, containerName?: string); + } + + /** + * Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document + * symbols can be hierarchical and they have two ranges: one that encloses its definition and one that points to + * its most interesting range, e.g. the range of an identifier. + */ + export class DocumentSymbol { + + /** + * The name of this symbol. + */ + name: string; + + /** + * More detail for this symbol, e.g. the signature of a function. + */ + detail: string; + + /** + * The kind of this symbol. + */ + kind: SymbolKind; + + /** + * Tags for this symbol. + */ + tags?: readonly SymbolTag[]; + + /** + * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. + */ + range: Range; + + /** + * The range that should be selected and reveal when this symbol is being picked, e.g. the name of a function. + * Must be contained by the {@linkcode DocumentSymbol.range range}. + */ + selectionRange: Range; + + /** + * Children of this symbol, e.g. properties of a class. + */ + children: DocumentSymbol[]; + + /** + * Creates a new document symbol. + * + * @param name The name of the symbol. + * @param detail Details for the symbol. + * @param kind The kind of the symbol. + * @param range The full range of the symbol. + * @param selectionRange The range that should be reveal. + */ + constructor(name: string, detail: string, kind: SymbolKind, range: Range, selectionRange: Range); + } + + /** + * The document symbol provider interface defines the contract between extensions and + * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature. + */ + export interface DocumentSymbolProvider { + + /** + * Provide symbol information for the given document. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult; + } + + /** + * Metadata about a document symbol provider. + */ + export interface DocumentSymbolProviderMetadata { + /** + * A human-readable string that is shown when multiple outlines trees show for one document. + */ + label?: string; + } + + /** + * The workspace symbol provider interface defines the contract between extensions and + * the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature. + */ + export interface WorkspaceSymbolProvider { + + /** + * Project-wide search for a symbol matching the given query string. + * + * The `query`-parameter should be interpreted in a *relaxed way* as the editor will apply its own highlighting + * and scoring on the results. A good rule of thumb is to match case-insensitive and to simply check that the + * characters of *query* appear in their order in a candidate symbol. Don't use prefix, substring, or similar + * strict matching. + * + * To improve performance implementors can implement `resolveWorkspaceSymbol` and then provide symbols with partial + * {@link SymbolInformation.location location}-objects, without a `range` defined. The editor will then call + * `resolveWorkspaceSymbol` for selected symbols only, e.g. when opening a workspace symbol. + * + * @param query A query string, can be the empty string in which case all symbols should be returned. + * @param token A cancellation token. + * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult; + + /** + * Given a symbol fill in its {@link SymbolInformation.location location}. This method is called whenever a symbol + * is selected in the UI. Providers can implement this method and return incomplete symbols from + * {@linkcode WorkspaceSymbolProvider.provideWorkspaceSymbols provideWorkspaceSymbols} which often helps to improve + * performance. + * + * @param symbol The symbol that is to be resolved. Guaranteed to be an instance of an object returned from an + * earlier call to `provideWorkspaceSymbols`. + * @param token A cancellation token. + * @return The resolved symbol or a thenable that resolves to that. When no result is returned, + * the given `symbol` is used. + */ + resolveWorkspaceSymbol?(symbol: T, token: CancellationToken): ProviderResult; + } + + /** + * Value-object that contains additional information when + * requesting references. + */ + export interface ReferenceContext { + + /** + * Include the declaration of the current symbol. + */ + readonly includeDeclaration: boolean; + } + + /** + * The reference provider interface defines the contract between extensions and + * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. + */ + export interface ReferenceProvider { + + /** + * Provide a set of project-wide references for the given position and document. + * + * @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 An array of locations or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; + } + + /** + * A text edit represents edits that should be applied + * to a document. + */ + export class TextEdit { + + /** + * Utility to create a replace edit. + * + * @param range A range. + * @param newText A string. + * @return A new text edit object. + */ + static replace(range: Range, newText: string): TextEdit; + + /** + * Utility to create an insert edit. + * + * @param position A position, will become an empty range. + * @param newText A string. + * @return A new text edit object. + */ + static insert(position: Position, newText: string): TextEdit; + + /** + * Utility to create a delete edit. + * + * @param range A range. + * @return A new text edit object. + */ + static delete(range: Range): TextEdit; + + /** + * Utility to create an eol-edit. + * + * @param eol An eol-sequence + * @return A new text edit object. + */ + static setEndOfLine(eol: EndOfLine): TextEdit; + + /** + * The range this edit applies to. + */ + range: Range; + + /** + * The string this edit will insert. + */ + newText: string; + + /** + * The eol-sequence used in the document. + * + * *Note* that the eol-sequence will be applied to the + * whole document. + */ + newEol?: EndOfLine; + + /** + * Create a new TextEdit. + * + * @param range A range. + * @param newText A string. + */ + constructor(range: Range, newText: string); + } + + /** + * A snippet edit represents an interactive edit that is performed by + * the editor. + * + * *Note* that a snippet edit can always be performed as a normal {@link TextEdit text edit}. + * This will happen when no matching editor is open or when a {@link WorkspaceEdit workspace edit} + * contains snippet edits for multiple files. In that case only those that match the active editor + * will be performed as snippet edits and the others as normal text edits. + */ + export class SnippetTextEdit { + + /** + * Utility to create a replace snippet edit. + * + * @param range A range. + * @param snippet A snippet string. + * @return A new snippet edit object. + */ + static replace(range: Range, snippet: SnippetString): SnippetTextEdit; + + /** + * Utility to create an insert snippet edit. + * + * @param position A position, will become an empty range. + * @param snippet A snippet string. + * @return A new snippet edit object. + */ + static insert(position: Position, snippet: SnippetString): SnippetTextEdit; + + /** + * The range this edit applies to. + */ + range: Range; + + /** + * The {@link SnippetString snippet} this edit will perform. + */ + snippet: SnippetString; + + /** + * Create a new snippet edit. + * + * @param range A range. + * @param snippet A snippet string. + */ + constructor(range: Range, snippet: SnippetString); + } + + /** + * A notebook edit represents edits that should be applied to the contents of a notebook. + */ + export class NotebookEdit { + + /** + * Utility to create a edit that replaces cells in a notebook. + * + * @param range The range of cells to replace + * @param newCells The new notebook cells. + */ + static replaceCells(range: NotebookRange, newCells: NotebookCellData[]): NotebookEdit; + + /** + * Utility to create an edit that replaces cells in a notebook. + * + * @param index The index to insert cells at. + * @param newCells The new notebook cells. + */ + static insertCells(index: number, newCells: NotebookCellData[]): NotebookEdit; + + /** + * Utility to create an edit that deletes cells in a notebook. + * + * @param range The range of cells to delete. + */ + static deleteCells(range: NotebookRange): NotebookEdit; + + /** + * Utility to create an edit that update a cell's metadata. + * + * @param index The index of the cell to update. + * @param newCellMetadata The new metadata for the cell. + */ + static updateCellMetadata(index: number, newCellMetadata: { [key: string]: any }): NotebookEdit; + + /** + * Utility to create an edit that updates the notebook's metadata. + * + * @param newNotebookMetadata The new metadata for the notebook. + */ + static updateNotebookMetadata(newNotebookMetadata: { [key: string]: any }): NotebookEdit; + + /** + * Range of the cells being edited. May be empty. + */ + range: NotebookRange; + + /** + * New cells being inserted. May be empty. + */ + newCells: NotebookCellData[]; + + /** + * Optional new metadata for the cells. + */ + newCellMetadata?: { [key: string]: any }; + + /** + * Optional new metadata for the notebook. + */ + newNotebookMetadata?: { [key: string]: any }; + + constructor(range: NotebookRange, newCells: NotebookCellData[]); + } + + /** + * Additional data for entries of a workspace edit. Supports to label entries and marks entries + * as needing confirmation by the user. The editor groups edits with equal labels into tree nodes, + * for instance all edits labelled with "Changes in Strings" would be a tree node. + */ + export interface WorkspaceEditEntryMetadata { + + /** + * A flag which indicates that user confirmation is needed. + */ + needsConfirmation: boolean; + + /** + * A human-readable string which is rendered prominent. + */ + label: string; + + /** + * A human-readable string which is rendered less prominent on the same line. + */ + description?: string; + + /** + * The icon path or {@link ThemeIcon} for the edit. + */ + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + } + + /** + * Additional data about a workspace edit. + */ + export interface WorkspaceEditMetadata { + /** + * Signal to the editor that this edit is a refactoring. + */ + isRefactoring?: boolean; + } + + /** + * A workspace edit is a collection of textual and files changes for + * multiple resources and documents. + * + * Use the {@link workspace.applyEdit applyEdit}-function to apply a workspace edit. + */ + export class WorkspaceEdit { + + /** + * The number of affected resources of textual or resource changes. + */ + readonly size: number; + + /** + * Replace the given range with given text for the given resource. + * + * @param uri A resource identifier. + * @param range A range. + * @param newText A string. + * @param metadata Optional metadata for the entry. + */ + replace(uri: Uri, range: Range, newText: string, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Insert the given text at the given position. + * + * @param uri A resource identifier. + * @param position A position. + * @param newText A string. + * @param metadata Optional metadata for the entry. + */ + insert(uri: Uri, position: Position, newText: string, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Delete the text at the given range. + * + * @param uri A resource identifier. + * @param range A range. + * @param metadata Optional metadata for the entry. + */ + delete(uri: Uri, range: Range, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Check if a text edit for a resource exists. + * + * @param uri A resource identifier. + * @return `true` if the given resource will be touched by this edit. + */ + has(uri: Uri): boolean; + + /** + * Set (and replace) notebook edits for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: readonly NotebookEdit[]): void; + + /** + * Set (and replace) notebook edits with metadata for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata]>): void; + + /** + * Set (and replace) text edits or snippet edits for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: ReadonlyArray): void; + + /** + * Set (and replace) text edits or snippet edits with metadata for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata]>): void; + + /** + * Get the text edits for a resource. + * + * @param uri A resource identifier. + * @return An array of text edits. + */ + get(uri: Uri): TextEdit[]; + + /** + * Create a regular file. + * + * @param uri Uri of the new file. + * @param options Defines if an existing file should be overwritten or be + * ignored. When `overwrite` and `ignoreIfExists` are both set `overwrite` wins. + * When both are unset and when the file already exists then the edit cannot + * be applied successfully. The `content`-property allows to set the initial contents + * the file is being created with. + * @param metadata Optional metadata for the entry. + */ + createFile(uri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean; readonly contents?: Uint8Array }, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Delete a file or folder. + * + * @param uri The uri of the file that is to be deleted. + * @param metadata Optional metadata for the entry. + */ + deleteFile(uri: Uri, options?: { readonly recursive?: boolean; readonly ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Rename a file or folder. + * + * @param oldUri The existing file. + * @param newUri The new location. + * @param options Defines if existing files should be overwritten or be + * ignored. When overwrite and ignoreIfExists are both set overwrite wins. + * @param metadata Optional metadata for the entry. + */ + renameFile(oldUri: Uri, newUri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Get all text edits grouped by resource. + * + * @return A shallow copy of `[Uri, TextEdit[]]`-tuples. + */ + entries(): [Uri, TextEdit[]][]; + } + + /** + * A snippet string is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. Also see + * [the full snippet syntax](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_creating-your-own-snippets). + */ + export class SnippetString { + + /** + * The snippet string. + */ + value: string; + + constructor(value?: string); + + /** + * Builder-function that appends the given string to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param string A value to append 'as given'. The string will be escaped. + * @return This snippet string. + */ + appendText(string: string): SnippetString; + + /** + * Builder-function that appends a tabstop (`$1`, `$2` etc) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param number The number of this tabstop, defaults to an auto-increment + * value starting at 1. + * @return This snippet string. + */ + appendTabstop(number?: number): SnippetString; + + /** + * Builder-function that appends a placeholder (`${1:value}`) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param value The value of this placeholder - either a string or a function + * with which a nested snippet can be created. + * @param number The number of this tabstop, defaults to an auto-increment + * value starting at 1. + * @return This snippet string. + */ + appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString; + + /** + * Builder-function that appends a choice (`${1|a,b,c|}`) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param values The values for choices - the array of strings + * @param number The number of this tabstop, defaults to an auto-increment + * value starting at 1. + * @return This snippet string. + */ + appendChoice(values: readonly string[], number?: number): SnippetString; + + /** + * Builder-function that appends a variable (`${VAR}`) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param name The name of the variable - excluding the `$`. + * @param defaultValue The default value which is used when the variable name cannot + * be resolved - either a string or a function with which a nested snippet can be created. + * @return This snippet string. + */ + appendVariable(name: string, defaultValue: string | ((snippet: SnippetString) => any)): SnippetString; + } + + /** + * The rename provider interface defines the contract between extensions and + * the [rename](https://code.visualstudio.com/docs/editor/editingevolved#_rename-symbol)-feature. + */ + export interface RenameProvider { + + /** + * Provide an edit that describes changes that have to be made to one + * or many resources to rename a symbol to a different name. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param newName The new name of the symbol. If the given name is not valid, the provider must return a rejected promise. + * @param token A cancellation token. + * @return A workspace edit or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult; + + /** + * Optional function for resolving and validating a position *before* running rename. The result can + * be a range or a range and a placeholder text. The placeholder text should be the identifier of the symbol + * which is being renamed - when omitted the text in the returned range is used. + * + * *Note:* This function should throw an error or return a rejected thenable when the provided location + * doesn't allow for a rename. + * + * @param document The document in which rename will be invoked. + * @param position The position at which rename will be invoked. + * @param token A cancellation token. + * @return The range or range and placeholder text of the identifier that is to be renamed. The lack of a result can signaled by returning `undefined` or `null`. + */ + prepareRename?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * A semantic tokens legend contains the needed information to decipher + * the integer encoded representation of semantic tokens. + */ + export class SemanticTokensLegend { + /** + * The possible token types. + */ + readonly tokenTypes: string[]; + /** + * The possible token modifiers. + */ + readonly tokenModifiers: string[]; + + constructor(tokenTypes: string[], tokenModifiers?: string[]); + } + + /** + * A semantic tokens builder can help with creating a `SemanticTokens` instance + * which contains delta encoded semantic tokens. + */ + export class SemanticTokensBuilder { + + constructor(legend?: SemanticTokensLegend); + + /** + * Add another token. + * + * @param line The token start line number (absolute value). + * @param char The token start character (absolute value). + * @param length The token length in characters. + * @param tokenType The encoded token type. + * @param tokenModifiers The encoded token modifiers. + */ + push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void; + + /** + * Add another token. Use only when providing a legend. + * + * @param range The range of the token. Must be single-line. + * @param tokenType The token type. + * @param tokenModifiers The token modifiers. + */ + push(range: Range, tokenType: string, tokenModifiers?: readonly string[]): void; + + /** + * Finish and create a `SemanticTokens` instance. + */ + build(resultId?: string): SemanticTokens; + } + + /** + * Represents semantic tokens, either in a range or in an entire document. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format. + * @see {@link SemanticTokensBuilder} for a helper to create an instance. + */ + export class SemanticTokens { + /** + * The result id of the tokens. + * + * This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented). + */ + readonly resultId: string | undefined; + /** + * The actual tokens data. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format. + */ + readonly data: Uint32Array; + + constructor(data: Uint32Array, resultId?: string); + } + + /** + * Represents edits to semantic tokens. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits provideDocumentSemanticTokensEdits} for an explanation of the format. + */ + export class SemanticTokensEdits { + /** + * The result id of the tokens. + * + * This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented). + */ + readonly resultId: string | undefined; + /** + * The edits to the tokens data. + * All edits refer to the initial data state. + */ + readonly edits: SemanticTokensEdit[]; + + constructor(edits: SemanticTokensEdit[], resultId?: string); + } + + /** + * Represents an edit to semantic tokens. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits provideDocumentSemanticTokensEdits} for an explanation of the format. + */ + export class SemanticTokensEdit { + /** + * The start offset of the edit. + */ + readonly start: number; + /** + * The count of elements to remove. + */ + readonly deleteCount: number; + /** + * The elements to insert. + */ + readonly data: Uint32Array | undefined; + + constructor(start: number, deleteCount: number, data?: Uint32Array); + } + + /** + * The document semantic tokens provider interface defines the contract between extensions and + * semantic tokens. + */ + export interface DocumentSemanticTokensProvider { + /** + * An optional event to signal that the semantic tokens from this provider have changed. + */ + onDidChangeSemanticTokens?: Event; + + /** + * Tokens in a file are represented as an array of integers. The position of each token is expressed relative to + * the token before it, because most tokens remain stable relative to each other when edits are made in a file. + * + * --- + * In short, each token takes 5 integers to represent, so a specific token `i` in the file consists of the following array indices: + * - at index `5*i` - `deltaLine`: token line number, relative to the previous token + * - at index `5*i+1` - `deltaStart`: token start character, relative to the previous token (relative to 0 or the previous token's start if they are on the same line) + * - at index `5*i+2` - `length`: the length of the token. A token cannot be multiline. + * - at index `5*i+3` - `tokenType`: will be looked up in `SemanticTokensLegend.tokenTypes`. We currently ask that `tokenType` < 65536. + * - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticTokensLegend.tokenModifiers` + * + * --- + * ### How to encode tokens + * + * Here is an example for encoding a file with 3 tokens in a uint32 array: + * ``` + * { line: 2, startChar: 5, length: 3, tokenType: "property", tokenModifiers: ["private", "static"] }, + * { line: 2, startChar: 10, length: 4, tokenType: "type", tokenModifiers: [] }, + * { line: 5, startChar: 2, length: 7, tokenType: "class", tokenModifiers: [] } + * ``` + * + * 1. First of all, a legend must be devised. This legend must be provided up-front and capture all possible token types. + * For this example, we will choose the following legend which must be passed in when registering the provider: + * ``` + * tokenTypes: ['property', 'type', 'class'], + * tokenModifiers: ['private', 'static'] + * ``` + * + * 2. The first transformation step is to encode `tokenType` and `tokenModifiers` as integers using the legend. Token types are looked + * up by index, so a `tokenType` value of `1` means `tokenTypes[1]`. Multiple token modifiers can be set by using bit flags, + * so a `tokenModifier` value of `3` is first viewed as binary `0b00000011`, which means `[tokenModifiers[0], tokenModifiers[1]]` because + * bits 0 and 1 are set. Using this legend, the tokens now are: + * ``` + * { line: 2, startChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, + * { line: 2, startChar: 10, length: 4, tokenType: 1, tokenModifiers: 0 }, + * { line: 5, startChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } + * ``` + * + * 3. The next step is to represent each token relative to the previous token in the file. In this case, the second token + * is on the same line as the first token, so the `startChar` of the second token is made relative to the `startChar` + * of the first token, so it will be `10 - 5`. The third token is on a different line than the second token, so the + * `startChar` of the third token will not be altered: + * ``` + * { deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, + * { deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 1, tokenModifiers: 0 }, + * { deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } + * ``` + * + * 4. Finally, the last step is to inline each of the 5 fields for a token in a single array, which is a memory friendly representation: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * + * @see {@link SemanticTokensBuilder} for a helper to encode tokens as integers. + * *NOTE*: When doing edits, it is possible that multiple edits occur until the editor decides to invoke the semantic tokens provider. + * *NOTE*: If the provider cannot temporarily compute semantic tokens, it can indicate this by throwing an error with the message 'Busy'. + */ + provideDocumentSemanticTokens(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Instead of always returning all the tokens in a file, it is possible for a `DocumentSemanticTokensProvider` to implement + * this method (`provideDocumentSemanticTokensEdits`) and then return incremental updates to the previously provided semantic tokens. + * + * --- + * ### How tokens change when the document changes + * + * Suppose that `provideDocumentSemanticTokens` has previously returned the following semantic tokens: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * + * Also suppose that after some edits, the new semantic tokens in a file are: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * It is possible to express these new tokens in terms of an edit applied to the previous tokens: + * ``` + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // old tokens + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // new tokens + * + * edit: { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3 + * ``` + * + * *NOTE*: If the provider cannot compute `SemanticTokensEdits`, it can "give up" and return all the tokens in the document again. + * *NOTE*: All edits in `SemanticTokensEdits` contain indices in the old integers array, so they all refer to the previous result state. + */ + provideDocumentSemanticTokensEdits?(document: TextDocument, previousResultId: string, token: CancellationToken): ProviderResult; + } + + /** + * The document range semantic tokens provider interface defines the contract between extensions and + * semantic tokens. + */ + export interface DocumentRangeSemanticTokensProvider { + /** + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens}. + */ + provideDocumentRangeSemanticTokens(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; + } + + /** + * Value-object describing what options formatting should use. + */ + export interface FormattingOptions { + + /** + * Size of a tab in spaces. + */ + tabSize: number; + + /** + * Prefer spaces over tabs. + */ + insertSpaces: boolean; + + /** + * Signature for further properties. + */ + [key: string]: boolean | number | string; + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ + export interface DocumentFormattingEditProvider { + + /** + * Provide formatting edits for a whole document. + * + * @param document The document in which the command was invoked. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult; + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ + export interface DocumentRangeFormattingEditProvider { + + /** + * Provide formatting edits for a range in a document. + * + * The given range is a hint and providers can decide to format a smaller + * or larger range. Often this is done by adjusting the start and end + * of the range to full syntax nodes. + * + * @param document The document in which the command was invoked. + * @param range The range which should be formatted. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ + export interface OnTypeFormattingEditProvider { + + /** + * Provide formatting edits after a character has been typed. + * + * The given position and character should hint to the provider + * what range the position to expand to, like find the matching `{` + * when `}` has been entered. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param ch The character that has been typed. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; + } + + /** + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ + export class ParameterInformation { + + /** + * The label of this signature. + * + * Either a string or inclusive start and exclusive end offsets within its containing + * {@link SignatureInformation.label signature label}. *Note*: A label of type string must be + * a substring of its containing signature information's {@link SignatureInformation.label label}. + */ + label: string | [number, number]; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string | MarkdownString; + + /** + * Creates a new parameter information object. + * + * @param label A label string or inclusive start and exclusive end offsets within its containing signature label. + * @param documentation A doc string. + */ + constructor(label: string | [number, number], documentation?: string | MarkdownString); + } + + /** + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ + export class SignatureInformation { + + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string | MarkdownString; + + /** + * The parameters of this signature. + */ + parameters: ParameterInformation[]; + + /** + * The index of the active parameter. + * + * If provided, this is used in place of {@linkcode SignatureHelp.activeParameter}. + */ + activeParameter?: number; + + /** + * Creates a new signature information object. + * + * @param label A label string. + * @param documentation A doc string. + */ + constructor(label: string, documentation?: string | MarkdownString); + } + + /** + * Signature help represents the signature of something + * callable. There can be multiple signatures but only one + * active and only one active parameter. + */ + export class SignatureHelp { + + /** + * One or more signatures. + */ + signatures: SignatureInformation[]; + + /** + * The active signature. + */ + activeSignature: number; + + /** + * The active parameter of the active signature. + */ + activeParameter: number; + } + + /** + * How a {@linkcode SignatureHelpProvider} was triggered. + */ + export enum SignatureHelpTriggerKind { + /** + * Signature help was invoked manually by the user or by a command. + */ + Invoke = 1, + + /** + * Signature help was triggered by a trigger character. + */ + TriggerCharacter = 2, + + /** + * Signature help was triggered by the cursor moving or by the document content changing. + */ + ContentChange = 3, + } + + /** + * Additional information about the context in which a + * {@linkcode SignatureHelpProvider.provideSignatureHelp SignatureHelpProvider} was triggered. + */ + export interface SignatureHelpContext { + /** + * Action that caused signature help to be triggered. + */ + readonly triggerKind: SignatureHelpTriggerKind; + + /** + * Character that caused signature help to be triggered. + * + * This is `undefined` when signature help is not triggered by typing, such as when manually invoking + * signature help or when moving the cursor. + */ + readonly triggerCharacter: string | undefined; + + /** + * `true` if signature help was already showing when it was triggered. + * + * Retriggers occur when the signature help is already active and can be caused by actions such as + * typing a trigger character, a cursor move, or document content changes. + */ + readonly isRetrigger: boolean; + + /** + * The currently active {@linkcode SignatureHelp}. + * + * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on + * the user arrowing through available signatures. + */ + readonly activeSignatureHelp: SignatureHelp | undefined; + } + + /** + * The signature help provider interface defines the contract between extensions and + * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ + export interface SignatureHelpProvider { + + /** + * Provide help for the signature at the given position and document. + * + * @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. + * @param context Information about how signature help was triggered. + * + * @return Signature help or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; + } + + /** + * Metadata about a registered {@linkcode SignatureHelpProvider}. + */ + export interface SignatureHelpProviderMetadata { + /** + * List of characters that trigger signature help. + */ + readonly triggerCharacters: readonly string[]; + + /** + * List of characters that re-trigger signature help. + * + * These trigger characters are only active when signature help is already showing. All trigger characters + * are also counted as re-trigger characters. + */ + readonly retriggerCharacters: readonly string[]; + } + + /** + * A structured label for a {@link CompletionItem completion item}. + */ + export interface CompletionItemLabel { + + /** + * The label of this completion item. + * + * By default this is also the text that is inserted when this completion is selected. + */ + label: string; + + /** + * An optional string which is rendered less prominently directly after {@link CompletionItemLabel.label label}, + * without any spacing. Should be used for function signatures or type annotations. + */ + detail?: string; + + /** + * An optional string which is rendered less prominently after {@link CompletionItemLabel.detail}. Should be used + * for fully qualified names or file path. + */ + description?: string; + } + + /** + * Completion item kinds. + */ + export enum CompletionItemKind { + Text = 0, + Method = 1, + Function = 2, + Constructor = 3, + Field = 4, + Variable = 5, + Class = 6, + Interface = 7, + Module = 8, + Property = 9, + Unit = 10, + Value = 11, + Enum = 12, + Keyword = 13, + Snippet = 14, + Color = 15, + Reference = 17, + File = 16, + Folder = 18, + EnumMember = 19, + Constant = 20, + Struct = 21, + Event = 22, + Operator = 23, + TypeParameter = 24, + User = 25, + Issue = 26, + } + + /** + * Completion item tags are extra annotations that tweak the rendering of a completion + * item. + */ + export enum CompletionItemTag { + /** + * Render a completion as obsolete, usually using a strike-out. + */ + Deprecated = 1 + } + + /** + * A completion item represents a text snippet that is proposed to complete text that is being typed. + * + * It is sufficient to create a completion item from just a {@link CompletionItem.label label}. In that + * case the completion item will replace the {@link TextDocument.getWordRangeAtPosition word} + * until the cursor with the given label or {@link CompletionItem.insertText insertText}. Otherwise the + * given {@link CompletionItem.textEdit edit} is used. + * + * When selecting a completion item in the editor its defined or synthesized text edit will be applied + * to *all* cursors/selections whereas {@link CompletionItem.additionalTextEdits additionalTextEdits} will be + * applied as provided. + * + * @see {@link CompletionItemProvider.provideCompletionItems} + * @see {@link CompletionItemProvider.resolveCompletionItem} + */ + export class CompletionItem { + + /** + * The label of this completion item. By default + * this is also the text that is inserted when selecting + * this completion. + */ + label: string | CompletionItemLabel; + + /** + * The kind of this completion item. Based on the kind + * an icon is chosen by the editor. + */ + kind?: CompletionItemKind; + + /** + * Tags for this completion item. + */ + tags?: readonly CompletionItemTag[]; + + /** + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + detail?: string; + + /** + * A human-readable string that represents a doc-comment. + */ + documentation?: string | MarkdownString; + + /** + * A string that should be used when comparing this item + * with other items. When `falsy` the {@link CompletionItem.label label} + * is used. + * + * Note that `sortText` is only used for the initial ordering of completion + * items. When having a leading word (prefix) ordering is based on how + * well completions match that prefix and the initial ordering is only used + * when completions match equally well. The prefix is defined by the + * {@linkcode CompletionItem.range range}-property and can therefore be different + * for each completion. + */ + sortText?: string; + + /** + * A string that should be used when filtering a set of + * completion items. When `falsy` the {@link CompletionItem.label label} + * is used. + * + * Note that the filter text is matched against the leading word (prefix) which is defined + * by the {@linkcode CompletionItem.range range}-property. + */ + filterText?: string; + + /** + * Select this item when showing. *Note* that only one completion item can be selected and + * that the editor decides which item that is. The rule is that the *first* item of those + * that match best is selected. + */ + preselect?: boolean; + + /** + * A string or snippet that should be inserted in a document when selecting + * this completion. When `falsy` the {@link CompletionItem.label label} + * is used. + */ + insertText?: string | SnippetString; + + /** + * A range or a insert and replace range selecting the text that should be replaced by this completion item. + * + * When omitted, the range of the {@link TextDocument.getWordRangeAtPosition current word} is used as replace-range + * and as insert-range the start of the {@link TextDocument.getWordRangeAtPosition current word} to the + * current position is used. + * + * *Note 1:* A range must be a {@link Range.isSingleLine single line} and it must + * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. + * *Note 2:* A insert range must be a prefix of a replace range, that means it must be contained and starting at the same position. + */ + range?: Range | { inserting: Range; replacing: Range }; + + /** + * An optional set of characters that when pressed while this completion is active will accept it first and + * then type that character. *Note* that all commit characters should have `length=1` and that superfluous + * characters will be ignored. + */ + commitCharacters?: string[]; + + /** + * Keep whitespace of the {@link CompletionItem.insertText insertText} as is. By default, the editor adjusts leading + * whitespace of new lines so that they match the indentation of the line for which the item is accepted - setting + * this to `true` will prevent that. + */ + keepWhitespace?: boolean; + + /** + * @deprecated Use `CompletionItem.insertText` and `CompletionItem.range` instead. + * + * An {@link TextEdit edit} which is applied to a document when selecting + * this completion. When an edit is provided the value of + * {@link CompletionItem.insertText insertText} is ignored. + * + * The {@link Range} of the edit must be single-line and on the same + * line completions were {@link CompletionItemProvider.provideCompletionItems requested} at. + */ + textEdit?: TextEdit; + + /** + * An optional array of additional {@link TextEdit text edits} that are applied when + * selecting this completion. Edits must not overlap with the main {@link CompletionItem.textEdit edit} + * nor with themselves. + */ + additionalTextEdits?: TextEdit[]; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. *Note* that + * additional modifications to the current document should be described with the + * {@link CompletionItem.additionalTextEdits additionalTextEdits}-property. + */ + command?: Command; + + /** + * Creates a new completion item. + * + * Completion items must have at least a {@link CompletionItem.label label} which then + * will be used as insert text as well as for sorting and filtering. + * + * @param label The label of the completion. + * @param kind The {@link CompletionItemKind kind} of the completion. + */ + constructor(label: string | CompletionItemLabel, kind?: CompletionItemKind); + } + + /** + * Represents a collection of {@link CompletionItem completion items} to be presented + * in the editor. + */ + export class CompletionList { + + /** + * This list is not complete. Further typing should result in recomputing + * this list. + */ + isIncomplete?: boolean; + + /** + * The completion items. + */ + items: T[]; + + /** + * Creates a new completion list. + * + * @param items The completion items. + * @param isIncomplete The list is not complete. + */ + constructor(items?: T[], isIncomplete?: boolean); + } + + /** + * How a {@link CompletionItemProvider completion provider} was triggered + */ + export enum CompletionTriggerKind { + /** + * Completion was triggered normally. + */ + Invoke = 0, + /** + * Completion was triggered by a trigger character. + */ + TriggerCharacter = 1, + /** + * Completion was re-triggered as current completion list is incomplete + */ + TriggerForIncompleteCompletions = 2 + } + + /** + * Contains additional information about the context in which + * {@link CompletionItemProvider.provideCompletionItems completion provider} is triggered. + */ + export interface CompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: CompletionTriggerKind; + + /** + * Character that triggered the completion item provider. + * + * `undefined` if the provider was not triggered by a character. + * + * The trigger character is already in the document when the completion provider is triggered. + */ + readonly triggerCharacter: string | undefined; + } + + /** + * The completion item provider interface defines the contract between extensions and + * [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). + * + * Providers can delay the computation of the {@linkcode CompletionItem.detail detail} + * and {@linkcode CompletionItem.documentation documentation} properties by implementing the + * {@linkcode CompletionItemProvider.resolveCompletionItem resolveCompletionItem}-function. However, properties that + * are needed for the initial sorting and filtering, like `sortText`, `filterText`, `insertText`, and `range`, must + * not be changed during resolve. + * + * Providers are asked for completions either explicitly by a user gesture or -depending on the configuration- + * implicitly when typing words or trigger characters. + */ + export interface CompletionItemProvider { + + /** + * Provide completion items for the given position and document. + * + * @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. + * @param context How the completion was triggered. + * + * @return An array of completions, a {@link CompletionList completion list}, or a thenable that resolves to either. + * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult>; + + /** + * Given a completion item fill in more data, like {@link CompletionItem.documentation doc-comment} + * or {@link CompletionItem.detail details}. + * + * The editor will only resolve a completion item once. + * + * *Note* that this function is called when completion items are already showing in the UI or when an item has been + * selected for insertion. Because of that, no property that changes the presentation (label, sorting, filtering etc) + * or the (primary) insert behaviour ({@link CompletionItem.insertText insertText}) can be changed. + * + * This function may fill in {@link CompletionItem.additionalTextEdits additionalTextEdits}. However, that means an item might be + * inserted *before* resolving is done and in that case the editor will do a best effort to still apply those additional + * text edits. + * + * @param item A completion item currently active in the UI. + * @param token A cancellation token. + * @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult; + } + + + /** + * The inline completion item provider interface defines the contract between extensions and + * the inline completion feature. + * + * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. + */ + export interface InlineCompletionItemProvider { + + /** + * Provides inline completion items for the given position and document. + * If inline completions are enabled, this method will be called whenever the user stopped typing. + * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. + * In that case, all available inline completions should be returned. + * `context.triggerKind` can be used to distinguish between these scenarios. + * + * @param document The document inline completions are requested for. + * @param position The position inline completions are requested for. + * @param context A context object with additional information. + * @param token A cancellation token. + * @return An array of completion items or a thenable that resolves to an array of completion items. + */ + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; + } + + /** + * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented + * in the editor. + */ + export class InlineCompletionList { + /** + * The inline completion items. + */ + items: InlineCompletionItem[]; + + /** + * Creates a new list of inline completion items. + */ + constructor(items: InlineCompletionItem[]); + } + + /** + * Provides information about the context in which an inline completion was requested. + */ + export interface InlineCompletionContext { + /** + * Describes how the inline completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + * + * If set, provided inline completions must extend the text of the selected item + * and use the same range, otherwise they are not shown as preview. + * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, + * the inline completion must also replace `.` and start with `.log`, for example `.log()`. + * + * Inline completion providers are requested again whenever the selected item changes. + */ + readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; + } + + /** + * Describes the currently selected completion item. + */ + export interface SelectedCompletionInfo { + /** + * The range that will be replaced if this completion item is accepted. + */ + readonly range: Range; + + /** + * The text the range will be replaced with if this completion is accepted. + */ + readonly text: string; + } + + /** + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Invoke = 0, + + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 1, + } + + /** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + * + * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} + */ + export class InlineCompletionItem { + /** + * The text to replace the range with. Must be set. + * Is used both for the preview and the accept operation. + */ + insertText: string | SnippetString; + + /** + * A text that is used to decide if this inline completion should be shown. When `falsy` + * the {@link InlineCompletionItem.insertText} is used. + * + * An inline completion is shown if the text to replace is a prefix of the filter text. + */ + filterText?: string; + + /** + * The range to replace. + * Must begin and end on the same line. + * + * Prefer replacements over insertions to provide a better experience when the user deletes typed text. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; + + /** + * Creates a new inline completion item. + * + * @param insertText The text to replace the range with. + * @param range The range to replace. If not set, the word at the requested position will be used. + * @param command An optional {@link Command} that is executed *after* inserting this completion. + */ + constructor(insertText: string | SnippetString, range?: Range, command?: Command); + } + + /** + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ + export class DocumentLink { + + /** + * The range this link applies to. + */ + range: Range; + + /** + * The uri this link points to. + */ + target?: Uri; + + /** + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on how to + * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, + * user settings, and localization. + */ + tooltip?: string; + + /** + * Creates a new document link. + * + * @param range The range the document link applies to. Must not be empty. + * @param target The uri the document link points to. + */ + constructor(range: Range, target?: Uri); + } + + /** + * The document link provider defines the contract between extensions and feature of showing + * links in the editor. + */ + export interface DocumentLinkProvider { + + /** + * Provide links for the given document. Note that the editor ships with a default provider that detects + * `http(s)` and `file` links. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @return An array of {@link DocumentLink document links} or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Given a link fill in its {@link DocumentLink.target target}. This method is called when an incomplete + * link is selected in the UI. Providers can implement this method and return incomplete links + * (without target) from the {@linkcode DocumentLinkProvider.provideDocumentLinks provideDocumentLinks} method which + * often helps to improve performance. + * + * @param link The link that is to be resolved. + * @param token A cancellation token. + */ + resolveDocumentLink?(link: T, token: CancellationToken): ProviderResult; + } + + /** + * Represents a color in RGBA space. + */ + export class Color { + + /** + * The red component of this color in the range [0-1]. + */ + readonly red: number; + + /** + * The green component of this color in the range [0-1]. + */ + readonly green: number; + + /** + * The blue component of this color in the range [0-1]. + */ + readonly blue: number; + + /** + * The alpha component of this color in the range [0-1]. + */ + readonly alpha: number; + + /** + * Creates a new color instance. + * + * @param red The red component. + * @param green The green component. + * @param blue The blue component. + * @param alpha The alpha component. + */ + constructor(red: number, green: number, blue: number, alpha: number); + } + + /** + * Represents a color range from a document. + */ + export class ColorInformation { + + /** + * The range in the document where this color appears. + */ + range: Range; + + /** + * The actual color value for this color range. + */ + color: Color; + + /** + * Creates a new color range. + * + * @param range The range the color appears in. Must not be empty. + * @param color The value of the color. + */ + constructor(range: Range, color: Color); + } + + /** + * A color presentation object describes how a {@linkcode Color} should be represented as text and what + * edits are required to refer to it from source code. + * + * For some languages one color can have multiple presentations, e.g. css can represent the color red with + * the constant `Red`, the hex-value `#ff0000`, or in rgba and hsla forms. In csharp other representations + * apply, e.g. `System.Drawing.Color.Red`. + */ + export class ColorPresentation { + + /** + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + label: string; + + /** + * An {@link TextEdit edit} which is applied to a document when selecting + * this presentation for the color. When `falsy` the {@link ColorPresentation.label label} + * is used. + */ + textEdit?: TextEdit; + + /** + * An optional array of additional {@link TextEdit text edits} that are applied when + * selecting this color presentation. Edits must not overlap with the main {@link ColorPresentation.textEdit edit} nor with themselves. + */ + additionalTextEdits?: TextEdit[]; + + /** + * Creates a new color presentation. + * + * @param label The label of this color presentation. + */ + constructor(label: string); + } + + /** + * The document color provider defines the contract between extensions and feature of + * picking and modifying colors in the editor. + */ + export interface DocumentColorProvider { + + /** + * Provide colors for the given document. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @return An array of {@link ColorInformation color information} or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Provide {@link ColorPresentation representations} for a color. + * + * @param color The color to show and insert. + * @param context A context object with additional information + * @param token A cancellation token. + * @return An array of color presentations or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideColorPresentations(color: Color, context: { readonly document: TextDocument; readonly range: Range }, token: CancellationToken): ProviderResult; + } + + /** + * Inlay hint kinds. + * + * The kind of an inline hint defines its appearance, e.g the corresponding foreground and background colors are being + * used. + */ + export enum InlayHintKind { + /** + * An inlay hint that for a type annotation. + */ + Type = 1, + /** + * An inlay hint that is for a parameter. + */ + Parameter = 2, + } + + /** + * An inlay hint label part allows for interactive and composite labels of inlay hints. + */ + export class InlayHintLabelPart { + + /** + * The value of this label part. + */ + value: string; + + /** + * The tooltip text when you hover over this label part. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + tooltip?: string | MarkdownString | undefined; + + /** + * An optional {@link Location source code location} that represents this label + * part. + * + * The editor will use this location for the hover and for code navigation features: This + * part will become a clickable link that resolves to the definition of the symbol at the + * given location (not necessarily the location itself), it shows the hover that shows at + * the given location, and it shows a context menu with further code navigation commands. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + location?: Location | undefined; + + /** + * An optional command for this label part. + * + * The editor renders parts with commands as clickable links. The command is added to the context menu + * when a label part defines {@link InlayHintLabelPart.location location} and {@link InlayHintLabelPart.command command} . + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + command?: Command | undefined; + + /** + * Creates a new inlay hint label part. + * + * @param value The value of the part. + */ + constructor(value: string); + } + + /** + * Inlay hint information. + */ + export class InlayHint { + + /** + * The position of this hint. + */ + position: Position; + + /** + * The label of this hint. A human readable string or an array of {@link InlayHintLabelPart label parts}. + * + * *Note* that neither the string nor the label part can be empty. + */ + label: string | InlayHintLabelPart[]; + + /** + * The tooltip text when you hover over this item. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + tooltip?: string | MarkdownString | undefined; + + /** + * The kind of this hint. The inlay hint kind defines the appearance of this inlay hint. + */ + kind?: InlayHintKind; + + /** + * Optional {@link TextEdit text edits} that are performed when accepting this inlay hint. The default + * gesture for accepting an inlay hint is the double click. + * + * *Note* that edits are expected to change the document so that the inlay hint (or its nearest variant) is + * now part of the document and the inlay hint itself is now obsolete. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + textEdits?: TextEdit[]; + + /** + * Render padding before the hint. Padding will use the editor's background color, + * not the background color of the hint itself. That means padding can be used to visually + * align/separate an inlay hint. + */ + paddingLeft?: boolean; + + /** + * Render padding after the hint. Padding will use the editor's background color, + * not the background color of the hint itself. That means padding can be used to visually + * align/separate an inlay hint. + */ + paddingRight?: boolean; + + /** + * Creates a new inlay hint. + * + * @param position The position of the hint. + * @param label The label of the hint. + * @param kind The {@link InlayHintKind kind} of the hint. + */ + constructor(position: Position, label: string | InlayHintLabelPart[], kind?: InlayHintKind); + } + + /** + * The inlay hints provider interface defines the contract between extensions and + * the inlay hints feature. + */ + export interface InlayHintsProvider { + + /** + * An optional event to signal that inlay hints from this provider have changed. + */ + onDidChangeInlayHints?: Event; + + /** + * Provide inlay hints for the given range and document. + * + * *Note* that inlay hints that are not {@link Range.contains contained} by the given range are ignored. + * + * @param document The document in which the command was invoked. + * @param range The range for which inlay hints should be computed. + * @param token A cancellation token. + * @return An array of inlay hints or a thenable that resolves to such. + */ + provideInlayHints(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; + + /** + * Given an inlay hint fill in {@link InlayHint.tooltip tooltip}, {@link InlayHint.textEdits text edits}, + * or complete label {@link InlayHintLabelPart parts}. + * + * *Note* that the editor will resolve an inlay hint at most once. + * + * @param hint An inlay hint. + * @param token A cancellation token. + * @return The resolved inlay hint or a thenable that resolves to such. It is OK to return the given `item`. When no result is returned, the given `item` will be used. + */ + resolveInlayHint?(hint: T, token: CancellationToken): ProviderResult; + } + + /** + * A line based folding range. To be valid, start and end line must be bigger than zero and smaller than the number of lines in the document. + * Invalid ranges will be ignored. + */ + export class FoldingRange { + + /** + * The zero-based start line of the range to fold. The folded area starts after the line's last character. + * To be valid, the end must be zero or larger and smaller than the number of lines in the document. + */ + start: number; + + /** + * The zero-based end line of the range to fold. The folded area ends with the line's last character. + * To be valid, the end must be zero or larger and smaller than the number of lines in the document. + */ + end: number; + + /** + * Describes the {@link FoldingRangeKind Kind} of the folding range such as {@link FoldingRangeKind.Comment Comment} or + * {@link FoldingRangeKind.Region Region}. The kind is used to categorize folding ranges and used by commands + * like 'Fold all comments'. See + * {@link FoldingRangeKind} for an enumeration of all kinds. + * If not set, the range is originated from a syntax element. + */ + kind?: FoldingRangeKind; + + /** + * Creates a new folding range. + * + * @param start The start line of the folded range. + * @param end The end line of the folded range. + * @param kind The kind of the folding range. + */ + constructor(start: number, end: number, kind?: FoldingRangeKind); + } + + /** + * An enumeration of specific folding range kinds. The kind is an optional field of a {@link FoldingRange} + * and is used to distinguish specific folding ranges such as ranges originated from comments. The kind is used by commands like + * `Fold all comments` or `Fold all regions`. + * If the kind is not set on the range, the range originated from a syntax element other than comments, imports or region markers. + */ + export enum FoldingRangeKind { + /** + * Kind for folding range representing a comment. + */ + Comment = 1, + /** + * Kind for folding range representing a import. + */ + Imports = 2, + /** + * Kind for folding range representing regions originating from folding markers like `#region` and `#endregion`. + */ + Region = 3 + } + + /** + * Folding context (for future use) + */ + export interface FoldingContext { + } + + /** + * The folding range provider interface defines the contract between extensions and + * [Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) in the editor. + */ + export interface FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChangeFoldingRanges?: Event; + + /** + * Returns a list of folding ranges or null and undefined if the provider + * does not want to participate or was cancelled. + * @param document The document in which the command was invoked. + * @param context Additional context information (for future use) + * @param token A cancellation token. + */ + provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken): ProviderResult; + } + + /** + * A selection range represents a part of a selection hierarchy. A selection range + * may have a parent selection range that contains it. + */ + export class SelectionRange { + + /** + * The {@link Range} of this selection range. + */ + range: Range; + + /** + * The parent selection range containing this range. + */ + parent?: SelectionRange; + + /** + * Creates a new selection range. + * + * @param range The range of the selection range. + * @param parent The parent of the selection range. + */ + constructor(range: Range, parent?: SelectionRange); + } + + export interface SelectionRangeProvider { + /** + * Provide selection ranges for the given positions. + * + * Selection ranges should be computed individually and independent for each position. The editor will merge + * and deduplicate ranges but providers must return hierarchies of selection ranges so that a range + * is {@link Range.contains contained} by its parent. + * + * @param document The document in which the command was invoked. + * @param positions The positions at which the command was invoked. + * @param token A cancellation token. + * @return Selection ranges or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideSelectionRanges(document: TextDocument, positions: readonly Position[], token: CancellationToken): ProviderResult; + } + + /** + * Represents programming constructs like functions or constructors in the context + * of call hierarchy. + */ + export class CallHierarchyItem { + /** + * The name of this item. + */ + name: string; + + /** + * The kind of this item. + */ + kind: SymbolKind; + + /** + * Tags for this item. + */ + tags?: readonly SymbolTag[]; + + /** + * More detail for this item, e.g. the signature of a function. + */ + detail?: string; + + /** + * The resource identifier of this item. + */ + uri: Uri; + + /** + * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. + */ + range: Range; + + /** + * The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function. + * Must be contained by the {@linkcode CallHierarchyItem.range range}. + */ + selectionRange: Range; + + /** + * Creates a new call hierarchy item. + */ + constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); + } + + /** + * Represents an incoming call, e.g. a caller of a method or constructor. + */ + export class CallHierarchyIncomingCall { + + /** + * The item that makes the call. + */ + from: CallHierarchyItem; + + /** + * The range at which at which the calls appears. This is relative to the caller + * denoted by {@linkcode CallHierarchyIncomingCall.from this.from}. + */ + fromRanges: Range[]; + + /** + * Create a new call object. + * + * @param item The item making the call. + * @param fromRanges The ranges at which the calls appear. + */ + constructor(item: CallHierarchyItem, fromRanges: Range[]); + } + + /** + * Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. + */ + export class CallHierarchyOutgoingCall { + + /** + * The item that is called. + */ + to: CallHierarchyItem; + + /** + * The range at which this item is called. This is the range relative to the caller, e.g the item + * passed to {@linkcode CallHierarchyProvider.provideCallHierarchyOutgoingCalls provideCallHierarchyOutgoingCalls} + * and not {@linkcode CallHierarchyOutgoingCall.to this.to}. + */ + fromRanges: Range[]; + + /** + * Create a new call object. + * + * @param item The item being called + * @param fromRanges The ranges at which the calls appear. + */ + constructor(item: CallHierarchyItem, fromRanges: Range[]); + } + + /** + * The call hierarchy provider interface describes the contract between extensions + * and the call hierarchy feature which allows to browse calls and caller of function, + * methods, constructor etc. + */ + export interface CallHierarchyProvider { + + /** + * Bootstraps call hierarchy by returning the item that is denoted by the given document + * and position. This item will be used as entry into the call graph. Providers should + * return `undefined` or `null` when there is no item at the given location. + * + * @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. + * @returns One or multiple call hierarchy items or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + prepareCallHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + + /** + * Provide all incoming calls for an item, e.g all callers for a method. In graph terms this describes directed + * and annotated edges inside the call graph, e.g the given item is the starting node and the result is the nodes + * that can be reached. + * + * @param item The hierarchy item for which incoming calls should be computed. + * @param token A cancellation token. + * @returns A set of incoming calls or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideCallHierarchyIncomingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; + + /** + * Provide all outgoing calls for an item, e.g call calls to functions, methods, or constructors from the given item. In + * graph terms this describes directed and annotated edges inside the call graph, e.g the given item is the starting + * node and the result is the nodes that can be reached. + * + * @param item The hierarchy item for which outgoing calls should be computed. + * @param token A cancellation token. + * @returns A set of outgoing calls or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; + } + + /** + * Represents an item of a type hierarchy, like a class or an interface. + */ + export class TypeHierarchyItem { + /** + * The name of this item. + */ + name: string; + + /** + * The kind of this item. + */ + kind: SymbolKind; + + /** + * Tags for this item. + */ + tags?: ReadonlyArray; + + /** + * More detail for this item, e.g. the signature of a function. + */ + detail?: string; + + /** + * The resource identifier of this item. + */ + uri: Uri; + + /** + * The range enclosing this symbol not including leading/trailing whitespace + * but everything else, e.g. comments and code. + */ + range: Range; + + /** + * The range that should be selected and revealed when this symbol is being + * picked, e.g. the name of a class. Must be contained by the {@link TypeHierarchyItem.range range}-property. + */ + selectionRange: Range; + + /** + * Creates a new type hierarchy item. + * + * @param kind The kind of the item. + * @param name The name of the item. + * @param detail The details of the item. + * @param uri The Uri of the item. + * @param range The whole range of the item. + * @param selectionRange The selection range of the item. + */ + constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); + } + + /** + * The type hierarchy provider interface describes the contract between extensions + * and the type hierarchy feature. + */ + export interface TypeHierarchyProvider { + + /** + * Bootstraps type hierarchy by returning the item that is denoted by the given document + * and position. This item will be used as entry into the type graph. Providers should + * return `undefined` or `null` when there is no item at the given location. + * + * @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. + * @returns One or multiple type hierarchy items or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + prepareTypeHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + + /** + * Provide all supertypes for an item, e.g all types from which a type is derived/inherited. In graph terms this describes directed + * and annotated edges inside the type graph, e.g the given item is the starting node and the result is the nodes + * that can be reached. + * + * @param item The hierarchy item for which super types should be computed. + * @param token A cancellation token. + * @returns A set of direct supertypes or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeHierarchySupertypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult; + + /** + * Provide all subtypes for an item, e.g all types which are derived/inherited from the given item. In + * graph terms this describes directed and annotated edges inside the type graph, e.g the given item is the starting + * node and the result is the nodes that can be reached. + * + * @param item The hierarchy item for which subtypes should be computed. + * @param token A cancellation token. + * @returns A set of direct subtypes or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeHierarchySubtypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult; + } + + /** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid range contents. + */ + export class LinkedEditingRanges { + /** + * Create a new linked editing ranges object. + * + * @param ranges A list of ranges that can be edited together + * @param wordPattern An optional word pattern that describes valid contents for the given ranges + */ + constructor(ranges: Range[], wordPattern?: RegExp); + + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap. + */ + readonly ranges: Range[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + readonly wordPattern: RegExp | undefined; + } + + /** + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. + */ + export interface LinkedEditingRangeProvider { + /** + * For a given position in a document, returns the range of the symbol at the position and all ranges + * that have the same content. A change to one of the ranges can be applied to all other ranges if the new content + * is valid. An optional word pattern can be returned with the result to describe valid contents. + * If no result-specific word pattern is provided, the word pattern from the language configuration is used. + * + * @param document The document in which the provider was invoked. + * @param position The position at which the provider was invoked. + * @param token A cancellation token. + * @return A list of ranges that can be edited together + */ + provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * An edit operation applied {@link DocumentDropEditProvider on drop}. + */ + export class DocumentDropEdit { + /** + * The text or snippet to insert at the drop location. + */ + insertText: string | SnippetString; + + /** + * An optional additional edit to apply on drop. + */ + additionalEdit?: WorkspaceEdit; + + /** + * @param insertText The text or snippet to insert at the drop location. + */ + constructor(insertText: string | SnippetString); + } + + /** + * Provider which handles dropping of resources into a text editor. + * + * This allows users to drag and drop resources (including resources from external apps) into the editor. While dragging + * and dropping files, users can hold down `shift` to drop the file into the editor instead of opening it. + * Requires `editor.dropIntoEditor.enabled` to be on. + */ + export interface DocumentDropEditProvider { + /** + * Provide edits which inserts the content being dragged and dropped into the document. + * + * @param document The document in which the drop occurred. + * @param position The position in the document where the drop occurred. + * @param dataTransfer A {@link DataTransfer} object that holds data about what is being dragged and dropped. + * @param token A cancellation token. + * + * @return A {@link DocumentDropEdit} or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDocumentDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; + } + + /** + * A tuple of two characters, like a pair of + * opening and closing brackets. + */ + export type CharacterPair = [string, string]; + + /** + * Describes how comments for a language work. + */ + export interface CommentRule { + + /** + * The line comment token, like `// this is a comment` + */ + lineComment?: string; + + /** + * The block comment character pair, like `/* block comment */` + */ + blockComment?: CharacterPair; + } + + /** + * Describes indentation rules for a language. + */ + export interface IndentationRule { + /** + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). + */ + decreaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). + */ + increaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then **only the next line** after it should be indented once. + */ + indentNextLinePattern?: RegExp; + /** + * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. + */ + unIndentedLinePattern?: RegExp; + } + + /** + * Describes what to do with the indentation when pressing Enter. + */ + export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 + } + + /** + * Describes what to do when pressing Enter. + */ + export interface EnterAction { + /** + * Describe what to do with the indentation. + */ + indentAction: IndentAction; + /** + * Describes text to be appended after the new line and after the indentation. + */ + appendText?: string; + /** + * Describes the number of characters to remove from the new line's indentation. + */ + removeText?: number; + } + + /** + * Describes a rule to be evaluated when pressing Enter. + */ + export interface OnEnterRule { + /** + * This rule will only execute if the text before the cursor matches this regular expression. + */ + beforeText: RegExp; + /** + * This rule will only execute if the text after the cursor matches this regular expression. + */ + afterText?: RegExp; + /** + * This rule will only execute if the text above the current line matches this regular expression. + */ + previousLineText?: RegExp; + /** + * The action to execute. + */ + action: EnterAction; + } + + /** + * The language configuration interfaces defines the contract between extensions + * and various editor features, like automatic bracket insertion, automatic indentation etc. + */ + export interface LanguageConfiguration { + /** + * The language's comment settings. + */ + comments?: CommentRule; + /** + * The language's brackets. + * This configuration implicitly affects pressing Enter around these brackets. + */ + brackets?: CharacterPair[]; + /** + * The language's word definition. + * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable + * to provide a word definition that uses exclusion of known separators. + * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + */ + wordPattern?: RegExp; + /** + * The language's indentation settings. + */ + indentationRules?: IndentationRule; + /** + * The language's rules to be evaluated when pressing Enter. + */ + onEnterRules?: OnEnterRule[]; + + /** + * **Deprecated** Do not use. + * + * @deprecated Will be replaced by a better API soon. + */ + __electricCharacterSupport?: { + /** + * This property is deprecated and will be **ignored** from + * the editor. + * @deprecated + */ + brackets?: any; + /** + * This property is deprecated and not fully supported anymore by + * the editor (scope and lineStart are ignored). + * Use the autoClosingPairs property in the language configuration file instead. + * @deprecated + */ + docComment?: { + scope: string; + open: string; + lineStart: string; + close?: string; + }; + }; + + /** + * **Deprecated** Do not use. + * + * @deprecated * Use the autoClosingPairs property in the language configuration file instead. + */ + __characterPairSupport?: { + autoClosingPairs: { + open: string; + close: string; + notIn?: string[]; + }[]; + }; + } + + /** + * The configuration target + */ + export enum ConfigurationTarget { + /** + * Global configuration + */ + Global = 1, + + /** + * Workspace configuration + */ + Workspace = 2, + + /** + * Workspace folder configuration + */ + WorkspaceFolder = 3 + } + + /** + * Represents the configuration. It is a merged view of + * + * - *Default Settings* + * - *Global (User) Settings* + * - *Workspace settings* + * - *Workspace Folder settings* - From one of the {@link workspace.workspaceFolders Workspace Folders} under which requested resource belongs to. + * - *Language settings* - Settings defined under requested language. + * + * The *effective* value (returned by {@linkcode WorkspaceConfiguration.get get}) is computed by overriding or merging the values in the following order: + * + * 1. `defaultValue` (if defined in `package.json` otherwise derived from the value's type) + * 1. `globalValue` (if defined) + * 1. `workspaceValue` (if defined) + * 1. `workspaceFolderValue` (if defined) + * 1. `defaultLanguageValue` (if defined) + * 1. `globalLanguageValue` (if defined) + * 1. `workspaceLanguageValue` (if defined) + * 1. `workspaceFolderLanguageValue` (if defined) + * + * **Note:** Only `object` value types are merged and all other value types are overridden. + * + * Example 1: Overriding + * + * ```ts + * defaultValue = 'on'; + * globalValue = 'relative' + * workspaceFolderValue = 'off' + * value = 'off' + * ``` + * + * Example 2: Language Values + * + * ```ts + * defaultValue = 'on'; + * globalValue = 'relative' + * workspaceFolderValue = 'off' + * globalLanguageValue = 'on' + * value = 'on' + * ``` + * + * Example 3: Object Values + * + * ```ts + * defaultValue = { "a": 1, "b": 2 }; + * globalValue = { "b": 3, "c": 4 }; + * value = { "a": 1, "b": 3, "c": 4 }; + * ``` + * + * *Note:* Workspace and Workspace Folder configurations contains `launch` and `tasks` settings. Their basename will be + * part of the section identifier. The following snippets shows how to retrieve all configurations + * from `launch.json`: + * + * ```ts + * // launch.json configuration + * const config = workspace.getConfiguration('launch', vscode.workspace.workspaceFolders[0].uri); + * + * // retrieve values + * const values = config.get('configurations'); + * ``` + * + * Refer to [Settings](https://code.visualstudio.com/docs/getstarted/settings) for more information. + */ + export interface WorkspaceConfiguration { + + /** + * Return a value from this configuration. + * + * @param section Configuration name, supports _dotted_ names. + * @return The value `section` denotes or `undefined`. + */ + get(section: string): T | undefined; + + /** + * Return a value from this configuration. + * + * @param section Configuration name, supports _dotted_ names. + * @param defaultValue A value should be returned when no value could be found, is `undefined`. + * @return The value `section` denotes or the default. + */ + get(section: string, defaultValue: T): T; + + /** + * Check if this configuration has a certain value. + * + * @param section Configuration name, supports _dotted_ names. + * @return `true` if the section doesn't resolve to `undefined`. + */ + has(section: string): boolean; + + /** + * Retrieve all information about a configuration setting. A configuration value + * often consists of a *default* value, a global or installation-wide value, + * a workspace-specific value, folder-specific value + * and language-specific values (if {@link WorkspaceConfiguration} is scoped to a language). + * + * Also provides all language ids under which the given configuration setting is defined. + * + * *Note:* The configuration name must denote a leaf in the configuration tree + * (`editor.fontSize` vs `editor`) otherwise no result is returned. + * + * @param section Configuration name, supports _dotted_ names. + * @return Information about a configuration setting or `undefined`. + */ + inspect(section: string): { + key: string; + + defaultValue?: T; + globalValue?: T; + workspaceValue?: T; + workspaceFolderValue?: T; + + defaultLanguageValue?: T; + globalLanguageValue?: T; + workspaceLanguageValue?: T; + workspaceFolderLanguageValue?: T; + + languageIds?: string[]; + + } | undefined; + + /** + * Update a configuration value. The updated configuration values are persisted. + * + * A value can be changed in + * + * - {@link ConfigurationTarget.Global Global settings}: Changes the value for all instances of the editor. + * - {@link ConfigurationTarget.Workspace Workspace settings}: Changes the value for current workspace, if available. + * - {@link ConfigurationTarget.WorkspaceFolder Workspace folder settings}: Changes the value for settings from one of the {@link workspace.workspaceFolders Workspace Folders} under which the requested resource belongs to. + * - Language settings: Changes the value for the requested languageId. + * + * *Note:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` + * + * @param section Configuration name, supports _dotted_ names. + * @param value The new value. + * @param configurationTarget The {@link ConfigurationTarget configuration target} or a boolean value. + * - If `true` updates {@link ConfigurationTarget.Global Global settings}. + * - If `false` updates {@link ConfigurationTarget.Workspace Workspace settings}. + * - If `undefined` or `null` updates to {@link ConfigurationTarget.WorkspaceFolder Workspace folder settings} if configuration is resource specific, + * otherwise to {@link ConfigurationTarget.Workspace Workspace settings}. + * @param overrideInLanguage Whether to update the value in the scope of requested languageId or not. + * - If `true` updates the value under the requested languageId. + * - If `undefined` updates the value under the requested languageId only if the configuration is defined for the language. + * @throws error while updating + * - configuration which is not registered. + * - window configuration to workspace folder + * - configuration to workspace or workspace folder when no workspace is opened. + * - configuration to workspace folder when there is no workspace folder settings. + * - configuration to workspace folder when {@link WorkspaceConfiguration} is not scoped to a resource. + */ + update(section: string, value: any, configurationTarget?: ConfigurationTarget | boolean | null, overrideInLanguage?: boolean): Thenable; + + /** + * Readable dictionary that backs this configuration. + */ + readonly [key: string]: any; + } + + /** + * Represents a location inside a resource, such as a line + * inside a text file. + */ + export class Location { + + /** + * The resource identifier of this location. + */ + uri: Uri; + + /** + * The document range of this location. + */ + range: Range; + + /** + * Creates a new location object. + * + * @param uri The resource identifier. + * @param rangeOrPosition The range or position. Positions will be converted to an empty range. + */ + constructor(uri: Uri, rangeOrPosition: Range | Position); + } + + /** + * Represents the connection of two locations. Provides additional metadata over normal {@link Location locations}, + * including an origin range. + */ + export interface LocationLink { + /** + * Span of the origin of this link. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + originSelectionRange?: Range; + + /** + * The target resource identifier of this link. + */ + targetUri: Uri; + + /** + * The full target range of this link. + */ + targetRange: Range; + + /** + * The span of this link. + */ + targetSelectionRange?: Range; + } + + /** + * The event that is fired when diagnostics change. + */ + export interface DiagnosticChangeEvent { + + /** + * An array of resources for which diagnostics have changed. + */ + readonly uris: readonly Uri[]; + } + + /** + * Represents the severity of diagnostics. + */ + export enum DiagnosticSeverity { + + /** + * 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, + + /** + * Something to hint to a better way of doing it, like proposing + * a refactoring. + */ + Hint = 3 + } + + /** + * Represents a related message and source code location for a diagnostic. This should be + * used to point to code locations that cause or related to a diagnostics, e.g. when duplicating + * a symbol in a scope. + */ + export class DiagnosticRelatedInformation { + + /** + * The location of this related diagnostic information. + */ + location: Location; + + /** + * The message of this related diagnostic information. + */ + message: string; + + /** + * Creates a new related diagnostic information object. + * + * @param location The location. + * @param message The message. + */ + constructor(location: Location, message: string); + } + + /** + * Additional metadata about the type of a diagnostic. + */ + export enum DiagnosticTag { + /** + * Unused or unnecessary code. + * + * Diagnostics with this tag are rendered faded out. The amount of fading + * is controlled by the `"editorUnnecessaryCode.opacity"` theme color. For + * example, `"editorUnnecessaryCode.opacity": "#000000c0"` will render the + * code with 75% opacity. For high contrast themes, use the + * `"editorUnnecessaryCode.border"` theme color to underline unnecessary code + * instead of fading it out. + */ + Unnecessary = 1, + + /** + * Deprecated or obsolete code. + * + * Diagnostics with this tag are rendered with a strike through. + */ + Deprecated = 2, + } + + /** + * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects + * are only valid in the scope of a file. + */ + export class Diagnostic { + + /** + * The range to which this diagnostic applies. + */ + range: Range; + + /** + * The human-readable message. + */ + message: string; + + /** + * The severity, default is {@link DiagnosticSeverity.Error error}. + */ + severity: DiagnosticSeverity; + + /** + * A human-readable string describing the source of this + * diagnostic, e.g. 'typescript' or 'super lint'. + */ + source?: string; + + /** + * A code or identifier for this diagnostic. + * Should be used for later processing, e.g. when providing {@link CodeActionContext code actions}. + */ + code?: string | number | { + /** + * A code or identifier for this diagnostic. + * Should be used for later processing, e.g. when providing {@link CodeActionContext code actions}. + */ + value: string | number; + + /** + * A target URI to open with more information about the diagnostic error. + */ + target: Uri; + }; + + /** + * An array of related diagnostic information, e.g. when symbol-names within + * a scope collide all definitions can be marked via this property. + */ + relatedInformation?: DiagnosticRelatedInformation[]; + + /** + * Additional metadata about the diagnostic. + */ + tags?: DiagnosticTag[]; + + /** + * Creates a new diagnostic object. + * + * @param range The range to which this diagnostic applies. + * @param message The human-readable message. + * @param severity The severity, default is {@link DiagnosticSeverity.Error error}. + */ + constructor(range: Range, message: string, severity?: DiagnosticSeverity); + } + + /** + * A diagnostics collection is a container that manages a set of + * {@link Diagnostic diagnostics}. Diagnostics are always scopes to a + * diagnostics collection and a resource. + * + * To get an instance of a `DiagnosticCollection` use + * {@link languages.createDiagnosticCollection createDiagnosticCollection}. + */ + export interface DiagnosticCollection extends Iterable<[uri: Uri, diagnostics: readonly Diagnostic[]]> { + + /** + * The name of this diagnostic collection, for instance `typescript`. Every diagnostic + * from this collection will be associated with this name. Also, the task framework uses this + * name when defining [problem matchers](https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher). + */ + readonly name: string; + + /** + * Assign diagnostics for given resource. Will replace + * existing diagnostics for that resource. + * + * @param uri A resource identifier. + * @param diagnostics Array of diagnostics or `undefined` + */ + set(uri: Uri, diagnostics: readonly Diagnostic[] | undefined): void; + + /** + * Replace diagnostics for multiple resources in this collection. + * + * _Note_ that multiple tuples of the same uri will be merged, e.g + * `[[file1, [d1]], [file1, [d2]]]` is equivalent to `[[file1, [d1, d2]]]`. + * If a diagnostics item is `undefined` as in `[file1, undefined]` + * all previous but not subsequent diagnostics are removed. + * + * @param entries An array of tuples, like `[[file1, [d1, d2]], [file2, [d3, d4, d5]]]`, or `undefined`. + */ + set(entries: ReadonlyArray<[Uri, readonly Diagnostic[] | undefined]>): void; + + /** + * Remove all diagnostics from this collection that belong + * to the provided `uri`. The same as `#set(uri, undefined)`. + * + * @param uri A resource identifier. + */ + delete(uri: Uri): void; + + /** + * Remove all diagnostics from this collection. The same + * as calling `#set(undefined)`; + */ + clear(): void; + + /** + * Iterate over each entry in this collection. + * + * @param callback Function to execute for each entry. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callback: (uri: Uri, diagnostics: readonly Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void; + + /** + * Get the diagnostics for a given resource. *Note* that you cannot + * modify the diagnostics-array returned from this call. + * + * @param uri A resource identifier. + * @returns An immutable array of {@link Diagnostic diagnostics} or `undefined`. + */ + get(uri: Uri): readonly Diagnostic[] | undefined; + + /** + * Check if this collection contains diagnostics for a + * given resource. + * + * @param uri A resource identifier. + * @returns `true` if this collection has diagnostic for the given resource. + */ + has(uri: Uri): boolean; + + /** + * Dispose and free associated resources. Calls + * {@link DiagnosticCollection.clear clear}. + */ + dispose(): void; + } + + /** + * Represents the severity of a language status item. + */ + export enum LanguageStatusSeverity { + Information = 0, + Warning = 1, + Error = 2 + } + + /** + * A language status item is the preferred way to present language status reports for the active text editors, + * such as selected linter or notifying about a configuration problem. + */ + export interface LanguageStatusItem { + + /** + * The identifier of this item. + */ + readonly id: string; + + /** + * The short name of this item, like 'Java Language Status', etc. + */ + name: string | undefined; + + /** + * A {@link DocumentSelector selector} that defines for what editors + * this item shows. + */ + selector: DocumentSelector; + + /** + * The severity of this item. + * + * Defaults to {@link LanguageStatusSeverity.Information information}. You can use this property to + * signal to users that there is a problem that needs attention, like a missing executable or an + * invalid configuration. + */ + severity: LanguageStatusSeverity; + + /** + * The text to show for the entry. You can embed icons in the text by leveraging the syntax: + * + * `My text $(icon-name) contains icons like $(icon-name) this one.` + * + * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. + * `light-bulb`, `thumbsup`, `zap` etc. + */ + text: string; + + /** + * Optional, human-readable details for this item. + */ + detail?: string; + + /** + * Controls whether the item is shown as "busy". Defaults to `false`. + */ + busy: boolean; + + /** + * A {@linkcode Command command} for this item. + */ + command: Command | undefined; + + /** + * Accessibility information used when a screen reader interacts with this item + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * Denotes a location of an editor in the window. Editors can be arranged in a grid + * and each column represents one editor location in that grid by counting the editors + * in order of their appearance. + */ + export enum ViewColumn { + /** + * A *symbolic* editor column representing the currently active column. This value + * can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value + * of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Active`. + */ + Active = -1, + /** + * A *symbolic* editor column representing the column to the side of the active one. This value + * can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value + * of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Beside`. + */ + Beside = -2, + /** + * The first editor column. + */ + One = 1, + /** + * The second editor column. + */ + Two = 2, + /** + * The third editor column. + */ + Three = 3, + /** + * The fourth editor column. + */ + Four = 4, + /** + * The fifth editor column. + */ + Five = 5, + /** + * The sixth editor column. + */ + Six = 6, + /** + * The seventh editor column. + */ + Seven = 7, + /** + * The eighth editor column. + */ + Eight = 8, + /** + * The ninth editor column. + */ + Nine = 9 + } + + /** + * An output channel is a container for readonly textual information. + * + * To get an instance of an `OutputChannel` use + * {@link window.createOutputChannel createOutputChannel}. + */ + export interface OutputChannel { + + /** + * The human-readable name of this output channel. + */ + readonly name: string; + + /** + * Append the given value to the channel. + * + * @param value A string, falsy values will not be printed. + */ + append(value: string): void; + + /** + * Append the given value and a line feed character + * to the channel. + * + * @param value A string, falsy values will be printed. + */ + appendLine(value: string): void; + + /** + * Replaces all output from the channel with the given value. + * + * @param value A string, falsy values will not be printed. + */ + replace(value: string): void; + + /** + * Removes all output from the channel. + */ + clear(): void; + + /** + * Reveal this channel in the UI. + * + * @param preserveFocus When `true` the channel will not take focus. + */ + show(preserveFocus?: boolean): void; + + /** + * Reveal this channel in the UI. + * + * @deprecated Use the overload with just one parameter (`show(preserveFocus?: boolean): void`). + * + * @param column This argument is **deprecated** and will be ignored. + * @param preserveFocus When `true` the channel will not take focus. + */ + show(column?: ViewColumn, preserveFocus?: boolean): void; + + /** + * Hide this channel from the UI. + */ + hide(): void; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * Accessibility information which controls screen reader behavior. + */ + export interface AccessibilityInformation { + /** + * Label to be read out by a screen reader once the item has focus. + */ + readonly label: string; + + /** + * Role of the widget which defines how a screen reader interacts with it. + * The role should be set in special cases when for example a tree-like element behaves like a checkbox. + * If role is not specified the editor will pick the appropriate role automatically. + * More about aria roles can be found here https://w3c.github.io/aria/#widget_roles + */ + readonly role?: string; + } + + /** + * Represents the alignment of status bar items. + */ + export enum StatusBarAlignment { + + /** + * Aligned to the left side. + */ + Left = 1, + + /** + * Aligned to the right side. + */ + Right = 2 + } + + /** + * A status bar item is a status bar contribution that can + * show text and icons and run a command on click. + */ + export interface StatusBarItem { + + /** + * The identifier of this item. + * + * *Note*: if no identifier was provided by the {@linkcode window.createStatusBarItem} + * method, the identifier will match the {@link Extension.id extension identifier}. + */ + readonly id: string; + + /** + * The alignment of this item. + */ + readonly alignment: StatusBarAlignment; + + /** + * The priority of this item. Higher value means the item should + * be shown more to the left. + */ + readonly priority: number | undefined; + + /** + * The name of the entry, like 'Python Language Indicator', 'Git Status' etc. + * Try to keep the length of the name short, yet descriptive enough that + * users can understand what the status bar item is about. + */ + name: string | undefined; + + /** + * The text to show for the entry. You can embed icons in the text by leveraging the syntax: + * + * `My text $(icon-name) contains icons like $(icon-name) this one.` + * + * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. + * `light-bulb`, `thumbsup`, `zap` etc. + */ + text: string; + + /** + * The tooltip text when you hover over this entry. + */ + tooltip: string | MarkdownString | undefined; + + /** + * The foreground color for this entry. + */ + color: string | ThemeColor | undefined; + + /** + * The background color for this entry. + * + * *Note*: only the following colors are supported: + * * `new ThemeColor('statusBarItem.errorBackground')` + * * `new ThemeColor('statusBarItem.warningBackground')` + * + * More background colors may be supported in the future. + * + * *Note*: when a background color is set, the statusbar may override + * the `color` choice to ensure the entry is readable in all themes. + */ + backgroundColor: ThemeColor | undefined; + + /** + * {@linkcode Command} or identifier of a command to run on click. + * + * The command must be {@link commands.getCommands known}. + * + * Note that if this is a {@linkcode Command} object, only the {@linkcode Command.command command} and {@linkcode Command.arguments arguments} + * are used by the editor. + */ + command: string | Command | undefined; + + /** + * Accessibility information used when a screen reader interacts with this StatusBar item + */ + accessibilityInformation: AccessibilityInformation | undefined; + + /** + * Shows the entry in the status bar. + */ + show(): void; + + /** + * Hide the entry in the status bar. + */ + hide(): void; + + /** + * Dispose and free associated resources. Call + * {@link StatusBarItem.hide hide}. + */ + dispose(): void; + } + + /** + * Defines a generalized way of reporting progress updates. + */ + export interface Progress { + + /** + * Report a progress update. + * @param value A progress item, like a message and/or an + * report on how much work finished + */ + report(value: T): void; + } + + /** + * An individual terminal instance within the integrated terminal. + */ + export interface Terminal { + + /** + * The name of the terminal. + */ + readonly name: string; + + /** + * The process ID of the shell process. + */ + readonly processId: Thenable; + + /** + * The object used to initialize the terminal, this is useful for example to detecting the + * shell type of when the terminal was not launched by this extension or for detecting what + * folder the shell was launched in. + */ + readonly creationOptions: Readonly; + + /** + * The exit status of the terminal, this will be undefined while the terminal is active. + * + * **Example:** Show a notification with the exit code when the terminal exits with a + * non-zero exit code. + * ```typescript + * window.onDidCloseTerminal(t => { + * if (t.exitStatus && t.exitStatus.code) { + * vscode.window.showInformationMessage(`Exit code: ${t.exitStatus.code}`); + * } + * }); + * ``` + */ + readonly exitStatus: TerminalExitStatus | undefined; + + /** + * The current state of the {@link Terminal}. + */ + readonly state: TerminalState; + + /** + * Send text to the terminal. The text is written to the stdin of the underlying pty process + * (shell) of the terminal. + * + * @param text The text to send. + * @param addNewLine Whether to add a new line to the text being sent, this is normally + * required to run a command in the terminal. The character(s) added are \n or \r\n + * depending on the platform. This defaults to `true`. + */ + sendText(text: string, addNewLine?: boolean): void; + + /** + * Show the terminal panel and reveal this terminal in the UI. + * + * @param preserveFocus When `true` the terminal will not take focus. + */ + show(preserveFocus?: boolean): void; + + /** + * Hide the terminal panel if this terminal is currently showing. + */ + hide(): void; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * The location of the terminal. + */ + export enum TerminalLocation { + /** + * In the terminal view + */ + Panel = 1, + /** + * In the editor area + */ + Editor = 2, + } + + /** + * Assumes a {@link TerminalLocation} of editor and allows specifying a {@link ViewColumn} and + * {@link TerminalEditorLocationOptions.preserveFocus preserveFocus } property + */ + export interface TerminalEditorLocationOptions { + /** + * A view column in which the {@link Terminal terminal} should be shown in the editor area. + * The default is the {@link ViewColumn.Active active}. Columns that do not exist + * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. + * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently + * active one. + */ + viewColumn: ViewColumn; + /** + * An optional flag that when `true` will stop the {@link Terminal} from taking focus. + */ + preserveFocus?: boolean; + } + + /** + * Uses the parent {@link Terminal}'s location for the terminal + */ + export interface TerminalSplitLocationOptions { + /** + * The parent terminal to split this terminal beside. This works whether the parent terminal + * is in the panel or the editor area. + */ + parentTerminal: Terminal; + } + + /** + * Represents the state of a {@link Terminal}. + */ + export interface TerminalState { + /** + * Whether the {@link Terminal} has been interacted with. Interaction means that the + * terminal has sent data to the process which depending on the terminal's _mode_. By + * default input is sent when a key is pressed or when a command or extension sends text, + * but based on the terminal's mode it can also happen on: + * + * - a pointer click event + * - a pointer scroll event + * - a pointer move event + * - terminal focus in/out + * + * For more information on events that can send data see "DEC Private Mode Set (DECSET)" on + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html + */ + readonly isInteractedWith: boolean; + } + + /** + * Provides information on a line in a terminal in order to provide links for it. + */ + export interface TerminalLinkContext { + /** + * This is the text from the unwrapped line in the terminal. + */ + line: string; + + /** + * The terminal the link belongs to. + */ + terminal: Terminal; + } + + /** + * A provider that enables detection and handling of links within terminals. + */ + export interface TerminalLinkProvider { + /** + * Provide terminal links for the given context. Note that this can be called multiple times + * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) + * that could have problems when asynchronous usage may overlap. + * @param context Information about what links are being provided for. + * @param token A cancellation token. + * @return A list of terminal links for the given line. + */ + provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult; + + /** + * Handle an activated terminal link. + * @param link The link to handle. + */ + handleTerminalLink(link: T): ProviderResult; + } + + /** + * A link on a terminal line. + */ + export class TerminalLink { + /** + * The start index of the link on {@link TerminalLinkContext.line}. + */ + startIndex: number; + + /** + * The length of the link on {@link TerminalLinkContext.line}. + */ + length: number; + + /** + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on + * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary + * depending on OS, user settings, and localization. + */ + tooltip?: string; + + /** + * Creates a new terminal link. + * @param startIndex The start index of the link on {@link TerminalLinkContext.line}. + * @param length The length of the link on {@link TerminalLinkContext.line}. + * @param tooltip The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on + * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary + * depending on OS, user settings, and localization. + */ + constructor(startIndex: number, length: number, tooltip?: string); + } + + /** + * Provides a terminal profile for the contributed terminal profile when launched via the UI or + * command. + */ + export interface TerminalProfileProvider { + /** + * Provide the terminal profile. + * @param token A cancellation token that indicates the result is no longer needed. + * @returns The terminal profile. + */ + provideTerminalProfile(token: CancellationToken): ProviderResult; + } + + /** + * A terminal profile defines how a terminal will be launched. + */ + export class TerminalProfile { + /** + * The options that the terminal will launch with. + */ + options: TerminalOptions | ExtensionTerminalOptions; + + /** + * Creates a new terminal profile. + * @param options The options that the terminal will launch with. + */ + constructor(options: TerminalOptions | ExtensionTerminalOptions); + } + + /** + * A file decoration represents metadata that can be rendered with a file. + */ + export class FileDecoration { + + /** + * A very short string that represents this decoration. + */ + badge?: string; + + /** + * A human-readable tooltip for this decoration. + */ + tooltip?: string; + + /** + * The color of this decoration. + */ + color?: ThemeColor; + + /** + * A flag expressing that this decoration should be + * propagated to its parents. + */ + propagate?: boolean; + + /** + * Creates a new decoration. + * + * @param badge A letter that represents the decoration. + * @param tooltip The tooltip of the decoration. + * @param color The color of the decoration. + */ + constructor(badge?: string, tooltip?: string, color?: ThemeColor); + } + + /** + * The decoration provider interfaces defines the contract between extensions and + * file decorations. + */ + export interface FileDecorationProvider { + + /** + * An optional event to signal that decorations for one or many files have changed. + * + * *Note* that this event should be used to propagate information about children. + * + * @see {@link EventEmitter} + */ + onDidChangeFileDecorations?: Event; + + /** + * Provide decorations for a given uri. + * + * *Note* that this function is only called when a file gets rendered in the UI. + * This means a decoration from a descendent that propagates upwards must be signaled + * to the editor via the {@link FileDecorationProvider.onDidChangeFileDecorations onDidChangeFileDecorations}-event. + * + * @param uri The uri of the file to provide a decoration for. + * @param token A cancellation token. + * @returns A decoration or a thenable that resolves to such. + */ + provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; + } + + + /** + * In a remote window the extension kind describes if an extension + * runs where the UI (window) runs or if an extension runs remotely. + */ + export enum ExtensionKind { + + /** + * Extension runs where the UI runs. + */ + UI = 1, + + /** + * Extension runs where the remote extension host runs. + */ + Workspace = 2 + } + + /** + * Represents an extension. + * + * To get an instance of an `Extension` use {@link extensions.getExtension getExtension}. + */ + export interface Extension { + + /** + * The canonical extension identifier in the form of: `publisher.name`. + */ + readonly id: string; + + /** + * The uri of the directory containing the extension. + */ + readonly extensionUri: Uri; + + /** + * The absolute file path of the directory containing this extension. Shorthand + * notation for {@link Extension.extensionUri Extension.extensionUri.fsPath} (independent of the uri scheme). + */ + readonly extensionPath: string; + + /** + * `true` if the extension has been activated. + */ + readonly isActive: boolean; + + /** + * The parsed contents of the extension's package.json. + */ + readonly packageJSON: any; + + /** + * The extension kind describes if an extension runs where the UI runs + * or if an extension runs where the remote extension host runs. The extension kind + * is defined in the `package.json`-file of extensions but can also be refined + * via the `remote.extensionKind`-setting. When no remote extension host exists, + * the value is {@linkcode ExtensionKind.UI}. + */ + extensionKind: ExtensionKind; + + /** + * The public API exported by this extension (return value of `activate`). + * It is an invalid action to access this field before this extension has been activated. + */ + readonly exports: T; + + /** + * Activates this extension and returns its public API. + * + * @return A promise that will resolve when this extension has been activated. + */ + activate(): Thenable; + } + + /** + * The ExtensionMode is provided on the `ExtensionContext` and indicates the + * mode the specific extension is running in. + */ + export enum ExtensionMode { + /** + * The extension is installed normally (for example, from the marketplace + * or VSIX) in the editor. + */ + Production = 1, + + /** + * The extension is running from an `--extensionDevelopmentPath` provided + * when launching the editor. + */ + Development = 2, + + /** + * The extension is running from an `--extensionTestsPath` and + * the extension host is running unit tests. + */ + Test = 3, + } + + /** + * An extension context is a collection of utilities private to an + * extension. + * + * An instance of an `ExtensionContext` is provided as the first + * parameter to the `activate`-call of an extension. + */ + export interface ExtensionContext { + + /** + * An array to which disposables can be added. When this + * extension is deactivated the disposables will be disposed. + * + * *Note* that asynchronous dispose-functions aren't awaited. + */ + readonly subscriptions: { dispose(): any }[]; + + /** + * A memento object that stores state in the context + * of the currently opened {@link workspace.workspaceFolders workspace}. + */ + readonly workspaceState: Memento; + + /** + * A memento object that stores state independent + * of the current opened {@link workspace.workspaceFolders workspace}. + */ + readonly globalState: Memento & { + /** + * Set the keys whose values should be synchronized across devices when synchronizing user-data + * like configuration, extensions, and mementos. + * + * Note that this function defines the whole set of keys whose values are synchronized: + * - calling it with an empty array stops synchronization for this memento + * - calling it with a non-empty array replaces all keys whose values are synchronized + * + * For any given set of keys this function needs to be called only once but there is no harm in + * repeatedly calling it. + * + * @param keys The set of keys whose values are synced. + */ + setKeysForSync(keys: readonly string[]): void; + }; + + /** + * A storage utility for secrets. Secrets are persisted across reloads and are independent of the + * current opened {@link workspace.workspaceFolders workspace}. + */ + readonly secrets: SecretStorage; + + /** + * The uri of the directory containing the extension. + */ + readonly extensionUri: Uri; + + /** + * The absolute file path of the directory containing the extension. Shorthand + * notation for {@link TextDocument.uri ExtensionContext.extensionUri.fsPath} (independent of the uri scheme). + */ + readonly extensionPath: string; + + /** + * Gets the extension's environment variable collection for this workspace, enabling changes + * to be applied to terminal environment variables. + */ + readonly environmentVariableCollection: EnvironmentVariableCollection; + + /** + * Get the absolute path of a resource contained in the extension. + * + * *Note* that an absolute uri can be constructed via {@linkcode Uri.joinPath} and + * {@linkcode ExtensionContext.extensionUri extensionUri}, e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` + * + * @param relativePath A relative path to a resource contained in the extension. + * @return The absolute path of the resource. + */ + asAbsolutePath(relativePath: string): string; + + /** + * The uri of a workspace specific directory in which the extension + * can store private state. The directory might not exist and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * The value is `undefined` when no workspace nor folder has been opened. + * + * Use {@linkcode ExtensionContext.workspaceState workspaceState} or + * {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from + * an uri. + */ + readonly storageUri: Uri | undefined; + + /** + * An absolute file path of a workspace specific directory in which the extension + * can store private state. The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use {@linkcode ExtensionContext.workspaceState workspaceState} or + * {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @deprecated Use {@link ExtensionContext.storageUri storageUri} instead. + */ + readonly storagePath: string | undefined; + + /** + * The uri of a directory in which the extension can store global state. + * The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from + * an uri. + */ + readonly globalStorageUri: Uri; + + /** + * An absolute file path in which the extension can store global state. + * The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @deprecated Use {@link ExtensionContext.globalStorageUri globalStorageUri} instead. + */ + readonly globalStoragePath: string; + + /** + * The uri of a directory in which the extension can create log files. + * The directory might not exist on disk and creation is up to the extension. However, + * the parent directory is guaranteed to be existent. + * + * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from + * an uri. + */ + readonly logUri: Uri; + + /** + * An absolute file path of a directory in which the extension can create log files. + * The directory might not exist on disk and creation is up to the extension. However, + * the parent directory is guaranteed to be existent. + * + * @deprecated Use {@link ExtensionContext.logUri logUri} instead. + */ + readonly logPath: string; + + /** + * The mode the extension is running in. This is specific to the current + * extension. One extension may be in `ExtensionMode.Development` while + * other extensions in the host run in `ExtensionMode.Release`. + */ + readonly extensionMode: ExtensionMode; + + /** + * The current `Extension` instance. + */ + readonly extension: Extension; + } + + /** + * A memento represents a storage utility. It can store and retrieve + * values. + */ + export interface Memento { + + /** + * Returns the stored keys. + * + * @return The stored keys. + */ + keys(): readonly string[]; + + /** + * Return a value. + * + * @param key A string. + * @return The stored value or `undefined`. + */ + get(key: string): T | undefined; + + /** + * Return a value. + * + * @param key A string. + * @param defaultValue A value that should be returned when there is no + * value (`undefined`) with the given key. + * @return The stored value or the defaultValue. + */ + get(key: string, defaultValue: T): T; + + /** + * Store a value. The value must be JSON-stringifyable. + * + * *Note* that using `undefined` as value removes the key from the underlying + * storage. + * + * @param key A string. + * @param value A value. MUST not contain cyclic references. + */ + update(key: string, value: any): Thenable; + } + + /** + * The event data that is fired when a secret is added or removed. + */ + export interface SecretStorageChangeEvent { + /** + * The key of the secret that has changed. + */ + readonly key: string; + } + + /** + * Represents a storage utility for secrets, information that is + * sensitive. + */ + export interface SecretStorage { + /** + * Retrieve a secret that was stored with key. Returns undefined if there + * is no password matching that key. + * @param key The key the secret was stored under. + * @returns The stored value or `undefined`. + */ + get(key: string): Thenable; + + /** + * Store a secret under a given key. + * @param key The key to store the secret under. + * @param value The secret. + */ + store(key: string, value: string): Thenable; + + /** + * Remove a secret from storage. + * @param key The key the secret was stored under. + */ + delete(key: string): Thenable; + + /** + * Fires when a secret is stored or deleted. + */ + onDidChange: Event; + } + + /** + * Represents a color theme kind. + */ + export enum ColorThemeKind { + Light = 1, + Dark = 2, + HighContrast = 3, + HighContrastLight = 4 + } + + /** + * Represents a color theme. + */ + export interface ColorTheme { + + /** + * The kind of this color theme: light, dark, high contrast dark and high contrast light. + */ + readonly kind: ColorThemeKind; + } + + /** + * Controls the behaviour of the terminal's visibility. + */ + export enum TaskRevealKind { + /** + * Always brings the terminal to front if the task is executed. + */ + Always = 1, + + /** + * Only brings the terminal to front if a problem is detected executing the task + * (e.g. the task couldn't be started because). + */ + Silent = 2, + + /** + * The terminal never comes to front when the task is executed. + */ + Never = 3 + } + + /** + * Controls how the task channel is used between tasks + */ + export enum TaskPanelKind { + + /** + * Shares a panel with other tasks. This is the default. + */ + Shared = 1, + + /** + * Uses a dedicated panel for this tasks. The panel is not + * shared with other tasks. + */ + Dedicated = 2, + + /** + * Creates a new panel whenever this task is executed. + */ + New = 3 + } + + /** + * Controls how the task is presented in the UI. + */ + export interface TaskPresentationOptions { + /** + * Controls whether the task output is reveal in the user interface. + * Defaults to `RevealKind.Always`. + */ + reveal?: TaskRevealKind; + + /** + * Controls whether the command associated with the task is echoed + * in the user interface. + */ + echo?: boolean; + + /** + * Controls whether the panel showing the task output is taking focus. + */ + focus?: boolean; + + /** + * Controls if the task panel is used for this task only (dedicated), + * shared between tasks (shared) or if a new panel is created on + * every task execution (new). Defaults to `TaskInstanceKind.Shared` + */ + panel?: TaskPanelKind; + + /** + * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message. + */ + showReuseMessage?: boolean; + + /** + * Controls whether the terminal is cleared before executing the task. + */ + clear?: boolean; + } + + /** + * A grouping for tasks. The editor by default supports the + * 'Clean', 'Build', 'RebuildAll' and 'Test' group. + */ + export class TaskGroup { + + /** + * The clean task group; + */ + static Clean: TaskGroup; + + /** + * The build task group; + */ + static Build: TaskGroup; + + /** + * The rebuild all task group; + */ + static Rebuild: TaskGroup; + + /** + * The test all task group; + */ + static Test: TaskGroup; + + /** + * Whether the task that is part of this group is the default for the group. + * This property cannot be set through API, and is controlled by a user's task configurations. + */ + readonly isDefault: boolean | undefined; + + /** + * The ID of the task group. Is one of TaskGroup.Clean.id, TaskGroup.Build.id, TaskGroup.Rebuild.id, or TaskGroup.Test.id. + */ + readonly id: string; + + private constructor(id: string, label: string); + } + + /** + * A structure that defines a task kind in the system. + * The value must be JSON-stringifyable. + */ + export interface TaskDefinition { + /** + * The task definition describing the task provided by an extension. + * Usually a task provider defines more properties to identify + * a task. They need to be defined in the package.json of the + * extension under the 'taskDefinitions' extension point. The npm + * task definition for example looks like this + * ```typescript + * interface NpmTaskDefinition extends TaskDefinition { + * script: string; + * } + * ``` + * + * Note that type identifier starting with a '$' are reserved for internal + * usages and shouldn't be used by extensions. + */ + readonly type: string; + + /** + * Additional attributes of a concrete task definition. + */ + [name: string]: any; + } + + /** + * Options for a process execution + */ + export interface ProcessExecutionOptions { + /** + * The current working directory of the executed program or shell. + * If omitted the tools current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } + + /** + * The execution of a task happens as an external process + * without shell interaction. + */ + export class ProcessExecution { + + /** + * Creates a process execution. + * + * @param process The process to start. + * @param options Optional options for the started process. + */ + constructor(process: string, options?: ProcessExecutionOptions); + + /** + * Creates a process execution. + * + * @param process The process to start. + * @param args Arguments to be passed to the process. + * @param options Optional options for the started process. + */ + constructor(process: string, args: string[], options?: ProcessExecutionOptions); + + /** + * The process to be executed. + */ + process: string; + + /** + * The arguments passed to the process. Defaults to an empty array. + */ + args: string[]; + + /** + * The process options used when the process is executed. + * Defaults to undefined. + */ + options?: ProcessExecutionOptions; + } + + /** + * The shell quoting options. + */ + export interface ShellQuotingOptions { + + /** + * The character used to do character escaping. If a string is provided only spaces + * are escaped. If a `{ escapeChar, charsToEscape }` literal is provide all characters + * in `charsToEscape` are escaped using the `escapeChar`. + */ + escape?: string | { + /** + * The escape character. + */ + escapeChar: string; + /** + * The characters to escape. + */ + charsToEscape: string; + }; + + /** + * The character used for strong quoting. The string's length must be 1. + */ + strong?: string; + + /** + * The character used for weak quoting. The string's length must be 1. + */ + weak?: string; + } + + /** + * Options for a shell execution + */ + export interface ShellExecutionOptions { + /** + * The shell executable. + */ + executable?: string; + + /** + * The arguments to be passed to the shell executable used to run the task. Most shells + * require special arguments to execute a command. For example `bash` requires the `-c` + * argument to execute a command, `PowerShell` requires `-Command` and `cmd` requires both + * `/d` and `/c`. + */ + shellArgs?: string[]; + + /** + * The shell quotes supported by this shell. + */ + shellQuoting?: ShellQuotingOptions; + + /** + * The current working directory of the executed shell. + * If omitted the tools current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } + + /** + * Defines how an argument should be quoted if it contains + * spaces or unsupported characters. + */ + export enum ShellQuoting { + + /** + * Character escaping should be used. This for example + * uses \ on bash and ` on PowerShell. + */ + Escape = 1, + + /** + * Strong string quoting should be used. This for example + * uses " for Windows cmd and ' for bash and PowerShell. + * Strong quoting treats arguments as literal strings. + * Under PowerShell echo 'The value is $(2 * 3)' will + * print `The value is $(2 * 3)` + */ + Strong = 2, + + /** + * Weak string quoting should be used. This for example + * uses " for Windows cmd, bash and PowerShell. Weak quoting + * still performs some kind of evaluation inside the quoted + * string. Under PowerShell echo "The value is $(2 * 3)" + * will print `The value is 6` + */ + Weak = 3 + } + + /** + * A string that will be quoted depending on the used shell. + */ + export interface ShellQuotedString { + /** + * The actual string value. + */ + value: string; + + /** + * The quoting style to use. + */ + quoting: ShellQuoting; + } + + export class ShellExecution { + /** + * Creates a shell execution with a full command line. + * + * @param commandLine The command line to execute. + * @param options Optional options for the started the shell. + */ + constructor(commandLine: string, options?: ShellExecutionOptions); + + /** + * Creates a shell execution with a command and arguments. For the real execution the editor will + * construct a command line from the command and the arguments. This is subject to interpretation + * especially when it comes to quoting. If full control over the command line is needed please + * use the constructor that creates a `ShellExecution` with the full command line. + * + * @param command The command to execute. + * @param args The command arguments. + * @param options Optional options for the started the shell. + */ + constructor(command: string | ShellQuotedString, args: (string | ShellQuotedString)[], options?: ShellExecutionOptions); + + /** + * The shell command line. Is `undefined` if created with a command and arguments. + */ + commandLine: string | undefined; + + /** + * The shell command. Is `undefined` if created with a full command line. + */ + command: string | ShellQuotedString; + + /** + * The shell args. Is `undefined` if created with a full command line. + */ + args: (string | ShellQuotedString)[]; + + /** + * The shell options used when the command line is executed in a shell. + * Defaults to undefined. + */ + options?: ShellExecutionOptions; + } + + /** + * Class used to execute an extension callback as a task. + */ + export class CustomExecution { + /** + * Constructs a CustomExecution task object. The callback will be executed when 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 + * {@link Pseudoterminal.open} is called. Task cancellation should be handled using + * {@link Pseudoterminal.close}. When the task is complete fire + * {@link Pseudoterminal.onDidClose}. + * @param callback The callback that will be called when the task is started by a user. Any ${} style variables that + * were in the task definition will be resolved and passed into the callback as `resolvedDefinition`. + */ + constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); + } + + /** + * The scope of a task. + */ + export enum TaskScope { + /** + * The task is a global task. Global tasks are currently not supported. + */ + Global = 1, + + /** + * The task is a workspace task + */ + Workspace = 2 + } + + /** + * Run options for a task. + */ + export interface RunOptions { + /** + * Controls whether task variables are re-evaluated on rerun. + */ + reevaluateOnRerun?: boolean; + } + + /** + * A task to execute + */ + export class Task { + + /** + * Creates a new task. + * + * @param taskDefinition The task definition as defined in the taskDefinitions extension point. + * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. Global tasks are currently not supported. + * @param name The task's name. Is presented in the user interface. + * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. + * @param execution The process or shell execution. + * @param problemMatchers the names of problem matchers to use, like '$tsc' + * or '$eslint'. Problem matchers can be contributed by an extension using + * the `problemMatchers` extension point. + */ + constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); + + /** + * Creates a new task. + * + * @deprecated Use the new constructors that allow specifying a scope for the task. + * + * @param taskDefinition The task definition as defined in the taskDefinitions extension point. + * @param name The task's name. Is presented in the user interface. + * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. + * @param execution The process or shell execution. + * @param problemMatchers the names of problem matchers to use, like '$tsc' + * or '$eslint'. Problem matchers can be contributed by an extension using + * the `problemMatchers` extension point. + */ + constructor(taskDefinition: TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + + /** + * The task's definition. + */ + definition: TaskDefinition; + + /** + * The task's scope. + */ + readonly scope: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder | undefined; + + /** + * The task's name + */ + name: string; + + /** + * A human-readable string which is rendered less prominently on a separate line in places + * where the task's name is displayed. Supports rendering of {@link ThemeIcon theme icons} + * via the `$()`-syntax. + */ + detail?: string; + + /** + * The task's execution engine + */ + execution?: ProcessExecution | ShellExecution | CustomExecution; + + /** + * Whether the task is a background task or not. + */ + isBackground: boolean; + + /** + * A human-readable string describing the source of this shell task, e.g. 'gulp' + * or 'npm'. Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax. + */ + source: string; + + /** + * The task group this tasks belongs to. See TaskGroup + * for a predefined set of available groups. + * Defaults to undefined meaning that the task doesn't + * belong to any special group. + */ + group?: TaskGroup; + + /** + * The presentation options. Defaults to an empty literal. + */ + presentationOptions: TaskPresentationOptions; + + /** + * The problem matchers attached to the task. Defaults to an empty + * array. + */ + problemMatchers: string[]; + + /** + * Run options for the task + */ + runOptions: RunOptions; + } + + /** + * A task provider allows to add tasks to the task service. + * A task provider is registered via {@link tasks.registerTaskProvider}. + */ + export interface TaskProvider { + /** + * Provides tasks. + * @param token A cancellation token. + * @return an array of tasks + */ + provideTasks(token: CancellationToken): ProviderResult; + + /** + * Resolves a task that has no {@linkcode Task.execution execution} set. Tasks are + * often created from information found in the `tasks.json`-file. Such tasks miss + * the information on how to execute them and a task provider must fill in + * the missing information in the `resolveTask`-method. This method will not be + * called for tasks returned from the above `provideTasks` method since those + * tasks are always fully resolved. A valid default implementation for the + * `resolveTask` method is to return `undefined`. + * + * Note that when filling in the properties of `task`, you _must_ be sure to + * use the exact same `TaskDefinition` and not create a new one. Other properties + * may be changed. + * + * @param task The task to resolve. + * @param token A cancellation token. + * @return The resolved task + */ + resolveTask(task: T, token: CancellationToken): ProviderResult; + } + + /** + * An object representing an executed Task. It can be used + * to terminate a task. + * + * This interface is not intended to be implemented. + */ + export interface TaskExecution { + /** + * The task that got started. + */ + task: Task; + + /** + * Terminates the task execution. + */ + terminate(): void; + } + + /** + * An event signaling the start of a task execution. + * + * This interface is not intended to be implemented. + */ + interface TaskStartEvent { + /** + * The task item representing the task that got started. + */ + readonly execution: TaskExecution; + } + + /** + * An event signaling the end of an executed task. + * + * This interface is not intended to be implemented. + */ + interface TaskEndEvent { + /** + * The task item representing the task that finished. + */ + readonly execution: TaskExecution; + } + + /** + * An event signaling the start of a process execution + * triggered through a task + */ + export interface TaskProcessStartEvent { + + /** + * The task execution for which the process got started. + */ + readonly execution: TaskExecution; + + /** + * The underlying process id. + */ + readonly processId: number; + } + + /** + * An event signaling the end of a process execution + * triggered through a task + */ + export interface TaskProcessEndEvent { + + /** + * The task execution for which the process got started. + */ + readonly execution: TaskExecution; + + /** + * The process's exit code. Will be `undefined` when the task is terminated. + */ + readonly exitCode: number | undefined; + } + + export interface TaskFilter { + /** + * The task version as used in the tasks.json file. + * The string support the package.json semver notation. + */ + version?: string; + + /** + * The task type to return; + */ + type?: string; + } + + /** + * Namespace for tasks functionality. + */ + export namespace tasks { + + /** + * Register a task provider. + * + * @param type The task kind type this provider is registered for. + * @param provider A task provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; + + /** + * Fetches all tasks available in the systems. This includes tasks + * from `tasks.json` files as well as tasks from task providers + * contributed through extensions. + * + * @param filter Optional filter to select tasks of a certain type or version. + */ + export function fetchTasks(filter?: TaskFilter): Thenable; + + /** + * Executes a task that is managed by the editor. The returned + * task execution can be used to terminate the task. + * + * @throws When running a ShellExecution or a ProcessExecution + * task in an environment where a new process cannot be started. + * In such an environment, only CustomExecution tasks can be run. + * + * @param task the task to execute + */ + export function executeTask(task: Task): Thenable; + + /** + * The currently active task executions or an empty array. + */ + export const taskExecutions: readonly TaskExecution[]; + + /** + * Fires when a task starts. + */ + export const onDidStartTask: Event; + + /** + * Fires when a task ends. + */ + export const onDidEndTask: Event; + + /** + * Fires when the underlying process has been started. + * This event will not fire for tasks that don't + * execute an underlying process. + */ + export const onDidStartTaskProcess: Event; + + /** + * Fires when the underlying process has ended. + * This event will not fire for tasks that don't + * execute an underlying process. + */ + export const onDidEndTaskProcess: Event; + } + + /** + * Enumeration of file types. The types `File` and `Directory` can also be + * a symbolic links, in that case use `FileType.File | FileType.SymbolicLink` and + * `FileType.Directory | FileType.SymbolicLink`. + */ + export enum FileType { + /** + * The file type is unknown. + */ + Unknown = 0, + /** + * A regular file. + */ + File = 1, + /** + * A directory. + */ + Directory = 2, + /** + * A symbolic link to a file. + */ + SymbolicLink = 64 + } + + export enum FilePermission { + /** + * The file is readonly. + * + * *Note:* All `FileStat` from a `FileSystemProvider` that is registered with + * the option `isReadonly: true` will be implicitly handled as if `FilePermission.Readonly` + * is set. As a consequence, it is not possible to have a readonly file system provider + * registered where some `FileStat` are not readonly. + */ + Readonly = 1 + } + + /** + * The `FileStat`-type represents metadata about a file + */ + export interface FileStat { + /** + * The type of the file, e.g. is a regular file, a directory, or symbolic link + * to a file. + * + * *Note:* This value might be a bitmask, e.g. `FileType.File | FileType.SymbolicLink`. + */ + type: FileType; + /** + * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + */ + ctime: number; + /** + * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + * + * *Note:* If the file changed, it is important to provide an updated `mtime` that advanced + * from the previous value. Otherwise there may be optimizations in place that will not show + * the updated file contents in an editor for example. + */ + mtime: number; + /** + * The size in bytes. + * + * *Note:* If the file changed, it is important to provide an updated `size`. Otherwise there + * may be optimizations in place that will not show the updated file contents in an editor for + * example. + */ + size: number; + /** + * The permissions of the file, e.g. whether the file is readonly. + * + * *Note:* This value might be a bitmask, e.g. `FilePermission.Readonly | FilePermission.Other`. + */ + permissions?: FilePermission; + } + + /** + * A type that filesystem providers should use to signal errors. + * + * This class has factory methods for common error-cases, like `FileNotFound` when + * a file or folder doesn't exist, use them like so: `throw vscode.FileSystemError.FileNotFound(someUri);` + */ + export class FileSystemError extends Error { + + /** + * Create an error to signal that a file or folder wasn't found. + * @param messageOrUri Message or uri. + */ + static FileNotFound(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that a file or folder already exists, e.g. when + * creating but not overwriting a file. + * @param messageOrUri Message or uri. + */ + static FileExists(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that a file is not a folder. + * @param messageOrUri Message or uri. + */ + static FileNotADirectory(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that a file is a folder. + * @param messageOrUri Message or uri. + */ + static FileIsADirectory(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that an operation lacks required permissions. + * @param messageOrUri Message or uri. + */ + static NoPermissions(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that the file system is unavailable or too busy to + * complete a request. + * @param messageOrUri Message or uri. + */ + static Unavailable(messageOrUri?: string | Uri): FileSystemError; + + /** + * Creates a new filesystem error. + * + * @param messageOrUri Message or uri. + */ + constructor(messageOrUri?: string | Uri); + + /** + * A code that identifies this error. + * + * Possible values are names of errors, like {@linkcode FileSystemError.FileNotFound FileNotFound}, + * or `Unknown` for unspecified errors. + */ + readonly code: string; + } + + /** + * Enumeration of file change types. + */ + export enum FileChangeType { + + /** + * The contents or metadata of a file have changed. + */ + Changed = 1, + + /** + * A file has been created. + */ + Created = 2, + + /** + * A file has been deleted. + */ + Deleted = 3, + } + + /** + * The event filesystem providers must use to signal a file change. + */ + export interface FileChangeEvent { + + /** + * The type of change. + */ + readonly type: FileChangeType; + + /** + * The uri of the file that has changed. + */ + readonly uri: Uri; + } + + /** + * The filesystem provider defines what the editor needs to read, write, discover, + * and to manage files and folders. It allows extensions to serve files from remote places, + * like ftp-servers, and to seamlessly integrate those into the editor. + * + * * *Note 1:* The filesystem provider API works with {@link Uri uris} and assumes hierarchical + * paths, e.g. `foo:/my/path` is a child of `foo:/my/` and a parent of `foo:/my/path/deeper`. + * * *Note 2:* There is an activation event `onFileSystem:` that fires when a file + * or folder is being accessed. + * * *Note 3:* The word 'file' is often used to denote all {@link FileType kinds} of files, e.g. + * folders, symbolic links, and regular files. + */ + export interface FileSystemProvider { + + /** + * An event to signal that a resource has been created, changed, or deleted. This + * event should fire for resources that are being {@link FileSystemProvider.watch watched} + * by clients of this provider. + * + * *Note:* It is important that the metadata of the file that changed provides an + * updated `mtime` that advanced from the previous value in the {@link FileStat stat} and a + * correct `size` value. Otherwise there may be optimizations in place that will not show + * the change in an editor for example. + */ + readonly onDidChangeFile: Event; + + /** + * Subscribes to file change events in the file or folder denoted by `uri`. For folders, + * the option `recursive` indicates whether subfolders, sub-subfolders, etc. should + * be watched for file changes as well. With `recursive: false`, only changes to the + * files that are direct children of the folder should trigger an event. + * + * The `excludes` array is used to indicate paths that should be excluded from file + * watching. It is typically derived from the `files.watcherExclude` setting that + * is configurable by the user. Each entry can be be: + * - the absolute path to exclude + * - a relative path to exclude (for example `build/output`) + * - a simple glob pattern (for example `**​/build`, `output/**`) + * + * It is the file system provider's job to call {@linkcode FileSystemProvider.onDidChangeFile onDidChangeFile} + * for every change given these rules. No event should be emitted for files that match any of the provided + * excludes. + * + * @param uri The uri of the file or folder to be watched. + * @param options Configures the watch. + * @returns A disposable that tells the provider to stop watching the `uri`. + */ + watch(uri: Uri, options: { readonly recursive: boolean; readonly excludes: readonly string[] }): Disposable; + + /** + * Retrieve metadata about a file. + * + * Note that the metadata for symbolic links should be the metadata of the file they refer to. + * Still, the {@link FileType.SymbolicLink SymbolicLink}-type must be used in addition to the actual type, e.g. + * `FileType.SymbolicLink | FileType.Directory`. + * + * @param uri The uri of the file to retrieve metadata about. + * @return The file metadata about the file. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + */ + stat(uri: Uri): FileStat | Thenable; + + /** + * Retrieve all entries of a {@link FileType.Directory directory}. + * + * @param uri The uri of the folder. + * @return An array of name/type-tuples or a thenable that resolves to such. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + */ + readDirectory(uri: Uri): [string, FileType][] | Thenable<[string, FileType][]>; + + /** + * Create a new directory (Note, that new files are created via `write`-calls). + * + * @param uri The uri of the new folder. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when the parent of `uri` doesn't exist, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + createDirectory(uri: Uri): void | Thenable; + + /** + * Read the entire contents of a file. + * + * @param uri The uri of the file. + * @return An array of bytes or a thenable that resolves to such. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + */ + readFile(uri: Uri): Uint8Array | Thenable; + + /** + * Write data to a file, replacing its entire contents. + * + * @param uri The uri of the file. + * @param content The new content of the file. + * @param options Defines if missing files should or must be created. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist and `create` is not set. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when the parent of `uri` doesn't exist and `create` is set, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists, `create` is set but `overwrite` is not set. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + writeFile(uri: Uri, content: Uint8Array, options: { readonly create: boolean; readonly overwrite: boolean }): void | Thenable; + + /** + * Delete a file. + * + * @param uri The resource that is to be deleted. + * @param options Defines if deletion of folders is recursive. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + delete(uri: Uri, options: { readonly recursive: boolean }): void | Thenable; + + /** + * Rename a file or folder. + * + * @param oldUri The existing file. + * @param newUri The new location. + * @param options Defines if existing files should be overwritten. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `oldUri` doesn't exist. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when parent of `newUri` doesn't exist, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `newUri` exists and when the `overwrite` option is not `true`. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + rename(oldUri: Uri, newUri: Uri, options: { readonly overwrite: boolean }): void | Thenable; + + /** + * Copy files or folders. Implementing this function is optional but it will speedup + * the copy operation. + * + * @param source The existing file. + * @param destination The destination location. + * @param options Defines if existing files should be overwritten. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `source` doesn't exist. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when parent of `destination` doesn't exist, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `destination` exists and when the `overwrite` option is not `true`. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + copy?(source: Uri, destination: Uri, options: { readonly overwrite: boolean }): void | Thenable; + } + + /** + * The file system interface exposes the editor's built-in and contributed + * {@link FileSystemProvider file system providers}. It allows extensions to work + * with files from the local disk as well as files from remote places, like the + * remote extension host or ftp-servers. + * + * *Note* that an instance of this interface is available as {@linkcode workspace.fs}. + */ + export interface FileSystem { + + /** + * Retrieve metadata about a file. + * + * @param uri The uri of the file to retrieve metadata about. + * @return The file metadata about the file. + */ + stat(uri: Uri): Thenable; + + /** + * Retrieve all entries of a {@link FileType.Directory directory}. + * + * @param uri The uri of the folder. + * @return An array of name/type-tuples or a thenable that resolves to such. + */ + readDirectory(uri: Uri): Thenable<[string, FileType][]>; + + /** + * Create a new directory (Note, that new files are created via `write`-calls). + * + * *Note* that missing directories are created automatically, e.g this call has + * `mkdirp` semantics. + * + * @param uri The uri of the new folder. + */ + createDirectory(uri: Uri): Thenable; + + /** + * Read the entire contents of a file. + * + * @param uri The uri of the file. + * @return An array of bytes or a thenable that resolves to such. + */ + readFile(uri: Uri): Thenable; + + /** + * Write data to a file, replacing its entire contents. + * + * @param uri The uri of the file. + * @param content The new content of the file. + */ + writeFile(uri: Uri, content: Uint8Array): Thenable; + + /** + * Delete a file. + * + * @param uri The resource that is to be deleted. + * @param options Defines if trash can should be used and if deletion of folders is recursive + */ + delete(uri: Uri, options?: { recursive?: boolean; useTrash?: boolean }): Thenable; + + /** + * Rename a file or folder. + * + * @param source The existing file. + * @param target The new location. + * @param options Defines if existing files should be overwritten. + */ + rename(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; + + /** + * Copy files or folders. + * + * @param source The existing file. + * @param target The destination location. + * @param options Defines if existing files should be overwritten. + */ + copy(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; + + /** + * Check if a given file system supports writing files. + * + * Keep in mind that just because a file system supports writing, that does + * not mean that writes will always succeed. There may be permissions issues + * or other errors that prevent writing a file. + * + * @param scheme The scheme of the filesystem, for example `file` or `git`. + * + * @return `true` if the file system supports writing, `false` if it does not + * support writing (i.e. it is readonly), and `undefined` if the editor does not + * know about the filesystem. + */ + isWritableFileSystem(scheme: string): boolean | undefined; + } + + /** + * Defines a port mapping used for localhost inside the webview. + */ + export interface WebviewPortMapping { + /** + * Localhost port to remap inside the webview. + */ + readonly webviewPort: number; + + /** + * Destination port. The `webviewPort` is resolved to this port. + */ + readonly extensionHostPort: number; + } + + /** + * Content settings for a webview. + */ + export interface WebviewOptions { + /** + * Controls whether scripts are enabled in the webview content or not. + * + * Defaults to false (scripts-disabled). + */ + readonly enableScripts?: boolean; + + /** + * Controls whether forms are enabled in the webview content or not. + * + * Defaults to true if {@link WebviewOptions.enableScripts scripts are enabled}. Otherwise defaults to false. + * Explicitly setting this property to either true or false overrides the default. + */ + readonly enableForms?: boolean; + + /** + * Controls whether command uris are enabled in webview content or not. + * + * Defaults to `false` (command uris are disabled). + * + * If you pass in an array, only the commands in the array are allowed. + */ + readonly enableCommandUris?: boolean | readonly string[]; + + /** + * Root paths from which the webview can load local (filesystem) resources using uris from `asWebviewUri` + * + * Default to the root folders of the current workspace plus the extension's install directory. + * + * Pass in an empty array to disallow access to any local resources. + */ + readonly localResourceRoots?: readonly Uri[]; + + /** + * Mappings of localhost ports used inside the webview. + * + * Port mapping allow webviews to transparently define how localhost ports are resolved. This can be used + * to allow using a static localhost port inside the webview that is resolved to random port that a service is + * running on. + * + * If a webview accesses localhost content, we recommend that you specify port mappings even if + * the `webviewPort` and `extensionHostPort` ports are the same. + * + * *Note* that port mappings only work for `http` or `https` urls. Websocket urls (e.g. `ws://localhost:3000`) + * cannot be mapped to another port. + */ + readonly portMapping?: readonly WebviewPortMapping[]; + } + + /** + * Displays html content, similarly to an iframe. + */ + export interface Webview { + /** + * Content settings for the webview. + */ + options: WebviewOptions; + + /** + * HTML contents of the webview. + * + * This should be a complete, valid html document. Changing this property causes the webview to be reloaded. + * + * Webviews are sandboxed from normal extension process, so all communication with the webview must use + * message passing. To send a message from the extension to the webview, use {@linkcode Webview.postMessage postMessage}. + * To send message from the webview back to an extension, use the `acquireVsCodeApi` function inside the webview + * to get a handle to the editor's api and then call `.postMessage()`: + * + * ```html + * + * ``` + * + * To load a resources from the workspace inside a webview, use the {@linkcode Webview.asWebviewUri asWebviewUri} method + * and ensure the resource's directory is listed in {@linkcode WebviewOptions.localResourceRoots}. + * + * Keep in mind that even though webviews are sandboxed, they still allow running scripts and loading arbitrary content, + * so extensions must follow all standard web security best practices when working with webviews. This includes + * properly sanitizing all untrusted input (including content from the workspace) and + * setting a [content security policy](https://aka.ms/vscode-api-webview-csp). + */ + html: string; + + /** + * Fired when the webview content posts a message. + * + * Webview content can post strings or json serializable objects back to an extension. They cannot + * post `Blob`, `File`, `ImageData` and other DOM specific objects since the extension that receives the + * message does not run in a browser environment. + */ + readonly onDidReceiveMessage: Event; + + /** + * Post a message to the webview content. + * + * Messages are only delivered if the webview is live (either visible or in the + * background with `retainContextWhenHidden`). + * + * @param message Body of the message. This must be a string or other json serializable object. + * + * For older versions of vscode, if an `ArrayBuffer` is included in `message`, + * it will not be serialized properly and will not be received by the webview. + * Similarly any TypedArrays, such as a `Uint8Array`, will be very inefficiently + * serialized and will also not be recreated as a typed array inside the webview. + * + * However if your extension targets vscode 1.57+ in the `engines` field of its + * `package.json`, any `ArrayBuffer` values that appear in `message` will be more + * efficiently transferred to the webview and will also be correctly recreated inside + * of the webview. + * + * @return A promise that resolves when the message is posted to a webview or when it is + * dropped because the message was not deliverable. + * + * Returns `true` if the message was posted to the webview. Messages can only be posted to + * live webviews (i.e. either visible webviews or hidden webviews that set `retainContextWhenHidden`). + * + * A response of `true` does not mean that the message was actually received by the webview. + * For example, no message listeners may be have been hooked up inside the webview or the webview may + * have been destroyed after the message was posted but before it was received. + * + * If you want confirm that a message as actually received, you can try having your webview posting a + * confirmation message back to your extension. + */ + postMessage(message: any): Thenable; + + /** + * Convert a uri for the local file system to one that can be used inside webviews. + * + * Webviews cannot directly load resources from the workspace or local file system using `file:` uris. The + * `asWebviewUri` function takes a local `file:` uri and converts it into a uri that can be used inside of + * a webview to load the same resource: + * + * ```ts + * webview.html = `` + * ``` + */ + asWebviewUri(localResource: Uri): Uri; + + /** + * Content security policy source for webview resources. + * + * This is the origin that should be used in a content security policy rule: + * + * ```ts + * `img-src https: ${webview.cspSource} ...;` + * ``` + */ + readonly cspSource: string; + } + + /** + * Content settings for a webview panel. + */ + export interface WebviewPanelOptions { + /** + * Controls if the find widget is enabled in the panel. + * + * Defaults to `false`. + */ + readonly enableFindWidget?: boolean; + + /** + * Controls if the webview panel's content (iframe) is kept around even when the panel + * is no longer visible. + * + * Normally the webview panel's html context is created when the panel becomes visible + * and destroyed when it is hidden. Extensions that have complex state + * or UI can set the `retainContextWhenHidden` to make the editor keep the webview + * context around, even when the webview moves to a background tab. When a webview using + * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. + * When the panel becomes visible again, the context is automatically restored + * in the exact same state it was in originally. You cannot send messages to a + * hidden webview, even with `retainContextWhenHidden` enabled. + * + * `retainContextWhenHidden` has a high memory overhead and should only be used if + * your panel's context cannot be quickly saved and restored. + */ + readonly retainContextWhenHidden?: boolean; + } + + /** + * A panel that contains a webview. + */ + interface WebviewPanel { + /** + * Identifies the type of the webview panel, such as `'markdown.preview'`. + */ + readonly viewType: string; + + /** + * Title of the panel shown in UI. + */ + title: string; + + /** + * Icon for the panel shown in UI. + */ + iconPath?: Uri | { readonly light: Uri; readonly dark: Uri }; + + /** + * {@linkcode Webview} belonging to the panel. + */ + readonly webview: Webview; + + /** + * Content settings for the webview panel. + */ + readonly options: WebviewPanelOptions; + + /** + * Editor position of the panel. This property is only set if the webview is in + * one of the editor view columns. + */ + readonly viewColumn: ViewColumn | undefined; + + /** + * Whether the panel is active (focused by the user). + */ + readonly active: boolean; + + /** + * Whether the panel is visible. + */ + readonly visible: boolean; + + /** + * Fired when the panel's view state changes. + */ + readonly onDidChangeViewState: Event; + + /** + * Fired when the panel is disposed. + * + * This may be because the user closed the panel or because `.dispose()` was + * called on it. + * + * Trying to use the panel after it has been disposed throws an exception. + */ + readonly onDidDispose: Event; + + /** + * Show the webview panel in a given column. + * + * A webview panel may only show in a single column at a time. If it is already showing, this + * method moves it to a new column. + * + * @param viewColumn View column to show the panel in. Shows in the current `viewColumn` if undefined. + * @param preserveFocus When `true`, the webview will not take focus. + */ + reveal(viewColumn?: ViewColumn, preserveFocus?: boolean): void; + + /** + * Dispose of the webview panel. + * + * This closes the panel if it showing and disposes of the resources owned by the webview. + * Webview panels are also disposed when the user closes the webview panel. Both cases + * fire the `onDispose` event. + */ + dispose(): any; + } + + /** + * Event fired when a webview panel's view state changes. + */ + export interface WebviewPanelOnDidChangeViewStateEvent { + /** + * Webview panel whose view state changed. + */ + readonly webviewPanel: WebviewPanel; + } + + /** + * Restore webview panels that have been persisted when vscode shuts down. + * + * There are two types of webview persistence: + * + * - Persistence within a session. + * - Persistence across sessions (across restarts of the editor). + * + * A `WebviewPanelSerializer` is only required for the second case: persisting a webview across sessions. + * + * Persistence within a session allows a webview to save its state when it becomes hidden + * and restore its content from this state when it becomes visible again. It is powered entirely + * by the webview content itself. To save off a persisted state, call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()` + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * A `WebviewPanelSerializer` extends this persistence across restarts of the editor. When the editor is shutdown, + * it will save off the state from `setState` of all webviews that have a serializer. When the + * webview first becomes visible after the restart, this state is passed to `deserializeWebviewPanel`. + * The extension can then restore the old `WebviewPanel` from this state. + * + * @param T Type of the webview's state. + */ + interface WebviewPanelSerializer { + /** + * Restore a webview panel from its serialized `state`. + * + * Called when a serialized webview first becomes visible. + * + * @param webviewPanel Webview panel to restore. The serializer should take ownership of this panel. The + * serializer must restore the webview's `.html` and hook up all webview events. + * @param state Persisted state from the webview content. + * + * @return Thenable indicating that the webview has been fully restored. + */ + deserializeWebviewPanel(webviewPanel: WebviewPanel, state: T): Thenable; + } + + /** + * A webview based view. + */ + export interface WebviewView { + /** + * Identifies the type of the webview view, such as `'hexEditor.dataView'`. + */ + readonly viewType: string; + + /** + * The underlying webview for the view. + */ + readonly webview: Webview; + + /** + * View title displayed in the UI. + * + * The view title is initially taken from the extension `package.json` contribution. + */ + title?: string; + + /** + * Human-readable string which is rendered less prominently in the title. + */ + description?: string; + + /** + * The badge to display for this webview view. + * To remove the badge, set to undefined. + */ + badge?: ViewBadge | undefined; + + /** + * Event fired when the view is disposed. + * + * Views are disposed when they are explicitly hidden by a user (this happens when a user + * right clicks in a view and unchecks the webview view). + * + * Trying to use the view after it has been disposed throws an exception. + */ + readonly onDidDispose: Event; + + /** + * Tracks if the webview is currently visible. + * + * Views are visible when they are on the screen and expanded. + */ + readonly visible: boolean; + + /** + * Event fired when the visibility of the view changes. + * + * Actions that trigger a visibility change: + * + * - The view is collapsed or expanded. + * - The user switches to a different view group in the sidebar or panel. + * + * Note that hiding a view using the context menu instead disposes of the view and fires `onDidDispose`. + */ + readonly onDidChangeVisibility: Event; + + /** + * Reveal the view in the UI. + * + * If the view is collapsed, this will expand it. + * + * @param preserveFocus When `true` the view will not take focus. + */ + show(preserveFocus?: boolean): void; + } + + /** + * Additional information the webview view being resolved. + * + * @param T Type of the webview's state. + */ + interface WebviewViewResolveContext { + /** + * Persisted state from the webview content. + * + * To save resources, the editor normally deallocates webview documents (the iframe content) that are not visible. + * For example, when the user collapse a view or switches to another top level activity in the sidebar, the + * `WebviewView` itself is kept alive but the webview's underlying document is deallocated. It is recreated when + * the view becomes visible again. + * + * You can prevent this behavior by setting `retainContextWhenHidden` in the `WebviewOptions`. However this + * increases resource usage and should be avoided wherever possible. Instead, you can use persisted state to + * save off a webview's state so that it can be quickly recreated as needed. + * + * To save off a persisted state, inside the webview call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()`. For example: + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * The editor ensures that the persisted state is saved correctly when a webview is hidden and across + * editor restarts. + */ + readonly state: T | undefined; + } + + /** + * Provider for creating `WebviewView` elements. + */ + export interface WebviewViewProvider { + /** + * Revolves a webview view. + * + * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is + * first loaded or when the user hides and then shows a view again. + * + * @param webviewView Webview view to restore. The provider should take ownership of this view. The + * provider must set the webview's `.html` and hook up all webview events it is interested in. + * @param context Additional metadata about the view being resolved. + * @param token Cancellation token indicating that the view being provided is no longer needed. + * + * @return Optional thenable indicating that the view has been fully resolved. + */ + resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Thenable | void; + } + + /** + * Provider for text based custom editors. + * + * Text based custom editors use a {@linkcode TextDocument} as their data model. This considerably simplifies + * implementing a custom editor as it allows the editor to handle many common operations such as + * undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`. + */ + export interface CustomTextEditorProvider { + + /** + * Resolve a custom editor for a given text resource. + * + * This is called when a user first opens a resource for a `CustomTextEditorProvider`, or if they reopen an + * existing editor using this `CustomTextEditorProvider`. + * + * + * @param document Document for the resource to resolve. + * + * @param webviewPanel The webview panel used to display the editor UI for this resource. + * + * During resolve, the provider must fill in the initial html for the content webview panel and hook up all + * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to + * use later for example in a command. See {@linkcode WebviewPanel} for additional details. + * + * @param token A cancellation token that indicates the result is no longer needed. + * + * @return Thenable indicating that the custom editor has been resolved. + */ + resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; + } + + /** + * Represents a custom document used by a {@linkcode CustomEditorProvider}. + * + * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is + * managed by the editor. 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 the editor 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 the editor that an edit has occurred on an {@linkcode CustomDocument}. + * + * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. + */ + interface CustomDocumentEditEvent { + + /** + * The document that the edit is for. + */ + readonly document: T; + + /** + * Undo the edit operation. + * + * This is invoked by the editor 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 the editor's internal edit stack by `onDidChangeCustomDocument`. + */ + undo(): Thenable | void; + + /** + * Redo the edit operation. + * + * This is invoked by the editor 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 the editor's internal edit stack by `onDidChangeCustomDocument`. + */ + redo(): Thenable | void; + + /** + * Display name describing the edit. + * + * This will be shown to users in the UI for undo/redo operations. + */ + readonly label?: string; + } + + /** + * Event triggered by extensions to signal to the editor that the content of a {@linkcode CustomDocument} + * has changed. + * + * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. + */ + interface CustomDocumentContentChangeEvent { + /** + * The document that the change is for. + */ + readonly document: T; + } + + /** + * A backup for an {@linkcode 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 the editor 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 {@linkcode 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. + * + * If the editor is for a resource from the current workspace, `destination` will point to a file inside + * `ExtensionContext.storagePath`. The parent folder of `destination` may not exist, so make sure to created it + * before writing the backup to this location. + */ + 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 + * from the user's workspace. + */ + readonly backupId: string | undefined; + + /** + * If the URI is an untitled file, this will be populated with the byte data of that file + * + * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in + */ + readonly untitledDocumentData: Uint8Array | undefined; + } + + /** + * Provider for readonly custom editors that use a custom document model. + * + * Custom editors use {@linkcode CustomDocument} as their document model instead of a {@linkcode TextDocument}. + * + * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple + * text based documents, use {@linkcode 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 time an editor for a given resource is opened. The opened + * document is then passed to `resolveCustomEditor` so that the editor can be shown to the user. + * + * Already opened `CustomDocument` are re-used if the user opened additional editors. When 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`. + * + * @param document Document for the resource being resolved. + * + * @param webviewPanel The webview panel used to display the editor UI for this resource. + * + * During resolve, the provider must fill in the initial html for the content webview panel and hook up all + * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to + * use later for example in a command. See {@linkcode WebviewPanel} for additional details. + * + * @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 editable custom editors that use a custom document model. + * + * Custom editors use {@linkcode CustomDocument} as their document model instead of a {@linkcode 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 {@linkcode 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 the editors to be marked 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 the editor's standard keyboard shortcuts. The editor 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 the editor'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 the editor 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 the editor 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 the editor when the user triggers `File: Revert File` in a custom editor. (Note that + * this is only used using the editor'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 the editor 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 approximately one second after the user stops editing the document. If the user + * rapidly edits the document, `backup` will not be invoked until the editing stops. + * + * `backup` is not invoked when `auto save` is enabled (since auto save already persists the 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 the editor has some valid backup. + */ + backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable; + } + + /** + * The clipboard provides read and write access to the system's clipboard. + */ + export interface Clipboard { + + /** + * Read the current clipboard contents as text. + * @returns A thenable that resolves to a string. + */ + readText(): Thenable; + + /** + * Writes text into the clipboard. + * @returns A thenable that resolves when writing happened. + */ + writeText(value: string): Thenable; + } + + /** + * Possible kinds of UI that can use extensions. + */ + export enum UIKind { + + /** + * Extensions are accessed from a desktop application. + */ + Desktop = 1, + + /** + * Extensions are accessed from a web browser. + */ + Web = 2 + } + + /** + * Namespace describing the environment the editor runs in. + */ + export namespace env { + + /** + * The application name of the editor, like 'VS Code'. + */ + export const appName: string; + + /** + * The application root folder from which the editor is running. + * + * *Note* that the value is the empty string when running in an + * environment that has no representation of an application root folder. + */ + export const appRoot: string; + + /** + * The hosted location of the application + * On desktop this is 'desktop' + * In the web this is the specified embedder i.e. 'github.dev', 'codespaces', or 'web' if the embedder + * does not provide that information + */ + export const appHost: string; + + /** + * The custom uri scheme the editor registers to in the operating system. + */ + export const uriScheme: string; + + /** + * Represents the preferred user-language, like `de-CH`, `fr`, or `en-US`. + */ + export const language: string; + + /** + * The system clipboard. + */ + export const clipboard: Clipboard; + + /** + * A unique identifier for the computer. + */ + export const machineId: string; + + /** + * A unique identifier for the current session. + * Changes each time the editor is started. + */ + export const sessionId: string; + + /** + * Indicates that this is a fresh install of the application. + * `true` if within the first day of installation otherwise `false`. + */ + export const isNewAppInstall: boolean; + + /** + * Indicates whether the users has telemetry enabled. + * Can be observed to determine if the extension should send telemetry. + */ + export const isTelemetryEnabled: boolean; + + /** + * An {@link Event} which fires when the user enabled or disables telemetry. + * `true` if the user has enabled telemetry or `false` if the user has disabled telemetry. + */ + export const onDidChangeTelemetryEnabled: Event; + + /** + * The name of a remote. Defined by extensions, popular samples are `wsl` for the Windows + * Subsystem for Linux or `ssh-remote` for remotes using a secure shell. + * + * *Note* that the value is `undefined` when there is no remote extension host but that the + * value is defined in all extension hosts (local and remote) in case a remote extension host + * exists. Use {@link Extension.extensionKind} to know if + * a specific extension runs remote or not. + */ + export const remoteName: string | undefined; + + /** + * The detected default shell for the extension host, this is overridden by the + * `terminal.integrated.defaultProfile` setting for the extension host's platform. Note that in + * environments that do not support a shell the value is the empty string. + */ + export const shell: string; + + /** + * The UI kind property indicates from which UI extensions + * are accessed from. For example, extensions could be accessed + * from a desktop application or a web browser. + */ + export const uiKind: UIKind; + + /** + * Opens a link externally using the default application. Depending on the + * used scheme this can be: + * * a browser (`http:`, `https:`) + * * a mail client (`mailto:`) + * * VSCode itself (`vscode:` from `vscode.env.uriScheme`) + * + * *Note* that {@linkcode window.showTextDocument showTextDocument} is the right + * way to open a text document inside the editor, not this function. + * + * @param target The uri that should be opened. + * @returns A promise indicating if open was successful. + */ + export function openExternal(target: Uri): Thenable; + + /** + * Resolves a uri to a form that is accessible externally. + * + * #### `http:` or `https:` scheme + * + * Resolves an *external* uri, such as a `http:` or `https:` link, from where the extension is running to a + * uri to the same resource on the client machine. + * + * This is a no-op if the extension is running on the client machine. + * + * If the extension is running remotely, this function automatically establishes a port forwarding tunnel + * from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of + * the port forwarding tunnel is managed by the editor and the tunnel can be closed by the user. + * + * *Note* that uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` on them. + * + * #### `vscode.env.uriScheme` + * + * Creates a uri that - if opened in a browser (e.g. via `openExternal`) - will result in a registered {@link UriHandler} + * to trigger. + * + * Extensions should not make any assumptions about the resulting uri and should not alter it in any way. + * Rather, extensions can e.g. use this uri in an authentication flow, by adding the uri as callback query + * argument to the server to authenticate to. + * + * *Note* that if the server decides to add additional query parameters to the uri (e.g. a token or secret), it + * will appear in the uri that is passed to the {@link UriHandler}. + * + * **Example** of an authentication flow: + * ```typescript + * vscode.window.registerUriHandler({ + * handleUri(uri: vscode.Uri): vscode.ProviderResult { + * if (uri.path === '/did-authenticate') { + * console.log(uri.toString()); + * } + * } + * }); + * + * const callableUri = await vscode.env.asExternalUri(vscode.Uri.parse(vscode.env.uriScheme + '://my.extension/did-authenticate')); + * await vscode.env.openExternal(callableUri); + * ``` + * + * *Note* that extensions should not cache the result of `asExternalUri` as the resolved uri may become invalid due to + * a system or user action — for example, in remote cases, a user may close a port forwarding tunnel that was opened by + * `asExternalUri`. + * + * #### Any other scheme + * + * Any other scheme will be handled as if the provided URI is a workspace URI. In that case, the method will return + * a URI which, when handled, will make the editor open the workspace. + * + * @return A uri that can be used on the client machine. + */ + export function asExternalUri(target: Uri): Thenable; + } + + /** + * Namespace for dealing with commands. In short, a command is a function with a + * unique identifier. The function is sometimes also called _command handler_. + * + * Commands can be added to the editor using the {@link commands.registerCommand registerCommand} + * and {@link commands.registerTextEditorCommand registerTextEditorCommand} functions. Commands + * can be executed {@link commands.executeCommand manually} or from a UI gesture. Those are: + * + * * palette - Use the `commands`-section in `package.json` to make a command show in + * the [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette). + * * keybinding - Use the `keybindings`-section in `package.json` to enable + * [keybindings](https://code.visualstudio.com/docs/getstarted/keybindings#_customizing-shortcuts) + * for your extension. + * + * Commands from other extensions and from the editor itself are accessible to an extension. However, + * when invoking an editor command not all argument types are supported. + * + * This is a sample that registers a command handler and adds an entry for that command to the palette. First + * register a command handler with the identifier `extension.sayHello`. + * ```javascript + * commands.registerCommand('extension.sayHello', () => { + * window.showInformationMessage('Hello World!'); + * }); + * ``` + * Second, bind the command identifier to a title under which it will show in the palette (`package.json`). + * ```json + * { + * "contributes": { + * "commands": [{ + * "command": "extension.sayHello", + * "title": "Hello World" + * }] + * } + * } + * ``` + */ + export namespace commands { + + /** + * Registers a command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Registering a command with an existing command identifier twice + * will cause an error. + * + * @param command A unique identifier for the command. + * @param callback A command handler function. + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this command on disposal. + */ + export function registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): Disposable; + + /** + * Registers a text editor command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Text editor commands are different from ordinary {@link commands.registerCommand commands} as + * they only execute when there is an active editor when the command is called. Also, the + * command handler of an editor command has access to the active editor and to an + * {@link TextEditorEdit edit}-builder. Note that the edit-builder is only valid while the + * callback executes. + * + * @param command A unique identifier for the command. + * @param callback A command handler function with access to an {@link TextEditor editor} and an {@link TextEditorEdit edit}. + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this command on disposal. + */ + export function registerTextEditorCommand(command: string, callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, thisArg?: any): Disposable; + + /** + * Executes the command denoted by the given command identifier. + * + * * *Note 1:* When executing an editor command not all types are allowed to + * be passed as arguments. Allowed are the primitive types `string`, `boolean`, + * `number`, `undefined`, and `null`, as well as {@linkcode Position}, {@linkcode Range}, {@linkcode Uri} and {@linkcode Location}. + * * *Note 2:* There are no restrictions when executing commands that have been contributed + * by extensions. + * + * @param command Identifier of the command to execute. + * @param rest Parameters passed to the command function. + * @return A thenable that resolves to the returned value of the given command. Returns `undefined` when + * the command handler function doesn't return anything. + */ + export function executeCommand(command: string, ...rest: any[]): Thenable; + + /** + * Retrieve the list of all available commands. Commands starting with an underscore are + * treated as internal commands. + * + * @param filterInternal Set `true` to not see internal commands (starting with an underscore) + * @return Thenable that resolves to a list of command ids. + */ + export function getCommands(filterInternal?: boolean): Thenable; + } + + /** + * Represents the state of a window. + */ + export interface WindowState { + + /** + * Whether the current window is focused. + */ + readonly focused: boolean; + } + + /** + * A uri handler is responsible for handling system-wide {@link Uri uris}. + * + * @see {@link window.registerUriHandler}. + */ + export interface UriHandler { + + /** + * Handle the provided system-wide {@link Uri}. + * + * @see {@link window.registerUriHandler}. + */ + handleUri(uri: Uri): ProviderResult; + } + + /** + * Namespace for dealing with the current window of the editor. That is visible + * and active editors, as well as, UI elements to show messages, selections, and + * asking for user input. + */ + export namespace window { + + /** + * Represents the grid widget within the main editor area + */ + export const tabGroups: TabGroups; + + /** + * The currently active editor or `undefined`. The active editor is the one + * that currently has focus or, when none has focus, the one that has changed + * input most recently. + */ + export let activeTextEditor: TextEditor | undefined; + + /** + * The currently visible editors or an empty array. + */ + export let visibleTextEditors: readonly TextEditor[]; + + /** + * An {@link Event} which fires when the {@link window.activeTextEditor active editor} + * has changed. *Note* that the event also fires when the active editor changes + * to `undefined`. + */ + export const onDidChangeActiveTextEditor: Event; + + /** + * An {@link Event} which fires when the array of {@link window.visibleTextEditors visible editors} + * has changed. + */ + export const onDidChangeVisibleTextEditors: Event; + + /** + * An {@link Event} which fires when the selection in an editor has changed. + */ + export const onDidChangeTextEditorSelection: Event; + + /** + * An {@link Event} which fires when the visible ranges of an editor has changed. + */ + export const onDidChangeTextEditorVisibleRanges: Event; + + /** + * An {@link Event} which fires when the options of an editor have changed. + */ + export const onDidChangeTextEditorOptions: Event; + + /** + * An {@link Event} which fires when the view column of an editor has changed. + */ + export const onDidChangeTextEditorViewColumn: Event; + + /** + * The currently visible {@link NotebookEditor notebook editors} or an empty array. + */ + export const visibleNotebookEditors: readonly NotebookEditor[]; + + /** + * An {@link Event} which fires when the {@link window.visibleNotebookEditors visible notebook editors} + * has changed. + */ + export const onDidChangeVisibleNotebookEditors: Event; + + /** + * The currently active {@link NotebookEditor notebook editor} or `undefined`. The active editor is the one + * that currently has focus or, when none has focus, the one that has changed + * input most recently. + */ + export const activeNotebookEditor: NotebookEditor | undefined; + + /** + * An {@link Event} which fires when the {@link window.activeNotebookEditor active notebook editor} + * has changed. *Note* that the event also fires when the active editor changes + * to `undefined`. + */ + export const onDidChangeActiveNotebookEditor: Event; + + /** + * An {@link Event} which fires when the {@link NotebookEditor.selections notebook editor selections} + * have changed. + */ + export const onDidChangeNotebookEditorSelection: Event; + + /** + * An {@link Event} which fires when the {@link NotebookEditor.visibleRanges notebook editor visible ranges} + * have changed. + */ + export const onDidChangeNotebookEditorVisibleRanges: Event; + + /** + * The currently opened terminals or an empty array. + */ + export const terminals: readonly Terminal[]; + + /** + * The currently active terminal or `undefined`. The active terminal is the one that + * currently has focus or most recently had focus. + */ + export const activeTerminal: Terminal | undefined; + + /** + * An {@link Event} which fires when the {@link window.activeTerminal active terminal} + * has changed. *Note* that the event also fires when the active terminal changes + * to `undefined`. + */ + export const onDidChangeActiveTerminal: Event; + + /** + * An {@link Event} which fires when a terminal has been created, either through the + * {@link window.createTerminal createTerminal} API or commands. + */ + export const onDidOpenTerminal: Event; + + /** + * An {@link Event} which fires when a terminal is disposed. + */ + export const onDidCloseTerminal: Event; + + /** + * An {@link Event} which fires when a {@link Terminal.state terminal's state} has changed. + */ + export const onDidChangeTerminalState: Event; + + /** + * Represents the current window's state. + */ + export const state: WindowState; + + /** + * An {@link Event} which fires when the focus state of the current window + * changes. The value of the event represents whether the window is focused. + */ + export const onDidChangeWindowState: Event; + + /** + * Show the given document in a text editor. A {@link ViewColumn column} can be provided + * to control where the editor is being shown. Might change the {@link window.activeTextEditor active editor}. + * + * @param document A text document to be shown. + * @param column A view column in which the {@link TextEditor editor} should be shown. The default is the {@link ViewColumn.Active active}. + * Columns that do not exist will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. Use {@linkcode ViewColumn.Beside} + * to open the editor to the side of the currently active one. + * @param preserveFocus When `true` the editor will not take focus. + * @return A promise that resolves to an {@link TextEditor editor}. + */ + export function showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; + + /** + * Show the given document in a text editor. {@link TextDocumentShowOptions Options} can be provided + * to control options of the editor is being shown. Might change the {@link window.activeTextEditor active editor}. + * + * @param document A text document to be shown. + * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. + * @return A promise that resolves to an {@link TextEditor editor}. + */ + export function showTextDocument(document: TextDocument, options?: TextDocumentShowOptions): Thenable; + + /** + * A short-hand for `openTextDocument(uri).then(document => showTextDocument(document, options))`. + * + * @see {@link workspace.openTextDocument} + * + * @param uri A resource identifier. + * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. + * @return A promise that resolves to an {@link TextEditor editor}. + */ + export function showTextDocument(uri: Uri, options?: TextDocumentShowOptions): Thenable; + + /** + * Show the given {@link NotebookDocument} in a {@link NotebookEditor notebook editor}. + * + * @param document A text document to be shown. + * @param options {@link NotebookDocumentShowOptions Editor options} to configure the behavior of showing the {@link NotebookEditor notebook editor}. + * + * @return A promise that resolves to an {@link NotebookEditor notebook editor}. + */ + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; + + /** + * Create a TextEditorDecorationType that can be used to add decorations to text editors. + * + * @param options Rendering options for the decoration type. + * @return A new decoration type instance. + */ + export function createTextEditorDecorationType(options: DecorationRenderOptions): TextEditorDecorationType; + + /** + * Show an information message to users. Optionally provide an array of items which will be presented as + * clickable buttons. + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an information message to users. Optionally provide an array of items which will be presented as + * clickable buttons. + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show an information message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an information message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Shows a selection list allowing multiple selections. + * + * @param items An array of strings, or a promise that resolves to an array of strings. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @return A promise that resolves to the selected items or `undefined`. + */ + export function showQuickPick(items: readonly string[] | Thenable, options: QuickPickOptions & { canPickMany: true }, token?: CancellationToken): Thenable; + + /** + * Shows a selection list. + * + * @param items An array of strings, or a promise that resolves to an array of strings. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @return A promise that resolves to the selection or `undefined`. + */ + export function showQuickPick(items: readonly string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + + /** + * Shows a selection list allowing multiple selections. + * + * @param items An array of items, or a promise that resolves to an array of items. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @return A promise that resolves to the selected items or `undefined`. + */ + export function showQuickPick(items: readonly T[] | Thenable, options: QuickPickOptions & { canPickMany: true }, token?: CancellationToken): Thenable; + + /** + * Shows a selection list. + * + * @param items An array of items, or a promise that resolves to an array of items. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @return A promise that resolves to the selected item or `undefined`. + */ + export function showQuickPick(items: readonly T[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + + /** + * Shows a selection list of {@link workspace.workspaceFolders workspace folders} to pick from. + * Returns `undefined` if no folder is open. + * + * @param options Configures the behavior of the workspace folder list. + * @return A promise that resolves to the workspace folder or `undefined`. + */ + export function showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable; + + /** + * Shows a file open dialog to the user which allows to select a file + * for opening-purposes. + * + * @param options Options that control the dialog. + * @returns A promise that resolves to the selected resources or `undefined`. + */ + export function showOpenDialog(options?: OpenDialogOptions): Thenable; + + /** + * Shows a file save dialog to the user which allows to select a file + * for saving-purposes. + * + * @param options Options that control the dialog. + * @returns A promise that resolves to the selected resource or `undefined`. + */ + export function showSaveDialog(options?: SaveDialogOptions): Thenable; + + /** + * Opens an input box to ask the user for input. + * + * The returned value will be `undefined` if the input box was canceled (e.g. pressing ESC). Otherwise the + * returned value will be the string typed by the user or an empty string if the user did not type + * anything but dismissed the input box with OK. + * + * @param options Configures the behavior of the input box. + * @param token A token that can be used to signal cancellation. + * @return A promise that resolves to a string the user provided or to `undefined` in case of dismissal. + */ + export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; + + /** + * Creates a {@link QuickPick} to let the user pick an item from a list + * of items of type T. + * + * Note that in many cases the more convenient {@link window.showQuickPick} + * is easier to use. {@link window.createQuickPick} should be used + * when {@link window.showQuickPick} does not offer the required flexibility. + * + * @return A new {@link QuickPick}. + */ + export function createQuickPick(): QuickPick; + + /** + * Creates a {@link InputBox} to let the user enter some text input. + * + * Note that in many cases the more convenient {@link window.showInputBox} + * is easier to use. {@link window.createInputBox} should be used + * when {@link window.showInputBox} does not offer the required flexibility. + * + * @return A new {@link InputBox}. + */ + export function createInputBox(): InputBox; + + /** + * Creates a new {@link OutputChannel output channel} with the given name and language id + * If language id is not provided, then **Log** is used as default language id. + * + * You can access the visible or active output channel as a {@link TextDocument text document} from {@link window.visibleTextEditors visible editors} or {@link window.activeTextEditor active editor} + * and use the language id to contribute language features like syntax coloring, code lens etc., + * + * @param name Human-readable string which will be used to represent the channel in the UI. + * @param languageId The identifier of the language associated with the channel. + */ + export function createOutputChannel(name: string, languageId?: string): OutputChannel; + + /** + * Create and show a new webview panel. + * + * @param viewType Identifies the type of the webview panel. + * @param title Title of the panel. + * @param showOptions Where to show the webview in the editor. If preserveFocus is set, the new webview will not take focus. + * @param options Settings for the new panel. + * + * @return New webview panel. + */ + export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { readonly viewColumn: ViewColumn; readonly preserveFocus?: boolean }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; + + /** + * Set a message to the status bar. This is a short hand for the more powerful + * status bar {@link window.createStatusBarItem items}. + * + * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. + * @param hideAfterTimeout Timeout in milliseconds after which the message will be disposed. + * @return A disposable which hides the status bar message. + */ + export function setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; + + /** + * Set a message to the status bar. This is a short hand for the more powerful + * status bar {@link window.createStatusBarItem items}. + * + * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. + * @param hideWhenDone Thenable on which completion (resolve or reject) the message will be disposed. + * @return A disposable which hides the status bar message. + */ + export function setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; + + /** + * Set a message to the status bar. This is a short hand for the more powerful + * status bar {@link window.createStatusBarItem items}. + * + * *Note* that status bar messages stack and that they must be disposed when no + * longer used. + * + * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. + * @return A disposable which hides the status bar message. + */ + export function setStatusBarMessage(text: string): Disposable; + + /** + * Show progress in the Source Control viewlet while running the given callback and while + * its returned promise isn't resolve or rejected. + * + * @deprecated Use `withProgress` instead. + * + * @param task A callback returning a promise. Progress increments can be reported with + * the provided {@link Progress}-object. + * @return The thenable the task did return. + */ + export function withScmProgress(task: (progress: Progress) => Thenable): Thenable; + + /** + * Show progress in the editor. Progress is shown while running the given callback + * and while the promise it returned isn't resolved nor rejected. The location at which + * progress should show (and other details) is defined via the passed {@linkcode ProgressOptions}. + * + * @param task A callback returning a promise. Progress state can be reported with + * the provided {@link Progress}-object. + * + * To report discrete progress, use `increment` to indicate how much work has been completed. Each call with + * a `increment` value will be summed up and reflected as overall progress until 100% is reached (a value of + * e.g. `10` accounts for `10%` of work done). + * Note that currently only `ProgressLocation.Notification` is capable of showing discrete progress. + * + * To monitor if the operation has been cancelled by the user, use the provided {@linkcode CancellationToken}. + * Note that currently only `ProgressLocation.Notification` is supporting to show a cancel button to cancel the + * long running operation. + * + * @return The thenable the task-callback returned. + */ + export function withProgress(options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable): Thenable; + + /** + * Creates a status bar {@link StatusBarItem item}. + * + * @param alignment The alignment of the item. + * @param priority The priority of the item. Higher values mean the item should be shown more to the left. + * @return A new status bar item. + */ + export function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem; + + /** + * Creates a status bar {@link StatusBarItem item}. + * + * @param id The unique identifier of the item. + * @param alignment The alignment of the item. + * @param priority The priority of the item. Higher values mean the item should be shown more to the left. + * @return A new status bar item. + */ + export function createStatusBarItem(id: string, alignment?: StatusBarAlignment, priority?: number): StatusBarItem; + + /** + * Creates a {@link Terminal} with a backing shell process. The cwd of the terminal will be the workspace + * directory if it exists. + * + * @param name Optional human-readable string which will be used to represent the terminal in the UI. + * @param shellPath Optional path to a custom shell executable to be used in the terminal. + * @param shellArgs Optional args for the custom shell executable. A string can be used on Windows only which + * allows specifying shell args in + * [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). + * @return A new Terminal. + * @throws When running in an environment where a new process cannot be started. + */ + export function createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): Terminal; + + /** + * Creates a {@link Terminal} with a backing shell process. + * + * @param options A TerminalOptions object describing the characteristics of the new terminal. + * @return A new Terminal. + * @throws When running in an environment where a new process cannot be started. + */ + export function createTerminal(options: TerminalOptions): Terminal; + + /** + * Creates a {@link Terminal} where an extension controls its input and output. + * + * @param options An {@link ExtensionTerminalOptions} object describing + * the characteristics of the new terminal. + * @return A new Terminal. + */ + export function createTerminal(options: ExtensionTerminalOptions): Terminal; + + /** + * Register a {@link TreeDataProvider} for the view contributed using the extension point `views`. + * This will allow you to contribute data to the {@link TreeView} and update if the data changes. + * + * **Note:** To get access to the {@link TreeView} and perform operations on it, use {@link window.createTreeView createTreeView}. + * + * @param viewId Id of the view contributed using the extension point `views`. + * @param treeDataProvider A {@link TreeDataProvider} that provides tree data for the view + */ + export function registerTreeDataProvider(viewId: string, treeDataProvider: TreeDataProvider): Disposable; + + /** + * Create a {@link TreeView} for the view contributed using the extension point `views`. + * @param viewId Id of the view contributed using the extension point `views`. + * @param options Options for creating the {@link TreeView} + * @returns a {@link TreeView}. + */ + export function createTreeView(viewId: string, options: TreeViewOptions): TreeView; + + /** + * Registers a {@link UriHandler uri handler} capable of handling system-wide {@link Uri uris}. + * In case there are multiple windows open, the topmost window will handle the uri. + * A uri handler is scoped to the extension it is contributed from; it will only + * be able to handle uris which are directed to the extension itself. A uri must respect + * the following rules: + * + * - The uri-scheme must be `vscode.env.uriScheme`; + * - The uri-authority must be the extension id (e.g. `my.extension`); + * - The uri-path, -query and -fragment parts are arbitrary. + * + * For example, if the `my.extension` extension registers a uri handler, it will only + * be allowed to handle uris with the prefix `product-name://my.extension`. + * + * An extension can only register a single uri handler in its entire activation lifetime. + * + * * *Note:* There is an activation event `onUri` that fires when a uri directed for + * the current extension is about to be handled. + * + * @param handler The uri handler to register for this extension. + */ + export function registerUriHandler(handler: UriHandler): Disposable; + + /** + * Registers a webview panel serializer. + * + * Extensions that support reviving should have an `"onWebviewPanel:viewType"` activation event and + * make sure that `registerWebviewPanelSerializer` is called during activation. + * + * Only a single serializer may be registered at a time for a given `viewType`. + * + * @param viewType Type of the webview panel that can be serialized. + * @param serializer Webview serializer. + */ + export function registerWebviewPanelSerializer(viewType: string, serializer: WebviewPanelSerializer): Disposable; + + /** + * Register a new provider for webview views. + * + * @param viewId Unique id of the view. This should match the `id` from the + * `views` contribution in the package.json. + * @param provider Provider for the webview views. + * + * @return Disposable that unregisters the provider. + */ + export function registerWebviewViewProvider(viewId: string, provider: WebviewViewProvider, options?: { + /** + * Content settings for the webview created for this view. + */ + readonly webviewOptions?: { + /** + * Controls if the webview element itself (iframe) is kept around even when the view + * is no longer visible. + * + * Normally the webview's html context is created when the view becomes visible + * and destroyed when it is hidden. Extensions that have complex state + * or UI can set the `retainContextWhenHidden` to make the editor keep the webview + * context around, even when the webview moves to a background tab. When a webview using + * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. + * When the view becomes visible again, the context is automatically restored + * in the exact same state it was in originally. You cannot send messages to a + * hidden webview, even with `retainContextWhenHidden` enabled. + * + * `retainContextWhenHidden` has a high memory overhead and should only be used if + * your view's context cannot be quickly saved and restored. + */ + readonly retainContextWhenHidden?: boolean; + }; + }): Disposable; + + /** + * Register a provider for custom editors for the `viewType` contributed by the `customEditors` extension point. + * + * When a custom editor is opened, an `onCustomEditor:viewType` activation event is fired. Your extension + * must register a {@linkcode CustomTextEditorProvider}, {@linkcode CustomReadonlyEditorProvider}, + * {@linkcode CustomEditorProvider}for `viewType` as part of activation. + * + * @param viewType Unique identifier for the custom editor provider. This should match the `viewType` from the + * `customEditors` contribution point. + * @param provider Provider that resolves custom editors. + * @param options Options for the provider. + * + * @return Disposable that unregisters the provider. + */ + export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider | CustomReadonlyEditorProvider | CustomEditorProvider, options?: { + /** + * Content settings for the webview panels created for this custom editor. + */ + 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. + * + * By default, the editor 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 `supportsMultipleEditorsPerDocument` is enabled, users can split and create copies of the custom + * editor. In this case, 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; + + /** + * Register provider that enables the detection and handling of links within the terminal. + * @param provider The provider that provides the terminal links. + * @return Disposable that unregisters the provider. + */ + export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; + + /** + * Registers a provider for a contributed terminal profile. + * @param id The ID of the contributed terminal profile. + * @param provider The terminal profile provider. + */ + export function registerTerminalProfileProvider(id: string, provider: TerminalProfileProvider): Disposable; + /** + * Register a file decoration provider. + * + * @param provider A {@link FileDecorationProvider}. + * @return A {@link Disposable} that unregisters the provider. + */ + export function registerFileDecorationProvider(provider: FileDecorationProvider): Disposable; + + /** + * The currently active color theme as configured in the settings. The active + * theme can be changed via the `workbench.colorTheme` setting. + */ + export let activeColorTheme: ColorTheme; + + /** + * An {@link Event} which fires when the active color theme is changed or has changes. + */ + export const onDidChangeActiveColorTheme: Event; + } + + /** + * Options for creating a {@link TreeView} + */ + export interface TreeViewOptions { + + /** + * A data provider that provides tree data. + */ + treeDataProvider: TreeDataProvider; + + /** + * Whether to show collapse all action or not. + */ + showCollapseAll?: boolean; + + /** + * Whether the tree supports multi-select. When the tree supports multi-select and a command is executed from the tree, + * the first argument to the command is the tree item that the command was executed on and the second argument is an + * array containing all selected tree items. + */ + canSelectMany?: boolean; + + /** + * An optional interface to implement drag and drop in the tree view. + */ + dragAndDropController?: TreeDragAndDropController; + } + + /** + * The event that is fired when an element in the {@link TreeView} is expanded or collapsed + */ + export interface TreeViewExpansionEvent { + + /** + * Element that is expanded or collapsed. + */ + readonly element: T; + + } + + /** + * The event that is fired when there is a change in {@link TreeView.selection tree view's selection} + */ + export interface TreeViewSelectionChangeEvent { + + /** + * Selected elements. + */ + readonly selection: readonly T[]; + + } + + /** + * The event that is fired when there is a change in {@link TreeView.visible tree view's visibility} + */ + export interface TreeViewVisibilityChangeEvent { + + /** + * `true` if the {@link TreeView tree view} is visible otherwise `false`. + */ + readonly visible: boolean; + } + + /** + * A file associated with a {@linkcode DataTransferItem}. + */ + export interface DataTransferFile { + /** + * The name of the file. + */ + readonly name: string; + + /** + * The full file path of the file. + * + * May be `undefined` on web. + */ + readonly uri?: Uri; + + /** + * The full file contents of the file. + */ + data(): Thenable; + } + + /** + * Encapsulates data transferred during drag and drop operations. + */ + export class DataTransferItem { + /** + * Get a string representation of this item. + * + * If {@linkcode DataTransferItem.value} is an object, this returns the result of json stringifying {@linkcode DataTransferItem.value} value. + */ + asString(): Thenable; + + /** + * Try getting the {@link DataTransferFile file} associated with this data transfer item. + * + * Note that the file object is only valid for the scope of the drag and drop operation. + * + * @returns The file for the data transfer or `undefined` if the item is either not a file or the + * file data cannot be accessed. + */ + asFile(): DataTransferFile | undefined; + + /** + * Custom data stored on this item. + * + * You can use `value` to share data across operations. The original object can be retrieved so long as the extension that + * created the `DataTransferItem` runs in the same extension host. + */ + readonly value: any; + + /** + * @param value Custom data stored on this item. Can be retrieved using {@linkcode DataTransferItem.value}. + */ + constructor(value: any); + } + + /** + * A map containing a mapping of the mime type of the corresponding transferred data. + * + * Drag and drop controllers that implement {@link TreeDragAndDropController.handleDrag `handleDrag`} can add additional mime types to the + * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from + * an element in the same drag and drop controller. + */ + export class DataTransfer implements Iterable<[mimeType: string, item: DataTransferItem]> { + /** + * Retrieves the data transfer item for a given mime type. + * + * @param mimeType The mime type to get the data transfer item for, such as `text/plain` or `image/png`. + * + * Special mime types: + * - `text/uri-list` — A string with `toString()`ed Uris separated by `\r\n`. To specify a cursor position in the file, + * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. + */ + get(mimeType: string): DataTransferItem | undefined; + + /** + * Sets a mime type to data transfer item mapping. + * @param mimeType The mime type to set the data for. + * @param value The data transfer item for the given mime type. + */ + set(mimeType: string, value: DataTransferItem): void; + + /** + * Allows iteration through the data transfer items. + * + * @param callbackfn Callback for iteration through the data transfer items. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callbackfn: (item: DataTransferItem, mimeType: string, dataTransfer: DataTransfer) => void, thisArg?: any): void; + + /** + * Get a new iterator with the `[mime, item]` pairs for each element in this data transfer. + */ + [Symbol.iterator](): IterableIterator<[mimeType: string, item: DataTransferItem]>; + } + + /** + * Provides support for drag and drop in `TreeView`. + */ + export interface TreeDragAndDropController { + + /** + * The mime types that the {@link TreeDragAndDropController.handleDrop `handleDrop`} method of this `DragAndDropController` supports. + * This could be well-defined, existing, mime types, and also mime types defined by the extension. + * + * To support drops from trees, you will need to add the mime type of that tree. + * This includes drops from within the same tree. + * The mime type of a tree is recommended to be of the format `application/vnd.code.tree.`. + * + * Use the special `files` mime type to support all types of dropped files {@link DataTransferFile files}, regardless of the file's actual mime type. + * + * To learn the mime type of a dragged item: + * 1. Set up your `DragAndDropController` + * 2. Use the Developer: Set Log Level... command to set the level to "Debug" + * 3. Open the developer tools and drag the item with unknown mime type over your tree. The mime types will be logged to the developer console + * + * Note that mime types that cannot be sent to the extension will be omitted. + */ + readonly dropMimeTypes: readonly string[]; + + /** + * The mime types that the {@link TreeDragAndDropController.handleDrag `handleDrag`} method of this `TreeDragAndDropController` may add to the tree data transfer. + * This could be well-defined, existing, mime types, and also mime types defined by the extension. + * + * The recommended mime type of the tree (`application/vnd.code.tree.`) will be automatically added. + */ + readonly dragMimeTypes: readonly string[]; + + /** + * When the user starts dragging items from this `DragAndDropController`, `handleDrag` will be called. + * Extensions can use `handleDrag` to add their {@link DataTransferItem `DataTransferItem`} items to the drag and drop. + * + * When the items are dropped on **another tree item** in **the same tree**, your `DataTransferItem` objects + * will be preserved. Use the recommended mime type for the tree (`application/vnd.code.tree.`) to add + * tree objects in a data transfer. See the documentation for `DataTransferItem` for how best to take advantage of this. + * + * To add a data transfer item that can be dragged into the editor, use the application specific mime type "text/uri-list". + * The data for "text/uri-list" should be a string with `toString()`ed Uris separated by newlines. To specify a cursor position in the file, + * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. + * + * @param source The source items for the drag and drop operation. + * @param dataTransfer The data transfer associated with this drag. + * @param token A cancellation token indicating that drag has been cancelled. + */ + handleDrag?(source: readonly T[], dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; + + /** + * Called when a drag and drop action results in a drop on the tree that this `DragAndDropController` belongs to. + * + * Extensions should fire {@link TreeDataProvider.onDidChangeTreeData onDidChangeTreeData} for any elements that need to be refreshed. + * + * @param dataTransfer The data transfer items of the source of the drag. + * @param target The target tree element that the drop is occurring on. When undefined, the target is the root. + * @param token A cancellation token indicating that the drop has been cancelled. + */ + handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; + } + + /** + * A badge presenting a value for a view + */ + export interface ViewBadge { + + /** + * A label to present in tooltip for the badge. + */ + readonly tooltip: string; + + /** + * The value to present in the badge. + */ + readonly value: number; + } + + /** + * Represents a Tree view + */ + export interface TreeView extends Disposable { + + /** + * Event that is fired when an element is expanded + */ + readonly onDidExpandElement: Event>; + + /** + * Event that is fired when an element is collapsed + */ + readonly onDidCollapseElement: Event>; + + /** + * Currently selected elements. + */ + readonly selection: readonly T[]; + + /** + * Event that is fired when the {@link TreeView.selection selection} has changed + */ + readonly onDidChangeSelection: Event>; + + /** + * `true` if the {@link TreeView tree view} is visible otherwise `false`. + */ + readonly visible: boolean; + + /** + * Event that is fired when {@link TreeView.visible visibility} has changed + */ + readonly onDidChangeVisibility: Event; + + /** + * An optional human-readable message that will be rendered in the view. + * Setting the message to null, undefined, or empty string will remove the message from the view. + */ + message?: string; + + /** + * The tree view title is initially taken from the extension package.json + * Changes to the title property will be properly reflected in the UI in the title of the view. + */ + title?: string; + + /** + * An optional human-readable description which is rendered less prominently in the title of the view. + * Setting the title description to null, undefined, or empty string will remove the description from the view. + */ + description?: string; + + /** + * The badge to display for this TreeView. + * To remove the badge, set to undefined. + */ + badge?: ViewBadge | undefined; + + /** + * Reveals the given element in the tree view. + * If the tree view is not visible then the tree view is shown and element is revealed. + * + * By default revealed element is selected. + * In order to not to select, set the option `select` to `false`. + * In order to focus, set the option `focus` to `true`. + * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. + * **NOTE:** You can expand only to 3 levels maximum. + * + * **NOTE:** The {@link TreeDataProvider} that the `TreeView` {@link window.createTreeView is registered with} with must implement {@link TreeDataProvider.getParent getParent} method to access this API. + */ + reveal(element: T, options?: { select?: boolean; focus?: boolean; expand?: boolean | number }): Thenable; + } + + /** + * A data provider that provides tree data + */ + export interface TreeDataProvider { + /** + * An optional event to signal that an element or root has changed. + * This will trigger the view to update the changed element/root and its children recursively (if shown). + * To signal that root has changed, do not pass any argument or pass `undefined` or `null`. + */ + onDidChangeTreeData?: Event; + + /** + * Get {@link TreeItem} representation of the `element` + * + * @param element The element for which {@link TreeItem} representation is asked for. + * @return TreeItem representation of the element. + */ + getTreeItem(element: T): TreeItem | Thenable; + + /** + * Get the children of `element` or root if no element is passed. + * + * @param element The element from which the provider gets children. Can be `undefined`. + * @return Children of `element` or root if no element is passed. + */ + getChildren(element?: T): ProviderResult; + + /** + * Optional method to return the parent of `element`. + * Return `null` or `undefined` if `element` is a child of root. + * + * **NOTE:** This method should be implemented in order to access {@link TreeView.reveal reveal} API. + * + * @param element The element for which the parent has to be returned. + * @return Parent of `element`. + */ + getParent?(element: T): ProviderResult; + + /** + * Called on hover to resolve the {@link TreeItem.tooltip TreeItem} property if it is undefined. + * Called on tree item click/open to resolve the {@link TreeItem.command TreeItem} property if it is undefined. + * Only properties that were undefined can be resolved in `resolveTreeItem`. + * Functionality may be expanded later to include being called to resolve other missing + * properties on selection and/or on open. + * + * Will only ever be called once per TreeItem. + * + * onDidChangeTreeData should not be triggered from within resolveTreeItem. + * + * *Note* that this function is called when tree items are already showing in the UI. + * Because of that, no property that changes the presentation (label, description, etc.) + * can be changed. + * + * @param item Undefined properties of `item` should be set then `item` should be returned. + * @param element The object associated with the TreeItem. + * @param token A cancellation token. + * @return The resolved tree item or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveTreeItem?(item: TreeItem, element: T, token: CancellationToken): ProviderResult; + } + + export class TreeItem { + /** + * A human-readable string describing this item. When `falsy`, it is derived from {@link TreeItem.resourceUri resourceUri}. + */ + label?: string | TreeItemLabel; + + /** + * Optional id for the tree item that has to be unique across tree. The id is used to preserve the selection and expansion state of the tree item. + * + * If not provided, an id is generated using the tree item's label. **Note** that when labels change, ids will change and that selection and expansion state cannot be kept stable anymore. + */ + id?: string; + + /** + * The icon path or {@link ThemeIcon} for the tree item. + * When `falsy`, {@link ThemeIcon.Folder Folder Theme Icon} is assigned, if item is collapsible otherwise {@link ThemeIcon.File File Theme Icon}. + * When a file or folder {@link ThemeIcon} is specified, icon is derived from the current file icon theme for the specified theme icon using {@link TreeItem.resourceUri resourceUri} (if provided). + */ + iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; + + /** + * A human-readable string which is rendered less prominent. + * When `true`, it is derived from {@link TreeItem.resourceUri resourceUri} and when `falsy`, it is not shown. + */ + description?: string | boolean; + + /** + * The {@link Uri} of the resource representing this item. + * + * Will be used to derive the {@link TreeItem.label label}, when it is not provided. + * Will be used to derive the icon from current file icon theme, when {@link TreeItem.iconPath iconPath} has {@link ThemeIcon} value. + */ + resourceUri?: Uri; + + /** + * The tooltip text when you hover over this item. + */ + tooltip?: string | MarkdownString | undefined; + + /** + * The {@link Command} that should be executed when the tree item is selected. + * + * Please use `vscode.open` or `vscode.diff` as command IDs when the tree item is opening + * something in the editor. Using these commands ensures that the resulting editor will + * appear consistent with how other built-in trees open editors. + */ + command?: Command; + + /** + * {@link TreeItemCollapsibleState} of the tree item. + */ + collapsibleState?: TreeItemCollapsibleState; + + /** + * Context value of the tree item. This can be used to contribute item specific actions in the tree. + * For example, a tree item is given a context value as `folder`. When contributing actions to `view/item/context` + * using `menus` extension point, you can specify context value for key `viewItem` in `when` expression like `viewItem == folder`. + * ```json + * "contributes": { + * "menus": { + * "view/item/context": [ + * { + * "command": "extension.deleteFolder", + * "when": "viewItem == folder" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteFolder` only for items with `contextValue` is `folder`. + */ + contextValue?: string; + + /** + * Accessibility information used when screen reader interacts with this tree item. + * Generally, a TreeItem has no need to set the `role` of the accessibilityInformation; + * however, there are cases where a TreeItem is not displayed in a tree-like way where setting the `role` may make sense. + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * @param label A human-readable string describing this item + * @param collapsibleState {@link TreeItemCollapsibleState} of the tree item. Default is {@link TreeItemCollapsibleState.None} + */ + constructor(label: string | TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); + + /** + * @param resourceUri The {@link Uri} of the resource representing this item. + * @param collapsibleState {@link TreeItemCollapsibleState} of the tree item. Default is {@link TreeItemCollapsibleState.None} + */ + constructor(resourceUri: Uri, collapsibleState?: TreeItemCollapsibleState); + } + + /** + * Collapsible state of the tree item + */ + export enum TreeItemCollapsibleState { + /** + * Determines an item can be neither collapsed nor expanded. Implies it has no children. + */ + None = 0, + /** + * Determines an item is collapsed + */ + Collapsed = 1, + /** + * Determines an item is expanded + */ + Expanded = 2 + } + + /** + * Label describing the {@link TreeItem Tree item} + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the {@link TreeItem Tree item}. + */ + 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][]; + } + + /** + * Value-object describing what options a terminal should use. + */ + export interface TerminalOptions { + /** + * A human-readable string which will be used to represent the terminal in the UI. + */ + name?: string; + + /** + * A path to a custom shell executable to be used in the terminal. + */ + shellPath?: string; + + /** + * Args for the custom shell executable. A string can be used on Windows only which allows + * specifying shell args in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). + */ + shellArgs?: string[] | string; + + /** + * A path or Uri for the current working directory to be used for the terminal. + */ + cwd?: string | Uri; + + /** + * Object with environment variables that will be added to the editor process. + */ + env?: { [key: string]: string | null | undefined }; + + /** + * Whether the terminal process environment should be exactly as provided in + * `TerminalOptions.env`. When this is false (default), the environment will be based on the + * window's environment and also apply configured platform settings like + * `terminal.integrated.env.windows` on top. When this is true, the complete environment + * must be provided as nothing will be inherited from the process or any configuration. + */ + strictEnv?: boolean; + + /** + * When enabled the terminal will run the process as normal but not be surfaced to the user + * until `Terminal.show` is called. The typical usage for this is when you need to run + * something that may need interactivity but only want to tell the user about it when + * interaction is needed. Note that the terminals will still be exposed to all extensions + * as normal. + */ + hideFromUser?: boolean; + + /** + * A message to write to the terminal on first launch, note that this is not sent to the + * process but, rather written directly to the terminal. This supports escape sequences such + * a setting text style. + */ + message?: string; + + /** + * The icon path or {@link ThemeIcon} for the terminal. + */ + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + + /** + * The icon {@link ThemeColor} for the terminal. + * The `terminal.ansi*` theme keys are + * recommended for the best contrast and consistency across themes. + */ + color?: ThemeColor; + + /** + * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. + */ + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + + /** + * Opt-out of the default terminal persistence on restart and reload. + * This will only take effect when `terminal.integrated.enablePersistentSessions` is enabled. + */ + isTransient?: boolean; + } + + /** + * Value-object describing what options a virtual process terminal should use. + */ + export interface ExtensionTerminalOptions { + /** + * A human-readable string which will be used to represent the terminal in the UI. + */ + name: string; + + /** + * An implementation of {@link Pseudoterminal} that allows an extension to + * control a terminal. + */ + pty: Pseudoterminal; + + /** + * The icon path or {@link ThemeIcon} for the terminal. + */ + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + + /** + * The icon {@link ThemeColor} for the terminal. + * The standard `terminal.ansi*` theme keys are + * recommended for the best contrast and consistency across themes. + */ + color?: ThemeColor; + + /** + * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. + */ + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + + /** + * Opt-out of the default terminal persistence on restart and reload. + * This will only take effect when `terminal.integrated.enablePersistentSessions` is enabled. + */ + isTransient?: boolean; + } + + /** + * Defines the interface of a terminal pty, enabling extensions to control a terminal. + */ + interface Pseudoterminal { + /** + * An event that when fired will write data to the terminal. Unlike + * {@link Terminal.sendText} which sends text to the underlying child + * pseudo-device (the child), this will write the text to parent pseudo-device (the + * _terminal_ itself). + * + * Note writing `\n` will just move the cursor down 1 row, you need to write `\r` as well + * to move the cursor to the left-most cell. + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * **Example:** Write red text to the terminal + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * open: () => writeEmitter.fire('\x1b[31mHello world\x1b[0m'), + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + * + * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk + * ```typescript + * writeEmitter.fire('\x1b[10;20H*'); + * ``` + */ + onDidWrite: Event; + + /** + * An event that when fired allows overriding the {@link Pseudoterminal.setDimensions dimensions} of the + * terminal. Note that when set, the overridden dimensions will only take effect when they + * are lower than the actual dimensions of the terminal (ie. there will never be a scroll + * bar). Set to `undefined` for the terminal to go back to the regular dimensions (fit to + * the size of the panel). + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * **Example:** Override the dimensions of a terminal to 20 columns and 10 rows + * ```typescript + * const dimensionsEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidOverrideDimensions: dimensionsEmitter.event, + * open: () => { + * dimensionsEmitter.fire({ + * columns: 20, + * rows: 10 + * }); + * }, + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + */ + onDidOverrideDimensions?: Event; + + /** + * An event that when fired will signal that the pty is closed and dispose of the terminal. + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * A number can be used to provide an exit code for the terminal. Exit codes must be + * positive and a non-zero exit codes signals failure which shows a notification for a + * regular terminal and allows dependent tasks to proceed when used with the + * `CustomExecution` API. + * + * **Example:** Exit the terminal when "y" is pressed, otherwise show a notification. + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const closeEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidClose: closeEmitter.event, + * open: () => writeEmitter.fire('Press y to exit successfully'), + * close: () => {}, + * handleInput: data => { + * if (data !== 'y') { + * vscode.window.showInformationMessage('Something went wrong'); + * } + * closeEmitter.fire(); + * } + * }; + * vscode.window.createTerminal({ name: 'Exit example', pty }); + * ``` + */ + onDidClose?: Event; + + /** + * An event that when fired allows changing the name of the terminal. + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * **Example:** Change the terminal name to "My new terminal". + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const changeNameEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidChangeName: changeNameEmitter.event, + * open: () => changeNameEmitter.fire('My new terminal'), + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + */ + onDidChangeName?: Event; + + /** + * Implement to handle when the pty is open and ready to start firing events. + * + * @param initialDimensions The dimensions of the terminal, this will be undefined if the + * terminal panel has not been opened before this is called. + */ + open(initialDimensions: TerminalDimensions | undefined): void; + + /** + * Implement to handle when the terminal is closed by an act of the user. + */ + close(): void; + + /** + * Implement to handle incoming keystrokes in the terminal or when an extension calls + * {@link Terminal.sendText}. `data` contains the keystrokes/text serialized into + * their corresponding VT sequence representation. + * + * @param data The incoming data. + * + * **Example:** Echo input in the terminal. The sequence for enter (`\r`) is translated to + * CRLF to go to a new line and move the cursor to the start of the line. + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * open: () => {}, + * close: () => {}, + * handleInput: data => writeEmitter.fire(data === '\r' ? '\r\n' : data) + * }; + * vscode.window.createTerminal({ name: 'Local echo', pty }); + * ``` + */ + handleInput?(data: string): void; + + /** + * Implement to handle when the number of rows and columns that fit into the terminal panel + * changes, for example when font size changes or when the panel is resized. The initial + * state of a terminal's dimensions should be treated as `undefined` until this is triggered + * as the size of a terminal isn't known until it shows up in the user interface. + * + * When dimensions are overridden by + * {@link Pseudoterminal.onDidOverrideDimensions onDidOverrideDimensions}, `setDimensions` will + * continue to be called with the regular panel dimensions, allowing the extension continue + * to react dimension changes. + * + * @param dimensions The new dimensions. + */ + setDimensions?(dimensions: TerminalDimensions): void; + } + + /** + * Represents the dimensions of a terminal. + */ + export interface TerminalDimensions { + /** + * The number of columns in the terminal. + */ + readonly columns: number; + + /** + * The number of rows in the terminal. + */ + readonly rows: number; + } + + /** + * Represents how a terminal exited. + */ + export interface TerminalExitStatus { + /** + * The exit code that a terminal exited with, it can have the following values: + * - Zero: the terminal process or custom execution succeeded. + * - Non-zero: the terminal process or custom execution failed. + * - `undefined`: the user forcibly closed the terminal or a custom execution exited + * without providing an exit code. + */ + readonly code: number | undefined; + + /** + * The reason that triggered the exit of a terminal. + */ + readonly reason: TerminalExitReason; + } + + /** + * Terminal exit reason kind. + */ + export enum TerminalExitReason { + /** + * Unknown reason. + */ + Unknown = 0, + + /** + * The window closed/reloaded. + */ + Shutdown = 1, + + /** + * The shell process exited. + */ + Process = 2, + + /** + * The user closed the terminal. + */ + User = 3, + + /** + * An extension disposed the terminal. + */ + Extension = 4, + } + + /** + * A type of mutation that can be applied to an environment variable. + */ + 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 + } + + /** + * A type of mutation and its value to be applied to an environment variable. + */ + 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 extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { + /** + * 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. + * + * @param variable The variable to replace. + * @param value The value to replace the variable with. + */ + 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. + * + * @param variable The variable to append to. + * @param value The value to append to the variable. + */ + 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. + * + * @param variable The variable to prepend. + * @param value The value to prepend to the variable. + */ + prepend(variable: string, value: string): void; + + /** + * Gets the mutator that this collection applies to a variable, if any. + * + * @param variable The variable to get the mutator for. + */ + get(variable: string): EnvironmentVariableMutator | undefined; + + /** + * Iterate over each mutator in this collection. + * + * @param callback Function to execute for each entry. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callback: (variable: string, mutator: EnvironmentVariableMutator, collection: EnvironmentVariableCollection) => any, thisArg?: any): void; + + /** + * Deletes this collection's mutator for a variable. + * + * @param variable The variable to delete the mutator for. + */ + delete(variable: string): void; + + /** + * Clears all mutators from this collection. + */ + clear(): void; + } + + /** + * A location in the editor at which progress information can be shown. It depends on the + * location how progress is visually represented. + */ + export enum ProgressLocation { + + /** + * Show progress for the source control viewlet, as overlay for the icon and as progress bar + * inside the viewlet (when visible). Neither supports cancellation nor discrete progress nor + * a label to describe the operation. + */ + SourceControl = 1, + + /** + * Show progress in the status bar of the editor. Neither supports cancellation nor discrete progress. + * Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax in the progress label. + */ + Window = 10, + + /** + * Show progress as notification with an optional cancel button. Supports to show infinite and discrete + * progress but does not support rendering of icons. + */ + Notification = 15 + } + + /** + * Value-object describing where and how progress should show. + */ + export interface ProgressOptions { + + /** + * The location at which progress should show. + */ + location: ProgressLocation | { viewId: string }; + + /** + * A human-readable string which will be used to describe the + * operation. + */ + title?: string; + + /** + * Controls if a cancel button should show to allow the user to + * cancel the long running operation. Note that currently only + * `ProgressLocation.Notification` is supporting to show a cancel + * button. + */ + cancellable?: boolean; + } + + /** + * A light-weight user input UI that is initially not visible. After + * configuring it through its properties the extension can make it + * visible by calling {@link QuickInput.show}. + * + * There are several reasons why this UI might have to be hidden and + * the extension will be notified through {@link QuickInput.onDidHide}. + * (Examples include: an explicit call to {@link QuickInput.hide}, + * the user pressing Esc, some other input UI opening, etc.) + * + * A user pressing Enter or some other gesture implying acceptance + * of the current state does not automatically hide this UI component. + * It is up to the extension to decide whether to accept the user's input + * and if the UI should indeed be hidden through a call to {@link QuickInput.hide}. + * + * When the extension no longer needs this input UI, it should + * {@link QuickInput.dispose} it to allow for freeing up + * any resources associated with it. + * + * See {@link QuickPick} and {@link InputBox} for concrete UIs. + */ + export interface QuickInput { + + /** + * An optional title. + */ + title: string | undefined; + + /** + * An optional current step count. + */ + step: number | undefined; + + /** + * An optional total step count. + */ + totalSteps: number | undefined; + + /** + * If the UI should allow for user input. Defaults to true. + * + * Change this to false, e.g., while validating user input or + * loading data for the next step in user input. + */ + enabled: boolean; + + /** + * If the UI should show a progress indicator. Defaults to false. + * + * Change this to true, e.g., while loading more data or validating + * user input. + */ + busy: boolean; + + /** + * If the UI should stay open even when loosing UI focus. Defaults to false. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut: boolean; + + /** + * Makes the input UI visible in its current configuration. Any other input + * UI will first fire an {@link QuickInput.onDidHide} event. + */ + show(): void; + + /** + * Hides this input UI. This will also fire an {@link QuickInput.onDidHide} + * event. + */ + hide(): void; + + /** + * An event signaling when this input UI is hidden. + * + * There are several reasons why this UI might have to be hidden and + * the extension will be notified through {@link QuickInput.onDidHide}. + * (Examples include: an explicit call to {@link QuickInput.hide}, + * the user pressing Esc, some other input UI opening, etc.) + */ + onDidHide: Event; + + /** + * Dispose of this input UI and any associated resources. If it is still + * visible, it is first hidden. After this call the input UI is no longer + * functional and no additional methods or properties on it should be + * accessed. Instead a new input UI should be created. + */ + dispose(): void; + } + + /** + * A concrete {@link QuickInput} to let the user pick an item from a + * list of items of type T. The items can be filtered through a filter text field and + * there is an option {@link QuickPick.canSelectMany canSelectMany} to allow for + * selecting multiple items. + * + * Note that in many cases the more convenient {@link window.showQuickPick} + * is easier to use. {@link window.createQuickPick} should be used + * when {@link window.showQuickPick} does not offer the required flexibility. + */ + export interface QuickPick extends QuickInput { + + /** + * Current value of the filter text. + */ + value: string; + + /** + * Optional placeholder shown in the filter textbox when no filter has been entered. + */ + placeholder: string | undefined; + + /** + * An event signaling when the value of the filter text has changed. + */ + readonly onDidChangeValue: Event; + + /** + * An event signaling when the user indicated acceptance of the selected item(s). + */ + readonly onDidAccept: Event; + + /** + * Buttons for actions in the UI. + */ + buttons: readonly QuickInputButton[]; + + /** + * An event signaling when a button in the title bar was triggered. + * This event does not fire for buttons on a {@link QuickPickItem}. + */ + readonly onDidTriggerButton: Event; + + /** + * An event signaling when a button in a particular {@link QuickPickItem} was triggered. + * This event does not fire for buttons in the title bar. + */ + readonly onDidTriggerItemButton: Event>; + + /** + * Items to pick from. This can be read and updated by the extension. + */ + items: readonly T[]; + + /** + * If multiple items can be selected at the same time. Defaults to false. + */ + canSelectMany: boolean; + + /** + * If the filter text should also be matched against the description of the items. Defaults to false. + */ + matchOnDescription: boolean; + + /** + * If the filter text should also be matched against the detail of the items. Defaults to false. + */ + matchOnDetail: boolean; + + /* + * An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false. + */ + keepScrollPosition?: boolean; + + /** + * Active items. This can be read and updated by the extension. + */ + activeItems: readonly T[]; + + /** + * An event signaling when the active items have changed. + */ + readonly onDidChangeActive: Event; + + /** + * Selected items. This can be read and updated by the extension. + */ + selectedItems: readonly T[]; + + /** + * An event signaling when the selected items have changed. + */ + readonly onDidChangeSelection: Event; + } + + /** + * A concrete {@link QuickInput} to let the user input a text value. + * + * Note that in many cases the more convenient {@link window.showInputBox} + * is easier to use. {@link window.createInputBox} should be used + * when {@link window.showInputBox} does not offer the required flexibility. + */ + export interface InputBox extends QuickInput { + + /** + * Current input value. + */ + value: string; + + /** + * Selection range in the input value. Defined as tuple of two number where the + * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole + * pre-filled value will be selected, when empty (start equals end) only the cursor will be set, + * otherwise the defined range will be selected. + * + * This property does not get updated when the user types or makes a selection, + * but it can be updated by the extension. + */ + valueSelection: readonly [number, number] | undefined; + + /** + * Optional placeholder shown when no value has been input. + */ + placeholder: string | undefined; + + /** + * If the input value should be hidden. Defaults to false. + */ + password: boolean; + + /** + * An event signaling when the value has changed. + */ + readonly onDidChangeValue: Event; + + /** + * An event signaling when the user indicated acceptance of the input value. + */ + readonly onDidAccept: Event; + + /** + * Buttons for actions in the UI. + */ + buttons: readonly QuickInputButton[]; + + /** + * An event signaling when a button was triggered. + */ + readonly onDidTriggerButton: Event; + + /** + * An optional prompt text providing some ask or explanation to the user. + */ + prompt: string | undefined; + + /** + * An optional validation message indicating a problem with the current input value. + * By returning a string, the InputBox will use a default {@link InputBoxValidationSeverity} of Error. + * Returning undefined clears the validation message. + */ + validationMessage: string | InputBoxValidationMessage | undefined; + } + + /** + * Button for an action in a {@link QuickPick} or {@link InputBox}. + */ + export interface QuickInputButton { + + /** + * Icon for the button. + */ + readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; + + /** + * An optional tooltip. + */ + readonly tooltip?: string | undefined; + } + + /** + * Predefined buttons for {@link QuickPick} and {@link InputBox}. + */ + export class QuickInputButtons { + + /** + * A back button for {@link QuickPick} and {@link InputBox}. + * + * When a navigation 'back' button is needed this one should be used for consistency. + * It comes with a predefined icon, tooltip and location. + */ + static readonly Back: QuickInputButton; + + /** + * @hidden + */ + private constructor(); + } + + /** + * An event signaling when a button in a particular {@link QuickPickItem} was triggered. + * This event does not fire for buttons in the title bar. + */ + export interface QuickPickItemButtonEvent { + /** + * The button that was clicked. + */ + readonly button: QuickInputButton; + /** + * The item that the button belongs to. + */ + readonly item: T; + } + + /** + * An event describing an individual change in the text of a {@link TextDocument document}. + */ + export interface TextDocumentContentChangeEvent { + /** + * The range that got replaced. + */ + readonly range: Range; + /** + * The offset of the range that got replaced. + */ + readonly rangeOffset: number; + /** + * The length of the range that got replaced. + */ + readonly rangeLength: number; + /** + * The new text for the range. + */ + readonly text: string; + } + + export enum TextDocumentChangeReason { + /** The text change is caused by an undo operation. */ + Undo = 1, + + /** The text change is caused by an redo operation. */ + Redo = 2, + } + + /** + * An event describing a transactional {@link TextDocument document} change. + */ + export interface TextDocumentChangeEvent { + + /** + * The affected document. + */ + readonly document: TextDocument; + + /** + * An array of content changes. + */ + readonly contentChanges: readonly TextDocumentContentChangeEvent[]; + + /** + * The reason why the document was changed. + * Is `undefined` if the reason is not known. + */ + readonly reason: TextDocumentChangeReason | undefined; + } + + /** + * Represents reasons why a text document is saved. + */ + export enum TextDocumentSaveReason { + + /** + * Manually triggered, e.g. by the user pressing save, by starting debugging, + * or by an API call. + */ + Manual = 1, + + /** + * Automatic after a delay. + */ + AfterDelay = 2, + + /** + * When the editor lost focus. + */ + FocusOut = 3 + } + + /** + * An event that is fired when a {@link TextDocument document} will be saved. + * + * To make modifications to the document before it is being saved, call the + * {@linkcode TextDocumentWillSaveEvent.waitUntil waitUntil}-function with a thenable + * that resolves to an array of {@link TextEdit text edits}. + */ + export interface TextDocumentWillSaveEvent { + + /** + * The document that will be saved. + */ + readonly document: TextDocument; + + /** + * The reason why save was triggered. + */ + readonly reason: TextDocumentSaveReason; + + /** + * Allows to pause the event loop and to apply {@link TextEdit pre-save-edits}. + * Edits of subsequent calls to this function will be applied in order. The + * edits will be *ignored* if concurrent modifications of the document happened. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillSaveTextDocument(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that resolves to {@link TextEdit pre-save-edits}. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event loop until the provided thenable resolved. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired when files are going to be created. + * + * To make modifications to the workspace before the files are created, + * call the {@linkcode FileWillCreateEvent.waitUntil waitUntil}-function with a + * thenable that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface FileWillCreateEvent { + + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The files that are going to be created. + */ + readonly files: readonly Uri[]; + + /** + * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are created. + */ + export interface FileCreateEvent { + + /** + * The files that got created. + */ + readonly files: readonly Uri[]; + } + + /** + * An event that is fired when files are going to be deleted. + * + * To make modifications to the workspace before the files are deleted, + * call the {@link FileWillCreateEvent.waitUntil `waitUntil}-function with a + * thenable that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface FileWillDeleteEvent { + + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The files that are going to be deleted. + */ + readonly files: readonly Uri[]; + + /** + * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are deleted. + */ + export interface FileDeleteEvent { + + /** + * The files that got deleted. + */ + readonly files: readonly Uri[]; + } + + /** + * An event that is fired when files are going to be renamed. + * + * To make modifications to the workspace before the files are renamed, + * call the {@link FileWillCreateEvent.waitUntil `waitUntil}-function with a + * thenable that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface FileWillRenameEvent { + + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The files that are going to be renamed. + */ + readonly files: ReadonlyArray<{ readonly oldUri: Uri; readonly newUri: Uri }>; + + /** + * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are renamed. + */ + export interface FileRenameEvent { + + /** + * The files that got renamed. + */ + readonly files: ReadonlyArray<{ readonly oldUri: Uri; readonly newUri: Uri }>; + } + + /** + * An event describing a change to the set of {@link workspace.workspaceFolders workspace folders}. + */ + export interface WorkspaceFoldersChangeEvent { + /** + * Added workspace folders. + */ + readonly added: readonly WorkspaceFolder[]; + + /** + * Removed workspace folders. + */ + readonly removed: readonly WorkspaceFolder[]; + } + + /** + * A workspace folder is one of potentially many roots opened by the editor. All workspace folders + * are equal which means there is no notion of an active or primary workspace folder. + */ + export interface WorkspaceFolder { + + /** + * The associated uri for this workspace folder. + * + * *Note:* The {@link Uri}-type was intentionally chosen such that future releases of the editor can support + * workspace folders that are not stored on the local disk, e.g. `ftp://server/workspaces/foo`. + */ + readonly uri: Uri; + + /** + * The name of this workspace folder. Defaults to + * the basename of its {@link Uri.path uri-path} + */ + readonly name: string; + + /** + * The ordinal number of this workspace folder. + */ + readonly index: number; + } + + /** + * Namespace for dealing with the current workspace. A workspace is the collection of one + * or more folders that are opened in an editor window (instance). + * + * It is also possible to open an editor without a workspace. For example, when you open a + * new editor window by selecting a file from your platform's File menu, you will not be + * inside a workspace. In this mode, some of the editor's capabilities are reduced but you can + * still open text files and edit them. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on + * the concept of workspaces. + * + * The workspace offers support for {@link workspace.createFileSystemWatcher listening} to fs + * events and for {@link workspace.findFiles finding} files. Both perform well and run _outside_ + * the editor-process so that they should be always used instead of nodejs-equivalents. + */ + export namespace workspace { + + /** + * A {@link FileSystem file system} instance that allows to interact with local and remote + * files, e.g. `vscode.workspace.fs.readDirectory(someUri)` allows to retrieve all entries + * of a directory or `vscode.workspace.fs.stat(anotherUri)` returns the meta data for a + * file. + */ + export const fs: FileSystem; + + /** + * The uri of the first entry of {@linkcode workspace.workspaceFolders workspaceFolders} + * as `string`. `undefined` if there is no first entry. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information + * on workspaces. + * + * @deprecated Use {@linkcode workspace.workspaceFolders workspaceFolders} instead. + */ + export const rootPath: string | undefined; + + /** + * List of workspace folders (0-N) that are open in the editor. `undefined` when no workspace + * has been opened. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information + * on workspaces. + */ + export const workspaceFolders: readonly WorkspaceFolder[] | undefined; + + /** + * The name of the workspace. `undefined` when no workspace + * has been opened. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on + * the concept of workspaces. + */ + export const name: string | undefined; + + /** + * The location of the workspace file, for example: + * + * `file:///Users/name/Development/myProject.code-workspace` + * + * or + * + * `untitled:1555503116870` + * + * for a workspace that is untitled and not yet saved. + * + * Depending on the workspace that is opened, the value will be: + * * `undefined` when no workspace is opened + * * the path of the workspace file as `Uri` otherwise. if the workspace + * is untitled, the returned URI will use the `untitled:` scheme + * + * The location can e.g. be used with the `vscode.openFolder` command to + * open the workspace again after it has been closed. + * + * **Example:** + * ```typescript + * vscode.commands.executeCommand('vscode.openFolder', uriOfWorkspace); + * ``` + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on + * the concept of workspaces. + * + * **Note:** it is not advised to use `workspace.workspaceFile` to write + * configuration data into the file. You can use `workspace.getConfiguration().update()` + * for that purpose which will work both when a single folder is opened as + * well as an untitled or saved workspace. + */ + export const workspaceFile: Uri | undefined; + + /** + * An event that is emitted when a workspace folder is added or removed. + * + * **Note:** this event will not fire if the first workspace folder is added, removed or changed, + * because in that case the currently executing extensions (including the one that listens to this + * event) will be terminated and restarted so that the (deprecated) `rootPath` property is updated + * to point to the first workspace folder. + */ + export const onDidChangeWorkspaceFolders: Event; + + /** + * Returns the {@link WorkspaceFolder workspace folder} that contains a given uri. + * * returns `undefined` when the given uri doesn't match any workspace folder + * * returns the *input* when the given uri is a workspace folder itself + * + * @param uri An uri. + * @return A workspace folder or `undefined` + */ + export function getWorkspaceFolder(uri: Uri): WorkspaceFolder | undefined; + + /** + * Returns a path that is relative to the workspace folder or folders. + * + * When there are no {@link workspace.workspaceFolders workspace folders} or when the path + * is not contained in them, the input is returned. + * + * @param pathOrUri A path or uri. When a uri is given its {@link Uri.fsPath fsPath} is used. + * @param includeWorkspaceFolder When `true` and when the given path is contained inside a + * workspace folder the name of the workspace is prepended. Defaults to `true` when there are + * multiple workspace folders and `false` otherwise. + * @return A path relative to the root or the input. + */ + export function asRelativePath(pathOrUri: string | Uri, includeWorkspaceFolder?: boolean): string; + + /** + * This method replaces `deleteCount` {@link workspace.workspaceFolders workspace folders} starting at index `start` + * by an optional set of `workspaceFoldersToAdd` on the `vscode.workspace.workspaceFolders` array. This "splice" + * behavior can be used to add, remove and change workspace folders in a single operation. + * + * If the first workspace folder is added, removed or changed, the currently executing extensions (including the + * one that called this method) will be terminated and restarted so that the (deprecated) `rootPath` property is + * updated to point to the first workspace folder. + * + * Use the {@linkcode onDidChangeWorkspaceFolders onDidChangeWorkspaceFolders()} event to get notified when the + * workspace folders have been updated. + * + * **Example:** adding a new workspace folder at the end of workspace folders + * ```typescript + * workspace.updateWorkspaceFolders(workspace.workspaceFolders ? workspace.workspaceFolders.length : 0, null, { uri: ...}); + * ``` + * + * **Example:** removing the first workspace folder + * ```typescript + * workspace.updateWorkspaceFolders(0, 1); + * ``` + * + * **Example:** replacing an existing workspace folder with a new one + * ```typescript + * workspace.updateWorkspaceFolders(0, 1, { uri: ...}); + * ``` + * + * It is valid to remove an existing workspace folder and add it again with a different name + * to rename that folder. + * + * **Note:** it is not valid to call {@link updateWorkspaceFolders updateWorkspaceFolders()} multiple times + * without waiting for the {@linkcode onDidChangeWorkspaceFolders onDidChangeWorkspaceFolders()} to fire. + * + * @param start the zero-based location in the list of currently opened {@link WorkspaceFolder workspace folders} + * from which to start deleting workspace folders. + * @param deleteCount the optional number of workspace folders to remove. + * @param workspaceFoldersToAdd the optional variable set of workspace folders to add in place of the deleted ones. + * Each workspace is identified with a mandatory URI and an optional name. + * @return true if the operation was successfully started and false otherwise if arguments were used that would result + * in invalid workspace folder state (e.g. 2 folders with the same URI). + */ + export function updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, ...workspaceFoldersToAdd: { readonly uri: Uri; readonly name?: string }[]): boolean; + + /** + * Creates a file system watcher that is notified on file events (create, change, delete) + * depending on the parameters provided. + * + * By default, all opened {@link workspace.workspaceFolders workspace folders} will be watched + * for file changes recursively. + * + * Additional paths can be added for file watching by providing a {@link RelativePattern} with + * a `base` path to watch. If the `pattern` is complex (e.g. contains `**` or path segments), + * the path will be watched recursively and otherwise will be watched non-recursively (i.e. only + * changes to the first level of the path will be reported). + * + * *Note* that requests for recursive file watchers for a `base` path that is inside the opened + * workspace are ignored given all opened {@link workspace.workspaceFolders workspace folders} are + * watched for file changes recursively by default. Non-recursive file watchers however are always + * supported, even inside the opened workspace because they allow to bypass the configured settings + * for excludes (`files.watcherExclude`). If you need to watch in a location that is typically + * excluded (for example `node_modules` or `.git` folder), then you can use a non-recursive watcher + * in the workspace for this purpose. + * + * If possible, keep the use of recursive watchers to a minimum because recursive file watching + * is quite resource intense. + * + * Providing a `string` as `globPattern` acts as convenience method for watching file events in + * all opened workspace folders. It cannot be used to add more folders for file watching, nor will + * it report any file events from folders that are not part of the opened workspace folders. + * + * Optionally, flags to ignore certain kinds of events can be provided. + * + * To stop listening to events the watcher must be disposed. + * + * *Note* that file events from recursive file watchers may be excluded based on user configuration. + * The setting `files.watcherExclude` helps to reduce the overhead of file events from folders + * that are known to produce many file changes at once (such as `node_modules` folders). As such, + * it is highly recommended to watch with simple patterns that do not require recursive watchers + * where the exclude settings are ignored and you have full control over the events. + * + * *Note* that symbolic links are not automatically followed for file watching unless the path to + * watch itself is a symbolic link. + * + * *Note* that file changes for the path to be watched may not be delivered when the path itself + * changes. For example, when watching a path `/Users/somename/Desktop` and the path itself is + * being deleted, the watcher may not report an event and may not work anymore from that moment on. + * The underlying behaviour depends on the path that is provided for watching: + * * if the path is within any of the workspace folders, deletions are tracked and reported unless + * excluded via `files.watcherExclude` setting + * * if the path is equal to any of the workspace folders, deletions are not tracked + * * if the path is outside of any of the workspace folders, deletions are not tracked + * + * If you are interested in being notified when the watched path itself is being deleted, you have + * to watch it's parent folder. Make sure to use a simple `pattern` (such as putting the name of the + * folder) to not accidentally watch all sibling folders recursively. + * + * *Note* that the file paths that are reported for having changed may have a different path casing + * compared to the actual casing on disk on case-insensitive platforms (typically macOS and Windows + * but not Linux). We allow a user to open a workspace folder with any desired path casing and try + * to preserve that. This means: + * * if the path is within any of the workspace folders, the path will match the casing of the + * workspace folder up to that portion of the path and match the casing on disk for children + * * if the path is outside of any of the workspace folders, the casing will match the case of the + * path that was provided for watching + * In the same way, symbolic links are preserved, i.e. the file event will report the path of the + * symbolic link as it was provided for watching and not the target. + * + * ### Examples + * + * The basic anatomy of a file watcher is as follows: + * + * ```ts + * const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(, )); + * + * watcher.onDidChange(uri => { ... }); // listen to files being changed + * watcher.onDidCreate(uri => { ... }); // listen to files/folders being created + * watcher.onDidDelete(uri => { ... }); // listen to files/folders getting deleted + * + * watcher.dispose(); // dispose after usage + * ``` + * + * #### Workspace file watching + * + * If you only care about file events in a specific workspace folder: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], '**​/*.js')); + * ``` + * + * If you want to monitor file events across all opened workspace folders: + * + * ```ts + * vscode.workspace.createFileSystemWatcher('**​/*.js')); + * ``` + * + * *Note:* the array of workspace folders can be empty if no workspace is opened (empty window). + * + * #### Out of workspace file watching + * + * To watch a folder for changes to *.js files outside the workspace (non recursively), pass in a `Uri` to such + * a folder: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.file(), '*.js')); + * ``` + * + * And use a complex glob pattern to watch recursively: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.file(), '**​/*.js')); + * ``` + * + * Here is an example for watching the active editor for file changes: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.window.activeTextEditor.document.uri, '*')); + * ``` + * + * @param globPattern A {@link GlobPattern glob pattern} that controls which file events the watcher should report. + * @param ignoreCreateEvents Ignore when files have been created. + * @param ignoreChangeEvents Ignore when files have been changed. + * @param ignoreDeleteEvents Ignore when files have been deleted. + * @return A new file system watcher instance. Must be disposed when no longer needed. + */ + export function createFileSystemWatcher(globPattern: GlobPattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; + + /** + * Find files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * + * @example + * findFiles('**​/*.js', '**​/node_modules/**', 10) + * + * @param include A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. Use a {@link RelativePattern relative pattern} + * to restrict the search results to a {@link WorkspaceFolder workspace folder}. + * @param exclude A {@link GlobPattern glob pattern} 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 file-excludes (e.g. the `files.exclude`-setting + * but not `search.exclude`) will apply. When `null`, no excludes will apply. + * @param maxResults An upper-bound for the result. + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves to an array of resource identifiers. Will return no results if no + * {@link workspace.workspaceFolders workspace folders} are opened. + */ + export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable; + + /** + * Save all dirty files. + * + * @param includeUntitled Also save files that have been created during this session. + * @return A thenable that resolves when the files have been saved. Will return `false` + * for any file that failed to save. + */ + export function saveAll(includeUntitled?: boolean): Thenable; + + /** + * Make changes to one or many resources or create, delete, and rename resources as defined by the given + * {@link WorkspaceEdit workspace edit}. + * + * All changes of a workspace edit are applied in the same order in which they have been added. If + * multiple textual inserts are made at the same position, these strings appear in the resulting text + * in the order the 'inserts' were made, unless that are interleaved with resource edits. Invalid sequences + * like 'delete file a' -> 'insert text in file a' cause failure of the operation. + * + * When applying a workspace edit that consists only of text edits an 'all-or-nothing'-strategy is used. + * A workspace edit with resource creations or deletions aborts the operation, e.g. consecutive edits will + * not be attempted, when a single edit fails. + * + * @param edit A workspace edit. + * @param metadata Optional {@link WorkspaceEditMetadata metadata} for the edit. + * @return A thenable that resolves when the edit could be applied. + */ + export function applyEdit(edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata): Thenable; + + /** + * All text documents currently known to the editor. + */ + export const textDocuments: readonly TextDocument[]; + + /** + * Opens a document. Will return early if this document is already open. Otherwise + * the document is loaded and the {@link workspace.onDidOpenTextDocument didOpen}-event fires. + * + * The document is denoted by an {@link Uri}. Depending on the {@link Uri.scheme scheme} the + * following rules apply: + * * `file`-scheme: Open a file on disk (`openTextDocument(Uri.file(path))`). Will be rejected if the file + * does not exist or cannot be loaded. + * * `untitled`-scheme: Open a blank untitled file with associated path (`openTextDocument(Uri.file(path).with({ scheme: 'untitled' }))`). + * The language will be derived from the file name. + * * For all other schemes contributed {@link TextDocumentContentProvider text document content providers} and + * {@link FileSystemProvider file system providers} are consulted. + * + * *Note* that the lifecycle of the returned document is owned by the editor and not by the extension. That means an + * {@linkcode workspace.onDidCloseTextDocument onDidClose}-event can occur at any time after opening it. + * + * @param uri Identifies the resource to open. + * @return A promise that resolves to a {@link TextDocument document}. + */ + export function openTextDocument(uri: Uri): Thenable; + + /** + * A short-hand for `openTextDocument(Uri.file(fileName))`. + * + * @see {@link workspace.openTextDocument} + * @param fileName A name of a file on disk. + * @return A promise that resolves to a {@link TextDocument document}. + */ + export function openTextDocument(fileName: string): Thenable; + + /** + * Opens an untitled text document. The editor will prompt the user for a file + * path when the document is to be saved. The `options` parameter allows to + * specify the *language* and/or the *content* of the document. + * + * @param options Options to control how the document will be created. + * @return A promise that resolves to a {@link TextDocument document}. + */ + export function openTextDocument(options?: { language?: string; content?: string }): Thenable; + + /** + * Register a text document content provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The uri-scheme to register for. + * @param provider A content provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTextDocumentContentProvider(scheme: string, provider: TextDocumentContentProvider): Disposable; + + /** + * An event that is emitted when a {@link TextDocument text document} is opened or when the language id + * of a text document {@link languages.setTextDocumentLanguage has been changed}. + * + * To add an event listener when a visible text document is opened, use the {@link TextEditor} events in the + * {@link window} namespace. Note that: + * + * - The event is emitted before the {@link TextDocument document} is updated in the + * {@link window.activeTextEditor active text editor} + * - When a {@link TextDocument text document} is already open (e.g.: open in another {@link window.visibleTextEditors visible text editor}) this event is not emitted + * + */ + export const onDidOpenTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} is disposed or when the language id + * of a text document {@link languages.setTextDocumentLanguage has been changed}. + * + * *Note 1:* There is no guarantee that this event fires when an editor tab is closed, use the + * {@linkcode window.onDidChangeVisibleTextEditors onDidChangeVisibleTextEditors}-event to know when editors change. + * + * *Note 2:* A document can be open but not shown in an editor which means this event can fire + * for a document that has not been shown in an editor. + */ + export const onDidCloseTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} is changed. This usually happens + * when the {@link TextDocument.getText contents} changes but also when other things like the + * {@link TextDocument.isDirty dirty}-state changes. + */ + export const onDidChangeTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} will be saved to disk. + * + * *Note 1:* Subscribers can delay saving by registering asynchronous work. For the sake of data integrity the editor + * might save without firing this event. For instance when shutting down with dirty files. + * + * *Note 2:* Subscribers are called sequentially and they can {@link TextDocumentWillSaveEvent.waitUntil delay} saving + * by registering asynchronous work. Protection against misbehaving listeners is implemented as such: + * * there is an overall time budget that all listeners share and if that is exhausted no further listener is called + * * listeners that take a long time or produce errors frequently will not be called anymore + * + * The current thresholds are 1.5 seconds as overall time budget and a listener can misbehave 3 times before being ignored. + */ + export const onWillSaveTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} is saved to disk. + */ + export const onDidSaveTextDocument: Event; + + /** + * All notebook documents currently known to the editor. + */ + export const notebookDocuments: readonly NotebookDocument[]; + + /** + * Open a notebook. Will return early if this notebook is already {@link notebook.notebookDocuments loaded}. Otherwise + * the notebook is loaded and the {@linkcode notebook.onDidOpenNotebookDocument onDidOpenNotebookDocument}-event fires. + * + * *Note* that the lifecycle of the returned notebook is owned by the editor and not by the extension. That means an + * {@linkcode notebook.onDidCloseNotebookDocument onDidCloseNotebookDocument}-event can occur at any time after. + * + * *Note* that opening a notebook does not show a notebook editor. This function only returns a notebook document which + * can be shown in a notebook editor but it can also be used for other things. + * + * @param uri The resource to open. + * @returns A promise that resolves to a {@link NotebookDocument notebook} + */ + export function openNotebookDocument(uri: Uri): Thenable; + + /** + * Open an untitled notebook. The editor will prompt the user for a file + * path when the document is to be saved. + * + * @see {@link workspace.openNotebookDocument} + * @param notebookType The notebook type that should be used. + * @param content The initial contents of the notebook. + * @returns A promise that resolves to a {@link NotebookDocument notebook}. + */ + export function openNotebookDocument(notebookType: string, content?: NotebookData): Thenable; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} has changed. + */ + export const onDidChangeNotebookDocument: Event; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is saved. + */ + export const onDidSaveNotebookDocument: Event; + + /** + * Register a {@link NotebookSerializer notebook serializer}. + * + * A notebook serializer must be contributed through the `notebooks` extension point. When opening a notebook file, the editor will send + * the `onNotebook:` activation event, and extensions must register their serializer in return. + * + * @param notebookType A notebook. + * @param serializer A notebook serializer. + * @param options Optional context options that define what parts of a notebook should be persisted + * @return A {@link Disposable} that unregisters this serializer when being disposed. + */ + export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions): Disposable; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is opened. + */ + export const onDidOpenNotebookDocument: Event; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is disposed. + * + * *Note 1:* There is no guarantee that this event fires when an editor tab is closed. + * + * *Note 2:* A notebook can be open but not shown in an editor which means this event can fire + * for a notebook that has not been shown in an editor. + */ + export const onDidCloseNotebookDocument: Event; + + /** + * An event that is emitted when files are being created. + * + * *Note 1:* This event is triggered by user gestures, like creating a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api. This event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When this event is fired, edits to files that are are being created cannot be applied. + */ + export const onWillCreateFiles: Event; + + /** + * An event that is emitted when files have been created. + * + * *Note:* This event is triggered by user gestures, like creating a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + */ + export const onDidCreateFiles: Event; + + /** + * An event that is emitted when files are being deleted. + * + * *Note 1:* This event is triggered by user gestures, like deleting a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When deleting a folder with children only one event is fired. + */ + export const onWillDeleteFiles: Event; + + /** + * An event that is emitted when files have been deleted. + * + * *Note 1:* This event is triggered by user gestures, like deleting a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When deleting a folder with children only one event is fired. + */ + export const onDidDeleteFiles: Event; + + /** + * An event that is emitted when files are being renamed. + * + * *Note 1:* This event is triggered by user gestures, like renaming a file from the + * explorer, and from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When renaming a folder with children only one event is fired. + */ + export const onWillRenameFiles: Event; + + /** + * An event that is emitted when files have been renamed. + * + * *Note 1:* This event is triggered by user gestures, like renaming a file from the + * explorer, and from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When renaming a folder with children only one event is fired. + */ + export const onDidRenameFiles: Event; + + /** + * Get a workspace configuration object. + * + * When a section-identifier is provided only that part of the configuration + * is returned. Dots in the section-identifier are interpreted as child-access, + * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. + * + * When a scope is provided configuration confined to that scope is returned. Scope can be a resource or a language identifier or both. + * + * @param section A dot-separated identifier. + * @param scope A scope for which the configuration is asked for. + * @return The full configuration or a subset. + */ + export function getConfiguration(section?: string, scope?: ConfigurationScope | null): WorkspaceConfiguration; + + /** + * An event that is emitted when the {@link WorkspaceConfiguration configuration} changed. + */ + export const onDidChangeConfiguration: Event; + + /** + * Register a task provider. + * + * @deprecated Use the corresponding function on the `tasks` namespace instead + * + * @param type The task kind type this provider is registered for. + * @param provider A task provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; + + /** + * Register a filesystem provider for a given scheme, e.g. `ftp`. + * + * There can only be one provider per scheme and an error is being thrown when a scheme + * has been claimed by another provider or when it is reserved. + * + * @param scheme The uri-{@link Uri.scheme scheme} the provider registers for. + * @param provider The filesystem provider. + * @param options Immutable metadata about the provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { readonly isCaseSensitive?: boolean; readonly isReadonly?: boolean }): Disposable; + + /** + * When true, the user has explicitly trusted the contents of the workspace. + */ + export const isTrusted: boolean; + + /** + * Event that fires when the current workspace has been trusted. + */ + export const onDidGrantWorkspaceTrust: Event; + } + + /** + * The configuration scope which can be a + * a 'resource' or a languageId or both or + * a '{@link TextDocument}' or + * a '{@link WorkspaceFolder}' + */ + export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { uri?: Uri; languageId: string }; + + /** + * An event describing the change in Configuration + */ + export interface ConfigurationChangeEvent { + + /** + * Checks if the given section has changed. + * If scope is provided, checks if the section has changed for resources under the given scope. + * + * @param section Configuration name, supports _dotted_ names. + * @param scope A scope in which to check. + * @return `true` if the given section has changed. + */ + affectsConfiguration(section: string, scope?: ConfigurationScope): boolean; + } + + /** + * Namespace for participating in language-specific editor [features](https://code.visualstudio.com/docs/editor/editingevolved), + * like IntelliSense, code actions, diagnostics etc. + * + * Many programming languages exist and there is huge variety in syntaxes, semantics, and paradigms. Despite that, features + * like automatic word-completion, code navigation, or code checking have become popular across different tools for different + * programming languages. + * + * The editor provides an API that makes it simple to provide such common features by having all UI and actions already in place and + * by allowing you to participate by providing data only. For instance, to contribute a hover all you have to do is provide a function + * that can be called with a {@link TextDocument} and a {@link Position} returning hover info. The rest, like tracking the + * mouse, positioning the hover, keeping the hover stable etc. is taken care of by the editor. + * + * ```javascript + * languages.registerHoverProvider('javascript', { + * provideHover(document, position, token) { + * return new Hover('I am a hover!'); + * } + * }); + * ``` + * + * Registration is done using a {@link DocumentSelector document selector} which is either a language id, like `javascript` or + * a more complex {@link DocumentFilter filter} like `{ language: 'typescript', scheme: 'file' }`. Matching a document against such + * a selector will result in a {@link languages.match score} that is used to determine if and how a provider shall be used. When + * scores are equal the provider that came last wins. For features that allow full arity, like {@link languages.registerHoverProvider hover}, + * the score is only checked to be `>0`, for other features, like {@link languages.registerCompletionItemProvider IntelliSense} the + * score is used for determining the order in which providers are asked to participate. + */ + export namespace languages { + + /** + * Return the identifiers of all known languages. + * @return Promise resolving to an array of identifier strings. + */ + export function getLanguages(): Thenable; + + /** + * Set (and change) the {@link TextDocument.languageId language} that is associated + * with the given document. + * + * *Note* that calling this function will trigger the {@linkcode workspace.onDidCloseTextDocument onDidCloseTextDocument} event + * followed by the {@linkcode workspace.onDidOpenTextDocument onDidOpenTextDocument} event. + * + * @param document The document which language is to be changed + * @param languageId The new language identifier. + * @returns A thenable that resolves with the updated document. + */ + export function setTextDocumentLanguage(document: TextDocument, languageId: string): Thenable; + + /** + * Compute the match between a document {@link DocumentSelector selector} and a document. Values + * greater than zero mean the selector matches the document. + * + * A match is computed according to these rules: + * 1. When {@linkcode DocumentSelector} is an array, compute the match for each contained `DocumentFilter` or language identifier and take the maximum value. + * 2. A string will be desugared to become the `language`-part of a {@linkcode DocumentFilter}, so `"fooLang"` is like `{ language: "fooLang" }`. + * 3. A {@linkcode DocumentFilter} will be matched against the document by comparing its parts with the document. The following rules apply: + * 1. When the `DocumentFilter` is empty (`{}`) the result is `0` + * 2. When `scheme`, `language`, `pattern`, or `notebook` are defined but one doesn't match, the result is `0` + * 3. Matching against `*` gives a score of `5`, matching via equality or via a glob-pattern gives a score of `10` + * 4. The result is the maximum value of each match + * + * Samples: + * ```js + * // default document from disk (file-scheme) + * doc.uri; //'file:///my/file.js' + * doc.languageId; // 'javascript' + * match('javascript', doc); // 10; + * match({ language: 'javascript' }, doc); // 10; + * match({ language: 'javascript', scheme: 'file' }, doc); // 10; + * match('*', doc); // 5 + * match('fooLang', doc); // 0 + * match(['fooLang', '*'], doc); // 5 + * + * // virtual document, e.g. from git-index + * doc.uri; // 'git:/my/file.js' + * doc.languageId; // 'javascript' + * match('javascript', doc); // 10; + * match({ language: 'javascript', scheme: 'git' }, doc); // 10; + * match('*', doc); // 5 + * + * // notebook cell document + * doc.uri; // `vscode-notebook-cell:///my/notebook.ipynb#gl65s2pmha`; + * doc.languageId; // 'python' + * match({ notebookType: 'jupyter-notebook' }, doc) // 10 + * match({ notebookType: 'fooNotebook', language: 'python' }, doc) // 0 + * match({ language: 'python' }, doc) // 10 + * match({ notebookType: '*' }, doc) // 5 + * ``` + * + * @param selector A document selector. + * @param document A text document. + * @return A number `>0` when the selector matches and `0` when the selector does not match. + */ + export function match(selector: DocumentSelector, document: TextDocument): number; + + /** + * An {@link Event} which fires when the global set of diagnostics changes. This is + * newly added and removed diagnostics. + */ + export const onDidChangeDiagnostics: Event; + + /** + * Get all diagnostics for a given resource. + * + * @param resource A resource + * @returns An array of {@link Diagnostic diagnostics} objects or an empty array. + */ + export function getDiagnostics(resource: Uri): Diagnostic[]; + + /** + * Get all diagnostics. + * + * @returns An array of uri-diagnostics tuples or an empty array. + */ + export function getDiagnostics(): [Uri, Diagnostic[]][]; + + /** + * Create a diagnostics collection. + * + * @param name The {@link DiagnosticCollection.name name} of the collection. + * @return A new diagnostic collection. + */ + export function createDiagnosticCollection(name?: string): DiagnosticCollection; + + /** + * Creates a new {@link LanguageStatusItem language status item}. + * + * @param id The identifier of the item. + * @param selector The document selector that defines for what editors the item shows. + */ + export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem; + + /** + * Register a completion provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups of equal score are sequentially asked for + * completion items. The process stops when one or many providers of a group return a + * result. A failing provider (rejected promise or exception) will not fail the whole + * operation. + * + * A completion item provider can be associated with a set of `triggerCharacters`. When trigger + * characters are being typed, completions are requested but only from providers that registered + * the typed character. Because of that trigger characters should be different than {@link LanguageConfiguration.wordPattern word characters}, + * a common trigger character is `.` to trigger member completions. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A completion provider. + * @param triggerCharacters Trigger completion when the user types one of the characters. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable; + + /** + * Registers an inline completion provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider An inline completion provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; + + /** + * Register a code action provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A code action provider. + * @param metadata Metadata about the kind of code actions the provider provides. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable; + + /** + * Register a code lens provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A code lens provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; + + /** + * Register a definition provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A definition provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable; + + /** + * Register an implementation provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider An implementation provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerImplementationProvider(selector: DocumentSelector, provider: ImplementationProvider): Disposable; + + /** + * Register a type definition provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A type definition provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; + + /** + * Register a declaration provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A declaration provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDeclarationProvider(selector: DocumentSelector, provider: DeclarationProvider): Disposable; + + /** + * Register a hover provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A hover provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable; + + /** + * Register a provider that locates evaluatable expressions in text documents. + * The editor will evaluate the expression in the active debug session and will show the result in the debug hover. + * + * If multiple providers are registered for a language an arbitrary provider will be used. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An evaluatable expression provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable; + + /** + * Register a provider that returns data for the debugger's 'inline value' feature. + * Whenever the generic debugger has stopped in a source file, providers registered for the language of the file + * are called to return textual data that will be shown in the editor at the end of lines. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider An inline values provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; + + /** + * Register a document highlight provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups sequentially asked for document highlights. + * The process stops when a provider returns a `non-falsy` or `non-failure` result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document highlight provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentHighlightProvider(selector: DocumentSelector, provider: DocumentHighlightProvider): Disposable; + + /** + * Register a document symbol provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A document symbol provider. + * @param metaData metadata about the provider + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider, metaData?: DocumentSymbolProviderMetadata): Disposable; + + /** + * Register a workspace symbol 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 provider A workspace symbol provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerWorkspaceSymbolProvider(provider: WorkspaceSymbolProvider): Disposable; + + /** + * Register a reference provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A reference provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerReferenceProvider(selector: DocumentSelector, provider: ReferenceProvider): Disposable; + + /** + * Register a rename provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and asked in sequence. The first provider producing a result + * defines the result of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A rename provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerRenameProvider(selector: DocumentSelector, provider: RenameProvider): Disposable; + + /** + * Register a semantic tokens provider for a whole document. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} 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 A document semantic tokens provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentSemanticTokensProvider(selector: DocumentSelector, provider: DocumentSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; + + /** + * Register a semantic tokens provider for a document range. + * + * *Note:* If a document has both a `DocumentSemanticTokensProvider` and a `DocumentRangeSemanticTokensProvider`, + * the range provider will be invoked only initially, for the time in which the full document provider takes + * to resolve the first request. Once the full document provider resolves the first request, the semantic tokens + * provided via the range provider will be discarded and from that point forward, only the document provider + * will be used. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} 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 A document range semantic tokens provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentRangeSemanticTokensProvider(selector: DocumentSelector, provider: DocumentRangeSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; + + /** + * Register a formatting provider for a document. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} 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 A document formatting edit provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentFormattingEditProvider(selector: DocumentSelector, provider: DocumentFormattingEditProvider): Disposable; + + /** + * Register a formatting provider for a document range. + * + * *Note:* A document range provider is also a {@link DocumentFormattingEditProvider document formatter} + * which means there is no need to {@link languages.registerDocumentFormattingEditProvider register} a document + * formatter when also registering a range provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} 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 A document range formatting edit provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentRangeFormattingEditProvider(selector: DocumentSelector, provider: DocumentRangeFormattingEditProvider): Disposable; + + /** + * Register a formatting provider that works on type. The provider is active when the user enables the setting `editor.formatOnType`. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} 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 formatting edit provider. + * @param firstTriggerCharacter A character on which formatting should be triggered, like `}`. + * @param moreTriggerCharacter More trigger characters. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerOnTypeFormattingEditProvider(selector: DocumentSelector, provider: OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacter: string[]): Disposable; + + /** + * Register a signature help provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and called sequentially until a provider returns a + * valid result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A signature help provider. + * @param triggerCharacters Trigger signature help when the user types one of the characters, like `,` or `(`. + * @param metadata Information about the provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; + export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; + + /** + * Register a document link provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A document link provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable; + + /** + * Register a color provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A color provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; + + /** + * Register a inlay hints provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider An inlay hints provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable; + + /** + * Register a folding range provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. + * If multiple folding ranges start at the same position, only the range of the first registered provider is used. + * If a folding range overlaps with an other range that has a smaller position, it is also ignored. + * + * A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A folding range provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerFoldingRangeProvider(selector: DocumentSelector, provider: FoldingRangeProvider): Disposable; + + /** + * Register a selection range provider. + * + * Multiple providers can be registered for a language. 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 selector A selector that defines the documents this provider is applicable to. + * @param provider A selection range provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; + + /** + * Register a call hierarchy provider. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A call hierarchy provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable; + + /** + * Register a type hierarchy provider. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A type hierarchy provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTypeHierarchyProvider(selector: DocumentSelector, provider: TypeHierarchyProvider): Disposable; + + /** + * Register a linked editing range provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider that has a result 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 A linked editing range provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerLinkedEditingRangeProvider(selector: DocumentSelector, provider: LinkedEditingRangeProvider): Disposable; + + /** + * Registers a new {@link DocumentDropEditProvider}. + * + * @param selector A selector that defines the documents this provider applies to. + * @param provider A drop provider. + * + * @return A {@link Disposable} that unregisters this provider when disposed of. + */ + export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider): Disposable; + + /** + * Set a {@link LanguageConfiguration language configuration} for a language. + * + * @param language A language identifier like `typescript`. + * @param configuration Language configuration. + * @return A {@link Disposable} that unsets this configuration. + */ + export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; + } + + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + */ + export enum NotebookEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 + } + + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + * Additional properties of the NotebookEditor are available in the proposed + * API, which will be finalized later. + */ + export interface NotebookEditor { + + /** + * The {@link NotebookDocument notebook document} associated with this notebook editor. + */ + readonly notebook: NotebookDocument; + + /** + * The primary selection in this notebook editor. + */ + selection: NotebookRange; + + /** + * All selections in this notebook editor. + * + * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; + */ + selections: readonly NotebookRange[]; + + /** + * The current visible ranges in the editor (vertically). + */ + readonly visibleRanges: readonly NotebookRange[]; + + /** + * The column in which this editor shows. + */ + readonly viewColumn?: ViewColumn; + + /** + * Scroll as indicated by `revealType` in order to reveal the given range. + * + * @param range A range. + * @param revealType The scrolling strategy for revealing `range`. + */ + revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; + } + + /** + * Renderer messaging is used to communicate with a single renderer. It's returned from {@link notebooks.createRendererMessaging}. + */ + export interface NotebookRendererMessaging { + /** + * An event that fires when a message is received from a renderer. + */ + readonly onDidReceiveMessage: Event<{ + readonly editor: NotebookEditor; + readonly message: any; + }>; + + /** + * Send a message to one or all renderer. + * + * @param message Message to send + * @param editor Editor to target with the message. If not provided, the + * message is sent to all renderers. + * @returns a boolean indicating whether the message was successfully + * delivered to any renderer. + */ + postMessage(message: any, editor?: NotebookEditor): Thenable; + } + + /** + * A notebook cell kind. + */ + export enum NotebookCellKind { + + /** + * A markup-cell is formatted source that is used for display. + */ + Markup = 1, + + /** + * A code-cell is source that can be {@link NotebookController executed} and that + * produces {@link NotebookCellOutput output}. + */ + Code = 2 + } + + /** + * Represents a cell of a {@link NotebookDocument notebook}, either a {@link NotebookCellKind.Code code}-cell + * or {@link NotebookCellKind.Markup markup}-cell. + * + * NotebookCell instances are immutable and are kept in sync for as long as they are part of their notebook. + */ + export interface NotebookCell { + + /** + * The index of this cell in its {@link NotebookDocument.cellAt containing notebook}. The + * index is updated when a cell is moved within its notebook. The index is `-1` + * when the cell has been removed from its notebook. + */ + readonly index: number; + + /** + * The {@link NotebookDocument notebook} that contains this cell. + */ + readonly notebook: NotebookDocument; + + /** + * The kind of this cell. + */ + readonly kind: NotebookCellKind; + + /** + * The {@link TextDocument text} of this cell, represented as text document. + */ + readonly document: TextDocument; + + /** + * The metadata of this cell. Can be anything but must be JSON-stringifyable. + */ + readonly metadata: { readonly [key: string]: any }; + + /** + * The outputs of this cell. + */ + readonly outputs: readonly NotebookCellOutput[]; + + /** + * The most recent {@link NotebookCellExecutionSummary execution summary} for this cell. + */ + readonly executionSummary: NotebookCellExecutionSummary | undefined; + } + + /** + * Represents a notebook which itself is a sequence of {@link NotebookCell code or markup cells}. Notebook documents are + * created from {@link NotebookData notebook data}. + */ + export interface NotebookDocument { + + /** + * The associated uri for this notebook. + * + * *Note* that most notebooks use the `file`-scheme, which means they are files on disk. However, **not** all notebooks are + * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. + * + * @see {@link FileSystemProvider} + */ + readonly uri: Uri; + + /** + * The type of notebook. + */ + readonly notebookType: string; + + /** + * The version number of this notebook (it will strictly increase after each + * change, including undo/redo). + */ + readonly version: number; + + /** + * `true` if there are unpersisted changes. + */ + readonly isDirty: boolean; + + /** + * Is this notebook representing an untitled file which has not been saved yet. + */ + readonly isUntitled: boolean; + + /** + * `true` if the notebook has been closed. A closed notebook isn't synchronized anymore + * and won't be re-used when the same resource is opened again. + */ + readonly isClosed: boolean; + + /** + * Arbitrary metadata for this notebook. Can be anything but must be JSON-stringifyable. + */ + readonly metadata: { [key: string]: any }; + + /** + * The number of cells in the notebook. + */ + readonly cellCount: number; + + /** + * Return the cell at the specified index. The index will be adjusted to the notebook. + * + * @param index - The index of the cell to retrieve. + * @return A {@link NotebookCell cell}. + */ + cellAt(index: number): NotebookCell; + + /** + * Get the cells of this notebook. A subset can be retrieved by providing + * a range. The range will be adjusted to the notebook. + * + * @param range A notebook range. + * @returns The cells contained by the range or all cells. + */ + getCells(range?: NotebookRange): NotebookCell[]; + + /** + * Save the document. The saving will be handled by the corresponding {@link NotebookSerializer serializer}. + * + * @return A promise that will resolve to true when the document + * has been saved. Will return false if the file was not dirty or when save failed. + */ + save(): Thenable; + } + + /** + * Describes a change to a notebook cell. + * + * @see {@link NotebookDocumentChangeEvent} + */ + export interface NotebookDocumentCellChange { + + /** + * The affected cell. + */ + readonly cell: NotebookCell; + + /** + * The document of the cell or `undefined` when it did not change. + * + * *Note* that you should use the {@link workspace.onDidChangeTextDocument onDidChangeTextDocument}-event + * for detailed change information, like what edits have been performed. + */ + readonly document: TextDocument | undefined; + + /** + * The new metadata of the cell or `undefined` when it did not change. + */ + readonly metadata: { [key: string]: any } | undefined; + + /** + * The new outputs of the cell or `undefined` when they did not change. + */ + readonly outputs: readonly NotebookCellOutput[] | undefined; + + /** + * The new execution summary of the cell or `undefined` when it did not change. + */ + readonly executionSummary: NotebookCellExecutionSummary | undefined; + } + + /** + * Describes a structural change to a notebook document, e.g newly added and removed cells. + * + * @see {@link NotebookDocumentChangeEvent} + */ + export interface NotebookDocumentContentChange { + + /** + * The range at which cells have been either added or removed. + * + * Note that no cells have been {@link NotebookDocumentContentChange.removedCells removed} + * when this range is {@link NotebookRange.isEmpty empty}. + */ + readonly range: NotebookRange; + + /** + * Cells that have been added to the document. + */ + readonly addedCells: readonly NotebookCell[]; + + /** + * Cells that have been removed from the document. + */ + readonly removedCells: readonly NotebookCell[]; + } + + /** + * An event describing a transactional {@link NotebookDocument notebook} change. + */ + export interface NotebookDocumentChangeEvent { + + /** + * The affected notebook. + */ + readonly notebook: NotebookDocument; + + /** + * The new metadata of the notebook or `undefined` when it did not change. + */ + readonly metadata: { [key: string]: any } | undefined; + + /** + * An array of content changes describing added or removed {@link NotebookCell cells}. + */ + readonly contentChanges: readonly NotebookDocumentContentChange[]; + + /** + * An array of {@link NotebookDocumentCellChange cell changes}. + */ + readonly cellChanges: readonly NotebookDocumentCellChange[]; + } + + /** + * The summary of a notebook cell execution. + */ + export interface NotebookCellExecutionSummary { + + /** + * The order in which the execution happened. + */ + readonly executionOrder?: number; + + /** + * If the execution finished successfully. + */ + readonly success?: boolean; + + /** + * The times at which execution started and ended, as unix timestamps + */ + readonly timing?: { readonly startTime: number; readonly endTime: number }; + } + + /** + * A notebook range represents an ordered pair of two cell indices. + * It is guaranteed that start is less than or equal to end. + */ + export class NotebookRange { + + /** + * The zero-based start index of this range. + */ + readonly start: number; + + /** + * The exclusive end index of this range (zero-based). + */ + readonly end: number; + + /** + * `true` if `start` and `end` are equal. + */ + readonly isEmpty: boolean; + + /** + * Create a new notebook range. If `start` is not + * before or equal to `end`, the values will be swapped. + * + * @param start start index + * @param end end index. + */ + constructor(start: number, end: number); + + /** + * Derive a new range for this range. + * + * @param change An object that describes a change to this range. + * @return A range that reflects the given change. Will return `this` range if the change + * is not changing anything. + */ + with(change: { start?: number; end?: number }): NotebookRange; + } + + /** + * One representation of a {@link NotebookCellOutput notebook output}, defined by MIME type and data. + */ + export class NotebookCellOutputItem { + + /** + * Factory function to create a `NotebookCellOutputItem` from a string. + * + * *Note* that an UTF-8 encoder is used to create bytes for the string. + * + * @param value A string. + * @param mime Optional MIME type, defaults to `text/plain`. + * @returns A new output item object. + */ + static text(value: string, mime?: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` from + * a JSON object. + * + * *Note* that this function is not expecting "stringified JSON" but + * an object that can be stringified. This function will throw an error + * when the passed value cannot be JSON-stringified. + * + * @param value A JSON-stringifyable value. + * @param mime Optional MIME type, defaults to `application/json` + * @returns A new output item object. + */ + static json(value: any, mime?: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` that uses + * uses the `application/vnd.code.notebook.stdout` mime type. + * + * @param value A string. + * @returns A new output item object. + */ + static stdout(value: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` that uses + * uses the `application/vnd.code.notebook.stderr` mime type. + * + * @param value A string. + * @returns A new output item object. + */ + static stderr(value: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` that uses + * uses the `application/vnd.code.notebook.error` mime type. + * + * @param value An error object. + * @returns A new output item object. + */ + static error(value: Error): NotebookCellOutputItem; + + /** + * The mime type which determines how the {@linkcode NotebookCellOutputItem.data data}-property + * is interpreted. + * + * Notebooks have built-in support for certain mime-types, extensions can add support for new + * types and override existing types. + */ + mime: string; + + /** + * The data of this output item. Must always be an array of unsigned 8-bit integers. + */ + data: Uint8Array; + + /** + * Create a new notebook cell output item. + * + * @param data The value of the output item. + * @param mime The mime type of the output item. + */ + constructor(data: Uint8Array, mime: string); + } + + /** + * Notebook cell output represents a result of executing a cell. It is a container type for multiple + * {@link NotebookCellOutputItem output items} where contained items represent the same result but + * use different MIME types. + */ + export class NotebookCellOutput { + + /** + * The output items of this output. Each item must represent the same result. _Note_ that repeated + * MIME types per output is invalid and that the editor will just pick one of them. + * + * ```ts + * new vscode.NotebookCellOutput([ + * vscode.NotebookCellOutputItem.text('Hello', 'text/plain'), + * vscode.NotebookCellOutputItem.text('Hello', 'text/html'), + * vscode.NotebookCellOutputItem.text('_Hello_', 'text/markdown'), + * vscode.NotebookCellOutputItem.text('Hey', 'text/plain'), // INVALID: repeated type, editor will pick just one + * ]) + * ``` + */ + items: NotebookCellOutputItem[]; + + /** + * Arbitrary metadata for this cell output. Can be anything but must be JSON-stringifyable. + */ + metadata?: { [key: string]: any }; + + /** + * Create new notebook output. + * + * @param items Notebook output items. + * @param metadata Optional metadata. + */ + constructor(items: NotebookCellOutputItem[], metadata?: { [key: string]: any }); + } + + /** + * NotebookCellData is the raw representation of notebook cells. Its is part of {@linkcode NotebookData}. + */ + export class NotebookCellData { + + /** + * The {@link NotebookCellKind kind} of this cell data. + */ + kind: NotebookCellKind; + + /** + * The source value of this cell data - either source code or formatted text. + */ + value: string; + + /** + * The language identifier of the source value of this cell data. Any value from + * {@linkcode languages.getLanguages getLanguages} is possible. + */ + languageId: string; + + /** + * The outputs of this cell data. + */ + outputs?: NotebookCellOutput[]; + + /** + * Arbitrary metadata of this cell data. Can be anything but must be JSON-stringifyable. + */ + metadata?: { [key: string]: any }; + + /** + * The execution summary of this cell data. + */ + executionSummary?: NotebookCellExecutionSummary; + + /** + * Create new cell data. Minimal cell data specifies its kind, its source value, and the + * language identifier of its source. + * + * @param kind The kind. + * @param value The source value. + * @param languageId The language identifier of the source value. + */ + constructor(kind: NotebookCellKind, value: string, languageId: string); + } + + /** + * Raw representation of a notebook. + * + * Extensions are responsible for creating {@linkcode NotebookData} so that the editor + * can create a {@linkcode NotebookDocument}. + * + * @see {@link NotebookSerializer} + */ + export class NotebookData { + /** + * The cell data of this notebook data. + */ + cells: NotebookCellData[]; + + /** + * Arbitrary metadata of notebook data. + */ + metadata?: { [key: string]: any }; + + /** + * Create new notebook data. + * + * @param cells An array of cell data. + */ + constructor(cells: NotebookCellData[]); + } + + /** + * The notebook serializer enables the editor to open notebook files. + * + * At its core the editor only knows a {@link NotebookData notebook data structure} but not + * how that data structure is written to a file, nor how it is read from a file. The + * notebook serializer bridges this gap by deserializing bytes into notebook data and + * vice versa. + */ + export interface NotebookSerializer { + + /** + * Deserialize contents of a notebook file into the notebook data structure. + * + * @param content Contents of a notebook file. + * @param token A cancellation token. + * @return Notebook data or a thenable that resolves to such. + */ + deserializeNotebook(content: Uint8Array, token: CancellationToken): NotebookData | Thenable; + + /** + * Serialize notebook data into file contents. + * + * @param data A notebook data structure. + * @param token A cancellation token. + * @returns An array of bytes or a thenable that resolves to such. + */ + serializeNotebook(data: NotebookData, token: CancellationToken): Uint8Array | Thenable; + } + + /** + * Notebook content options define what parts of a notebook are persisted. Note + * + * For instance, a notebook serializer can opt-out of saving outputs and in that case the editor doesn't mark a + * notebooks as {@link NotebookDocument.isDirty dirty} when its output has changed. + */ + export interface NotebookDocumentContentOptions { + /** + * Controls if output change events will trigger notebook document content change events and + * if it will be used in the diff editor, defaults to false. If the content provider doesn't + * persist the outputs in the file document, this should be set to true. + */ + transientOutputs?: boolean; + + /** + * Controls if a cell metadata property change event will trigger notebook document content + * change events and if it will be used in the diff editor, defaults to false. If the + * content provider doesn't persist a metadata property in the file document, it should be + * set to true. + */ + transientCellMetadata?: { [key: string]: boolean | undefined }; + + /** + * Controls if a document metadata property change event will trigger notebook document + * content change event and if it will be used in the diff editor, defaults to false. If the + * content provider doesn't persist a metadata property in the file document, it should be + * set to true. + */ + transientDocumentMetadata?: { [key: string]: boolean | undefined }; + } + + /** + * Notebook controller affinity for notebook documents. + * + * @see {@link NotebookController.updateNotebookAffinity} + */ + export enum NotebookControllerAffinity { + /** + * Default affinity. + */ + Default = 1, + /** + * A controller is preferred for a notebook. + */ + Preferred = 2 + } + + /** + * A notebook controller represents an entity that can execute notebook cells. This is often referred to as a kernel. + * + * There can be multiple controllers and the editor will let users choose which controller to use for a certain notebook. The + * {@linkcode NotebookController.notebookType notebookType}-property defines for what kind of notebooks a controller is for and + * the {@linkcode NotebookController.updateNotebookAffinity updateNotebookAffinity}-function allows controllers to set a preference + * for specific notebook documents. When a controller has been selected its + * {@link NotebookController.onDidChangeSelectedNotebooks onDidChangeSelectedNotebooks}-event fires. + * + * When a cell is being run the editor will invoke the {@linkcode NotebookController.executeHandler executeHandler} and a controller + * is expected to create and finalize a {@link NotebookCellExecution notebook cell execution}. However, controllers are also free + * to create executions by themselves. + */ + export interface NotebookController { + + /** + * The identifier of this notebook controller. + * + * _Note_ that controllers are remembered by their identifier and that extensions should use + * stable identifiers across sessions. + */ + readonly id: string; + + /** + * The notebook type this controller is for. + */ + readonly notebookType: string; + + /** + * An array of language identifiers that are supported by this + * controller. Any language identifier from {@linkcode languages.getLanguages getLanguages} + * is possible. When falsy all languages are supported. + * + * Samples: + * ```js + * // support JavaScript and TypeScript + * myController.supportedLanguages = ['javascript', 'typescript'] + * + * // support all languages + * myController.supportedLanguages = undefined; // falsy + * myController.supportedLanguages = []; // falsy + * ``` + */ + supportedLanguages?: string[]; + + /** + * The human-readable label of this notebook controller. + */ + label: string; + + /** + * The human-readable description which is rendered less prominent. + */ + description?: string; + + /** + * The human-readable detail which is rendered less prominent. + */ + detail?: string; + + /** + * Whether this controller supports execution order so that the + * editor can render placeholders for them. + */ + supportsExecutionOrder?: boolean; + + /** + * Create a cell execution task. + * + * _Note_ that there can only be one execution per cell at a time and that an error is thrown if + * a cell execution is created while another is still active. + * + * This should be used in response to the {@link NotebookController.executeHandler execution handler} + * being called or when cell execution has been started else, e.g when a cell was already + * executing or when cell execution was triggered from another source. + * + * @param cell The notebook cell for which to create the execution. + * @returns A notebook cell execution. + */ + createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution; + + /** + * The execute handler is invoked when the run gestures in the UI are selected, e.g Run Cell, Run All, + * Run Selection etc. The execute handler is responsible for creating and managing {@link NotebookCellExecution execution}-objects. + */ + executeHandler: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable; + + /** + * Optional interrupt handler. + * + * By default cell execution is canceled via {@link NotebookCellExecution.token tokens}. Cancellation + * tokens require that a controller can keep track of its execution so that it can cancel a specific execution at a later + * point. Not all scenarios allow for that, eg. REPL-style controllers often work by interrupting whatever is currently + * running. For those cases the interrupt handler exists - it can be thought of as the equivalent of `SIGINT` + * or `Control+C` in terminals. + * + * _Note_ that supporting {@link NotebookCellExecution.token cancellation tokens} is preferred and that interrupt handlers should + * only be used when tokens cannot be supported. + */ + interruptHandler?: (notebook: NotebookDocument) => void | Thenable; + + /** + * An event that fires whenever a controller has been selected or un-selected for a notebook document. + * + * There can be multiple controllers for a notebook and in that case a controllers needs to be _selected_. This is a user gesture + * and happens either explicitly or implicitly when interacting with a notebook for which a controller was _suggested_. When possible, + * the editor _suggests_ a controller that is most likely to be _selected_. + * + * _Note_ that controller selection is persisted (by the controllers {@link NotebookController.id id}) and restored as soon as a + * controller is re-created or as a notebook is {@link workspace.onDidOpenNotebookDocument opened}. + */ + readonly onDidChangeSelectedNotebooks: Event<{ readonly notebook: NotebookDocument; readonly selected: boolean }>; + + /** + * A controller can set affinities for specific notebook documents. This allows a controller + * to be presented more prominent for some notebooks. + * + * @param notebook The notebook for which a priority is set. + * @param affinity A controller affinity + */ + updateNotebookAffinity(notebook: NotebookDocument, affinity: NotebookControllerAffinity): void; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * A NotebookCellExecution is how {@link NotebookController notebook controller} modify a notebook cell as + * it is executing. + * + * When a cell execution object is created, the cell enters the {@linkcode NotebookCellExecutionState.Pending Pending} state. + * When {@linkcode NotebookCellExecution.start start(...)} is called on the execution task, it enters the {@linkcode NotebookCellExecutionState.Executing Executing} state. When + * {@linkcode NotebookCellExecution.end end(...)} is called, it enters the {@linkcode NotebookCellExecutionState.Idle Idle} state. + */ + export interface NotebookCellExecution { + + /** + * The {@link NotebookCell cell} for which this execution has been created. + */ + readonly cell: NotebookCell; + + /** + * A cancellation token which will be triggered when the cell execution is canceled + * from the UI. + * + * _Note_ that the cancellation token will not be triggered when the {@link NotebookController controller} + * that created this execution uses an {@link NotebookController.interruptHandler interrupt-handler}. + */ + readonly token: CancellationToken; + + /** + * Set and unset the order of this cell execution. + */ + executionOrder: number | undefined; + + /** + * Signal that the execution has begun. + * + * @param startTime The time that execution began, in milliseconds in the Unix epoch. Used to drive the clock + * that shows for how long a cell has been running. If not given, the clock won't be shown. + */ + start(startTime?: number): void; + + /** + * Signal that execution has ended. + * + * @param success If true, a green check is shown on the cell status bar. + * If false, a red X is shown. + * If undefined, no check or X icon is shown. + * @param endTime The time that execution finished, in milliseconds in the Unix epoch. + */ + end(success: boolean | undefined, endTime?: number): void; + + /** + * Clears the output of the cell that is executing or of another cell that is affected by this execution. + * + * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of + * this execution. + * @return A thenable that resolves when the operation finished. + */ + clearOutput(cell?: NotebookCell): Thenable; + + /** + * Replace the output of the cell that is executing or of another cell that is affected by this execution. + * + * @param out Output that replaces the current output. + * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of + * this execution. + * @return A thenable that resolves when the operation finished. + */ + replaceOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; + + /** + * Append to the output of the cell that is executing or to another cell that is affected by this execution. + * + * @param out Output that is appended to the current output. + * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of + * this execution. + * @return A thenable that resolves when the operation finished. + */ + appendOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; + + /** + * Replace all output items of existing cell output. + * + * @param items Output items that replace the items of existing output. + * @param output Output object that already exists. + * @return A thenable that resolves when the operation finished. + */ + replaceOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; + + /** + * Append output items to existing cell output. + * + * @param items Output items that are append to existing output. + * @param output Output object that already exists. + * @return A thenable that resolves when the operation finished. + */ + appendOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; + } + + /** + * Represents the alignment of status bar items. + */ + export enum NotebookCellStatusBarAlignment { + + /** + * Aligned to the left side. + */ + Left = 1, + + /** + * Aligned to the right side. + */ + Right = 2 + } + + /** + * A contribution to a cell's status bar + */ + export class NotebookCellStatusBarItem { + /** + * The text to show for the item. + */ + text: string; + + /** + * Whether the item is aligned to the left or right. + */ + alignment: NotebookCellStatusBarAlignment; + + /** + * An optional {@linkcode Command} or identifier of a command to run on click. + * + * The command must be {@link commands.getCommands known}. + * + * Note that if this is a {@linkcode Command} object, only the {@linkcode Command.command command} and {@linkcode Command.arguments arguments} + * are used by the editor. + */ + command?: string | Command; + + /** + * A tooltip to show when the item is hovered. + */ + tooltip?: string; + + /** + * The priority of the item. A higher value item will be shown more to the left. + */ + priority?: number; + + /** + * Accessibility information used when a screen reader interacts with this item. + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * Creates a new NotebookCellStatusBarItem. + * @param text The text to show for the item. + * @param alignment Whether the item is aligned to the left or right. + */ + constructor(text: string, alignment: NotebookCellStatusBarAlignment); + } + + /** + * A provider that can contribute items to the status bar that appears below a cell's editor. + */ + export interface NotebookCellStatusBarItemProvider { + /** + * An optional event to signal that statusbar items have changed. The provide method will be called again. + */ + onDidChangeCellStatusBarItems?: Event; + + /** + * The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state. + * @param cell The cell for which to return items. + * @param token A token triggered if this request should be cancelled. + * @return One or more {@link NotebookCellStatusBarItem cell statusbar items} + */ + provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult; + } + + /** + * Namespace for notebooks. + * + * The notebooks functionality is composed of three loosely coupled components: + * + * 1. {@link NotebookSerializer} enable the editor to open, show, and save notebooks + * 2. {@link NotebookController} own the execution of notebooks, e.g they create output from code cells. + * 3. NotebookRenderer present notebook output in the editor. They run in a separate context. + */ + export namespace notebooks { + + /** + * Creates a new notebook controller. + * + * @param id Identifier of the controller. Must be unique per extension. + * @param notebookType A notebook type for which this controller is for. + * @param label The label of the controller. + * @param handler The execute-handler of the controller. + */ + export function createNotebookController(id: string, notebookType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable): NotebookController; + + /** + * Register a {@link NotebookCellStatusBarItemProvider cell statusbar item provider} for the given notebook type. + * + * @param notebookType The notebook type to register for. + * @param provider A cell status bar provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable; + + /** + * Creates a new messaging instance used to communicate with a specific renderer. + * + * * *Note 1:* Extensions can only create renderer that they have defined in their `package.json`-file + * * *Note 2:* A renderer only has access to messaging if `requiresMessaging` is set to `always` or `optional` in + * its `notebookRenderer` contribution. + * + * @param rendererId The renderer ID to communicate with + * @returns A new notebook renderer messaging object. + */ + export function createRendererMessaging(rendererId: string): NotebookRendererMessaging; + } + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Setter and getter for the contents of the input box. + */ + value: string; + + /** + * A string to show as placeholder in the input box to guide the user. + */ + placeholder: string; + + /** + * Controls whether the input box is enabled (default is `true`). + */ + enabled: boolean; + + /** + * Controls whether the input box is visible (default is `true`). + */ + visible: boolean; + } + + interface QuickDiffProvider { + + /** + * Provide a {@link Uri} to the original resource of any given resource uri. + * + * @param uri The uri of the resource open in a text editor. + * @param token A cancellation token. + * @return A thenable that resolves to uri of the matching original resource. + */ + provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; + } + + /** + * The theme-aware decorations for a + * {@link SourceControlResourceState source control resource state}. + */ + export interface SourceControlResourceThemableDecorations { + + /** + * The icon path for a specific + * {@link SourceControlResourceState source control resource state}. + */ + readonly iconPath?: string | Uri | ThemeIcon; + } + + /** + * The decorations for a {@link SourceControlResourceState source control resource state}. + * Can be independently specified for light and dark themes. + */ + export interface SourceControlResourceDecorations extends SourceControlResourceThemableDecorations { + + /** + * Whether the {@link SourceControlResourceState source control resource state} should + * be striked-through in the UI. + */ + readonly strikeThrough?: boolean; + + /** + * Whether the {@link SourceControlResourceState source control resource state} should + * be faded in the UI. + */ + readonly faded?: boolean; + + /** + * The title for a specific + * {@link SourceControlResourceState source control resource state}. + */ + readonly tooltip?: string; + + /** + * The light theme decorations. + */ + readonly light?: SourceControlResourceThemableDecorations; + + /** + * The dark theme decorations. + */ + readonly dark?: SourceControlResourceThemableDecorations; + } + + /** + * An source control resource state represents the state of an underlying workspace + * resource within a certain {@link SourceControlResourceGroup source control group}. + */ + export interface SourceControlResourceState { + + /** + * The {@link Uri} of the underlying resource inside the workspace. + */ + readonly resourceUri: Uri; + + /** + * The {@link Command} which should be run when the resource + * state is open in the Source Control viewlet. + */ + readonly command?: Command; + + /** + * The {@link SourceControlResourceDecorations decorations} for this source control + * resource state. + */ + readonly decorations?: SourceControlResourceDecorations; + + /** + * Context value of the resource state. This can be used to contribute resource specific actions. + * For example, if a resource is given a context value as `diffable`. When contributing actions to `scm/resourceState/context` + * using `menus` extension point, you can specify context value for key `scmResourceState` in `when` expressions, like `scmResourceState == diffable`. + * ```json + * "contributes": { + * "menus": { + * "scm/resourceState/context": [ + * { + * "command": "extension.diff", + * "when": "scmResourceState == diffable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.diff` only for resources with `contextValue` is `diffable`. + */ + readonly contextValue?: string; + } + + /** + * A source control resource group is a collection of + * {@link SourceControlResourceState source control resource states}. + */ + export interface SourceControlResourceGroup { + + /** + * The id of this source control resource group. + */ + readonly id: string; + + /** + * The label of this source control resource group. + */ + label: string; + + /** + * Whether this source control resource group is hidden when it contains + * no {@link SourceControlResourceState source control resource states}. + */ + hideWhenEmpty?: boolean; + + /** + * This group's collection of + * {@link SourceControlResourceState source control resource states}. + */ + resourceStates: SourceControlResourceState[]; + + /** + * Dispose this source control resource group. + */ + dispose(): void; + } + + /** + * An source control is able to provide {@link SourceControlResourceState resource states} + * to the editor and interact with the editor in several source control related ways. + */ + export interface SourceControl { + + /** + * The id of this source control. + */ + readonly id: string; + + /** + * The human-readable label of this source control. + */ + readonly label: string; + + /** + * The (optional) Uri of the root of this source control. + */ + readonly rootUri: Uri | undefined; + + /** + * The {@link SourceControlInputBox input box} for this source control. + */ + readonly inputBox: SourceControlInputBox; + + /** + * The UI-visible count of {@link SourceControlResourceState resource states} of + * this source control. + * + * If undefined, this source control will + * - display its UI-visible count as zero, and + * - contribute the count of its {@link SourceControlResourceState resource states} to the UI-visible aggregated count for all source controls + */ + count?: number; + + /** + * An optional {@link QuickDiffProvider quick diff provider}. + */ + quickDiffProvider?: QuickDiffProvider; + + /** + * Optional commit template string. + * + * The Source Control viewlet will populate the Source Control + * input with this value when appropriate. + */ + commitTemplate?: string; + + /** + * Optional accept input command. + * + * This command will be invoked when the user accepts the value + * in the Source Control input. + */ + acceptInputCommand?: Command; + + /** + * Optional status bar commands. + * + * These commands will be displayed in the editor's status bar. + */ + statusBarCommands?: Command[]; + + /** + * Create a new {@link SourceControlResourceGroup resource group}. + */ + createResourceGroup(id: string, label: string): SourceControlResourceGroup; + + /** + * Dispose this source control. + */ + dispose(): void; + } + + export namespace scm { + + /** + * The {@link SourceControlInputBox input box} for the last source control + * created by the extension. + * + * @deprecated Use SourceControl.inputBox instead + */ + export const inputBox: SourceControlInputBox; + + /** + * Creates a new {@link SourceControl source control} instance. + * + * @param id An `id` for the source control. Something short, e.g.: `git`. + * @param label A human-readable string for the source control. E.g.: `Git`. + * @param rootUri An optional Uri of the root of the source control. E.g.: `Uri.parse(workspaceRoot)`. + * @return An instance of {@link SourceControl source control}. + */ + export function createSourceControl(id: string, label: string, rootUri?: Uri): SourceControl; + } + + /** + * A DebugProtocolMessage is an opaque stand-in type for the [ProtocolMessage](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolMessage { + // Properties: see [ProtocolMessage details](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage). + } + + /** + * A DebugProtocolSource is an opaque stand-in type for the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolSource { + // Properties: see [Source details](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source). + } + + /** + * A DebugProtocolBreakpoint is an opaque stand-in type for the [Breakpoint](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolBreakpoint { + // Properties: see [Breakpoint details](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint). + } + + /** + * Configuration for a debug session. + */ + export interface DebugConfiguration { + /** + * The type of the debug session. + */ + type: string; + + /** + * The name of the debug session. + */ + name: string; + + /** + * The request type of the debug session. + */ + request: string; + + /** + * Additional debug type specific properties. + */ + [key: string]: any; + } + + /** + * A debug session. + */ + export interface DebugSession { + + /** + * The unique ID of this debug session. + */ + readonly id: string; + + /** + * The debug session's type from the {@link DebugConfiguration debug configuration}. + */ + readonly type: string; + + /** + * The parent session of this debug session, if it was created as a child. + * @see DebugSessionOptions.parentSession + */ + readonly parentSession?: DebugSession; + + /** + * The debug session's name is initially taken from the {@link DebugConfiguration debug configuration}. + * Any changes will be properly reflected in the UI. + */ + name: string; + + /** + * The workspace folder of this session or `undefined` for a folderless setup. + */ + readonly workspaceFolder: WorkspaceFolder | undefined; + + /** + * The "resolved" {@link DebugConfiguration debug configuration} of this session. + * "Resolved" means that + * - all variables have been substituted and + * - platform specific attribute sections have been "flattened" for the matching platform and removed for non-matching platforms. + */ + readonly configuration: DebugConfiguration; + + /** + * Send a custom request to the debug adapter. + */ + customRequest(command: string, args?: any): Thenable; + + /** + * Maps a breakpoint in the editor to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session. + * If no DAP breakpoint exists (either because the editor breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned. + * + * @param breakpoint A {@link Breakpoint} in the editor. + * @return A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. + */ + getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable; + } + + /** + * A custom Debug Adapter Protocol event received from a {@link DebugSession debug session}. + */ + export interface DebugSessionCustomEvent { + /** + * The {@link DebugSession debug session} for which the custom event was received. + */ + readonly session: DebugSession; + + /** + * Type of event. + */ + readonly event: string; + + /** + * Event specific information. + */ + readonly body: any; + } + + /** + * A debug configuration provider allows to add debug configurations to the debug service + * and to resolve launch configurations before they are used to start a debug session. + * A debug configuration provider is registered via {@link debug.registerDebugConfigurationProvider}. + */ + export interface DebugConfigurationProvider { + /** + * Provides {@link DebugConfiguration debug configuration} to the debug service. If more than one debug configuration provider is + * registered for the same type, debug configurations are concatenated in arbitrary order. + * + * @param folder The workspace folder for which the configurations are used or `undefined` for a folderless setup. + * @param token A cancellation token. + * @return An array of {@link DebugConfiguration debug configurations}. + */ + provideDebugConfigurations?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; + + /** + * Resolves a {@link DebugConfiguration debug configuration} by filling in missing values or by adding/changing/removing attributes. + * If more than one debug configuration provider is registered for the same type, the resolveDebugConfiguration calls are chained + * in arbitrary order and the initial debug configuration is piped through the chain. + * Returning the value 'undefined' prevents the debug session from starting. + * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. + * + * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. + * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. + * @param token A cancellation token. + * @return The resolved debug configuration or undefined or null. + */ + resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; + + /** + * This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted. + * It can be used to resolve or verify a {@link DebugConfiguration debug configuration} by filling in missing values or by adding/changing/removing attributes. + * If more than one debug configuration provider is registered for the same type, the 'resolveDebugConfigurationWithSubstitutedVariables' calls are chained + * in arbitrary order and the initial debug configuration is piped through the chain. + * Returning the value 'undefined' prevents the debug session from starting. + * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. + * + * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. + * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. + * @param token A cancellation token. + * @return The resolved debug configuration or undefined or null. + */ + resolveDebugConfigurationWithSubstitutedVariables?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; + } + + /** + * Represents a debug adapter executable and optional arguments and runtime options passed to it. + */ + export class DebugAdapterExecutable { + + /** + * Creates a description for a debug adapter based on an executable program. + * + * @param command The command or executable path that implements the debug adapter. + * @param args Optional arguments to be passed to the command or executable. + * @param options Optional options to be used when starting the command or executable. + */ + constructor(command: string, args?: string[], options?: DebugAdapterExecutableOptions); + + /** + * The command or path of the debug adapter executable. + * A command must be either an absolute path of an executable or the name of an command to be looked up via the PATH environment variable. + * The special value 'node' will be mapped to the editor's built-in Node.js runtime. + */ + readonly command: string; + + /** + * The arguments passed to the debug adapter executable. Defaults to an empty array. + */ + readonly args: string[]; + + /** + * Optional options to be used when the debug adapter is started. + * Defaults to undefined. + */ + readonly options?: DebugAdapterExecutableOptions; + } + + /** + * Options for a debug adapter executable. + */ + export interface DebugAdapterExecutableOptions { + + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + + /** + * The current working directory for the executed debug adapter. + */ + cwd?: string; + } + + /** + * Represents a debug adapter running as a socket based server. + */ + export class DebugAdapterServer { + + /** + * The port. + */ + readonly port: number; + + /** + * The host. + */ + readonly host?: string | undefined; + + /** + * Create a description for a debug adapter running as a socket based server. + */ + constructor(port: number, host?: string); + } + + /** + * Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. + */ + export class DebugAdapterNamedPipeServer { + /** + * The path to the NamedPipe/UNIX Domain Socket. + */ + readonly path: string; + + /** + * Create a description for a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. + */ + constructor(path: string); + } + + /** + * A debug adapter that implements the Debug Adapter Protocol can be registered with the editor if it implements the DebugAdapter interface. + */ + export interface DebugAdapter extends Disposable { + + /** + * An event which fires after the debug adapter has sent a Debug Adapter Protocol message to the editor. + * Messages can be requests, responses, or events. + */ + readonly onDidSendMessage: Event; + + /** + * Handle a Debug Adapter Protocol message. + * Messages can be requests, responses, or events. + * Results or errors are returned via onSendMessage events. + * @param message A Debug Adapter Protocol message + */ + handleMessage(message: DebugProtocolMessage): void; + } + + /** + * A debug adapter descriptor for an inline implementation. + */ + export class DebugAdapterInlineImplementation { + + /** + * Create a descriptor for an inline implementation of a debug adapter. + */ + constructor(implementation: DebugAdapter); + } + + export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation; + + export interface DebugAdapterDescriptorFactory { + /** + * 'createDebugAdapterDescriptor' is called at the start of a debug session to provide details about the debug adapter to use. + * These details must be returned as objects of type {@link DebugAdapterDescriptor}. + * Currently two types of debug adapters are supported: + * - a debug adapter executable is specified as a command path and arguments (see {@link DebugAdapterExecutable}), + * - a debug adapter server reachable via a communication port (see {@link DebugAdapterServer}). + * If the method is not implemented the default behavior is this: + * createDebugAdapter(session: DebugSession, executable: DebugAdapterExecutable) { + * if (typeof session.configuration.debugServer === 'number') { + * return new DebugAdapterServer(session.configuration.debugServer); + * } + * return executable; + * } + * @param session The {@link DebugSession debug session} for which the debug adapter will be used. + * @param executable The debug adapter's executable information as specified in the package.json (or undefined if no such information exists). + * @return a {@link DebugAdapterDescriptor debug adapter descriptor} or undefined. + */ + createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): ProviderResult; + } + + /** + * A Debug Adapter Tracker is a means to track the communication between the editor and a Debug Adapter. + */ + export interface DebugAdapterTracker { + /** + * A session with the debug adapter is about to be started. + */ + onWillStartSession?(): void; + /** + * The debug adapter is about to receive a Debug Adapter Protocol message from the editor. + */ + onWillReceiveMessage?(message: any): void; + /** + * The debug adapter has sent a Debug Adapter Protocol message to the editor. + */ + onDidSendMessage?(message: any): void; + /** + * The debug adapter session is about to be stopped. + */ + onWillStopSession?(): void; + /** + * An error with the debug adapter has occurred. + */ + onError?(error: Error): void; + /** + * The debug adapter has exited with the given exit code or signal. + */ + onExit?(code: number | undefined, signal: string | undefined): void; + } + + export interface DebugAdapterTrackerFactory { + /** + * The method 'createDebugAdapterTracker' is called at the start of a debug session in order + * to return a "tracker" object that provides read-access to the communication between the editor and a debug adapter. + * + * @param session The {@link DebugSession debug session} for which the debug adapter tracker will be used. + * @return A {@link DebugAdapterTracker debug adapter tracker} or undefined. + */ + createDebugAdapterTracker(session: DebugSession): ProviderResult; + } + + /** + * Represents the debug console. + */ + export interface DebugConsole { + /** + * Append the given value to the debug console. + * + * @param value A string, falsy values will not be printed. + */ + append(value: string): void; + + /** + * Append the given value and a line feed character + * to the debug console. + * + * @param value A string, falsy values will be printed. + */ + appendLine(value: string): void; + } + + /** + * An event describing the changes to the set of {@link Breakpoint breakpoints}. + */ + export interface BreakpointsChangeEvent { + /** + * Added breakpoints. + */ + readonly added: readonly Breakpoint[]; + + /** + * Removed breakpoints. + */ + readonly removed: readonly Breakpoint[]; + + /** + * Changed breakpoints. + */ + readonly changed: readonly Breakpoint[]; + } + + /** + * The base class of all breakpoint types. + */ + export class Breakpoint { + /** + * The unique ID of the breakpoint. + */ + readonly id: string; + /** + * Is breakpoint enabled. + */ + readonly enabled: boolean; + /** + * An optional expression for conditional breakpoints. + */ + readonly condition?: string | undefined; + /** + * An optional expression that controls how many hits of the breakpoint are ignored. + */ + readonly hitCondition?: string | undefined; + /** + * An optional message that gets logged when this breakpoint is hit. Embedded expressions within {} are interpolated by the debug adapter. + */ + readonly logMessage?: string | undefined; + + protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); + } + + /** + * A breakpoint specified by a source location. + */ + export class SourceBreakpoint extends Breakpoint { + /** + * The source and line position of this breakpoint. + */ + readonly location: Location; + + /** + * Create a new breakpoint for a source location. + */ + constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); + } + + /** + * A breakpoint specified by a function name. + */ + export class FunctionBreakpoint extends Breakpoint { + /** + * The name of the function to which this breakpoint is attached. + */ + readonly functionName: string; + + /** + * Create a new function breakpoint. + */ + constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); + } + + /** + * Debug console mode used by debug session, see {@link DebugSessionOptions options}. + */ + export enum DebugConsoleMode { + /** + * Debug session should have a separate debug console. + */ + Separate = 0, + + /** + * Debug session should share debug console with its parent session. + * This value has no effect for sessions which do not have a parent session. + */ + MergeWithParent = 1 + } + + /** + * Options for {@link debug.startDebugging starting a debug session}. + */ + export interface DebugSessionOptions { + + /** + * When specified the newly created debug session is registered as a "child" session of this + * "parent" debug session. + */ + parentSession?: DebugSession; + + /** + * Controls whether lifecycle requests like 'restart' are sent to the newly created session or its parent session. + * By default (if the property is false or missing), lifecycle requests are sent to the new session. + * This property is ignored if the session has no parent session. + */ + lifecycleManagedByParent?: boolean; + + /** + * Controls whether this session should have a separate debug console or share it + * with the parent session. Has no effect for sessions which do not have a parent session. + * Defaults to Separate. + */ + consoleMode?: DebugConsoleMode; + + /** + * Controls whether this session should run without debugging, thus ignoring breakpoints. + * When this property is not specified, the value from the parent session (if there is one) is used. + */ + noDebug?: boolean; + + /** + * Controls if the debug session's parent session is shown in the CALL STACK view even if it has only a single child. + * By default, the debug session will never hide its parent. + * If compact is true, debug sessions with a single child are hidden in the CALL STACK view to make the tree more compact. + */ + compact?: boolean; + + /** + * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. + */ + suppressSaveBeforeStart?: boolean; + + /** + * When true, the debug toolbar will not be shown for this session. + */ + suppressDebugToolbar?: boolean; + + /** + * When true, the window statusbar color will not be changed for this session. + */ + suppressDebugStatusbar?: boolean; + + /** + * When true, the debug viewlet will not be automatically revealed for this session. + */ + suppressDebugView?: boolean; + } + + /** + * 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 {@link 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 + } + + /** + * Namespace for debug functionality. + */ + export namespace debug { + + /** + * The currently active {@link DebugSession debug session} or `undefined`. The active debug session is the one + * represented by the debug action floating window or the one currently shown in the drop down menu of the debug action floating window. + * If no debug session is active, the value is `undefined`. + */ + export let activeDebugSession: DebugSession | undefined; + + /** + * The currently active {@link DebugConsole debug console}. + * If no debug session is active, output sent to the debug console is not shown. + */ + export let activeDebugConsole: DebugConsole; + + /** + * List of breakpoints. + */ + export let breakpoints: readonly Breakpoint[]; + + /** + * An {@link Event} which fires when the {@link debug.activeDebugSession active debug session} + * has changed. *Note* that the event also fires when the active debug session changes + * to `undefined`. + */ + export const onDidChangeActiveDebugSession: Event; + + /** + * An {@link Event} which fires when a new {@link DebugSession debug session} has been started. + */ + export const onDidStartDebugSession: Event; + + /** + * An {@link Event} which fires when a custom DAP event is received from the {@link DebugSession debug session}. + */ + export const onDidReceiveDebugSessionCustomEvent: Event; + + /** + * An {@link Event} which fires when a {@link DebugSession debug session} has terminated. + */ + export const onDidTerminateDebugSession: Event; + + /** + * An {@link Event} that is emitted when the set of breakpoints is added, removed, or changed. + */ + export const onDidChangeBreakpoints: Event; + + /** + * Register a {@link DebugConfigurationProvider debug configuration provider} for a specific debug type. + * The optional {@link DebugConfigurationProviderTriggerKind triggerKind} 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 debugType The debug type for which the provider is registered. + * @param provider The {@link DebugConfigurationProvider debug configuration provider} to register. + * @param triggerKind The {@link DebugConfigurationProviderTrigger trigger} for which the 'provideDebugConfiguration' method of the provider is registered. If `triggerKind` is missing, the value `DebugConfigurationProviderTriggerKind.Initial` is assumed. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable; + + /** + * Register a {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} for a specific debug type. + * An extension is only allowed to register a DebugAdapterDescriptorFactory for the debug type(s) defined by the extension. Otherwise an error is thrown. + * Registering more than one DebugAdapterDescriptorFactory for a debug type results in an error. + * + * @param debugType The debug type for which the factory is registered. + * @param factory The {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} to register. + * @return A {@link Disposable} that unregisters this factory when being disposed. + */ + export function registerDebugAdapterDescriptorFactory(debugType: string, factory: DebugAdapterDescriptorFactory): Disposable; + + /** + * Register a debug adapter tracker factory for the given debug type. + * + * @param debugType The debug type for which the factory is registered or '*' for matching all debug types. + * @param factory The {@link DebugAdapterTrackerFactory debug adapter tracker factory} to register. + * @return A {@link Disposable} that unregisters this factory when being disposed. + */ + export function registerDebugAdapterTrackerFactory(debugType: string, factory: DebugAdapterTrackerFactory): Disposable; + + /** + * Start debugging by using either a named launch or named compound configuration, + * or by directly passing a {@link DebugConfiguration}. + * The named configurations are looked up in '.vscode/launch.json' found in the given folder. + * Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date. + * Folder specific variables used in the configuration (e.g. '${workspaceFolder}') are resolved against the given folder. + * @param folder The {@link WorkspaceFolder workspace folder} for looking up named configurations and resolving variables or `undefined` for a non-folder setup. + * @param nameOrConfiguration Either the name of a debug or compound configuration or a {@link DebugConfiguration} object. + * @param parentSessionOrOptions Debug session options. When passed a parent {@link DebugSession debug session}, assumes options with just this parent session. + * @return A thenable that resolves when debugging could be successfully started. + */ + export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable; + + /** + * Stop the given debug session or stop all debug sessions if session is omitted. + * @param session The {@link DebugSession debug session} to stop; if omitted all sessions are stopped. + */ + export function stopDebugging(session?: DebugSession): Thenable; + + /** + * Add breakpoints. + * @param breakpoints The breakpoints to add. + */ + export function addBreakpoints(breakpoints: readonly Breakpoint[]): void; + + /** + * Remove breakpoints. + * @param breakpoints The breakpoints to remove. + */ + export function removeBreakpoints(breakpoints: readonly Breakpoint[]): void; + + /** + * Converts a "Source" descriptor object received via the Debug Adapter Protocol into a Uri that can be used to load its contents. + * If the source descriptor is based on a path, a file Uri is returned. + * If the source descriptor uses a reference number, a specific debug Uri (scheme 'debug') is constructed that requires a corresponding ContentProvider and a running debug session + * + * If the "Source" descriptor has insufficient information for creating the Uri, an error is thrown. + * + * @param source An object conforming to the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. + * @param session An optional debug session that will be used when the source descriptor uses a reference number to load the contents from an active debug session. + * @return A uri that can be used to load the contents of the source. + */ + export function asDebugSourceUri(source: DebugProtocolSource, session?: DebugSession): Uri; + } + + /** + * Namespace for dealing with installed extensions. Extensions are represented + * by an {@link Extension}-interface which enables reflection on them. + * + * Extension writers can provide APIs to other extensions by returning their API public + * surface from the `activate`-call. + * + * ```javascript + * export function activate(context: vscode.ExtensionContext) { + * let api = { + * sum(a, b) { + * return a + b; + * }, + * mul(a, b) { + * return a * b; + * } + * }; + * // 'export' public api-surface + * return api; + * } + * ``` + * When depending on the API of another extension add an `extensionDependencies`-entry + * to `package.json`, and use the {@link extensions.getExtension getExtension}-function + * and the {@link Extension.exports exports}-property, like below: + * + * ```javascript + * let mathExt = extensions.getExtension('genius.math'); + * let importedApi = mathExt.exports; + * + * console.log(importedApi.mul(42, 1)); + * ``` + */ + export namespace extensions { + + /** + * Get an extension by its full identifier in the form of: `publisher.name`. + * + * @param extensionId An extension identifier. + * @return An extension or `undefined`. + */ + export function getExtension(extensionId: string): Extension | undefined; + + /** + * All extensions currently known to the system. + */ + export const all: readonly Extension[]; + + /** + * An event which fires when `extensions.all` changes. This can happen when extensions are + * installed, uninstalled, enabled or disabled. + */ + export const onDidChange: Event; + } + + /** + * Collapsible state of a {@link CommentThread comment thread} + */ + export enum CommentThreadCollapsibleState { + /** + * Determines an item is collapsed + */ + Collapsed = 0, + + /** + * Determines an item is expanded + */ + Expanded = 1 + } + + /** + * Comment mode of a {@link Comment} + */ + export enum CommentMode { + /** + * Displays the comment editor + */ + Editing = 0, + + /** + * Displays the preview of the comment + */ + Preview = 1 + } + + /** + * A collection of {@link Comment comments} representing a conversation at a particular range in a document. + */ + export interface CommentThread { + /** + * The uri of the document the thread has been created on. + */ + readonly uri: Uri; + + /** + * The range the comment thread is located within the document. The thread icon will be shown + * at the last line of the range. + */ + range: Range; + + /** + * The ordered comments of the thread. + */ + comments: readonly Comment[]; + + /** + * Whether the thread should be collapsed or expanded when opening the document. + * Defaults to Collapsed. + */ + collapsibleState: CommentThreadCollapsibleState; + + /** + * Whether the thread supports reply. + * Defaults to true. + */ + canReply: boolean; + + /** + * Context value of the comment thread. This can be used to contribute thread specific actions. + * For example, a comment thread is given a context value as `editable`. When contributing actions to `comments/commentThread/title` + * using `menus` extension point, you can specify context value for key `commentThread` in `when` expression like `commentThread == editable`. + * ```json + * "contributes": { + * "menus": { + * "comments/commentThread/title": [ + * { + * "command": "extension.deleteCommentThread", + * "when": "commentThread == editable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteCommentThread` only for comment threads with `contextValue` is `editable`. + */ + contextValue?: string; + + /** + * The optional human-readable label describing the {@link CommentThread Comment Thread} + */ + label?: string; + + /** + * Dispose this comment thread. + * + * Once disposed, this comment thread will be removed from visible editors and Comment Panel when appropriate. + */ + dispose(): void; + } + + /** + * Author information of a {@link Comment} + */ + export interface CommentAuthorInformation { + /** + * The display name of the author of the comment + */ + name: string; + + /** + * The optional icon path for the author + */ + iconPath?: Uri; + } + + /** + * Reactions of a {@link Comment} + */ + export interface CommentReaction { + /** + * The human-readable label for the reaction + */ + readonly label: string; + + /** + * Icon for the reaction shown in UI. + */ + readonly iconPath: string | Uri; + + /** + * The number of users who have reacted to this reaction + */ + readonly count: number; + + /** + * Whether the {@link CommentAuthorInformation author} of the comment has reacted to this reaction + */ + readonly authorHasReacted: boolean; + } + + /** + * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. + */ + export interface Comment { + /** + * The human-readable comment body + */ + body: string | MarkdownString; + + /** + * {@link CommentMode Comment mode} of the comment + */ + mode: CommentMode; + + /** + * The {@link CommentAuthorInformation author information} of the comment + */ + author: CommentAuthorInformation; + + /** + * Context value of the comment. This can be used to contribute comment specific actions. + * For example, a comment is given a context value as `editable`. When contributing actions to `comments/comment/title` + * using `menus` extension point, you can specify context value for key `comment` in `when` expression like `comment == editable`. + * ```json + * "contributes": { + * "menus": { + * "comments/comment/title": [ + * { + * "command": "extension.deleteComment", + * "when": "comment == editable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteComment` only for comments with `contextValue` is `editable`. + */ + contextValue?: string; + + /** + * Optional reactions of the {@link Comment} + */ + reactions?: CommentReaction[]; + + /** + * Optional label describing the {@link Comment} + * Label will be rendered next to authorName if exists. + */ + label?: string; + + /** + * Optional timestamp that will be displayed in comments. + * The date will be formatted according to the user's locale and settings. + */ + timestamp?: Date; + } + + /** + * Command argument for actions registered in `comments/commentThread/context`. + */ + export interface CommentReply { + /** + * The active {@link CommentThread comment thread} + */ + thread: CommentThread; + + /** + * The value in the comment editor + */ + text: string; + } + + /** + * Commenting range provider for a {@link CommentController comment controller}. + */ + export interface CommentingRangeProvider { + /** + * Provide a list of ranges which allow new comment threads creation or null for a given document + */ + provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + } + + /** + * Represents a {@link CommentController comment controller}'s {@link CommentController.options options}. + */ + 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; + } + + /** + * A comment controller is able to provide {@link CommentThread comments} support to the editor and + * provide users various ways to interact with comments. + */ + export interface CommentController { + /** + * The id of this comment controller. + */ + readonly id: string; + + /** + * The human-readable label of this comment controller. + */ + readonly label: string; + + /** + * Comment controller options + */ + options?: CommentOptions; + + /** + * Optional commenting range provider. Provide a list {@link Range ranges} which support commenting to any given resource uri. + * + * If not provided, users cannot leave any comments. + */ + commentingRangeProvider?: CommentingRangeProvider; + + /** + * Create a {@link CommentThread comment thread}. The comment thread will be displayed in visible text editors (if the resource matches) + * and Comments Panel once created. + * + * @param uri The uri of the document the thread has been created on. + * @param range The range the comment thread is located within the document. + * @param comments The ordered comments of the thread. + */ + createCommentThread(uri: Uri, range: Range, comments: readonly Comment[]): CommentThread; + + /** + * Optional reaction handler for creating and deleting reactions on a {@link Comment}. + */ + reactionHandler?: (comment: Comment, reaction: CommentReaction) => Thenable; + + /** + * Dispose this comment controller. + * + * Once disposed, all {@link CommentThread comment threads} created by this comment controller will also be removed from the editor + * and Comments Panel. + */ + dispose(): void; + } + + namespace comments { + /** + * Creates a new {@link CommentController comment controller} instance. + * + * @param id An `id` for the comment controller. + * @param label A human-readable string for the comment controller. + * @return An instance of {@link CommentController comment controller}. + */ + export function createCommentController(id: string, label: string): CommentController; + } + + /** + * Represents a session of a currently logged in user. + */ + export interface AuthenticationSession { + /** + * The identifier of the authentication session. + */ + readonly id: string; + + /** + * The access token. + */ + readonly accessToken: string; + + /** + * The account associated with the session. + */ + readonly account: AuthenticationSessionAccountInformation; + + /** + * The permissions granted by the session's access token. Available scopes + * are defined by the {@link AuthenticationProvider}. + */ + readonly scopes: readonly string[]; + } + + /** + * The information of an account associated with an {@link AuthenticationSession}. + */ + export interface AuthenticationSessionAccountInformation { + /** + * The unique identifier of the account. + */ + readonly id: string; + + /** + * The human-readable name of the account. + */ + readonly label: string; + } + + + /** + * Options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. + */ + export interface AuthenticationGetSessionOptions { + /** + * Whether the existing user session preference should be cleared. + * + * For authentication providers that support being signed into multiple accounts at once, the user will be + * prompted to select an account to use when {@link authentication.getSession getSession} is called. This preference + * is remembered until {@link authentication.getSession getSession} is called with this flag. + * + * Defaults to false. + */ + clearSessionPreference?: boolean; + + /** + * Whether login should be performed if there is no matching session. + * + * If true, a modal dialog will be shown asking the user to sign in. If false, a numbered badge will be shown + * on the accounts activity bar icon. An entry for the extension will be added under the menu to sign in. This + * allows quietly prompting the user to sign in. + * + * If there is a matching session but the extension has not been granted access to it, setting this to true + * will also result in an immediate modal dialog, and false will add a numbered badge to the accounts icon. + * + * Defaults to false. + * + * Note: you cannot use this option with {@link AuthenticationGetSessionOptions.silent silent}. + */ + createIfNone?: boolean; + + /** + * Whether we should attempt to reauthenticate even if there is already a session available. + * + * If true, a modal dialog will be shown asking the user to sign in again. This is mostly used for scenarios + * where the token needs to be re minted because it has lost some authorization. + * + * If there are no existing sessions and forceNewSession is true, it will behave identically to + * {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. + * + * This defaults to false. + */ + forceNewSession?: boolean | { detail: string }; + + /** + * Whether we should show the indication to sign in in the Accounts menu. + * + * If false, the user will be shown a badge on the Accounts menu with an option to sign in for the extension. + * If true, no indication will be shown. + * + * Defaults to false. + * + * Note: you cannot use this option with any other options that prompt the user like {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. + */ + silent?: boolean; + } + + /** + * Basic information about an {@link AuthenticationProvider} + */ + export interface AuthenticationProviderInformation { + /** + * The unique identifier of the authentication provider. + */ + readonly id: string; + + /** + * The human-readable name of the authentication provider. + */ + readonly label: string; + } + + /** + * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. + */ + export interface AuthenticationSessionsChangeEvent { + /** + * The {@link AuthenticationProvider} that has had its sessions change. + */ + readonly provider: AuthenticationProviderInformation; + } + + /** + * Options for creating an {@link AuthenticationProvider}. + */ + export interface AuthenticationProviderOptions { + /** + * Whether it is possible to be signed into multiple accounts at once with this provider. + * If not specified, will default to false. + */ + readonly supportsMultipleAccounts?: boolean; + } + + /** + * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. + */ + export interface AuthenticationProviderAuthenticationSessionsChangeEvent { + /** + * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been added. + */ + readonly added: readonly AuthenticationSession[] | undefined; + + /** + * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been removed. + */ + readonly removed: readonly AuthenticationSession[] | undefined; + + /** + * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been changed. + * A session changes when its data excluding the id are updated. An example of this is a session refresh that results in a new + * access token being set for the session. + */ + readonly changed: readonly AuthenticationSession[] | undefined; + } + + /** + * A provider for performing authentication to a service. + */ + export interface AuthenticationProvider { + /** + * An {@link Event} which fires when the array of sessions has changed, or data + * within a session has changed. + */ + readonly onDidChangeSessions: Event; + + /** + * Get a list of sessions. + * @param scopes An optional list of scopes. If provided, the sessions returned should match + * these permissions, otherwise all sessions should be returned. + * @returns A promise that resolves to an array of authentication sessions. + */ + getSessions(scopes?: readonly string[]): Thenable; + + /** + * Prompts a user to login. + * + * If login is successful, the onDidChangeSessions event should be fired. + * + * If login fails, a rejected promise should be returned. + * + * If the provider has specified that it does not support multiple accounts, + * then this should never be called if there is already an existing session matching these + * scopes. + * @param scopes A list of scopes, permissions, that the new session should be created with. + * @returns A promise that resolves to an authentication session. + */ + createSession(scopes: readonly string[]): Thenable; + + /** + * Removes the session corresponding to session id. + * + * If the removal is successful, the onDidChangeSessions event should be fired. + * + * If a session cannot be removed, the provider should reject with an error message. + * @param sessionId The id of the session to remove. + */ + removeSession(sessionId: string): Thenable; + } + + + /** + * Namespace for authentication. + */ + export namespace authentication { + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @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 + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session + */ + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { createIfNone: true }): Thenable; + + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @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 + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session + */ + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | { detail: string } }): Thenable; + + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @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 + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session if available, or undefined if there are no sessions + */ + export function getSession(providerId: string, scopes: readonly string[], options?: AuthenticationGetSessionOptions): Thenable; + + /** + * An {@link Event} which fires when the authentication sessions of an authentication provider have + * been added, removed, or changed. + */ + export const onDidChangeSessions: Event; + + /** + * Register an authentication provider. + * + * There can only be one provider per id and an error is being thrown when an id + * has already been used by another provider. Ids are case-sensitive. + * + * @param id The unique identifier of the provider. + * @param label The human-readable name of the provider. + * @param provider The authentication provider provider. + * @params options Additional options for the provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerAuthenticationProvider(id: string, label: string, provider: AuthenticationProvider, options?: AuthenticationProviderOptions): Disposable; + } + + /** + * Namespace for localization-related functionality in the extension API. To use this properly, + * you must have `l10n` defined in your extension manifest and have bundle.l10n..json files. + * For more information on how to generate bundle.l10n..json files, check out the + * [vscode-l10n repo](https://github.com/microsoft/vscode-l10n). + * + * Note: Built-in extensions (for example, Git, TypeScript Language Features, GitHub Authentication) + * are excluded from the `l10n` property requirement. In other words, they do not need to specify + * a `l10n` in the extension manifest because their translated strings come from Language Packs. + */ + export namespace l10n { + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected {@link args} values for any templated values). + * @param message - The message to localize. Supports index templating where strings like `{0}` and `{1}` are + * replaced by the item at that index in the {@link args} array. + * @param args - The arguments to be used in the localized string. The index of the argument is used to + * match the template placeholder in the localized string. + * @returns localized string with injected arguments. + * @example `l10n.t('Hello {0}!', 'World');` + */ + export function t(message: string, ...args: Array): string; + + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected {@link args} values for any templated values). + * @param message The message to localize. Supports named templating where strings like `{foo}` and `{bar}` are + * replaced by the value in the Record for that key (foo, bar, etc). + * @param args The arguments to be used in the localized string. The name of the key in the record is used to + * match the template placeholder in the localized string. + * @returns localized string with injected arguments. + * @example `l10n.t('Hello {name}', { name: 'Erich' });` + */ + export function t(message: string, args: Record): string; + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected args values for any templated values). + * @param options The options to use when localizing the message. + * @returns localized string with injected arguments. + */ + export function t(options: { + /** + * The message to localize. If {@link args} is an array, this message supports index templating where strings like + * `{0}` and `{1}` are replaced by the item at that index in the {@link args} array. If `args` is a `Record`, + * this supports named templating where strings like `{foo}` and `{bar}` are replaced by the value in + * the Record for that key (foo, bar, etc). + */ + message: string; + /** + * The arguments to be used in the localized string. As an array, the index of the argument is used to + * match the template placeholder in the localized string. As a Record, the key is used to match the template + * placeholder in the localized string. + */ + args?: Array | Record; + /** + * A comment to help translators understand the context of the message. + */ + comment: string | string[]; + }): string; + /** + * The bundle of localized strings that have been loaded for the extension. + * It's undefined if no bundle has been loaded. The bundle is typically not loaded if + * there was no bundle found or when we are running with the default language. + */ + export const bundle: { [key: string]: string } | undefined; + /** + * The URI of the localization bundle that has been loaded for the extension. + * It's undefined if no bundle has been loaded. The bundle is typically not loaded if + * there was no bundle found or when we are running with the default language. + */ + export const uri: Uri | undefined; + } + + /** + * Namespace for testing functionality. Tests are published by registering + * {@link TestController} instances, then adding {@link TestItem TestItems}. + * Controllers may also describe how to run tests by creating one or more + * {@link TestRunProfile} instances. + */ + export namespace tests { + /** + * Creates a new test controller. + * + * @param id Identifier for the controller, must be globally unique. + * @param label A human-readable label for the controller. + * @returns An instance of the {@link TestController}. + */ + export function createTestController(id: string, label: string): TestController; + } + + /** + * The kind of executions that {@link TestRunProfile TestRunProfiles} control. + */ + export enum TestRunProfileKind { + Run = 1, + Debug = 2, + Coverage = 3, + } + + /** + * Tags can be associated with {@link TestItem TestItems} and + * {@link TestRunProfile TestRunProfiles}. A profile with a tag can only + * execute tests that include that tag in their {@link TestItem.tags} array. + */ + export class TestTag { + /** + * ID of the test tag. `TestTag` instances with the same ID are considered + * to be identical. + */ + readonly id: string; + + /** + * Creates a new TestTag instance. + * @param id ID of the test tag. + */ + constructor(id: string); + } + + /** + * A TestRunProfile describes one way to execute tests in a {@link TestController}. + */ + export interface TestRunProfile { + /** + * Label shown to the user in the UI. + * + * Note that the label has some significance if the user requests that + * tests be re-run in a certain way. For example, if tests were run + * normally and the user requests to re-run them in debug mode, the editor + * will attempt use a configuration with the same label of the `Debug` + * kind. If there is no such configuration, the default will be used. + */ + label: string; + + /** + * Configures what kind of execution this profile controls. If there + * are no profiles for a kind, it will not be available in the UI. + */ + readonly kind: TestRunProfileKind; + + /** + * Controls whether this profile is the default action that will + * be taken when its kind is actioned. For example, if the user clicks + * the generic "run all" button, then the default profile for + * {@link TestRunProfileKind.Run} will be executed, although the + * user can configure this. + */ + isDefault: boolean; + + /** + * Associated tag for the profile. If this is set, only {@link TestItem} + * instances with the same tag will be eligible to execute in this profile. + */ + tag: TestTag | undefined; + + /** + * If this method is present, a configuration gear will be present in the + * UI, and this method will be invoked when it's clicked. When called, + * you can take other editor actions, such as showing a quick pick or + * opening a configuration file. + */ + configureHandler: (() => void) | undefined; + + /** + * Handler called to start a test run. When invoked, the function should call + * {@link TestController.createTestRun} at least once, and all test runs + * associated with the request should be created before the function returns + * or the returned promise is resolved. + * + * @param request Request information for the test run. + * @param cancellationToken Token that signals the used asked to abort the + * test run. If cancellation is requested on this token, all {@link TestRun} + * instances associated with the request will be + * automatically cancelled as well. + */ + runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void; + + /** + * Deletes the run profile. + */ + dispose(): void; + } + + /** + * Entry point to discover and execute tests. It contains {@link TestController.items} which + * are used to populate the editor UI, and is associated with + * {@link TestController.createRunProfile run profiles} to allow + * for tests to be executed. + */ + export interface TestController { + /** + * The id of the controller passed in {@link vscode.tests.createTestController}. + * This must be globally unique. + */ + readonly id: string; + + /** + * Human-readable label for the test controller. + */ + label: string; + + /** + * A collection of "top-level" {@link TestItem} instances, which can in + * turn have their own {@link TestItem.children children} to form the + * "test tree." + * + * The extension controls when to add tests. For example, extensions should + * add tests for a file when {@link vscode.workspace.onDidOpenTextDocument} + * fires in order for decorations for tests within a file to be visible. + * + * However, the editor may sometimes explicitly request children using the + * {@link resolveHandler} See the documentation on that method for more details. + */ + readonly items: TestItemCollection; + + /** + * Creates a profile used for running tests. Extensions must create + * at least one profile in order for tests to be run. + * @param label A human-readable label for this profile. + * @param kind Configures what kind of execution this profile manages. + * @param runHandler Function called to start a test run. + * @param isDefault Whether this is the default action for its kind. + * @param tag Profile test tag. + * @returns An instance of a {@link TestRunProfile}, which is automatically + * associated with this controller. + */ + createRunProfile(label: string, kind: TestRunProfileKind, runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void, isDefault?: boolean, tag?: TestTag): TestRunProfile; + + /** + * A function provided by the extension that the editor may call to request + * children of a test item, if the {@link TestItem.canResolveChildren} is + * `true`. When called, the item should discover children and call + * {@link vscode.tests.createTestItem} as children are discovered. + * + * Generally the extension manages the lifecycle of test items, but under + * certain conditions the editor may request the children of a specific + * item to be loaded. For example, if the user requests to re-run tests + * after reloading the editor, the editor may need to call this method + * to resolve the previously-run tests. + * + * The item in the explorer will automatically be marked as "busy" until + * the function returns or the returned thenable resolves. + * + * @param item An unresolved test item for which children are being + * requested, or `undefined` to resolve the controller's initial {@link TestController.items items}. + */ + resolveHandler?: (item: TestItem | undefined) => Thenable | void; + + /** + * If this method is present, a refresh button will be present in the + * UI, and this method will be invoked when it's clicked. When called, + * the extension should scan the workspace for any new, changed, or + * removed tests. + * + * It's recommended that extensions try to update tests in realtime, using + * a {@link FileSystemWatcher} for example, and use this method as a fallback. + * + * @returns A thenable that resolves when tests have been refreshed. + */ + refreshHandler: ((token: CancellationToken) => Thenable | void) | undefined; + + /** + * Creates a {@link TestRun}. This should be called by the + * {@link TestRunProfile} when a request is made to execute tests, and may + * also be called if a test run is detected externally. Once created, tests + * that are included in the request will be moved into the queued state. + * + * All runs created using the same `request` instance will be grouped + * together. This is useful if, for example, a single suite of tests is + * run on multiple platforms. + * + * @param request Test run request. Only tests inside the `include` may be + * modified, and tests in its `exclude` are ignored. + * @param name The human-readable name of the run. This can be used to + * disambiguate multiple sets of results in a test run. It is useful if + * tests are run across multiple platforms, for example. + * @param persist Whether the results created by the run should be + * persisted in the editor. This may be false if the results are coming from + * a file already saved externally, such as a coverage information file. + * @returns An instance of the {@link TestRun}. It will be considered "running" + * from the moment this method is invoked until {@link TestRun.end} is called. + */ + createTestRun(request: TestRunRequest, name?: string, persist?: boolean): TestRun; + + /** + * Creates a new managed {@link TestItem} instance. It can be added into + * the {@link TestItem.children} of an existing item, or into the + * {@link TestController.items}. + * + * @param id Identifier for the TestItem. The test item's ID must be unique + * in the {@link TestItemCollection} it's added to. + * @param label Human-readable label of the test item. + * @param uri URI this TestItem is associated with. May be a file or directory. + */ + createTestItem(id: string, label: string, uri?: Uri): TestItem; + + /** + * Unregisters the test controller, disposing of its associated tests + * and unpersisted results. + */ + dispose(): void; + } + + /** + * A TestRunRequest is a precursor to a {@link TestRun}, which in turn is + * created by passing a request to {@link TestController.createTestRun}. The + * TestRunRequest contains information about which tests should be run, which + * should not be run, and how they are run (via the {@link TestRunRequest.profile profile}). + * + * In general, TestRunRequests are created by the editor and pass to + * {@link TestRunProfile.runHandler}, however you can also create test + * requests and runs outside of the `runHandler`. + */ + export class TestRunRequest { + /** + * A filter for specific tests to run. If given, the extension should run + * all of the included tests and all their children, excluding any tests + * that appear in {@link TestRunRequest.exclude}. If this property is + * undefined, then the extension should simply run all tests. + * + * The process of running tests should resolve the children of any test + * items who have not yet been resolved. + */ + readonly include: readonly TestItem[] | undefined; + + /** + * An array of tests the user has marked as excluded from the test included + * in this run; exclusions should apply after inclusions. + * + * May be omitted if no exclusions were requested. Test controllers should + * not run excluded tests or any children of excluded tests. + */ + readonly exclude: readonly TestItem[] | undefined; + + /** + * The profile used for this request. This will always be defined + * for requests issued from the editor UI, though extensions may + * programmatically create requests not associated with any profile. + */ + readonly profile: TestRunProfile | undefined; + + /** + * @param include Array of specific tests to run, or undefined to run all tests + * @param exclude An array of tests to exclude from the run. + * @param profile The run profile used for this request. + */ + constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile); + } + + /** + * A TestRun represents an in-progress or completed test run and + * provides methods to report the state of individual tests in the run. + */ + export interface TestRun { + /** + * The human-readable name of the run. This can be used to + * disambiguate multiple sets of results in a test run. It is useful if + * tests are run across multiple platforms, for example. + */ + readonly name: string | undefined; + + /** + * A cancellation token which will be triggered when the test run is + * canceled from the UI. + */ + readonly token: CancellationToken; + + /** + * Whether the test run will be persisted across reloads by the editor. + */ + readonly isPersisted: boolean; + + /** + * Indicates a test is queued for later execution. + * @param test Test item to update. + */ + enqueued(test: TestItem): void; + + /** + * Indicates a test has started running. + * @param test Test item to update. + */ + started(test: TestItem): void; + + /** + * Indicates a test has been skipped. + * @param test Test item to update. + */ + skipped(test: TestItem): void; + + /** + * Indicates a test has failed. You should pass one or more + * {@link TestMessage TestMessages} to describe the failure. + * @param test Test item to update. + * @param message Messages associated with the test failure. + * @param duration How long the test took to execute, in milliseconds. + */ + failed(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void; + + /** + * Indicates a test has errored. You should pass one or more + * {@link TestMessage TestMessages} to describe the failure. This differs + * from the "failed" state in that it indicates a test that couldn't be + * executed at all, from a compilation error for example. + * @param test Test item to update. + * @param message Messages associated with the test failure. + * @param duration How long the test took to execute, in milliseconds. + */ + errored(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void; + + /** + * Indicates a test has passed. + * @param test Test item to update. + * @param duration How long the test took to execute, in milliseconds. + */ + passed(test: TestItem, duration?: number): void; + + /** + * Appends raw output from the test runner. On the user's request, the + * output will be displayed in a terminal. ANSI escape sequences, + * such as colors and text styles, are supported. + * + * @param output Output text to append. + * @param location Indicate that the output was logged at the given + * location. + * @param test Test item to associate the output with. + */ + appendOutput(output: string, location?: Location, test?: TestItem): void; + + /** + * Signals that the end of the test run. Any tests included in the run whose + * states have not been updated will have their state reset. + */ + end(): void; + } + + /** + * Collection of test items, found in {@link TestItem.children} and + * {@link TestController.items}. + */ + export interface TestItemCollection extends Iterable<[id: string, testItem: TestItem]> { + /** + * Gets the number of items in the collection. + */ + readonly size: number; + + /** + * Replaces the items stored by the collection. + * @param items Items to store. + */ + replace(items: readonly TestItem[]): void; + + /** + * Iterate over each entry in this collection. + * + * @param callback Function to execute for each entry. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callback: (item: TestItem, collection: TestItemCollection) => unknown, thisArg?: any): void; + + /** + * Adds the test item to the children. If an item with the same ID already + * exists, it'll be replaced. + * @param item Item to add. + */ + add(item: TestItem): void; + + /** + * Removes a single test item from the collection. + * @param itemId Item ID to delete. + */ + delete(itemId: string): void; + + /** + * Efficiently gets a test item by ID, if it exists, in the children. + * @param itemId Item ID to get. + * @returns The found item or undefined if it does not exist. + */ + get(itemId: string): TestItem | undefined; + } + + /** + * An item shown in the "test explorer" view. + * + * A `TestItem` can represent either a test suite or a test itself, since + * they both have similar capabilities. + */ + export interface TestItem { + /** + * Identifier for the `TestItem`. This is used to correlate + * test results and tests in the document with those in the workspace + * (test explorer). This cannot change for the lifetime of the `TestItem`, + * and must be unique among its parent's direct children. + */ + readonly id: string; + + /** + * URI this `TestItem` is associated with. May be a file or directory. + */ + readonly uri: Uri | undefined; + + /** + * The children of this test item. For a test suite, this may contain the + * individual test cases or nested suites. + */ + readonly children: TestItemCollection; + + /** + * The parent of this item. It's set automatically, and is undefined + * top-level items in the {@link TestController.items} and for items that + * aren't yet included in another item's {@link TestItem.children children}. + */ + readonly parent: TestItem | undefined; + + /** + * Tags associated with this test item. May be used in combination with + * {@link TestRunProfile.tags}, or simply as an organizational feature. + */ + tags: readonly TestTag[]; + + /** + * Indicates whether this test item may have children discovered by resolving. + * + * If true, this item is shown as expandable in the Test Explorer view and + * expanding the item will cause {@link TestController.resolveHandler} + * to be invoked with the item. + * + * Default to `false`. + */ + canResolveChildren: boolean; + + /** + * Controls whether the item is shown as "busy" in the Test Explorer view. + * This is useful for showing status while discovering children. + * + * Defaults to `false`. + */ + busy: boolean; + + /** + * Display name describing the test case. + */ + label: string; + + /** + * Optional description that appears next to the label. + */ + description?: string; + + /** + * A string that should be used when comparing this item + * with other items. When `falsy` the {@link TestItem.label label} + * is used. + */ + sortText?: string | undefined; + + /** + * Location of the test item in its {@link TestItem.uri uri}. + * + * This is only meaningful if the `uri` points to a file. + */ + range: Range | undefined; + + /** + * Optional error encountered while loading the test. + * + * Note that this is not a test result and should only be used to represent errors in + * test discovery, such as syntax errors. + */ + error: string | MarkdownString | undefined; + } + + /** + * Message associated with the test state. Can be linked to a specific + * source range -- useful for assertion failures, for example. + */ + export class TestMessage { + /** + * Human-readable message text to display. + */ + message: string | MarkdownString; + + /** + * Expected test output. If given with {@link TestMessage.actualOutput actualOutput }, a diff view will be shown. + */ + expectedOutput?: string; + + /** + * Actual test output. If given with {@link TestMessage.expectedOutput expectedOutput }, a diff view will be shown. + */ + actualOutput?: string; + + /** + * Associated file location. + */ + location?: Location; + + /** + * Creates a new TestMessage that will present as a diff in the editor. + * @param message Message to display to the user. + * @param expected Expected output. + * @param actual Actual output. + */ + static diff(message: string | MarkdownString, expected: string, actual: string): TestMessage; + + /** + * Creates a new TestMessage instance. + * @param message The message to show to the user. + */ + constructor(message: string | MarkdownString); + } + + /** + * The tab represents a single text based resource. + */ + export class TabInputText { + /** + * The uri represented by the tab. + */ + readonly uri: Uri; + /** + * Constructs a text tab input with the given URI. + * @param uri The URI of the tab. + */ + constructor(uri: Uri); + } + + /** + * The tab represents two text based resources + * being rendered as a diff. + */ + export class TabInputTextDiff { + /** + * The uri of the original text resource. + */ + readonly original: Uri; + /** + * The uri of the modified text resource. + */ + readonly modified: Uri; + /** + * Constructs a new text diff tab input with the given URIs. + * @param original The uri of the original text resource. + * @param modified The uri of the modified text resource. + */ + constructor(original: Uri, modified: Uri); + } + + /** + * The tab represents a custom editor. + */ + export class TabInputCustom { + /** + * The uri that the tab is representing. + */ + readonly uri: Uri; + /** + * The type of custom editor. + */ + readonly viewType: string; + /** + * Constructs a custom editor tab input. + * @param uri The uri of the tab. + * @param viewType The viewtype of the custom editor. + */ + constructor(uri: Uri, viewType: string); + } + + /** + * The tab represents a webview. + */ + export class TabInputWebview { + /** + * The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} + */ + readonly viewType: string; + /** + * Constructs a webview tab input with the given view type. + * @param viewType The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} + */ + constructor(viewType: string); + } + + /** + * The tab represents a notebook. + */ + export class TabInputNotebook { + /** + * The uri that the tab is representing. + */ + readonly uri: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + readonly notebookType: string; + /** + * Constructs a new tab input for a notebook. + * @param uri The uri of the notebook. + * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + constructor(uri: Uri, notebookType: string); + } + + /** + * The tabs represents two notebooks in a diff configuration. + */ + export class TabInputNotebookDiff { + /** + * The uri of the original notebook. + */ + readonly original: Uri; + /** + * The uri of the modified notebook. + */ + readonly modified: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + readonly notebookType: string; + /** + * Constructs a notebook diff tab input. + * @param original The uri of the original unmodified notebook. + * @param modified The uri of the modified notebook. + * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + constructor(original: Uri, modified: Uri, notebookType: string); + } + + /** + * The tab represents a terminal in the editor area. + */ + export class TabInputTerminal { + /** + * Constructs a terminal tab input. + */ + constructor(); + } + + /** + * Represents a tab within a {@link TabGroup group of tabs}. + * Tabs are merely the graphical representation within the editor area. + * A backing editor is not a guarantee. + */ + export interface Tab { + + /** + * The text displayed on the tab. + */ + readonly label: string; + + /** + * The group which the tab belongs to. + */ + readonly group: TabGroup; + + /** + * Defines the structure of the tab i.e. text, notebook, custom, etc. + * Resource and other useful properties are defined on the tab kind. + */ + readonly input: TabInputText | TabInputTextDiff | TabInputCustom | TabInputWebview | TabInputNotebook | TabInputNotebookDiff | TabInputTerminal | unknown; + + /** + * Whether or not the tab is currently active. + * This is dictated by being the selected tab in the group. + */ + readonly isActive: boolean; + + /** + * Whether or not the dirty indicator is present on the tab. + */ + readonly isDirty: boolean; + + /** + * Whether or not the tab is pinned (pin icon is present). + */ + readonly isPinned: boolean; + + /** + * Whether or not the tab is in preview mode. + */ + readonly isPreview: boolean; + } + + /** + * An event describing change to tabs. + */ + export interface TabChangeEvent { + /** + * The tabs that have been opened. + */ + readonly opened: readonly Tab[]; + /** + * The tabs that have been closed. + */ + readonly closed: readonly Tab[]; + /** + * Tabs that have changed, e.g have changed + * their {@link Tab.isActive active} state. + */ + readonly changed: readonly Tab[]; + } + + /** + * An event describing changes to tab groups. + */ + export interface TabGroupChangeEvent { + /** + * Tab groups that have been opened. + */ + readonly opened: readonly TabGroup[]; + /** + * Tab groups that have been closed. + */ + readonly closed: readonly TabGroup[]; + /** + * Tab groups that have changed, e.g have changed + * their {@link TabGroup.isActive active} state. + */ + readonly changed: readonly TabGroup[]; + } + + /** + * Represents a group of tabs. A tab group itself consists of multiple tabs. + */ + export interface TabGroup { + /** + * Whether or not the group is currently active. + * + * *Note* that only one tab group is active at a time, but that multiple tab + * groups can have an {@link TabGroup.aciveTab active tab}. + * + * @see {@link Tab.isActive} + */ + readonly isActive: boolean; + + /** + * The view column of the group. + */ + readonly viewColumn: ViewColumn; + + /** + * The active {@link Tab tab} in the group. This is the tab whose contents are currently + * being rendered. + * + * *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}. + */ + readonly activeTab: Tab | undefined; + + /** + * The list of tabs contained within the group. + * This can be empty if the group has no tabs open. + */ + readonly tabs: readonly Tab[]; + } + + /** + * Represents the main editor area which consists of multple groups which contain tabs. + */ + export interface TabGroups { + /** + * All the groups within the group container. + */ + readonly all: readonly TabGroup[]; + + /** + * The currently active group. + */ + readonly activeTabGroup: TabGroup; + + /** + * An {@link Event event} which fires when {@link TabGroup tab groups} have changed. + */ + readonly onDidChangeTabGroups: Event; + + /** + * An {@link Event event} which fires when {@link Tab tabs} have changed. + */ + readonly onDidChangeTabs: Event; + + /** + * Closes the tab. This makes the tab object invalid and the tab + * should no longer be used for further actions. + * Note: In the case of a dirty tab, a confirmation dialog will be shown which may be cancelled. If cancelled the tab is still valid + * + * @param tab The tab to close. + * @param preserveFocus When `true` focus will remain in its current position. If `false` it will jump to the next tab. + * @returns A promise that resolves to `true` when all tabs have been closed. + */ + close(tab: Tab | readonly Tab[], preserveFocus?: boolean): Thenable; + + /** + * Closes the tab group. This makes the tab group object invalid and the tab group + * should no longer be used for further actions. + * @param tabGroup The tab group to close. + * @param preserveFocus When `true` focus will remain in its current position. + * @returns A promise that resolves to `true` when all tab groups have been closed. + */ + close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; + } +} + +/** + * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, + * and others. This API makes no assumption about what promise library is being used which + * enables reusing existing code without migrating to a specific promise implementation. Still, + * we recommend the use of native promises which are available in this editor. + */ +interface Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; +} diff --git a/l10n-sample/src/cli.ts b/l10n-sample/src/cli.ts index 00c95b22..d9961d64 100644 --- a/l10n-sample/src/cli.ts +++ b/l10n-sample/src/cli.ts @@ -1,10 +1,10 @@ -import * as l10n from '@vscode/l10n'; - -if (process.env['EXTENSION_BUNDLE_URI']) { - l10n.config({ - uri: process.env['EXTENSION_BUNDLE_URI'] - }); -} - -const message = l10n.t('Hello {0}', 'CLI'); -console.log(message + '\n'); +import * as l10n from '@vscode/l10n'; + +if (process.env['EXTENSION_BUNDLE_URI']) { + l10n.config({ + uri: process.env['EXTENSION_BUNDLE_URI'] + }); +} + +const message = l10n.t('Hello {0}', 'CLI'); +console.log(message + '\n'); diff --git a/l10n-sample/src/command/sayBye.ts b/l10n-sample/src/command/sayBye.ts index a5b4dbd3..dbb0e203 100644 --- a/l10n-sample/src/command/sayBye.ts +++ b/l10n-sample/src/command/sayBye.ts @@ -1,10 +1,10 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ -import { l10n, window } from 'vscode'; - -export function sayByeCommand() { - const message = l10n.t('Bye'); - window.showInformationMessage(message); -} +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import { l10n, window } from 'vscode'; + +export function sayByeCommand() { + const message = l10n.t('Bye'); + window.showInformationMessage(message); +} diff --git a/l10n-sample/src/extension.ts b/l10n-sample/src/extension.ts index 7d47b0eb..66b90fb3 100644 --- a/l10n-sample/src/extension.ts +++ b/l10n-sample/src/extension.ts @@ -1,40 +1,40 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import path = require('path'); -import * as vscode from 'vscode'; -import { sayByeCommand } from './command/sayBye'; - -export function activate(context: vscode.ExtensionContext) { - - const helloCmd = vscode.commands.registerCommand('extension.sayHello', async () => { - const message = vscode.l10n.t('Hello'); - vscode.window.showInformationMessage(message); - console.log(context.extensionUri); - - // This is showing how you might pass the vscode.l10n.uri down to - // a subprocess if you have one that your extension spawns. - await vscode.tasks.executeTask( - new vscode.Task( - { type: 'shell' }, - vscode.TaskScope.Global, - message, - message, - new vscode.ShellExecution(`node ${path.join(__dirname, 'cli.js')}`, { - // eslint-disable-next-line @typescript-eslint/naming-convention - env: vscode.l10n.uri ? { EXTENSION_BUNDLE_URI: vscode.l10n.uri?.toString(true) } : undefined - }))); - - const messageDone = vscode.l10n.t('Hello {done}', { done: 'FINISHED' }); - vscode.window.showInformationMessage(messageDone); - }); - - const byeCmd = vscode.commands.registerCommand( - 'extension.sayBye', - sayByeCommand - ); - - context.subscriptions.push(helloCmd, byeCmd); -} - -// this method is called when your extension is deactivated -export function deactivate() {} +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import path = require('path'); +import * as vscode from 'vscode'; +import { sayByeCommand } from './command/sayBye'; + +export function activate(context: vscode.ExtensionContext) { + + const helloCmd = vscode.commands.registerCommand('extension.sayHello', async () => { + const message = vscode.l10n.t('Hello'); + vscode.window.showInformationMessage(message); + console.log(context.extensionUri); + + // This is showing how you might pass the vscode.l10n.uri down to + // a subprocess if you have one that your extension spawns. + await vscode.tasks.executeTask( + new vscode.Task( + { type: 'shell' }, + vscode.TaskScope.Global, + message, + message, + new vscode.ShellExecution(`node ${path.join(__dirname, 'cli.js')}`, { + // eslint-disable-next-line @typescript-eslint/naming-convention + env: vscode.l10n.uri ? { EXTENSION_BUNDLE_URI: vscode.l10n.uri?.toString(true) } : undefined + }))); + + const messageDone = vscode.l10n.t('Hello {done}', { done: 'FINISHED' }); + vscode.window.showInformationMessage(messageDone); + }); + + const byeCmd = vscode.commands.registerCommand( + 'extension.sayBye', + sayByeCommand + ); + + context.subscriptions.push(helloCmd, byeCmd); +} + +// this method is called when your extension is deactivated +export function deactivate() { } diff --git a/nodefs-provider-sample/src/extension.ts b/nodefs-provider-sample/src/extension.ts index 1b8617ec..ab80ad74 100644 --- a/nodefs-provider-sample/src/extension.ts +++ b/nodefs-provider-sample/src/extension.ts @@ -1,298 +1,298 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ - -import * as fs from 'fs'; -import * as mkdirp from 'mkdirp'; -import * as path from 'path'; -import * as rimraf from 'rimraf'; -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - vscode.workspace.registerFileSystemProvider('datei', new DateiFileSystemProvider(), { - isCaseSensitive: process.platform === 'linux' - }); -} - -class DateiFileSystemProvider implements vscode.FileSystemProvider { - - private _onDidChangeFile: vscode.EventEmitter; - - constructor() { - this._onDidChangeFile = new vscode.EventEmitter(); - } - - get onDidChangeFile(): vscode.Event { - return this._onDidChangeFile.event; - } - - watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable { - const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => { - const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString())); - - // TODO support excludes (using minimatch library?) - - this._onDidChangeFile.fire([{ - type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted, - uri: uri.with({ path: filepath }) - } as vscode.FileChangeEvent]); - }); - - return { dispose: () => watcher.close() }; - } - - stat(uri: vscode.Uri): vscode.FileStat | Thenable { - return this._stat(uri.fsPath); - } - - async _stat(path: string): Promise { - const res = await _.statLink(path); - return new FileStat(res.stat, res.isSymbolicLink); - } - - readDirectory(uri: vscode.Uri): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> { - return this._readDirectory(uri); - } - - async _readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { - const children = await _.readdir(uri.fsPath); - - const result: [string, vscode.FileType][] = []; - for (let i = 0; i < children.length; i++) { - const child = children[i]; - const stat = await this._stat(path.join(uri.fsPath, child)); - result.push([child, stat.type]); - } - - return Promise.resolve(result); - } - - createDirectory(uri: vscode.Uri): void | Thenable { - return _.mkdir(uri.fsPath); - } - - readFile(uri: vscode.Uri): Uint8Array | Thenable { - return _.readfile(uri.fsPath); - } - - writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Thenable { - return this._writeFile(uri, content, options); - } - - async _writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise { - const exists = await _.exists(uri.fsPath); - if (!exists) { - if (!options.create) { - throw vscode.FileSystemError.FileNotFound(); - } - - await _.mkdir(path.dirname(uri.fsPath)); - } else { - if (!options.overwrite) { - throw vscode.FileSystemError.FileExists(); - } - } - - return _.writefile(uri.fsPath, content as Buffer); - } - - delete(uri: vscode.Uri, options: { recursive: boolean; }): void | Thenable { - if (options.recursive) { - return _.rmrf(uri.fsPath); - } - - return _.unlink(uri.fsPath); - } - - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Thenable { - return this._rename(oldUri, newUri, options); - } - - async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise { - const exists = await _.exists(newUri.fsPath); - if (exists) { - if (!options.overwrite) { - throw vscode.FileSystemError.FileExists(); - } else { - await _.rmrf(newUri.fsPath); - } - } - - const parentExists = await _.exists(path.dirname(newUri.fsPath)); - if (!parentExists) { - await _.mkdir(path.dirname(newUri.fsPath)); - } - - return _.rename(oldUri.fsPath, newUri.fsPath); - } - - // TODO can implement a fast copy() method with node.js 8.x new fs.copy method -} - -//#region Utilities - -export interface IStatAndLink { - stat: fs.Stats; - isSymbolicLink: boolean; -} - -namespace _ { - - function handleResult(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T | undefined): void { - if (error) { - reject(messageError(error)); - } else { - resolve(result!); - } - } - - function messageError(error: Error & { code?: string }): Error { - if (error.code === 'ENOENT') { - return vscode.FileSystemError.FileNotFound(); - } - - if (error.code === 'EISDIR') { - return vscode.FileSystemError.FileIsADirectory(); - } - - if (error.code === 'EEXIST') { - return vscode.FileSystemError.FileExists(); - } - - if (error.code === 'EPERM' || error.code === 'EACCESS') { - return vscode.FileSystemError.NoPermissions(); - } - - return error; - } - - export function checkCancellation(token: vscode.CancellationToken): void { - if (token.isCancellationRequested) { - throw new Error('Operation cancelled'); - } - } - - export function normalizeNFC(items: string): string; - export function normalizeNFC(items: string[]): string[]; - export function normalizeNFC(items: string | string[]): string | string[] { - if (process.platform !== 'darwin') { - return items; - } - - if (Array.isArray(items)) { - return items.map(item => item.normalize('NFC')); - } - - return items.normalize('NFC'); - } - - export function readdir(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children))); - }); - } - - export function readfile(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer)); - }); - } - - export function writefile(path: string, content: Buffer): Promise { - return new Promise((resolve, reject) => { - fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function exists(path: string): Promise { - return new Promise((resolve, reject) => { - fs.exists(path, exists => handleResult(resolve, reject, null, exists)); - }); - } - - export function rmrf(path: string): Promise { - return new Promise((resolve, reject) => { - rimraf(path, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function mkdir(path: string): Promise { - return new Promise((resolve, reject) => { - mkdirp(path, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function rename(oldPath: string, newPath: string): Promise { - return new Promise((resolve, reject) => { - fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function unlink(path: string): Promise { - return new Promise((resolve, reject) => { - fs.unlink(path, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function statLink(path: string): Promise { - return new Promise((resolve, reject) => { - fs.lstat(path, (error, lstat) => { - if (error || lstat.isSymbolicLink()) { - fs.stat(path, (error, stat) => { - if (error) { - return handleResult(resolve, reject, error, void 0); - } - - handleResult(resolve, reject, error, { stat, isSymbolicLink: lstat && lstat.isSymbolicLink() }); - }); - } else { - handleResult(resolve, reject, error, { stat: lstat, isSymbolicLink: false }); - } - }); - - }); - } -} - -export class FileStat implements vscode.FileStat { - - constructor(private fsStat: fs.Stats, private _isSymbolicLink: boolean) { } - - get type(): vscode.FileType { - let type: number; - if (this._isSymbolicLink) { - type = vscode.FileType.SymbolicLink | (this.fsStat.isDirectory() ? vscode.FileType.Directory : vscode.FileType.File); - } else { - type = this.fsStat.isFile() ? vscode.FileType.File : this.fsStat.isDirectory() ? vscode.FileType.Directory : vscode.FileType.Unknown; - } - - return type; - } - - get isFile(): boolean | undefined { - return this.fsStat.isFile(); - } - - get isDirectory(): boolean | undefined { - return this.fsStat.isDirectory(); - } - - get isSymbolicLink(): boolean | undefined { - return this._isSymbolicLink; - } - - get size(): number { - return this.fsStat.size; - } - - get ctime(): number { - return this.fsStat.ctime.getTime(); - } - - get mtime(): number { - return this.fsStat.mtime.getTime(); - } -} - +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as fs from 'fs'; +import * as mkdirp from 'mkdirp'; +import * as path from 'path'; +import * as rimraf from 'rimraf'; +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + vscode.workspace.registerFileSystemProvider('datei', new DateiFileSystemProvider(), { + isCaseSensitive: process.platform === 'linux' + }); +} + +class DateiFileSystemProvider implements vscode.FileSystemProvider { + + private _onDidChangeFile: vscode.EventEmitter; + + constructor() { + this._onDidChangeFile = new vscode.EventEmitter(); + } + + get onDidChangeFile(): vscode.Event { + return this._onDidChangeFile.event; + } + + watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable { + const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => { + const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString())); + + // TODO support excludes (using minimatch library?) + + this._onDidChangeFile.fire([{ + type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted, + uri: uri.with({ path: filepath }) + } as vscode.FileChangeEvent]); + }); + + return { dispose: () => watcher.close() }; + } + + stat(uri: vscode.Uri): vscode.FileStat | Thenable { + return this._stat(uri.fsPath); + } + + async _stat(path: string): Promise { + const res = await _.statLink(path); + return new FileStat(res.stat, res.isSymbolicLink); + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> { + return this._readDirectory(uri); + } + + async _readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { + const children = await _.readdir(uri.fsPath); + + const result: [string, vscode.FileType][] = []; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const stat = await this._stat(path.join(uri.fsPath, child)); + result.push([child, stat.type]); + } + + return Promise.resolve(result); + } + + createDirectory(uri: vscode.Uri): void | Thenable { + return _.mkdir(uri.fsPath); + } + + readFile(uri: vscode.Uri): Uint8Array | Thenable { + return _.readfile(uri.fsPath); + } + + writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Thenable { + return this._writeFile(uri, content, options); + } + + async _writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise { + const exists = await _.exists(uri.fsPath); + if (!exists) { + if (!options.create) { + throw vscode.FileSystemError.FileNotFound(); + } + + await _.mkdir(path.dirname(uri.fsPath)); + } else { + if (!options.overwrite) { + throw vscode.FileSystemError.FileExists(); + } + } + + return _.writefile(uri.fsPath, content as Buffer); + } + + delete(uri: vscode.Uri, options: { recursive: boolean; }): void | Thenable { + if (options.recursive) { + return _.rmrf(uri.fsPath); + } + + return _.unlink(uri.fsPath); + } + + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Thenable { + return this._rename(oldUri, newUri, options); + } + + async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise { + const exists = await _.exists(newUri.fsPath); + if (exists) { + if (!options.overwrite) { + throw vscode.FileSystemError.FileExists(); + } else { + await _.rmrf(newUri.fsPath); + } + } + + const parentExists = await _.exists(path.dirname(newUri.fsPath)); + if (!parentExists) { + await _.mkdir(path.dirname(newUri.fsPath)); + } + + return _.rename(oldUri.fsPath, newUri.fsPath); + } + + // TODO can implement a fast copy() method with node.js 8.x new fs.copy method +} + +//#region Utilities + +export interface IStatAndLink { + stat: fs.Stats; + isSymbolicLink: boolean; +} + +namespace _ { + + function handleResult(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T | undefined): void { + if (error) { + reject(messageError(error)); + } else { + resolve(result!); + } + } + + function messageError(error: Error & { code?: string }): Error { + if (error.code === 'ENOENT') { + return vscode.FileSystemError.FileNotFound(); + } + + if (error.code === 'EISDIR') { + return vscode.FileSystemError.FileIsADirectory(); + } + + if (error.code === 'EEXIST') { + return vscode.FileSystemError.FileExists(); + } + + if (error.code === 'EPERM' || error.code === 'EACCESS') { + return vscode.FileSystemError.NoPermissions(); + } + + return error; + } + + export function checkCancellation(token: vscode.CancellationToken): void { + if (token.isCancellationRequested) { + throw new Error('Operation cancelled'); + } + } + + export function normalizeNFC(items: string): string; + export function normalizeNFC(items: string[]): string[]; + export function normalizeNFC(items: string | string[]): string | string[] { + if (process.platform !== 'darwin') { + return items; + } + + if (Array.isArray(items)) { + return items.map(item => item.normalize('NFC')); + } + + return items.normalize('NFC'); + } + + export function readdir(path: string): Promise { + return new Promise((resolve, reject) => { + fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children))); + }); + } + + export function readfile(path: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer)); + }); + } + + export function writefile(path: string, content: Buffer): Promise { + return new Promise((resolve, reject) => { + fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function exists(path: string): Promise { + return new Promise((resolve, reject) => { + fs.exists(path, exists => handleResult(resolve, reject, null, exists)); + }); + } + + export function rmrf(path: string): Promise { + return new Promise((resolve, reject) => { + rimraf(path, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function mkdir(path: string): Promise { + return new Promise((resolve, reject) => { + mkdirp(path, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function rename(oldPath: string, newPath: string): Promise { + return new Promise((resolve, reject) => { + fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function unlink(path: string): Promise { + return new Promise((resolve, reject) => { + fs.unlink(path, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function statLink(path: string): Promise { + return new Promise((resolve, reject) => { + fs.lstat(path, (error, lstat) => { + if (error || lstat.isSymbolicLink()) { + fs.stat(path, (error, stat) => { + if (error) { + return handleResult(resolve, reject, error, void 0); + } + + handleResult(resolve, reject, error, { stat, isSymbolicLink: lstat && lstat.isSymbolicLink() }); + }); + } else { + handleResult(resolve, reject, error, { stat: lstat, isSymbolicLink: false }); + } + }); + + }); + } +} + +export class FileStat implements vscode.FileStat { + + constructor(private fsStat: fs.Stats, private _isSymbolicLink: boolean) { } + + get type(): vscode.FileType { + let type: number; + if (this._isSymbolicLink) { + type = vscode.FileType.SymbolicLink | (this.fsStat.isDirectory() ? vscode.FileType.Directory : vscode.FileType.File); + } else { + type = this.fsStat.isFile() ? vscode.FileType.File : this.fsStat.isDirectory() ? vscode.FileType.Directory : vscode.FileType.Unknown; + } + + return type; + } + + get isFile(): boolean | undefined { + return this.fsStat.isFile(); + } + + get isDirectory(): boolean | undefined { + return this.fsStat.isDirectory(); + } + + get isSymbolicLink(): boolean | undefined { + return this._isSymbolicLink; + } + + get size(): number { + return this.fsStat.size; + } + + get ctime(): number { + return this.fsStat.ctime.getTime(); + } + + get mtime(): number { + return this.fsStat.mtime.getTime(); + } +} + //#endregion \ No newline at end of file diff --git a/notebook-extend-markdown-renderer-sample/src/emoji.ts b/notebook-extend-markdown-renderer-sample/src/emoji.ts index fe75007d..9b936989 100644 --- a/notebook-extend-markdown-renderer-sample/src/emoji.ts +++ b/notebook-extend-markdown-renderer-sample/src/emoji.ts @@ -1,23 +1,22 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import type * as MarkdownIt from 'markdown-it'; -import type { RendererContext } from 'vscode-notebook-renderer'; - -interface MarkdownItRenderer { - extendMarkdownIt(fn: (md: MarkdownIt) => void): void; -} - -export async function activate(ctx: RendererContext) { - const markdownItRenderer = await ctx.getRenderer('vscode.markdown-it-renderer') as MarkdownItRenderer | undefined; - if (!markdownItRenderer) { - throw new Error(`Could not load 'vscode.markdown-it-renderer'`); - } - - const emoji = require('markdown-it-emoji'); - markdownItRenderer.extendMarkdownIt((md: MarkdownIt) => { - return md.use(emoji, {}); - }) -} - \ No newline at end of file +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import type * as MarkdownIt from 'markdown-it'; +import type { RendererContext } from 'vscode-notebook-renderer'; + +interface MarkdownItRenderer { + extendMarkdownIt(fn: (md: MarkdownIt) => void): void; +} + +export async function activate(ctx: RendererContext) { + const markdownItRenderer = await ctx.getRenderer('vscode.markdown-it-renderer') as MarkdownItRenderer | undefined; + if (!markdownItRenderer) { + throw new Error(`Could not load 'vscode.markdown-it-renderer'`); + } + + const emoji = require('markdown-it-emoji'); + markdownItRenderer.extendMarkdownIt((md: MarkdownIt) => { + return md.use(emoji, {}); + }) +} diff --git a/notebook-renderer-sample/src/css.d.ts b/notebook-renderer-sample/src/css.d.ts index c814f574..606c6480 100644 --- a/notebook-renderer-sample/src/css.d.ts +++ b/notebook-renderer-sample/src/css.d.ts @@ -1,4 +1,4 @@ -declare module '*.css' { - const classes: { [className: string]: string }; - export = classes; -} +declare module '*.css' { + const classes: { [className: string]: string }; + export = classes; +} diff --git a/notebook-renderer-sample/src/index.ts b/notebook-renderer-sample/src/index.ts index b9bd68a3..6aa24fe8 100644 --- a/notebook-renderer-sample/src/index.ts +++ b/notebook-renderer-sample/src/index.ts @@ -1,43 +1,43 @@ -import { render } from './render'; -import errorOverlay from 'vscode-notebook-error-overlay'; -import type { ActivationFunction } from 'vscode-notebook-renderer'; - -// Fix the public path so that any async import()'s work as expected. -// eslint-disable-next-line @typescript-eslint/naming-convention -declare const __webpack_relative_entrypoint_to_root__: string; -declare const scriptUrl: string; - -__webpack_public_path__ = new URL(scriptUrl.replace(/[^/]+$/, '') + __webpack_relative_entrypoint_to_root__).toString(); - -// ---------------------------------------------------------------------------- -// This is the entrypoint to the notebook renderer's webview client-side code. -// This contains some boilerplate that calls the `render()` function when new -// output is available. You probably don't need to change this code; put your -// rendering logic inside of the `render()` function. -// ---------------------------------------------------------------------------- - -export const activate: ActivationFunction = context => { - return { - renderOutputItem(outputItem, element) { - let shadow = element.shadowRoot; - if (!shadow) { - shadow = element.attachShadow({ mode: 'open' }); - const root = document.createElement('div'); - root.id = 'root'; - shadow.append(root); - } - const root = shadow.querySelector('#root')!; - errorOverlay.wrap(root, () => { - root.innerHTML = ''; - const node = document.createElement('div'); - root.appendChild(node); - - render({ container: node, mime: outputItem.mime, value: outputItem.json(), context }); - }); - }, - disposeOutputItem(outputId) { - // Do any teardown here. outputId is the cell output being deleted, or - // undefined if we're clearing all outputs. - } - }; -}; +import { render } from './render'; +import errorOverlay from 'vscode-notebook-error-overlay'; +import type { ActivationFunction } from 'vscode-notebook-renderer'; + +// Fix the public path so that any async import()'s work as expected. +// eslint-disable-next-line @typescript-eslint/naming-convention +declare const __webpack_relative_entrypoint_to_root__: string; +declare const scriptUrl: string; + +__webpack_public_path__ = new URL(scriptUrl.replace(/[^/]+$/, '') + __webpack_relative_entrypoint_to_root__).toString(); + +// ---------------------------------------------------------------------------- +// This is the entrypoint to the notebook renderer's webview client-side code. +// This contains some boilerplate that calls the `render()` function when new +// output is available. You probably don't need to change this code; put your +// rendering logic inside of the `render()` function. +// ---------------------------------------------------------------------------- + +export const activate: ActivationFunction = context => { + return { + renderOutputItem(outputItem, element) { + let shadow = element.shadowRoot; + if (!shadow) { + shadow = element.attachShadow({ mode: 'open' }); + const root = document.createElement('div'); + root.id = 'root'; + shadow.append(root); + } + const root = shadow.querySelector('#root')!; + errorOverlay.wrap(root, () => { + root.innerHTML = ''; + const node = document.createElement('div'); + root.appendChild(node); + + render({ container: node, mime: outputItem.mime, value: outputItem.json(), context }); + }); + }, + disposeOutputItem(outputId) { + // Do any teardown here. outputId is the cell output being deleted, or + // undefined if we're clearing all outputs. + } + }; +}; diff --git a/notebook-renderer-sample/src/render.ts b/notebook-renderer-sample/src/render.ts index 7bc95e25..0e6ba80c 100644 --- a/notebook-renderer-sample/src/render.ts +++ b/notebook-renderer-sample/src/render.ts @@ -1,60 +1,60 @@ -// We've set up this sample using CSS modules, which lets you import class -// names into JavaScript: https://github.com/css-modules/css-modules -// You can configure or change this in the webpack.config.js file. -import * as style from './style.css'; -import type { RendererContext } from 'vscode-notebook-renderer'; - -interface IRenderInfo { - container: HTMLElement; - mime: string; - value: GitHubIssuesValue[]; - context: RendererContext; -} -interface GitHubIssuesValue { - title: string; - url: string; - body: string; -} - -// This function is called to render your contents. -export function render({ container, mime, value }: IRenderInfo) { - // Format the JSON and insert it as
{ ... }
- // Replace this with your custom code! - const pre = document.createElement('pre'); - pre.classList.add(style.json); - - // Create a simple table with issue titles and links - const table = document.createElement('table'); - table.className = 'issues-list'; - const headerRow = document.createElement('tr'); - const tableHeaders = ['Issue', 'Description']; - - tableHeaders.forEach(label => { - const header = document.createElement('th'); - header.textContent = label; - headerRow.appendChild(header); - }); - - table.appendChild(headerRow); - - value.forEach(item => { - const row = document.createElement('tr'); - - const title = document.createElement('td'); - const link = document.createElement('a'); - link.href = item.url; - link.textContent = item.title; - title.appendChild(link); - row.appendChild(title); - - const body = document.createElement('td'); - body.textContent = item.body; - row.appendChild(body); - - table.appendChild(row); - }); - - pre.appendChild(table); - container.appendChild(pre); -} - +// We've set up this sample using CSS modules, which lets you import class +// names into JavaScript: https://github.com/css-modules/css-modules +// You can configure or change this in the webpack.config.js file. +import * as style from './style.css'; +import type { RendererContext } from 'vscode-notebook-renderer'; + +interface IRenderInfo { + container: HTMLElement; + mime: string; + value: GitHubIssuesValue[]; + context: RendererContext; +} +interface GitHubIssuesValue { + title: string; + url: string; + body: string; +} + +// This function is called to render your contents. +export function render({ container, mime, value }: IRenderInfo) { + // Format the JSON and insert it as
{ ... }
+ // Replace this with your custom code! + const pre = document.createElement('pre'); + pre.classList.add(style.json); + + // Create a simple table with issue titles and links + const table = document.createElement('table'); + table.className = 'issues-list'; + const headerRow = document.createElement('tr'); + const tableHeaders = ['Issue', 'Description']; + + tableHeaders.forEach(label => { + const header = document.createElement('th'); + header.textContent = label; + headerRow.appendChild(header); + }); + + table.appendChild(headerRow); + + value.forEach(item => { + const row = document.createElement('tr'); + + const title = document.createElement('td'); + const link = document.createElement('a'); + link.href = item.url; + link.textContent = item.title; + title.appendChild(link); + row.appendChild(title); + + const body = document.createElement('td'); + body.textContent = item.body; + row.appendChild(body); + + table.appendChild(row); + }); + + pre.appendChild(table); + container.appendChild(pre); +} + diff --git a/progress-sample/src/extension.ts b/progress-sample/src/extension.ts index d5b790c6..48bb8a83 100644 --- a/progress-sample/src/extension.ts +++ b/progress-sample/src/extension.ts @@ -1,42 +1,42 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - - -import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands, ProgressLocation } from 'vscode'; - -export function activate(context: ExtensionContext) { - context.subscriptions.push(commands.registerCommand('extension.startTask', () => { - window.withProgress({ - location: ProgressLocation.Notification, - title: "I am long running!", - cancellable: true - }, (progress, token) => { - token.onCancellationRequested(() => { - console.log("User canceled the long running operation"); - }); - - progress.report({ increment: 0 }); - - setTimeout(() => { - progress.report({ increment: 10, message: "I am long running! - still going..." }); - }, 1000); - - setTimeout(() => { - progress.report({ increment: 40, message: "I am long running! - still going even more..." }); - }, 2000); - - setTimeout(() => { - progress.report({ increment: 50, message: "I am long running! - almost there..." }); - }, 3000); - - const p = new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 5000); - }); - - return p; - }); - })); -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + + +import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands, ProgressLocation } from 'vscode'; + +export function activate(context: ExtensionContext) { + context.subscriptions.push(commands.registerCommand('extension.startTask', () => { + window.withProgress({ + location: ProgressLocation.Notification, + title: "I am long running!", + cancellable: true + }, (progress, token) => { + token.onCancellationRequested(() => { + console.log("User canceled the long running operation"); + }); + + progress.report({ increment: 0 }); + + setTimeout(() => { + progress.report({ increment: 10, message: "I am long running! - still going..." }); + }, 1000); + + setTimeout(() => { + progress.report({ increment: 40, message: "I am long running! - still going even more..." }); + }, 2000); + + setTimeout(() => { + progress.report({ increment: 50, message: "I am long running! - almost there..." }); + }, 3000); + + const p = new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 5000); + }); + + return p; + }); + })); +} diff --git a/proposed-api-sample/src/extension.ts b/proposed-api-sample/src/extension.ts index 55f28699..f6728059 100644 --- a/proposed-api-sample/src/extension.ts +++ b/proposed-api-sample/src/extension.ts @@ -1,16 +1,16 @@ -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - console.log('Congratulations, your extension "proposed-api-sample" is now active!'); - - /** - * You can use proposed API here. `vscode.` should start auto complete - * Proposed API as defined in vscode.proposed..d.ts. - */ - - const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { - vscode.window.showInformationMessage('Hello World!'); - }); - - context.subscriptions.push(disposable); -} +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + console.log('Congratulations, your extension "proposed-api-sample" is now active!'); + + /** + * You can use proposed API here. `vscode.` should start auto complete + * Proposed API as defined in vscode.proposed..d.ts. + */ + + const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { + vscode.window.showInformationMessage('Hello World!'); + }); + + context.subscriptions.push(disposable); +} diff --git a/quickinput-sample/src/basicInput.ts b/quickinput-sample/src/basicInput.ts index 4146db1c..28c317f3 100644 --- a/quickinput-sample/src/basicInput.ts +++ b/quickinput-sample/src/basicInput.ts @@ -1,34 +1,34 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { window } from 'vscode'; - -/** - * Shows a pick list using window.showQuickPick(). - */ -export async function showQuickPick() { - let i = 0; - const result = await window.showQuickPick(['eins', 'zwei', 'drei'], { - placeHolder: 'eins, zwei or drei', - onDidSelectItem: item => window.showInformationMessage(`Focus ${++i}: ${item}`) - }); - window.showInformationMessage(`Got: ${result}`); -} - -/** - * Shows an input box using window.showInputBox(). - */ -export async function showInputBox() { - const result = await window.showInputBox({ - value: 'abcdef', - valueSelection: [2, 4], - placeHolder: 'For example: fedcba. But not: 123', - validateInput: text => { - window.showInformationMessage(`Validating: ${text}`); - return text === '123' ? 'Not 123!' : null; - } - }); - window.showInformationMessage(`Got: ${result}`); -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { window } from 'vscode'; + +/** + * Shows a pick list using window.showQuickPick(). + */ +export async function showQuickPick() { + let i = 0; + const result = await window.showQuickPick(['eins', 'zwei', 'drei'], { + placeHolder: 'eins, zwei or drei', + onDidSelectItem: item => window.showInformationMessage(`Focus ${++i}: ${item}`) + }); + window.showInformationMessage(`Got: ${result}`); +} + +/** + * Shows an input box using window.showInputBox(). + */ +export async function showInputBox() { + const result = await window.showInputBox({ + value: 'abcdef', + valueSelection: [2, 4], + placeHolder: 'For example: fedcba. But not: 123', + validateInput: text => { + window.showInformationMessage(`Validating: ${text}`); + return text === '123' ? 'Not 123!' : null; + } + }); + window.showInformationMessage(`Got: ${result}`); +} diff --git a/quickinput-sample/src/extension.ts b/quickinput-sample/src/extension.ts index e2bb6371..23c62aa4 100644 --- a/quickinput-sample/src/extension.ts +++ b/quickinput-sample/src/extension.ts @@ -1,30 +1,30 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { window, commands, ExtensionContext } from 'vscode'; -import { showQuickPick, showInputBox } from './basicInput'; -import { multiStepInput } from './multiStepInput'; -import { quickOpen } from './quickOpen'; - -export function activate(context: ExtensionContext) { - context.subscriptions.push(commands.registerCommand('samples.quickInput', async () => { - const options: { [key: string]: (context: ExtensionContext) => Promise } = { - showQuickPick, - showInputBox, - multiStepInput, - quickOpen, - }; - const quickPick = window.createQuickPick(); - quickPick.items = Object.keys(options).map(label => ({ label })); - quickPick.onDidChangeSelection(selection => { - if (selection[0]) { - options[selection[0].label](context) - .catch(console.error); - } - }); - quickPick.onDidHide(() => quickPick.dispose()); - quickPick.show(); - })); -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { window, commands, ExtensionContext } from 'vscode'; +import { showQuickPick, showInputBox } from './basicInput'; +import { multiStepInput } from './multiStepInput'; +import { quickOpen } from './quickOpen'; + +export function activate(context: ExtensionContext) { + context.subscriptions.push(commands.registerCommand('samples.quickInput', async () => { + const options: { [key: string]: (context: ExtensionContext) => Promise } = { + showQuickPick, + showInputBox, + multiStepInput, + quickOpen, + }; + const quickPick = window.createQuickPick(); + quickPick.items = Object.keys(options).map(label => ({ label })); + quickPick.onDidChangeSelection(selection => { + if (selection[0]) { + options[selection[0].label](context) + .catch(console.error); + } + }); + quickPick.onDidHide(() => quickPick.dispose()); + quickPick.show(); + })); +} diff --git a/quickinput-sample/src/multiStepInput.ts b/quickinput-sample/src/multiStepInput.ts index ed29b160..999beb6e 100644 --- a/quickinput-sample/src/multiStepInput.ts +++ b/quickinput-sample/src/multiStepInput.ts @@ -1,306 +1,306 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { QuickPickItem, window, Disposable, CancellationToken, QuickInputButton, QuickInput, ExtensionContext, QuickInputButtons, Uri } from 'vscode'; - -/** - * A multi-step input using window.createQuickPick() and window.createInputBox(). - * - * This first part uses the helper class `MultiStepInput` that wraps the API for the multi-step case. - */ -export async function multiStepInput(context: ExtensionContext) { - - class MyButton implements QuickInputButton { - constructor(public iconPath: { light: Uri; dark: Uri; }, public tooltip: string) { } - } - - const createResourceGroupButton = new MyButton({ - dark: Uri.file(context.asAbsolutePath('resources/dark/add.svg')), - light: Uri.file(context.asAbsolutePath('resources/light/add.svg')), - }, 'Create Resource Group'); - - const resourceGroups: QuickPickItem[] = ['vscode-data-function', 'vscode-appservice-microservices', 'vscode-appservice-monitor', 'vscode-appservice-preview', 'vscode-appservice-prod'] - .map(label => ({ label })); - - - interface State { - title: string; - step: number; - totalSteps: number; - resourceGroup: QuickPickItem | string; - name: string; - runtime: QuickPickItem; - } - - async function collectInputs() { - const state = {} as Partial; - await MultiStepInput.run(input => pickResourceGroup(input, state)); - return state as State; - } - - const title = 'Create Application Service'; - - async function pickResourceGroup(input: MultiStepInput, state: Partial) { - const pick = await input.showQuickPick({ - title, - step: 1, - totalSteps: 3, - placeholder: 'Pick a resource group', - items: resourceGroups, - activeItem: typeof state.resourceGroup !== 'string' ? state.resourceGroup : undefined, - buttons: [createResourceGroupButton], - shouldResume: shouldResume - }); - if (pick instanceof MyButton) { - return (input: MultiStepInput) => inputResourceGroupName(input, state); - } - state.resourceGroup = pick; - return (input: MultiStepInput) => inputName(input, state); - } - - async function inputResourceGroupName(input: MultiStepInput, state: Partial) { - state.resourceGroup = await input.showInputBox({ - title, - step: 2, - totalSteps: 4, - value: typeof state.resourceGroup === 'string' ? state.resourceGroup : '', - prompt: 'Choose a unique name for the resource group', - validate: validateNameIsUnique, - shouldResume: shouldResume - }); - return (input: MultiStepInput) => inputName(input, state); - } - - async function inputName(input: MultiStepInput, state: Partial) { - const additionalSteps = typeof state.resourceGroup === 'string' ? 1 : 0; - // TODO: Remember current value when navigating back. - state.name = await input.showInputBox({ - title, - step: 2 + additionalSteps, - totalSteps: 3 + additionalSteps, - value: state.name || '', - prompt: 'Choose a unique name for the Application Service', - validate: validateNameIsUnique, - shouldResume: shouldResume - }); - return (input: MultiStepInput) => pickRuntime(input, state); - } - - async function pickRuntime(input: MultiStepInput, state: Partial) { - const additionalSteps = typeof state.resourceGroup === 'string' ? 1 : 0; - const runtimes = await getAvailableRuntimes(state.resourceGroup!, undefined /* TODO: token */); - // TODO: Remember currently active item when navigating back. - state.runtime = await input.showQuickPick({ - title, - step: 3 + additionalSteps, - totalSteps: 3 + additionalSteps, - placeholder: 'Pick a runtime', - items: runtimes, - activeItem: state.runtime, - shouldResume: shouldResume - }); - } - - function shouldResume() { - // Could show a notification with the option to resume. - return new Promise((resolve, reject) => { - // noop - }); - } - - async function validateNameIsUnique(name: string) { - // ...validate... - await new Promise(resolve => setTimeout(resolve, 1000)); - return name === 'vscode' ? 'Name not unique' : undefined; - } - - async function getAvailableRuntimes(resourceGroup: QuickPickItem | string, token?: CancellationToken): Promise { - // ...retrieve... - await new Promise(resolve => setTimeout(resolve, 1000)); - return ['Node 8.9', 'Node 6.11', 'Node 4.5'] - .map(label => ({ label })); - } - - const state = await collectInputs(); - window.showInformationMessage(`Creating Application Service '${state.name}'`); -} - - -// ------------------------------------------------------- -// Helper code that wraps the API for the multi-step case. -// ------------------------------------------------------- - - -class InputFlowAction { - static back = new InputFlowAction(); - static cancel = new InputFlowAction(); - static resume = new InputFlowAction(); -} - -type InputStep = (input: MultiStepInput) => Thenable; - -interface QuickPickParameters { - title: string; - step: number; - totalSteps: number; - items: T[]; - activeItem?: T; - placeholder: string; - buttons?: QuickInputButton[]; - shouldResume: () => Thenable; -} - -interface InputBoxParameters { - title: string; - step: number; - totalSteps: number; - value: string; - prompt: string; - validate: (value: string) => Promise; - buttons?: QuickInputButton[]; - shouldResume: () => Thenable; -} - -class MultiStepInput { - - static async run(start: InputStep) { - const input = new MultiStepInput(); - return input.stepThrough(start); - } - - private current?: QuickInput; - private steps: InputStep[] = []; - - private async stepThrough(start: InputStep) { - let step: InputStep | void = start; - while (step) { - this.steps.push(step); - if (this.current) { - this.current.enabled = false; - this.current.busy = true; - } - try { - step = await step(this); - } catch (err) { - if (err === InputFlowAction.back) { - this.steps.pop(); - step = this.steps.pop(); - } else if (err === InputFlowAction.resume) { - step = this.steps.pop(); - } else if (err === InputFlowAction.cancel) { - step = undefined; - } else { - throw err; - } - } - } - if (this.current) { - this.current.dispose(); - } - } - - async showQuickPick>({ title, step, totalSteps, items, activeItem, placeholder, buttons, shouldResume }: P) { - const disposables: Disposable[] = []; - try { - return await new Promise((resolve, reject) => { - const input = window.createQuickPick(); - input.title = title; - input.step = step; - input.totalSteps = totalSteps; - input.placeholder = placeholder; - input.items = items; - if (activeItem) { - input.activeItems = [activeItem]; - } - input.buttons = [ - ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), - ...(buttons || []) - ]; - disposables.push( - input.onDidTriggerButton(item => { - if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); - } else { - resolve(item); - } - }), - input.onDidChangeSelection(items => resolve(items[0])), - input.onDidHide(() => { - (async () => { - reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel); - })() - .catch(reject); - }) - ); - if (this.current) { - this.current.dispose(); - } - this.current = input; - this.current.show(); - }); - } finally { - disposables.forEach(d => d.dispose()); - } - } - - async showInputBox

({ title, step, totalSteps, value, prompt, validate, buttons, shouldResume }: P) { - const disposables: Disposable[] = []; - try { - return await new Promise((resolve, reject) => { - const input = window.createInputBox(); - input.title = title; - input.step = step; - input.totalSteps = totalSteps; - input.value = value || ''; - input.prompt = prompt; - input.buttons = [ - ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), - ...(buttons || []) - ]; - let validating = validate(''); - disposables.push( - input.onDidTriggerButton(item => { - if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); - } else { - resolve(item); - } - }), - input.onDidAccept(async () => { - const value = input.value; - input.enabled = false; - input.busy = true; - if (!(await validate(value))) { - resolve(value); - } - input.enabled = true; - input.busy = false; - }), - input.onDidChangeValue(async text => { - const current = validate(text); - validating = current; - const validationMessage = await current; - if (current === validating) { - input.validationMessage = validationMessage; - } - }), - input.onDidHide(() => { - (async () => { - reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel); - })() - .catch(reject); - }) - ); - if (this.current) { - this.current.dispose(); - } - this.current = input; - this.current.show(); - }); - } finally { - disposables.forEach(d => d.dispose()); - } - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { QuickPickItem, window, Disposable, CancellationToken, QuickInputButton, QuickInput, ExtensionContext, QuickInputButtons, Uri } from 'vscode'; + +/** + * A multi-step input using window.createQuickPick() and window.createInputBox(). + * + * This first part uses the helper class `MultiStepInput` that wraps the API for the multi-step case. + */ +export async function multiStepInput(context: ExtensionContext) { + + class MyButton implements QuickInputButton { + constructor(public iconPath: { light: Uri; dark: Uri; }, public tooltip: string) { } + } + + const createResourceGroupButton = new MyButton({ + dark: Uri.file(context.asAbsolutePath('resources/dark/add.svg')), + light: Uri.file(context.asAbsolutePath('resources/light/add.svg')), + }, 'Create Resource Group'); + + const resourceGroups: QuickPickItem[] = ['vscode-data-function', 'vscode-appservice-microservices', 'vscode-appservice-monitor', 'vscode-appservice-preview', 'vscode-appservice-prod'] + .map(label => ({ label })); + + + interface State { + title: string; + step: number; + totalSteps: number; + resourceGroup: QuickPickItem | string; + name: string; + runtime: QuickPickItem; + } + + async function collectInputs() { + const state = {} as Partial; + await MultiStepInput.run(input => pickResourceGroup(input, state)); + return state as State; + } + + const title = 'Create Application Service'; + + async function pickResourceGroup(input: MultiStepInput, state: Partial) { + const pick = await input.showQuickPick({ + title, + step: 1, + totalSteps: 3, + placeholder: 'Pick a resource group', + items: resourceGroups, + activeItem: typeof state.resourceGroup !== 'string' ? state.resourceGroup : undefined, + buttons: [createResourceGroupButton], + shouldResume: shouldResume + }); + if (pick instanceof MyButton) { + return (input: MultiStepInput) => inputResourceGroupName(input, state); + } + state.resourceGroup = pick; + return (input: MultiStepInput) => inputName(input, state); + } + + async function inputResourceGroupName(input: MultiStepInput, state: Partial) { + state.resourceGroup = await input.showInputBox({ + title, + step: 2, + totalSteps: 4, + value: typeof state.resourceGroup === 'string' ? state.resourceGroup : '', + prompt: 'Choose a unique name for the resource group', + validate: validateNameIsUnique, + shouldResume: shouldResume + }); + return (input: MultiStepInput) => inputName(input, state); + } + + async function inputName(input: MultiStepInput, state: Partial) { + const additionalSteps = typeof state.resourceGroup === 'string' ? 1 : 0; + // TODO: Remember current value when navigating back. + state.name = await input.showInputBox({ + title, + step: 2 + additionalSteps, + totalSteps: 3 + additionalSteps, + value: state.name || '', + prompt: 'Choose a unique name for the Application Service', + validate: validateNameIsUnique, + shouldResume: shouldResume + }); + return (input: MultiStepInput) => pickRuntime(input, state); + } + + async function pickRuntime(input: MultiStepInput, state: Partial) { + const additionalSteps = typeof state.resourceGroup === 'string' ? 1 : 0; + const runtimes = await getAvailableRuntimes(state.resourceGroup!, undefined /* TODO: token */); + // TODO: Remember currently active item when navigating back. + state.runtime = await input.showQuickPick({ + title, + step: 3 + additionalSteps, + totalSteps: 3 + additionalSteps, + placeholder: 'Pick a runtime', + items: runtimes, + activeItem: state.runtime, + shouldResume: shouldResume + }); + } + + function shouldResume() { + // Could show a notification with the option to resume. + return new Promise((resolve, reject) => { + // noop + }); + } + + async function validateNameIsUnique(name: string) { + // ...validate... + await new Promise(resolve => setTimeout(resolve, 1000)); + return name === 'vscode' ? 'Name not unique' : undefined; + } + + async function getAvailableRuntimes(resourceGroup: QuickPickItem | string, token?: CancellationToken): Promise { + // ...retrieve... + await new Promise(resolve => setTimeout(resolve, 1000)); + return ['Node 8.9', 'Node 6.11', 'Node 4.5'] + .map(label => ({ label })); + } + + const state = await collectInputs(); + window.showInformationMessage(`Creating Application Service '${state.name}'`); +} + + +// ------------------------------------------------------- +// Helper code that wraps the API for the multi-step case. +// ------------------------------------------------------- + + +class InputFlowAction { + static back = new InputFlowAction(); + static cancel = new InputFlowAction(); + static resume = new InputFlowAction(); +} + +type InputStep = (input: MultiStepInput) => Thenable; + +interface QuickPickParameters { + title: string; + step: number; + totalSteps: number; + items: T[]; + activeItem?: T; + placeholder: string; + buttons?: QuickInputButton[]; + shouldResume: () => Thenable; +} + +interface InputBoxParameters { + title: string; + step: number; + totalSteps: number; + value: string; + prompt: string; + validate: (value: string) => Promise; + buttons?: QuickInputButton[]; + shouldResume: () => Thenable; +} + +class MultiStepInput { + + static async run(start: InputStep) { + const input = new MultiStepInput(); + return input.stepThrough(start); + } + + private current?: QuickInput; + private steps: InputStep[] = []; + + private async stepThrough(start: InputStep) { + let step: InputStep | void = start; + while (step) { + this.steps.push(step); + if (this.current) { + this.current.enabled = false; + this.current.busy = true; + } + try { + step = await step(this); + } catch (err) { + if (err === InputFlowAction.back) { + this.steps.pop(); + step = this.steps.pop(); + } else if (err === InputFlowAction.resume) { + step = this.steps.pop(); + } else if (err === InputFlowAction.cancel) { + step = undefined; + } else { + throw err; + } + } + } + if (this.current) { + this.current.dispose(); + } + } + + async showQuickPick>({ title, step, totalSteps, items, activeItem, placeholder, buttons, shouldResume }: P) { + const disposables: Disposable[] = []; + try { + return await new Promise((resolve, reject) => { + const input = window.createQuickPick(); + input.title = title; + input.step = step; + input.totalSteps = totalSteps; + input.placeholder = placeholder; + input.items = items; + if (activeItem) { + input.activeItems = [activeItem]; + } + input.buttons = [ + ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), + ...(buttons || []) + ]; + disposables.push( + input.onDidTriggerButton(item => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + } else { + resolve(item); + } + }), + input.onDidChangeSelection(items => resolve(items[0])), + input.onDidHide(() => { + (async () => { + reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel); + })() + .catch(reject); + }) + ); + if (this.current) { + this.current.dispose(); + } + this.current = input; + this.current.show(); + }); + } finally { + disposables.forEach(d => d.dispose()); + } + } + + async showInputBox

({ title, step, totalSteps, value, prompt, validate, buttons, shouldResume }: P) { + const disposables: Disposable[] = []; + try { + return await new Promise((resolve, reject) => { + const input = window.createInputBox(); + input.title = title; + input.step = step; + input.totalSteps = totalSteps; + input.value = value || ''; + input.prompt = prompt; + input.buttons = [ + ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), + ...(buttons || []) + ]; + let validating = validate(''); + disposables.push( + input.onDidTriggerButton(item => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + } else { + resolve(item); + } + }), + input.onDidAccept(async () => { + const value = input.value; + input.enabled = false; + input.busy = true; + if (!(await validate(value))) { + resolve(value); + } + input.enabled = true; + input.busy = false; + }), + input.onDidChangeValue(async text => { + const current = validate(text); + validating = current; + const validationMessage = await current; + if (current === validating) { + input.validationMessage = validationMessage; + } + }), + input.onDidHide(() => { + (async () => { + reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel); + })() + .catch(reject); + }) + ); + if (this.current) { + this.current.dispose(); + } + this.current = input; + this.current.show(); + }); + } finally { + disposables.forEach(d => d.dispose()); + } + } +} diff --git a/quickinput-sample/src/quickOpen.ts b/quickinput-sample/src/quickOpen.ts index 3fb95cfa..709d2819 100644 --- a/quickinput-sample/src/quickOpen.ts +++ b/quickinput-sample/src/quickOpen.ts @@ -1,112 +1,112 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import * as cp from 'child_process'; -import { Uri, window, Disposable } from 'vscode'; -import { QuickPickItem } from 'vscode'; -import { workspace } from 'vscode'; - -/** - * A file opener using window.createQuickPick(). - * - * It shows how the list of items can be dynamically updated based on - * the user's input in the filter field. - */ -export async function quickOpen() { - const uri = await pickFile(); - if (uri) { - const document = await workspace.openTextDocument(uri); - await window.showTextDocument(document); - } -} - -class FileItem implements QuickPickItem { - - label: string; - description: string; - - constructor(public base: Uri, public uri: Uri) { - this.label = path.basename(uri.fsPath); - this.description = path.dirname(path.relative(base.fsPath, uri.fsPath)); - } -} - -class MessageItem implements QuickPickItem { - - label: string; - description = ''; - detail: string; - - constructor(public base: Uri, public message: string) { - this.label = message.replace(/\r?\n/g, ' '); - this.detail = base.fsPath; - } -} - -async function pickFile() { - const disposables: Disposable[] = []; - try { - return await new Promise((resolve, reject) => { - const input = window.createQuickPick(); - input.placeholder = 'Type to search for files'; - let rgs: cp.ChildProcess[] = []; - disposables.push( - input.onDidChangeValue(value => { - rgs.forEach(rg => rg.kill()); - if (!value) { - input.items = []; - return; - } - input.busy = true; - const cwds = workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.fsPath) : [process.cwd()]; - const q = process.platform === 'win32' ? '"' : '\''; - rgs = cwds.map(cwd => { - const rg = cp.exec(`rg --files -g ${q}*${value}*${q}`, { cwd }, (err, stdout) => { - const i = rgs.indexOf(rg); - if (i !== -1) { - if (rgs.length === cwds.length) { - input.items = []; - } - if (!err) { - input.items = input.items.concat( - stdout - .split('\n').slice(0, 50) - .map(relative => new FileItem(Uri.file(cwd), Uri.file(path.join(cwd, relative)))) - ); - } - if (err && !(err).killed && (err).code !== 1 && err.message) { - input.items = input.items.concat([ - new MessageItem(Uri.file(cwd), err.message) - ]); - } - rgs.splice(i, 1); - if (!rgs.length) { - input.busy = false; - } - } - }); - return rg; - }); - }), - input.onDidChangeSelection(items => { - const item = items[0]; - if (item instanceof FileItem) { - resolve(item.uri); - input.hide(); - } - }), - input.onDidHide(() => { - rgs.forEach(rg => rg.kill()); - resolve(undefined); - input.dispose(); - }) - ); - input.show(); - }); - } finally { - disposables.forEach(d => d.dispose()); - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as cp from 'child_process'; +import { Uri, window, Disposable } from 'vscode'; +import { QuickPickItem } from 'vscode'; +import { workspace } from 'vscode'; + +/** + * A file opener using window.createQuickPick(). + * + * It shows how the list of items can be dynamically updated based on + * the user's input in the filter field. + */ +export async function quickOpen() { + const uri = await pickFile(); + if (uri) { + const document = await workspace.openTextDocument(uri); + await window.showTextDocument(document); + } +} + +class FileItem implements QuickPickItem { + + label: string; + description: string; + + constructor(public base: Uri, public uri: Uri) { + this.label = path.basename(uri.fsPath); + this.description = path.dirname(path.relative(base.fsPath, uri.fsPath)); + } +} + +class MessageItem implements QuickPickItem { + + label: string; + description = ''; + detail: string; + + constructor(public base: Uri, public message: string) { + this.label = message.replace(/\r?\n/g, ' '); + this.detail = base.fsPath; + } +} + +async function pickFile() { + const disposables: Disposable[] = []; + try { + return await new Promise((resolve, reject) => { + const input = window.createQuickPick(); + input.placeholder = 'Type to search for files'; + let rgs: cp.ChildProcess[] = []; + disposables.push( + input.onDidChangeValue(value => { + rgs.forEach(rg => rg.kill()); + if (!value) { + input.items = []; + return; + } + input.busy = true; + const cwds = workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.fsPath) : [process.cwd()]; + const q = process.platform === 'win32' ? '"' : '\''; + rgs = cwds.map(cwd => { + const rg = cp.exec(`rg --files -g ${q}*${value}*${q}`, { cwd }, (err, stdout) => { + const i = rgs.indexOf(rg); + if (i !== -1) { + if (rgs.length === cwds.length) { + input.items = []; + } + if (!err) { + input.items = input.items.concat( + stdout + .split('\n').slice(0, 50) + .map(relative => new FileItem(Uri.file(cwd), Uri.file(path.join(cwd, relative)))) + ); + } + if (err && !(err).killed && (err).code !== 1 && err.message) { + input.items = input.items.concat([ + new MessageItem(Uri.file(cwd), err.message) + ]); + } + rgs.splice(i, 1); + if (!rgs.length) { + input.busy = false; + } + } + }); + return rg; + }); + }), + input.onDidChangeSelection(items => { + const item = items[0]; + if (item instanceof FileItem) { + resolve(item.uri); + input.hide(); + } + }), + input.onDidHide(() => { + rgs.forEach(rg => rg.kill()); + resolve(undefined); + input.dispose(); + }) + ); + input.show(); + }); + } finally { + disposables.forEach(d => d.dispose()); + } +} diff --git a/semantic-tokens-sample/src/extension.ts b/semantic-tokens-sample/src/extension.ts index 7407462c..2d059121 100644 --- a/semantic-tokens-sample/src/extension.ts +++ b/semantic-tokens-sample/src/extension.ts @@ -1,103 +1,103 @@ -import * as vscode from 'vscode'; - -const tokenTypes = new Map(); -const tokenModifiers = new Map(); - -const legend = (function () { - const tokenTypesLegend = [ - 'comment', 'string', 'keyword', 'number', 'regexp', 'operator', 'namespace', - 'type', 'struct', 'class', 'interface', 'enum', 'typeParameter', 'function', - 'method', 'decorator', 'macro', 'variable', 'parameter', 'property', 'label' - ]; - tokenTypesLegend.forEach((tokenType, index) => tokenTypes.set(tokenType, index)); - - const tokenModifiersLegend = [ - 'declaration', 'documentation', 'readonly', 'static', 'abstract', 'deprecated', - 'modification', 'async' - ]; - tokenModifiersLegend.forEach((tokenModifier, index) => tokenModifiers.set(tokenModifier, index)); - - return new vscode.SemanticTokensLegend(tokenTypesLegend, tokenModifiersLegend); -})(); - -export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push(vscode.languages.registerDocumentSemanticTokensProvider({ language: 'semanticLanguage'}, new DocumentSemanticTokensProvider(), legend)); -} - -interface IParsedToken { - line: number; - startCharacter: number; - length: number; - tokenType: string; - tokenModifiers: string[]; -} - -class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensProvider { - async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { - const allTokens = this._parseText(document.getText()); - const builder = new vscode.SemanticTokensBuilder(); - allTokens.forEach((token) => { - builder.push(token.line, token.startCharacter, token.length, this._encodeTokenType(token.tokenType), this._encodeTokenModifiers(token.tokenModifiers)); - }); - return builder.build(); - } - - private _encodeTokenType(tokenType: string): number { - if (tokenTypes.has(tokenType)) { - return tokenTypes.get(tokenType)!; - } else if (tokenType === 'notInLegend') { - return tokenTypes.size + 2; - } - return 0; - } - - private _encodeTokenModifiers(strTokenModifiers: string[]): number { - let result = 0; - for (let i = 0; i < strTokenModifiers.length; i++) { - const tokenModifier = strTokenModifiers[i]; - if (tokenModifiers.has(tokenModifier)) { - result = result | (1 << tokenModifiers.get(tokenModifier)!); - } else if (tokenModifier === 'notInLegend') { - result = result | (1 << tokenModifiers.size + 2); - } - } - return result; - } - - private _parseText(text: string): IParsedToken[] { - const r: IParsedToken[] = []; - const lines = text.split(/\r\n|\r|\n/); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - let currentOffset = 0; - do { - const openOffset = line.indexOf('[', currentOffset); - if (openOffset === -1) { - break; - } - const closeOffset = line.indexOf(']', openOffset); - if (closeOffset === -1) { - break; - } - const tokenData = this._parseTextToken(line.substring(openOffset + 1, closeOffset)); - r.push({ - line: i, - startCharacter: openOffset + 1, - length: closeOffset - openOffset - 1, - tokenType: tokenData.tokenType, - tokenModifiers: tokenData.tokenModifiers - }); - currentOffset = closeOffset; - } while (true); - } - return r; - } - - private _parseTextToken(text: string): { tokenType: string; tokenModifiers: string[]; } { - const parts = text.split('.'); - return { - tokenType: parts[0], - tokenModifiers: parts.slice(1) - }; - } -} +import * as vscode from 'vscode'; + +const tokenTypes = new Map(); +const tokenModifiers = new Map(); + +const legend = (function() { + const tokenTypesLegend = [ + 'comment', 'string', 'keyword', 'number', 'regexp', 'operator', 'namespace', + 'type', 'struct', 'class', 'interface', 'enum', 'typeParameter', 'function', + 'method', 'decorator', 'macro', 'variable', 'parameter', 'property', 'label' + ]; + tokenTypesLegend.forEach((tokenType, index) => tokenTypes.set(tokenType, index)); + + const tokenModifiersLegend = [ + 'declaration', 'documentation', 'readonly', 'static', 'abstract', 'deprecated', + 'modification', 'async' + ]; + tokenModifiersLegend.forEach((tokenModifier, index) => tokenModifiers.set(tokenModifier, index)); + + return new vscode.SemanticTokensLegend(tokenTypesLegend, tokenModifiersLegend); +})(); + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push(vscode.languages.registerDocumentSemanticTokensProvider({ language: 'semanticLanguage' }, new DocumentSemanticTokensProvider(), legend)); +} + +interface IParsedToken { + line: number; + startCharacter: number; + length: number; + tokenType: string; + tokenModifiers: string[]; +} + +class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensProvider { + async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + const allTokens = this._parseText(document.getText()); + const builder = new vscode.SemanticTokensBuilder(); + allTokens.forEach((token) => { + builder.push(token.line, token.startCharacter, token.length, this._encodeTokenType(token.tokenType), this._encodeTokenModifiers(token.tokenModifiers)); + }); + return builder.build(); + } + + private _encodeTokenType(tokenType: string): number { + if (tokenTypes.has(tokenType)) { + return tokenTypes.get(tokenType)!; + } else if (tokenType === 'notInLegend') { + return tokenTypes.size + 2; + } + return 0; + } + + private _encodeTokenModifiers(strTokenModifiers: string[]): number { + let result = 0; + for (let i = 0; i < strTokenModifiers.length; i++) { + const tokenModifier = strTokenModifiers[i]; + if (tokenModifiers.has(tokenModifier)) { + result = result | (1 << tokenModifiers.get(tokenModifier)!); + } else if (tokenModifier === 'notInLegend') { + result = result | (1 << tokenModifiers.size + 2); + } + } + return result; + } + + private _parseText(text: string): IParsedToken[] { + const r: IParsedToken[] = []; + const lines = text.split(/\r\n|\r|\n/); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + let currentOffset = 0; + do { + const openOffset = line.indexOf('[', currentOffset); + if (openOffset === -1) { + break; + } + const closeOffset = line.indexOf(']', openOffset); + if (closeOffset === -1) { + break; + } + const tokenData = this._parseTextToken(line.substring(openOffset + 1, closeOffset)); + r.push({ + line: i, + startCharacter: openOffset + 1, + length: closeOffset - openOffset - 1, + tokenType: tokenData.tokenType, + tokenModifiers: tokenData.tokenModifiers + }); + currentOffset = closeOffset; + } while (true); + } + return r; + } + + private _parseTextToken(text: string): { tokenType: string; tokenModifiers: string[]; } { + const parts = text.split('.'); + return { + tokenType: parts[0], + tokenModifiers: parts.slice(1) + }; + } +} diff --git a/source-control-sample/src/afs.ts b/source-control-sample/src/afs.ts index dacbcff1..18b1c67d 100644 --- a/source-control-sample/src/afs.ts +++ b/source-control-sample/src/afs.ts @@ -1,64 +1,64 @@ -// This file promisifies necessary file system functions. -// This should be removed when VS Code updates to Node.js ^11.14 and replaced by the native fs promises. - -import * as vscode from 'vscode'; -import * as fs from 'fs'; - -function handleResult(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void { - if (error) { - reject(massageError(error)); - } else { - resolve(result); - } -} - - -function massageError(error: Error & { code?: string }): Error { - if (error.code === 'ENOENT') { - return vscode.FileSystemError.FileNotFound(); - } - - if (error.code === 'EISDIR') { - return vscode.FileSystemError.FileIsADirectory(); - } - - if (error.code === 'EEXIST') { - return vscode.FileSystemError.FileExists(); - } - - if (error.code === 'EPERM' || error.code === 'EACCESS') { - return vscode.FileSystemError.NoPermissions(); - } - - return error; -} - -export function readFile(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer)); - }); -} - -export function writeFile(path: string, content: Buffer): Promise { - return new Promise((resolve, reject) => { - fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0)); - }); -} - -export function exists(path: string): Promise { - return new Promise((resolve, reject) => { - fs.exists(path, exists => handleResult(resolve, reject, null, exists)); - }); -} - -export function readdir(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readdir(path, (error, files) => handleResult(resolve, reject, error, files)); - }); -} - -export function unlink(path: string): Promise { - return new Promise((resolve, reject) => { - fs.unlink(path, error => handleResult(resolve, reject, error, void 0)); - }); +// This file promisifies necessary file system functions. +// This should be removed when VS Code updates to Node.js ^11.14 and replaced by the native fs promises. + +import * as vscode from 'vscode'; +import * as fs from 'fs'; + +function handleResult(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void { + if (error) { + reject(massageError(error)); + } else { + resolve(result); + } +} + + +function massageError(error: Error & { code?: string }): Error { + if (error.code === 'ENOENT') { + return vscode.FileSystemError.FileNotFound(); + } + + if (error.code === 'EISDIR') { + return vscode.FileSystemError.FileIsADirectory(); + } + + if (error.code === 'EEXIST') { + return vscode.FileSystemError.FileExists(); + } + + if (error.code === 'EPERM' || error.code === 'EACCESS') { + return vscode.FileSystemError.NoPermissions(); + } + + return error; +} + +export function readFile(path: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer)); + }); +} + +export function writeFile(path: string, content: Buffer): Promise { + return new Promise((resolve, reject) => { + fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0)); + }); +} + +export function exists(path: string): Promise { + return new Promise((resolve, reject) => { + fs.exists(path, exists => handleResult(resolve, reject, null, exists)); + }); +} + +export function readdir(path: string): Promise { + return new Promise((resolve, reject) => { + fs.readdir(path, (error, files) => handleResult(resolve, reject, error, files)); + }); +} + +export function unlink(path: string): Promise { + return new Promise((resolve, reject) => { + fs.unlink(path, error => handleResult(resolve, reject, error, void 0)); + }); } \ No newline at end of file diff --git a/source-control-sample/src/extension.ts b/source-control-sample/src/extension.ts index 3a6bac09..04f22cd0 100644 --- a/source-control-sample/src/extension.ts +++ b/source-control-sample/src/extension.ts @@ -1,349 +1,349 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; -import { JSFIDDLE_SCHEME } from './fiddleRepository'; -import { FiddleSourceControl, CONFIGURATION_FILE } from './fiddleSourceControl'; -import { JSFiddleDocumentContentProvider } from './fiddleDocumentContentProvider'; -import * as path from 'path'; -import * as afs from './afs'; -import { FiddleConfiguration, parseFiddleId } from './fiddleConfiguration'; -import { firstIndex, UTF8 } from './util'; - -const SOURCE_CONTROL_OPEN_COMMAND = 'extension.source-control.open'; -let jsFiddleDocumentContentProvider: JSFiddleDocumentContentProvider; -const fiddleSourceControlRegister = new Map(); - -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed -export async function activate(context: vscode.ExtensionContext) { - console.log('Congratulations, your extension "source-control-sample" is now active!'); - - jsFiddleDocumentContentProvider = new JSFiddleDocumentContentProvider(); - - try { - await initializeFromConfigurationFile(context); - } - catch (err) { - console.log('Failed to initialize a Fiddle workspace.'); - vscode.window.showErrorMessage(err); - } - - const openCommand = vscode.commands.registerCommand(SOURCE_CONTROL_OPEN_COMMAND, - (fiddleId?: string, workspaceUri?: vscode.Uri) => { - tryOpenFiddle(context, fiddleId, workspaceUri); - }); - context.subscriptions.push(openCommand); - - context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(JSFIDDLE_SCHEME, jsFiddleDocumentContentProvider)); - - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.refresh", - async (sourceControlPane: vscode.SourceControl) => { - const sourceControl = await pickSourceControl(sourceControlPane); - if (sourceControl) { sourceControl.refresh(); } - })); - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.discard", - async (sourceControlPane: vscode.SourceControl) => { - const sourceControl = await pickSourceControl(sourceControlPane); - if (sourceControl) { sourceControl.resetFilesToCheckedOutVersion(); } - })); - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.commit", - async (sourceControlPane: vscode.SourceControl) => { - const 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(null); - if (sourceControl) { sourceControl.tryCheckout(newVersion); } - })); - context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.browse", - async (sourceControlPane: vscode.SourceControl) => { - const sourceControl = await pickSourceControl(sourceControlPane); - if (sourceControl) { sourceControl.openInBrowser(); } - })); - - - context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(e => { - try { - // initialize new source control for manually added workspace folders - e.added.forEach(wf => { - initializeFolderFromConfigurationFile(wf, context); - }); - } catch (ex) { - vscode.window.showErrorMessage(ex.message); - } finally { - // dispose source control for removed workspace folders - e.removed.forEach(wf => { - unregisterFiddleSourceControl(wf.uri); - }); - } - })); -} - -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; } - else if (fiddleSourceControlRegister.size === 1) { return [...fiddleSourceControlRegister.values()][0]; } - else { - - const picks = [...fiddleSourceControlRegister.values()].map(fsc => new RepositoryPick(fsc)); - - if (vscode.window.activeTextEditor) { - const activeWorkspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri); - const activeSourceControl = activeWorkspaceFolder && fiddleSourceControlRegister.get(activeWorkspaceFolder.uri); - const activeIndex = firstIndex(picks, pick => pick.fiddleSourceControl === activeSourceControl); - - // if there is an active editor, move its folder to be the first in the pick list - if (activeIndex > -1) { - picks.unshift(...picks.splice(activeIndex, 1)); - } - } - - const pick = await vscode.window.showQuickPick(picks, { placeHolder: 'Select repository' }); - return pick && pick.fiddleSourceControl; - } -} - -async function tryOpenFiddle(context: vscode.ExtensionContext, fiddleId?: string, workspaceUri?: vscode.Uri): Promise { - try { - await openFiddle(context, fiddleId, workspaceUri); - } - catch (ex) { - vscode.window.showErrorMessage(ex); - console.log(ex); - } -} - -async function openFiddle(context: vscode.ExtensionContext, fiddleId?: string, workspaceUri?: vscode.Uri) { - if (workspaceUri && fiddleSourceControlRegister.has(workspaceUri)) { vscode.window.showErrorMessage("Another Fiddle was already open in this workspace. Open a new workspace first."); } - - if (!fiddleId) { - fiddleId = (await vscode.window.showInputBox({ prompt: 'Paste JSFiddle ID and optionally version', placeHolder: 'slug or slug/version, e.g. u8B29/1', value: 'demo' })) || ''; - } - - const workspaceFolder = - workspaceUri ? - vscode.workspace.getWorkspaceFolder(workspaceUri) : - await selectWorkspaceFolder(context, fiddleId); - - if (! await clearWorkspaceFolder(workspaceFolder.uri)) { return; } - - // show the file explorer with the three new files - vscode.commands.executeCommand("workbench.view.explorer"); - - // register source control - const fiddleSourceControl = await FiddleSourceControl.fromFiddleId(fiddleId, context, workspaceFolder, true); - - registerFiddleSourceControl(fiddleSourceControl, context); - showFiddleInEditor(fiddleSourceControl); -} - -async function showFiddleInEditor(fiddleSourceControl: FiddleSourceControl): Promise { - // open the 3 fiddle parts in 3 view columns - await openDocumentInColumn(fiddleSourceControl.getRepository().createLocalResourcePath('html'), vscode.ViewColumn.One); - await openDocumentInColumn(fiddleSourceControl.getRepository().createLocalResourcePath('js'), vscode.ViewColumn.Two); - await openDocumentInColumn(fiddleSourceControl.getRepository().createLocalResourcePath('css'), vscode.ViewColumn.Three); -} - -function registerFiddleSourceControl(fiddleSourceControl: FiddleSourceControl, context: vscode.ExtensionContext) { - // update the fiddle document content provider with the latest content - jsFiddleDocumentContentProvider.updated(fiddleSourceControl.getFiddle()); - - // every time the repository is updated with new fiddle version, notify the content provider - fiddleSourceControl.onRepositoryChange(fiddle => jsFiddleDocumentContentProvider.updated(fiddle)); - - if (fiddleSourceControlRegister.has(fiddleSourceControl.getWorkspaceFolder().uri)) { - // the folder was already under source control - const previousSourceControl = fiddleSourceControlRegister.get(fiddleSourceControl.getWorkspaceFolder().uri)!; - previousSourceControl.dispose(); - } - - fiddleSourceControlRegister.set(fiddleSourceControl.getWorkspaceFolder().uri, fiddleSourceControl); - - context.subscriptions.push(fiddleSourceControl); -} - -function unregisterFiddleSourceControl(folderUri: vscode.Uri): void { - if (fiddleSourceControlRegister.has(folderUri)) { - const previousSourceControl = fiddleSourceControlRegister.get(folderUri)!; - previousSourceControl.dispose(); - - fiddleSourceControlRegister.delete(folderUri); - } -} - -/** - * When the extension starts up, it must visit all workspace folders to see if any of them are fiddles. - * @param context extension context - */ -async function initializeFromConfigurationFile(context: vscode.ExtensionContext): Promise { - if (!vscode.workspace.workspaceFolders) { return; } - - const folderPromises = vscode.workspace.workspaceFolders.map(async (folder) => await initializeFolderFromConfigurationFile(folder, context)); - await Promise.all(folderPromises); -} - -async function initializeFolderFromConfigurationFile(folder: vscode.WorkspaceFolder, context: vscode.ExtensionContext): Promise { - const configurationPath = path.join(folder.uri.fsPath, CONFIGURATION_FILE); - - const configFileExists = await afs.exists(configurationPath); - - if (configFileExists) { - const data = await afs.readFile(configurationPath); - const fiddleConfiguration = JSON.parse(data.toString(UTF8)); - const fiddleSourceControl = await FiddleSourceControl.fromConfiguration(fiddleConfiguration, folder, context, !fiddleConfiguration.downloaded); - registerFiddleSourceControl(fiddleSourceControl, context); - if (!fiddleConfiguration.downloaded) { - // the fiddle was not downloaded before the extension restart, so let's show it now - showFiddleInEditor(fiddleSourceControl); - } - } -} - -async function selectWorkspaceFolder(context: vscode.ExtensionContext, fiddleId: string): Promise { - let selectedFolder: vscode.WorkspaceFolder | undefined; - let workspaceFolderUri: vscode.Uri | undefined; - let workspaceFolderIndex: number | undefined; - let folderOpeningMode: FolderOpeningMode; - - const folderPicks: WorkspaceFolderPick[] = [newFolderPick]; - - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { - folderPicks.push(newWorkspaceFolderPick); - - for (const wf of vscode.workspace.workspaceFolders) { - const content = await afs.readdir(wf.uri.fsPath); - folderPicks.push(new ExistingWorkspaceFolderPick(wf, content)); - } - } - - const selectedFolderPick: WorkspaceFolderPick = - folderPicks.length === 1 ? - folderPicks[0] : - await vscode.window.showQuickPick(folderPicks, { - canPickMany: false, ignoreFocusOut: true, placeHolder: 'Pick workspace folder to create files in.' - }); - - if (!selectedFolderPick) { return null; } - - if (selectedFolderPick instanceof ExistingWorkspaceFolderPick) { - selectedFolder = selectedFolderPick.workspaceFolder; - workspaceFolderIndex = selectedFolder.index; - workspaceFolderUri = selectedFolder.uri; - } - - // eslint-disable-next-line prefer-const - folderOpeningMode = selectedFolderPick.folderOpeningMode; - - if (!workspaceFolderUri && !selectedFolder) { - const folderUris = await vscode.window.showOpenDialog({ canSelectFolders: true, canSelectFiles: false, canSelectMany: false, openLabel: 'Select folder' }); - if (!folderUris) { - return null; - } - - workspaceFolderUri = folderUris[0]; - // was such workspace folder already open? - workspaceFolderIndex = vscode.workspace.workspaceFolders && firstIndex(vscode.workspace.workspaceFolders, (folder1: any) => folder1.uri.toString() === workspaceFolderUri!.toString()); - } - - if (! await clearWorkspaceFolder(workspaceFolderUri)) { return null; } - - const fiddleConfiguration = parseFiddleId(fiddleId); - - // save folder configuration - FiddleSourceControl.saveConfiguration(workspaceFolderUri, fiddleConfiguration); - - if (folderOpeningMode === FolderOpeningMode.AddToWorkspace || folderOpeningMode === undefined) { - const workSpacesToReplace = typeof workspaceFolderIndex === 'number' && workspaceFolderIndex > -1 ? 1 : 0; - if (workspaceFolderIndex === undefined || workspaceFolderIndex < 0) { workspaceFolderIndex = 0; } - - // replace or insert the workspace - if (workspaceFolderUri) { - vscode.workspace.updateWorkspaceFolders(workspaceFolderIndex, workSpacesToReplace, { uri: workspaceFolderUri }); - } - } - else if (folderOpeningMode === FolderOpeningMode.OpenFolder) { - vscode.commands.executeCommand("vscode.openFolder", workspaceFolderUri); - } - - return selectedFolder; -} - -async function clearWorkspaceFolder(workspaceFolderUri: vscode.Uri): Promise { - - if (!workspaceFolderUri) { return undefined; } - - // check if the workspace is empty, or clear it - const existingWorkspaceFiles: string[] = await afs.readdir(workspaceFolderUri.fsPath); - if (existingWorkspaceFiles.length > 0) { - const answer = await vscode.window.showQuickPick(["Yes", "No"], - { placeHolder: `Remove ${existingWorkspaceFiles.length} file(s) from the folder ${workspaceFolderUri.fsPath} before cloning the remote repository?` }); - if (!answer) { return false; } - - if (answer === "Yes") { - existingWorkspaceFiles - .forEach(async filename => - await afs.unlink(path.join(workspaceFolderUri.fsPath, filename))); - } - } - - return true; -} - -class RepositoryPick implements vscode.QuickPickItem { - - constructor(public readonly fiddleSourceControl: FiddleSourceControl) { } - - get label(): string { - return this.fiddleSourceControl.getSourceControl().label; - } -} - -async function openDocumentInColumn(fileName: string, column: vscode.ViewColumn): Promise { - const uri = vscode.Uri.file(fileName); - - // assuming the file was saved, let's open it in a view column - const doc = await vscode.workspace.openTextDocument(uri); - - await vscode.window.showTextDocument(doc, { viewColumn: column }); -} - -abstract class WorkspaceFolderPick implements vscode.QuickPickItem { - abstract get label(): string; - constructor(public folderOpeningMode: FolderOpeningMode) { } -} - -class ExistingWorkspaceFolderPick extends WorkspaceFolderPick { - - constructor(public readonly workspaceFolder: vscode.WorkspaceFolder, private content: string[]) { - super(FolderOpeningMode.AddToWorkspace); - } - - get label(): string { - return this.workspaceFolder.name; - } - - get description(): string { - return this.workspaceFolder.uri.fsPath; - } - - get detail(): string { - return this.content.length ? `${this.content.length} files/directories may need to be removed..` : null; - } -} - -class NewWorkspaceFolderPick extends WorkspaceFolderPick { - constructor(public label: string, folderOpeningMode: FolderOpeningMode) { - super(folderOpeningMode); - } -} - -enum FolderOpeningMode { AddToWorkspace, OpenFolder } - -const newWorkspaceFolderPick = new NewWorkspaceFolderPick("Select/create a local folder to add to this workspace...", FolderOpeningMode.AddToWorkspace); -const newFolderPick = new NewWorkspaceFolderPick("Select/create a local folder...", FolderOpeningMode.OpenFolder); +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +import { JSFIDDLE_SCHEME } from './fiddleRepository'; +import { FiddleSourceControl, CONFIGURATION_FILE } from './fiddleSourceControl'; +import { JSFiddleDocumentContentProvider } from './fiddleDocumentContentProvider'; +import * as path from 'path'; +import * as afs from './afs'; +import { FiddleConfiguration, parseFiddleId } from './fiddleConfiguration'; +import { firstIndex, UTF8 } from './util'; + +const SOURCE_CONTROL_OPEN_COMMAND = 'extension.source-control.open'; +let jsFiddleDocumentContentProvider: JSFiddleDocumentContentProvider; +const fiddleSourceControlRegister = new Map(); + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export async function activate(context: vscode.ExtensionContext) { + console.log('Congratulations, your extension "source-control-sample" is now active!'); + + jsFiddleDocumentContentProvider = new JSFiddleDocumentContentProvider(); + + try { + await initializeFromConfigurationFile(context); + } + catch (err) { + console.log('Failed to initialize a Fiddle workspace.'); + vscode.window.showErrorMessage(err); + } + + const openCommand = vscode.commands.registerCommand(SOURCE_CONTROL_OPEN_COMMAND, + (fiddleId?: string, workspaceUri?: vscode.Uri) => { + tryOpenFiddle(context, fiddleId, workspaceUri); + }); + context.subscriptions.push(openCommand); + + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(JSFIDDLE_SCHEME, jsFiddleDocumentContentProvider)); + + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.refresh", + async (sourceControlPane: vscode.SourceControl) => { + const sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.refresh(); } + })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.discard", + async (sourceControlPane: vscode.SourceControl) => { + const sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.resetFilesToCheckedOutVersion(); } + })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.commit", + async (sourceControlPane: vscode.SourceControl) => { + const 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(null); + if (sourceControl) { sourceControl.tryCheckout(newVersion); } + })); + context.subscriptions.push(vscode.commands.registerCommand("extension.source-control.browse", + async (sourceControlPane: vscode.SourceControl) => { + const sourceControl = await pickSourceControl(sourceControlPane); + if (sourceControl) { sourceControl.openInBrowser(); } + })); + + + context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(e => { + try { + // initialize new source control for manually added workspace folders + e.added.forEach(wf => { + initializeFolderFromConfigurationFile(wf, context); + }); + } catch (ex) { + vscode.window.showErrorMessage(ex.message); + } finally { + // dispose source control for removed workspace folders + e.removed.forEach(wf => { + unregisterFiddleSourceControl(wf.uri); + }); + } + })); +} + +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; } + else if (fiddleSourceControlRegister.size === 1) { return [...fiddleSourceControlRegister.values()][0]; } + else { + + const picks = [...fiddleSourceControlRegister.values()].map(fsc => new RepositoryPick(fsc)); + + if (vscode.window.activeTextEditor) { + const activeWorkspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri); + const activeSourceControl = activeWorkspaceFolder && fiddleSourceControlRegister.get(activeWorkspaceFolder.uri); + const activeIndex = firstIndex(picks, pick => pick.fiddleSourceControl === activeSourceControl); + + // if there is an active editor, move its folder to be the first in the pick list + if (activeIndex > -1) { + picks.unshift(...picks.splice(activeIndex, 1)); + } + } + + const pick = await vscode.window.showQuickPick(picks, { placeHolder: 'Select repository' }); + return pick && pick.fiddleSourceControl; + } +} + +async function tryOpenFiddle(context: vscode.ExtensionContext, fiddleId?: string, workspaceUri?: vscode.Uri): Promise { + try { + await openFiddle(context, fiddleId, workspaceUri); + } + catch (ex) { + vscode.window.showErrorMessage(ex); + console.log(ex); + } +} + +async function openFiddle(context: vscode.ExtensionContext, fiddleId?: string, workspaceUri?: vscode.Uri) { + if (workspaceUri && fiddleSourceControlRegister.has(workspaceUri)) { vscode.window.showErrorMessage("Another Fiddle was already open in this workspace. Open a new workspace first."); } + + if (!fiddleId) { + fiddleId = (await vscode.window.showInputBox({ prompt: 'Paste JSFiddle ID and optionally version', placeHolder: 'slug or slug/version, e.g. u8B29/1', value: 'demo' })) || ''; + } + + const workspaceFolder = + workspaceUri ? + vscode.workspace.getWorkspaceFolder(workspaceUri) : + await selectWorkspaceFolder(context, fiddleId); + + if (! await clearWorkspaceFolder(workspaceFolder.uri)) { return; } + + // show the file explorer with the three new files + vscode.commands.executeCommand("workbench.view.explorer"); + + // register source control + const fiddleSourceControl = await FiddleSourceControl.fromFiddleId(fiddleId, context, workspaceFolder, true); + + registerFiddleSourceControl(fiddleSourceControl, context); + showFiddleInEditor(fiddleSourceControl); +} + +async function showFiddleInEditor(fiddleSourceControl: FiddleSourceControl): Promise { + // open the 3 fiddle parts in 3 view columns + await openDocumentInColumn(fiddleSourceControl.getRepository().createLocalResourcePath('html'), vscode.ViewColumn.One); + await openDocumentInColumn(fiddleSourceControl.getRepository().createLocalResourcePath('js'), vscode.ViewColumn.Two); + await openDocumentInColumn(fiddleSourceControl.getRepository().createLocalResourcePath('css'), vscode.ViewColumn.Three); +} + +function registerFiddleSourceControl(fiddleSourceControl: FiddleSourceControl, context: vscode.ExtensionContext) { + // update the fiddle document content provider with the latest content + jsFiddleDocumentContentProvider.updated(fiddleSourceControl.getFiddle()); + + // every time the repository is updated with new fiddle version, notify the content provider + fiddleSourceControl.onRepositoryChange(fiddle => jsFiddleDocumentContentProvider.updated(fiddle)); + + if (fiddleSourceControlRegister.has(fiddleSourceControl.getWorkspaceFolder().uri)) { + // the folder was already under source control + const previousSourceControl = fiddleSourceControlRegister.get(fiddleSourceControl.getWorkspaceFolder().uri)!; + previousSourceControl.dispose(); + } + + fiddleSourceControlRegister.set(fiddleSourceControl.getWorkspaceFolder().uri, fiddleSourceControl); + + context.subscriptions.push(fiddleSourceControl); +} + +function unregisterFiddleSourceControl(folderUri: vscode.Uri): void { + if (fiddleSourceControlRegister.has(folderUri)) { + const previousSourceControl = fiddleSourceControlRegister.get(folderUri)!; + previousSourceControl.dispose(); + + fiddleSourceControlRegister.delete(folderUri); + } +} + +/** + * When the extension starts up, it must visit all workspace folders to see if any of them are fiddles. + * @param context extension context + */ +async function initializeFromConfigurationFile(context: vscode.ExtensionContext): Promise { + if (!vscode.workspace.workspaceFolders) { return; } + + const folderPromises = vscode.workspace.workspaceFolders.map(async (folder) => await initializeFolderFromConfigurationFile(folder, context)); + await Promise.all(folderPromises); +} + +async function initializeFolderFromConfigurationFile(folder: vscode.WorkspaceFolder, context: vscode.ExtensionContext): Promise { + const configurationPath = path.join(folder.uri.fsPath, CONFIGURATION_FILE); + + const configFileExists = await afs.exists(configurationPath); + + if (configFileExists) { + const data = await afs.readFile(configurationPath); + const fiddleConfiguration = JSON.parse(data.toString(UTF8)); + const fiddleSourceControl = await FiddleSourceControl.fromConfiguration(fiddleConfiguration, folder, context, !fiddleConfiguration.downloaded); + registerFiddleSourceControl(fiddleSourceControl, context); + if (!fiddleConfiguration.downloaded) { + // the fiddle was not downloaded before the extension restart, so let's show it now + showFiddleInEditor(fiddleSourceControl); + } + } +} + +async function selectWorkspaceFolder(context: vscode.ExtensionContext, fiddleId: string): Promise { + let selectedFolder: vscode.WorkspaceFolder | undefined; + let workspaceFolderUri: vscode.Uri | undefined; + let workspaceFolderIndex: number | undefined; + let folderOpeningMode: FolderOpeningMode; + + const folderPicks: WorkspaceFolderPick[] = [newFolderPick]; + + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + folderPicks.push(newWorkspaceFolderPick); + + for (const wf of vscode.workspace.workspaceFolders) { + const content = await afs.readdir(wf.uri.fsPath); + folderPicks.push(new ExistingWorkspaceFolderPick(wf, content)); + } + } + + const selectedFolderPick: WorkspaceFolderPick = + folderPicks.length === 1 ? + folderPicks[0] : + await vscode.window.showQuickPick(folderPicks, { + canPickMany: false, ignoreFocusOut: true, placeHolder: 'Pick workspace folder to create files in.' + }); + + if (!selectedFolderPick) { return null; } + + if (selectedFolderPick instanceof ExistingWorkspaceFolderPick) { + selectedFolder = selectedFolderPick.workspaceFolder; + workspaceFolderIndex = selectedFolder.index; + workspaceFolderUri = selectedFolder.uri; + } + + // eslint-disable-next-line prefer-const + folderOpeningMode = selectedFolderPick.folderOpeningMode; + + if (!workspaceFolderUri && !selectedFolder) { + const folderUris = await vscode.window.showOpenDialog({ canSelectFolders: true, canSelectFiles: false, canSelectMany: false, openLabel: 'Select folder' }); + if (!folderUris) { + return null; + } + + workspaceFolderUri = folderUris[0]; + // was such workspace folder already open? + workspaceFolderIndex = vscode.workspace.workspaceFolders && firstIndex(vscode.workspace.workspaceFolders, (folder1: any) => folder1.uri.toString() === workspaceFolderUri!.toString()); + } + + if (! await clearWorkspaceFolder(workspaceFolderUri)) { return null; } + + const fiddleConfiguration = parseFiddleId(fiddleId); + + // save folder configuration + FiddleSourceControl.saveConfiguration(workspaceFolderUri, fiddleConfiguration); + + if (folderOpeningMode === FolderOpeningMode.AddToWorkspace || folderOpeningMode === undefined) { + const workSpacesToReplace = typeof workspaceFolderIndex === 'number' && workspaceFolderIndex > -1 ? 1 : 0; + if (workspaceFolderIndex === undefined || workspaceFolderIndex < 0) { workspaceFolderIndex = 0; } + + // replace or insert the workspace + if (workspaceFolderUri) { + vscode.workspace.updateWorkspaceFolders(workspaceFolderIndex, workSpacesToReplace, { uri: workspaceFolderUri }); + } + } + else if (folderOpeningMode === FolderOpeningMode.OpenFolder) { + vscode.commands.executeCommand("vscode.openFolder", workspaceFolderUri); + } + + return selectedFolder; +} + +async function clearWorkspaceFolder(workspaceFolderUri: vscode.Uri): Promise { + + if (!workspaceFolderUri) { return undefined; } + + // check if the workspace is empty, or clear it + const existingWorkspaceFiles: string[] = await afs.readdir(workspaceFolderUri.fsPath); + if (existingWorkspaceFiles.length > 0) { + const answer = await vscode.window.showQuickPick(["Yes", "No"], + { placeHolder: `Remove ${existingWorkspaceFiles.length} file(s) from the folder ${workspaceFolderUri.fsPath} before cloning the remote repository?` }); + if (!answer) { return false; } + + if (answer === "Yes") { + existingWorkspaceFiles + .forEach(async filename => + await afs.unlink(path.join(workspaceFolderUri.fsPath, filename))); + } + } + + return true; +} + +class RepositoryPick implements vscode.QuickPickItem { + + constructor(public readonly fiddleSourceControl: FiddleSourceControl) { } + + get label(): string { + return this.fiddleSourceControl.getSourceControl().label; + } +} + +async function openDocumentInColumn(fileName: string, column: vscode.ViewColumn): Promise { + const uri = vscode.Uri.file(fileName); + + // assuming the file was saved, let's open it in a view column + const doc = await vscode.workspace.openTextDocument(uri); + + await vscode.window.showTextDocument(doc, { viewColumn: column }); +} + +abstract class WorkspaceFolderPick implements vscode.QuickPickItem { + abstract get label(): string; + constructor(public folderOpeningMode: FolderOpeningMode) { } +} + +class ExistingWorkspaceFolderPick extends WorkspaceFolderPick { + + constructor(public readonly workspaceFolder: vscode.WorkspaceFolder, private content: string[]) { + super(FolderOpeningMode.AddToWorkspace); + } + + get label(): string { + return this.workspaceFolder.name; + } + + get description(): string { + return this.workspaceFolder.uri.fsPath; + } + + get detail(): string { + return this.content.length ? `${this.content.length} files/directories may need to be removed..` : null; + } +} + +class NewWorkspaceFolderPick extends WorkspaceFolderPick { + constructor(public label: string, folderOpeningMode: FolderOpeningMode) { + super(folderOpeningMode); + } +} + +enum FolderOpeningMode { AddToWorkspace, OpenFolder } + +const newWorkspaceFolderPick = new NewWorkspaceFolderPick("Select/create a local folder to add to this workspace...", FolderOpeningMode.AddToWorkspace); +const newFolderPick = new NewWorkspaceFolderPick("Select/create a local folder...", FolderOpeningMode.OpenFolder); diff --git a/source-control-sample/src/fiddleConfiguration.ts b/source-control-sample/src/fiddleConfiguration.ts index a4485a83..e4e4d232 100644 --- a/source-control-sample/src/fiddleConfiguration.ts +++ b/source-control-sample/src/fiddleConfiguration.ts @@ -1,13 +1,13 @@ -export interface FiddleConfiguration { - readonly slug: string; - readonly version?: number; - readonly downloaded: boolean; -} - -export function parseFiddleId(id: string): FiddleConfiguration { - const idFragments = id.split('/'); - const fiddleSlug = idFragments[0]; - const fiddleVersion = idFragments.length > 1 ? parseInt(id.split('/')[1]) : undefined; - - return { slug: fiddleSlug, version: fiddleVersion, downloaded: false }; +export interface FiddleConfiguration { + readonly slug: string; + readonly version?: number; + readonly downloaded: boolean; +} + +export function parseFiddleId(id: string): FiddleConfiguration { + const idFragments = id.split('/'); + const fiddleSlug = idFragments[0]; + const fiddleVersion = idFragments.length > 1 ? parseInt(id.split('/')[1]) : undefined; + + return { slug: fiddleSlug, version: fiddleVersion, downloaded: false }; } \ No newline at end of file diff --git a/source-control-sample/src/fiddleDocumentContentProvider.ts b/source-control-sample/src/fiddleDocumentContentProvider.ts index deed22a3..a543b227 100644 --- a/source-control-sample/src/fiddleDocumentContentProvider.ts +++ b/source-control-sample/src/fiddleDocumentContentProvider.ts @@ -1,43 +1,43 @@ -import { CancellationToken, ProviderResult, TextDocumentContentProvider, Event, Uri, EventEmitter, Disposable } from "vscode"; -import { toExtension, JSFIDDLE_SCHEME, Fiddle } from "./fiddleRepository"; -import { basename } from "path"; - -/** - * Provides the content of the JS Fiddle documents as fetched from the server i.e. without the local edits. - * This is used for the source control diff. - */ -export class JSFiddleDocumentContentProvider implements TextDocumentContentProvider, Disposable { - private _onDidChange = new EventEmitter(); - private fiddles = new Map(); // this assumes each fiddle is only open once per workspace - - get onDidChange(): Event { - return this._onDidChange.event; - } - - dispose(): void { - this._onDidChange.dispose(); - } - - updated(newFiddle: Fiddle): void { - this.fiddles.set(newFiddle.slug, newFiddle); - - // let's assume all 3 documents actually changed and notify the quick-diff - this._onDidChange.fire(Uri.parse(`${JSFIDDLE_SCHEME}:${newFiddle.slug}.html`)); - this._onDidChange.fire(Uri.parse(`${JSFIDDLE_SCHEME}:${newFiddle.slug}.css`)); - this._onDidChange.fire(Uri.parse(`${JSFIDDLE_SCHEME}:${newFiddle.slug}.js`)); - } - - provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult { - if (token.isCancellationRequested) { return "Canceled"; } - - let fiddleSlug = basename(uri.fsPath); - // strip off the file extension - fiddleSlug = fiddleSlug.split('.').slice(0, -1).join('.'); - const fiddlePart = toExtension(uri); - - const fiddle = this.fiddles.get(fiddleSlug); - if (!fiddle) { return "Resource not found: " + uri.toString(); } - - return fiddle.data[fiddlePart]; - } +import { CancellationToken, ProviderResult, TextDocumentContentProvider, Event, Uri, EventEmitter, Disposable } from "vscode"; +import { toExtension, JSFIDDLE_SCHEME, Fiddle } from "./fiddleRepository"; +import { basename } from "path"; + +/** + * Provides the content of the JS Fiddle documents as fetched from the server i.e. without the local edits. + * This is used for the source control diff. + */ +export class JSFiddleDocumentContentProvider implements TextDocumentContentProvider, Disposable { + private _onDidChange = new EventEmitter(); + private fiddles = new Map(); // this assumes each fiddle is only open once per workspace + + get onDidChange(): Event { + return this._onDidChange.event; + } + + dispose(): void { + this._onDidChange.dispose(); + } + + updated(newFiddle: Fiddle): void { + this.fiddles.set(newFiddle.slug, newFiddle); + + // let's assume all 3 documents actually changed and notify the quick-diff + this._onDidChange.fire(Uri.parse(`${JSFIDDLE_SCHEME}:${newFiddle.slug}.html`)); + this._onDidChange.fire(Uri.parse(`${JSFIDDLE_SCHEME}:${newFiddle.slug}.css`)); + this._onDidChange.fire(Uri.parse(`${JSFIDDLE_SCHEME}:${newFiddle.slug}.js`)); + } + + provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult { + if (token.isCancellationRequested) { return "Canceled"; } + + let fiddleSlug = basename(uri.fsPath); + // strip off the file extension + fiddleSlug = fiddleSlug.split('.').slice(0, -1).join('.'); + const fiddlePart = toExtension(uri); + + const fiddle = this.fiddles.get(fiddleSlug); + if (!fiddle) { return "Resource not found: " + uri.toString(); } + + return fiddle.data[fiddlePart]; + } } \ No newline at end of file diff --git a/source-control-sample/src/fiddleRepository.ts b/source-control-sample/src/fiddleRepository.ts index e845a789..269c0d52 100644 --- a/source-control-sample/src/fiddleRepository.ts +++ b/source-control-sample/src/fiddleRepository.ts @@ -1,165 +1,165 @@ -import JSFiddle = require("jsfiddle"); -import { QuickDiffProvider, Uri, CancellationToken, ProviderResult, WorkspaceFolder, workspace, window, env } from "vscode"; -import * as path from 'path'; - -/** Represents one JSFiddle data and meta-data. */ -export class Fiddle { - constructor(public slug: string, public version: number, public data: FiddleData) { } -} - -/** Represents JSFiddle HTML, JavaScript and CSS text. */ -export interface FiddleData { - html: string; - js: string; - css: string; -} - -export function areIdentical(first: FiddleData, second: FiddleData): boolean { - return first.html === second.html - && first.css === second.css - && first.js === second.js; -} - -export const JSFIDDLE_SCHEME = 'jsfiddle'; - -export class FiddleRepository implements QuickDiffProvider { - - constructor(private workspaceFolder: WorkspaceFolder, private fiddleSlug: string) { } - - provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult { - // converts the local file uri to jsfiddle:file.ext - const relativePath = workspace.asRelativePath(uri.fsPath); - return Uri.parse(`${JSFIDDLE_SCHEME}:${relativePath}`); - } - - /** - * Enumerates the resources under source control. - */ - provideSourceControlledResources(): Uri[] { - return [ - Uri.file(this.createLocalResourcePath('html')), - Uri.file(this.createLocalResourcePath('js')), - Uri.file(this.createLocalResourcePath('css'))]; - } - - /** - * Creates a local file path in the local workspace that corresponds to the part of the - * fiddle denoted by the given extension. - * - * @param extension fiddle part, which is also used as a file extension - * @returns path of the locally cloned fiddle resource ending with the given extension - */ - createLocalResourcePath(extension: string) { - return path.join(this.workspaceFolder.uri.fsPath, this.fiddleSlug + '.' + extension); - } -} - -const DEMO: FiddleData[] = [ - { - html: '

Hi
', - css: `.hi {\n color: red;\n}`, - js: '$(".hi").fadeOut();' - } -]; - -// emulates prior versions mock-committed in previous sessions -let demoVersionOffset: number | undefined = undefined; - -export async function downloadFiddle(slug: string, version: number | undefined): Promise { - - if (slug === "demo") { - // use mock fiddle - if (demoVersionOffset === undefined && version === undefined) { version = 0; } - if (demoVersionOffset === undefined) { demoVersionOffset = version; } - const maxDemoVersion = DEMO.length - 1 + demoVersionOffset; - if (version === undefined) { version = maxDemoVersion; } - - if (version >= 0 && version <= maxDemoVersion) { - // mock all versions committed in previous sessions by the first version - const index = Math.max(0, version - demoVersionOffset); - const fiddleData = DEMO[index]; - return new Fiddle(slug, version, fiddleData); - } - else { - throw new Error("Invalid demo fiddle version."); - } - } - - const id = toFiddleId(slug, version); - - return new Promise((resolve, reject) => { - JSFiddle.getFiddle(id, (err: any, fiddleData: any) => { - // handle error - if (err) { reject(err); } - - const fiddle = new Fiddle(slug, version, fiddleData); - - resolve(fiddle); - }); - }); -} - -export async function uploadFiddle(slug: string, version: number, html: string, js: string, css: string): Promise { - - if (slug === "demo") { - // using mock fiddle - const fiddleData: FiddleData = { html: html, js: js, css: css }; - DEMO.push(fiddleData); - return new Fiddle(slug, version, fiddleData); - } - else { - - const answer = await window.showQuickPick(["Yes, open in the browser. I will paste the new Fiddle code, discard changes, refresh source control and checkout latest.", "No, I was just clicking around."], - { placeHolder: "JS Fiddle saving is not supported. Do you want to open the JSFiddle in the browser?" }); - - if (answer && answer.toLowerCase().startsWith("yes")) { - env.openExternal(Uri.parse(`https://jsfiddle.net/${slug}/`)); - return undefined; - } - - if (false) { - // this, sadly, does not work as advertised - const data = { - slug: slug, - version: version, - html: html, - js: js, - css: css - }; - - return new Promise((resolve, reject) => { - JSFiddle.saveFiddle(data, (err: any, fiddleData: any) => { - // handle error - if (err) { - reject(err); - } - else { - const fiddle = new Fiddle(slug, version, fiddleData); - - resolve(fiddle); - } - }); - }); - } - else { - return undefined; - } - } -} - -export function toFiddleId(slug: string, version: number | undefined): string { - if (version === undefined) { - return slug; - } - else { - return slug + '/' + version; - } -} - -/** - * Gets extension trimming the dot character. - * @param uri document uri - */ -export function toExtension(uri: Uri): string { - return path.extname(uri.fsPath).substr(1); +import JSFiddle = require("jsfiddle"); +import { QuickDiffProvider, Uri, CancellationToken, ProviderResult, WorkspaceFolder, workspace, window, env } from "vscode"; +import * as path from 'path'; + +/** Represents one JSFiddle data and meta-data. */ +export class Fiddle { + constructor(public slug: string, public version: number, public data: FiddleData) { } +} + +/** Represents JSFiddle HTML, JavaScript and CSS text. */ +export interface FiddleData { + html: string; + js: string; + css: string; +} + +export function areIdentical(first: FiddleData, second: FiddleData): boolean { + return first.html === second.html + && first.css === second.css + && first.js === second.js; +} + +export const JSFIDDLE_SCHEME = 'jsfiddle'; + +export class FiddleRepository implements QuickDiffProvider { + + constructor(private workspaceFolder: WorkspaceFolder, private fiddleSlug: string) { } + + provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult { + // converts the local file uri to jsfiddle:file.ext + const relativePath = workspace.asRelativePath(uri.fsPath); + return Uri.parse(`${JSFIDDLE_SCHEME}:${relativePath}`); + } + + /** + * Enumerates the resources under source control. + */ + provideSourceControlledResources(): Uri[] { + return [ + Uri.file(this.createLocalResourcePath('html')), + Uri.file(this.createLocalResourcePath('js')), + Uri.file(this.createLocalResourcePath('css'))]; + } + + /** + * Creates a local file path in the local workspace that corresponds to the part of the + * fiddle denoted by the given extension. + * + * @param extension fiddle part, which is also used as a file extension + * @returns path of the locally cloned fiddle resource ending with the given extension + */ + createLocalResourcePath(extension: string) { + return path.join(this.workspaceFolder.uri.fsPath, this.fiddleSlug + '.' + extension); + } +} + +const DEMO: FiddleData[] = [ + { + html: '
Hi
', + css: `.hi {\n color: red;\n}`, + js: '$(".hi").fadeOut();' + } +]; + +// emulates prior versions mock-committed in previous sessions +let demoVersionOffset: number | undefined = undefined; + +export async function downloadFiddle(slug: string, version: number | undefined): Promise { + + if (slug === "demo") { + // use mock fiddle + if (demoVersionOffset === undefined && version === undefined) { version = 0; } + if (demoVersionOffset === undefined) { demoVersionOffset = version; } + const maxDemoVersion = DEMO.length - 1 + demoVersionOffset; + if (version === undefined) { version = maxDemoVersion; } + + if (version >= 0 && version <= maxDemoVersion) { + // mock all versions committed in previous sessions by the first version + const index = Math.max(0, version - demoVersionOffset); + const fiddleData = DEMO[index]; + return new Fiddle(slug, version, fiddleData); + } + else { + throw new Error("Invalid demo fiddle version."); + } + } + + const id = toFiddleId(slug, version); + + return new Promise((resolve, reject) => { + JSFiddle.getFiddle(id, (err: any, fiddleData: any) => { + // handle error + if (err) { reject(err); } + + const fiddle = new Fiddle(slug, version, fiddleData); + + resolve(fiddle); + }); + }); +} + +export async function uploadFiddle(slug: string, version: number, html: string, js: string, css: string): Promise { + + if (slug === "demo") { + // using mock fiddle + const fiddleData: FiddleData = { html: html, js: js, css: css }; + DEMO.push(fiddleData); + return new Fiddle(slug, version, fiddleData); + } + else { + + const answer = await window.showQuickPick(["Yes, open in the browser. I will paste the new Fiddle code, discard changes, refresh source control and checkout latest.", "No, I was just clicking around."], + { placeHolder: "JS Fiddle saving is not supported. Do you want to open the JSFiddle in the browser?" }); + + if (answer && answer.toLowerCase().startsWith("yes")) { + env.openExternal(Uri.parse(`https://jsfiddle.net/${slug}/`)); + return undefined; + } + + if (false) { + // this, sadly, does not work as advertised + const data = { + slug: slug, + version: version, + html: html, + js: js, + css: css + }; + + return new Promise((resolve, reject) => { + JSFiddle.saveFiddle(data, (err: any, fiddleData: any) => { + // handle error + if (err) { + reject(err); + } + else { + const fiddle = new Fiddle(slug, version, fiddleData); + + resolve(fiddle); + } + }); + }); + } + else { + return undefined; + } + } +} + +export function toFiddleId(slug: string, version: number | undefined): string { + if (version === undefined) { + return slug; + } + else { + return slug + '/' + version; + } +} + +/** + * Gets extension trimming the dot character. + * @param uri document uri + */ +export function toExtension(uri: Uri): string { + return path.extname(uri.fsPath).substr(1); } \ No newline at end of file diff --git a/source-control-sample/src/fiddleSourceControl.ts b/source-control-sample/src/fiddleSourceControl.ts index ae40d2d7..d18724a3 100644 --- a/source-control-sample/src/fiddleSourceControl.ts +++ b/source-control-sample/src/fiddleSourceControl.ts @@ -1,354 +1,354 @@ -import * as vscode from 'vscode'; -import { FiddleRepository, toExtension, downloadFiddle, areIdentical, uploadFiddle, Fiddle, toFiddleId } from './fiddleRepository'; -import * as path from 'path'; -import * as afs from './afs'; -import { FiddleConfiguration, parseFiddleId } from './fiddleConfiguration'; -import { UTF8 } from './util'; - -export const CONFIGURATION_FILE = '.jsfiddle'; - -export class FiddleSourceControl implements vscode.Disposable { - private jsFiddleScm: vscode.SourceControl; - private changedResources: vscode.SourceControlResourceGroup; - private fiddleRepository: FiddleRepository; - private latestFiddleVersion: number = Number.POSITIVE_INFINITY; // until actual value is established - private _onRepositoryChange = new vscode.EventEmitter(); - private timeout?: NodeJS.Timer; - private fiddle!: Fiddle; - - constructor(context: vscode.ExtensionContext, private readonly workspaceFolder: vscode.WorkspaceFolder, fiddle: Fiddle, download: boolean) { - this.jsFiddleScm = vscode.scm.createSourceControl('jsfiddle', 'JSFiddle #' + fiddle.slug, workspaceFolder.uri); - this.changedResources = this.jsFiddleScm.createResourceGroup('workingTree', 'Changes'); - this.fiddleRepository = new FiddleRepository(workspaceFolder, fiddle.slug); - this.jsFiddleScm.quickDiffProvider = this.fiddleRepository; - this.jsFiddleScm.inputBox.placeholder = 'Message is ignored by JS Fiddle :-]'; - - const fileSystemWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(workspaceFolder, "*.*")); - fileSystemWatcher.onDidChange(uri => this.onResourceChange(uri), context.subscriptions); - fileSystemWatcher.onDidCreate(uri => this.onResourceChange(uri), context.subscriptions); - fileSystemWatcher.onDidDelete(uri => this.onResourceChange(uri), context.subscriptions); - - context.subscriptions.push(this.jsFiddleScm); - context.subscriptions.push(fileSystemWatcher); - - // clone fiddle to the local workspace - this.setFiddle(fiddle, download); - - if (this.fiddle.version === undefined || Number.isNaN(this.fiddle.version)) { - this.establishVersion(); - } else { - this.refresh(); - } - } - - static async fromFiddleId(id: string, context: vscode.ExtensionContext, workspaceFolder: vscode.WorkspaceFolder, overwrite: boolean): Promise { - const fiddleConfiguration = parseFiddleId(id); - - return await FiddleSourceControl.fromConfiguration(fiddleConfiguration, workspaceFolder, context, overwrite); - } - - static async fromConfiguration(configuration: FiddleConfiguration, workspaceFolder: vscode.WorkspaceFolder, context: vscode.ExtensionContext, overwrite: boolean): Promise { - return await FiddleSourceControl.fromFiddle(configuration.slug, configuration.version, workspaceFolder, context, overwrite); - } - - private static async fromFiddle(fiddleSlug: string, fiddleVersion: number, workspaceFolder: vscode.WorkspaceFolder, context: vscode.ExtensionContext, overwrite: boolean): Promise { - const fiddle = await downloadFiddle(fiddleSlug, fiddleVersion); - const workspacePath = workspaceFolder.uri.fsPath; - return new FiddleSourceControl(context, workspaceFolder, fiddle, overwrite); - } - - private refreshStatusBar() { - this.jsFiddleScm.statusBarCommands = [ - { - "command": "extension.source-control.checkout", - "arguments": [this], - "title": `↕ ${this.fiddle.slug} #${this.fiddle.version} / ${this.latestFiddleVersion}`, - "tooltip": "Checkout another version of this fiddle.", - } - ]; - } - - async commitAll(): Promise { - if (!this.changedResources.resourceStates.length) { - vscode.window.showErrorMessage("There is nothing to commit."); - } - else if (this.fiddle.version < this.latestFiddleVersion) { - vscode.window.showErrorMessage("Checkout the latest fiddle version before committing your changes."); - } - else { - const html = await this.getLocalResourceText('html'); - const js = await this.getLocalResourceText('js'); - const css = await this.getLocalResourceText('css'); - - // here we assume nobody updated the Fiddle on the server since we refreshed the list of versions - try { - const newFiddle = await uploadFiddle(this.fiddle.slug, this.fiddle.version + 1, html, js, css); - if (!newFiddle) { return; } - this.setFiddle(newFiddle, false); - this.jsFiddleScm.inputBox.value = ''; - } catch (ex) { - vscode.window.showErrorMessage("Cannot commit changes to JS Fiddle. " + ex.message); - } - } - } - - private async getLocalResourceText(extension: string) { - const document = await vscode.workspace.openTextDocument(this.fiddleRepository.createLocalResourcePath(extension)); - return document.getText(); - } - - /** - * Throws away all local changes and resets all files to the checked out version of the repository. - */ - resetFilesToCheckedOutVersion(): void { - this.resetFile('html'); - this.resetFile('css'); - this.resetFile('js'); - } - - /** Resets the given local file content to the checked-out version. */ - private async resetFile(extension: string): Promise { - const filePath = this.fiddleRepository.createLocalResourcePath(extension); - await afs.writeFile(filePath, this.fiddle.data[extension]); - } - - async tryCheckout(newVersion: number | undefined): Promise { - if (!Number.isFinite(this.latestFiddleVersion)) { return; } - - if (newVersion === undefined) { - const allVersions = [...Array(this.latestFiddleVersion + 1).keys()] - .map(ver => new VersionQuickPickItem(ver, ver === this.fiddle.version)); - const newVersionPick = await vscode.window.showQuickPick(allVersions, { canPickMany: false, placeHolder: 'Select a version...' }); - if (newVersionPick) { - newVersion = newVersionPick.version; - } - else { - return; - } - } - - if (newVersion === this.fiddle.version) { return; } // the same version was selected - - if (this.changedResources.resourceStates.length) { - const changedResourcesCount = this.changedResources.resourceStates.length; - vscode.window.showErrorMessage(`There is one or more changed resources. Discard or commit your local changes before checking out another version.`); - } - else { - try { - const newFiddle = await downloadFiddle(this.fiddle.slug, newVersion); - this.setFiddle(newFiddle, true); - } catch (ex) { - vscode.window.showErrorMessage(ex); - } - } - } - - private setFiddle(newFiddle: Fiddle, overwrite: boolean) { - if (newFiddle.version > this.latestFiddleVersion) { this.latestFiddleVersion = newFiddle.version; } - this.fiddle = newFiddle; - if (overwrite) { this.resetFilesToCheckedOutVersion(); } // overwrite local file content - this._onRepositoryChange.fire(this.fiddle); - this.refreshStatusBar(); - - this.saveCurrentConfiguration(); - } - - getFiddle(): Fiddle { - return this.fiddle; - } - - getWorkspaceFolder(): vscode.WorkspaceFolder { - return this.workspaceFolder; - } - - getSourceControl(): vscode.SourceControl { - return this.jsFiddleScm; - } - - getRepository(): FiddleRepository { - return this.fiddleRepository; - } - - /** save configuration for later VS Code sessions */ - private saveCurrentConfiguration(): void { - const fiddleConfiguration: FiddleConfiguration = { - slug: this.fiddle.slug, - version: this.fiddle.version, - downloaded: true - }; - - FiddleSourceControl.saveConfiguration(this.workspaceFolder.uri, fiddleConfiguration); - } - - static saveConfiguration(workspaceFolderUri: vscode.Uri, fiddleConfiguration: FiddleConfiguration): void { - const fiddleConfigurationString = JSON.stringify(fiddleConfiguration); - afs.writeFile(path.join(workspaceFolderUri.fsPath, CONFIGURATION_FILE), Buffer.from(fiddleConfigurationString, UTF8)); - } - - get onRepositoryChange(): vscode.Event { - return this._onRepositoryChange.event; - } - - onResourceChange(_uri: vscode.Uri): void { - if (this.timeout) { clearTimeout(this.timeout); } - this.timeout = setTimeout(() => this.tryUpdateChangedGroup(), 500); - } - - async tryUpdateChangedGroup(): Promise { - try { - await this.updateChangedGroup(); - } - catch (ex) { - vscode.window.showErrorMessage(ex); - } - } - - /** 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 - const changedResources: vscode.SourceControlResourceState[] = []; - - const uris = this.fiddleRepository.provideSourceControlledResources(); - - for (const uri of uris) { - let isDirty: boolean; - let wasDeleted: boolean; - - const pathExists = await afs.exists(uri.fsPath); - - if (pathExists) { - const document = await vscode.workspace.openTextDocument(uri); - isDirty = this.isDirty(document); - wasDeleted = false; - } - else { - isDirty = true; - wasDeleted = true; - } - - if (isDirty) { - const resourceState = this.toSourceControlResourceState(uri, wasDeleted); - changedResources.push(resourceState); - } - } - - 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. */ - isDirty(doc: vscode.TextDocument): boolean { - const originalText = this.fiddle.data[toExtension(doc.uri)]; - return originalText.replace('\r', '') !== doc.getText().replace('\r', ''); - } - - toSourceControlResourceState(docUri: vscode.Uri, deleted: boolean): vscode.SourceControlResourceState { - - const repositoryUri = this.fiddleRepository.provideOriginalResource(docUri, null); - - const fiddlePart = toExtension(docUri).toUpperCase(); - - const command: vscode.Command = !deleted - ? { - title: "Show changes", - command: "vscode.diff", - arguments: [repositoryUri, docUri, `JSFiddle#${this.fiddle.slug} ${fiddlePart} ↔ Local changes`], - tooltip: "Diff your changes" - } - : null; - - const resourceState: vscode.SourceControlResourceState = { - resourceUri: docUri, - command: command, - decorations: { - strikeThrough: deleted, - tooltip: 'File was locally deleted.' - } - }; - - 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 { - const 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 { - const 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() { - const 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(); - } -} - -class VersionQuickPickItem implements vscode.QuickPickItem { - - constructor(public readonly version: number, public readonly picked: boolean) { - } - - get label(): string { - return `Version ${this.version}`; - } - - get description(): string { - return this.picked ? '(currently checked-out)' : ''; - } - - get alwaysShow(): boolean { - return this.picked; - } -} +import * as vscode from 'vscode'; +import { FiddleRepository, toExtension, downloadFiddle, areIdentical, uploadFiddle, Fiddle, toFiddleId } from './fiddleRepository'; +import * as path from 'path'; +import * as afs from './afs'; +import { FiddleConfiguration, parseFiddleId } from './fiddleConfiguration'; +import { UTF8 } from './util'; + +export const CONFIGURATION_FILE = '.jsfiddle'; + +export class FiddleSourceControl implements vscode.Disposable { + private jsFiddleScm: vscode.SourceControl; + private changedResources: vscode.SourceControlResourceGroup; + private fiddleRepository: FiddleRepository; + private latestFiddleVersion: number = Number.POSITIVE_INFINITY; // until actual value is established + private _onRepositoryChange = new vscode.EventEmitter(); + private timeout?: NodeJS.Timer; + private fiddle!: Fiddle; + + constructor(context: vscode.ExtensionContext, private readonly workspaceFolder: vscode.WorkspaceFolder, fiddle: Fiddle, download: boolean) { + this.jsFiddleScm = vscode.scm.createSourceControl('jsfiddle', 'JSFiddle #' + fiddle.slug, workspaceFolder.uri); + this.changedResources = this.jsFiddleScm.createResourceGroup('workingTree', 'Changes'); + this.fiddleRepository = new FiddleRepository(workspaceFolder, fiddle.slug); + this.jsFiddleScm.quickDiffProvider = this.fiddleRepository; + this.jsFiddleScm.inputBox.placeholder = 'Message is ignored by JS Fiddle :-]'; + + const fileSystemWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(workspaceFolder, "*.*")); + fileSystemWatcher.onDidChange(uri => this.onResourceChange(uri), context.subscriptions); + fileSystemWatcher.onDidCreate(uri => this.onResourceChange(uri), context.subscriptions); + fileSystemWatcher.onDidDelete(uri => this.onResourceChange(uri), context.subscriptions); + + context.subscriptions.push(this.jsFiddleScm); + context.subscriptions.push(fileSystemWatcher); + + // clone fiddle to the local workspace + this.setFiddle(fiddle, download); + + if (this.fiddle.version === undefined || Number.isNaN(this.fiddle.version)) { + this.establishVersion(); + } else { + this.refresh(); + } + } + + static async fromFiddleId(id: string, context: vscode.ExtensionContext, workspaceFolder: vscode.WorkspaceFolder, overwrite: boolean): Promise { + const fiddleConfiguration = parseFiddleId(id); + + return await FiddleSourceControl.fromConfiguration(fiddleConfiguration, workspaceFolder, context, overwrite); + } + + static async fromConfiguration(configuration: FiddleConfiguration, workspaceFolder: vscode.WorkspaceFolder, context: vscode.ExtensionContext, overwrite: boolean): Promise { + return await FiddleSourceControl.fromFiddle(configuration.slug, configuration.version, workspaceFolder, context, overwrite); + } + + private static async fromFiddle(fiddleSlug: string, fiddleVersion: number, workspaceFolder: vscode.WorkspaceFolder, context: vscode.ExtensionContext, overwrite: boolean): Promise { + const fiddle = await downloadFiddle(fiddleSlug, fiddleVersion); + const workspacePath = workspaceFolder.uri.fsPath; + return new FiddleSourceControl(context, workspaceFolder, fiddle, overwrite); + } + + private refreshStatusBar() { + this.jsFiddleScm.statusBarCommands = [ + { + "command": "extension.source-control.checkout", + "arguments": [this], + "title": `↕ ${this.fiddle.slug} #${this.fiddle.version} / ${this.latestFiddleVersion}`, + "tooltip": "Checkout another version of this fiddle.", + } + ]; + } + + async commitAll(): Promise { + if (!this.changedResources.resourceStates.length) { + vscode.window.showErrorMessage("There is nothing to commit."); + } + else if (this.fiddle.version < this.latestFiddleVersion) { + vscode.window.showErrorMessage("Checkout the latest fiddle version before committing your changes."); + } + else { + const html = await this.getLocalResourceText('html'); + const js = await this.getLocalResourceText('js'); + const css = await this.getLocalResourceText('css'); + + // here we assume nobody updated the Fiddle on the server since we refreshed the list of versions + try { + const newFiddle = await uploadFiddle(this.fiddle.slug, this.fiddle.version + 1, html, js, css); + if (!newFiddle) { return; } + this.setFiddle(newFiddle, false); + this.jsFiddleScm.inputBox.value = ''; + } catch (ex) { + vscode.window.showErrorMessage("Cannot commit changes to JS Fiddle. " + ex.message); + } + } + } + + private async getLocalResourceText(extension: string) { + const document = await vscode.workspace.openTextDocument(this.fiddleRepository.createLocalResourcePath(extension)); + return document.getText(); + } + + /** + * Throws away all local changes and resets all files to the checked out version of the repository. + */ + resetFilesToCheckedOutVersion(): void { + this.resetFile('html'); + this.resetFile('css'); + this.resetFile('js'); + } + + /** Resets the given local file content to the checked-out version. */ + private async resetFile(extension: string): Promise { + const filePath = this.fiddleRepository.createLocalResourcePath(extension); + await afs.writeFile(filePath, this.fiddle.data[extension]); + } + + async tryCheckout(newVersion: number | undefined): Promise { + if (!Number.isFinite(this.latestFiddleVersion)) { return; } + + if (newVersion === undefined) { + const allVersions = [...Array(this.latestFiddleVersion + 1).keys()] + .map(ver => new VersionQuickPickItem(ver, ver === this.fiddle.version)); + const newVersionPick = await vscode.window.showQuickPick(allVersions, { canPickMany: false, placeHolder: 'Select a version...' }); + if (newVersionPick) { + newVersion = newVersionPick.version; + } + else { + return; + } + } + + if (newVersion === this.fiddle.version) { return; } // the same version was selected + + if (this.changedResources.resourceStates.length) { + const changedResourcesCount = this.changedResources.resourceStates.length; + vscode.window.showErrorMessage(`There is one or more changed resources. Discard or commit your local changes before checking out another version.`); + } + else { + try { + const newFiddle = await downloadFiddle(this.fiddle.slug, newVersion); + this.setFiddle(newFiddle, true); + } catch (ex) { + vscode.window.showErrorMessage(ex); + } + } + } + + private setFiddle(newFiddle: Fiddle, overwrite: boolean) { + if (newFiddle.version > this.latestFiddleVersion) { this.latestFiddleVersion = newFiddle.version; } + this.fiddle = newFiddle; + if (overwrite) { this.resetFilesToCheckedOutVersion(); } // overwrite local file content + this._onRepositoryChange.fire(this.fiddle); + this.refreshStatusBar(); + + this.saveCurrentConfiguration(); + } + + getFiddle(): Fiddle { + return this.fiddle; + } + + getWorkspaceFolder(): vscode.WorkspaceFolder { + return this.workspaceFolder; + } + + getSourceControl(): vscode.SourceControl { + return this.jsFiddleScm; + } + + getRepository(): FiddleRepository { + return this.fiddleRepository; + } + + /** save configuration for later VS Code sessions */ + private saveCurrentConfiguration(): void { + const fiddleConfiguration: FiddleConfiguration = { + slug: this.fiddle.slug, + version: this.fiddle.version, + downloaded: true + }; + + FiddleSourceControl.saveConfiguration(this.workspaceFolder.uri, fiddleConfiguration); + } + + static saveConfiguration(workspaceFolderUri: vscode.Uri, fiddleConfiguration: FiddleConfiguration): void { + const fiddleConfigurationString = JSON.stringify(fiddleConfiguration); + afs.writeFile(path.join(workspaceFolderUri.fsPath, CONFIGURATION_FILE), Buffer.from(fiddleConfigurationString, UTF8)); + } + + get onRepositoryChange(): vscode.Event { + return this._onRepositoryChange.event; + } + + onResourceChange(_uri: vscode.Uri): void { + if (this.timeout) { clearTimeout(this.timeout); } + this.timeout = setTimeout(() => this.tryUpdateChangedGroup(), 500); + } + + async tryUpdateChangedGroup(): Promise { + try { + await this.updateChangedGroup(); + } + catch (ex) { + vscode.window.showErrorMessage(ex); + } + } + + /** 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 + const changedResources: vscode.SourceControlResourceState[] = []; + + const uris = this.fiddleRepository.provideSourceControlledResources(); + + for (const uri of uris) { + let isDirty: boolean; + let wasDeleted: boolean; + + const pathExists = await afs.exists(uri.fsPath); + + if (pathExists) { + const document = await vscode.workspace.openTextDocument(uri); + isDirty = this.isDirty(document); + wasDeleted = false; + } + else { + isDirty = true; + wasDeleted = true; + } + + if (isDirty) { + const resourceState = this.toSourceControlResourceState(uri, wasDeleted); + changedResources.push(resourceState); + } + } + + 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. */ + isDirty(doc: vscode.TextDocument): boolean { + const originalText = this.fiddle.data[toExtension(doc.uri)]; + return originalText.replace('\r', '') !== doc.getText().replace('\r', ''); + } + + toSourceControlResourceState(docUri: vscode.Uri, deleted: boolean): vscode.SourceControlResourceState { + + const repositoryUri = this.fiddleRepository.provideOriginalResource(docUri, null); + + const fiddlePart = toExtension(docUri).toUpperCase(); + + const command: vscode.Command = !deleted + ? { + title: "Show changes", + command: "vscode.diff", + arguments: [repositoryUri, docUri, `JSFiddle#${this.fiddle.slug} ${fiddlePart} ↔ Local changes`], + tooltip: "Diff your changes" + } + : null; + + const resourceState: vscode.SourceControlResourceState = { + resourceUri: docUri, + command: command, + decorations: { + strikeThrough: deleted, + tooltip: 'File was locally deleted.' + } + }; + + 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 { + const 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 { + const 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() { + const 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(); + } +} + +class VersionQuickPickItem implements vscode.QuickPickItem { + + constructor(public readonly version: number, public readonly picked: boolean) { + } + + get label(): string { + return `Version ${this.version}`; + } + + get description(): string { + return this.picked ? '(currently checked-out)' : ''; + } + + get alwaysShow(): boolean { + return this.picked; + } +} diff --git a/source-control-sample/src/util.ts b/source-control-sample/src/util.ts index a282e001..c9d98795 100644 --- a/source-control-sample/src/util.ts +++ b/source-control-sample/src/util.ts @@ -1,11 +1,11 @@ -export function firstIndex(array: T[], fn: (t: T) => boolean): number { - for (let i = 0; i < array.length; i++) { - if (fn(array[i])) { - return i; - } - } - - return -1; -} - +export function firstIndex(array: T[], fn: (t: T) => boolean): number { + for (let i = 0; i < array.length; i++) { + if (fn(array[i])) { + return i; + } + } + + return -1; +} + export const UTF8 = 'utf8'; \ No newline at end of file diff --git a/statusbar-sample/src/extension.ts b/statusbar-sample/src/extension.ts index d8c14dc3..fa6805b6 100644 --- a/statusbar-sample/src/extension.ts +++ b/statusbar-sample/src/extension.ts @@ -1,50 +1,50 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - - -import * as vscode from 'vscode'; - -let myStatusBarItem: vscode.StatusBarItem; - -export function activate({ subscriptions }: vscode.ExtensionContext) { - - // register a command that is invoked when the status bar - // item is selected - const myCommandId = 'sample.showSelectionCount'; - subscriptions.push(vscode.commands.registerCommand(myCommandId, () => { - const n = getNumberOfSelectedLines(vscode.window.activeTextEditor); - vscode.window.showInformationMessage(`Yeah, ${n} line(s) selected... Keep going!`); - })); - - // create a new status bar item that we can now manage - myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); - myStatusBarItem.command = myCommandId; - subscriptions.push(myStatusBarItem); - - // register some listener that make sure the status bar - // item always up-to-date - subscriptions.push(vscode.window.onDidChangeActiveTextEditor(updateStatusBarItem)); - subscriptions.push(vscode.window.onDidChangeTextEditorSelection(updateStatusBarItem)); - - // update status bar item once at start - updateStatusBarItem(); -} - -function updateStatusBarItem(): void { - const n = getNumberOfSelectedLines(vscode.window.activeTextEditor); - if (n > 0) { - myStatusBarItem.text = `$(megaphone) ${n} line(s) selected`; - myStatusBarItem.show(); - } else { - myStatusBarItem.hide(); - } -} - -function getNumberOfSelectedLines(editor: vscode.TextEditor | undefined): number { - let lines = 0; - if (editor) { - lines = editor.selections.reduce((prev, curr) => prev + (curr.end.line - curr.start.line), 0); - } - return lines; -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + + +import * as vscode from 'vscode'; + +let myStatusBarItem: vscode.StatusBarItem; + +export function activate({ subscriptions }: vscode.ExtensionContext) { + + // register a command that is invoked when the status bar + // item is selected + const myCommandId = 'sample.showSelectionCount'; + subscriptions.push(vscode.commands.registerCommand(myCommandId, () => { + const n = getNumberOfSelectedLines(vscode.window.activeTextEditor); + vscode.window.showInformationMessage(`Yeah, ${n} line(s) selected... Keep going!`); + })); + + // create a new status bar item that we can now manage + myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); + myStatusBarItem.command = myCommandId; + subscriptions.push(myStatusBarItem); + + // register some listener that make sure the status bar + // item always up-to-date + subscriptions.push(vscode.window.onDidChangeActiveTextEditor(updateStatusBarItem)); + subscriptions.push(vscode.window.onDidChangeTextEditorSelection(updateStatusBarItem)); + + // update status bar item once at start + updateStatusBarItem(); +} + +function updateStatusBarItem(): void { + const n = getNumberOfSelectedLines(vscode.window.activeTextEditor); + if (n > 0) { + myStatusBarItem.text = `$(megaphone) ${n} line(s) selected`; + myStatusBarItem.show(); + } else { + myStatusBarItem.hide(); + } +} + +function getNumberOfSelectedLines(editor: vscode.TextEditor | undefined): number { + let lines = 0; + if (editor) { + lines = editor.selections.reduce((prev, curr) => prev + (curr.end.line - curr.start.line), 0); + } + return lines; +} diff --git a/task-provider-sample/src/customTaskProvider.ts b/task-provider-sample/src/customTaskProvider.ts index 6ee23a2f..30e1ed94 100644 --- a/task-provider-sample/src/customTaskProvider.ts +++ b/task-provider-sample/src/customTaskProvider.ts @@ -1,135 +1,135 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as vscode from 'vscode'; - -interface CustomBuildTaskDefinition extends vscode.TaskDefinition { - /** - * The build flavor. Should be either '32' or '64'. - */ - flavor: string; - - /** - * Additional build flags - */ - flags?: string[]; -} - -export class CustomBuildTaskProvider implements vscode.TaskProvider { - static CustomBuildScriptType = 'custombuildscript'; - private tasks: vscode.Task[] | undefined; - - // We use a CustomExecution task when state needs to be shared across runs of the task or when - // the task requires use of some VS Code API to run. - // If you don't need to share state between runs and if you don't need to execute VS Code API in your task, - // then a simple ShellExecution or ProcessExecution should be enough. - // Since our build has this shared state, the CustomExecution is used below. - private sharedState: string | undefined; - - constructor(private workspaceRoot: string) { } - - public async provideTasks(): Promise { - return this.getTasks(); - } - - public resolveTask(_task: vscode.Task): vscode.Task | undefined { - const flavor: string = _task.definition.flavor; - if (flavor) { - const definition: CustomBuildTaskDefinition = _task.definition; - return this.getTask(definition.flavor, definition.flags ? definition.flags : [], definition); - } - return undefined; - } - - private getTasks(): vscode.Task[] { - if (this.tasks !== undefined) { - return this.tasks; - } - // In our fictional build, we have two build flavors - const flavors: string[] = ['32', '64']; - // Each flavor can have some options. - const flags: string[][] = [['watch', 'incremental'], ['incremental'], []]; - - this.tasks = []; - flavors.forEach(flavor => { - flags.forEach(flagGroup => { - this.tasks!.push(this.getTask(flavor, flagGroup)); - }); - }); - return this.tasks; - } - - private getTask(flavor: string, flags: string[], definition?: CustomBuildTaskDefinition): vscode.Task { - if (definition === undefined) { - definition = { - type: CustomBuildTaskProvider.CustomBuildScriptType, - flavor, - flags - }; - } - return new vscode.Task(definition, vscode.TaskScope.Workspace, `${flavor} ${flags.join(' ')}`, - CustomBuildTaskProvider.CustomBuildScriptType, new vscode.CustomExecution(async (): Promise => { - // When the task is executed, this callback will run. Here, we setup for running the task. - return new CustomBuildTaskTerminal(this.workspaceRoot, flavor, flags, () => this.sharedState, (state: string) => this.sharedState = state); - })); - } -} - -class CustomBuildTaskTerminal implements vscode.Pseudoterminal { - private writeEmitter = new vscode.EventEmitter(); - onDidWrite: vscode.Event = this.writeEmitter.event; - private closeEmitter = new vscode.EventEmitter(); - onDidClose?: vscode.Event = this.closeEmitter.event; - - private fileWatcher: vscode.FileSystemWatcher | undefined; - - constructor(private workspaceRoot: string, private flavor: string, private flags: string[], private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) { - } - - open(initialDimensions: vscode.TerminalDimensions | undefined): void { - // At this point we can start using the terminal. - if (this.flags.indexOf('watch') > -1) { - const pattern = path.join(this.workspaceRoot, 'customBuildFile'); - this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); - this.fileWatcher.onDidChange(() => this.doBuild()); - this.fileWatcher.onDidCreate(() => this.doBuild()); - this.fileWatcher.onDidDelete(() => this.doBuild()); - } - this.doBuild(); - } - - close(): void { - // The terminal has been closed. Shutdown the build. - if (this.fileWatcher) { - this.fileWatcher.dispose(); - } - } - - private async doBuild(): Promise { - return new Promise((resolve) => { - this.writeEmitter.fire('Starting build...\r\n'); - let isIncremental = this.flags.indexOf('incremental') > -1; - if (isIncremental) { - if (this.getSharedState()) { - this.writeEmitter.fire('Using last build results: ' + this.getSharedState() + '\r\n'); - } else { - isIncremental = false; - this.writeEmitter.fire('No result from last build. Doing full build.\r\n'); - } - } - - // Since we don't actually build anything in this example set a timeout instead. - setTimeout(() => { - const date = new Date(); - this.setSharedState(date.toTimeString() + ' ' + date.toDateString()); - this.writeEmitter.fire('Build complete.\r\n\r\n'); - if (this.flags.indexOf('watch') === -1) { - this.closeEmitter.fire(0); - resolve(); - } - }, isIncremental ? 1000 : 4000); - }); - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import * as vscode from 'vscode'; + +interface CustomBuildTaskDefinition extends vscode.TaskDefinition { + /** + * The build flavor. Should be either '32' or '64'. + */ + flavor: string; + + /** + * Additional build flags + */ + flags?: string[]; +} + +export class CustomBuildTaskProvider implements vscode.TaskProvider { + static CustomBuildScriptType = 'custombuildscript'; + private tasks: vscode.Task[] | undefined; + + // We use a CustomExecution task when state needs to be shared across runs of the task or when + // the task requires use of some VS Code API to run. + // If you don't need to share state between runs and if you don't need to execute VS Code API in your task, + // then a simple ShellExecution or ProcessExecution should be enough. + // Since our build has this shared state, the CustomExecution is used below. + private sharedState: string | undefined; + + constructor(private workspaceRoot: string) { } + + public async provideTasks(): Promise { + return this.getTasks(); + } + + public resolveTask(_task: vscode.Task): vscode.Task | undefined { + const flavor: string = _task.definition.flavor; + if (flavor) { + const definition: CustomBuildTaskDefinition = _task.definition; + return this.getTask(definition.flavor, definition.flags ? definition.flags : [], definition); + } + return undefined; + } + + private getTasks(): vscode.Task[] { + if (this.tasks !== undefined) { + return this.tasks; + } + // In our fictional build, we have two build flavors + const flavors: string[] = ['32', '64']; + // Each flavor can have some options. + const flags: string[][] = [['watch', 'incremental'], ['incremental'], []]; + + this.tasks = []; + flavors.forEach(flavor => { + flags.forEach(flagGroup => { + this.tasks!.push(this.getTask(flavor, flagGroup)); + }); + }); + return this.tasks; + } + + private getTask(flavor: string, flags: string[], definition?: CustomBuildTaskDefinition): vscode.Task { + if (definition === undefined) { + definition = { + type: CustomBuildTaskProvider.CustomBuildScriptType, + flavor, + flags + }; + } + return new vscode.Task(definition, vscode.TaskScope.Workspace, `${flavor} ${flags.join(' ')}`, + CustomBuildTaskProvider.CustomBuildScriptType, new vscode.CustomExecution(async (): Promise => { + // When the task is executed, this callback will run. Here, we setup for running the task. + return new CustomBuildTaskTerminal(this.workspaceRoot, flavor, flags, () => this.sharedState, (state: string) => this.sharedState = state); + })); + } +} + +class CustomBuildTaskTerminal implements vscode.Pseudoterminal { + private writeEmitter = new vscode.EventEmitter(); + onDidWrite: vscode.Event = this.writeEmitter.event; + private closeEmitter = new vscode.EventEmitter(); + onDidClose?: vscode.Event = this.closeEmitter.event; + + private fileWatcher: vscode.FileSystemWatcher | undefined; + + constructor(private workspaceRoot: string, private flavor: string, private flags: string[], private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) { + } + + open(initialDimensions: vscode.TerminalDimensions | undefined): void { + // At this point we can start using the terminal. + if (this.flags.indexOf('watch') > -1) { + const pattern = path.join(this.workspaceRoot, 'customBuildFile'); + this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); + this.fileWatcher.onDidChange(() => this.doBuild()); + this.fileWatcher.onDidCreate(() => this.doBuild()); + this.fileWatcher.onDidDelete(() => this.doBuild()); + } + this.doBuild(); + } + + close(): void { + // The terminal has been closed. Shutdown the build. + if (this.fileWatcher) { + this.fileWatcher.dispose(); + } + } + + private async doBuild(): Promise { + return new Promise((resolve) => { + this.writeEmitter.fire('Starting build...\r\n'); + let isIncremental = this.flags.indexOf('incremental') > -1; + if (isIncremental) { + if (this.getSharedState()) { + this.writeEmitter.fire('Using last build results: ' + this.getSharedState() + '\r\n'); + } else { + isIncremental = false; + this.writeEmitter.fire('No result from last build. Doing full build.\r\n'); + } + } + + // Since we don't actually build anything in this example set a timeout instead. + setTimeout(() => { + const date = new Date(); + this.setSharedState(date.toTimeString() + ' ' + date.toDateString()); + this.writeEmitter.fire('Build complete.\r\n\r\n'); + if (this.flags.indexOf('watch') === -1) { + this.closeEmitter.fire(0); + resolve(); + } + }, isIncremental ? 1000 : 4000); + }); + } +} diff --git a/task-provider-sample/src/extension.ts b/task-provider-sample/src/extension.ts index 7a41b77d..8283308e 100644 --- a/task-provider-sample/src/extension.ts +++ b/task-provider-sample/src/extension.ts @@ -15,7 +15,7 @@ export function activate(_context: vscode.ExtensionContext): void { if (!workspaceRoot) { return; } - + rakeTaskProvider = vscode.tasks.registerTaskProvider(RakeTaskProvider.RakeType, new RakeTaskProvider(workspaceRoot)); customTaskProvider = vscode.tasks.registerTaskProvider(CustomBuildTaskProvider.CustomBuildScriptType, new CustomBuildTaskProvider(workspaceRoot)); } diff --git a/task-provider-sample/src/rakeTaskProvider.ts b/task-provider-sample/src/rakeTaskProvider.ts index ca9d0081..0f851a77 100644 --- a/task-provider-sample/src/rakeTaskProvider.ts +++ b/task-provider-sample/src/rakeTaskProvider.ts @@ -1,163 +1,163 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as fs from 'fs'; -import * as cp from 'child_process'; -import * as vscode from 'vscode'; -import { error } from 'console'; - -export class RakeTaskProvider implements vscode.TaskProvider { - static RakeType = 'rake'; - private rakePromise: Thenable | undefined = undefined; - - constructor(workspaceRoot: string) { - const pattern = path.join(workspaceRoot, 'Rakefile'); - const fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); - fileWatcher.onDidChange(() => this.rakePromise = undefined); - fileWatcher.onDidCreate(() => this.rakePromise = undefined); - fileWatcher.onDidDelete(() => this.rakePromise = undefined); - } - - public provideTasks(): Thenable | undefined { - if (!this.rakePromise) { - this.rakePromise = getRakeTasks(); - } - return this.rakePromise; - } - - public resolveTask(_task: vscode.Task): vscode.Task | undefined { - const task = _task.definition.task; - // A Rake task consists of a task and an optional file as specified in RakeTaskDefinition - // Make sure that this looks like a Rake task by checking that there is a task. - if (task) { - // resolveTask requires that the same definition object be used. - const definition: RakeTaskDefinition = _task.definition; - return new vscode.Task(definition, _task.scope ?? vscode.TaskScope.Workspace, definition.task, 'rake', new vscode.ShellExecution(`rake ${definition.task}`)); - } - return undefined; - } -} - -function exists(file: string): Promise { - return new Promise((resolve, _reject) => { - fs.exists(file, (value) => { - resolve(value); - }); - }); -} - -function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> { - return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { - cp.exec(command, options, (error, stdout, stderr) => { - if (error) { - reject({ error, stdout, stderr }); - } - resolve({ stdout, stderr }); - }); - }); -} - -let _channel: vscode.OutputChannel; -function getOutputChannel(): vscode.OutputChannel { - if (!_channel) { - _channel = vscode.window.createOutputChannel('Rake Auto Detection'); - } - return _channel; -} - -interface RakeTaskDefinition extends vscode.TaskDefinition { - /** - * The task name - */ - task: string; - - /** - * The rake file containing the task - */ - file?: string; -} - -const buildNames: string[] = ['build', 'compile', 'watch']; -function isBuildTask(name: string): boolean { - for (const buildName of buildNames) { - if (name.indexOf(buildName) !== -1) { - return true; - } - } - return false; -} - -const testNames: string[] = ['test']; -function isTestTask(name: string): boolean { - for (const testName of testNames) { - if (name.indexOf(testName) !== -1) { - return true; - } - } - return false; -} - -async function getRakeTasks(): Promise { - const workspaceFolders = vscode.workspace.workspaceFolders; - const result: vscode.Task[] = []; - if (!workspaceFolders || workspaceFolders.length === 0) { - return result; - } - for (const workspaceFolder of workspaceFolders) { - const folderString = workspaceFolder.uri.fsPath; - if (!folderString) { - continue; - } - const rakeFile = path.join(folderString, 'Rakefile'); - if (!await exists(rakeFile)) { - continue; - } - - const commandLine = 'rake -AT -f Rakefile'; - try { - const { stdout, stderr } = await exec(commandLine, { cwd: folderString }); - if (stderr && stderr.length > 0) { - getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); - } - if (stdout) { - const lines = stdout.split(/\r{0,1}\n/); - for (const line of lines) { - if (line.length === 0) { - continue; - } - const regExp = /rake\s(.*)#/; - const matches = regExp.exec(line); - if (matches && matches.length === 2) { - const taskName = matches[1].trim(); - const kind: RakeTaskDefinition = { - type: 'rake', - task: taskName - }; - const task = new vscode.Task(kind, workspaceFolder, taskName, 'rake', new vscode.ShellExecution(`rake ${taskName}`)); - result.push(task); - const lowerCaseLine = line.toLowerCase(); - if (isBuildTask(lowerCaseLine)) { - task.group = vscode.TaskGroup.Build; - } else if (isTestTask(lowerCaseLine)) { - task.group = vscode.TaskGroup.Test; - } - } - } - } - } catch (err: any) { - const channel = getOutputChannel(); - if (err.stderr) { - channel.appendLine(err.stderr); - } - if (err.stdout) { - channel.appendLine(err.stdout); - } - channel.appendLine('Auto detecting rake tasks failed.'); - channel.show(true); - } - } - return result; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import * as fs from 'fs'; +import * as cp from 'child_process'; +import * as vscode from 'vscode'; +import { error } from 'console'; + +export class RakeTaskProvider implements vscode.TaskProvider { + static RakeType = 'rake'; + private rakePromise: Thenable | undefined = undefined; + + constructor(workspaceRoot: string) { + const pattern = path.join(workspaceRoot, 'Rakefile'); + const fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); + fileWatcher.onDidChange(() => this.rakePromise = undefined); + fileWatcher.onDidCreate(() => this.rakePromise = undefined); + fileWatcher.onDidDelete(() => this.rakePromise = undefined); + } + + public provideTasks(): Thenable | undefined { + if (!this.rakePromise) { + this.rakePromise = getRakeTasks(); + } + return this.rakePromise; + } + + public resolveTask(_task: vscode.Task): vscode.Task | undefined { + const task = _task.definition.task; + // A Rake task consists of a task and an optional file as specified in RakeTaskDefinition + // Make sure that this looks like a Rake task by checking that there is a task. + if (task) { + // resolveTask requires that the same definition object be used. + const definition: RakeTaskDefinition = _task.definition; + return new vscode.Task(definition, _task.scope ?? vscode.TaskScope.Workspace, definition.task, 'rake', new vscode.ShellExecution(`rake ${definition.task}`)); + } + return undefined; + } +} + +function exists(file: string): Promise { + return new Promise((resolve, _reject) => { + fs.exists(file, (value) => { + resolve(value); + }); + }); +} + +function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> { + return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { + cp.exec(command, options, (error, stdout, stderr) => { + if (error) { + reject({ error, stdout, stderr }); + } + resolve({ stdout, stderr }); + }); + }); +} + +let _channel: vscode.OutputChannel; +function getOutputChannel(): vscode.OutputChannel { + if (!_channel) { + _channel = vscode.window.createOutputChannel('Rake Auto Detection'); + } + return _channel; +} + +interface RakeTaskDefinition extends vscode.TaskDefinition { + /** + * The task name + */ + task: string; + + /** + * The rake file containing the task + */ + file?: string; +} + +const buildNames: string[] = ['build', 'compile', 'watch']; +function isBuildTask(name: string): boolean { + for (const buildName of buildNames) { + if (name.indexOf(buildName) !== -1) { + return true; + } + } + return false; +} + +const testNames: string[] = ['test']; +function isTestTask(name: string): boolean { + for (const testName of testNames) { + if (name.indexOf(testName) !== -1) { + return true; + } + } + return false; +} + +async function getRakeTasks(): Promise { + const workspaceFolders = vscode.workspace.workspaceFolders; + const result: vscode.Task[] = []; + if (!workspaceFolders || workspaceFolders.length === 0) { + return result; + } + for (const workspaceFolder of workspaceFolders) { + const folderString = workspaceFolder.uri.fsPath; + if (!folderString) { + continue; + } + const rakeFile = path.join(folderString, 'Rakefile'); + if (!await exists(rakeFile)) { + continue; + } + + const commandLine = 'rake -AT -f Rakefile'; + try { + const { stdout, stderr } = await exec(commandLine, { cwd: folderString }); + if (stderr && stderr.length > 0) { + getOutputChannel().appendLine(stderr); + getOutputChannel().show(true); + } + if (stdout) { + const lines = stdout.split(/\r{0,1}\n/); + for (const line of lines) { + if (line.length === 0) { + continue; + } + const regExp = /rake\s(.*)#/; + const matches = regExp.exec(line); + if (matches && matches.length === 2) { + const taskName = matches[1].trim(); + const kind: RakeTaskDefinition = { + type: 'rake', + task: taskName + }; + const task = new vscode.Task(kind, workspaceFolder, taskName, 'rake', new vscode.ShellExecution(`rake ${taskName}`)); + result.push(task); + const lowerCaseLine = line.toLowerCase(); + if (isBuildTask(lowerCaseLine)) { + task.group = vscode.TaskGroup.Build; + } else if (isTestTask(lowerCaseLine)) { + task.group = vscode.TaskGroup.Test; + } + } + } + } + } catch (err: any) { + const channel = getOutputChannel(); + if (err.stderr) { + channel.appendLine(err.stderr); + } + if (err.stdout) { + channel.appendLine(err.stdout); + } + channel.appendLine('Auto detecting rake tasks failed.'); + channel.show(true); + } + } + return result; +} diff --git a/terminal-sample/src/extension.ts b/terminal-sample/src/extension.ts index 77985d6a..622eb205 100644 --- a/terminal-sample/src/extension.ts +++ b/terminal-sample/src/extension.ts @@ -1,235 +1,235 @@ -'use strict'; - -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - let NEXT_TERM_ID = 1; - - console.log("Terminals: " + (vscode.window).terminals.length); - - // vscode.window.onDidOpenTerminal - vscode.window.onDidOpenTerminal(terminal => { - console.log("Terminal opened. Total count: " + (vscode.window).terminals.length); - }); - vscode.window.onDidOpenTerminal((terminal: vscode.Terminal) => { - vscode.window.showInformationMessage(`onDidOpenTerminal, name: ${terminal.name}`); - }); - - // vscode.window.onDidChangeActiveTerminal - vscode.window.onDidChangeActiveTerminal(e => { - console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`); - }); - - // vscode.window.createTerminal - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createTerminal', () => { - vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`); - vscode.window.showInformationMessage('Hello World 2!'); - })); - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createTerminalHideFromUser', () => { - vscode.window.createTerminal({ - name: `Ext Terminal #${NEXT_TERM_ID++}`, - hideFromUser: true - } as any); - })); - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => { - const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`); - terminal.sendText("echo 'Sent text immediately after creating'"); - })); - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createZshLoginShell', () => { - vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`, '/bin/zsh', ['-l']); - })); - - // Terminal.hide - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.hide', () => { - if (ensureTerminalExists()) { - selectTerminal().then(terminal => { - if (terminal) { - terminal.hide(); - } - }); - } - })); - - // Terminal.show - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.show', () => { - if (ensureTerminalExists()) { - selectTerminal().then(terminal => { - if (terminal) { - terminal.show(); - } - }); - } - })); - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.showPreserveFocus', () => { - if (ensureTerminalExists()) { - selectTerminal().then(terminal => { - if (terminal) { - terminal.show(true); - } - }); - } - })); - - // Terminal.sendText - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.sendText', () => { - if (ensureTerminalExists()) { - selectTerminal().then(terminal => { - if (terminal) { - terminal.sendText("echo 'Hello world!'"); - } - }); - } - })); - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.sendTextNoNewLine', () => { - if (ensureTerminalExists()) { - selectTerminal().then(terminal => { - if (terminal) { - terminal.sendText("echo 'Hello world!'", false); - } - }); - } - })); - - // Terminal.dispose - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.dispose', () => { - if (ensureTerminalExists()) { - selectTerminal().then(terminal => { - if (terminal) { - terminal.dispose(); - } - }); - } - })); - - // Terminal.processId - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.processId', () => { - selectTerminal().then(terminal => { - if (!terminal) { - return; - } - terminal.processId.then((processId) => { - if (processId) { - vscode.window.showInformationMessage(`Terminal.processId: ${processId}`); - } else { - vscode.window.showInformationMessage('Terminal does not have a process ID'); - } - }); - }); - })); - - // vscode.window.onDidCloseTerminal - vscode.window.onDidCloseTerminal((terminal) => { - vscode.window.showInformationMessage(`onDidCloseTerminal, name: ${terminal.name}`); - }); - - // vscode.window.terminals - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.terminals', () => { - selectTerminal(); - })); - - // ExtensionContext.environmentVariableCollection - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.updateEnvironment', () => { - const collection = context.environmentVariableCollection; - collection.replace('FOO', 'BAR'); - collection.append('PATH', '/test/path'); - })); - - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.clearEnvironment', () => { - context.environmentVariableCollection.clear(); - })); - - // vvv Proposed APIs below vvv - - // vscode.window.onDidWriteTerminalData - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.onDidWriteTerminalData', () => { - (vscode.window).onDidWriteTerminalData((e: any) => { - vscode.window.showInformationMessage(`onDidWriteTerminalData listener attached, check the devtools console to see events`); - console.log('onDidWriteData', e); - }); - })); - - // vscode.window.onDidChangeTerminalDimensions - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.onDidChangeTerminalDimensions', () => { - vscode.window.showInformationMessage(`Listening to onDidChangeTerminalDimensions, check the devtools console to see events`); - (vscode.window).onDidChangeTerminalDimensions((event: any) => { - console.log(`onDidChangeTerminalDimensions: terminal:${event.terminal.name}, columns=${event.dimensions.columns}, rows=${event.dimensions.rows}`); - }); - })); - - // vscode.window.registerTerminalLinkProvider - context.subscriptions.push(vscode.commands.registerCommand('terminalTest.registerTerminalLinkProvider', () => { - (vscode.window).registerTerminalLinkProvider({ - provideTerminalLinks: (context: any, token: vscode.CancellationToken) => { - // Detect the first instance of the word "link" if it exists and linkify it - const startIndex = (context.line as string).indexOf('link'); - if (startIndex === -1) { - return []; - } - return [ - { - startIndex, - length: 'link'.length, - tooltip: 'Show a notification', - // You can return data in this object to access inside handleTerminalLink - data: 'Example data' - } - ]; - }, - handleTerminalLink: (link: any) => { - vscode.window.showInformationMessage(`Link activated (data = ${link.data})`); - } - }); - })); - - context.subscriptions.push(vscode.window.registerTerminalProfileProvider('terminalTest.terminal-profile', { - provideTerminalProfile(token: vscode.CancellationToken): vscode.ProviderResult { - return { - options: { - name: 'Terminal API', - shellPath: process.title || 'C:/Windows/System32/cmd.exe' - } - }; - } - })); -} - -function colorText(text: string): string { - let output = ''; - let colorIndex = 1; - for (let i = 0; i < text.length; i++) { - const char = text.charAt(i); - if (char === ' ' || char === '\r' || char === '\n') { - output += char; - } else { - output += `\x1b[3${colorIndex++}m${text.charAt(i)}\x1b[0m`; - if (colorIndex > 6) { - colorIndex = 1; - } - } - } - return output; -} - -function selectTerminal(): Thenable { - interface TerminalQuickPickItem extends vscode.QuickPickItem { - terminal: vscode.Terminal; - } - const terminals = (vscode.window).terminals; - const items: TerminalQuickPickItem[] = terminals.map(t => { - return { - label: `name: ${t.name}`, - terminal: t - }; - }); - return vscode.window.showQuickPick(items).then(item => { - return item ? item.terminal : undefined; - }); -} - -function ensureTerminalExists(): boolean { - if ((vscode.window).terminals.length === 0) { - vscode.window.showErrorMessage('No active terminals'); - return false; - } - return true; -} +'use strict'; + +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + let NEXT_TERM_ID = 1; + + console.log("Terminals: " + (vscode.window).terminals.length); + + // vscode.window.onDidOpenTerminal + vscode.window.onDidOpenTerminal(terminal => { + console.log("Terminal opened. Total count: " + (vscode.window).terminals.length); + }); + vscode.window.onDidOpenTerminal((terminal: vscode.Terminal) => { + vscode.window.showInformationMessage(`onDidOpenTerminal, name: ${terminal.name}`); + }); + + // vscode.window.onDidChangeActiveTerminal + vscode.window.onDidChangeActiveTerminal(e => { + console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`); + }); + + // vscode.window.createTerminal + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createTerminal', () => { + vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`); + vscode.window.showInformationMessage('Hello World 2!'); + })); + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createTerminalHideFromUser', () => { + vscode.window.createTerminal({ + name: `Ext Terminal #${NEXT_TERM_ID++}`, + hideFromUser: true + } as any); + })); + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => { + const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`); + terminal.sendText("echo 'Sent text immediately after creating'"); + })); + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createZshLoginShell', () => { + vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`, '/bin/zsh', ['-l']); + })); + + // Terminal.hide + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.hide', () => { + if (ensureTerminalExists()) { + selectTerminal().then(terminal => { + if (terminal) { + terminal.hide(); + } + }); + } + })); + + // Terminal.show + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.show', () => { + if (ensureTerminalExists()) { + selectTerminal().then(terminal => { + if (terminal) { + terminal.show(); + } + }); + } + })); + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.showPreserveFocus', () => { + if (ensureTerminalExists()) { + selectTerminal().then(terminal => { + if (terminal) { + terminal.show(true); + } + }); + } + })); + + // Terminal.sendText + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.sendText', () => { + if (ensureTerminalExists()) { + selectTerminal().then(terminal => { + if (terminal) { + terminal.sendText("echo 'Hello world!'"); + } + }); + } + })); + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.sendTextNoNewLine', () => { + if (ensureTerminalExists()) { + selectTerminal().then(terminal => { + if (terminal) { + terminal.sendText("echo 'Hello world!'", false); + } + }); + } + })); + + // Terminal.dispose + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.dispose', () => { + if (ensureTerminalExists()) { + selectTerminal().then(terminal => { + if (terminal) { + terminal.dispose(); + } + }); + } + })); + + // Terminal.processId + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.processId', () => { + selectTerminal().then(terminal => { + if (!terminal) { + return; + } + terminal.processId.then((processId) => { + if (processId) { + vscode.window.showInformationMessage(`Terminal.processId: ${processId}`); + } else { + vscode.window.showInformationMessage('Terminal does not have a process ID'); + } + }); + }); + })); + + // vscode.window.onDidCloseTerminal + vscode.window.onDidCloseTerminal((terminal) => { + vscode.window.showInformationMessage(`onDidCloseTerminal, name: ${terminal.name}`); + }); + + // vscode.window.terminals + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.terminals', () => { + selectTerminal(); + })); + + // ExtensionContext.environmentVariableCollection + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.updateEnvironment', () => { + const collection = context.environmentVariableCollection; + collection.replace('FOO', 'BAR'); + collection.append('PATH', '/test/path'); + })); + + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.clearEnvironment', () => { + context.environmentVariableCollection.clear(); + })); + + // vvv Proposed APIs below vvv + + // vscode.window.onDidWriteTerminalData + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.onDidWriteTerminalData', () => { + (vscode.window).onDidWriteTerminalData((e: any) => { + vscode.window.showInformationMessage(`onDidWriteTerminalData listener attached, check the devtools console to see events`); + console.log('onDidWriteData', e); + }); + })); + + // vscode.window.onDidChangeTerminalDimensions + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.onDidChangeTerminalDimensions', () => { + vscode.window.showInformationMessage(`Listening to onDidChangeTerminalDimensions, check the devtools console to see events`); + (vscode.window).onDidChangeTerminalDimensions((event: any) => { + console.log(`onDidChangeTerminalDimensions: terminal:${event.terminal.name}, columns=${event.dimensions.columns}, rows=${event.dimensions.rows}`); + }); + })); + + // vscode.window.registerTerminalLinkProvider + context.subscriptions.push(vscode.commands.registerCommand('terminalTest.registerTerminalLinkProvider', () => { + (vscode.window).registerTerminalLinkProvider({ + provideTerminalLinks: (context: any, token: vscode.CancellationToken) => { + // Detect the first instance of the word "link" if it exists and linkify it + const startIndex = (context.line as string).indexOf('link'); + if (startIndex === -1) { + return []; + } + return [ + { + startIndex, + length: 'link'.length, + tooltip: 'Show a notification', + // You can return data in this object to access inside handleTerminalLink + data: 'Example data' + } + ]; + }, + handleTerminalLink: (link: any) => { + vscode.window.showInformationMessage(`Link activated (data = ${link.data})`); + } + }); + })); + + context.subscriptions.push(vscode.window.registerTerminalProfileProvider('terminalTest.terminal-profile', { + provideTerminalProfile(token: vscode.CancellationToken): vscode.ProviderResult { + return { + options: { + name: 'Terminal API', + shellPath: process.title || 'C:/Windows/System32/cmd.exe' + } + }; + } + })); +} + +function colorText(text: string): string { + let output = ''; + let colorIndex = 1; + for (let i = 0; i < text.length; i++) { + const char = text.charAt(i); + if (char === ' ' || char === '\r' || char === '\n') { + output += char; + } else { + output += `\x1b[3${colorIndex++}m${text.charAt(i)}\x1b[0m`; + if (colorIndex > 6) { + colorIndex = 1; + } + } + } + return output; +} + +function selectTerminal(): Thenable { + interface TerminalQuickPickItem extends vscode.QuickPickItem { + terminal: vscode.Terminal; + } + const terminals = (vscode.window).terminals; + const items: TerminalQuickPickItem[] = terminals.map(t => { + return { + label: `name: ${t.name}`, + terminal: t + }; + }); + return vscode.window.showQuickPick(items).then(item => { + return item ? item.terminal : undefined; + }); +} + +function ensureTerminalExists(): boolean { + if ((vscode.window).terminals.length === 0) { + vscode.window.showErrorMessage('No active terminals'); + return false; + } + return true; +} diff --git a/test-provider-sample/src/extension.ts b/test-provider-sample/src/extension.ts index 815e3142..93bbd3cf 100644 --- a/test-provider-sample/src/extension.ts +++ b/test-provider-sample/src/extension.ts @@ -1,186 +1,186 @@ -import * as vscode from 'vscode'; -import { getContentFromFilesystem, MarkdownTestData, TestCase, testData, TestFile } from './testTree'; - -export async function activate(context: vscode.ExtensionContext) { - const ctrl = vscode.tests.createTestController('mathTestController', 'Markdown Math'); - context.subscriptions.push(ctrl); - - const runHandler = (request: vscode.TestRunRequest, cancellation: vscode.CancellationToken) => { - const queue: { test: vscode.TestItem; data: TestCase }[] = []; - const run = ctrl.createTestRun(request); - // map of file uris to statements on each line: - const coveredLines = new Map(); - - const discoverTests = async (tests: Iterable) => { - for (const test of tests) { - if (request.exclude?.includes(test)) { - continue; - } - - const data = testData.get(test); - if (data instanceof TestCase) { - run.enqueued(test); - queue.push({ test, data }); - } else { - if (data instanceof TestFile && !data.didResolve) { - await data.updateFromDisk(ctrl, test); - } - - await discoverTests(gatherTestItems(test.children)); - } - - if (test.uri && !coveredLines.has(test.uri.toString())) { - try { - const lines = (await getContentFromFilesystem(test.uri)).split('\n'); - coveredLines.set( - test.uri.toString(), - lines.map((lineText, lineNo) => - lineText.trim().length ? new vscode.StatementCoverage(0, new vscode.Position(lineNo, 0)) : undefined - ) - ); - } catch { - // ignored - } - } - } - }; - - const runTestQueue = async () => { - for (const { test, data } of queue) { - run.appendOutput(`Running ${test.id}\r\n`); - if (cancellation.isCancellationRequested) { - run.skipped(test); - } else { - run.started(test); - await data.run(test, run); - } - - const lineNo = test.range!.start.line; - const fileCoverage = coveredLines.get(test.uri!.toString()); - if (fileCoverage) { - fileCoverage[lineNo]!.executionCount++; - } - - run.appendOutput(`Completed ${test.id}\r\n`); - } - - run.end(); - }; - - run.coverageProvider = { - provideFileCoverage() { - const coverage: vscode.FileCoverage[] = []; - for (const [uri, statements] of coveredLines) { - coverage.push( - vscode.FileCoverage.fromDetails( - vscode.Uri.parse(uri), - statements.filter((s): s is vscode.StatementCoverage => !!s) - ) - ); - } - - return coverage; - }, - }; - - discoverTests(request.include ?? gatherTestItems(ctrl.items)).then(runTestQueue); - }; - - ctrl.refreshHandler = async () => { - await Promise.all(getWorkspaceTestPatterns().map(({ pattern }) => findInitialFiles(ctrl, pattern))); - }; - - ctrl.createRunProfile('Run Tests', vscode.TestRunProfileKind.Run, runHandler, true); - - ctrl.resolveHandler = async item => { - if (!item) { - context.subscriptions.push(...startWatchingWorkspace(ctrl)); - return; - } - - const data = testData.get(item); - if (data instanceof TestFile) { - await data.updateFromDisk(ctrl, item); - } - }; - - function updateNodeForDocument(e: vscode.TextDocument) { - if (e.uri.scheme !== 'file') { - return; - } - - if (!e.uri.path.endsWith('.md')) { - return; - } - - const { file, data } = getOrCreateFile(ctrl, e.uri); - data.updateFromContents(ctrl, e.getText(), file); - } - - for (const document of vscode.workspace.textDocuments) { - updateNodeForDocument(document); - } - - context.subscriptions.push( - vscode.workspace.onDidOpenTextDocument(updateNodeForDocument), - vscode.workspace.onDidChangeTextDocument(e => updateNodeForDocument(e.document)), - ); -} - -function getOrCreateFile(controller: vscode.TestController, uri: vscode.Uri) { - const existing = controller.items.get(uri.toString()); - if (existing) { - return { file: existing, data: testData.get(existing) as TestFile }; - } - - const file = controller.createTestItem(uri.toString(), uri.path.split('/').pop()!, uri); - controller.items.add(file); - - const data = new TestFile(); - testData.set(file, data); - - file.canResolveChildren = true; - return { file, data }; -} - -function gatherTestItems(collection: vscode.TestItemCollection) { - const items: vscode.TestItem[] = []; - collection.forEach(item => items.push(item)); - return items; -} - -function getWorkspaceTestPatterns() { - if (!vscode.workspace.workspaceFolders) { - return []; - } - - return vscode.workspace.workspaceFolders.map(workspaceFolder => ({ - workspaceFolder, - pattern: new vscode.RelativePattern(workspaceFolder, '**/*.md'), - })); -} - -async function findInitialFiles(controller: vscode.TestController, pattern: vscode.GlobPattern) { - for (const file of await vscode.workspace.findFiles(pattern)) { - getOrCreateFile(controller, file); - } -} - -function startWatchingWorkspace(controller: vscode.TestController) { - return getWorkspaceTestPatterns().map(({ workspaceFolder, pattern }) => { - const watcher = vscode.workspace.createFileSystemWatcher(pattern); - - watcher.onDidCreate(uri => getOrCreateFile(controller, uri)); - watcher.onDidChange(uri => { - const { file, data } = getOrCreateFile(controller, uri); - if (data.didResolve) { - data.updateFromDisk(controller, file); - } - }); - watcher.onDidDelete(uri => controller.items.delete(uri.toString())); - - findInitialFiles(controller, pattern); - - return watcher; - }); -} +import * as vscode from 'vscode'; +import { getContentFromFilesystem, MarkdownTestData, TestCase, testData, TestFile } from './testTree'; + +export async function activate(context: vscode.ExtensionContext) { + const ctrl = vscode.tests.createTestController('mathTestController', 'Markdown Math'); + context.subscriptions.push(ctrl); + + const runHandler = (request: vscode.TestRunRequest, cancellation: vscode.CancellationToken) => { + const queue: { test: vscode.TestItem; data: TestCase }[] = []; + const run = ctrl.createTestRun(request); + // map of file uris to statements on each line: + const coveredLines = new Map(); + + const discoverTests = async (tests: Iterable) => { + for (const test of tests) { + if (request.exclude?.includes(test)) { + continue; + } + + const data = testData.get(test); + if (data instanceof TestCase) { + run.enqueued(test); + queue.push({ test, data }); + } else { + if (data instanceof TestFile && !data.didResolve) { + await data.updateFromDisk(ctrl, test); + } + + await discoverTests(gatherTestItems(test.children)); + } + + if (test.uri && !coveredLines.has(test.uri.toString())) { + try { + const lines = (await getContentFromFilesystem(test.uri)).split('\n'); + coveredLines.set( + test.uri.toString(), + lines.map((lineText, lineNo) => + lineText.trim().length ? new vscode.StatementCoverage(0, new vscode.Position(lineNo, 0)) : undefined + ) + ); + } catch { + // ignored + } + } + } + }; + + const runTestQueue = async () => { + for (const { test, data } of queue) { + run.appendOutput(`Running ${test.id}\r\n`); + if (cancellation.isCancellationRequested) { + run.skipped(test); + } else { + run.started(test); + await data.run(test, run); + } + + const lineNo = test.range!.start.line; + const fileCoverage = coveredLines.get(test.uri!.toString()); + if (fileCoverage) { + fileCoverage[lineNo]!.executionCount++; + } + + run.appendOutput(`Completed ${test.id}\r\n`); + } + + run.end(); + }; + + run.coverageProvider = { + provideFileCoverage() { + const coverage: vscode.FileCoverage[] = []; + for (const [uri, statements] of coveredLines) { + coverage.push( + vscode.FileCoverage.fromDetails( + vscode.Uri.parse(uri), + statements.filter((s): s is vscode.StatementCoverage => !!s) + ) + ); + } + + return coverage; + }, + }; + + discoverTests(request.include ?? gatherTestItems(ctrl.items)).then(runTestQueue); + }; + + ctrl.refreshHandler = async () => { + await Promise.all(getWorkspaceTestPatterns().map(({ pattern }) => findInitialFiles(ctrl, pattern))); + }; + + ctrl.createRunProfile('Run Tests', vscode.TestRunProfileKind.Run, runHandler, true); + + ctrl.resolveHandler = async item => { + if (!item) { + context.subscriptions.push(...startWatchingWorkspace(ctrl)); + return; + } + + const data = testData.get(item); + if (data instanceof TestFile) { + await data.updateFromDisk(ctrl, item); + } + }; + + function updateNodeForDocument(e: vscode.TextDocument) { + if (e.uri.scheme !== 'file') { + return; + } + + if (!e.uri.path.endsWith('.md')) { + return; + } + + const { file, data } = getOrCreateFile(ctrl, e.uri); + data.updateFromContents(ctrl, e.getText(), file); + } + + for (const document of vscode.workspace.textDocuments) { + updateNodeForDocument(document); + } + + context.subscriptions.push( + vscode.workspace.onDidOpenTextDocument(updateNodeForDocument), + vscode.workspace.onDidChangeTextDocument(e => updateNodeForDocument(e.document)), + ); +} + +function getOrCreateFile(controller: vscode.TestController, uri: vscode.Uri) { + const existing = controller.items.get(uri.toString()); + if (existing) { + return { file: existing, data: testData.get(existing) as TestFile }; + } + + const file = controller.createTestItem(uri.toString(), uri.path.split('/').pop()!, uri); + controller.items.add(file); + + const data = new TestFile(); + testData.set(file, data); + + file.canResolveChildren = true; + return { file, data }; +} + +function gatherTestItems(collection: vscode.TestItemCollection) { + const items: vscode.TestItem[] = []; + collection.forEach(item => items.push(item)); + return items; +} + +function getWorkspaceTestPatterns() { + if (!vscode.workspace.workspaceFolders) { + return []; + } + + return vscode.workspace.workspaceFolders.map(workspaceFolder => ({ + workspaceFolder, + pattern: new vscode.RelativePattern(workspaceFolder, '**/*.md'), + })); +} + +async function findInitialFiles(controller: vscode.TestController, pattern: vscode.GlobPattern) { + for (const file of await vscode.workspace.findFiles(pattern)) { + getOrCreateFile(controller, file); + } +} + +function startWatchingWorkspace(controller: vscode.TestController) { + return getWorkspaceTestPatterns().map(({ workspaceFolder, pattern }) => { + const watcher = vscode.workspace.createFileSystemWatcher(pattern); + + watcher.onDidCreate(uri => getOrCreateFile(controller, uri)); + watcher.onDidChange(uri => { + const { file, data } = getOrCreateFile(controller, uri); + if (data.didResolve) { + data.updateFromDisk(controller, file); + } + }); + watcher.onDidDelete(uri => controller.items.delete(uri.toString())); + + findInitialFiles(controller, pattern); + + return watcher; + }); +} diff --git a/test-provider-sample/src/parser.ts b/test-provider-sample/src/parser.ts index c25b7820..3f769fba 100644 --- a/test-provider-sample/src/parser.ts +++ b/test-provider-sample/src/parser.ts @@ -1,29 +1,29 @@ -import * as vscode from 'vscode'; - -const testRe = /^([0-9]+)\s*([+*/-])\s*([0-9]+)\s*=\s*([0-9]+)/; -const headingRe = /^(#+)\s*(.+)$/; - -export const parseMarkdown = (text: string, events: { - onTest(range: vscode.Range, a: number, operator: string, b: number, expected: number): void; - onHeading(range: vscode.Range, name: string, depth: number): void; -}) => { - const lines = text.split('\n'); - - for (let lineNo = 0; lineNo < lines.length; lineNo++) { - const line = lines[lineNo]; - const test = testRe.exec(line); - if (test) { - const [, a, operator, b, expected] = test; - const range = new vscode.Range(new vscode.Position(lineNo, 0), new vscode.Position(lineNo, test[0].length)); - events.onTest(range, Number(a), operator, Number(b), Number(expected)); - continue; - } - - const heading = headingRe.exec(line); - if (heading) { - const [, pounds, name] = heading; - const range = new vscode.Range(new vscode.Position(lineNo, 0), new vscode.Position(lineNo, line.length)); - events.onHeading(range, name, pounds.length); - } - } -}; +import * as vscode from 'vscode'; + +const testRe = /^([0-9]+)\s*([+*/-])\s*([0-9]+)\s*=\s*([0-9]+)/; +const headingRe = /^(#+)\s*(.+)$/; + +export const parseMarkdown = (text: string, events: { + onTest(range: vscode.Range, a: number, operator: string, b: number, expected: number): void; + onHeading(range: vscode.Range, name: string, depth: number): void; +}) => { + const lines = text.split('\n'); + + for (let lineNo = 0; lineNo < lines.length; lineNo++) { + const line = lines[lineNo]; + const test = testRe.exec(line); + if (test) { + const [, a, operator, b, expected] = test; + const range = new vscode.Range(new vscode.Position(lineNo, 0), new vscode.Position(lineNo, test[0].length)); + events.onTest(range, Number(a), operator, Number(b), Number(expected)); + continue; + } + + const heading = headingRe.exec(line); + if (heading) { + const [, pounds, name] = heading; + const range = new vscode.Range(new vscode.Position(lineNo, 0), new vscode.Position(lineNo, line.length)); + events.onHeading(range, name, pounds.length); + } + } +}; diff --git a/test-provider-sample/src/testTree.ts b/test-provider-sample/src/testTree.ts index bd99d637..b1c11cd8 100644 --- a/test-provider-sample/src/testTree.ts +++ b/test-provider-sample/src/testTree.ts @@ -1,128 +1,128 @@ -import { TextDecoder } from 'util'; -import * as vscode from 'vscode'; -import { parseMarkdown } from './parser'; - -const textDecoder = new TextDecoder('utf-8'); - -export type MarkdownTestData = TestFile | TestHeading | TestCase; - -export const testData = new WeakMap(); - -let generationCounter = 0; - -export const getContentFromFilesystem = async (uri: vscode.Uri) => { - try { - const rawContent = await vscode.workspace.fs.readFile(uri); - return textDecoder.decode(rawContent); - } catch (e) { - console.warn(`Error providing tests for ${uri.fsPath}`, e); - return ''; - } -}; - -export class TestFile { - public didResolve = false; - - public async updateFromDisk(controller: vscode.TestController, item: vscode.TestItem) { - try { - const content = await getContentFromFilesystem(item.uri!); - item.error = undefined; - this.updateFromContents(controller, content, item); - } catch (e) { - item.error = (e as Error).stack; - } - } - - /** - * Parses the tests from the input text, and updates the tests contained - * by this file to be those from the text, - */ - public updateFromContents(controller: vscode.TestController, content: string, item: vscode.TestItem) { - const ancestors = [{ item, children: [] as vscode.TestItem[] }]; - const thisGeneration = generationCounter++; - this.didResolve = true; - - const ascend = (depth: number) => { - while (ancestors.length > depth) { - const finished = ancestors.pop()!; - finished.item.children.replace(finished.children); - } - }; - - parseMarkdown(content, { - onTest: (range, a, operator, b, expected) => { - const parent = ancestors[ancestors.length - 1]; - const data = new TestCase(a, operator as Operator, b, expected, thisGeneration); - const id = `${item.uri}/${data.getLabel()}`; - - - const tcase = controller.createTestItem(id, data.getLabel(), item.uri); - testData.set(tcase, data); - tcase.range = range; - parent.children.push(tcase); - }, - - onHeading: (range, name, depth) => { - ascend(depth); - const parent = ancestors[ancestors.length - 1]; - const id = `${item.uri}/${name}`; - - const thead = controller.createTestItem(id, name, item.uri); - thead.range = range; - testData.set(thead, new TestHeading(thisGeneration)); - parent.children.push(thead); - ancestors.push({ item: thead, children: [] }); - }, - }); - - ascend(0); // finish and assign children for all remaining items - } -} - -export class TestHeading { - constructor(public generation: number) { } -} - -type Operator = '+' | '-' | '*' | '/'; - -export class TestCase { - constructor( - private readonly a: number, - private readonly operator: Operator, - private readonly b: number, - private readonly expected: number, - public generation: number - ) { } - - getLabel() { - return `${this.a} ${this.operator} ${this.b} = ${this.expected}`; - } - - async run(item: vscode.TestItem, options: vscode.TestRun): Promise { - const start = Date.now(); - await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 1000)); - const actual = this.evaluate(); - const duration = Date.now() - start; - - if (actual === this.expected) { - options.passed(item, duration); - } else { - const message = vscode.TestMessage.diff(`Expected ${item.label}`, String(this.expected), String(actual)); - message.location = new vscode.Location(item.uri!, item.range!); - options.failed(item, message, duration); - } - } - - private evaluate() { - switch (this.operator) { - case '-': - return this.a - this.b; - case '+': - return this.a + this.b; - case '/': - return Math.floor(this.a / this.b); - case '*': - return this.a * this.b; - } - } -} +import { TextDecoder } from 'util'; +import * as vscode from 'vscode'; +import { parseMarkdown } from './parser'; + +const textDecoder = new TextDecoder('utf-8'); + +export type MarkdownTestData = TestFile | TestHeading | TestCase; + +export const testData = new WeakMap(); + +let generationCounter = 0; + +export const getContentFromFilesystem = async (uri: vscode.Uri) => { + try { + const rawContent = await vscode.workspace.fs.readFile(uri); + return textDecoder.decode(rawContent); + } catch (e) { + console.warn(`Error providing tests for ${uri.fsPath}`, e); + return ''; + } +}; + +export class TestFile { + public didResolve = false; + + public async updateFromDisk(controller: vscode.TestController, item: vscode.TestItem) { + try { + const content = await getContentFromFilesystem(item.uri!); + item.error = undefined; + this.updateFromContents(controller, content, item); + } catch (e) { + item.error = (e as Error).stack; + } + } + + /** + * Parses the tests from the input text, and updates the tests contained + * by this file to be those from the text, + */ + public updateFromContents(controller: vscode.TestController, content: string, item: vscode.TestItem) { + const ancestors = [{ item, children: [] as vscode.TestItem[] }]; + const thisGeneration = generationCounter++; + this.didResolve = true; + + const ascend = (depth: number) => { + while (ancestors.length > depth) { + const finished = ancestors.pop()!; + finished.item.children.replace(finished.children); + } + }; + + parseMarkdown(content, { + onTest: (range, a, operator, b, expected) => { + const parent = ancestors[ancestors.length - 1]; + const data = new TestCase(a, operator as Operator, b, expected, thisGeneration); + const id = `${item.uri}/${data.getLabel()}`; + + + const tcase = controller.createTestItem(id, data.getLabel(), item.uri); + testData.set(tcase, data); + tcase.range = range; + parent.children.push(tcase); + }, + + onHeading: (range, name, depth) => { + ascend(depth); + const parent = ancestors[ancestors.length - 1]; + const id = `${item.uri}/${name}`; + + const thead = controller.createTestItem(id, name, item.uri); + thead.range = range; + testData.set(thead, new TestHeading(thisGeneration)); + parent.children.push(thead); + ancestors.push({ item: thead, children: [] }); + }, + }); + + ascend(0); // finish and assign children for all remaining items + } +} + +export class TestHeading { + constructor(public generation: number) { } +} + +type Operator = '+' | '-' | '*' | '/'; + +export class TestCase { + constructor( + private readonly a: number, + private readonly operator: Operator, + private readonly b: number, + private readonly expected: number, + public generation: number + ) { } + + getLabel() { + return `${this.a} ${this.operator} ${this.b} = ${this.expected}`; + } + + async run(item: vscode.TestItem, options: vscode.TestRun): Promise { + const start = Date.now(); + await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 1000)); + const actual = this.evaluate(); + const duration = Date.now() - start; + + if (actual === this.expected) { + options.passed(item, duration); + } else { + const message = vscode.TestMessage.diff(`Expected ${item.label}`, String(this.expected), String(actual)); + message.location = new vscode.Location(item.uri!, item.range!); + options.failed(item, message, duration); + } + } + + private evaluate() { + switch (this.operator) { + case '-': + return this.a - this.b; + case '+': + return this.a + this.b; + case '/': + return Math.floor(this.a / this.b); + case '*': + return this.a * this.b; + } + } +} diff --git a/tree-view-sample/src/extension.ts b/tree-view-sample/src/extension.ts index f4bf9eaa..844b0ebf 100644 --- a/tree-view-sample/src/extension.ts +++ b/tree-view-sample/src/extension.ts @@ -1,54 +1,54 @@ -'use strict'; - -import * as vscode from 'vscode'; - -import { DepNodeProvider, Dependency } from './nodeDependencies'; -import { JsonOutlineProvider } from './jsonOutline'; -import { FtpExplorer } from './ftpExplorer'; -import { FileExplorer } from './fileExplorer'; -import { TestViewDragAndDrop } from './testViewDragAndDrop'; -import { TestView } from './testView'; - -export function activate(context: vscode.ExtensionContext) { - const rootPath = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) - ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined; - - // Samples of `window.registerTreeDataProvider` - const nodeDependenciesProvider = new DepNodeProvider(rootPath); - vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider); - vscode.commands.registerCommand('nodeDependencies.refreshEntry', () => nodeDependenciesProvider.refresh()); - vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`))); - vscode.commands.registerCommand('nodeDependencies.addEntry', () => vscode.window.showInformationMessage(`Successfully called add entry.`)); - vscode.commands.registerCommand('nodeDependencies.editEntry', (node: Dependency) => vscode.window.showInformationMessage(`Successfully called edit entry on ${node.label}.`)); - vscode.commands.registerCommand('nodeDependencies.deleteEntry', (node: Dependency) => vscode.window.showInformationMessage(`Successfully called delete entry on ${node.label}.`)); - - const jsonOutlineProvider = new JsonOutlineProvider(context); - vscode.window.registerTreeDataProvider('jsonOutline', jsonOutlineProvider); - vscode.commands.registerCommand('jsonOutline.refresh', () => jsonOutlineProvider.refresh()); - vscode.commands.registerCommand('jsonOutline.refreshNode', offset => jsonOutlineProvider.refresh(offset)); - vscode.commands.registerCommand('jsonOutline.renameNode', args => { - let offset = undefined; - if(args.selectedTreeItems && args.selectedTreeItems.length){ - offset = args.selectedTreeItems[0]; - }else if(typeof args === 'number'){ - offset = args; - } - if(offset){ - jsonOutlineProvider.rename(offset); - } - }); - vscode.commands.registerCommand('extension.openJsonSelection', range => jsonOutlineProvider.select(range)); - - // Samples of `window.createView` - new FtpExplorer(context); - new FileExplorer(context); - - // Test View - new TestView(context); - - // Drag and Drop proposed API sample - // This check is for older versions of VS Code that don't have the most up-to-date tree drag and drop API proposal. - if (typeof vscode.DataTransferItem === 'function') { - new TestViewDragAndDrop(context); - } +'use strict'; + +import * as vscode from 'vscode'; + +import { DepNodeProvider, Dependency } from './nodeDependencies'; +import { JsonOutlineProvider } from './jsonOutline'; +import { FtpExplorer } from './ftpExplorer'; +import { FileExplorer } from './fileExplorer'; +import { TestViewDragAndDrop } from './testViewDragAndDrop'; +import { TestView } from './testView'; + +export function activate(context: vscode.ExtensionContext) { + const rootPath = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) + ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined; + + // Samples of `window.registerTreeDataProvider` + const nodeDependenciesProvider = new DepNodeProvider(rootPath); + vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider); + vscode.commands.registerCommand('nodeDependencies.refreshEntry', () => nodeDependenciesProvider.refresh()); + vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`))); + vscode.commands.registerCommand('nodeDependencies.addEntry', () => vscode.window.showInformationMessage(`Successfully called add entry.`)); + vscode.commands.registerCommand('nodeDependencies.editEntry', (node: Dependency) => vscode.window.showInformationMessage(`Successfully called edit entry on ${node.label}.`)); + vscode.commands.registerCommand('nodeDependencies.deleteEntry', (node: Dependency) => vscode.window.showInformationMessage(`Successfully called delete entry on ${node.label}.`)); + + const jsonOutlineProvider = new JsonOutlineProvider(context); + vscode.window.registerTreeDataProvider('jsonOutline', jsonOutlineProvider); + vscode.commands.registerCommand('jsonOutline.refresh', () => jsonOutlineProvider.refresh()); + vscode.commands.registerCommand('jsonOutline.refreshNode', offset => jsonOutlineProvider.refresh(offset)); + vscode.commands.registerCommand('jsonOutline.renameNode', args => { + let offset = undefined; + if (args.selectedTreeItems && args.selectedTreeItems.length) { + offset = args.selectedTreeItems[0]; + } else if (typeof args === 'number') { + offset = args; + } + if (offset) { + jsonOutlineProvider.rename(offset); + } + }); + vscode.commands.registerCommand('extension.openJsonSelection', range => jsonOutlineProvider.select(range)); + + // Samples of `window.createView` + new FtpExplorer(context); + new FileExplorer(context); + + // Test View + new TestView(context); + + // Drag and Drop proposed API sample + // This check is for older versions of VS Code that don't have the most up-to-date tree drag and drop API proposal. + if (typeof vscode.DataTransferItem === 'function') { + new TestViewDragAndDrop(context); + } } \ No newline at end of file diff --git a/tree-view-sample/src/fileExplorer.ts b/tree-view-sample/src/fileExplorer.ts index 5779e2b1..adc4a29f 100644 --- a/tree-view-sample/src/fileExplorer.ts +++ b/tree-view-sample/src/fileExplorer.ts @@ -1,308 +1,308 @@ -import * as vscode from 'vscode'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as mkdirp from 'mkdirp'; -import * as rimraf from 'rimraf'; - -//#region Utilities - -namespace _ { - - function handleResult(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void { - if (error) { - reject(massageError(error)); - } else { - resolve(result); - } - } - - function massageError(error: Error & { code?: string }): Error { - if (error.code === 'ENOENT') { - return vscode.FileSystemError.FileNotFound(); - } - - if (error.code === 'EISDIR') { - return vscode.FileSystemError.FileIsADirectory(); - } - - if (error.code === 'EEXIST') { - return vscode.FileSystemError.FileExists(); - } - - if (error.code === 'EPERM' || error.code === 'EACCESS') { - return vscode.FileSystemError.NoPermissions(); - } - - return error; - } - - export function checkCancellation(token: vscode.CancellationToken): void { - if (token.isCancellationRequested) { - throw new Error('Operation cancelled'); - } - } - - export function normalizeNFC(items: string): string; - export function normalizeNFC(items: string[]): string[]; - export function normalizeNFC(items: string | string[]): string | string[] { - if (process.platform !== 'darwin') { - return items; - } - - if (Array.isArray(items)) { - return items.map(item => item.normalize('NFC')); - } - - return items.normalize('NFC'); - } - - export function readdir(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children))); - }); - } - - export function stat(path: string): Promise { - return new Promise((resolve, reject) => { - fs.stat(path, (error, stat) => handleResult(resolve, reject, error, stat)); - }); - } - - export function readfile(path: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer)); - }); - } - - export function writefile(path: string, content: Buffer): Promise { - return new Promise((resolve, reject) => { - fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function exists(path: string): Promise { - return new Promise((resolve, reject) => { - fs.exists(path, exists => handleResult(resolve, reject, null, exists)); - }); - } - - export function rmrf(path: string): Promise { - return new Promise((resolve, reject) => { - rimraf(path, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function mkdir(path: string): Promise { - return new Promise((resolve, reject) => { - mkdirp(path, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function rename(oldPath: string, newPath: string): Promise { - return new Promise((resolve, reject) => { - fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0)); - }); - } - - export function unlink(path: string): Promise { - return new Promise((resolve, reject) => { - fs.unlink(path, error => handleResult(resolve, reject, error, void 0)); - }); - } -} - -export class FileStat implements vscode.FileStat { - - constructor(private fsStat: fs.Stats) { } - - get type(): vscode.FileType { - return this.fsStat.isFile() ? vscode.FileType.File : this.fsStat.isDirectory() ? vscode.FileType.Directory : this.fsStat.isSymbolicLink() ? vscode.FileType.SymbolicLink : vscode.FileType.Unknown; - } - - get isFile(): boolean | undefined { - return this.fsStat.isFile(); - } - - get isDirectory(): boolean | undefined { - return this.fsStat.isDirectory(); - } - - get isSymbolicLink(): boolean | undefined { - return this.fsStat.isSymbolicLink(); - } - - get size(): number { - return this.fsStat.size; - } - - get ctime(): number { - return this.fsStat.ctime.getTime(); - } - - get mtime(): number { - return this.fsStat.mtime.getTime(); - } -} - -interface Entry { - uri: vscode.Uri; - type: vscode.FileType; -} - -//#endregion - -export class FileSystemProvider implements vscode.TreeDataProvider, vscode.FileSystemProvider { - - private _onDidChangeFile: vscode.EventEmitter; - - constructor() { - this._onDidChangeFile = new vscode.EventEmitter(); - } - - get onDidChangeFile(): vscode.Event { - return this._onDidChangeFile.event; - } - - watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable { - const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => { - const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString())); - - // TODO support excludes (using minimatch library?) - - this._onDidChangeFile.fire([{ - type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted, - uri: uri.with({ path: filepath }) - } as vscode.FileChangeEvent]); - }); - - return { dispose: () => watcher.close() }; - } - - stat(uri: vscode.Uri): vscode.FileStat | Thenable { - return this._stat(uri.fsPath); - } - - async _stat(path: string): Promise { - return new FileStat(await _.stat(path)); - } - - readDirectory(uri: vscode.Uri): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> { - return this._readDirectory(uri); - } - - async _readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { - const children = await _.readdir(uri.fsPath); - - const result: [string, vscode.FileType][] = []; - for (let i = 0; i < children.length; i++) { - const child = children[i]; - const stat = await this._stat(path.join(uri.fsPath, child)); - result.push([child, stat.type]); - } - - return Promise.resolve(result); - } - - createDirectory(uri: vscode.Uri): void | Thenable { - return _.mkdir(uri.fsPath); - } - - readFile(uri: vscode.Uri): Uint8Array | Thenable { - return _.readfile(uri.fsPath); - } - - writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Thenable { - return this._writeFile(uri, content, options); - } - - async _writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise { - const exists = await _.exists(uri.fsPath); - if (!exists) { - if (!options.create) { - throw vscode.FileSystemError.FileNotFound(); - } - - await _.mkdir(path.dirname(uri.fsPath)); - } else { - if (!options.overwrite) { - throw vscode.FileSystemError.FileExists(); - } - } - - return _.writefile(uri.fsPath, content as Buffer); - } - - delete(uri: vscode.Uri, options: { recursive: boolean; }): void | Thenable { - if (options.recursive) { - return _.rmrf(uri.fsPath); - } - - return _.unlink(uri.fsPath); - } - - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Thenable { - return this._rename(oldUri, newUri, options); - } - - async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise { - const exists = await _.exists(newUri.fsPath); - if (exists) { - if (!options.overwrite) { - throw vscode.FileSystemError.FileExists(); - } else { - await _.rmrf(newUri.fsPath); - } - } - - const parentExists = await _.exists(path.dirname(newUri.fsPath)); - if (!parentExists) { - await _.mkdir(path.dirname(newUri.fsPath)); - } - - return _.rename(oldUri.fsPath, newUri.fsPath); - } - - // tree data provider - - async getChildren(element?: Entry): Promise { - if (element) { - const children = await this.readDirectory(element.uri); - return children.map(([name, type]) => ({ uri: vscode.Uri.file(path.join(element.uri.fsPath, name)), type })); - } - - const workspaceFolder = (vscode.workspace.workspaceFolders ?? []).filter(folder => folder.uri.scheme === 'file')[0]; - if (workspaceFolder) { - const children = await this.readDirectory(workspaceFolder.uri); - children.sort((a, b) => { - if (a[1] === b[1]) { - return a[0].localeCompare(b[0]); - } - return a[1] === vscode.FileType.Directory ? -1 : 1; - }); - return children.map(([name, type]) => ({ uri: vscode.Uri.file(path.join(workspaceFolder.uri.fsPath, name)), type })); - } - - return []; - } - - getTreeItem(element: Entry): vscode.TreeItem { - const treeItem = new vscode.TreeItem(element.uri, element.type === vscode.FileType.Directory ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None); - if (element.type === vscode.FileType.File) { - treeItem.command = { command: 'fileExplorer.openFile', title: "Open File", arguments: [element.uri], }; - treeItem.contextValue = 'file'; - } - return treeItem; - } -} - -export class FileExplorer { - constructor(context: vscode.ExtensionContext) { - const treeDataProvider = new FileSystemProvider(); - context.subscriptions.push(vscode.window.createTreeView('fileExplorer', { treeDataProvider })); - vscode.commands.registerCommand('fileExplorer.openFile', (resource) => this.openResource(resource)); - } - - private openResource(resource: vscode.Uri): void { - vscode.window.showTextDocument(resource); - } +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as mkdirp from 'mkdirp'; +import * as rimraf from 'rimraf'; + +//#region Utilities + +namespace _ { + + function handleResult(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void { + if (error) { + reject(massageError(error)); + } else { + resolve(result); + } + } + + function massageError(error: Error & { code?: string }): Error { + if (error.code === 'ENOENT') { + return vscode.FileSystemError.FileNotFound(); + } + + if (error.code === 'EISDIR') { + return vscode.FileSystemError.FileIsADirectory(); + } + + if (error.code === 'EEXIST') { + return vscode.FileSystemError.FileExists(); + } + + if (error.code === 'EPERM' || error.code === 'EACCESS') { + return vscode.FileSystemError.NoPermissions(); + } + + return error; + } + + export function checkCancellation(token: vscode.CancellationToken): void { + if (token.isCancellationRequested) { + throw new Error('Operation cancelled'); + } + } + + export function normalizeNFC(items: string): string; + export function normalizeNFC(items: string[]): string[]; + export function normalizeNFC(items: string | string[]): string | string[] { + if (process.platform !== 'darwin') { + return items; + } + + if (Array.isArray(items)) { + return items.map(item => item.normalize('NFC')); + } + + return items.normalize('NFC'); + } + + export function readdir(path: string): Promise { + return new Promise((resolve, reject) => { + fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children))); + }); + } + + export function stat(path: string): Promise { + return new Promise((resolve, reject) => { + fs.stat(path, (error, stat) => handleResult(resolve, reject, error, stat)); + }); + } + + export function readfile(path: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer)); + }); + } + + export function writefile(path: string, content: Buffer): Promise { + return new Promise((resolve, reject) => { + fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function exists(path: string): Promise { + return new Promise((resolve, reject) => { + fs.exists(path, exists => handleResult(resolve, reject, null, exists)); + }); + } + + export function rmrf(path: string): Promise { + return new Promise((resolve, reject) => { + rimraf(path, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function mkdir(path: string): Promise { + return new Promise((resolve, reject) => { + mkdirp(path, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function rename(oldPath: string, newPath: string): Promise { + return new Promise((resolve, reject) => { + fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0)); + }); + } + + export function unlink(path: string): Promise { + return new Promise((resolve, reject) => { + fs.unlink(path, error => handleResult(resolve, reject, error, void 0)); + }); + } +} + +export class FileStat implements vscode.FileStat { + + constructor(private fsStat: fs.Stats) { } + + get type(): vscode.FileType { + return this.fsStat.isFile() ? vscode.FileType.File : this.fsStat.isDirectory() ? vscode.FileType.Directory : this.fsStat.isSymbolicLink() ? vscode.FileType.SymbolicLink : vscode.FileType.Unknown; + } + + get isFile(): boolean | undefined { + return this.fsStat.isFile(); + } + + get isDirectory(): boolean | undefined { + return this.fsStat.isDirectory(); + } + + get isSymbolicLink(): boolean | undefined { + return this.fsStat.isSymbolicLink(); + } + + get size(): number { + return this.fsStat.size; + } + + get ctime(): number { + return this.fsStat.ctime.getTime(); + } + + get mtime(): number { + return this.fsStat.mtime.getTime(); + } +} + +interface Entry { + uri: vscode.Uri; + type: vscode.FileType; +} + +//#endregion + +export class FileSystemProvider implements vscode.TreeDataProvider, vscode.FileSystemProvider { + + private _onDidChangeFile: vscode.EventEmitter; + + constructor() { + this._onDidChangeFile = new vscode.EventEmitter(); + } + + get onDidChangeFile(): vscode.Event { + return this._onDidChangeFile.event; + } + + watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable { + const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => { + const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString())); + + // TODO support excludes (using minimatch library?) + + this._onDidChangeFile.fire([{ + type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted, + uri: uri.with({ path: filepath }) + } as vscode.FileChangeEvent]); + }); + + return { dispose: () => watcher.close() }; + } + + stat(uri: vscode.Uri): vscode.FileStat | Thenable { + return this._stat(uri.fsPath); + } + + async _stat(path: string): Promise { + return new FileStat(await _.stat(path)); + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> { + return this._readDirectory(uri); + } + + async _readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { + const children = await _.readdir(uri.fsPath); + + const result: [string, vscode.FileType][] = []; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const stat = await this._stat(path.join(uri.fsPath, child)); + result.push([child, stat.type]); + } + + return Promise.resolve(result); + } + + createDirectory(uri: vscode.Uri): void | Thenable { + return _.mkdir(uri.fsPath); + } + + readFile(uri: vscode.Uri): Uint8Array | Thenable { + return _.readfile(uri.fsPath); + } + + writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Thenable { + return this._writeFile(uri, content, options); + } + + async _writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise { + const exists = await _.exists(uri.fsPath); + if (!exists) { + if (!options.create) { + throw vscode.FileSystemError.FileNotFound(); + } + + await _.mkdir(path.dirname(uri.fsPath)); + } else { + if (!options.overwrite) { + throw vscode.FileSystemError.FileExists(); + } + } + + return _.writefile(uri.fsPath, content as Buffer); + } + + delete(uri: vscode.Uri, options: { recursive: boolean; }): void | Thenable { + if (options.recursive) { + return _.rmrf(uri.fsPath); + } + + return _.unlink(uri.fsPath); + } + + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Thenable { + return this._rename(oldUri, newUri, options); + } + + async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise { + const exists = await _.exists(newUri.fsPath); + if (exists) { + if (!options.overwrite) { + throw vscode.FileSystemError.FileExists(); + } else { + await _.rmrf(newUri.fsPath); + } + } + + const parentExists = await _.exists(path.dirname(newUri.fsPath)); + if (!parentExists) { + await _.mkdir(path.dirname(newUri.fsPath)); + } + + return _.rename(oldUri.fsPath, newUri.fsPath); + } + + // tree data provider + + async getChildren(element?: Entry): Promise { + if (element) { + const children = await this.readDirectory(element.uri); + return children.map(([name, type]) => ({ uri: vscode.Uri.file(path.join(element.uri.fsPath, name)), type })); + } + + const workspaceFolder = (vscode.workspace.workspaceFolders ?? []).filter(folder => folder.uri.scheme === 'file')[0]; + if (workspaceFolder) { + const children = await this.readDirectory(workspaceFolder.uri); + children.sort((a, b) => { + if (a[1] === b[1]) { + return a[0].localeCompare(b[0]); + } + return a[1] === vscode.FileType.Directory ? -1 : 1; + }); + return children.map(([name, type]) => ({ uri: vscode.Uri.file(path.join(workspaceFolder.uri.fsPath, name)), type })); + } + + return []; + } + + getTreeItem(element: Entry): vscode.TreeItem { + const treeItem = new vscode.TreeItem(element.uri, element.type === vscode.FileType.Directory ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None); + if (element.type === vscode.FileType.File) { + treeItem.command = { command: 'fileExplorer.openFile', title: "Open File", arguments: [element.uri], }; + treeItem.contextValue = 'file'; + } + return treeItem; + } +} + +export class FileExplorer { + constructor(context: vscode.ExtensionContext) { + const treeDataProvider = new FileSystemProvider(); + context.subscriptions.push(vscode.window.createTreeView('fileExplorer', { treeDataProvider })); + vscode.commands.registerCommand('fileExplorer.openFile', (resource) => this.openResource(resource)); + } + + private openResource(resource: vscode.Uri): void { + vscode.window.showTextDocument(resource); + } } \ No newline at end of file diff --git a/tree-view-sample/src/ftpExplorer.ts b/tree-view-sample/src/ftpExplorer.ts index 5668a694..dbcf1ef2 100644 --- a/tree-view-sample/src/ftpExplorer.ts +++ b/tree-view-sample/src/ftpExplorer.ts @@ -1,186 +1,186 @@ -import * as vscode from 'vscode'; -import * as Client from 'ftp'; -import { basename, dirname, join } from 'path'; - -interface IEntry { - name: string; - type: string; -} - -export interface FtpNode { - - resource: vscode.Uri; - isDirectory: boolean; - -} - -export class FtpModel { - constructor(readonly host: string, private user: string, private password: string) { - } - - public connect(): Thenable { - return new Promise((c, e) => { - const client = new Client(); - client.on('ready', () => { - c(client); - }); - - client.on('error', error => { - e('Error while connecting: ' + error.message); - }); - - client.connect({ - host: this.host, - user: this.user, - password: this.password - }); - }); - } - - public get roots(): Thenable { - return this.connect().then(client => { - return new Promise((c, e) => { - client.list((err, list) => { - if (err) { - return e(err); - } - - client.end(); - - return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`ftp://${this.host}///${entry.name}`), isDirectory: entry.type === 'd' })))); - }); - }); - }); - } - - public getChildren(node: FtpNode): Thenable { - return this.connect().then(client => { - return new Promise((c, e) => { - client.list(node.resource.fsPath, (err, list) => { - if (err) { - return e(err); - } - - client.end(); - - return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`${node.resource.fsPath}/${entry.name}`), isDirectory: entry.type === 'd' })))); - }); - }); - }); - } - - private sort(nodes: FtpNode[]): FtpNode[] { - return nodes.sort((n1, n2) => { - if (n1.isDirectory && !n2.isDirectory) { - return -1; - } - - if (!n1.isDirectory && n2.isDirectory) { - return 1; - } - - return basename(n1.resource.fsPath).localeCompare(basename(n2.resource.fsPath)); - }); - } - - public getContent(resource: vscode.Uri): Thenable { - return this.connect().then(client => { - return new Promise((c, e) => { - client.get(resource.path.substr(2), (err, stream) => { - if (err) { - return e(err); - } - - let string = ''; - stream.on('data', function (buffer) { - if (buffer) { - const part = buffer.toString(); - string += part; - } - }); - - stream.on('end', function () { - client.end(); - c(string); - }); - }); - }); - }); - } -} - -export class FtpTreeDataProvider implements vscode.TreeDataProvider, vscode.TextDocumentContentProvider { - - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - - constructor(private readonly model: FtpModel) { } - - public refresh(): any { - this._onDidChangeTreeData.fire(undefined); - } - - - public getTreeItem(element: FtpNode): vscode.TreeItem { - return { - resourceUri: element.resource, - collapsibleState: element.isDirectory ? vscode.TreeItemCollapsibleState.Collapsed : void 0, - command: element.isDirectory ? void 0 : { - command: 'ftpExplorer.openFtpResource', - arguments: [element.resource], - title: 'Open FTP Resource' - } - }; - } - - public getChildren(element?: FtpNode): FtpNode[] | Thenable { - return element ? this.model.getChildren(element) : this.model.roots; - } - - public getParent(element: FtpNode): FtpNode | undefined{ - const parent = element.resource.with({ path: dirname(element.resource.path) }); - return parent.path !== '//' ? { resource: parent, isDirectory: true } : undefined; - } - - public provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult { - return this.model.getContent(uri).then(content => content); - } -} - -export class FtpExplorer { - - private ftpViewer: vscode.TreeView; - - constructor(context: vscode.ExtensionContext) { - /* Please note that login information is hardcoded only for this example purpose and recommended not to do it in general. */ - const ftpModel = new FtpModel('mirror.switch.ch', 'anonymous', 'anonymous@anonymous.de'); - const treeDataProvider = new FtpTreeDataProvider(ftpModel); - context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('ftp', treeDataProvider)); - - this.ftpViewer = vscode.window.createTreeView('ftpExplorer', { treeDataProvider }); - - vscode.commands.registerCommand('ftpExplorer.refresh', () => treeDataProvider.refresh()); - vscode.commands.registerCommand('ftpExplorer.openFtpResource', resource => this.openResource(resource)); - vscode.commands.registerCommand('ftpExplorer.revealResource', () => this.reveal()); - } - - private openResource(resource: vscode.Uri): void { - vscode.window.showTextDocument(resource); - } - - private async reveal(): Promise { - const node = this.getNode(); - if (node) { - return this.ftpViewer.reveal(node); - } - } - - private getNode(): FtpNode | undefined { - if (vscode.window.activeTextEditor) { - if (vscode.window.activeTextEditor.document.uri.scheme === 'ftp') { - return { resource: vscode.window.activeTextEditor.document.uri, isDirectory: false }; - } - } - return undefined; - } +import * as vscode from 'vscode'; +import * as Client from 'ftp'; +import { basename, dirname, join } from 'path'; + +interface IEntry { + name: string; + type: string; +} + +export interface FtpNode { + + resource: vscode.Uri; + isDirectory: boolean; + +} + +export class FtpModel { + constructor(readonly host: string, private user: string, private password: string) { + } + + public connect(): Thenable { + return new Promise((c, e) => { + const client = new Client(); + client.on('ready', () => { + c(client); + }); + + client.on('error', error => { + e('Error while connecting: ' + error.message); + }); + + client.connect({ + host: this.host, + user: this.user, + password: this.password + }); + }); + } + + public get roots(): Thenable { + return this.connect().then(client => { + return new Promise((c, e) => { + client.list((err, list) => { + if (err) { + return e(err); + } + + client.end(); + + return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`ftp://${this.host}///${entry.name}`), isDirectory: entry.type === 'd' })))); + }); + }); + }); + } + + public getChildren(node: FtpNode): Thenable { + return this.connect().then(client => { + return new Promise((c, e) => { + client.list(node.resource.fsPath, (err, list) => { + if (err) { + return e(err); + } + + client.end(); + + return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`${node.resource.fsPath}/${entry.name}`), isDirectory: entry.type === 'd' })))); + }); + }); + }); + } + + private sort(nodes: FtpNode[]): FtpNode[] { + return nodes.sort((n1, n2) => { + if (n1.isDirectory && !n2.isDirectory) { + return -1; + } + + if (!n1.isDirectory && n2.isDirectory) { + return 1; + } + + return basename(n1.resource.fsPath).localeCompare(basename(n2.resource.fsPath)); + }); + } + + public getContent(resource: vscode.Uri): Thenable { + return this.connect().then(client => { + return new Promise((c, e) => { + client.get(resource.path.substr(2), (err, stream) => { + if (err) { + return e(err); + } + + let string = ''; + stream.on('data', function(buffer) { + if (buffer) { + const part = buffer.toString(); + string += part; + } + }); + + stream.on('end', function() { + client.end(); + c(string); + }); + }); + }); + }); + } +} + +export class FtpTreeDataProvider implements vscode.TreeDataProvider, vscode.TextDocumentContentProvider { + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + constructor(private readonly model: FtpModel) { } + + public refresh(): any { + this._onDidChangeTreeData.fire(undefined); + } + + + public getTreeItem(element: FtpNode): vscode.TreeItem { + return { + resourceUri: element.resource, + collapsibleState: element.isDirectory ? vscode.TreeItemCollapsibleState.Collapsed : void 0, + command: element.isDirectory ? void 0 : { + command: 'ftpExplorer.openFtpResource', + arguments: [element.resource], + title: 'Open FTP Resource' + } + }; + } + + public getChildren(element?: FtpNode): FtpNode[] | Thenable { + return element ? this.model.getChildren(element) : this.model.roots; + } + + public getParent(element: FtpNode): FtpNode | undefined { + const parent = element.resource.with({ path: dirname(element.resource.path) }); + return parent.path !== '//' ? { resource: parent, isDirectory: true } : undefined; + } + + public provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult { + return this.model.getContent(uri).then(content => content); + } +} + +export class FtpExplorer { + + private ftpViewer: vscode.TreeView; + + constructor(context: vscode.ExtensionContext) { + /* Please note that login information is hardcoded only for this example purpose and recommended not to do it in general. */ + const ftpModel = new FtpModel('mirror.switch.ch', 'anonymous', 'anonymous@anonymous.de'); + const treeDataProvider = new FtpTreeDataProvider(ftpModel); + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('ftp', treeDataProvider)); + + this.ftpViewer = vscode.window.createTreeView('ftpExplorer', { treeDataProvider }); + + vscode.commands.registerCommand('ftpExplorer.refresh', () => treeDataProvider.refresh()); + vscode.commands.registerCommand('ftpExplorer.openFtpResource', resource => this.openResource(resource)); + vscode.commands.registerCommand('ftpExplorer.revealResource', () => this.reveal()); + } + + private openResource(resource: vscode.Uri): void { + vscode.window.showTextDocument(resource); + } + + private async reveal(): Promise { + const node = this.getNode(); + if (node) { + return this.ftpViewer.reveal(node); + } + } + + private getNode(): FtpNode | undefined { + if (vscode.window.activeTextEditor) { + if (vscode.window.activeTextEditor.document.uri.scheme === 'ftp') { + return { resource: vscode.window.activeTextEditor.document.uri, isDirectory: false }; + } + } + return undefined; + } } \ No newline at end of file diff --git a/tree-view-sample/src/jsftp.d.ts b/tree-view-sample/src/jsftp.d.ts index a2db1146..9f970009 100644 --- a/tree-view-sample/src/jsftp.d.ts +++ b/tree-view-sample/src/jsftp.d.ts @@ -1,46 +1,46 @@ - - -import { Readable } from 'stream'; -import { EventEmitter } from 'events'; - -declare namespace JSFtp { - - interface JSFtpOptions { - host: string; - port?: number | 21; - user?: string | 'anonymous'; - pass?: string | '@anonymous'; - useList?: boolean - } - - interface Callback { - (err: any, result: T): void; - } - - interface Entry { - name: string; - size: number; - time: number; - type: 0 | 1; - } -} - -interface JSFtp extends EventEmitter { - auth(user: string, password: string, callback: JSFtp.Callback): void - keepAlive(wait?: number): void; - ls(path: string, callback: JSFtp.Callback): void; - list(path: string, callback: JSFtp.Callback): void; - put(buffer: Buffer, path: string, callback: JSFtp.Callback): void; - get(path: string, callback: JSFtp.Callback): void; - setType(type: 'A' | 'AN' | 'AT' | 'AC' | 'E' | 'I' | 'L', callback: JSFtp.Callback): void; - raw(command: string, args: any[], callback: JSFtp.Callback): void; - raw(command: string, args: any[], callback: JSFtp.Callback): void; -} - -interface JSFtpConstructor { - new(options: JSFtp.JSFtpOptions): JSFtp; -} - -declare const JSFtp: JSFtpConstructor; - -export = JSFtp; + + +import { Readable } from 'stream'; +import { EventEmitter } from 'events'; + +declare namespace JSFtp { + + interface JSFtpOptions { + host: string; + port?: number | 21; + user?: string | 'anonymous'; + pass?: string | '@anonymous'; + useList?: boolean + } + + interface Callback { + (err: any, result: T): void; + } + + interface Entry { + name: string; + size: number; + time: number; + type: 0 | 1; + } +} + +interface JSFtp extends EventEmitter { + auth(user: string, password: string, callback: JSFtp.Callback): void + keepAlive(wait?: number): void; + ls(path: string, callback: JSFtp.Callback): void; + list(path: string, callback: JSFtp.Callback): void; + put(buffer: Buffer, path: string, callback: JSFtp.Callback): void; + get(path: string, callback: JSFtp.Callback): void; + setType(type: 'A' | 'AN' | 'AT' | 'AC' | 'E' | 'I' | 'L', callback: JSFtp.Callback): void; + raw(command: string, args: any[], callback: JSFtp.Callback): void; + raw(command: string, args: any[], callback: JSFtp.Callback): void; +} + +interface JSFtpConstructor { + new(options: JSFtp.JSFtpOptions): JSFtp; +} + +declare const JSFtp: JSFtpConstructor; + +export = JSFtp; diff --git a/tree-view-sample/src/jsonOutline.ts b/tree-view-sample/src/jsonOutline.ts index cb93186e..8522088b 100644 --- a/tree-view-sample/src/jsonOutline.ts +++ b/tree-view-sample/src/jsonOutline.ts @@ -1,197 +1,197 @@ -import * as vscode from 'vscode'; -import * as json from 'jsonc-parser'; -import * as path from 'path'; - -export class JsonOutlineProvider implements vscode.TreeDataProvider { - - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - - private tree: json.Node | undefined; - private text = ''; - private editor: vscode.TextEditor | undefined; - private autoRefresh = true; - - constructor(private context: vscode.ExtensionContext) { - vscode.window.onDidChangeActiveTextEditor(() => this.onActiveEditorChanged()); - vscode.workspace.onDidChangeTextDocument(e => this.onDocumentChanged(e)); - this.autoRefresh = vscode.workspace.getConfiguration('jsonOutline').get('autorefresh', false); - vscode.workspace.onDidChangeConfiguration(() => { - this.autoRefresh = vscode.workspace.getConfiguration('jsonOutline').get('autorefresh', false); - }); - this.onActiveEditorChanged(); - } - - refresh(offset?: number): void { - this.parseTree(); - if (offset) { - this._onDidChangeTreeData.fire(offset); - } else { - this._onDidChangeTreeData.fire(undefined); - } - } - - rename(offset: number): void { - vscode.window.showInputBox({ placeHolder: 'Enter the new label' }).then(value => { - const editor = this.editor; - const tree = this.tree; - if (value !== null && value !== undefined && editor && tree) { - editor.edit(editBuilder => { - const path = json.getLocation(this.text, offset).path; - let propertyNode: json.Node | undefined = json.findNodeAtLocation(tree, path); - if (propertyNode.parent?.type !== 'array') { - propertyNode = propertyNode.parent?.children ? propertyNode.parent.children[0] : undefined; - } - if (propertyNode) { - const range = new vscode.Range(editor.document.positionAt(propertyNode.offset), editor.document.positionAt(propertyNode.offset + propertyNode.length)); - editBuilder.replace(range, `"${value}"`); - setTimeout(() => { - this.parseTree(); - this.refresh(offset); - }, 100); - } - }); - } - }); - } - - private onActiveEditorChanged(): void { - if (vscode.window.activeTextEditor) { - if (vscode.window.activeTextEditor.document.uri.scheme === 'file') { - const enabled = vscode.window.activeTextEditor.document.languageId === 'json' || vscode.window.activeTextEditor.document.languageId === 'jsonc'; - vscode.commands.executeCommand('setContext', 'jsonOutlineEnabled', enabled); - if (enabled) { - this.refresh(); - } - } - } else { - vscode.commands.executeCommand('setContext', 'jsonOutlineEnabled', false); - } - } - - private onDocumentChanged(changeEvent: vscode.TextDocumentChangeEvent): void { - if (this.tree && this.autoRefresh && changeEvent.document.uri.toString() === this.editor?.document.uri.toString()) { - for (const change of changeEvent.contentChanges) { - const path = json.getLocation(this.text, this.editor.document.offsetAt(change.range.start)).path; - path.pop(); - const node = path.length ? json.findNodeAtLocation(this.tree, path) : void 0; - this.parseTree(); - this._onDidChangeTreeData.fire(node ? node.offset : void 0); - } - } - } - - private parseTree(): void { - this.text = ''; - this.tree = undefined; - this.editor = vscode.window.activeTextEditor; - if (this.editor && this.editor.document) { - this.text = this.editor.document.getText(); - this.tree = json.parseTree(this.text); - } - } - - getChildren(offset?: number): Thenable { - if (offset && this.tree) { - const path = json.getLocation(this.text, offset).path; - const node = json.findNodeAtLocation(this.tree, path); - return Promise.resolve(this.getChildrenOffsets(node)); - } else { - return Promise.resolve(this.tree ? this.getChildrenOffsets(this.tree) : []); - } - } - - private getChildrenOffsets(node: json.Node): number[] { - const offsets: number[] = []; - if (node.children && this.tree) { - for (const child of node.children) { - const childPath = json.getLocation(this.text, child.offset).path; - const childNode = json.findNodeAtLocation(this.tree, childPath); - if (childNode) { - offsets.push(childNode.offset); - } - } - } - return offsets; - } - - getTreeItem(offset: number): vscode.TreeItem { - if (!this.tree) { - throw new Error('Invalid tree'); - } - if (!this.editor) { - throw new Error('Invalid editor'); - } - - const path = json.getLocation(this.text, offset).path; - const valueNode = json.findNodeAtLocation(this.tree, path); - if (valueNode) { - const hasChildren = valueNode.type === 'object' || valueNode.type === 'array'; - const treeItem: vscode.TreeItem = new vscode.TreeItem(this.getLabel(valueNode), hasChildren ? valueNode.type === 'object' ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None); - treeItem.command = { - command: 'extension.openJsonSelection', - title: '', - arguments: [new vscode.Range(this.editor.document.positionAt(valueNode.offset), this.editor.document.positionAt(valueNode.offset + valueNode.length))] - }; - treeItem.iconPath = this.getIcon(valueNode); - treeItem.contextValue = valueNode.type; - return treeItem; - } - throw (new Error(`Could not find json node at ${path}`)); - } - - select(range: vscode.Range) { - if (this.editor) { - this.editor.selection = new vscode.Selection(range.start, range.end); - } - } - - private getIcon(node: json.Node): any { - const nodeType = node.type; - if (nodeType === 'boolean') { - return { - light: this.context.asAbsolutePath(path.join('resources', 'light', 'boolean.svg')), - dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'boolean.svg')) - }; - } - if (nodeType === 'string') { - return { - light: this.context.asAbsolutePath(path.join('resources', 'light', 'string.svg')), - dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'string.svg')) - }; - } - if (nodeType === 'number') { - return { - light: this.context.asAbsolutePath(path.join('resources', 'light', 'number.svg')), - dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'number.svg')) - }; - } - return null; - } - - private getLabel(node: json.Node): string { - if (node.parent?.type === 'array') { - const prefix = node.parent.children?.indexOf(node).toString(); - if (node.type === 'object') { - return prefix + ':{ }'; - } - if (node.type === 'array') { - return prefix + ':[ ]'; - } - return prefix + ':' + node.value.toString(); - } - else { - const property = node.parent?.children ? node.parent.children[0].value.toString() : ''; - if (node.type === 'array' || node.type === 'object') { - if (node.type === 'object') { - return '{ } ' + property; - } - if (node.type === 'array') { - return '[ ] ' + property; - } - } - const value = this.editor?.document.getText(new vscode.Range(this.editor.document.positionAt(node.offset), this.editor.document.positionAt(node.offset + node.length))); - return `${property}: ${value}`; - } - } -} +import * as vscode from 'vscode'; +import * as json from 'jsonc-parser'; +import * as path from 'path'; + +export class JsonOutlineProvider implements vscode.TreeDataProvider { + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + private tree: json.Node | undefined; + private text = ''; + private editor: vscode.TextEditor | undefined; + private autoRefresh = true; + + constructor(private context: vscode.ExtensionContext) { + vscode.window.onDidChangeActiveTextEditor(() => this.onActiveEditorChanged()); + vscode.workspace.onDidChangeTextDocument(e => this.onDocumentChanged(e)); + this.autoRefresh = vscode.workspace.getConfiguration('jsonOutline').get('autorefresh', false); + vscode.workspace.onDidChangeConfiguration(() => { + this.autoRefresh = vscode.workspace.getConfiguration('jsonOutline').get('autorefresh', false); + }); + this.onActiveEditorChanged(); + } + + refresh(offset?: number): void { + this.parseTree(); + if (offset) { + this._onDidChangeTreeData.fire(offset); + } else { + this._onDidChangeTreeData.fire(undefined); + } + } + + rename(offset: number): void { + vscode.window.showInputBox({ placeHolder: 'Enter the new label' }).then(value => { + const editor = this.editor; + const tree = this.tree; + if (value !== null && value !== undefined && editor && tree) { + editor.edit(editBuilder => { + const path = json.getLocation(this.text, offset).path; + let propertyNode: json.Node | undefined = json.findNodeAtLocation(tree, path); + if (propertyNode.parent?.type !== 'array') { + propertyNode = propertyNode.parent?.children ? propertyNode.parent.children[0] : undefined; + } + if (propertyNode) { + const range = new vscode.Range(editor.document.positionAt(propertyNode.offset), editor.document.positionAt(propertyNode.offset + propertyNode.length)); + editBuilder.replace(range, `"${value}"`); + setTimeout(() => { + this.parseTree(); + this.refresh(offset); + }, 100); + } + }); + } + }); + } + + private onActiveEditorChanged(): void { + if (vscode.window.activeTextEditor) { + if (vscode.window.activeTextEditor.document.uri.scheme === 'file') { + const enabled = vscode.window.activeTextEditor.document.languageId === 'json' || vscode.window.activeTextEditor.document.languageId === 'jsonc'; + vscode.commands.executeCommand('setContext', 'jsonOutlineEnabled', enabled); + if (enabled) { + this.refresh(); + } + } + } else { + vscode.commands.executeCommand('setContext', 'jsonOutlineEnabled', false); + } + } + + private onDocumentChanged(changeEvent: vscode.TextDocumentChangeEvent): void { + if (this.tree && this.autoRefresh && changeEvent.document.uri.toString() === this.editor?.document.uri.toString()) { + for (const change of changeEvent.contentChanges) { + const path = json.getLocation(this.text, this.editor.document.offsetAt(change.range.start)).path; + path.pop(); + const node = path.length ? json.findNodeAtLocation(this.tree, path) : void 0; + this.parseTree(); + this._onDidChangeTreeData.fire(node ? node.offset : void 0); + } + } + } + + private parseTree(): void { + this.text = ''; + this.tree = undefined; + this.editor = vscode.window.activeTextEditor; + if (this.editor && this.editor.document) { + this.text = this.editor.document.getText(); + this.tree = json.parseTree(this.text); + } + } + + getChildren(offset?: number): Thenable { + if (offset && this.tree) { + const path = json.getLocation(this.text, offset).path; + const node = json.findNodeAtLocation(this.tree, path); + return Promise.resolve(this.getChildrenOffsets(node)); + } else { + return Promise.resolve(this.tree ? this.getChildrenOffsets(this.tree) : []); + } + } + + private getChildrenOffsets(node: json.Node): number[] { + const offsets: number[] = []; + if (node.children && this.tree) { + for (const child of node.children) { + const childPath = json.getLocation(this.text, child.offset).path; + const childNode = json.findNodeAtLocation(this.tree, childPath); + if (childNode) { + offsets.push(childNode.offset); + } + } + } + return offsets; + } + + getTreeItem(offset: number): vscode.TreeItem { + if (!this.tree) { + throw new Error('Invalid tree'); + } + if (!this.editor) { + throw new Error('Invalid editor'); + } + + const path = json.getLocation(this.text, offset).path; + const valueNode = json.findNodeAtLocation(this.tree, path); + if (valueNode) { + const hasChildren = valueNode.type === 'object' || valueNode.type === 'array'; + const treeItem: vscode.TreeItem = new vscode.TreeItem(this.getLabel(valueNode), hasChildren ? valueNode.type === 'object' ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None); + treeItem.command = { + command: 'extension.openJsonSelection', + title: '', + arguments: [new vscode.Range(this.editor.document.positionAt(valueNode.offset), this.editor.document.positionAt(valueNode.offset + valueNode.length))] + }; + treeItem.iconPath = this.getIcon(valueNode); + treeItem.contextValue = valueNode.type; + return treeItem; + } + throw (new Error(`Could not find json node at ${path}`)); + } + + select(range: vscode.Range) { + if (this.editor) { + this.editor.selection = new vscode.Selection(range.start, range.end); + } + } + + private getIcon(node: json.Node): any { + const nodeType = node.type; + if (nodeType === 'boolean') { + return { + light: this.context.asAbsolutePath(path.join('resources', 'light', 'boolean.svg')), + dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'boolean.svg')) + }; + } + if (nodeType === 'string') { + return { + light: this.context.asAbsolutePath(path.join('resources', 'light', 'string.svg')), + dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'string.svg')) + }; + } + if (nodeType === 'number') { + return { + light: this.context.asAbsolutePath(path.join('resources', 'light', 'number.svg')), + dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'number.svg')) + }; + } + return null; + } + + private getLabel(node: json.Node): string { + if (node.parent?.type === 'array') { + const prefix = node.parent.children?.indexOf(node).toString(); + if (node.type === 'object') { + return prefix + ':{ }'; + } + if (node.type === 'array') { + return prefix + ':[ ]'; + } + return prefix + ':' + node.value.toString(); + } + else { + const property = node.parent?.children ? node.parent.children[0].value.toString() : ''; + if (node.type === 'array' || node.type === 'object') { + if (node.type === 'object') { + return '{ } ' + property; + } + if (node.type === 'array') { + return '[ ] ' + property; + } + } + const value = this.editor?.document.getText(new vscode.Range(this.editor.document.positionAt(node.offset), this.editor.document.positionAt(node.offset + node.length))); + return `${property}: ${value}`; + } + } +} diff --git a/tree-view-sample/src/nodeDependencies.ts b/tree-view-sample/src/nodeDependencies.ts index 2adc0bc9..9da3b722 100644 --- a/tree-view-sample/src/nodeDependencies.ts +++ b/tree-view-sample/src/nodeDependencies.ts @@ -1,104 +1,104 @@ -import * as vscode from 'vscode'; -import * as fs from 'fs'; -import * as path from 'path'; - -export class DepNodeProvider implements vscode.TreeDataProvider { - - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - - constructor(private workspaceRoot: string | undefined) { - } - - refresh(): void { - this._onDidChangeTreeData.fire(); - } - - getTreeItem(element: Dependency): vscode.TreeItem { - return element; - } - - getChildren(element?: Dependency): Thenable { - if (!this.workspaceRoot) { - vscode.window.showInformationMessage('No dependency in empty workspace'); - return Promise.resolve([]); - } - - if (element) { - return Promise.resolve(this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json'))); - } else { - const packageJsonPath = path.join(this.workspaceRoot, 'package.json'); - if (this.pathExists(packageJsonPath)) { - return Promise.resolve(this.getDepsInPackageJson(packageJsonPath)); - } else { - vscode.window.showInformationMessage('Workspace has no package.json'); - return Promise.resolve([]); - } - } - - } - - /** - * Given the path to package.json, read all its dependencies and devDependencies. - */ - private getDepsInPackageJson(packageJsonPath: string): Dependency[] { - const workspaceRoot = this.workspaceRoot; - if (this.pathExists(packageJsonPath) && workspaceRoot) { - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - - const toDep = (moduleName: string, version: string): Dependency => { - if (this.pathExists(path.join(workspaceRoot, 'node_modules', moduleName))) { - return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.Collapsed); - } else { - return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.None, { - command: 'extension.openPackageOnNpm', - title: '', - arguments: [moduleName] - }); - } - }; - - const deps = packageJson.dependencies - ? Object.keys(packageJson.dependencies).map(dep => toDep(dep, packageJson.dependencies[dep])) - : []; - const devDeps = packageJson.devDependencies - ? Object.keys(packageJson.devDependencies).map(dep => toDep(dep, packageJson.devDependencies[dep])) - : []; - return deps.concat(devDeps); - } else { - return []; - } - } - - private pathExists(p: string): boolean { - try { - fs.accessSync(p); - } catch (err) { - return false; - } - - return true; - } -} - -export class Dependency extends vscode.TreeItem { - - constructor( - public readonly label: string, - private readonly version: string, - public readonly collapsibleState: vscode.TreeItemCollapsibleState, - public readonly command?: vscode.Command - ) { - super(label, collapsibleState); - - this.tooltip = `${this.label}-${this.version}`; - this.description = this.version; - } - - iconPath = { - light: path.join(__filename, '..', '..', 'resources', 'light', 'dependency.svg'), - dark: path.join(__filename, '..', '..', 'resources', 'dark', 'dependency.svg') - }; - - contextValue = 'dependency'; -} +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; + +export class DepNodeProvider implements vscode.TreeDataProvider { + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + constructor(private workspaceRoot: string | undefined) { + } + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: Dependency): vscode.TreeItem { + return element; + } + + getChildren(element?: Dependency): Thenable { + if (!this.workspaceRoot) { + vscode.window.showInformationMessage('No dependency in empty workspace'); + return Promise.resolve([]); + } + + if (element) { + return Promise.resolve(this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json'))); + } else { + const packageJsonPath = path.join(this.workspaceRoot, 'package.json'); + if (this.pathExists(packageJsonPath)) { + return Promise.resolve(this.getDepsInPackageJson(packageJsonPath)); + } else { + vscode.window.showInformationMessage('Workspace has no package.json'); + return Promise.resolve([]); + } + } + + } + + /** + * Given the path to package.json, read all its dependencies and devDependencies. + */ + private getDepsInPackageJson(packageJsonPath: string): Dependency[] { + const workspaceRoot = this.workspaceRoot; + if (this.pathExists(packageJsonPath) && workspaceRoot) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + + const toDep = (moduleName: string, version: string): Dependency => { + if (this.pathExists(path.join(workspaceRoot, 'node_modules', moduleName))) { + return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.Collapsed); + } else { + return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.None, { + command: 'extension.openPackageOnNpm', + title: '', + arguments: [moduleName] + }); + } + }; + + const deps = packageJson.dependencies + ? Object.keys(packageJson.dependencies).map(dep => toDep(dep, packageJson.dependencies[dep])) + : []; + const devDeps = packageJson.devDependencies + ? Object.keys(packageJson.devDependencies).map(dep => toDep(dep, packageJson.devDependencies[dep])) + : []; + return deps.concat(devDeps); + } else { + return []; + } + } + + private pathExists(p: string): boolean { + try { + fs.accessSync(p); + } catch (err) { + return false; + } + + return true; + } +} + +export class Dependency extends vscode.TreeItem { + + constructor( + public readonly label: string, + private readonly version: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly command?: vscode.Command + ) { + super(label, collapsibleState); + + this.tooltip = `${this.label}-${this.version}`; + this.description = this.version; + } + + iconPath = { + light: path.join(__filename, '..', '..', 'resources', 'light', 'dependency.svg'), + dark: path.join(__filename, '..', '..', 'resources', 'dark', 'dependency.svg') + }; + + contextValue = 'dependency'; +} diff --git a/tree-view-sample/src/testViewDragAndDrop.ts b/tree-view-sample/src/testViewDragAndDrop.ts index 0ef025a6..0babc159 100644 --- a/tree-view-sample/src/testViewDragAndDrop.ts +++ b/tree-view-sample/src/testViewDragAndDrop.ts @@ -1,213 +1,213 @@ -import * as vscode from 'vscode'; - -export class TestViewDragAndDrop implements vscode.TreeDataProvider, vscode.TreeDragAndDropController { - dropMimeTypes = ['application/vnd.code.tree.testViewDragAndDrop']; - dragMimeTypes = ['text/uri-list']; - private _onDidChangeTreeData: vscode.EventEmitter<(Node | undefined)[] | undefined> = new vscode.EventEmitter(); - // We want to use an array as the event type, but the API for this is currently being finalized. Until it's finalized, use any. - public onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - public tree: any = { - 'a': { - 'aa': { - 'aaa': { - 'aaaa': { - 'aaaaa': { - 'aaaaaa': { - - } - } - } - } - }, - 'ab': {} - }, - 'b': { - 'ba': {}, - 'bb': {} - } - }; - // Keep track of any nodes we create so that we can re-use the same objects. - private nodes: any = {}; - - constructor(context: vscode.ExtensionContext) { - const view = vscode.window.createTreeView('testViewDragAndDrop', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true, dragAndDropController: this }); - context.subscriptions.push(view); - } - - // Tree data provider - - public getChildren(element: Node): Node[] { - return this._getChildren(element ? element.key : undefined).map(key => this._getNode(key)); - } - - public getTreeItem(element: Node): vscode.TreeItem { - const treeItem = this._getTreeItem(element.key); - treeItem.id = element.key; - return treeItem; - } - public getParent(element: Node): Node { - return this._getParent(element.key); - } - - dispose(): void { - // nothing to dispose - } - - // Drag and drop controller - - public async handleDrop(target: Node | undefined, sources: vscode.DataTransfer, token: vscode.CancellationToken): Promise { - const transferItem = sources.get('application/vnd.code.tree.testViewDragAndDrop'); - if (!transferItem) { - return; - } - const treeItems: Node[] = transferItem.value; - let roots = this._getLocalRoots(treeItems); - // Remove nodes that are already target's parent nodes - roots = roots.filter(r => !this._isChild(this._getTreeElement(r.key), target)); - if (roots.length > 0) { - // Reload parents of the moving elements - const parents = roots.map(r => this.getParent(r)); - roots.forEach(r => this._reparentNode(r, target)); - this._onDidChangeTreeData.fire([...parents, target]); - } - } - - public async handleDrag(source: Node[], treeDataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { - treeDataTransfer.set('application/vnd.code.tree.testViewDragAndDrop', new vscode.DataTransferItem(source)); - } - - // Helper methods - - _isChild(node: Node, child: Node | undefined): boolean { - if (!child) { - return false; - } - for (const prop in node) { - if (prop === child.key) { - return true; - } else { - const isChild = this._isChild((node as any)[prop], child); - if (isChild) { - return isChild; - } - } - } - return false; - } - - // From the given nodes, filter out all nodes who's parent is already in the the array of Nodes. - _getLocalRoots(nodes: Node[]): Node[] { - const localRoots = []; - for (let i = 0; i < nodes.length; i++) { - const parent = this.getParent(nodes[i]); - if (parent) { - const isInList = nodes.find(n => n.key === parent.key); - if (isInList === undefined) { - localRoots.push(nodes[i]); - } - } else { - localRoots.push(nodes[i]); - } - } - return localRoots; - } - - // Remove node from current position and add node to new target element - _reparentNode(node: Node, target: Node | undefined): void { - const element: any = {}; - element[node.key] = this._getTreeElement(node.key); - const elementCopy = { ...element }; - this._removeNode(node); - const targetElement = this._getTreeElement(target?.key); - if (Object.keys(element).length === 0) { - targetElement[node.key] = {}; - } else { - Object.assign(targetElement, elementCopy); - } - } - - // Remove node from tree - _removeNode(element: Node, tree?: any): void { - const subTree = tree ? tree : this.tree; - for (const prop in subTree) { - if (prop === element.key) { - const parent = this.getParent(element); - if (parent) { - const parentObject = this._getTreeElement(parent.key); - delete parentObject[prop]; - } else { - delete this.tree[prop]; - } - } else { - this._removeNode(element, subTree[prop]); - } - } - } - - _getChildren(key: string | undefined): string[] { - if (!key) { - return Object.keys(this.tree); - } - const treeElement = this._getTreeElement(key); - if (treeElement) { - return Object.keys(treeElement); - } - return []; - } - - _getTreeItem(key: string): vscode.TreeItem { - const treeElement = this._getTreeElement(key); - // An example of how to use codicons in a MarkdownString in a tree item tooltip. - const tooltip = new vscode.MarkdownString(`$(zap) Tooltip for ${key}`, true); - return { - label: /**vscode.TreeItemLabel**/{ label: key, highlights: key.length > 1 ? [[key.length - 2, key.length - 1]] : void 0 }, - tooltip, - collapsibleState: treeElement && Object.keys(treeElement).length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None, - resourceUri: vscode.Uri.parse(`/tmp/${key}`), - }; - } - - _getTreeElement(element: string | undefined, tree?: any): any { - if (!element) { - return this.tree; - } - const currentNode = tree ?? this.tree; - for (const prop in currentNode) { - if (prop === element) { - return currentNode[prop]; - } else { - const treeElement = this._getTreeElement(element, currentNode[prop]); - if (treeElement) { - return treeElement; - } - } - } - } - - _getParent(element: string, parent?: string, tree?: any): any { - const currentNode = tree ?? this.tree; - for (const prop in currentNode) { - if (prop === element && parent) { - return this._getNode(parent); - } else { - const parent = this._getParent(element, prop, currentNode[prop]); - if (parent) { - return parent; - } - } - } - } - - _getNode(key: string): Node { - if (!this.nodes[key]) { - this.nodes[key] = new Key(key); - } - return this.nodes[key]; - } -} - -type Node = { key: string }; - -class Key { - constructor(readonly key: string) { } +import * as vscode from 'vscode'; + +export class TestViewDragAndDrop implements vscode.TreeDataProvider, vscode.TreeDragAndDropController { + dropMimeTypes = ['application/vnd.code.tree.testViewDragAndDrop']; + dragMimeTypes = ['text/uri-list']; + private _onDidChangeTreeData: vscode.EventEmitter<(Node | undefined)[] | undefined> = new vscode.EventEmitter(); + // We want to use an array as the event type, but the API for this is currently being finalized. Until it's finalized, use any. + public onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + public tree: any = { + 'a': { + 'aa': { + 'aaa': { + 'aaaa': { + 'aaaaa': { + 'aaaaaa': { + + } + } + } + } + }, + 'ab': {} + }, + 'b': { + 'ba': {}, + 'bb': {} + } + }; + // Keep track of any nodes we create so that we can re-use the same objects. + private nodes: any = {}; + + constructor(context: vscode.ExtensionContext) { + const view = vscode.window.createTreeView('testViewDragAndDrop', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true, dragAndDropController: this }); + context.subscriptions.push(view); + } + + // Tree data provider + + public getChildren(element: Node): Node[] { + return this._getChildren(element ? element.key : undefined).map(key => this._getNode(key)); + } + + public getTreeItem(element: Node): vscode.TreeItem { + const treeItem = this._getTreeItem(element.key); + treeItem.id = element.key; + return treeItem; + } + public getParent(element: Node): Node { + return this._getParent(element.key); + } + + dispose(): void { + // nothing to dispose + } + + // Drag and drop controller + + public async handleDrop(target: Node | undefined, sources: vscode.DataTransfer, token: vscode.CancellationToken): Promise { + const transferItem = sources.get('application/vnd.code.tree.testViewDragAndDrop'); + if (!transferItem) { + return; + } + const treeItems: Node[] = transferItem.value; + let roots = this._getLocalRoots(treeItems); + // Remove nodes that are already target's parent nodes + roots = roots.filter(r => !this._isChild(this._getTreeElement(r.key), target)); + if (roots.length > 0) { + // Reload parents of the moving elements + const parents = roots.map(r => this.getParent(r)); + roots.forEach(r => this._reparentNode(r, target)); + this._onDidChangeTreeData.fire([...parents, target]); + } + } + + public async handleDrag(source: Node[], treeDataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { + treeDataTransfer.set('application/vnd.code.tree.testViewDragAndDrop', new vscode.DataTransferItem(source)); + } + + // Helper methods + + _isChild(node: Node, child: Node | undefined): boolean { + if (!child) { + return false; + } + for (const prop in node) { + if (prop === child.key) { + return true; + } else { + const isChild = this._isChild((node as any)[prop], child); + if (isChild) { + return isChild; + } + } + } + return false; + } + + // From the given nodes, filter out all nodes who's parent is already in the the array of Nodes. + _getLocalRoots(nodes: Node[]): Node[] { + const localRoots = []; + for (let i = 0; i < nodes.length; i++) { + const parent = this.getParent(nodes[i]); + if (parent) { + const isInList = nodes.find(n => n.key === parent.key); + if (isInList === undefined) { + localRoots.push(nodes[i]); + } + } else { + localRoots.push(nodes[i]); + } + } + return localRoots; + } + + // Remove node from current position and add node to new target element + _reparentNode(node: Node, target: Node | undefined): void { + const element: any = {}; + element[node.key] = this._getTreeElement(node.key); + const elementCopy = { ...element }; + this._removeNode(node); + const targetElement = this._getTreeElement(target?.key); + if (Object.keys(element).length === 0) { + targetElement[node.key] = {}; + } else { + Object.assign(targetElement, elementCopy); + } + } + + // Remove node from tree + _removeNode(element: Node, tree?: any): void { + const subTree = tree ? tree : this.tree; + for (const prop in subTree) { + if (prop === element.key) { + const parent = this.getParent(element); + if (parent) { + const parentObject = this._getTreeElement(parent.key); + delete parentObject[prop]; + } else { + delete this.tree[prop]; + } + } else { + this._removeNode(element, subTree[prop]); + } + } + } + + _getChildren(key: string | undefined): string[] { + if (!key) { + return Object.keys(this.tree); + } + const treeElement = this._getTreeElement(key); + if (treeElement) { + return Object.keys(treeElement); + } + return []; + } + + _getTreeItem(key: string): vscode.TreeItem { + const treeElement = this._getTreeElement(key); + // An example of how to use codicons in a MarkdownString in a tree item tooltip. + const tooltip = new vscode.MarkdownString(`$(zap) Tooltip for ${key}`, true); + return { + label: /**vscode.TreeItemLabel**/{ label: key, highlights: key.length > 1 ? [[key.length - 2, key.length - 1]] : void 0 }, + tooltip, + collapsibleState: treeElement && Object.keys(treeElement).length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None, + resourceUri: vscode.Uri.parse(`/tmp/${key}`), + }; + } + + _getTreeElement(element: string | undefined, tree?: any): any { + if (!element) { + return this.tree; + } + const currentNode = tree ?? this.tree; + for (const prop in currentNode) { + if (prop === element) { + return currentNode[prop]; + } else { + const treeElement = this._getTreeElement(element, currentNode[prop]); + if (treeElement) { + return treeElement; + } + } + } + } + + _getParent(element: string, parent?: string, tree?: any): any { + const currentNode = tree ?? this.tree; + for (const prop in currentNode) { + if (prop === element && parent) { + return this._getNode(parent); + } else { + const parent = this._getParent(element, prop, currentNode[prop]); + if (parent) { + return parent; + } + } + } + } + + _getNode(key: string): Node { + if (!this.nodes[key]) { + this.nodes[key] = new Key(key); + } + return this.nodes[key]; + } +} + +type Node = { key: string }; + +class Key { + constructor(readonly key: string) { } } \ No newline at end of file diff --git a/uri-handler-sample/src/extension.ts b/uri-handler-sample/src/extension.ts index 51ccd910..9217ed42 100644 --- a/uri-handler-sample/src/extension.ts +++ b/uri-handler-sample/src/extension.ts @@ -1,35 +1,35 @@ -import * as vscode from 'vscode'; - -// Our implementation of a UriHandler. -class MyUriHandler implements vscode.UriHandler { - // This function will get run when something redirects to VS Code - // with your extension id as the authority. - handleUri(uri: vscode.Uri): vscode.ProviderResult { - let message = "Handled a Uri!"; - if (uri.query) { - message += ` It came with this query: ${uri.query}`; - } - vscode.window.showInformationMessage(message); - } -} - -export function activate(context: vscode.ExtensionContext) { - - let disposable = vscode.commands.registerCommand('uri-handler-sample.start', async () => { - // Create our new UriHandler - const uriHandler = new MyUriHandler(); - - // And register it with VS Code. You can only register a single UriHandler for your extension. - context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); - - // You don't have to get the Uri from the `vscode.env.asExternalUri` API but it will add a query - // parameter (ex: "windowId%3D14") that will help VS Code decide which window to redirect to. - // If this query parameter isn't specified, VS Code will pick the last windows that was focused. - const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode-samples.uri-handler-sample`)); - vscode.window.showInformationMessage(`Starting to handle Uris. Open ${uri} in your browser to test.`); - }); - - context.subscriptions.push(disposable); -} - -export function deactivate() {} +import * as vscode from 'vscode'; + +// Our implementation of a UriHandler. +class MyUriHandler implements vscode.UriHandler { + // This function will get run when something redirects to VS Code + // with your extension id as the authority. + handleUri(uri: vscode.Uri): vscode.ProviderResult { + let message = "Handled a Uri!"; + if (uri.query) { + message += ` It came with this query: ${uri.query}`; + } + vscode.window.showInformationMessage(message); + } +} + +export function activate(context: vscode.ExtensionContext) { + + let disposable = vscode.commands.registerCommand('uri-handler-sample.start', async () => { + // Create our new UriHandler + const uriHandler = new MyUriHandler(); + + // And register it with VS Code. You can only register a single UriHandler for your extension. + context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); + + // You don't have to get the Uri from the `vscode.env.asExternalUri` API but it will add a query + // parameter (ex: "windowId%3D14") that will help VS Code decide which window to redirect to. + // If this query parameter isn't specified, VS Code will pick the last windows that was focused. + const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode-samples.uri-handler-sample`)); + vscode.window.showInformationMessage(`Starting to handle Uris. Open ${uri} in your browser to test.`); + }); + + context.subscriptions.push(disposable); +} + +export function deactivate() { } diff --git a/vim-sample/src/common.ts b/vim-sample/src/common.ts index 4ae9f4e8..f91dc76d 100644 --- a/vim-sample/src/common.ts +++ b/vim-sample/src/common.ts @@ -1,51 +1,51 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { MotionState, Motion } from './motions'; - -export enum Mode { - INSERT, - NORMAL, - REPLACE -} - -export interface ModifierKeys { - ctrl?: boolean; - alt?: boolean; - shifit?: boolean; -} - -export class DeleteRegister { - public isWholeLine: boolean; - public content: string; - - constructor(isWholeLine: boolean, content: string) { - this.isWholeLine = isWholeLine; - this.content = content; - } -} - -export interface IController { - motionState: MotionState; - - setMode(mode: Mode): void; - setVisual(newVisual: boolean): void; - findMotion(input: string): Motion; - isMotionPrefix(input: string): boolean; - - setDeleteRegister(register: DeleteRegister): void; - getDeleteRegister(): DeleteRegister; -} - -export abstract class AbstractCommandDescriptor { - - public abstract createCommand(args?: any): Command; - -} - -export interface Command { - commandId: string; - args?: any[]; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MotionState, Motion } from './motions'; + +export enum Mode { + INSERT, + NORMAL, + REPLACE +} + +export interface ModifierKeys { + ctrl?: boolean; + alt?: boolean; + shifit?: boolean; +} + +export class DeleteRegister { + public isWholeLine: boolean; + public content: string; + + constructor(isWholeLine: boolean, content: string) { + this.isWholeLine = isWholeLine; + this.content = content; + } +} + +export interface IController { + motionState: MotionState; + + setMode(mode: Mode): void; + setVisual(newVisual: boolean): void; + findMotion(input: string): Motion; + isMotionPrefix(input: string): boolean; + + setDeleteRegister(register: DeleteRegister): void; + getDeleteRegister(): DeleteRegister; +} + +export abstract class AbstractCommandDescriptor { + + public abstract createCommand(args?: any): Command; + +} + +export interface Command { + commandId: string; + args?: any[]; +} diff --git a/vim-sample/src/controller.ts b/vim-sample/src/controller.ts index d05798ba..5356cee1 100644 --- a/vim-sample/src/controller.ts +++ b/vim-sample/src/controller.ts @@ -1,303 +1,303 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TextEditorCursorStyle, Position, Range, Selection, TextEditor, TextEditorRevealType, window } from 'vscode'; - -import { Words } from './words'; -import { MotionState, Motion } from './motions'; -import { Mode, IController, DeleteRegister, Command, ModifierKeys } from './common'; -import { Mappings } from './mappings'; - -export interface ITypeResult { - hasConsumedInput: boolean; - executeEditorCommand: Command; -} - -export class Controller implements IController { - - private _currentMode: Mode; - private _currentInput: string; - private _motionState: MotionState; - private _isVisual: boolean; - - public get motionState(): MotionState { return this._motionState; } - public findMotion(input: string): Motion { return Mappings.findMotion(input); } - public isMotionPrefix(input: string): boolean { return Mappings.isMotionPrefix(input); } - - private _deleteRegister: DeleteRegister; - public setDeleteRegister(register: DeleteRegister): void { this._deleteRegister = register; } - public getDeleteRegister(): DeleteRegister { return this._deleteRegister; } - - constructor() { - this._motionState = new MotionState(); - this._deleteRegister = null; - this.setVisual(false); - this.setMode(Mode.NORMAL); - } - - public setWordSeparators(wordSeparators: string): void { - this._motionState.wordCharacterClass = Words.createWordCharacters(wordSeparators); - } - - public ensureNormalModePosition(editor: TextEditor): void { - if (this._currentMode !== Mode.NORMAL) { - return; - } - if (this._isVisual) { - return; - } - const sel = editor.selection; - const pos = sel.active; - const doc = editor.document; - const lineContent = doc.lineAt(pos.line).text; - if (lineContent.length === 0) { - return; - } - const maxCharacter = lineContent.length - 1; - if (pos.character > maxCharacter) { - setPositionAndReveal(editor, pos.line, maxCharacter); - } - } - - public hasInput(): boolean { - return this._currentInput.length > 0; - } - - public clearInput(): void { - this._currentInput = ''; - } - - public getMode(): Mode { - return this._currentMode; - } - - public setMode(newMode: Mode): void { - if (newMode !== this._currentMode) { - this._currentMode = newMode; - this._motionState.cursorDesiredCharacter = -1; // uninitialized - this._currentInput = ''; - } - } - - public setVisual(newVisual: boolean): void { - if (this._isVisual !== newVisual) { - this._isVisual = newVisual; - } - } - - public getVisual(): boolean { - return this._isVisual; - } - - public getCursorStyle(): TextEditorCursorStyle { - if (this._currentMode === Mode.NORMAL) { - if (/^([1-9]\d*)?(r|c)/.test(this._currentInput)) { - return TextEditorCursorStyle.Underline; - } - return TextEditorCursorStyle.Block; - } - if (this._currentMode === Mode.REPLACE) { - return TextEditorCursorStyle.Underline; - } - return TextEditorCursorStyle.Line; - } - - private _getModeLabel(): string { - if (this._currentMode === Mode.NORMAL) { - if (this._isVisual) { - return '-- VISUAL --'; - } - return '-- NORMAL --'; - } - - if (this._currentMode === Mode.REPLACE) { - if (this._isVisual) { - return '-- (replace) VISUAL --'; - } - return '-- REPLACE --'; - } - - if (this._isVisual) { - return '-- (insert) VISUAL --'; - } - return '-- INSERT --'; - } - - public getStatusText(): string { - const label = this._getModeLabel(); - return `VIM:> ${label}` + (this._currentInput ? ` >${this._currentInput}` : ``); - } - - private _isInComposition = false; - private _composingText = ''; - - public compositionStart(editor: TextEditor): void { - this._isInComposition = true; - this._composingText = ''; - } - - public compositionEnd(editor: TextEditor): Thenable { - this._isInComposition = false; - const text = this._composingText; - this._composingText = ''; - - if (text.length === 0) { - return Promise.resolve({ - hasConsumedInput: true, - executeEditorCommand: null - }); - } - - return this.type(editor, text, {}); - } - - public type(editor: TextEditor, text: string, modifierKeys: ModifierKeys): Thenable { - if (this._currentMode !== Mode.NORMAL && this._currentMode !== Mode.REPLACE) { - return Promise.resolve({ - hasConsumedInput: false, - executeEditorCommand: null - }); - } - - if (this._isInComposition) { - this._composingText += text; - return Promise.resolve({ - hasConsumedInput: true, - executeEditorCommand: null - }); - } - - if (this._currentMode === Mode.REPLACE) { - const pos = editor.selection.active; - editor.edit((builder) => { - builder.replace(new Range(pos.line, pos.character, pos.line, pos.character + 1), text); - }).then(() => { - setPositionAndReveal(editor, pos.line, pos.character + 1); - }); - - return Promise.resolve({ - hasConsumedInput: true, - executeEditorCommand: null - }); - } - this._currentInput += text; - return this._interpretNormalModeInput(editor, modifierKeys); - } - - public replacePrevChar(editor: TextEditor, text: string, replaceCharCnt: number): boolean { - if (this._currentMode !== Mode.NORMAL && this._currentMode !== Mode.REPLACE) { - return false; - } - - if (this._isInComposition) { - this._composingText = this._composingText.substr(0, this._composingText.length - replaceCharCnt) + text; - return true; - } - - if (this._currentMode === Mode.REPLACE) { - const pos = editor.selection.active; - editor.edit((builder) => { - builder.replace(new Range(pos.line, pos.character - replaceCharCnt, pos.line, pos.character), text); - }); - - return true; - } - - return true; - } - - private _interpretNormalModeInput(editor: TextEditor, modifierKeys: ModifierKeys): Thenable { - if (this._currentInput.startsWith(':')) { - return window.showInputBox({ value: 'tabm' }).then((value) => { - return this._findMapping(value || '', editor, modifierKeys); - }); - } - const result = this._findMapping(this._currentInput, editor, modifierKeys); - return Promise.resolve(result); - } - - private _findMapping(input: string, editor: TextEditor, modifierKeys: ModifierKeys): ITypeResult { - const command = Mappings.findCommand(input, modifierKeys); - if (command) { - this._currentInput = ''; - return { - hasConsumedInput: true, - executeEditorCommand: command - }; - } - - const operator = Mappings.findOperator(input, modifierKeys); - if (operator) { - if (this._isVisual) { - if (operator.runVisual(this, editor)) { - this._currentInput = ''; - } - } else { - // Mode.NORMAL - if (operator.runNormal(this, editor)) { - this._currentInput = ''; - } - } - return { - hasConsumedInput: true, - executeEditorCommand: null - }; - } - - const motionCommand = Mappings.findMotionCommand(input, this._isVisual, modifierKeys); - if (motionCommand) { - this._currentInput = ''; - return { - hasConsumedInput: true, - executeEditorCommand: motionCommand - }; - } - - const motion = Mappings.findMotion(input); - if (motion) { - const newPos = motion.run(editor.document, editor.selection.active, this._motionState); - if (this._isVisual) { - setSelectionAndReveal(editor, this._motionState.anchor, newPos.line, newPos.character); - } else { - // Mode.NORMAL - setPositionAndReveal(editor, newPos.line, newPos.character); - } - this._currentInput = ''; - return { - hasConsumedInput: true, - executeEditorCommand: null - }; - } - - // is it motion building - if (this.isMotionPrefix(input)) { - return { - hasConsumedInput: true, - executeEditorCommand: null - }; - } - - // INVALID INPUT - beep!! - this._currentInput = ''; - return { - hasConsumedInput: true, - executeEditorCommand: null - }; - } -} - -function setSelectionAndReveal(editor: TextEditor, anchor: Position, line: number, char: number): void { - editor.selection = new Selection(anchor, new Position(line, char)); - revealPosition(editor, line, char); -} - -function setPositionAndReveal(editor: TextEditor, line: number, char: number): void { - editor.selection = new Selection(new Position(line, char), new Position(line, char)); - revealPosition(editor, line, char); -} - -function revealPosition(editor: TextEditor, line: number, char: number): void { - editor.revealRange(new Range(line, char, line, char), TextEditorRevealType.Default); -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TextEditorCursorStyle, Position, Range, Selection, TextEditor, TextEditorRevealType, window } from 'vscode'; + +import { Words } from './words'; +import { MotionState, Motion } from './motions'; +import { Mode, IController, DeleteRegister, Command, ModifierKeys } from './common'; +import { Mappings } from './mappings'; + +export interface ITypeResult { + hasConsumedInput: boolean; + executeEditorCommand: Command; +} + +export class Controller implements IController { + + private _currentMode: Mode; + private _currentInput: string; + private _motionState: MotionState; + private _isVisual: boolean; + + public get motionState(): MotionState { return this._motionState; } + public findMotion(input: string): Motion { return Mappings.findMotion(input); } + public isMotionPrefix(input: string): boolean { return Mappings.isMotionPrefix(input); } + + private _deleteRegister: DeleteRegister; + public setDeleteRegister(register: DeleteRegister): void { this._deleteRegister = register; } + public getDeleteRegister(): DeleteRegister { return this._deleteRegister; } + + constructor() { + this._motionState = new MotionState(); + this._deleteRegister = null; + this.setVisual(false); + this.setMode(Mode.NORMAL); + } + + public setWordSeparators(wordSeparators: string): void { + this._motionState.wordCharacterClass = Words.createWordCharacters(wordSeparators); + } + + public ensureNormalModePosition(editor: TextEditor): void { + if (this._currentMode !== Mode.NORMAL) { + return; + } + if (this._isVisual) { + return; + } + const sel = editor.selection; + const pos = sel.active; + const doc = editor.document; + const lineContent = doc.lineAt(pos.line).text; + if (lineContent.length === 0) { + return; + } + const maxCharacter = lineContent.length - 1; + if (pos.character > maxCharacter) { + setPositionAndReveal(editor, pos.line, maxCharacter); + } + } + + public hasInput(): boolean { + return this._currentInput.length > 0; + } + + public clearInput(): void { + this._currentInput = ''; + } + + public getMode(): Mode { + return this._currentMode; + } + + public setMode(newMode: Mode): void { + if (newMode !== this._currentMode) { + this._currentMode = newMode; + this._motionState.cursorDesiredCharacter = -1; // uninitialized + this._currentInput = ''; + } + } + + public setVisual(newVisual: boolean): void { + if (this._isVisual !== newVisual) { + this._isVisual = newVisual; + } + } + + public getVisual(): boolean { + return this._isVisual; + } + + public getCursorStyle(): TextEditorCursorStyle { + if (this._currentMode === Mode.NORMAL) { + if (/^([1-9]\d*)?(r|c)/.test(this._currentInput)) { + return TextEditorCursorStyle.Underline; + } + return TextEditorCursorStyle.Block; + } + if (this._currentMode === Mode.REPLACE) { + return TextEditorCursorStyle.Underline; + } + return TextEditorCursorStyle.Line; + } + + private _getModeLabel(): string { + if (this._currentMode === Mode.NORMAL) { + if (this._isVisual) { + return '-- VISUAL --'; + } + return '-- NORMAL --'; + } + + if (this._currentMode === Mode.REPLACE) { + if (this._isVisual) { + return '-- (replace) VISUAL --'; + } + return '-- REPLACE --'; + } + + if (this._isVisual) { + return '-- (insert) VISUAL --'; + } + return '-- INSERT --'; + } + + public getStatusText(): string { + const label = this._getModeLabel(); + return `VIM:> ${label}` + (this._currentInput ? ` >${this._currentInput}` : ``); + } + + private _isInComposition = false; + private _composingText = ''; + + public compositionStart(editor: TextEditor): void { + this._isInComposition = true; + this._composingText = ''; + } + + public compositionEnd(editor: TextEditor): Thenable { + this._isInComposition = false; + const text = this._composingText; + this._composingText = ''; + + if (text.length === 0) { + return Promise.resolve({ + hasConsumedInput: true, + executeEditorCommand: null + }); + } + + return this.type(editor, text, {}); + } + + public type(editor: TextEditor, text: string, modifierKeys: ModifierKeys): Thenable { + if (this._currentMode !== Mode.NORMAL && this._currentMode !== Mode.REPLACE) { + return Promise.resolve({ + hasConsumedInput: false, + executeEditorCommand: null + }); + } + + if (this._isInComposition) { + this._composingText += text; + return Promise.resolve({ + hasConsumedInput: true, + executeEditorCommand: null + }); + } + + if (this._currentMode === Mode.REPLACE) { + const pos = editor.selection.active; + editor.edit((builder) => { + builder.replace(new Range(pos.line, pos.character, pos.line, pos.character + 1), text); + }).then(() => { + setPositionAndReveal(editor, pos.line, pos.character + 1); + }); + + return Promise.resolve({ + hasConsumedInput: true, + executeEditorCommand: null + }); + } + this._currentInput += text; + return this._interpretNormalModeInput(editor, modifierKeys); + } + + public replacePrevChar(editor: TextEditor, text: string, replaceCharCnt: number): boolean { + if (this._currentMode !== Mode.NORMAL && this._currentMode !== Mode.REPLACE) { + return false; + } + + if (this._isInComposition) { + this._composingText = this._composingText.substr(0, this._composingText.length - replaceCharCnt) + text; + return true; + } + + if (this._currentMode === Mode.REPLACE) { + const pos = editor.selection.active; + editor.edit((builder) => { + builder.replace(new Range(pos.line, pos.character - replaceCharCnt, pos.line, pos.character), text); + }); + + return true; + } + + return true; + } + + private _interpretNormalModeInput(editor: TextEditor, modifierKeys: ModifierKeys): Thenable { + if (this._currentInput.startsWith(':')) { + return window.showInputBox({ value: 'tabm' }).then((value) => { + return this._findMapping(value || '', editor, modifierKeys); + }); + } + const result = this._findMapping(this._currentInput, editor, modifierKeys); + return Promise.resolve(result); + } + + private _findMapping(input: string, editor: TextEditor, modifierKeys: ModifierKeys): ITypeResult { + const command = Mappings.findCommand(input, modifierKeys); + if (command) { + this._currentInput = ''; + return { + hasConsumedInput: true, + executeEditorCommand: command + }; + } + + const operator = Mappings.findOperator(input, modifierKeys); + if (operator) { + if (this._isVisual) { + if (operator.runVisual(this, editor)) { + this._currentInput = ''; + } + } else { + // Mode.NORMAL + if (operator.runNormal(this, editor)) { + this._currentInput = ''; + } + } + return { + hasConsumedInput: true, + executeEditorCommand: null + }; + } + + const motionCommand = Mappings.findMotionCommand(input, this._isVisual, modifierKeys); + if (motionCommand) { + this._currentInput = ''; + return { + hasConsumedInput: true, + executeEditorCommand: motionCommand + }; + } + + const motion = Mappings.findMotion(input); + if (motion) { + const newPos = motion.run(editor.document, editor.selection.active, this._motionState); + if (this._isVisual) { + setSelectionAndReveal(editor, this._motionState.anchor, newPos.line, newPos.character); + } else { + // Mode.NORMAL + setPositionAndReveal(editor, newPos.line, newPos.character); + } + this._currentInput = ''; + return { + hasConsumedInput: true, + executeEditorCommand: null + }; + } + + // is it motion building + if (this.isMotionPrefix(input)) { + return { + hasConsumedInput: true, + executeEditorCommand: null + }; + } + + // INVALID INPUT - beep!! + this._currentInput = ''; + return { + hasConsumedInput: true, + executeEditorCommand: null + }; + } +} + +function setSelectionAndReveal(editor: TextEditor, anchor: Position, line: number, char: number): void { + editor.selection = new Selection(anchor, new Position(line, char)); + revealPosition(editor, line, char); +} + +function setPositionAndReveal(editor: TextEditor, line: number, char: number): void { + editor.selection = new Selection(new Position(line, char), new Position(line, char)); + revealPosition(editor, line, char); +} + +function revealPosition(editor: TextEditor, line: number, char: number): void { + editor.revealRange(new Range(line, char, line, char), TextEditorRevealType.Default); +} diff --git a/vim-sample/src/extension.ts b/vim-sample/src/extension.ts index a4bb36f0..e02d9d3d 100644 --- a/vim-sample/src/extension.ts +++ b/vim-sample/src/extension.ts @@ -13,7 +13,7 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand(commandId, run)); } function registerCtrlKeyBinding(key: string): void { - registerCommandNice(key, function (args) { + registerCommandNice(key, function(args) { if (!vscode.window.activeTextEditor) { return; } @@ -23,34 +23,34 @@ export function activate(context: vscode.ExtensionContext) { const vimExt = new VimExt(); - registerCommandNice('type', function (args) { + registerCommandNice('type', function(args) { if (!vscode.window.activeTextEditor) { return; } vimExt.type(args.text); }); - registerCommandNice('replacePreviousChar', function (args) { + registerCommandNice('replacePreviousChar', function(args) { if (!vscode.window.activeTextEditor) { return; } vimExt.replacePrevChar(args.text, args.replaceCharCnt); }); - registerCommandNice('compositionStart', function (args) { + registerCommandNice('compositionStart', function(args) { if (!vscode.window.activeTextEditor) { return; } vimExt.compositionStart(); }); - registerCommandNice('compositionEnd', function (args) { + registerCommandNice('compositionEnd', function(args) { if (!vscode.window.activeTextEditor) { return; } vimExt.compositionEnd(); }); - registerCommandNice('vim.goToNormalMode', function (args) { + registerCommandNice('vim.goToNormalMode', function(args) { vimExt.goToNormalMode(); }); - registerCommandNice('vim.clearInput', function (args) { + registerCommandNice('vim.clearInput', function(args) { vimExt.clearInput(); }); // registerCommandNice('paste', function(args) { diff --git a/vim-sample/src/mappings.ts b/vim-sample/src/mappings.ts index 131a3287..0811b6cc 100644 --- a/vim-sample/src/mappings.ts +++ b/vim-sample/src/mappings.ts @@ -1,232 +1,232 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TextEditor } from 'vscode'; -import { Motion, Motions } from './motions'; -import { Operator, Operators } from './operators'; -import { IController, Command, AbstractCommandDescriptor, ModifierKeys } from './common'; - -const CHAR_TO_BINDING: { [char: string]: any; } = {}; -function defineBinding(char: string, value: any, modifierKeys: ModifierKeys): void { - const key = modifierKeys.ctrl ? 'CTRL + ' + char : char; - CHAR_TO_BINDING[key] = value; -} -function getBinding(char: string, modifierKeys: ModifierKeys): any { - const key = modifierKeys.ctrl ? 'CTRL + ' + char : char; - return CHAR_TO_BINDING[key]; -} - -function defineOperator(char: string, operator: Operator, modifierKeys: ModifierKeys = {}): void { - defineBinding(char + '__operator__', operator, modifierKeys); -} -function getOperator(char: string, modifierKeys: ModifierKeys = {}): Operator { - return getBinding(char + '__operator__', modifierKeys); -} - -function defineCommand(char: string, commandId: string, modifierKeys: ModifierKeys = {}): void { - defineBinding(char + '__command__', { commandId: commandId }, modifierKeys); -} -function getCommand(char: string, modifierKeys: ModifierKeys = {}): Command { - return getBinding(char + '__command__', modifierKeys); -} - -function defineMotion(char: string, motion: Motion, modifierKeys: ModifierKeys = {}): void { - defineBinding(char + '__motion__', motion, modifierKeys); -} -function getMotion(char: string, modifierKeys: ModifierKeys = {}): Motion { - return getBinding(char + '__motion__', modifierKeys); -} - -function defineMotionCommand(char: string, motionCommand: AbstractCommandDescriptor, modifierKeys: ModifierKeys = {}): void { - defineBinding(char + '__motioncommand__', motionCommand, modifierKeys); -} -function getMotionCommand(char: string, modifierKeys: ModifierKeys = {}): AbstractCommandDescriptor { - return getBinding(char + '__motioncommand__', modifierKeys); -} - -// Operators -defineOperator('x', Operators.DeleteCharUnderCursor); -defineOperator('i', Operators.Insert); -defineOperator('a', Operators.Append); -defineOperator('A', Operators.AppendEndOfLine); -defineOperator('d', Operators.DeleteTo); -defineOperator('p', Operators.Put); -defineOperator('r', Operators.Replace); -defineOperator('R', Operators.ReplaceMode); -defineOperator('c', Operators.Change); -defineOperator('v', Operators.Visual); - -// Commands -defineCommand('u', 'undo'); -defineCommand('U', 'undo'); - -// Left-right motions -defineMotionCommand('h', Motions.Left); -defineMotionCommand('l', Motions.Right); -defineMotion('0', Motions.StartOfLine); -defineMotion('$', Motions.EndOfLine); -defineMotionCommand('g0', Motions.WrappedLineStart); -defineMotionCommand('g^', Motions.WrappedLineFirstNonWhiteSpaceCharacter); -defineMotionCommand('gm', Motions.WrappedLineColumnCenter); -defineMotionCommand('g$', Motions.WrappedLineEnd); -defineMotionCommand('g_', Motions.WrappedLineLastNonWhiteSpaceCharacter); - -// Cursor scroll motions -defineMotionCommand('zh', Motions.CursorScrollLeft); -defineMotionCommand('zl', Motions.CursorScrollRight); -defineMotionCommand('zH', Motions.CursorScrollLeftByHalfLine); -defineMotionCommand('zL', Motions.CursorScrollRightByHalfLine); - -// Up-down motions -defineMotionCommand('j', Motions.Down); -defineMotionCommand('k', Motions.Up); -defineMotionCommand('gj', Motions.WrappedLineDown); -defineMotionCommand('gk', Motions.WrappedLineUp); -defineMotion('G', Motions.GoToLine); -defineMotion('gg', Motions.GoToFirstLine); - -defineMotionCommand('H', Motions.ViewPortTop); -defineMotionCommand('M', Motions.ViewPortCenter); -defineMotionCommand('L', Motions.ViewPortBottom); - -// Word motions -defineMotion('w', Motions.NextWordStart); -defineMotion('e', Motions.NextWordEnd); - -// Tab motions -defineMotionCommand('tabm', Motions.MoveActiveEditor); -defineMotionCommand('tabm<', Motions.MoveActiveEditorLeft); -defineMotionCommand('tabm>', Motions.MoveActiveEditorRight); -defineMotionCommand('tabm<<', Motions.MoveActiveEditorFirst); -defineMotionCommand('tabm>>', Motions.MoveActiveEditorLast); -defineMotionCommand('tabm.', Motions.MoveActiveEditorCenter); - -// Scroll motions -defineMotionCommand('e', Motions.ScrollDownByLine, { ctrl: true }); -defineMotionCommand('d', Motions.ScrollDownByHalfPage, { ctrl: true }); -defineMotionCommand('f', Motions.ScrollDownByPage, { ctrl: true }); -defineMotionCommand('y', Motions.ScrollUpByLine, { ctrl: true }); -defineMotionCommand('u', Motions.ScrollUpByHalfPage, { ctrl: true }); -defineMotionCommand('b', Motions.ScrollUpByPage, { ctrl: true }); - -defineMotionCommand('zt', Motions.RevealCurrentLineAtTop); -defineMotionCommand('zz', Motions.RevealCurrentLineAtCenter); -defineMotionCommand('zb', Motions.RevealCurrentLineAtBottom); - -// Folding -defineMotionCommand('zc', Motions.FoldUnder); -defineMotionCommand('zo', Motions.UnfoldUnder); - - -export interface IFoundOperator { - runNormal(controller: IController, editor: TextEditor): boolean; - runVisual(controller: IController, editor: TextEditor): boolean; -} - -export class Mappings { - - public static findMotion(input: string): Motion { - const parsed = _parseNumberAndString(input); - let motion = getMotion(parsed.input.substr(0, 1)); - if (!motion) { - motion = getMotion(parsed.input.substr(0, 2)); - if (!motion) { - return null; - } - } - return motion.repeat(parsed.hasRepeatCount, parsed.repeatCount); - } - - public static findMotionCommand(input: string, isVisual: boolean, modifierKeys: ModifierKeys): Command { - let parsed = _parseNumberAndString(input); - let command = Mappings.findMotionCommandFromNumberAndString(parsed, isVisual, modifierKeys); - if (!command) { - parsed = _parseNumberAndString(input, false); - command = Mappings.findMotionCommandFromNumberAndString(parsed, isVisual, modifierKeys); - } - return command; - } - - private static findMotionCommandFromNumberAndString(numberAndString: INumberAndString, isVisual: boolean, modifierKeys: ModifierKeys): Command { - let motionCommand = getMotionCommand(numberAndString.input.substr(0, 1), modifierKeys); - if (!motionCommand) { - motionCommand = getMotionCommand(numberAndString.input.substr(0, 2), modifierKeys); - } - if (!motionCommand) { - motionCommand = getMotionCommand(numberAndString.input.substr(1, 2), modifierKeys); - } - if (!motionCommand) { - motionCommand = getMotionCommand(numberAndString.input.substr(1, 3), modifierKeys); - } - if (!motionCommand) { - motionCommand = getMotionCommand(numberAndString.input, modifierKeys); - } - return motionCommand ? motionCommand.createCommand({ isVisual: isVisual, repeat: numberAndString.hasRepeatCount ? numberAndString.repeatCount : undefined }) : null; - } - - public static findOperator(input: string, modifierKeys: ModifierKeys): IFoundOperator { - const parsed = _parseNumberAndString(input); - const operator = getOperator(parsed.input.substr(0, 1), modifierKeys); - if (!operator) { - return null; - } - const operatorArgs = parsed.input.substr(1); - return { - runNormal: (controller: IController, editor: TextEditor) => { - return operator.runNormalMode(controller, editor, parsed.repeatCount, operatorArgs); - }, - runVisual: (controller: IController, editor: TextEditor) => { - return operator.runVisualMode(controller, editor, operatorArgs); - } - }; - } - - public static findCommand(input: string, modifierKeys: ModifierKeys): Command { - return getCommand(input, modifierKeys) || null; - } - - public static isMotionPrefix(input: string): boolean { - if (input.length === 0) { - return true; - } - if (input === 'g' || input === 'v' || input === 'z') { - return true; - } - return /^[1-9]\d*v?g?z?$/.test(input); - } -} - -function _parseNumberAndString(input: string, numberAtBeginning = true): INumberAndString { - if (numberAtBeginning) { - const repeatCountMatch = input.match(/^([1-9]\d*)/); - if (repeatCountMatch) { - return { - hasRepeatCount: true, - repeatCount: parseInt(repeatCountMatch[0], 10), - input: input.substr(repeatCountMatch[0].length) - }; - } - } else { - const repeatCountMatch = input.match(/(\d+)$/); - if (repeatCountMatch) { - return { - hasRepeatCount: true, - repeatCount: parseInt(repeatCountMatch[1], 10), - input: input.substr(0, input.length - repeatCountMatch[1].length) - }; - } - } - return { - hasRepeatCount: false, - repeatCount: 1, - input: input - }; -} - -interface INumberAndString { - hasRepeatCount: boolean; - repeatCount: number; - input: string; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TextEditor } from 'vscode'; +import { Motion, Motions } from './motions'; +import { Operator, Operators } from './operators'; +import { IController, Command, AbstractCommandDescriptor, ModifierKeys } from './common'; + +const CHAR_TO_BINDING: { [char: string]: any; } = {}; +function defineBinding(char: string, value: any, modifierKeys: ModifierKeys): void { + const key = modifierKeys.ctrl ? 'CTRL + ' + char : char; + CHAR_TO_BINDING[key] = value; +} +function getBinding(char: string, modifierKeys: ModifierKeys): any { + const key = modifierKeys.ctrl ? 'CTRL + ' + char : char; + return CHAR_TO_BINDING[key]; +} + +function defineOperator(char: string, operator: Operator, modifierKeys: ModifierKeys = {}): void { + defineBinding(char + '__operator__', operator, modifierKeys); +} +function getOperator(char: string, modifierKeys: ModifierKeys = {}): Operator { + return getBinding(char + '__operator__', modifierKeys); +} + +function defineCommand(char: string, commandId: string, modifierKeys: ModifierKeys = {}): void { + defineBinding(char + '__command__', { commandId: commandId }, modifierKeys); +} +function getCommand(char: string, modifierKeys: ModifierKeys = {}): Command { + return getBinding(char + '__command__', modifierKeys); +} + +function defineMotion(char: string, motion: Motion, modifierKeys: ModifierKeys = {}): void { + defineBinding(char + '__motion__', motion, modifierKeys); +} +function getMotion(char: string, modifierKeys: ModifierKeys = {}): Motion { + return getBinding(char + '__motion__', modifierKeys); +} + +function defineMotionCommand(char: string, motionCommand: AbstractCommandDescriptor, modifierKeys: ModifierKeys = {}): void { + defineBinding(char + '__motioncommand__', motionCommand, modifierKeys); +} +function getMotionCommand(char: string, modifierKeys: ModifierKeys = {}): AbstractCommandDescriptor { + return getBinding(char + '__motioncommand__', modifierKeys); +} + +// Operators +defineOperator('x', Operators.DeleteCharUnderCursor); +defineOperator('i', Operators.Insert); +defineOperator('a', Operators.Append); +defineOperator('A', Operators.AppendEndOfLine); +defineOperator('d', Operators.DeleteTo); +defineOperator('p', Operators.Put); +defineOperator('r', Operators.Replace); +defineOperator('R', Operators.ReplaceMode); +defineOperator('c', Operators.Change); +defineOperator('v', Operators.Visual); + +// Commands +defineCommand('u', 'undo'); +defineCommand('U', 'undo'); + +// Left-right motions +defineMotionCommand('h', Motions.Left); +defineMotionCommand('l', Motions.Right); +defineMotion('0', Motions.StartOfLine); +defineMotion('$', Motions.EndOfLine); +defineMotionCommand('g0', Motions.WrappedLineStart); +defineMotionCommand('g^', Motions.WrappedLineFirstNonWhiteSpaceCharacter); +defineMotionCommand('gm', Motions.WrappedLineColumnCenter); +defineMotionCommand('g$', Motions.WrappedLineEnd); +defineMotionCommand('g_', Motions.WrappedLineLastNonWhiteSpaceCharacter); + +// Cursor scroll motions +defineMotionCommand('zh', Motions.CursorScrollLeft); +defineMotionCommand('zl', Motions.CursorScrollRight); +defineMotionCommand('zH', Motions.CursorScrollLeftByHalfLine); +defineMotionCommand('zL', Motions.CursorScrollRightByHalfLine); + +// Up-down motions +defineMotionCommand('j', Motions.Down); +defineMotionCommand('k', Motions.Up); +defineMotionCommand('gj', Motions.WrappedLineDown); +defineMotionCommand('gk', Motions.WrappedLineUp); +defineMotion('G', Motions.GoToLine); +defineMotion('gg', Motions.GoToFirstLine); + +defineMotionCommand('H', Motions.ViewPortTop); +defineMotionCommand('M', Motions.ViewPortCenter); +defineMotionCommand('L', Motions.ViewPortBottom); + +// Word motions +defineMotion('w', Motions.NextWordStart); +defineMotion('e', Motions.NextWordEnd); + +// Tab motions +defineMotionCommand('tabm', Motions.MoveActiveEditor); +defineMotionCommand('tabm<', Motions.MoveActiveEditorLeft); +defineMotionCommand('tabm>', Motions.MoveActiveEditorRight); +defineMotionCommand('tabm<<', Motions.MoveActiveEditorFirst); +defineMotionCommand('tabm>>', Motions.MoveActiveEditorLast); +defineMotionCommand('tabm.', Motions.MoveActiveEditorCenter); + +// Scroll motions +defineMotionCommand('e', Motions.ScrollDownByLine, { ctrl: true }); +defineMotionCommand('d', Motions.ScrollDownByHalfPage, { ctrl: true }); +defineMotionCommand('f', Motions.ScrollDownByPage, { ctrl: true }); +defineMotionCommand('y', Motions.ScrollUpByLine, { ctrl: true }); +defineMotionCommand('u', Motions.ScrollUpByHalfPage, { ctrl: true }); +defineMotionCommand('b', Motions.ScrollUpByPage, { ctrl: true }); + +defineMotionCommand('zt', Motions.RevealCurrentLineAtTop); +defineMotionCommand('zz', Motions.RevealCurrentLineAtCenter); +defineMotionCommand('zb', Motions.RevealCurrentLineAtBottom); + +// Folding +defineMotionCommand('zc', Motions.FoldUnder); +defineMotionCommand('zo', Motions.UnfoldUnder); + + +export interface IFoundOperator { + runNormal(controller: IController, editor: TextEditor): boolean; + runVisual(controller: IController, editor: TextEditor): boolean; +} + +export class Mappings { + + public static findMotion(input: string): Motion { + const parsed = _parseNumberAndString(input); + let motion = getMotion(parsed.input.substr(0, 1)); + if (!motion) { + motion = getMotion(parsed.input.substr(0, 2)); + if (!motion) { + return null; + } + } + return motion.repeat(parsed.hasRepeatCount, parsed.repeatCount); + } + + public static findMotionCommand(input: string, isVisual: boolean, modifierKeys: ModifierKeys): Command { + let parsed = _parseNumberAndString(input); + let command = Mappings.findMotionCommandFromNumberAndString(parsed, isVisual, modifierKeys); + if (!command) { + parsed = _parseNumberAndString(input, false); + command = Mappings.findMotionCommandFromNumberAndString(parsed, isVisual, modifierKeys); + } + return command; + } + + private static findMotionCommandFromNumberAndString(numberAndString: INumberAndString, isVisual: boolean, modifierKeys: ModifierKeys): Command { + let motionCommand = getMotionCommand(numberAndString.input.substr(0, 1), modifierKeys); + if (!motionCommand) { + motionCommand = getMotionCommand(numberAndString.input.substr(0, 2), modifierKeys); + } + if (!motionCommand) { + motionCommand = getMotionCommand(numberAndString.input.substr(1, 2), modifierKeys); + } + if (!motionCommand) { + motionCommand = getMotionCommand(numberAndString.input.substr(1, 3), modifierKeys); + } + if (!motionCommand) { + motionCommand = getMotionCommand(numberAndString.input, modifierKeys); + } + return motionCommand ? motionCommand.createCommand({ isVisual: isVisual, repeat: numberAndString.hasRepeatCount ? numberAndString.repeatCount : undefined }) : null; + } + + public static findOperator(input: string, modifierKeys: ModifierKeys): IFoundOperator { + const parsed = _parseNumberAndString(input); + const operator = getOperator(parsed.input.substr(0, 1), modifierKeys); + if (!operator) { + return null; + } + const operatorArgs = parsed.input.substr(1); + return { + runNormal: (controller: IController, editor: TextEditor) => { + return operator.runNormalMode(controller, editor, parsed.repeatCount, operatorArgs); + }, + runVisual: (controller: IController, editor: TextEditor) => { + return operator.runVisualMode(controller, editor, operatorArgs); + } + }; + } + + public static findCommand(input: string, modifierKeys: ModifierKeys): Command { + return getCommand(input, modifierKeys) || null; + } + + public static isMotionPrefix(input: string): boolean { + if (input.length === 0) { + return true; + } + if (input === 'g' || input === 'v' || input === 'z') { + return true; + } + return /^[1-9]\d*v?g?z?$/.test(input); + } +} + +function _parseNumberAndString(input: string, numberAtBeginning = true): INumberAndString { + if (numberAtBeginning) { + const repeatCountMatch = input.match(/^([1-9]\d*)/); + if (repeatCountMatch) { + return { + hasRepeatCount: true, + repeatCount: parseInt(repeatCountMatch[0], 10), + input: input.substr(repeatCountMatch[0].length) + }; + } + } else { + const repeatCountMatch = input.match(/(\d+)$/); + if (repeatCountMatch) { + return { + hasRepeatCount: true, + repeatCount: parseInt(repeatCountMatch[1], 10), + input: input.substr(0, input.length - repeatCountMatch[1].length) + }; + } + } + return { + hasRepeatCount: false, + repeatCount: 1, + input: input + }; +} + +interface INumberAndString { + hasRepeatCount: boolean; + repeatCount: number; + input: string; +} diff --git a/vim-sample/src/motions.ts b/vim-sample/src/motions.ts index a354ed53..54468243 100644 --- a/vim-sample/src/motions.ts +++ b/vim-sample/src/motions.ts @@ -1,432 +1,432 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Position, TextDocument, window } from 'vscode'; -import { Words, WordCharacters } from './words'; -import { Command, AbstractCommandDescriptor } from './common'; - -export class MotionState { - - public anchor: Position | null; - public cursorDesiredCharacter: number; - public wordCharacterClass: WordCharacters | null; - - constructor() { - this.cursorDesiredCharacter = -1; - this.wordCharacterClass = null; - this.anchor = null; - } - -} - -export abstract class Motion { - public abstract run(doc: TextDocument, pos: Position, state: MotionState): Position; - - public repeat(hasRepeatCount: boolean, count: number): Motion { - if (!hasRepeatCount) { - return this; - } - return new RepeatingMotion(this, count); - } -} - -class RepeatingMotion extends Motion { - - private _actual: Motion; - private _repeatCount: number; - - constructor(actual: Motion, repeatCount: number) { - super(); - this._actual = actual; - this._repeatCount = repeatCount; - } - - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - for (let cnt = 0; cnt < this._repeatCount; cnt++) { - pos = this._actual.run(doc, pos, state); - } - return pos; - } -} - -class NextCharacterMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - if (pos.character === doc.lineAt(pos.line).text.length) { - // on last character - return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos); - } - - return new Position(pos.line, pos.character + 1); - } -} - -class LeftMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - const line = pos.line; - - if (pos.character > 0) { - state.cursorDesiredCharacter = pos.character - 1; - return new Position(line, state.cursorDesiredCharacter); - } - - return pos; - } -} - -class DownMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - let line = pos.line; - - state.cursorDesiredCharacter = (state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter); - - if (line < doc.lineCount - 1) { - line++; - return new Position(line, Math.min(state.cursorDesiredCharacter, doc.lineAt(line).text.length)); - } - - return pos; - } -} - -class UpMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - let line = pos.line; - - state.cursorDesiredCharacter = (state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter); - - if (line > 0) { - line--; - return new Position(line, Math.min(state.cursorDesiredCharacter, doc.lineAt(line).text.length)); - } - - return pos; - } -} - -class RightMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - const line = pos.line; - const maxCharacter = doc.lineAt(line).text.length; - - if (pos.character < maxCharacter) { - state.cursorDesiredCharacter = pos.character + 1; - return new Position(line, state.cursorDesiredCharacter); - } - - return pos; - } -} - -class EndOfLineMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - return new Position(pos.line, doc.lineAt(pos.line).text.length); - } -} - -class StartOfLineMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - return new Position(pos.line, 0); - } -} - -class NextWordStartMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - const lineContent = doc.lineAt(pos.line).text; - - if (pos.character >= lineContent.length - 1) { - // cursor at end of line - return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos); - } - - const nextWord = Words.findNextWord(doc, pos, state.wordCharacterClass); - - if (!nextWord) { - // return end of the line - return Motions.EndOfLine.run(doc, pos, state); - } - - if (nextWord.start <= pos.character && pos.character < nextWord.end) { - // Sitting on a word - const nextNextWord = Words.findNextWord(doc, new Position(pos.line, nextWord.end), state.wordCharacterClass); - if (nextNextWord) { - // return start of the next next word - return new Position(pos.line, nextNextWord.start); - } else { - // return end of line - return Motions.EndOfLine.run(doc, pos, state); - } - } else { - // return start of the next word - return new Position(pos.line, nextWord.start); - } - } -} - -class NextWordEndMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - const lineContent = doc.lineAt(pos.line).text; - - if (pos.character >= lineContent.length - 1) { - // no content on this line or cursor at end of line - return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos); - } - - const nextWord = Words.findNextWord(doc, pos, state.wordCharacterClass); - - if (!nextWord) { - // return end of the line - return Motions.EndOfLine.run(doc, pos, state); - } - - // return start of the next word - return new Position(pos.line, nextWord.end); - } -} - -class GoToLineUndefinedMotion extends Motion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - // does not do anything - return pos; - } - - public repeat(hasRepeatCount: boolean, count: number): Motion { - if (!hasRepeatCount) { - return Motions.GoToLastLine; - } - return new GoToLineDefinedMotion(count); - } -} - -abstract class GoToLineMotion extends Motion { - - protected firstNonWhitespaceChar(doc: TextDocument, line: number): number { - const lineContent = doc.lineAt(line).text; - let character = 0; - while (character < lineContent.length) { - const ch = lineContent.charAt(character); - if (ch !== ' ' && ch !== '\t') { - break; - } - character++; - } - return character; - } - -} - -class GoToFirstLineMotion extends GoToLineMotion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - return new Position(0, this.firstNonWhitespaceChar(doc, 0)); - } -} - -class GoToLastLineMotion extends GoToLineMotion { - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - const lastLine = doc.lineCount - 1; - return new Position(lastLine, this.firstNonWhitespaceChar(doc, lastLine)); - } -} - -class GoToLineDefinedMotion extends GoToLineMotion { - private _lineNumber: number; - - constructor(lineNumber: number) { - super(); - this._lineNumber = lineNumber; - } - - public run(doc: TextDocument, pos: Position, state: MotionState): Position { - const line = Math.min(doc.lineCount - 1, Math.max(0, this._lineNumber - 1)); - return new Position(line, this.firstNonWhitespaceChar(doc, line)); - } -} - -class CursorMoveCommand extends AbstractCommandDescriptor { - - constructor(private to: string, private by?: string) { - super(); - } - - public createCommand(args?: any): Command { - const cursorMoveArgs: any = { - to: this.to, - by: this.by, - value: args.repeat || 1, - select: !!args.isVisual - }; - return { - commandId: 'cursorMove', - args: cursorMoveArgs - }; - } -} - -class EditorScrollCommand extends AbstractCommandDescriptor { - - constructor(private to: string, private by?: string) { - super(); - } - - public createCommand(args?: any): Command { - const editorScrollArgs: any = { - to: this.to, - by: this.by, - value: args.repeat || 1, - revealCursor: true - }; - return { - commandId: 'editorScroll', - args: editorScrollArgs - }; - } -} - -class RevealCurrentLineCommand extends AbstractCommandDescriptor { - - constructor(private at: string) { - super(); - } - - public createCommand(args?: any): Command { - const lineNumber = window.activeTextEditor.selection.start.line; - const revealLineArgs: any = { - lineNumber, - at: this.at - }; - return { - commandId: 'revealLine', - args: revealLineArgs - }; - } -} - -class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor { - - constructor() { - super(); - } - - public createCommand(args?: any): Command { - const moveActiveEditorArgs: any = { - to: args.repeat === void 0 ? 'last' : 'position', - value: args.repeat !== void 0 ? args.repeat + 1 : undefined - }; - return { - commandId: 'moveActiveEditor', - args: moveActiveEditorArgs - }; - } -} - -class MoveActiveEditorCommand extends AbstractCommandDescriptor { - - constructor(private to: string) { - super(); - } - - public createCommand(args?: any): Command { - const moveActiveEditorArgs: any = { - to: this.to, - value: args.repeat ? args.repeat : 1 - }; - return { - commandId: 'moveActiveEditor', - args: moveActiveEditorArgs - }; - } -} -class FoldCommand extends AbstractCommandDescriptor { - - constructor() { - super(); - } - - public createCommand(args?: any): Command { - const foldEditorArgs: any = { - levels: args.repeat ? args.repeat : 1, - direction: 'up' - }; - return { - commandId: 'editor.fold', - args: foldEditorArgs - }; - } -} - -class UnfoldCommand extends AbstractCommandDescriptor { - - constructor() { - super(); - } - - public createCommand(args?: any): Command { - const foldEditorArgs: any = { - levels: args.repeat ? args.repeat : 1, - direction: 'up' - }; - return { - commandId: 'editor.unfold', - args: foldEditorArgs - }; - } -} - -export const Motions = { - RightMotion: new RightMotion(), - - NextCharacter: new NextCharacterMotion(), - - Left: new CursorMoveCommand('left'), - Right: new CursorMoveCommand('right'), - Down: new CursorMoveCommand('down'), - Up: new CursorMoveCommand('up'), - - EndOfLine: new EndOfLineMotion(), - StartOfLine: new StartOfLineMotion(), - NextWordStart: new NextWordStartMotion(), - NextWordEnd: new NextWordEndMotion(), - GoToLine: new GoToLineUndefinedMotion(), - GoToFirstLine: new GoToFirstLineMotion(), - GoToLastLine: new GoToLastLineMotion(), - - CursorScrollLeft: new CursorMoveCommand('left'), - CursorScrollRight: new CursorMoveCommand('right'), - CursorScrollLeftByHalfLine: new CursorMoveCommand('left', 'halfLine'), - CursorScrollRightByHalfLine: new CursorMoveCommand('right', 'halfLine'), - - WrappedLineUp: new CursorMoveCommand('up', 'wrappedLine'), - WrappedLineDown: new CursorMoveCommand('down', 'wrappedLine'), - - WrappedLineStart: new CursorMoveCommand('wrappedLineStart'), - WrappedLineFirstNonWhiteSpaceCharacter: new CursorMoveCommand('wrappedLineFirstNonWhitespaceCharacter'), - WrappedLineColumnCenter: new CursorMoveCommand('wrappedLineColumnCenter'), - WrappedLineEnd: new CursorMoveCommand('wrappedLineEnd'), - WrappedLineLastNonWhiteSpaceCharacter: new CursorMoveCommand('wrappedLineLastNonWhitespaceCharacter'), - - ViewPortTop: new CursorMoveCommand('viewPortTop'), - ViewPortBottom: new CursorMoveCommand('viewPortBottom'), - ViewPortCenter: new CursorMoveCommand('viewPortCenter'), - - MoveActiveEditor: new MoveActiveEditorCommandByPosition(), - MoveActiveEditorLeft: new MoveActiveEditorCommand('left'), - MoveActiveEditorRight: new MoveActiveEditorCommand('right'), - MoveActiveEditorFirst: new MoveActiveEditorCommand('first'), - MoveActiveEditorLast: new MoveActiveEditorCommand('last'), - MoveActiveEditorCenter: new MoveActiveEditorCommand('center'), - - ScrollDownByLine: new EditorScrollCommand('down', 'line'), - ScrollDownByHalfPage: new EditorScrollCommand('down', 'halfPage'), - ScrollDownByPage: new EditorScrollCommand('down', 'page'), - ScrollUpByLine: new EditorScrollCommand('up', 'line'), - ScrollUpByHalfPage: new EditorScrollCommand('up', 'halfPage'), - ScrollUpByPage: new EditorScrollCommand('up', 'page'), - - RevealCurrentLineAtTop: new RevealCurrentLineCommand('top'), - RevealCurrentLineAtCenter: new RevealCurrentLineCommand('center'), - RevealCurrentLineAtBottom: new RevealCurrentLineCommand('bottom'), - - FoldUnder: new FoldCommand(), - UnfoldUnder: new UnfoldCommand() -}; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Position, TextDocument, window } from 'vscode'; +import { Words, WordCharacters } from './words'; +import { Command, AbstractCommandDescriptor } from './common'; + +export class MotionState { + + public anchor: Position | null; + public cursorDesiredCharacter: number; + public wordCharacterClass: WordCharacters | null; + + constructor() { + this.cursorDesiredCharacter = -1; + this.wordCharacterClass = null; + this.anchor = null; + } + +} + +export abstract class Motion { + public abstract run(doc: TextDocument, pos: Position, state: MotionState): Position; + + public repeat(hasRepeatCount: boolean, count: number): Motion { + if (!hasRepeatCount) { + return this; + } + return new RepeatingMotion(this, count); + } +} + +class RepeatingMotion extends Motion { + + private _actual: Motion; + private _repeatCount: number; + + constructor(actual: Motion, repeatCount: number) { + super(); + this._actual = actual; + this._repeatCount = repeatCount; + } + + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + for (let cnt = 0; cnt < this._repeatCount; cnt++) { + pos = this._actual.run(doc, pos, state); + } + return pos; + } +} + +class NextCharacterMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + if (pos.character === doc.lineAt(pos.line).text.length) { + // on last character + return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos); + } + + return new Position(pos.line, pos.character + 1); + } +} + +class LeftMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + const line = pos.line; + + if (pos.character > 0) { + state.cursorDesiredCharacter = pos.character - 1; + return new Position(line, state.cursorDesiredCharacter); + } + + return pos; + } +} + +class DownMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + let line = pos.line; + + state.cursorDesiredCharacter = (state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter); + + if (line < doc.lineCount - 1) { + line++; + return new Position(line, Math.min(state.cursorDesiredCharacter, doc.lineAt(line).text.length)); + } + + return pos; + } +} + +class UpMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + let line = pos.line; + + state.cursorDesiredCharacter = (state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter); + + if (line > 0) { + line--; + return new Position(line, Math.min(state.cursorDesiredCharacter, doc.lineAt(line).text.length)); + } + + return pos; + } +} + +class RightMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + const line = pos.line; + const maxCharacter = doc.lineAt(line).text.length; + + if (pos.character < maxCharacter) { + state.cursorDesiredCharacter = pos.character + 1; + return new Position(line, state.cursorDesiredCharacter); + } + + return pos; + } +} + +class EndOfLineMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + return new Position(pos.line, doc.lineAt(pos.line).text.length); + } +} + +class StartOfLineMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + return new Position(pos.line, 0); + } +} + +class NextWordStartMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + const lineContent = doc.lineAt(pos.line).text; + + if (pos.character >= lineContent.length - 1) { + // cursor at end of line + return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos); + } + + const nextWord = Words.findNextWord(doc, pos, state.wordCharacterClass); + + if (!nextWord) { + // return end of the line + return Motions.EndOfLine.run(doc, pos, state); + } + + if (nextWord.start <= pos.character && pos.character < nextWord.end) { + // Sitting on a word + const nextNextWord = Words.findNextWord(doc, new Position(pos.line, nextWord.end), state.wordCharacterClass); + if (nextNextWord) { + // return start of the next next word + return new Position(pos.line, nextNextWord.start); + } else { + // return end of line + return Motions.EndOfLine.run(doc, pos, state); + } + } else { + // return start of the next word + return new Position(pos.line, nextWord.start); + } + } +} + +class NextWordEndMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + const lineContent = doc.lineAt(pos.line).text; + + if (pos.character >= lineContent.length - 1) { + // no content on this line or cursor at end of line + return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos); + } + + const nextWord = Words.findNextWord(doc, pos, state.wordCharacterClass); + + if (!nextWord) { + // return end of the line + return Motions.EndOfLine.run(doc, pos, state); + } + + // return start of the next word + return new Position(pos.line, nextWord.end); + } +} + +class GoToLineUndefinedMotion extends Motion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + // does not do anything + return pos; + } + + public repeat(hasRepeatCount: boolean, count: number): Motion { + if (!hasRepeatCount) { + return Motions.GoToLastLine; + } + return new GoToLineDefinedMotion(count); + } +} + +abstract class GoToLineMotion extends Motion { + + protected firstNonWhitespaceChar(doc: TextDocument, line: number): number { + const lineContent = doc.lineAt(line).text; + let character = 0; + while (character < lineContent.length) { + const ch = lineContent.charAt(character); + if (ch !== ' ' && ch !== '\t') { + break; + } + character++; + } + return character; + } + +} + +class GoToFirstLineMotion extends GoToLineMotion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + return new Position(0, this.firstNonWhitespaceChar(doc, 0)); + } +} + +class GoToLastLineMotion extends GoToLineMotion { + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + const lastLine = doc.lineCount - 1; + return new Position(lastLine, this.firstNonWhitespaceChar(doc, lastLine)); + } +} + +class GoToLineDefinedMotion extends GoToLineMotion { + private _lineNumber: number; + + constructor(lineNumber: number) { + super(); + this._lineNumber = lineNumber; + } + + public run(doc: TextDocument, pos: Position, state: MotionState): Position { + const line = Math.min(doc.lineCount - 1, Math.max(0, this._lineNumber - 1)); + return new Position(line, this.firstNonWhitespaceChar(doc, line)); + } +} + +class CursorMoveCommand extends AbstractCommandDescriptor { + + constructor(private to: string, private by?: string) { + super(); + } + + public createCommand(args?: any): Command { + const cursorMoveArgs: any = { + to: this.to, + by: this.by, + value: args.repeat || 1, + select: !!args.isVisual + }; + return { + commandId: 'cursorMove', + args: cursorMoveArgs + }; + } +} + +class EditorScrollCommand extends AbstractCommandDescriptor { + + constructor(private to: string, private by?: string) { + super(); + } + + public createCommand(args?: any): Command { + const editorScrollArgs: any = { + to: this.to, + by: this.by, + value: args.repeat || 1, + revealCursor: true + }; + return { + commandId: 'editorScroll', + args: editorScrollArgs + }; + } +} + +class RevealCurrentLineCommand extends AbstractCommandDescriptor { + + constructor(private at: string) { + super(); + } + + public createCommand(args?: any): Command { + const lineNumber = window.activeTextEditor.selection.start.line; + const revealLineArgs: any = { + lineNumber, + at: this.at + }; + return { + commandId: 'revealLine', + args: revealLineArgs + }; + } +} + +class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor { + + constructor() { + super(); + } + + public createCommand(args?: any): Command { + const moveActiveEditorArgs: any = { + to: args.repeat === void 0 ? 'last' : 'position', + value: args.repeat !== void 0 ? args.repeat + 1 : undefined + }; + return { + commandId: 'moveActiveEditor', + args: moveActiveEditorArgs + }; + } +} + +class MoveActiveEditorCommand extends AbstractCommandDescriptor { + + constructor(private to: string) { + super(); + } + + public createCommand(args?: any): Command { + const moveActiveEditorArgs: any = { + to: this.to, + value: args.repeat ? args.repeat : 1 + }; + return { + commandId: 'moveActiveEditor', + args: moveActiveEditorArgs + }; + } +} +class FoldCommand extends AbstractCommandDescriptor { + + constructor() { + super(); + } + + public createCommand(args?: any): Command { + const foldEditorArgs: any = { + levels: args.repeat ? args.repeat : 1, + direction: 'up' + }; + return { + commandId: 'editor.fold', + args: foldEditorArgs + }; + } +} + +class UnfoldCommand extends AbstractCommandDescriptor { + + constructor() { + super(); + } + + public createCommand(args?: any): Command { + const foldEditorArgs: any = { + levels: args.repeat ? args.repeat : 1, + direction: 'up' + }; + return { + commandId: 'editor.unfold', + args: foldEditorArgs + }; + } +} + +export const Motions = { + RightMotion: new RightMotion(), + + NextCharacter: new NextCharacterMotion(), + + Left: new CursorMoveCommand('left'), + Right: new CursorMoveCommand('right'), + Down: new CursorMoveCommand('down'), + Up: new CursorMoveCommand('up'), + + EndOfLine: new EndOfLineMotion(), + StartOfLine: new StartOfLineMotion(), + NextWordStart: new NextWordStartMotion(), + NextWordEnd: new NextWordEndMotion(), + GoToLine: new GoToLineUndefinedMotion(), + GoToFirstLine: new GoToFirstLineMotion(), + GoToLastLine: new GoToLastLineMotion(), + + CursorScrollLeft: new CursorMoveCommand('left'), + CursorScrollRight: new CursorMoveCommand('right'), + CursorScrollLeftByHalfLine: new CursorMoveCommand('left', 'halfLine'), + CursorScrollRightByHalfLine: new CursorMoveCommand('right', 'halfLine'), + + WrappedLineUp: new CursorMoveCommand('up', 'wrappedLine'), + WrappedLineDown: new CursorMoveCommand('down', 'wrappedLine'), + + WrappedLineStart: new CursorMoveCommand('wrappedLineStart'), + WrappedLineFirstNonWhiteSpaceCharacter: new CursorMoveCommand('wrappedLineFirstNonWhitespaceCharacter'), + WrappedLineColumnCenter: new CursorMoveCommand('wrappedLineColumnCenter'), + WrappedLineEnd: new CursorMoveCommand('wrappedLineEnd'), + WrappedLineLastNonWhiteSpaceCharacter: new CursorMoveCommand('wrappedLineLastNonWhitespaceCharacter'), + + ViewPortTop: new CursorMoveCommand('viewPortTop'), + ViewPortBottom: new CursorMoveCommand('viewPortBottom'), + ViewPortCenter: new CursorMoveCommand('viewPortCenter'), + + MoveActiveEditor: new MoveActiveEditorCommandByPosition(), + MoveActiveEditorLeft: new MoveActiveEditorCommand('left'), + MoveActiveEditorRight: new MoveActiveEditorCommand('right'), + MoveActiveEditorFirst: new MoveActiveEditorCommand('first'), + MoveActiveEditorLast: new MoveActiveEditorCommand('last'), + MoveActiveEditorCenter: new MoveActiveEditorCommand('center'), + + ScrollDownByLine: new EditorScrollCommand('down', 'line'), + ScrollDownByHalfPage: new EditorScrollCommand('down', 'halfPage'), + ScrollDownByPage: new EditorScrollCommand('down', 'page'), + ScrollUpByLine: new EditorScrollCommand('up', 'line'), + ScrollUpByHalfPage: new EditorScrollCommand('up', 'halfPage'), + ScrollUpByPage: new EditorScrollCommand('up', 'page'), + + RevealCurrentLineAtTop: new RevealCurrentLineCommand('top'), + RevealCurrentLineAtCenter: new RevealCurrentLineCommand('center'), + RevealCurrentLineAtBottom: new RevealCurrentLineCommand('bottom'), + + FoldUnder: new FoldCommand(), + UnfoldUnder: new UnfoldCommand() +}; diff --git a/vim-sample/src/operators.ts b/vim-sample/src/operators.ts index 08c531fb..22ec46d1 100644 --- a/vim-sample/src/operators.ts +++ b/vim-sample/src/operators.ts @@ -1,343 +1,343 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Position, Selection, Range, TextDocument, TextEditor, TextEditorRevealType } from 'vscode'; -import { Motion, Motions } from './motions'; -import { Mode, IController, DeleteRegister } from './common'; - -export abstract class Operator { - - public abstract runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean; - public abstract runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean; - - protected doc(ed: TextEditor): TextDocument { - return ed.document; - } - - protected pos(ed: TextEditor): Position { - return ed.selection.active; - } - - protected sel(ed: TextEditor): Selection { - return ed.selection; - } - - protected setPosReveal(ed: TextEditor, line: number, char: number): void { - ed.selection = new Selection(new Position(line, char), new Position(line, char)); - ed.revealRange(ed.selection, TextEditorRevealType.Default); - } - - protected delete(ctrl: IController, ed: TextEditor, isWholeLine: boolean, range: Range): void { - ctrl.setDeleteRegister(new DeleteRegister(isWholeLine, ed.document.getText(range))); - ed.edit((builder) => { - builder.delete(range); - }); - } -} - -abstract class OperatorWithNoArgs extends Operator { - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - this._run(ctrl, ed); - return true; - } - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - this._run(ctrl, ed); - return true; - } - protected abstract _run(ctrl: IController, ed: TextEditor): void; -} - -class InsertOperator extends OperatorWithNoArgs { - protected _run(ctrl: IController, ed: TextEditor): void { - ctrl.setMode(Mode.INSERT); - } -} - -class AppendOperator extends OperatorWithNoArgs { - protected _run(ctrl: IController, ed: TextEditor): void { - const newPos = Motions.RightMotion.run(this.doc(ed), this.pos(ed), ctrl.motionState); - this.setPosReveal(ed, newPos.line, newPos.character); - ctrl.setMode(Mode.INSERT); - } -} - -class AppendEndOfLineOperator extends OperatorWithNoArgs { - protected _run(ctrl: IController, ed: TextEditor): void { - const newPos = Motions.EndOfLine.run(this.doc(ed), this.pos(ed), ctrl.motionState); - this.setPosReveal(ed, newPos.line, newPos.character); - ctrl.setMode(Mode.INSERT); - } -} - -class VisualOperator extends OperatorWithNoArgs { - protected _run(ctrl: IController, ed: TextEditor): void { - ctrl.motionState.anchor = this.pos(ed); - ctrl.setVisual(true); - } -} - -class DeleteCharUnderCursorOperator extends Operator { - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - const to = Motions.NextCharacter.repeat(repeatCount > 1, repeatCount).run(this.doc(ed), this.pos(ed), ctrl.motionState); - const from = this.pos(ed); - - this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character)); - - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - const sel = this.sel(ed); - this.delete(ctrl, ed, false, sel); - return true; - } -} - -class DeleteLineOperator extends Operator { - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - const pos = this.pos(ed); - const doc = this.doc(ed); - - let fromLine = pos.line; - let fromCharacter = 0; - - let toLine = fromLine + repeatCount; - let toCharacter = 0; - - if (toLine >= doc.lineCount - 1) { - // Deleting last line - toLine = doc.lineCount - 1; - toCharacter = doc.lineAt(toLine).text.length; - - if (fromLine > 0) { - fromLine = fromLine - 1; - fromCharacter = doc.lineAt(fromLine).text.length; - } - } - - this.delete(ctrl, ed, true, new Range(fromLine, fromCharacter, toLine, toCharacter)); - - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - const sel = this.sel(ed); - this.delete(ctrl, ed, false, sel); - return true; - } -} - -abstract class OperatorWithMotion extends Operator { - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - const motion = ctrl.findMotion(args); - if (!motion) { - - // is it motion building - if (ctrl.isMotionPrefix(args)) { - return false; - } - - // INVALID INPUT - beep!! - return true; - } - - return this._runNormalMode(ctrl, ed, motion.repeat(repeatCount > 1, repeatCount)); - } - - protected abstract _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean; -} - -class DeleteToOperator extends OperatorWithMotion { - - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - if (args === 'd') { - // dd - return Operators.DeleteLine.runNormalMode(ctrl, ed, repeatCount, args); - } - return super.runNormalMode(ctrl, ed, repeatCount, args); - } - - protected _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean { - const to = motion.run(this.doc(ed), this.pos(ed), ctrl.motionState); - const from = this.pos(ed); - - this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character)); - - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - const sel = this.sel(ed); - this.delete(ctrl, ed, false, sel); - return true; - } -} - -class PutOperator extends Operator { - - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - const register = ctrl.getDeleteRegister(); - if (!register) { - // No delete register - beep!! - return true; - } - - let str = repeatString(register.content, repeatCount); - - const pos = this.pos(ed); - if (!register.isWholeLine) { - ed.edit((builder) => { - builder.insert(new Position(pos.line, pos.character + 1), str); - }); - return true; - } - - const doc = this.doc(ed); - let insertLine = pos.line + 1; - let insertCharacter = 0; - - if (insertLine >= doc.lineCount) { - // on last line - insertLine = doc.lineCount - 1; - insertCharacter = doc.lineAt(insertLine).text.length; - str = '\n' + str; - } - - ed.edit((builder) => { - builder.insert(new Position(insertLine, insertCharacter), str); - }); - - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - const register = ctrl.getDeleteRegister(); - if (!register) { - // No delete register - beep!! - return false; - } - - const str = register.content; - - const sel = this.sel(ed); - ed.edit((builder) => { - builder.replace(sel, str); - }); - - return true; - } -} - -class ReplaceOperator extends Operator { - - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - if (args.length === 0) { - // input not ready - return false; - } - - const doc = this.doc(ed); - const pos = this.pos(ed); - const toCharacter = pos.character + repeatCount; - if (toCharacter > doc.lineAt(pos).text.length) { - // invalid replace (beep!) - return true; - } - - ed.edit((builder) => { - builder.replace(new Range(pos.line, pos.character, pos.line, toCharacter), repeatString(args, repeatCount)); - }); - - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - if (args.length === 0) { - // input not ready - return false; - } - - const doc = this.doc(ed); - const sel = this.sel(ed); - - const srcString = doc.getText(sel); - let dstString = ''; - for (let i = 0; i < srcString.length; i++) { - const ch = srcString.charAt(i); - if (ch === '\r' || ch === '\n') { - dstString += ch; - } else { - dstString += args; - } - } - - ed.edit((builder) => { - builder.replace(sel, dstString); - }); - - return true; - } -} - -class ReplaceModeOperator extends Operator { - - public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { - ctrl.setMode(Mode.REPLACE); - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - this.delete(ctrl, ed, false, this.sel(ed)); - ctrl.setMode(Mode.INSERT); - return true; - } - -} - -class ChangeOperator extends OperatorWithMotion { - - protected _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean { - const to = motion.run(this.doc(ed), this.pos(ed), ctrl.motionState); - const from = this.pos(ed); - - this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character)); - - ctrl.setMode(Mode.INSERT); - - return true; - } - - public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { - const sel = this.sel(ed); - - this.delete(ctrl, ed, false, sel); - - ctrl.setMode(Mode.INSERT); - - return true; - } -} - -function repeatString(str: string, repeatCount: number): string { - let result = ''; - for (let i = 0; i < repeatCount; i++) { - result += str; - } - return result; -} - -export const Operators = { - Insert: new InsertOperator(), - Visual: new VisualOperator(), - Append: new AppendOperator(), - AppendEndOfLine: new AppendEndOfLineOperator(), - DeleteCharUnderCursor: new DeleteCharUnderCursorOperator(), - DeleteTo: new DeleteToOperator(), - DeleteLine: new DeleteLineOperator(), - Put: new PutOperator(), - Replace: new ReplaceOperator(), - Change: new ChangeOperator(), - ReplaceMode: new ReplaceModeOperator(), -}; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Position, Selection, Range, TextDocument, TextEditor, TextEditorRevealType } from 'vscode'; +import { Motion, Motions } from './motions'; +import { Mode, IController, DeleteRegister } from './common'; + +export abstract class Operator { + + public abstract runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean; + public abstract runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean; + + protected doc(ed: TextEditor): TextDocument { + return ed.document; + } + + protected pos(ed: TextEditor): Position { + return ed.selection.active; + } + + protected sel(ed: TextEditor): Selection { + return ed.selection; + } + + protected setPosReveal(ed: TextEditor, line: number, char: number): void { + ed.selection = new Selection(new Position(line, char), new Position(line, char)); + ed.revealRange(ed.selection, TextEditorRevealType.Default); + } + + protected delete(ctrl: IController, ed: TextEditor, isWholeLine: boolean, range: Range): void { + ctrl.setDeleteRegister(new DeleteRegister(isWholeLine, ed.document.getText(range))); + ed.edit((builder) => { + builder.delete(range); + }); + } +} + +abstract class OperatorWithNoArgs extends Operator { + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + this._run(ctrl, ed); + return true; + } + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + this._run(ctrl, ed); + return true; + } + protected abstract _run(ctrl: IController, ed: TextEditor): void; +} + +class InsertOperator extends OperatorWithNoArgs { + protected _run(ctrl: IController, ed: TextEditor): void { + ctrl.setMode(Mode.INSERT); + } +} + +class AppendOperator extends OperatorWithNoArgs { + protected _run(ctrl: IController, ed: TextEditor): void { + const newPos = Motions.RightMotion.run(this.doc(ed), this.pos(ed), ctrl.motionState); + this.setPosReveal(ed, newPos.line, newPos.character); + ctrl.setMode(Mode.INSERT); + } +} + +class AppendEndOfLineOperator extends OperatorWithNoArgs { + protected _run(ctrl: IController, ed: TextEditor): void { + const newPos = Motions.EndOfLine.run(this.doc(ed), this.pos(ed), ctrl.motionState); + this.setPosReveal(ed, newPos.line, newPos.character); + ctrl.setMode(Mode.INSERT); + } +} + +class VisualOperator extends OperatorWithNoArgs { + protected _run(ctrl: IController, ed: TextEditor): void { + ctrl.motionState.anchor = this.pos(ed); + ctrl.setVisual(true); + } +} + +class DeleteCharUnderCursorOperator extends Operator { + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + const to = Motions.NextCharacter.repeat(repeatCount > 1, repeatCount).run(this.doc(ed), this.pos(ed), ctrl.motionState); + const from = this.pos(ed); + + this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character)); + + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + const sel = this.sel(ed); + this.delete(ctrl, ed, false, sel); + return true; + } +} + +class DeleteLineOperator extends Operator { + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + const pos = this.pos(ed); + const doc = this.doc(ed); + + let fromLine = pos.line; + let fromCharacter = 0; + + let toLine = fromLine + repeatCount; + let toCharacter = 0; + + if (toLine >= doc.lineCount - 1) { + // Deleting last line + toLine = doc.lineCount - 1; + toCharacter = doc.lineAt(toLine).text.length; + + if (fromLine > 0) { + fromLine = fromLine - 1; + fromCharacter = doc.lineAt(fromLine).text.length; + } + } + + this.delete(ctrl, ed, true, new Range(fromLine, fromCharacter, toLine, toCharacter)); + + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + const sel = this.sel(ed); + this.delete(ctrl, ed, false, sel); + return true; + } +} + +abstract class OperatorWithMotion extends Operator { + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + const motion = ctrl.findMotion(args); + if (!motion) { + + // is it motion building + if (ctrl.isMotionPrefix(args)) { + return false; + } + + // INVALID INPUT - beep!! + return true; + } + + return this._runNormalMode(ctrl, ed, motion.repeat(repeatCount > 1, repeatCount)); + } + + protected abstract _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean; +} + +class DeleteToOperator extends OperatorWithMotion { + + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + if (args === 'd') { + // dd + return Operators.DeleteLine.runNormalMode(ctrl, ed, repeatCount, args); + } + return super.runNormalMode(ctrl, ed, repeatCount, args); + } + + protected _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean { + const to = motion.run(this.doc(ed), this.pos(ed), ctrl.motionState); + const from = this.pos(ed); + + this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character)); + + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + const sel = this.sel(ed); + this.delete(ctrl, ed, false, sel); + return true; + } +} + +class PutOperator extends Operator { + + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + const register = ctrl.getDeleteRegister(); + if (!register) { + // No delete register - beep!! + return true; + } + + let str = repeatString(register.content, repeatCount); + + const pos = this.pos(ed); + if (!register.isWholeLine) { + ed.edit((builder) => { + builder.insert(new Position(pos.line, pos.character + 1), str); + }); + return true; + } + + const doc = this.doc(ed); + let insertLine = pos.line + 1; + let insertCharacter = 0; + + if (insertLine >= doc.lineCount) { + // on last line + insertLine = doc.lineCount - 1; + insertCharacter = doc.lineAt(insertLine).text.length; + str = '\n' + str; + } + + ed.edit((builder) => { + builder.insert(new Position(insertLine, insertCharacter), str); + }); + + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + const register = ctrl.getDeleteRegister(); + if (!register) { + // No delete register - beep!! + return false; + } + + const str = register.content; + + const sel = this.sel(ed); + ed.edit((builder) => { + builder.replace(sel, str); + }); + + return true; + } +} + +class ReplaceOperator extends Operator { + + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + if (args.length === 0) { + // input not ready + return false; + } + + const doc = this.doc(ed); + const pos = this.pos(ed); + const toCharacter = pos.character + repeatCount; + if (toCharacter > doc.lineAt(pos).text.length) { + // invalid replace (beep!) + return true; + } + + ed.edit((builder) => { + builder.replace(new Range(pos.line, pos.character, pos.line, toCharacter), repeatString(args, repeatCount)); + }); + + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + if (args.length === 0) { + // input not ready + return false; + } + + const doc = this.doc(ed); + const sel = this.sel(ed); + + const srcString = doc.getText(sel); + let dstString = ''; + for (let i = 0; i < srcString.length; i++) { + const ch = srcString.charAt(i); + if (ch === '\r' || ch === '\n') { + dstString += ch; + } else { + dstString += args; + } + } + + ed.edit((builder) => { + builder.replace(sel, dstString); + }); + + return true; + } +} + +class ReplaceModeOperator extends Operator { + + public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean { + ctrl.setMode(Mode.REPLACE); + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + this.delete(ctrl, ed, false, this.sel(ed)); + ctrl.setMode(Mode.INSERT); + return true; + } + +} + +class ChangeOperator extends OperatorWithMotion { + + protected _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean { + const to = motion.run(this.doc(ed), this.pos(ed), ctrl.motionState); + const from = this.pos(ed); + + this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character)); + + ctrl.setMode(Mode.INSERT); + + return true; + } + + public runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean { + const sel = this.sel(ed); + + this.delete(ctrl, ed, false, sel); + + ctrl.setMode(Mode.INSERT); + + return true; + } +} + +function repeatString(str: string, repeatCount: number): string { + let result = ''; + for (let i = 0; i < repeatCount; i++) { + result += str; + } + return result; +} + +export const Operators = { + Insert: new InsertOperator(), + Visual: new VisualOperator(), + Append: new AppendOperator(), + AppendEndOfLine: new AppendEndOfLineOperator(), + DeleteCharUnderCursor: new DeleteCharUnderCursorOperator(), + DeleteTo: new DeleteToOperator(), + DeleteLine: new DeleteLineOperator(), + Put: new PutOperator(), + Replace: new ReplaceOperator(), + Change: new ChangeOperator(), + ReplaceMode: new ReplaceModeOperator(), +}; diff --git a/vim-sample/src/words.ts b/vim-sample/src/words.ts index edca3a31..78cfce9e 100644 --- a/vim-sample/src/words.ts +++ b/vim-sample/src/words.ts @@ -1,104 +1,104 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Position, TextDocument } from 'vscode'; - -export enum CharacterClass { - REGULAR = 0, - WORD_SEPARATOR = 1, - WHITESPACE = 2 -} - -export enum WordType { - NONE = 0, - SEPARATOR = 1, - REGULAR = 2 -} - -export type WordCharacters = CharacterClass[]; - -export interface IWord { - start: number; - end: number; - wordType: WordType; -} - -export class Words { - - public static createWordCharacters(wordSeparators: string): WordCharacters { - const result: CharacterClass[] = []; - - // Make array fast for ASCII text - for (let chCode = 0; chCode < 256; chCode++) { - result[chCode] = CharacterClass.REGULAR; - } - - for (let i = 0, len = wordSeparators.length; i < len; i++) { - result[wordSeparators.charCodeAt(i)] = CharacterClass.WORD_SEPARATOR; - } - - result[' '.charCodeAt(0)] = CharacterClass.WHITESPACE; - result['\t'.charCodeAt(0)] = CharacterClass.WHITESPACE; - - return result; - } - - public static findNextWord(doc: TextDocument, pos: Position, wordCharacterClass: WordCharacters): IWord | null { - - const lineContent = doc.lineAt(pos.line).text; - let wordType = WordType.NONE; - const len = lineContent.length; - - for (let chIndex = pos.character; chIndex < len; chIndex++) { - const chCode = lineContent.charCodeAt(chIndex); - const chClass = (wordCharacterClass[chCode] || CharacterClass.REGULAR); - - if (chClass === CharacterClass.REGULAR) { - if (wordType === WordType.SEPARATOR) { - return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex); - } - wordType = WordType.REGULAR; - } else if (chClass === CharacterClass.WORD_SEPARATOR) { - if (wordType === WordType.REGULAR) { - return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex); - } - wordType = WordType.SEPARATOR; - } else if (chClass === CharacterClass.WHITESPACE) { - if (wordType !== WordType.NONE) { - return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex); - } - } - } - - if (wordType !== WordType.NONE) { - return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, len - 1), len); - } - - return null; - } - - private static _findStartOfWord(lineContent: string, wordCharacterClass: WordCharacters, wordType: WordType, startIndex: number): number { - for (let chIndex = startIndex; chIndex >= 0; chIndex--) { - const chCode = lineContent.charCodeAt(chIndex); - const chClass = (wordCharacterClass[chCode] || CharacterClass.REGULAR); - - if (chClass === CharacterClass.WHITESPACE) { - return chIndex + 1; - } - if (wordType === WordType.REGULAR && chClass === CharacterClass.WORD_SEPARATOR) { - return chIndex + 1; - } - if (wordType === WordType.SEPARATOR && chClass === CharacterClass.REGULAR) { - return chIndex + 1; - } - } - return 0; - } - - private static _createWord(lineContent: string, wordType: WordType, start: number, end: number): IWord { - // console.log('WORD ==> ' + start + ' => ' + end + ':::: <<<' + lineContent.substring(start, end) + '>>>'); - return { start: start, end: end, wordType: wordType }; - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Position, TextDocument } from 'vscode'; + +export enum CharacterClass { + REGULAR = 0, + WORD_SEPARATOR = 1, + WHITESPACE = 2 +} + +export enum WordType { + NONE = 0, + SEPARATOR = 1, + REGULAR = 2 +} + +export type WordCharacters = CharacterClass[]; + +export interface IWord { + start: number; + end: number; + wordType: WordType; +} + +export class Words { + + public static createWordCharacters(wordSeparators: string): WordCharacters { + const result: CharacterClass[] = []; + + // Make array fast for ASCII text + for (let chCode = 0; chCode < 256; chCode++) { + result[chCode] = CharacterClass.REGULAR; + } + + for (let i = 0, len = wordSeparators.length; i < len; i++) { + result[wordSeparators.charCodeAt(i)] = CharacterClass.WORD_SEPARATOR; + } + + result[' '.charCodeAt(0)] = CharacterClass.WHITESPACE; + result['\t'.charCodeAt(0)] = CharacterClass.WHITESPACE; + + return result; + } + + public static findNextWord(doc: TextDocument, pos: Position, wordCharacterClass: WordCharacters): IWord | null { + + const lineContent = doc.lineAt(pos.line).text; + let wordType = WordType.NONE; + const len = lineContent.length; + + for (let chIndex = pos.character; chIndex < len; chIndex++) { + const chCode = lineContent.charCodeAt(chIndex); + const chClass = (wordCharacterClass[chCode] || CharacterClass.REGULAR); + + if (chClass === CharacterClass.REGULAR) { + if (wordType === WordType.SEPARATOR) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex); + } + wordType = WordType.REGULAR; + } else if (chClass === CharacterClass.WORD_SEPARATOR) { + if (wordType === WordType.REGULAR) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex); + } + wordType = WordType.SEPARATOR; + } else if (chClass === CharacterClass.WHITESPACE) { + if (wordType !== WordType.NONE) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex); + } + } + } + + if (wordType !== WordType.NONE) { + return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, len - 1), len); + } + + return null; + } + + private static _findStartOfWord(lineContent: string, wordCharacterClass: WordCharacters, wordType: WordType, startIndex: number): number { + for (let chIndex = startIndex; chIndex >= 0; chIndex--) { + const chCode = lineContent.charCodeAt(chIndex); + const chClass = (wordCharacterClass[chCode] || CharacterClass.REGULAR); + + if (chClass === CharacterClass.WHITESPACE) { + return chIndex + 1; + } + if (wordType === WordType.REGULAR && chClass === CharacterClass.WORD_SEPARATOR) { + return chIndex + 1; + } + if (wordType === WordType.SEPARATOR && chClass === CharacterClass.REGULAR) { + return chIndex + 1; + } + } + return 0; + } + + private static _createWord(lineContent: string, wordType: WordType, start: number, end: number): IWord { + // console.log('WORD ==> ' + start + ' => ' + end + ':::: <<<' + lineContent.substring(start, end) + '>>>'); + return { start: start, end: end, wordType: wordType }; + } +} diff --git a/virtual-document-sample/src/cowsay.d.ts b/virtual-document-sample/src/cowsay.d.ts index 294105d8..fcc08b98 100644 --- a/virtual-document-sample/src/cowsay.d.ts +++ b/virtual-document-sample/src/cowsay.d.ts @@ -1,14 +1,14 @@ -declare module 'cowsay' { - - export interface CowsayOptions { - text: string; - cow?: string; - eyes?: string; - tongue?: string; - wrap?: boolean; - wrapLength?: number; - mode?: 'b' | 'd' | 'g' | 'p' | 's' | 't' | 'w' | 'y' - } - - export function say(options: CowsayOptions): string; -} +declare module 'cowsay' { + + export interface CowsayOptions { + text: string; + cow?: string; + eyes?: string; + tongue?: string; + wrap?: boolean; + wrapLength?: number; + mode?: 'b' | 'd' | 'g' | 'p' | 's' | 't' | 'w' | 'y' + } + + export function say(options: CowsayOptions): string; +} diff --git a/virtual-document-sample/src/extension.ts b/virtual-document-sample/src/extension.ts index 803cf97d..99e6ed5d 100644 --- a/virtual-document-sample/src/extension.ts +++ b/virtual-document-sample/src/extension.ts @@ -1,50 +1,50 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as cowsay from 'cowsay'; - -export function activate({ subscriptions }: vscode.ExtensionContext) { - - // register a content provider for the cowsay-scheme - const myScheme = 'cowsay'; - const myProvider = new class implements vscode.TextDocumentContentProvider { - - // emitter and its event - onDidChangeEmitter = new vscode.EventEmitter(); - onDidChange = this.onDidChangeEmitter.event; - - provideTextDocumentContent(uri: vscode.Uri): string { - // simply invoke cowsay, use uri-path as text - return cowsay.say({ text: uri.path }); - } - }; - subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(myScheme, myProvider)); - - // register a command that opens a cowsay-document - subscriptions.push(vscode.commands.registerCommand('cowsay.say', async () => { - const what = await vscode.window.showInputBox({ placeHolder: 'cowsay...' }); - if (what) { - const uri = vscode.Uri.parse('cowsay:' + what); - const doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider - await vscode.window.showTextDocument(doc, { preview: false }); - } - })); - - // register a command that updates the current cowsay - subscriptions.push(vscode.commands.registerCommand('cowsay.backwards', async () => { - if (!vscode.window.activeTextEditor) { - return; // no editor - } - const { document } = vscode.window.activeTextEditor; - if (document.uri.scheme !== myScheme) { - return; // not my scheme - } - // get path-components, reverse it, and create a new uri - const say = document.uri.path; - const newSay = say.split('').reverse().join(''); - const newUri = document.uri.with({ path: newSay }); - await vscode.window.showTextDocument(newUri, { preview: false }); - })); -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as cowsay from 'cowsay'; + +export function activate({ subscriptions }: vscode.ExtensionContext) { + + // register a content provider for the cowsay-scheme + const myScheme = 'cowsay'; + const myProvider = new class implements vscode.TextDocumentContentProvider { + + // emitter and its event + onDidChangeEmitter = new vscode.EventEmitter(); + onDidChange = this.onDidChangeEmitter.event; + + provideTextDocumentContent(uri: vscode.Uri): string { + // simply invoke cowsay, use uri-path as text + return cowsay.say({ text: uri.path }); + } + }; + subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(myScheme, myProvider)); + + // register a command that opens a cowsay-document + subscriptions.push(vscode.commands.registerCommand('cowsay.say', async () => { + const what = await vscode.window.showInputBox({ placeHolder: 'cowsay...' }); + if (what) { + const uri = vscode.Uri.parse('cowsay:' + what); + const doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider + await vscode.window.showTextDocument(doc, { preview: false }); + } + })); + + // register a command that updates the current cowsay + subscriptions.push(vscode.commands.registerCommand('cowsay.backwards', async () => { + if (!vscode.window.activeTextEditor) { + return; // no editor + } + const { document } = vscode.window.activeTextEditor; + if (document.uri.scheme !== myScheme) { + return; // not my scheme + } + // get path-components, reverse it, and create a new uri + const say = document.uri.path; + const newSay = say.split('').reverse().join(''); + const newUri = document.uri.with({ path: newSay }); + await vscode.window.showTextDocument(newUri, { preview: false }); + })); +} diff --git a/webpack-sample/src/extension.ts b/webpack-sample/src/extension.ts index 254f740f..89bab024 100644 --- a/webpack-sample/src/extension.ts +++ b/webpack-sample/src/extension.ts @@ -1,16 +1,16 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - - -import * as vscode from 'vscode'; -import { add } from './math'; - -export function activate(context: vscode.ExtensionContext) { - - const disposable = vscode.commands.registerCommand('extension.helloWebpack', () => { - vscode.window.showInformationMessage(`41 + 1 = ${add(41, 1)}`); - }); - - context.subscriptions.push(disposable); -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + + +import * as vscode from 'vscode'; +import { add } from './math'; + +export function activate(context: vscode.ExtensionContext) { + + const disposable = vscode.commands.registerCommand('extension.helloWebpack', () => { + vscode.window.showInformationMessage(`41 + 1 = ${add(41, 1)}`); + }); + + context.subscriptions.push(disposable); +} diff --git a/webpack-sample/src/math.ts b/webpack-sample/src/math.ts index 90e7b9d6..cad199e2 100644 --- a/webpack-sample/src/math.ts +++ b/webpack-sample/src/math.ts @@ -1,12 +1,12 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - - -export function add(a: number, b: number): number { - return a + b; -} - -export function sub(a: number, b: number): number { - return a - b; -} +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + + +export function add(a: number, b: number): number { + return a + b; +} + +export function sub(a: number, b: number): number { + return a - b; +} diff --git a/webview-codicons-sample/src/extension.ts b/webview-codicons-sample/src/extension.ts index d4478d61..040acce6 100644 --- a/webview-codicons-sample/src/extension.ts +++ b/webview-codicons-sample/src/extension.ts @@ -1,385 +1,385 @@ -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push( - vscode.commands.registerCommand('catCodicons.show', () => { - CatCodiconsPanel.show(context.extensionUri); - }) - ); -} - - -class CatCodiconsPanel { - - public static readonly viewType = 'catCodicons'; - - public static show(extensionUri: vscode.Uri) { - const column = vscode.window.activeTextEditor - ? vscode.window.activeTextEditor.viewColumn - : undefined; - - const panel = vscode.window.createWebviewPanel( - CatCodiconsPanel.viewType, - "Cat Codicons", - column || vscode.ViewColumn.One - ); - - panel.webview.html = this._getHtmlForWebview(panel.webview, extensionUri); - } - - private static _getHtmlForWebview(webview: vscode.Webview, extensionUri: vscode.Uri) { - - // Get resource paths - const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'styles.css')); - const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css')); - - return ` - - - - - - - - - Cat Coding - - - - - -

codicons

-
-
account
-
activate-breakpoints
-
add
-
archive
-
arrow-both
-
arrow-down
-
arrow-left
-
arrow-right
-
arrow-small-down
-
arrow-small-left
-
arrow-small-right
-
arrow-small-up
-
arrow-up
-
beaker
-
bell-dot
-
bell
-
bold
-
book
-
bookmark
-
briefcase
-
broadcast
-
browser
-
bug
-
calendar
-
call-incoming
-
call-outgoing
-
case-sensitive
-
check
-
checklist
-
chevron-down
-
chevron-left
-
chevron-right
-
chevron-up
-
chrome-close
-
chrome-maximize
-
chrome-minimize
-
chrome-restore
-
circle-filled
-
circle-outline
-
circle-slash
-
circuit-board
-
clear-all
-
clippy
-
close-all
-
close
-
cloud-download
-
cloud-upload
-
cloud
-
code
-
collapse-all
-
color-mode
-
comment-discussion
-
comment
-
credit-card
-
dash
-
dashboard
-
database
-
debug-alt-small
-
debug-alt
-
debug-breakpoint-conditional-unverified
-
debug-breakpoint-conditional
-
debug-breakpoint-data-unverified
-
debug-breakpoint-data
-
debug-breakpoint-function-unverified
-
debug-breakpoint-function
-
debug-breakpoint-log-unverified
-
debug-breakpoint-log
-
debug-breakpoint-unsupported
-
debug-console
-
debug-continue
-
debug-disconnect
-
debug-pause
-
debug-restart-frame
-
debug-restart
-
debug-reverse-continue
-
debug-stackframe-active
-
debug-stackframe-dot
-
debug-stackframe
-
debug-start
-
debug-step-back
-
debug-step-into
-
debug-step-out
-
debug-step-over
-
debug-stop
-
debug
-
desktop-download
-
device-camera-video
-
device-camera
-
device-mobile
-
diff-added
-
diff-ignored
-
diff-modified
-
diff-removed
-
diff-renamed
-
diff
-
discard
-
edit
-
editor-layout
-
ellipsis
-
empty-window
-
error
-
exclude
-
expand-all
-
extensions
-
eye-closed
-
eye
-
feedback
-
file-binary
-
file-code
-
file-media
-
file-pdf
-
file-submodule
-
file-symlink-directory
-
file-symlink-file
-
file-zip
-
file
-
files
-
filter
-
flame
-
fold-down
-
fold-up
-
fold
-
folder-active
-
folder-opened
-
folder
-
gear
-
gift
-
gist-secret
-
gist
-
git-commit
-
git-compare
-
git-merge
-
git-pull-request
-
github-action
-
github-alt
-
github-inverted
-
github
-
globe
-
go-to-file
-
grabber
-
graph
-
gripper
-
group-by-ref-type
-
heart
-
history
-
home
-
horizontal-rule
-
hubot
-
inbox
-
info
-
issue-closed
-
issue-reopened
-
issues
-
italic
-
jersey
-
json
-
kebab-vertical
-
key
-
law
-
library
-
lightbulb-autofix
-
lightbulb
-
link-external
-
link
-
list-filter
-
list-flat
-
list-ordered
-
list-selection
-
list-tree
-
list-unordered
-
live-share
-
loading
-
location
-
lock
-
mail-read
-
mail
-
markdown
-
megaphone
-
mention
-
menu
-
merge
-
milestone
-
mirror
-
mortar-board
-
move
-
multiple-windows
-
mute
-
new-file
-
new-folder
-
no-newline
-
note
-
octoface
-
open-preview
-
organization
-
output
-
package
-
paintcan
-
pass
-
person
-
pin
-
pinned
-
play-circle
-
play
-
plug
-
preserve-case
-
preview
-
primitive-square
-
project
-
pulse
-
question
-
quote
-
radio-tower
-
reactions
-
record-keys
-
record
-
references
-
refresh
-
regex
-
remote-explorer
-
remote
-
remove
-
replace-all
-
replace
-
reply
-
repo-clone
-
repo-force-push
-
repo-forked
-
repo-pull
-
repo-push
-
repo
-
report
-
request-changes
-
rocket
-
root-folder-opened
-
root-folder
-
rss
-
ruby
-
run-all
-
save-all
-
save-as
-
save
-
screen-full
-
screen-normal
-
search-stop
-
search
-
server-environment
-
server-process
-
server
-
settings-gear
-
settings
-
shield
-
sign-in
-
sign-out
-
smiley
-
sort-precedence
-
source-control
-
split-horizontal
-
split-vertical
-
squirrel
-
star-empty
-
star-full
-
star-half
-
stop-circle
-
symbol-array
-
symbol-boolean
-
symbol-class
-
symbol-color
-
symbol-constant
-
symbol-enum-member
-
symbol-enum
-
symbol-event
-
symbol-field
-
symbol-file
-
symbol-interface
-
symbol-key
-
symbol-keyword
-
symbol-method
-
symbol-misc
-
symbol-namespace
-
symbol-numeric
-
symbol-operator
-
symbol-parameter
-
symbol-property
-
symbol-ruler
-
symbol-snippet
-
symbol-string
-
symbol-structure
-
symbol-variable
-
sync-ignored
-
sync
-
tag
-
tasklist
-
telescope
-
terminal
-
text-size
-
three-bars
-
thumbsdown
-
thumbsup
-
tools
-
trash
-
triangle-down
-
triangle-left
-
triangle-right
-
triangle-up
-
twitter
-
unfold
-
ungroup-by-ref-type
-
unlock
-
unmute
-
unverified
-
verified
-
versions
-
vm-active
-
vm-connect
-
vm-outline
-
vm-running
-
vm
-
warning
-
watch
-
whitespace
-
whole-word
-
window
-
word-wrap
-
zoom-in
-
zoom-out
-
- - `; - } -} - +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.commands.registerCommand('catCodicons.show', () => { + CatCodiconsPanel.show(context.extensionUri); + }) + ); +} + + +class CatCodiconsPanel { + + public static readonly viewType = 'catCodicons'; + + public static show(extensionUri: vscode.Uri) { + const column = vscode.window.activeTextEditor + ? vscode.window.activeTextEditor.viewColumn + : undefined; + + const panel = vscode.window.createWebviewPanel( + CatCodiconsPanel.viewType, + "Cat Codicons", + column || vscode.ViewColumn.One + ); + + panel.webview.html = this._getHtmlForWebview(panel.webview, extensionUri); + } + + private static _getHtmlForWebview(webview: vscode.Webview, extensionUri: vscode.Uri) { + + // Get resource paths + const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'styles.css')); + const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css')); + + return ` + + + + + + + + + Cat Coding + + + + + +

codicons

+
+
account
+
activate-breakpoints
+
add
+
archive
+
arrow-both
+
arrow-down
+
arrow-left
+
arrow-right
+
arrow-small-down
+
arrow-small-left
+
arrow-small-right
+
arrow-small-up
+
arrow-up
+
beaker
+
bell-dot
+
bell
+
bold
+
book
+
bookmark
+
briefcase
+
broadcast
+
browser
+
bug
+
calendar
+
call-incoming
+
call-outgoing
+
case-sensitive
+
check
+
checklist
+
chevron-down
+
chevron-left
+
chevron-right
+
chevron-up
+
chrome-close
+
chrome-maximize
+
chrome-minimize
+
chrome-restore
+
circle-filled
+
circle-outline
+
circle-slash
+
circuit-board
+
clear-all
+
clippy
+
close-all
+
close
+
cloud-download
+
cloud-upload
+
cloud
+
code
+
collapse-all
+
color-mode
+
comment-discussion
+
comment
+
credit-card
+
dash
+
dashboard
+
database
+
debug-alt-small
+
debug-alt
+
debug-breakpoint-conditional-unverified
+
debug-breakpoint-conditional
+
debug-breakpoint-data-unverified
+
debug-breakpoint-data
+
debug-breakpoint-function-unverified
+
debug-breakpoint-function
+
debug-breakpoint-log-unverified
+
debug-breakpoint-log
+
debug-breakpoint-unsupported
+
debug-console
+
debug-continue
+
debug-disconnect
+
debug-pause
+
debug-restart-frame
+
debug-restart
+
debug-reverse-continue
+
debug-stackframe-active
+
debug-stackframe-dot
+
debug-stackframe
+
debug-start
+
debug-step-back
+
debug-step-into
+
debug-step-out
+
debug-step-over
+
debug-stop
+
debug
+
desktop-download
+
device-camera-video
+
device-camera
+
device-mobile
+
diff-added
+
diff-ignored
+
diff-modified
+
diff-removed
+
diff-renamed
+
diff
+
discard
+
edit
+
editor-layout
+
ellipsis
+
empty-window
+
error
+
exclude
+
expand-all
+
extensions
+
eye-closed
+
eye
+
feedback
+
file-binary
+
file-code
+
file-media
+
file-pdf
+
file-submodule
+
file-symlink-directory
+
file-symlink-file
+
file-zip
+
file
+
files
+
filter
+
flame
+
fold-down
+
fold-up
+
fold
+
folder-active
+
folder-opened
+
folder
+
gear
+
gift
+
gist-secret
+
gist
+
git-commit
+
git-compare
+
git-merge
+
git-pull-request
+
github-action
+
github-alt
+
github-inverted
+
github
+
globe
+
go-to-file
+
grabber
+
graph
+
gripper
+
group-by-ref-type
+
heart
+
history
+
home
+
horizontal-rule
+
hubot
+
inbox
+
info
+
issue-closed
+
issue-reopened
+
issues
+
italic
+
jersey
+
json
+
kebab-vertical
+
key
+
law
+
library
+
lightbulb-autofix
+
lightbulb
+
link-external
+
link
+
list-filter
+
list-flat
+
list-ordered
+
list-selection
+
list-tree
+
list-unordered
+
live-share
+
loading
+
location
+
lock
+
mail-read
+
mail
+
markdown
+
megaphone
+
mention
+
menu
+
merge
+
milestone
+
mirror
+
mortar-board
+
move
+
multiple-windows
+
mute
+
new-file
+
new-folder
+
no-newline
+
note
+
octoface
+
open-preview
+
organization
+
output
+
package
+
paintcan
+
pass
+
person
+
pin
+
pinned
+
play-circle
+
play
+
plug
+
preserve-case
+
preview
+
primitive-square
+
project
+
pulse
+
question
+
quote
+
radio-tower
+
reactions
+
record-keys
+
record
+
references
+
refresh
+
regex
+
remote-explorer
+
remote
+
remove
+
replace-all
+
replace
+
reply
+
repo-clone
+
repo-force-push
+
repo-forked
+
repo-pull
+
repo-push
+
repo
+
report
+
request-changes
+
rocket
+
root-folder-opened
+
root-folder
+
rss
+
ruby
+
run-all
+
save-all
+
save-as
+
save
+
screen-full
+
screen-normal
+
search-stop
+
search
+
server-environment
+
server-process
+
server
+
settings-gear
+
settings
+
shield
+
sign-in
+
sign-out
+
smiley
+
sort-precedence
+
source-control
+
split-horizontal
+
split-vertical
+
squirrel
+
star-empty
+
star-full
+
star-half
+
stop-circle
+
symbol-array
+
symbol-boolean
+
symbol-class
+
symbol-color
+
symbol-constant
+
symbol-enum-member
+
symbol-enum
+
symbol-event
+
symbol-field
+
symbol-file
+
symbol-interface
+
symbol-key
+
symbol-keyword
+
symbol-method
+
symbol-misc
+
symbol-namespace
+
symbol-numeric
+
symbol-operator
+
symbol-parameter
+
symbol-property
+
symbol-ruler
+
symbol-snippet
+
symbol-string
+
symbol-structure
+
symbol-variable
+
sync-ignored
+
sync
+
tag
+
tasklist
+
telescope
+
terminal
+
text-size
+
three-bars
+
thumbsdown
+
thumbsup
+
tools
+
trash
+
triangle-down
+
triangle-left
+
triangle-right
+
triangle-up
+
twitter
+
unfold
+
ungroup-by-ref-type
+
unlock
+
unmute
+
unverified
+
verified
+
versions
+
vm-active
+
vm-connect
+
vm-outline
+
vm-running
+
vm
+
warning
+
watch
+
whitespace
+
whole-word
+
window
+
word-wrap
+
zoom-in
+
zoom-out
+
+ + `; + } +} + diff --git a/webview-sample/src/extension.ts b/webview-sample/src/extension.ts index d2b6e26b..ab12b2a7 100644 --- a/webview-sample/src/extension.ts +++ b/webview-sample/src/extension.ts @@ -1,222 +1,222 @@ -import * as vscode from 'vscode'; - -const cats = { - 'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif', - 'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif', - 'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif' -}; - -export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push( - vscode.commands.registerCommand('catCoding.start', () => { - CatCodingPanel.createOrShow(context.extensionUri); - }) - ); - - context.subscriptions.push( - vscode.commands.registerCommand('catCoding.doRefactor', () => { - if (CatCodingPanel.currentPanel) { - CatCodingPanel.currentPanel.doRefactor(); - } - }) - ); - - if (vscode.window.registerWebviewPanelSerializer) { - // Make sure we register a serializer in activation event - vscode.window.registerWebviewPanelSerializer(CatCodingPanel.viewType, { - async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { - console.log(`Got state: ${state}`); - // Reset the webview options so we use latest uri for `localResourceRoots`. - webviewPanel.webview.options = getWebviewOptions(context.extensionUri); - CatCodingPanel.revive(webviewPanel, context.extensionUri); - } - }); - } -} - -function getWebviewOptions(extensionUri: vscode.Uri): vscode.WebviewOptions { - return { - // Enable javascript in the webview - enableScripts: true, - - // And restrict the webview to only loading content from our extension's `media` directory. - localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'media')] - }; -} - -/** - * Manages cat coding webview panels - */ -class CatCodingPanel { - /** - * Track the currently panel. Only allow a single panel to exist at a time. - */ - public static currentPanel: CatCodingPanel | undefined; - - public static readonly viewType = 'catCoding'; - - private readonly _panel: vscode.WebviewPanel; - private readonly _extensionUri: vscode.Uri; - private _disposables: vscode.Disposable[] = []; - - public static createOrShow(extensionUri: vscode.Uri) { - const column = vscode.window.activeTextEditor - ? vscode.window.activeTextEditor.viewColumn - : undefined; - - // If we already have a panel, show it. - if (CatCodingPanel.currentPanel) { - CatCodingPanel.currentPanel._panel.reveal(column); - return; - } - - // Otherwise, create a new panel. - const panel = vscode.window.createWebviewPanel( - CatCodingPanel.viewType, - 'Cat Coding', - column || vscode.ViewColumn.One, - getWebviewOptions(extensionUri), - ); - - CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionUri); - } - - public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { - CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionUri); - } - - private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { - this._panel = panel; - this._extensionUri = extensionUri; - - // Set the webview's initial html content - this._update(); - - // Listen for when the panel is disposed - // This happens when the user closes the panel or when the panel is closed programmatically - this._panel.onDidDispose(() => this.dispose(), null, this._disposables); - - // Update the content based on view changes - this._panel.onDidChangeViewState( - e => { - if (this._panel.visible) { - this._update(); - } - }, - null, - this._disposables - ); - - // Handle messages from the webview - this._panel.webview.onDidReceiveMessage( - message => { - switch (message.command) { - case 'alert': - vscode.window.showErrorMessage(message.text); - return; - } - }, - null, - this._disposables - ); - } - - public doRefactor() { - // Send a message to the webview webview. - // You can send any JSON serializable data. - this._panel.webview.postMessage({ command: 'refactor' }); - } - - public dispose() { - CatCodingPanel.currentPanel = undefined; - - // Clean up our resources - this._panel.dispose(); - - while (this._disposables.length) { - const x = this._disposables.pop(); - if (x) { - x.dispose(); - } - } - } - - private _update() { - const webview = this._panel.webview; - - // Vary the webview's content based on where it is located in the editor. - switch (this._panel.viewColumn) { - case vscode.ViewColumn.Two: - this._updateForCat(webview, 'Compiling Cat'); - return; - - case vscode.ViewColumn.Three: - this._updateForCat(webview, 'Testing Cat'); - return; - - case vscode.ViewColumn.One: - default: - this._updateForCat(webview, 'Coding Cat'); - return; - } - } - - private _updateForCat(webview: vscode.Webview, catName: keyof typeof cats) { - this._panel.title = catName; - this._panel.webview.html = this._getHtmlForWebview(webview, cats[catName]); - } - - private _getHtmlForWebview(webview: vscode.Webview, catGifPath: string) { - // Local path to main script run in the webview - const scriptPathOnDisk = vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js'); - - // And the uri we use to load this script in the webview - const scriptUri = webview.asWebviewUri(scriptPathOnDisk); - - // Local path to css styles - const styleResetPath = vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css'); - const stylesPathMainPath = vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css'); - - // Uri to load styles into webview - const stylesResetUri = webview.asWebviewUri(styleResetPath); - const stylesMainUri = webview.asWebviewUri(stylesPathMainPath); - - // Use a nonce to only allow specific scripts to be run - const nonce = getNonce(); - - return ` - - - - - - - - - - - - - Cat Coding - - - -

0

- - - - `; - } -} - -function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 32; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} +import * as vscode from 'vscode'; + +const cats = { + 'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif', + 'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif', + 'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif' +}; + +export function activate(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.commands.registerCommand('catCoding.start', () => { + CatCodingPanel.createOrShow(context.extensionUri); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand('catCoding.doRefactor', () => { + if (CatCodingPanel.currentPanel) { + CatCodingPanel.currentPanel.doRefactor(); + } + }) + ); + + if (vscode.window.registerWebviewPanelSerializer) { + // Make sure we register a serializer in activation event + vscode.window.registerWebviewPanelSerializer(CatCodingPanel.viewType, { + async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { + console.log(`Got state: ${state}`); + // Reset the webview options so we use latest uri for `localResourceRoots`. + webviewPanel.webview.options = getWebviewOptions(context.extensionUri); + CatCodingPanel.revive(webviewPanel, context.extensionUri); + } + }); + } +} + +function getWebviewOptions(extensionUri: vscode.Uri): vscode.WebviewOptions { + return { + // Enable javascript in the webview + enableScripts: true, + + // And restrict the webview to only loading content from our extension's `media` directory. + localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'media')] + }; +} + +/** + * Manages cat coding webview panels + */ +class CatCodingPanel { + /** + * Track the currently panel. Only allow a single panel to exist at a time. + */ + public static currentPanel: CatCodingPanel | undefined; + + public static readonly viewType = 'catCoding'; + + private readonly _panel: vscode.WebviewPanel; + private readonly _extensionUri: vscode.Uri; + private _disposables: vscode.Disposable[] = []; + + public static createOrShow(extensionUri: vscode.Uri) { + const column = vscode.window.activeTextEditor + ? vscode.window.activeTextEditor.viewColumn + : undefined; + + // If we already have a panel, show it. + if (CatCodingPanel.currentPanel) { + CatCodingPanel.currentPanel._panel.reveal(column); + return; + } + + // Otherwise, create a new panel. + const panel = vscode.window.createWebviewPanel( + CatCodingPanel.viewType, + 'Cat Coding', + column || vscode.ViewColumn.One, + getWebviewOptions(extensionUri), + ); + + CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionUri); + } + + public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { + CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionUri); + } + + private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) { + this._panel = panel; + this._extensionUri = extensionUri; + + // Set the webview's initial html content + this._update(); + + // Listen for when the panel is disposed + // This happens when the user closes the panel or when the panel is closed programmatically + this._panel.onDidDispose(() => this.dispose(), null, this._disposables); + + // Update the content based on view changes + this._panel.onDidChangeViewState( + e => { + if (this._panel.visible) { + this._update(); + } + }, + null, + this._disposables + ); + + // Handle messages from the webview + this._panel.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case 'alert': + vscode.window.showErrorMessage(message.text); + return; + } + }, + null, + this._disposables + ); + } + + public doRefactor() { + // Send a message to the webview webview. + // You can send any JSON serializable data. + this._panel.webview.postMessage({ command: 'refactor' }); + } + + public dispose() { + CatCodingPanel.currentPanel = undefined; + + // Clean up our resources + this._panel.dispose(); + + while (this._disposables.length) { + const x = this._disposables.pop(); + if (x) { + x.dispose(); + } + } + } + + private _update() { + const webview = this._panel.webview; + + // Vary the webview's content based on where it is located in the editor. + switch (this._panel.viewColumn) { + case vscode.ViewColumn.Two: + this._updateForCat(webview, 'Compiling Cat'); + return; + + case vscode.ViewColumn.Three: + this._updateForCat(webview, 'Testing Cat'); + return; + + case vscode.ViewColumn.One: + default: + this._updateForCat(webview, 'Coding Cat'); + return; + } + } + + private _updateForCat(webview: vscode.Webview, catName: keyof typeof cats) { + this._panel.title = catName; + this._panel.webview.html = this._getHtmlForWebview(webview, cats[catName]); + } + + private _getHtmlForWebview(webview: vscode.Webview, catGifPath: string) { + // Local path to main script run in the webview + const scriptPathOnDisk = vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js'); + + // And the uri we use to load this script in the webview + const scriptUri = webview.asWebviewUri(scriptPathOnDisk); + + // Local path to css styles + const styleResetPath = vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css'); + const stylesPathMainPath = vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css'); + + // Uri to load styles into webview + const stylesResetUri = webview.asWebviewUri(styleResetPath); + const stylesMainUri = webview.asWebviewUri(stylesPathMainPath); + + // Use a nonce to only allow specific scripts to be run + const nonce = getNonce(); + + return ` + + + + + + + + + + + + + Cat Coding + + + +

0

+ + + + `; + } +} + +function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/webview-view-sample/src/extension.ts b/webview-view-sample/src/extension.ts index 1fed4fb3..405a2ec5 100644 --- a/webview-view-sample/src/extension.ts +++ b/webview-view-sample/src/extension.ts @@ -1,124 +1,124 @@ -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - - const provider = new ColorsViewProvider(context.extensionUri); - - context.subscriptions.push( - vscode.window.registerWebviewViewProvider(ColorsViewProvider.viewType, provider)); - - context.subscriptions.push( - vscode.commands.registerCommand('calicoColors.addColor', () => { - provider.addColor(); - })); - - context.subscriptions.push( - vscode.commands.registerCommand('calicoColors.clearColors', () => { - provider.clearColors(); - })); -} - -class ColorsViewProvider implements vscode.WebviewViewProvider { - - public static readonly viewType = 'calicoColors.colorsView'; - - private _view?: vscode.WebviewView; - - constructor( - private readonly _extensionUri: vscode.Uri, - ) { } - - public resolveWebviewView( - webviewView: vscode.WebviewView, - context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken, - ) { - this._view = webviewView; - - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - - localResourceRoots: [ - this._extensionUri - ] - }; - - webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - - webviewView.webview.onDidReceiveMessage(data => { - switch (data.type) { - case 'colorSelected': - { - vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(`#${data.value}`)); - break; - } - } - }); - } - - public addColor() { - if (this._view) { - this._view.show?.(true); // `show` is not implemented in 1.49 but is for 1.50 insiders - this._view.webview.postMessage({ type: 'addColor' }); - } - } - - public clearColors() { - if (this._view) { - this._view.webview.postMessage({ type: 'clearColors' }); - } - } - - private _getHtmlForWebview(webview: vscode.Webview) { - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js')); - - // Do the same for the stylesheet. - const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css')); - const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css')); - const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.css')); - - // Use a nonce to only allow a specific script to be run. - const nonce = getNonce(); - - return ` - - - - - - - - - - - - - - Cat Colors - - -
    -
- - - - - - `; - } -} - -function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 32; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + + const provider = new ColorsViewProvider(context.extensionUri); + + context.subscriptions.push( + vscode.window.registerWebviewViewProvider(ColorsViewProvider.viewType, provider)); + + context.subscriptions.push( + vscode.commands.registerCommand('calicoColors.addColor', () => { + provider.addColor(); + })); + + context.subscriptions.push( + vscode.commands.registerCommand('calicoColors.clearColors', () => { + provider.clearColors(); + })); +} + +class ColorsViewProvider implements vscode.WebviewViewProvider { + + public static readonly viewType = 'calicoColors.colorsView'; + + private _view?: vscode.WebviewView; + + constructor( + private readonly _extensionUri: vscode.Uri, + ) { } + + public resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken, + ) { + this._view = webviewView; + + webviewView.webview.options = { + // Allow scripts in the webview + enableScripts: true, + + localResourceRoots: [ + this._extensionUri + ] + }; + + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + + webviewView.webview.onDidReceiveMessage(data => { + switch (data.type) { + case 'colorSelected': + { + vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(`#${data.value}`)); + break; + } + } + }); + } + + public addColor() { + if (this._view) { + this._view.show?.(true); // `show` is not implemented in 1.49 but is for 1.50 insiders + this._view.webview.postMessage({ type: 'addColor' }); + } + } + + public clearColors() { + if (this._view) { + this._view.webview.postMessage({ type: 'clearColors' }); + } + } + + private _getHtmlForWebview(webview: vscode.Webview) { + // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. + const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js')); + + // Do the same for the stylesheet. + const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css')); + const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css')); + const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.css')); + + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + + return ` + + + + + + + + + + + + + + Cat Colors + + +
    +
+ + + + + + `; + } +} + +function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/welcome-view-content-sample/src/extension.ts b/welcome-view-content-sample/src/extension.ts index 0cd450ec..ac5933d3 100644 --- a/welcome-view-content-sample/src/extension.ts +++ b/welcome-view-content-sample/src/extension.ts @@ -1,14 +1,14 @@ -import * as vscode from 'vscode'; - -export function activate(context: vscode.ExtensionContext) { - let disposable = vscode.commands.registerCommand( - 'welcome-view-content-sample.hello', - async () => { - vscode.window.showInformationMessage('Hello world!'); - } - ); - - context.subscriptions.push(disposable); -} - -export function deactivate() {} +import * as vscode from 'vscode'; + +export function activate(context: vscode.ExtensionContext) { + let disposable = vscode.commands.registerCommand( + 'welcome-view-content-sample.hello', + async () => { + vscode.window.showInformationMessage('Hello world!'); + } + ); + + context.subscriptions.push(disposable); +} + +export function deactivate() { }