From bc1324553ccaf5493392454630086a120b03cdcc Mon Sep 17 00:00:00 2001 From: Pranomvignesh Date: Thu, 6 May 2021 11:15:31 +0530 Subject: [PATCH 001/330] fix(keybindingLabels.ts) : Changed Keybinding Title from Alt to Options in Mac OS Fix for issue in monaco-editor#2468 --- src/vs/base/common/keybindingLabels.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index 1cdbc8d8e9e..485a1741e46 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -89,7 +89,7 @@ export const AriaLabelProvider = new ModifierLabelProvider( { ctrlKey: nls.localize({ key: 'ctrlKey.long', comment: ['This is the long form for the Control key on the keyboard'] }, "Control"), shiftKey: nls.localize({ key: 'shiftKey.long', comment: ['This is the long form for the Shift key on the keyboard'] }, "Shift"), - altKey: nls.localize({ key: 'altKey.long', comment: ['This is the long form for the Alt key on the keyboard'] }, "Alt"), + altKey: nls.localize({ key: 'optKey.long', comment: ['This is the long form for the Alt key on the keyboard'] }, "Option"), metaKey: nls.localize({ key: 'cmdKey.long', comment: ['This is the long form for the Command key on the keyboard'] }, "Command"), separator: '+', }, @@ -117,7 +117,7 @@ export const ElectronAcceleratorLabelProvider = new ModifierLabelProvider( { ctrlKey: 'Ctrl', shiftKey: 'Shift', - altKey: 'Alt', + altKey: 'Opt', metaKey: 'Cmd', separator: '+', }, @@ -137,7 +137,7 @@ export const UserSettingsLabelProvider = new ModifierLabelProvider( { ctrlKey: 'ctrl', shiftKey: 'shift', - altKey: 'alt', + altKey: 'opt', metaKey: 'cmd', separator: '+', }, From e77476e7a2c3bbfecf8131485170dc5a24ddfa74 Mon Sep 17 00:00:00 2001 From: Suzy Mueller Date: Wed, 22 Sep 2021 12:50:55 -0600 Subject: [PATCH 002/330] clear focused thread if it does not exist --- src/vs/workbench/contrib/debug/browser/debugSession.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index f5aa6f6dbc9..76f1319993e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -950,6 +950,11 @@ export class DebugSession implements IDebugSession { focus(); } } + // If the focus for the current session is on a non-existent thread, clear the focus. + const focusedThread = this.debugService.getViewModel().focusedThread; + if (focusedThread !== undefined && focusedThread.session === this && !this.threads.has(focusedThread.threadId)) { + this.debugService.focusStackFrame(undefined, undefined); + } this._onDidChangeState.fire(); })); From 8535fa18366452ce5a9df99a7aaf4a7a055e90a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Sun, 7 Nov 2021 15:16:56 +0000 Subject: [PATCH 003/330] rename `connectionToken` to `connection-token` --- resources/server/bin-dev/code-web.js | 2 +- src/vs/server/remoteExtensionHostAgentServer.ts | 6 +++--- src/vs/server/serverEnvironmentService.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/server/bin-dev/code-web.js b/resources/server/bin-dev/code-web.js index d5276fb41d0..0fdb7c76d2c 100644 --- a/resources/server/bin-dev/code-web.js +++ b/resources/server/bin-dev/code-web.js @@ -61,7 +61,7 @@ if (ENABLE_SYNC) { } // Connection Token -serverArgs.push('--connectionToken', '00000'); +serverArgs.push('--connection-token', '00000'); // Server should really only listen from localhost serverArgs.push('--host', '127.0.0.1'); diff --git a/src/vs/server/remoteExtensionHostAgentServer.ts b/src/vs/server/remoteExtensionHostAgentServer.ts index d755bca9589..89cc2b57878 100644 --- a/src/vs/server/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/remoteExtensionHostAgentServer.ts @@ -914,8 +914,8 @@ export class RemoteExtensionHostAgentServer extends Disposable { function parseConnectionToken(args: ServerParsedArgs): { connectionToken: string; connectionTokenIsMandatory: boolean; } { if (args['connection-secret']) { - if (args['connectionToken']) { - console.warn(`Please do not use the argument connectionToken at the same time as connection-secret.`); + if (args['connection-token']) { + console.warn(`Please do not use the argument connection-token at the same time as connection-secret.`); process.exit(1); } let rawConnectionToken = fs.readFileSync(args['connection-secret']).toString(); @@ -926,7 +926,7 @@ function parseConnectionToken(args: ServerParsedArgs): { connectionToken: string } return { connectionToken: rawConnectionToken, connectionTokenIsMandatory: true }; } else { - return { connectionToken: args['connectionToken'] || generateUuid(), connectionTokenIsMandatory: false }; + return { connectionToken: args['connection-token'] || generateUuid(), connectionTokenIsMandatory: false }; } } diff --git a/src/vs/server/serverEnvironmentService.ts b/src/vs/server/serverEnvironmentService.ts index 2e8a3ef9394..3715cfb25c1 100644 --- a/src/vs/server/serverEnvironmentService.ts +++ b/src/vs/server/serverEnvironmentService.ts @@ -11,7 +11,7 @@ import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/envi export const serverOptions: OptionDescriptions = { 'port': { type: 'string' }, - 'connectionToken': { type: 'string' }, + 'connection-token': { type: 'string' }, 'connection-secret': { type: 'string', description: nls.localize('connection-secret', "Path to file that contains the connection token. This will require that all incoming connections know the secret.") }, 'host': { type: 'string' }, 'socket-path': { type: 'string' }, @@ -58,7 +58,7 @@ export const serverOptions: OptionDescriptions = { export interface ServerParsedArgs { port?: string; - connectionToken?: string; + 'connection-token'?: string; /** * A path to a filename which will be read on startup. * Consider placing this file in a folder readable only by the same user (a `chmod 0700` directory). From b35098d6e328c16f411c67df911bd33ee4d0c7a0 Mon Sep 17 00:00:00 2001 From: Pedro Guedes Date: Sun, 7 Nov 2021 16:37:27 +0000 Subject: [PATCH 004/330] fix #131458 --- src/vs/editor/contrib/multicursor/multicursor.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 116308fbd83..497370e7a66 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -821,12 +821,14 @@ class SelectionHighlighterState { public readonly matchCase: boolean; public readonly wordSeparators: string | null; public readonly modelVersionId: number; + public selections: SelectionHighlighterState[]; - constructor(searchText: string, matchCase: boolean, wordSeparators: string | null, modelVersionId: number) { + constructor(searchText: string, matchCase: boolean, wordSeparators: string | null, modelVersionId: number, selections: any[]) { this.searchText = searchText; this.matchCase = matchCase; this.wordSeparators = wordSeparators; this.modelVersionId = modelVersionId; + this.selections = selections; } /** @@ -840,7 +842,8 @@ class SelectionHighlighterState { return false; } return ( - a.searchText === b.searchText + a.selections === b.selections + && a.searchText === b.searchText && a.matchCase === b.matchCase && a.wordSeparators === b.wordSeparators && a.modelVersionId === b.modelVersionId @@ -980,7 +983,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut } } - return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null, editor.getModel().getVersionId()); + return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null, editor.getModel().getVersionId(), editor.getSelections()); } private _setState(state: SelectionHighlighterState | null): void { From e1612baf967bdd1aa7a022582950ecadb4632148 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 8 Nov 2021 12:05:48 +0100 Subject: [PATCH 005/330] add shortcut between file system provider and consumer When a file system is implemented and used within the same extension host we can short cut some calls, e.g skip going through the renderer and stay within the extension host --- .../api/browser/mainThreadFileSystem.ts | 6 + .../workbench/api/common/extHost.api.impl.ts | 6 +- .../workbench/api/common/extHost.protocol.ts | 2 + .../api/common/extHostFileSystemConsumer.ts | 121 +++++++++++++++--- 4 files changed, 117 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadFileSystem.ts b/src/vs/workbench/api/browser/mainThreadFileSystem.ts index 152b6751fb1..03d2f98a980 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystem.ts @@ -10,6 +10,7 @@ import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileSer import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol'; import { VSBuffer } from 'vs/base/common/buffer'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadFileSystem) export class MainThreadFileSystem implements MainThreadFileSystemShape { @@ -21,6 +22,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { constructor( extHostContext: IExtHostContext, @IFileService private readonly _fileService: IFileService, + @IExtensionService private readonly _extensionService: IExtensionService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem); @@ -149,6 +151,10 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { throw err; } + + $ensureActivation(scheme: string): Promise { + return this._extensionService.activateByEvent(`onFileSystem:${scheme}`); + } } class RemoteFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileFolderCopyCapability { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6418034b29b..98babbfae3e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -91,6 +91,7 @@ import { matchesScheme } from 'vs/platform/opener/common/opener'; import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors'; import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments'; import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; +import { combinedDisposable } from 'vs/base/common/lifecycle'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -913,7 +914,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTask.registerTaskProvider(extension, type, provider); }, registerFileSystemProvider(scheme, provider, options) { - return extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options); + return combinedDisposable( + extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options), + extHostConsumerFileSystem.addFileSystemProvider(scheme, provider) + ); }, get fs() { return extHostConsumerFileSystem.value; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 03d33fe3739..909280db6fd 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1009,6 +1009,8 @@ export interface MainThreadFileSystemShape extends IDisposable { $copy(resource: UriComponents, target: UriComponents, opts: files.FileOverwriteOptions): Promise; $mkdir(resource: UriComponents): Promise; $delete(resource: UriComponents, opts: files.FileDeleteOptions): Promise; + + $ensureActivation(scheme: string): Promise; } export interface MainThreadLabelServiceShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts index 46e331f4c83..313692e3978 100644 --- a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts +++ b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MainContext } from './extHost.protocol'; +import { MainContext, MainThreadFileSystemShape } from './extHost.protocol'; import * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; @@ -11,6 +11,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; export class ExtHostConsumerFileSystem { @@ -18,36 +19,110 @@ export class ExtHostConsumerFileSystem { readonly value: vscode.FileSystem; + private readonly _proxy: MainThreadFileSystemShape; + private readonly _fileSystemProvider = new Map(); + constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @IExtHostFileSystemInfo fileSystemInfo: IExtHostFileSystemInfo, ) { - const proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem); + this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem); + const that = this; this.value = Object.freeze({ - stat(uri: vscode.Uri): Promise { - return proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError); + async stat(uri: vscode.Uri): Promise { + try { + const provider = that._fileSystemProvider.get(uri.scheme); + if (!provider) { + return await that._proxy.$stat(uri); + } + // use shortcut + await that._proxy.$ensureActivation(uri.scheme); + const stat = await provider.stat(uri); + return Object.freeze({ + type: stat.type, + ctime: stat.ctime, + mtime: stat.mtime, + size: stat.size, + permissions: stat.permissions + }); + } catch (err) { + ExtHostConsumerFileSystem._handleError(err); + } }, - readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { - return proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError); + async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { + try { + const provider = that._fileSystemProvider.get(uri.scheme); + if (provider) { + // use shortcut + await that._proxy.$ensureActivation(uri.scheme); + return (await provider.readDirectory(uri)).slice(); // safe-copy + } else { + return await that._proxy.$readdir(uri); + } + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, - createDirectory(uri: vscode.Uri): Promise { - return proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError); + async createDirectory(uri: vscode.Uri): Promise { + try { + // no shortcut: does mkdirp + return await that._proxy.$mkdir(uri); + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, async readFile(uri: vscode.Uri): Promise { - return proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError); + try { + const provider = that._fileSystemProvider.get(uri.scheme); + if (provider) { + // use shortcut + await that._proxy.$ensureActivation(uri.scheme); + return (await provider.readFile(uri)).slice(); // safe-copy + } else { + return that._proxy.$readFile(uri).then(buff => buff.buffer); + } + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, - writeFile(uri: vscode.Uri, content: Uint8Array): Promise { - return proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError); + async writeFile(uri: vscode.Uri, content: Uint8Array): Promise { + try { + // no shortcut: does mkdirp + return await that._proxy.$writeFile(uri, VSBuffer.wrap(content)); + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, - delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise { - return proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + async delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise { + try { + const provider = that._fileSystemProvider.get(uri.scheme); + if (provider) { + // use shortcut + await that._proxy.$ensureActivation(uri.scheme); + return await provider.delete(uri, { recursive: false, ...options }); + } else { + return await that._proxy.$delete(uri, { recursive: false, useTrash: false, ...options }); + } + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise { - return proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise { + try { + // no shortcut: potentially involves different schemes, does mkdirp + return await that._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }); + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, - copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise { - return proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + async copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise { + try { + // no shortcut: potentially involves different schemes, does mkdirp + return await that._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }); + } catch (err) { + return ExtHostConsumerFileSystem._handleError(err); + } }, isWritableFileSystem(scheme: string): boolean | undefined { const capabilities = fileSystemInfo.getCapabilities(scheme); @@ -60,6 +135,11 @@ export class ExtHostConsumerFileSystem { } private static _handleError(err: any): never { + // desired error type + if (err instanceof FileSystemError) { + throw err; + } + // generic error if (!(err instanceof Error)) { throw new FileSystemError(String(err)); @@ -82,6 +162,13 @@ export class ExtHostConsumerFileSystem { default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode); } } + + // --- + + addFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider): IDisposable { + this._fileSystemProvider.set(scheme, provider); + return toDisposable(() => this._fileSystemProvider.delete(scheme)); + } } export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { } From 4b837043440f3306d14829353b92020b24a85768 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 9 Nov 2021 10:02:29 +0100 Subject: [PATCH 006/330] use fileService.activateProvider instead of extension service --- src/vs/workbench/api/browser/mainThreadFileSystem.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadFileSystem.ts b/src/vs/workbench/api/browser/mainThreadFileSystem.ts index 03d2f98a980..042e2264f5f 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystem.ts @@ -10,7 +10,6 @@ import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileSer import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadFileSystem) export class MainThreadFileSystem implements MainThreadFileSystemShape { @@ -22,7 +21,6 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { constructor( extHostContext: IExtHostContext, @IFileService private readonly _fileService: IFileService, - @IExtensionService private readonly _extensionService: IExtensionService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem); @@ -153,7 +151,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } $ensureActivation(scheme: string): Promise { - return this._extensionService.activateByEvent(`onFileSystem:${scheme}`); + return this._fileService.activateProvider(scheme); } } From 9d4a8af6372219b7ee178efd20f0beac77ffaa9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Tue, 9 Nov 2021 15:57:05 +0000 Subject: [PATCH 007/330] Deprecate `connection-token` --- src/vs/server/remoteExtensionHostAgentServer.ts | 6 +++++- src/vs/server/serverEnvironmentService.ts | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/server/remoteExtensionHostAgentServer.ts b/src/vs/server/remoteExtensionHostAgentServer.ts index 89cc2b57878..4d2d3b53692 100644 --- a/src/vs/server/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/remoteExtensionHostAgentServer.ts @@ -913,6 +913,10 @@ export class RemoteExtensionHostAgentServer extends Disposable { } function parseConnectionToken(args: ServerParsedArgs): { connectionToken: string; connectionTokenIsMandatory: boolean; } { + if (args['connectionToken']) { + console.warn(`The argument connectionToken is deprecated, please use connection-token instead`); + } + if (args['connection-secret']) { if (args['connection-token']) { console.warn(`Please do not use the argument connection-token at the same time as connection-secret.`); @@ -926,7 +930,7 @@ function parseConnectionToken(args: ServerParsedArgs): { connectionToken: string } return { connectionToken: rawConnectionToken, connectionTokenIsMandatory: true }; } else { - return { connectionToken: args['connection-token'] || generateUuid(), connectionTokenIsMandatory: false }; + return { connectionToken: args['connection-token'] || args['connectionToken'] || generateUuid(), connectionTokenIsMandatory: false }; } } diff --git a/src/vs/server/serverEnvironmentService.ts b/src/vs/server/serverEnvironmentService.ts index 3715cfb25c1..d9e927c04ee 100644 --- a/src/vs/server/serverEnvironmentService.ts +++ b/src/vs/server/serverEnvironmentService.ts @@ -59,6 +59,10 @@ export const serverOptions: OptionDescriptions = { export interface ServerParsedArgs { port?: string; 'connection-token'?: string; + /** + * @deprecated use `connection-token` instead + */ + connectionToken?: string; /** * A path to a filename which will be read on startup. * Consider placing this file in a folder readable only by the same user (a `chmod 0700` directory). From 628f486b1ddf9e0c4fff295c1e7aed85971da444 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 9 Nov 2021 08:45:08 -0800 Subject: [PATCH 008/330] Move playwright driver impl to class Part of #136064 --- test/automation/src/playwrightDriver.ts | 150 ++++++++++++++---------- 1 file changed, 89 insertions(+), 61 deletions(-) diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index baa15ef0aef..36cab76f0eb 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -11,6 +11,7 @@ import { promisify } from 'util'; import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; import * as kill from 'tree-kill'; +import { IElement, ILocaleInfo, ILocalizedStrings } from '.'; const width = 1200; const height = 800; @@ -35,69 +36,96 @@ const vscodeToPlaywrightKey: { [key: string]: string } = { let traceCounter = 1; function buildDriver(browser: playwright.Browser, context: playwright.BrowserContext, page: playwright.Page): IDriver { - const driver: IDriver = { - _serviceBrand: undefined, - getWindowIds: () => { - return Promise.resolve([1]); - }, - capturePage: () => Promise.resolve(''), - reloadWindow: (windowId) => Promise.resolve(), - exitApplication: async () => { - try { - await context.tracing.stop({ path: join(logsPath, `playwright-trace-${traceCounter++}.zip`) }); - } catch (error) { - console.warn(`Failed to stop playwright tracing.`); // do not fail the build when this fails - } - await browser.close(); - await teardown(); + return new PlaywrightDriver(browser, context, page); +} - return false; - }, - dispatchKeybinding: async (windowId, keybinding) => { - const chords = keybinding.split(' '); - for (let i = 0; i < chords.length; i++) { - const chord = chords[i]; - if (i > 0) { - await timeout(100); - } - const keys = chord.split('+'); - const keysDown: string[] = []; - for (let i = 0; i < keys.length; i++) { - if (keys[i] in vscodeToPlaywrightKey) { - keys[i] = vscodeToPlaywrightKey[keys[i]]; - } - await page.keyboard.down(keys[i]); - keysDown.push(keys[i]); - } - while (keysDown.length > 0) { - await page.keyboard.up(keysDown.pop()!); - } - } +class PlaywrightDriver implements IDriver { + _serviceBrand: undefined; - await timeout(100); - }, - click: async (windowId, selector, xoffset, yoffset) => { - const { x, y } = await driver.getElementXY(windowId, selector, xoffset, yoffset); - await page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); - }, - doubleClick: async (windowId, selector) => { - await driver.click(windowId, selector, 0, 0); - await timeout(60); - await driver.click(windowId, selector, 0, 0); - await timeout(100); - }, - setValue: async (windowId, selector, text) => page.evaluate(`window.driver.setValue('${selector}', '${text}')`).then(undefined), - getTitle: (windowId) => page.evaluate(`window.driver.getTitle()`), - isActiveElement: (windowId, selector) => page.evaluate(`window.driver.isActiveElement('${selector}')`), - getElements: (windowId, selector, recursive) => page.evaluate(`window.driver.getElements('${selector}', ${recursive})`), - getElementXY: (windowId, selector, xoffset?, yoffset?) => page.evaluate(`window.driver.getElementXY('${selector}', ${xoffset}, ${yoffset})`), - typeInEditor: (windowId, selector, text) => page.evaluate(`window.driver.typeInEditor('${selector}', '${text}')`), - getTerminalBuffer: (windowId, selector) => page.evaluate(`window.driver.getTerminalBuffer('${selector}')`), - writeInTerminal: (windowId, selector, text) => page.evaluate(`window.driver.writeInTerminal('${selector}', '${text}')`), - getLocaleInfo: (windowId) => page.evaluate(`window.driver.getLocaleInfo()`), - getLocalizedStrings: (windowId) => page.evaluate(`window.driver.getLocalizedStrings()`) - }; - return driver; + constructor( + private readonly _browser: playwright.Browser, + private readonly _context: playwright.BrowserContext, + private readonly _page: playwright.Page + ) { + } + + async getWindowIds() { return [1]; } + async capturePage() { return ''; } + async reloadWindow(windowId: number) { } + async exitApplication() { + try { + await this._context.tracing.stop({ path: join(logsPath, `playwright-trace-${traceCounter++}.zip`) }); + } catch (error) { + console.warn(`Failed to stop playwright tracing.`); // do not fail the build when this fails + } + await this._browser.close(); + await teardown(); + + return false; + } + async dispatchKeybinding(windowId: number, keybinding: string) { + const chords = keybinding.split(' '); + for (let i = 0; i < chords.length; i++) { + const chord = chords[i]; + if (i > 0) { + await timeout(100); + } + const keys = chord.split('+'); + const keysDown: string[] = []; + for (let i = 0; i < keys.length; i++) { + if (keys[i] in vscodeToPlaywrightKey) { + keys[i] = vscodeToPlaywrightKey[keys[i]]; + } + await this._page.keyboard.down(keys[i]); + keysDown.push(keys[i]); + } + while (keysDown.length > 0) { + await this._page.keyboard.up(keysDown.pop()!); + } + } + + await timeout(100); + } + async click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined) { + const { x, y } = await this.getElementXY(windowId, selector, xoffset, yoffset); + await this._page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); + } + async doubleClick(windowId: number, selector: string) { + await this.click(windowId, selector, 0, 0); + await timeout(60); + await this.click(windowId, selector, 0, 0); + await timeout(100); + } + async setValue(windowId: number, selector: string, text: string) { + await this._page.evaluate(`window.driver.setValue('${selector}', '${text}')`); + } + async getTitle(windowId: number) { + return this._page.evaluate(`window.driver.getTitle()`); + } + isActiveElement(windowId: number, selector: string) { + return this._page.evaluate(`window.driver.isActiveElement('${selector}')`); + } + getElements(windowId: number, selector: string, recursive?: boolean) { + return this._page.evaluate(`window.driver.getElements('${selector}', ${recursive})`); + } + getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number) { + return this._page.evaluate<{ x: number, y: number }>(`window.driver.getElementXY('${selector}', ${xoffset}, ${yoffset})`); + } + async typeInEditor(windowId: number, selector: string, text: string) { + await this._page.evaluate(`window.driver.typeInEditor('${selector}', '${text}')`); + } + getTerminalBuffer(windowId: number, selector: string) { + return this._page.evaluate(`window.driver.getTerminalBuffer('${selector}')`); + } + async writeInTerminal(windowId: number, selector: string, text: string) { + await this._page.evaluate(`window.driver.writeInTerminal('${selector}', '${text}')`); + } + getLocaleInfo(windowId: number) { + return this._page.evaluate(`window.driver.getLocaleInfo()`); + } + getLocalizedStrings(windowId: number) { + return this._page.evaluate(`window.driver.getLocalizedStrings()`); + } } function timeout(ms: number): Promise { From bd98431baffb16702160fc3012d5c0c62c04317a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 9 Nov 2021 10:10:54 -0800 Subject: [PATCH 009/330] Write basic terminal default profile test Co-authored-by: Megan Rogge --- src/vs/platform/driver/browser/baseDriver.ts | 12 +++++----- test/automation/src/terminal.ts | 15 ++++++++---- .../areas/terminal/terminal-profiles.test.ts | 24 +++++++++++++++---- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index 482cabc24ce..af25c40d725 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -8,6 +8,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { language, locale } from 'vs/base/common/platform'; import { IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver } from 'vs/platform/driver/common/driver'; import localizedStrings from 'vs/platform/localizations/common/localizedStrings'; +import type { Terminal } from 'xterm'; function serializeElement(element: Element, recursive: boolean): IElement { const attributes = Object.create(null); @@ -132,16 +133,15 @@ export abstract class BaseWindowDriver implements IWindowDriver { throw new Error(`Terminal not found: ${selector}`); } - const xterm = (element as any).xterm; + const xterm = (element as any).xterm as Terminal; if (!xterm) { throw new Error(`Xterm not found: ${selector}`); } const lines: string[] = []; - - for (let i = 0; i < xterm.buffer.length; i++) { - lines.push(xterm.buffer.getLine(i)!.translateToString(true)); + for (let i = 0; i < xterm.buffer.active.length; i++) { + lines.push(xterm.buffer.active.getLine(i)!.translateToString(true)); } return lines; @@ -154,13 +154,13 @@ export abstract class BaseWindowDriver implements IWindowDriver { throw new Error(`Element not found: ${selector}`); } - const xterm = (element as any).xterm; + const xterm = (element as any).xterm as Terminal; if (!xterm) { throw new Error(`Xterm not found: ${selector}`); } - xterm._core._coreService.triggerDataEvent(text); + (xterm as any)._core._coreService.triggerDataEvent(text); } getLocaleInfo(): Promise { diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 207f8c90b16..16a6dae6197 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -6,8 +6,8 @@ import { Code } from './code'; import { QuickAccess } from './quickaccess'; -const PANEL_SELECTOR = 'div[id="workbench.panel.terminal"]'; -const XTERM_SELECTOR = `${PANEL_SELECTOR} .terminal-wrapper`; +const TERMINAL_VIEW_SELECTOR = `#terminal`; +const XTERM_SELECTOR = `${TERMINAL_VIEW_SELECTOR} .terminal-wrapper`; const XTERM_TEXTAREA = `${XTERM_SELECTOR} textarea.xterm-helper-textarea`; export class Terminal { @@ -27,7 +27,14 @@ export class Terminal { await this.code.dispatchKeybinding('enter'); } - async waitForTerminalText(accept: (buffer: string[]) => boolean): Promise { - await this.code.waitForTerminalBuffer(XTERM_SELECTOR, accept); + async waitForTerminalText(accept: (buffer: string[]) => boolean, message?: string): Promise { + try { + await this.code.waitForTerminalBuffer(XTERM_SELECTOR, accept); + } catch (err: any) { + if (message) { + throw new Error(`${message}\n\nInner exception:\n${err.message}`); + } + throw err; + } } } diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts index 17c5d3d3525..6a4004522e4 100644 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ b/test/smoke/src/areas/terminal/terminal-profiles.test.ts @@ -3,18 +3,34 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ok } from 'assert'; import { ParsedArgs } from 'minimist'; import { Application } from '../../../../automation'; import { afterSuite, beforeSuite } from '../../utils'; export function setup(opts: ParsedArgs) { - describe.skip('Terminal Profiles', () => { + describe('Terminal Profiles', () => { + let app: Application; + beforeSuite(opts); afterSuite(opts); - it.skip('should launch the default profile', async () => { - const app = this.app as Application; - console.log(app); + before(function () { + app = this.app; + }); + + it('should launch the default profile', async function () { + await app.workbench.terminal.showTerminal(); + + // Verify the terminal buffer has some content + await app.workbench.terminal.waitForTerminalText(buffer => { + return buffer.some(e => e.length > 100); + }, 'The terminal buffer should have some content'); + + + // Verify the terminal single tab shows up and has a title + const terminalTab = await app.code.waitForElement('.single-terminal-tab'); + ok(terminalTab.textContent.trim().length > 0); }); }); } From 6a6a1b65df1bdaab5c05a8a867cfa96d155629d7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 10 Nov 2021 08:03:22 -0800 Subject: [PATCH 010/330] Use handles in playwright driver --- .eslintrc.json | 2 + src/vs/platform/driver/browser/baseDriver.ts | 2 +- test/automation/src/playwrightDriver.ts | 45 ++++++++++++-------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index e55fa836ac9..8575a4bd391 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -951,6 +951,8 @@ "**/test/automation/**", "@vscode/*", "@parcel/*", + "playwright-core/**", + "**/vs/platform/driver/common/**", "*" // node modules ] }, diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index af25c40d725..a5fed8d84cb 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -8,7 +8,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { language, locale } from 'vs/base/common/platform'; import { IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver } from 'vs/platform/driver/common/driver'; import localizedStrings from 'vs/platform/localizations/common/localizedStrings'; -import type { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; // eslint-disable-line code-import-patterns function serializeElement(element: Element, recursive: boolean): IElement { const attributes = Object.create(null); diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 36cab76f0eb..5e9b93f030a 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -11,7 +11,8 @@ import { promisify } from 'util'; import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; import * as kill from 'tree-kill'; -import { IElement, ILocaleInfo, ILocalizedStrings } from '.'; +import { PageFunction } from 'playwright-core/types/structs'; +import { IWindowDriver } from '../../../src/vs/platform/driver/common/driver'; const width = 1200; const height = 800; @@ -96,35 +97,45 @@ class PlaywrightDriver implements IDriver { await this.click(windowId, selector, 0, 0); await timeout(100); } + async setValue(windowId: number, selector: string, text: string) { - await this._page.evaluate(`window.driver.setValue('${selector}', '${text}')`); + return this._page.evaluate(([driver, selector, text]) => driver.setValue(selector, text), [await this._getDriverHandle(), selector, text] as const); } async getTitle(windowId: number) { - return this._page.evaluate(`window.driver.getTitle()`); + return this._evaluateWithDriver(([driver]) => driver.getTitle()); } - isActiveElement(windowId: number, selector: string) { - return this._page.evaluate(`window.driver.isActiveElement('${selector}')`); + async isActiveElement(windowId: number, selector: string) { + return this._page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this._getDriverHandle(), selector] as const); } - getElements(windowId: number, selector: string, recursive?: boolean) { - return this._page.evaluate(`window.driver.getElements('${selector}', ${recursive})`); + async getElements(windowId: number, selector: string, recursive: boolean = false) { + return this._page.evaluate(([driver, selector, recursive]) => driver.getElements(selector, recursive), [await this._getDriverHandle(), selector, recursive] as const); } - getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number) { - return this._page.evaluate<{ x: number, y: number }>(`window.driver.getElementXY('${selector}', ${xoffset}, ${yoffset})`); + async getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number) { + return this._page.evaluate(([driver, selector, xoffset, yoffset]) => driver.getElementXY(selector, xoffset, yoffset), [await this._getDriverHandle(), selector, xoffset, yoffset] as const); } async typeInEditor(windowId: number, selector: string, text: string) { - await this._page.evaluate(`window.driver.typeInEditor('${selector}', '${text}')`); + return this._page.evaluate(([driver, selector, text]) => driver.typeInEditor(selector, text), [await this._getDriverHandle(), selector, text] as const); } - getTerminalBuffer(windowId: number, selector: string) { - return this._page.evaluate(`window.driver.getTerminalBuffer('${selector}')`); + async getTerminalBuffer(windowId: number, selector: string) { + return this._page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this._getDriverHandle(), selector] as const); } async writeInTerminal(windowId: number, selector: string, text: string) { - await this._page.evaluate(`window.driver.writeInTerminal('${selector}', '${text}')`); + return this._page.evaluate(([driver, selector, text]) => driver.writeInTerminal(selector, text), [await this._getDriverHandle(), selector, text] as const); } - getLocaleInfo(windowId: number) { - return this._page.evaluate(`window.driver.getLocaleInfo()`); + async getLocaleInfo(windowId: number) { + return this._evaluateWithDriver(([driver]) => driver.getLocaleInfo()); } - getLocalizedStrings(windowId: number) { - return this._page.evaluate(`window.driver.getLocalizedStrings()`); + async getLocalizedStrings(windowId: number) { + return this._evaluateWithDriver(([driver]) => driver.getLocalizedStrings()); + } + + private async _evaluateWithDriver(pageFunction: PageFunction[], T>) { + return this._page.evaluate(pageFunction, [await this._getDriverHandle()]); + } + + // TODO: Cache + private async _getDriverHandle(): Promise> { + return this._page.evaluateHandle('window.driver'); } } From 5bfaf29f2bbcfeb057f68f5df0ffcec33b9dbf18 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 10 Nov 2021 08:38:02 -0800 Subject: [PATCH 011/330] Fix layering problem --- test/automation/src/playwrightDriver.ts | 18 +++++++++++++++++- .../areas/terminal/terminal-profiles.test.ts | 1 - 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 5e9b93f030a..e406b740f85 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -12,7 +12,23 @@ import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; import * as kill from 'tree-kill'; import { PageFunction } from 'playwright-core/types/structs'; -import { IWindowDriver } from '../../../src/vs/platform/driver/common/driver'; +import { IElement, ILocaleInfo, ILocalizedStrings } from '.'; + +// TODO: Copy driver over to ./driver.d.ts? +export interface IWindowDriver { + click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; + doubleClick(selector: string): Promise; + setValue(selector: string, text: string): Promise; + getTitle(): Promise; + isActiveElement(selector: string): Promise; + getElements(selector: string, recursive: boolean): Promise; + getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>; + typeInEditor(selector: string, text: string): Promise; + getTerminalBuffer(selector: string): Promise; + writeInTerminal(selector: string, text: string): Promise; + getLocaleInfo(): Promise; + getLocalizedStrings(): Promise +} const width = 1200; const height = 800; diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts index 6a4004522e4..b4c980dc0e4 100644 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ b/test/smoke/src/areas/terminal/terminal-profiles.test.ts @@ -27,7 +27,6 @@ export function setup(opts: ParsedArgs) { return buffer.some(e => e.length > 100); }, 'The terminal buffer should have some content'); - // Verify the terminal single tab shows up and has a title const terminalTab = await app.code.waitForElement('.single-terminal-tab'); ok(terminalTab.textContent.trim().length > 0); From e6caf0da49000130a6145bd4296b7604e535d839 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 10 Nov 2021 12:27:38 -0800 Subject: [PATCH 012/330] Pass terminal profiles test --- test/smoke/src/areas/terminal/terminal-profiles.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts index b4c980dc0e4..6d5a9757cfe 100644 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ b/test/smoke/src/areas/terminal/terminal-profiles.test.ts @@ -9,7 +9,7 @@ import { Application } from '../../../../automation'; import { afterSuite, beforeSuite } from '../../utils'; export function setup(opts: ParsedArgs) { - describe('Terminal Profiles', () => { + describe.only('Terminal Profiles', () => { let app: Application; beforeSuite(opts); @@ -24,7 +24,7 @@ export function setup(opts: ParsedArgs) { // Verify the terminal buffer has some content await app.workbench.terminal.waitForTerminalText(buffer => { - return buffer.some(e => e.length > 100); + return buffer.some(e => e.length > 0); }, 'The terminal buffer should have some content'); // Verify the terminal single tab shows up and has a title From 9077463a8e19e26b37308dad98dd95e5d1338fd3 Mon Sep 17 00:00:00 2001 From: tanhakabir Date: Mon, 8 Nov 2021 23:20:46 -0800 Subject: [PATCH 013/330] Add option to server for port range to connect within Add option to server for port range to connect within Fix semicolon linting issue Add `pick-port` to list of accepted args --- src/vs/server/main.js | 60 +++++++++++++++++++++-- src/vs/server/serverEnvironmentService.ts | 2 + 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/vs/server/main.js b/src/vs/server/main.js index 7426c16dde7..0b62c86c84c 100644 --- a/src/vs/server/main.js +++ b/src/vs/server/main.js @@ -13,7 +13,7 @@ perf.mark('code/server/start'); // @ts-ignore global.vscodeServerStartTime = performance.now(); -function start() { +async function start() { if (process.argv[2] === '--exec') { process.argv.splice(1, 2); require(process.argv[1]); @@ -25,7 +25,7 @@ function start() { // Do a quick parse to determine if a server or the cli needs to be started const parsedArgs = minimist(process.argv.slice(2), { boolean: ['start-server', 'list-extensions', 'print-ip-address'], - string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port'] + string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port', 'pick-port'] }); const shouldSpawnCli = ( @@ -87,7 +87,7 @@ function start() { const nodeListenOptions = ( parsedArgs['socket-path'] ? { path: parsedArgs['socket-path'] } - : { host: parsedArgs['host'], port: parsePort(parsedArgs['port']) } + : { host: parsedArgs['host'], port: await parsePort(parsedArgs['port'], parsedArgs['pick-port']) } ); server.listen(nodeListenOptions, async () => { const serverGreeting = product.serverGreeting.join('\n'); @@ -129,10 +129,19 @@ function start() { } /** + * If `--port` is specified, connect to that port first. + * + * If not and a port range is specified through `--pick-port` + * then find a free port in that range. Throw error if no + * free port available in range. + * + * In absence of specified ports, connect to port 8000. * @param {string | undefined} strPort - * @returns {number} + * @param {string | undefined} strPickPort + * @returns {Promise} + * @throws */ -function parsePort(strPort) { +async function parsePort(strPort, strPickPort) { try { if (strPort) { return parseInt(strPort); @@ -140,9 +149,50 @@ function parsePort(strPort) { } catch (e) { console.log('Port is not a number, using 8000 instead.'); } + + if (strPickPort) { + let [start, end] = [-1, -1]; + try { + [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr); }); + } catch { + console.log('Port range are not numbers, using 8000 instead.'); + } + + if (start !== -1 && end !== -1) { + return await findFreePort(start, start, end); + } + } + return 8000; } +/** + * Starting at the `start` port, look for a free port incrementing + * by 1 until `end` inclusive. If no port is found error is thrown. + * + * @param {number} start + * @param {number} port + * @param {number} end + * @returns {Promise} + * @throws + */ +async function findFreePort(start, port, end) { + const http = require('http'); + return new Promise((resolve, reject) => { + if (port > end) { + throw new Error(`Could not find free port in range: ${start}-${end}`); + } + + const server = http.createServer(); + server.listen(port, () => { + server.close(); + resolve(port); + }).on('error', () => { + resolve(findFreePort(start, port + 1, end)); + }); + }); +} + /** @returns { Promise } */ function loadCode() { return new Promise((resolve, reject) => { diff --git a/src/vs/server/serverEnvironmentService.ts b/src/vs/server/serverEnvironmentService.ts index 2e8a3ef9394..34846c0e635 100644 --- a/src/vs/server/serverEnvironmentService.ts +++ b/src/vs/server/serverEnvironmentService.ts @@ -11,6 +11,7 @@ import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/envi export const serverOptions: OptionDescriptions = { 'port': { type: 'string' }, + 'pick-port': { type: 'string' }, 'connectionToken': { type: 'string' }, 'connection-secret': { type: 'string', description: nls.localize('connection-secret', "Path to file that contains the connection token. This will require that all incoming connections know the secret.") }, 'host': { type: 'string' }, @@ -58,6 +59,7 @@ export const serverOptions: OptionDescriptions = { export interface ServerParsedArgs { port?: string; + 'pick-port'?: string; connectionToken?: string; /** * A path to a filename which will be read on startup. From b3dbda6140f42b45eb55b07022e2b5fdf941d584 Mon Sep 17 00:00:00 2001 From: tanhakabir Date: Wed, 10 Nov 2021 19:11:07 -0800 Subject: [PATCH 014/330] Make `--port` lower priority in case of port=0. But use port if in pick-port range. port 0 requests any random free port https://unix.stackexchange.com/questions/180492/is-it-possible-to-connect-to-tcp-port-0 --- src/vs/server/main.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/vs/server/main.js b/src/vs/server/main.js index 0b62c86c84c..7c07af3ef5e 100644 --- a/src/vs/server/main.js +++ b/src/vs/server/main.js @@ -129,12 +129,14 @@ async function start() { } /** - * If `--port` is specified, connect to that port first. + * If `--pick-port` and `--port` is specified, connect to that port. * * If not and a port range is specified through `--pick-port` * then find a free port in that range. Throw error if no * free port available in range. * + * If only `--port` is provided then connect to that port. + * * In absence of specified ports, connect to port 8000. * @param {string | undefined} strPort * @param {string | undefined} strPickPort @@ -142,25 +144,32 @@ async function start() { * @throws */ async function parsePort(strPort, strPickPort) { + let specificPort = -1; + try { if (strPort) { - return parseInt(strPort); + specificPort = parseInt(strPort); } } catch (e) { - console.log('Port is not a number, using 8000 instead.'); + console.log('Port is not a number, will default to 8000 if no pick-port is given.'); } if (strPickPort) { - let [start, end] = [-1, -1]; try { - [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr); }); + let [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr); }); + + if (specificPort !== -1 && specificPort >= start && specificPort <= end) { + return specificPort; + } else { + return await findFreePort(start, start, end); + } } catch { console.log('Port range are not numbers, using 8000 instead.'); } + } - if (start !== -1 && end !== -1) { - return await findFreePort(start, start, end); - } + if (specificPort !== -1) { + return specificPort; } return 8000; From db4439c5565331bdcb8499293524980c46f03321 Mon Sep 17 00:00:00 2001 From: Andrii Date: Fri, 12 Nov 2021 06:36:22 +0200 Subject: [PATCH 015/330] Remove code duplication by using existing utils function --- src/vs/editor/common/modes/supports/electricCharacter.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index ead8e6da3a6..1d3ba8ed9ed 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { distinct } from 'vs/base/common/arrays'; import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; @@ -36,12 +37,7 @@ export class BracketElectricCharacterSupport { } } - // Filter duplicate entries - result = result.filter((item, pos, array) => { - return array.indexOf(item) === pos; - }); - - return result; + return distinct(result); } public onElectricCharacter(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { From a4d426a1c2ece4222f1343459d31fe948098b2f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 09:48:08 +0100 Subject: [PATCH 016/330] split vscode.proposed.d.ts into a file per proposal, https://github.com/microsoft/vscode/issues/131165 --- .eslintrc.json | 2 +- .../client/tsconfig.json | 2 +- extensions/debug-server-ready/tsconfig.json | 2 +- extensions/git/tsconfig.json | 2 +- .../github-authentication/tsconfig.json | 2 +- extensions/github/tsconfig.json | 2 +- .../client/tsconfig.json | 2 +- extensions/image-preview/tsconfig.json | 2 +- extensions/ipynb/tsconfig.json | 2 +- .../client/tsconfig.json | 2 +- .../markdown-language-features/tsconfig.json | 2 +- .../microsoft-authentication/tsconfig.json | 2 +- extensions/npm/tsconfig.json | 2 +- .../php-language-features/tsconfig.json | 2 +- extensions/search-result/tsconfig.json | 2 +- extensions/simple-browser/tsconfig.json | 2 +- .../tsconfig.json | 2 +- extensions/vscode-api-tests/tsconfig.json | 2 +- .../vscode-colorize-tests/tsconfig.json | 2 +- .../vscode-notebook-tests/tsconfig.json | 2 +- extensions/vscode-test-resolver/tsconfig.json | 2 +- src/tsconfig.json | 1 - src/tsconfig.vscode-proposed-dts.json | 2 +- .../workbench/api/common/extHost.api.impl.ts | 87 +- .../api/common/extHostExtensionService.ts | 2 +- .../api/common/extHostNotebookKernels.ts | 4 +- src/vs/workbench/api/common/extHostSCM.ts | 8 +- .../common/extensionsApiProposals.ts | 43 +- .../vscode.proposed.customEditorMove.d.ts | 29 + src/vscode-dts/vscode.proposed.d.ts | 2711 ----------------- .../vscode.proposed.diffCommand.d.ts | 38 + ...ode.proposed.documentFiltersExclusive.d.ts | 13 + .../vscode.proposed.extensionRuntime.d.ts | 24 + .../vscode.proposed.externalUriOpener.d.ts | 163 + .../vscode.proposed.fileSearchProvider.d.ts | 67 + .../vscode.proposed.findTextInFiles.d.ts | 98 + .../vscode.proposed.inlayHints.d.ts | 89 + .../vscode.proposed.inlineCompletions.d.ts | 127 + ...e.proposed.notebookCellExecutionState.d.ts | 52 + ...e.proposed.notebookConcatTextDocument.d.ts | 40 + ...code.proposed.notebookContentProvider.d.ts | 65 + .../vscode.proposed.notebookDebugOptions.d.ts | 28 + .../vscode.proposed.notebookDeprecated.d.ts | 13 + .../vscode.proposed.notebookEditor.d.ts | 170 ++ ...proposed.notebookEditorDecorationType.d.ts | 28 + .../vscode.proposed.notebookEditorEdit.d.ts | 53 + .../vscode.proposed.notebookLiveShare.d.ts | 22 + .../vscode.proposed.notebookMessaging.d.ts | 72 + .../vscode.proposed.notebookMime.d.ts | 33 + .../vscode.proposed.portsAttributes.d.ts | 62 + .../vscode.proposed.quickPickSortByLabel.d.ts | 16 + src/vscode-dts/vscode.proposed.resolvers.d.ts | 256 ++ .../vscode.proposed.scmActionButton.d.ts | 12 + .../vscode.proposed.scmSelectedProvider.d.ts | 22 + .../vscode.proposed.scmValidation.d.ts | 60 + src/vscode-dts/vscode.proposed.tabs.d.ts | 100 + ...vscode.proposed.taskPresentationGroup.d.ts | 21 + ...scode.proposed.terminalDataWriteEvent.d.ts | 29 + .../vscode.proposed.terminalDimensions.d.ts | 39 + .../vscode.proposed.terminalLocation.d.ts | 45 + ...code.proposed.terminalNameChangeEvent.d.ts | 30 + .../vscode.proposed.testCoverage.d.ts | 198 ++ .../vscode.proposed.testObserver.d.ts | 202 ++ .../vscode.proposed.textDocumentNotebook.d.ts | 18 + .../vscode.proposed.textSearchProvider.d.ts | 275 ++ src/vscode-dts/vscode.proposed.timeline.d.ts | 163 + .../vscode.proposed.tokenInformation.d.ts | 26 + .../vscode.proposed.treeViewDragAndDrop.d.ts | 71 + .../vscode.proposed.treeViewReveal.d.ts | 13 + .../vscode.proposed.workspaceTrust.d.ts | 30 + 70 files changed, 3027 insertions(+), 2785 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.customEditorMove.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.d.ts create mode 100644 src/vscode-dts/vscode.proposed.diffCommand.d.ts create mode 100644 src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts create mode 100644 src/vscode-dts/vscode.proposed.extensionRuntime.d.ts create mode 100644 src/vscode-dts/vscode.proposed.externalUriOpener.d.ts create mode 100644 src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts create mode 100644 src/vscode-dts/vscode.proposed.findTextInFiles.d.ts create mode 100644 src/vscode-dts/vscode.proposed.inlayHints.d.ts create mode 100644 src/vscode-dts/vscode.proposed.inlineCompletions.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookEditor.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookMessaging.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookMime.d.ts create mode 100644 src/vscode-dts/vscode.proposed.portsAttributes.d.ts create mode 100644 src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts create mode 100644 src/vscode-dts/vscode.proposed.resolvers.d.ts create mode 100644 src/vscode-dts/vscode.proposed.scmActionButton.d.ts create mode 100644 src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts create mode 100644 src/vscode-dts/vscode.proposed.scmValidation.d.ts create mode 100644 src/vscode-dts/vscode.proposed.tabs.d.ts create mode 100644 src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts create mode 100644 src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts create mode 100644 src/vscode-dts/vscode.proposed.terminalDimensions.d.ts create mode 100644 src/vscode-dts/vscode.proposed.terminalLocation.d.ts create mode 100644 src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts create mode 100644 src/vscode-dts/vscode.proposed.testCoverage.d.ts create mode 100644 src/vscode-dts/vscode.proposed.testObserver.d.ts create mode 100644 src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts create mode 100644 src/vscode-dts/vscode.proposed.textSearchProvider.d.ts create mode 100644 src/vscode-dts/vscode.proposed.timeline.d.ts create mode 100644 src/vscode-dts/vscode.proposed.tokenInformation.d.ts create mode 100644 src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts create mode 100644 src/vscode-dts/vscode.proposed.treeViewReveal.d.ts create mode 100644 src/vscode-dts/vscode.proposed.workspaceTrust.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index e55fa836ac9..fbbf4a5d391 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1064,7 +1064,7 @@ { "files": [ "**/vscode.d.ts", - "**/vscode.proposed.d.ts" + "**/vscode.proposed.*.d.ts" ], "rules": { "vscode-dts-create-func": "warn", diff --git a/extensions/css-language-features/client/tsconfig.json b/extensions/css-language-features/client/tsconfig.json index 1e11c9aaccc..e6a92d451d8 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -6,6 +6,6 @@ "include": [ "src/**/*", "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.d.ts", + "../../../src/vscode-dts/vscode.proposed.*.d.ts", ] } diff --git a/extensions/debug-server-ready/tsconfig.json b/extensions/debug-server-ready/tsconfig.json index 88c43e9eba8..ea25ee33fda 100644 --- a/extensions/debug-server-ready/tsconfig.json +++ b/extensions/debug-server-ready/tsconfig.json @@ -10,6 +10,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts", + "../../src/vscode-dts/vscode.proposed.*.d.ts", ] } diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 7f8065b28e7..c64e35be794 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -10,7 +10,7 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts", + "../../src/vscode-dts/vscode.proposed.*.d.ts", "../types/lib.textEncoder.d.ts" ] } diff --git a/extensions/github-authentication/tsconfig.json b/extensions/github-authentication/tsconfig.json index 5001991c9a6..4d6c63c3a86 100644 --- a/extensions/github-authentication/tsconfig.json +++ b/extensions/github-authentication/tsconfig.json @@ -10,6 +10,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/github/tsconfig.json b/extensions/github/tsconfig.json index 5001991c9a6..4d6c63c3a86 100644 --- a/extensions/github/tsconfig.json +++ b/extensions/github/tsconfig.json @@ -10,6 +10,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/html-language-features/client/tsconfig.json b/extensions/html-language-features/client/tsconfig.json index 959c5d37818..f71a7ae175e 100644 --- a/extensions/html-language-features/client/tsconfig.json +++ b/extensions/html-language-features/client/tsconfig.json @@ -6,6 +6,6 @@ "include": [ "src/**/*", "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.d.ts" + "../../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/image-preview/tsconfig.json b/extensions/image-preview/tsconfig.json index c9980a21e38..deb0d3842a3 100644 --- a/extensions/image-preview/tsconfig.json +++ b/extensions/image-preview/tsconfig.json @@ -7,6 +7,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 760d355845b..1e72bb74947 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/json-language-features/client/tsconfig.json b/extensions/json-language-features/client/tsconfig.json index 959c5d37818..f71a7ae175e 100644 --- a/extensions/json-language-features/client/tsconfig.json +++ b/extensions/json-language-features/client/tsconfig.json @@ -6,6 +6,6 @@ "include": [ "src/**/*", "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.d.ts" + "../../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index ecbf24dde1e..5bede697077 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -6,6 +6,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json index c340ac241e9..d8764400622 100644 --- a/extensions/microsoft-authentication/tsconfig.json +++ b/extensions/microsoft-authentication/tsconfig.json @@ -19,6 +19,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/npm/tsconfig.json b/extensions/npm/tsconfig.json index 980445e09b2..3ef85d919ec 100644 --- a/extensions/npm/tsconfig.json +++ b/extensions/npm/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/php-language-features/tsconfig.json b/extensions/php-language-features/tsconfig.json index 980445e09b2..3ef85d919ec 100644 --- a/extensions/php-language-features/tsconfig.json +++ b/extensions/php-language-features/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/search-result/tsconfig.json b/extensions/search-result/tsconfig.json index bd7e74d1d83..ab6639e3a9d 100644 --- a/extensions/search-result/tsconfig.json +++ b/extensions/search-result/tsconfig.json @@ -6,6 +6,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/simple-browser/tsconfig.json b/extensions/simple-browser/tsconfig.json index c9980a21e38..deb0d3842a3 100644 --- a/extensions/simple-browser/tsconfig.json +++ b/extensions/simple-browser/tsconfig.json @@ -7,6 +7,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 9eac9f6421f..2d5e8c31a3b 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -11,6 +11,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.languageStatus.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/vscode-api-tests/tsconfig.json b/extensions/vscode-api-tests/tsconfig.json index 980445e09b2..3ef85d919ec 100644 --- a/extensions/vscode-api-tests/tsconfig.json +++ b/extensions/vscode-api-tests/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/vscode-colorize-tests/tsconfig.json b/extensions/vscode-colorize-tests/tsconfig.json index 980445e09b2..3ef85d919ec 100644 --- a/extensions/vscode-colorize-tests/tsconfig.json +++ b/extensions/vscode-colorize-tests/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/vscode-notebook-tests/tsconfig.json b/extensions/vscode-notebook-tests/tsconfig.json index 980445e09b2..3ef85d919ec 100644 --- a/extensions/vscode-notebook-tests/tsconfig.json +++ b/extensions/vscode-notebook-tests/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/extensions/vscode-test-resolver/tsconfig.json b/extensions/vscode-test-resolver/tsconfig.json index 980445e09b2..3ef85d919ec 100644 --- a/extensions/vscode-test-resolver/tsconfig.json +++ b/extensions/vscode-test-resolver/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts" + "../../src/vscode-dts/vscode.proposed.*.d.ts" ] } diff --git a/src/tsconfig.json b/src/tsconfig.json index 02e85647832..eaaa3fb52b8 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -26,7 +26,6 @@ "./typings", "./vs", "vscode-dts/vscode.proposed.*.d.ts", - "vscode-dts/vscode.proposed.d.ts", "vscode-dts/vscode.d.ts" ] } diff --git a/src/tsconfig.vscode-proposed-dts.json b/src/tsconfig.vscode-proposed-dts.json index 802e37e567e..dd12f3bca28 100644 --- a/src/tsconfig.vscode-proposed-dts.json +++ b/src/tsconfig.vscode-proposed-dts.json @@ -2,6 +2,6 @@ "extends": "./tsconfig.vscode-dts.json", "include": [ "vscode-dts/vscode.d.ts", - "vscode-dts/vscode.proposed.d.ts", + "vscode-dts/vscode.proposed.*.d.ts", ] } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 052b141e659..96e3f393275 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -218,7 +218,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I informOnce(); } if (typeof filter.exclusive === 'boolean') { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'documentFiltersExclusive'); } } return selector; @@ -271,7 +271,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, undefined, undefined, extension); }, registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'diffCommand'); return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { const activeTextEditor = extHostDocumentsAndEditors.activeEditor(true); if (!activeTextEditor) { @@ -339,7 +339,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return getRemoteName(initData.remote.authority); }, get remoteAuthority() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return initData.remote.authority; }, get uiKind() { @@ -360,19 +360,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTesting.createTestController(provider, label); }, createTestObserver() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.createTestObserver(); }, runTests(provider) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.runTests(provider); }, get onDidChangeTestResults() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.onResultsChanged; }, get testResults() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'testObserver'); return extHostTesting.results; }, }; @@ -484,7 +484,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, registerInlineCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'inlineCompletions'); return extHostLanguageFeatures.registerInlineCompletionsProvider(extension, checkSelector(selector), provider); }, registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { @@ -509,11 +509,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.setLanguageConfiguration(extension, language, configuration); }, getTokenInformationAtPosition(doc: vscode.TextDocument, pos: vscode.Position) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tokenInformation'); return extHostLanguages.tokenAtPosition(doc, pos); }, registerInlayHintsProvider(selector: vscode.DocumentSelector, provider: vscode.InlayHintsProvider): vscode.Disposable { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'inlayHints'); return extHostLanguageFeatures.registerInlayHintsProvider(extension, selector, provider); }, createLanguageStatusItem(id: string, selector: vscode.DocumentSelector): vscode.LanguageStatusItem { @@ -574,14 +574,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables); }, onDidChangeTerminalDimensions(listener, thisArg?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'terminalDimensions'); return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables); }, onDidChangeTerminalState(listener, thisArg?, disposables?) { return extHostTerminalService.onDidChangeTerminalState(listener, thisArg, disposables); }, onDidWriteTerminalData(listener, thisArg?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'terminalDataWriteEvent'); return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables); }, get state() { @@ -655,7 +655,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { if (typeof nameOrOptions === 'object') { if ('location' in nameOrOptions) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'terminalLocation'); } if ('pty' in nameOrOptions) { return extHostTerminalService.createExtensionTerminal(nameOrOptions); @@ -708,55 +708,55 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWebviewViews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions); }, get activeNotebookEditor(): vscode.NotebookEditor | undefined { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.activeNotebookEditor; }, onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables); }, get visibleNotebookEditors() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.visibleNotebookEditors; }, get onDidChangeVisibleNotebookEditors() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeVisibleNotebookEditors; }, onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookEditors.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables); }, onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookEditors.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables); }, showNotebookDocument(uriOrDocument, options?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.showNotebookDocument(uriOrDocument, options); }, registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'externalUriOpener'); return extHostUriOpeners.registerExternalUriOpener(extension.identifier, id, opener, metadata); }, get tabs() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.tabs; }, get activeTab() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.activeTab; }, get onDidChangeTabs() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.onDidChangeTabs; }, get onDidChangeActiveTab() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.onDidChangeActiveTab; }, getInlineCompletionItemController(provider: vscode.InlineCompletionItemProvider): vscode.InlineCompletionController { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'inlineCompletions'); return InlineCompletionController.get(provider); } }; @@ -805,6 +805,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.identifier, token); }, findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback: vscode.FindTextInFilesOptions | ((result: vscode.TextSearchResult) => void), callbackOrToken?: vscode.CancellationToken | ((result: vscode.TextSearchResult) => void), token?: vscode.CancellationToken) => { + checkProposedApiEnabled(extension, 'findTextInFiles'); let options: vscode.FindTextInFilesOptions; let callback: (result: vscode.TextSearchResult) => void; @@ -891,11 +892,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.onDidCloseNotebookDocument; }, registerNotebookSerializer(viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData) { - return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options, isProposedApiEnabled(extension) ? registration : undefined); + return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options, isProposedApiEnabled(extension, 'notebookLiveShare') ? registration : undefined); }, registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData) => { - checkProposedApiEnabled(extension); - return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options, isProposedApiEnabled(extension) ? registration : undefined); + checkProposedApiEnabled(extension, 'notebookContentProvider'); + return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options, isProposedApiEnabled(extension, 'notebookLiveShare') ? registration : undefined); }, onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { return configProvider.onDidChangeConfiguration(listener, thisArgs, disposables); @@ -920,19 +921,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostConsumerFileSystem.value; }, registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'fileSearchProvider'); return extHostSearch.registerFileSearchProvider(scheme, provider); }, registerTextSearchProvider: (scheme: string, provider: vscode.TextSearchProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'textSearchProvider'); return extHostSearch.registerTextSearchProvider(scheme, provider); }, registerRemoteAuthorityResolver: (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); }, registerResourceLabelFormatter: (formatter: vscode.ResourceLabelFormatter) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostLabelService.$registerResourceLabelFormatter(formatter); }, onDidCreateFiles: (listener, thisArg, disposables) => { @@ -954,7 +955,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); }, openTunnel: (forward: vscode.TunnelOptions) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostTunnelService.openTunnel(extension, forward).then(value => { if (!value) { throw new Error('cannot open tunnel'); @@ -963,26 +964,26 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }); }, get tunnels() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostTunnelService.getTunnels(); }, onDidChangeTunnels: (listener, thisArg?, disposables?) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables); }, registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'portsAttributes'); return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider); }, registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'timeline'); return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter); }, get isTrusted() { return extHostWorkspace.trusted; }, requestWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'workspaceTrust'); return extHostWorkspace.requestWorkspaceTrust(options); }, onDidGrantWorkspaceTrust: (listener, thisArgs?, disposables?) => { @@ -1095,7 +1096,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: notebook const notebooks: typeof vscode.notebooks = { createNotebookController(id: string, notebookType: string, label: string, handler?, rendererScripts?: vscode.NotebookRendererScript[]) { - return extHostNotebookKernels.createNotebookController(extension, id, notebookType, label, handler, isProposedApiEnabled(extension) ? rendererScripts : undefined); + return extHostNotebookKernels.createNotebookController(extension, id, notebookType, label, handler, isProposedApiEnabled(extension, 'notebookMessaging') ? rendererScripts : undefined); }, registerNotebookCellStatusBarItemProvider: (notebookType: string, provider: vscode.NotebookCellStatusBarItemProvider) => { return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, notebookType, provider); @@ -1105,7 +1106,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebookDocuments.onDidSaveNotebookDocument; }, createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditorDecorationType'); return extHostNotebookEditors.createNotebookEditorDecorationType(options); }, createRendererMessaging(rendererId) { @@ -1120,7 +1121,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables); }, onDidChangeNotebookCellExecutionState(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookCellExecutionState'); return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables); }, onDidChangeCellOutputs(listener, thisArgs?, disposables?) { @@ -1132,7 +1133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables); }, createConcatTextDocument(notebook, selector) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookConcatTextDocument'); return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector); }, }; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 81bfcbc8280..7be1359c2ec 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -447,7 +447,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return extension; }, get extensionRuntime() { - checkProposedApiEnabled(extensionDescription); + checkProposedApiEnabled(extensionDescription, 'extensionRuntime'); return that.extensionRuntime; }, get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); } diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 91b9099a90b..9d6e323212a 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -234,11 +234,11 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { // --- ipc onDidReceiveMessage: onDidReceiveMessage.event, postMessage(message, editor) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookMessaging'); return that._proxy.$postMessage(handle, editor && that._extHostNotebook.getIdByEditor(editor), message); }, asWebviewUri(uri: URI) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookMessaging'); return asWebviewUri(uri, that._initData.remote); }, }; diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 1db3c75923c..306e7e373f8 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -230,13 +230,13 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { private _validateInput: IValidateInput | undefined; get validateInput(): IValidateInput | undefined { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmValidation'); return this._validateInput; } set validateInput(fn: IValidateInput | undefined) { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmValidation'); if (fn && typeof fn !== 'function') { throw new Error(`[${this._extension.identifier.value}]: Invalid SCM input box validation function`); @@ -268,7 +268,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { } showValidationMessage(message: string | vscode.MarkdownString, type: vscode.SourceControlInputBoxValidationType) { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmValidation'); this._proxy.$showValidationMessage(this._sourceControlHandle, message, type as any); } @@ -506,7 +506,7 @@ class ExtHostSourceControl implements vscode.SourceControl { return this._actionButton; } set actionButton(actionButton: vscode.Command | undefined) { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmActionButton'); this._actionButtonDisposables.value = new DisposableStore(); this._actionButton = actionButton; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 801927b4506..04349afa658 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -6,8 +6,49 @@ // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. export const allApiProposals = Object.freeze({ + customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', + diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', + documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', + extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts', + externalUriOpener: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts', + fileSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts', + findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts', fsChunks: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fsChunks.d.ts', - languageStatus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatus.d.ts' + inlayHints: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlayHints.d.ts', + inlineCompletions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts', + languageStatus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatus.d.ts', + notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', + notebookConcatTextDocument: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts', + notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', + notebookDebugOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts', + notebookDeprecated: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts', + notebookEditor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditor.d.ts', + notebookEditorDecorationType: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts', + notebookEditorEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts', + notebookLiveShare: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts', + notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', + notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', + portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', + quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', + resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', + scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', + scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', + scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', + tabs: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tabs.d.ts', + taskPresentationGroup: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', + terminalDataWriteEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts', + terminalDimensions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts', + terminalLocation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalLocation.d.ts', + terminalNameChangeEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts', + testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts', + testObserver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts', + textDocumentNotebook: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts', + textSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts', + timeline: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.timeline.d.ts', + tokenInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tokenInformation.d.ts', + treeViewDragAndDrop: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts', + treeViewReveal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts', + workspaceTrust: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts' }); export type ApiProposalName = keyof typeof allApiProposals; diff --git a/src/vscode-dts/vscode.proposed.customEditorMove.d.ts b/src/vscode-dts/vscode.proposed.customEditorMove.d.ts new file mode 100644 index 00000000000..f988913ea61 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.customEditorMove.d.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * 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/86146 + + // TODO: Also for custom editor + + export interface CustomTextEditorProvider { + + /** + * Handle when the underlying resource for a custom editor is renamed. + * + * This allows the webview for the editor be preserved throughout the rename. If this method is not implemented, + * the editor will destroy the previous custom editor and create a replacement one. + * + * @param newDocument New text document to use for the custom editor. + * @param existingWebviewPanel Webview panel for the custom editor. + * @param token A cancellation token that indicates the result is no longer needed. + * + * @return Thenable indicating that the webview editor has been moved. + */ + // eslint-disable-next-line vscode-dts-provider-naming + moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel, token: CancellationToken): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.d.ts b/src/vscode-dts/vscode.proposed.d.ts deleted file mode 100644 index 8c8510cfa24..00000000000 --- a/src/vscode-dts/vscode.proposed.d.ts +++ /dev/null @@ -1,2711 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * This is the place for API experiments and proposals. - * These API are NOT stable and subject to change. They are only available in the Insiders - * distribution and CANNOT be used in published extensions. - * - * To test these API in local environment: - * - Use Insiders release of 'VS Code'. - * - Add `"enableProposedApi": true` to your package.json. - * - Copy this file to your project. - */ - -declare module 'vscode' { - - //#region resolvers: @alexdima - - export interface MessageOptions { - /** - * Do not render a native message box. - */ - useCustom?: boolean; - } - - export interface RemoteAuthorityResolverContext { - resolveAttempt: number; - } - - export class ResolvedAuthority { - readonly host: string; - readonly port: number; - readonly connectionToken: string | undefined; - - constructor(host: string, port: number, connectionToken?: string); - } - - export interface ResolvedOptions { - extensionHostEnv?: { [key: string]: string | null; }; - - isTrusted?: boolean; - } - - export interface TunnelPrivacy { - themeIcon: string; - id: string; - label: string; - } - - export interface TunnelOptions { - remoteAddress: { port: number, host: string; }; - // The desired local port. If this port can't be used, then another will be chosen. - localAddressPort?: number; - label?: string; - /** - * @deprecated Use privacy instead - */ - public?: boolean; - privacy?: string; - protocol?: string; - } - - export interface TunnelDescription { - remoteAddress: { port: number, host: string; }; - //The complete local address(ex. localhost:1234) - localAddress: { port: number, host: string; } | string; - /** - * @deprecated Use privacy instead - */ - public?: boolean; - privacy?: string; - // If protocol is not provided it is assumed to be http, regardless of the localAddress. - protocol?: string; - } - - export interface Tunnel extends TunnelDescription { - // Implementers of Tunnel should fire onDidDispose when dispose is called. - onDidDispose: Event; - dispose(): void | Thenable; - } - - /** - * Used as part of the ResolverResult if the extension has any candidate, - * published, or forwarded ports. - */ - export interface TunnelInformation { - /** - * Tunnels that are detected by the extension. The remotePort is used for display purposes. - * The localAddress should be the complete local address (ex. localhost:1234) for connecting to the port. Tunnels provided through - * detected are read-only from the forwarded ports UI. - */ - environmentTunnels?: TunnelDescription[]; - - } - - export interface TunnelCreationOptions { - /** - * True when the local operating system will require elevation to use the requested local port. - */ - elevationRequired?: boolean; - } - - export enum CandidatePortSource { - None = 0, - Process = 1, - Output = 2 - } - - export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; - - export class RemoteAuthorityResolverError extends Error { - static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; - static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError; - - constructor(message?: string); - } - - export interface RemoteAuthorityResolver { - /** - * Resolve the authority part of the current opened `vscode-remote://` URI. - * - * This method will be invoked once during the startup of the editor and again each time - * the editor detects a disconnection. - * - * @param authority The authority part of the current opened `vscode-remote://` URI. - * @param context A context indicating if this is the first call or a subsequent call. - */ - resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable; - - /** - * Get the canonical URI (if applicable) for a `vscode-remote://` URI. - * - * @returns The canonical URI or undefined if the uri is already canonical. - */ - getCanonicalURI?(uri: Uri): ProviderResult; - - /** - * Can be optionally implemented if the extension can forward ports better than the core. - * When not implemented, the core will use its default forwarding logic. - * When implemented, the core will use this to forward ports. - * - * To enable the "Change Local Port" action on forwarded ports, make sure to set the `localAddress` of - * the returned `Tunnel` to a `{ port: number, host: string; }` and not a string. - */ - tunnelFactory?: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined; - - /**p - * Provides filtering for candidate ports. - */ - showCandidatePort?: (host: string, port: number, detail: string) => Thenable; - - /** - * Lets the resolver declare which tunnel factory features it supports. - * UNDER DISCUSSION! MAY CHANGE SOON. - */ - tunnelFeatures?: { - elevation: boolean; - /** - * @deprecated Use privacy instead - */ - public: boolean; - /** - * One of the the options must have the ID "private". - */ - privacyOptions: TunnelPrivacy[]; - }; - - candidatePortSource?: CandidatePortSource; - } - - /** - * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. - */ - export interface AuthenticationGetSessionOptions { - /** - * 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. - * - * Defaults to false. - */ - forceNewSession?: boolean | { detail: string }; - } - - 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 & { forceNewSession: true | { detail: string } }): Thenable; - export function hasSession(providerId: string, scopes: readonly string[]): Thenable; - } - - export namespace workspace { - /** - * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. - * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. - * - * @throws When run in an environment without a remote. - * - * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. - */ - export function openTunnel(tunnelOptions: TunnelOptions): Thenable; - - /** - * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user. - * Note that these are of type TunnelDescription and cannot be disposed. - */ - export let tunnels: Thenable; - - /** - * Fired when the list of tunnels has changed. - */ - export const onDidChangeTunnels: Event; - } - - export interface ResourceLabelFormatter { - scheme: string; - authority?: string; - formatting: ResourceLabelFormatting; - } - - export interface ResourceLabelFormatting { - label: string; // myLabel:/${path} - // For historic reasons we use an or string here. Once we finalize this API we should start using enums instead and adopt it in extensions. - // eslint-disable-next-line vscode-dts-literal-or-types - separator: '/' | '\\' | ''; - tildify?: boolean; - normalizeDriveLetter?: boolean; - workspaceSuffix?: string; - workspaceTooltip?: string; - authorityPrefix?: string; - stripPathStartingSeparator?: boolean; - } - - export namespace workspace { - export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; - export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable; - } - - export namespace env { - - /** - * The authority part of the current opened `vscode-remote://` URI. - * Defined by extensions, e.g. `ssh-remote+${host}` 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 remoteAuthority: string | undefined; - - } - - //#endregion - - //#region textSearchProvider: https://github.com/microsoft/vscode/issues/59921 - - /** - * The parameters of a query for text search. - */ - export interface TextSearchQuery { - /** - * The text pattern to search for. - */ - pattern: string; - - /** - * Whether or not `pattern` should match multiple lines of text. - */ - isMultiline?: boolean; - - /** - * Whether or not `pattern` should be interpreted as a regular expression. - */ - isRegExp?: boolean; - - /** - * Whether or not the search should be case-sensitive. - */ - isCaseSensitive?: boolean; - - /** - * Whether or not to search for whole word matches only. - */ - isWordMatch?: boolean; - } - - /** - * A file glob pattern to match file paths against. - * TODO@roblourens merge this with the GlobPattern docs/definition in vscode.d.ts. - * @see {@link GlobPattern} - */ - export type GlobString = string; - - /** - * Options common to file and text search - */ - export interface SearchOptions { - /** - * The root folder to search within. - */ - folder: Uri; - - /** - * Files that match an `includes` glob pattern should be included in the search. - */ - includes: GlobString[]; - - /** - * Files that match an `excludes` glob pattern should be excluded from the search. - */ - excludes: GlobString[]; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles: boolean; - } - - /** - * Options to specify the size of the result text preview. - * These options don't affect the size of the match itself, just the amount of preview text. - */ - export interface TextSearchPreviewOptions { - /** - * The maximum number of lines in the preview. - * Only search providers that support multiline search will ever return more than one line in the match. - */ - matchLines: number; - - /** - * The maximum number of characters included per line. - */ - charsPerLine: number; - } - - /** - * Options that apply to text search. - */ - export interface TextSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults: number; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Exclude files larger than `maxFileSize` in bytes. - */ - maxFileSize?: number; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - /** - * Represents the severiry of a TextSearchComplete message. - */ - export enum TextSearchCompleteMessageType { - Information = 1, - Warning = 2, - } - - /** - * A message regarding a completed search. - */ - export interface TextSearchCompleteMessage { - /** - * Markdown text of the message. - */ - text: string, - /** - * Whether the source of the message is trusted, command links are disabled for untrusted message sources. - * Messaged are untrusted by default. - */ - trusted?: boolean, - /** - * The message type, this affects how the message will be rendered. - */ - type: TextSearchCompleteMessageType, - } - - /** - * Information collected when text search is complete. - */ - export interface TextSearchComplete { - /** - * Whether the search hit the limit on the maximum number of search results. - * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results. - * - If exactly that number of matches exist, this should be false. - * - If `maxResults` matches are returned and more exist, this should be true. - * - If search hits an internal limit which is less than `maxResults`, this should be true. - */ - limitHit?: boolean; - - /** - * Additional information regarding the state of the completed search. - * - * Messages with "Information" style support links in markdown syntax: - * - Click to [run a command](command:workbench.action.OpenQuickPick) - * - Click to [open a website](https://aka.ms) - * - * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. - */ - message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; - } - - /** - * A preview of the text result. - */ - export interface TextSearchMatchPreview { - /** - * The matching lines of text, or a portion of the matching line that contains the match. - */ - text: string; - - /** - * The Range within `text` corresponding to the text of the match. - * The number of matches must match the TextSearchMatch's range property. - */ - matches: Range | Range[]; - } - - /** - * A match from a text search - */ - export interface TextSearchMatch { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * The range of the match within the document, or multiple ranges for multiple matches. - */ - ranges: Range | Range[]; - - /** - * A preview of the text match. - */ - preview: TextSearchMatchPreview; - } - - /** - * A line of context surrounding a TextSearchMatch. - */ - export interface TextSearchContext { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * One line of text. - * previewOptions.charsPerLine applies to this - */ - text: string; - - /** - * The line number of this line of context. - */ - lineNumber: number; - } - - export type TextSearchResult = TextSearchMatch | TextSearchContext; - - /** - * A TextSearchProvider provides search results for text results inside files in the workspace. - */ - export interface TextSearchProvider { - /** - * Provide results that match the given text pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; - } - - //#endregion - - //#region fileSearchProvider: https://github.com/microsoft/vscode/issues/73524 - - /** - * The parameters of a query for file search. - */ - export interface FileSearchQuery { - /** - * The search pattern to match against file paths. - */ - pattern: string; - } - - /** - * Options that apply to file search. - */ - export interface FileSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults?: number; - - /** - * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, - * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. - */ - session?: CancellationToken; - } - - /** - * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. - * - * A FileSearchProvider is the more powerful of two ways to implement file search in the editor. Use a FileSearchProvider if you wish to search within a folder for - * all files that match the user's query. - * - * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, - * and in that case, every file in the folder should be returned. - */ - export interface FileSearchProvider { - /** - * Provide the set of files that match a certain file path pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching files. - * @param token A cancellation token. - */ - provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * Register a search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; - - /** - * Register a text search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; - } - - //#endregion - - //#region findTextInFiles: https://github.com/microsoft/vscode/issues/59924 - - /** - * Options that can be set on a findTextInFiles search. - */ - export interface FindTextInFilesOptions { - /** - * A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern - * will be matched against the file paths of files relative to their workspace. Use a {@link RelativePattern relative pattern} - * to restrict the search results to a {@link WorkspaceFolder workspace folder}. - */ - include?: GlobPattern; - - /** - * 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 excludes will - * apply. - */ - exclude?: GlobPattern; - - /** - * Whether to use the default and user-configured excludes. Defaults to true. - */ - useDefaultExcludes?: boolean; - - /** - * The maximum number of results to search for - */ - maxResults?: number; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles?: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles?: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks?: boolean; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - export namespace workspace { - /** - * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - - /** - * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - } - - //#endregion - - //#region diffCommand: https://github.com/microsoft/vscode/issues/84899 - - /** - * The contiguous set of modified lines in a diff. - */ - export interface LineChange { - readonly originalStartLineNumber: number; - readonly originalEndLineNumber: number; - readonly modifiedStartLineNumber: number; - readonly modifiedEndLineNumber: number; - } - - export namespace commands { - - /** - * Registers a diff information command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Diff information commands are different from ordinary {@link commands.registerCommand commands} as - * they only execute when there is an active diff editor when the command is called, and the diff - * information has been computed. Also, the command handler of an editor command has access to - * the diff information. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to the {@link LineChange diff information}. - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; - } - - //#endregion - - // eslint-disable-next-line vscode-dts-region-comments - //#region notebookDebugOptions: @roblourens: new debug session option for simple UI 'managedByParent' (see https://github.com/microsoft/vscode/issues/128588) - - /** - * Options for {@link debug.startDebugging starting a debug session}. - */ - export interface DebugSessionOptions { - - debugUI?: { - /** - * When true, the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed. - */ - simple?: 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; - } - - //#endregion - - // #region scmValidation: @joaomoreno - - /** - * Represents the validation type of the Source Control input. - */ - export enum SourceControlInputBoxValidationType { - - /** - * Something not allowed by the rules of a language or other means. - */ - Error = 0, - - /** - * Something suspicious but allowed. - */ - Warning = 1, - - /** - * Something to inform about but not a problem. - */ - Information = 2 - } - - export interface SourceControlInputBoxValidation { - - /** - * The validation message to display. - */ - readonly message: string | MarkdownString; - - /** - * The validation type. - */ - readonly type: SourceControlInputBoxValidationType; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Shows a transient contextual message on the input. - */ - showValidationMessage(message: string | MarkdownString, type: SourceControlInputBoxValidationType): void; - - /** - * A validation function for the input box. It's possible to change - * the validation provider simply by setting this property to a different function. - */ - validateInput?(value: string, cursorPosition: number): ProviderResult; - } - - //#endregion - - //#region scmSelectedProvider: @joaomoreno - - export interface SourceControl { - - /** - * Whether the source control is selected. - */ - readonly selected: boolean; - - /** - * An event signaling when the selection state changes. - */ - readonly onDidChangeSelection: Event; - } - - //#endregion - - //#region terminalDataWriteEvent: https://github.com/microsoft/vscode/issues/78502 - - export interface TerminalDataWriteEvent { - /** - * The {@link Terminal} for which the data was written. - */ - readonly terminal: Terminal; - /** - * The data being written. - */ - readonly data: string; - } - - namespace window { - /** - * An event which fires when the terminal's child pseudo-device is written to (the shell). - * In other words, this provides access to the raw data stream from the process running - * within the terminal, including VT sequences. - */ - export const onDidWriteTerminalData: Event; - } - - //#endregion - - //#region terminalDimensions: https://github.com/microsoft/vscode/issues/55718 - - /** - * An {@link Event} which fires when a {@link Terminal}'s dimensions change. - */ - export interface TerminalDimensionsChangeEvent { - /** - * The {@link Terminal} for which the dimensions have changed. - */ - readonly terminal: Terminal; - /** - * The new value for the {@link Terminal.dimensions terminal's dimensions}. - */ - readonly dimensions: TerminalDimensions; - } - - export namespace window { - /** - * An event which fires when the {@link Terminal.dimensions dimensions} of the terminal change. - */ - export const onDidChangeTerminalDimensions: Event; - } - - export interface Terminal { - /** - * The current dimensions of the terminal. This will be `undefined` immediately after the - * terminal is created as the dimensions are not known until shortly after the terminal is - * created. - */ - readonly dimensions: TerminalDimensions | undefined; - } - - //#endregion - - //#region terminalLocation: https://github.com/microsoft/vscode/issues/45407 - - export interface TerminalOptions { - location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; - } - - export interface ExtensionTerminalOptions { - location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; - } - - export enum TerminalLocation { - Panel = 1, - Editor = 2, - } - - export interface TerminalEditorLocationOptions { - /** - * A view column in which the {@link Terminal terminal} should be shown in the editor area. - * Use {@link ViewColumn.Active active} to open in the active editor group, other values are - * adjusted to be `Min(column, columnCount + 1)`, the - * {@link ViewColumn.Active active}-column is not adjusted. 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; - } - - 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; - } - - //#endregion - - //#region terminalNameChangeEvent: https://github.com/microsoft/vscode/issues/114898 - - export interface Pseudoterminal { - /** - * An event that when fired allows changing the name of the terminal. - * - * **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; - } - - //#endregion - - //#region exclusiveDocumentFilters: @jrieken - - export interface DocumentFilter { - readonly exclusive?: boolean; - } - - //#endregion - - //#region treeViewReveal: https://github.com/microsoft/vscode/issues/61313 @alexr00 - export interface TreeView extends Disposable { - reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number; }): Thenable; - } - //#endregion - - //#region treeViewDragAndDrop: https://github.com/microsoft/vscode/issues/32592 - /** - * 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`. - */ - onDidChangeTreeData2?: Event; - } - - export interface TreeViewOptions { - /** - * An optional interface to implement drag and drop in the tree view. - */ - dragAndDropController?: DragAndDropController; - } - - export interface TreeDataTransferItem { - asString(): Thenable; - } - - export interface TreeDataTransfer { - /** - * A map containing a mapping of the mime type of the corresponding data. - * The type for tree elements is text/treeitem. - * For example, you can reconstruct the your tree elements: - * ```ts - * JSON.parse(await (items.get('text/treeitem')!.asString())) - * ``` - */ - items: { get: (mimeType: string) => TreeDataTransferItem | undefined }; - } - - export interface DragAndDropController extends Disposable { - readonly supportedTypes: string[]; - - /** - * todo@API maybe - * - * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, - * `onWillDrop` will be called with the dropped tree item. This is the DragAndDropController's opportunity to - * package the data from the dropped tree item into whatever format they want the target tree item to receive. - * - * The returned `TreeDataTransfer` will be merged with the original`TreeDataTransfer` for the operation. - * - * Note for implementation later: This means that the `text/treeItem` mime type will go away. - * - * @param source - */ - // onWillDrop?(source: T): Thenable; - - /** - * Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed. - * - * @param source - * @param target - */ - onDrop(source: TreeDataTransfer, target: T): Thenable; - } - //#endregion - - //#region taskPresentationGroup: https://github.com/microsoft/vscode/issues/47265 - export interface TaskPresentationOptions { - /** - * Controls whether the task is executed in a specific terminal group using split panes. - */ - group?: string; - - /** - * Controls whether the terminal is closed after executing the task. - */ - close?: boolean; - } - //#endregion - - //#region customEditorMove: https://github.com/microsoft/vscode/issues/86146 - - // TODO: Also for custom editor - - export interface CustomTextEditorProvider { - - /** - * Handle when the underlying resource for a custom editor is renamed. - * - * This allows the webview for the editor be preserved throughout the rename. If this method is not implemented, - * the editor will destroy the previous custom editor and create a replacement one. - * - * @param newDocument New text document to use for the custom editor. - * @param existingWebviewPanel Webview panel for the custom editor. - * @param token A cancellation token that indicates the result is no longer needed. - * - * @return Thenable indicating that the webview editor has been moved. - */ - // eslint-disable-next-line vscode-dts-provider-naming - moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel, token: CancellationToken): Thenable; - } - - //#endregion - - //#region quickPickSortByLabel: https://github.com/microsoft/vscode/issues/73904 - - export interface QuickPick extends QuickInput { - /** - * An optional flag to sort the final results by index of first query match in label. Defaults to true. - */ - sortByLabel: boolean; - } - - //#endregion - - //#region notebookCellExecutionState: https://github.com/microsoft/vscode/issues/124970 - - /** - * The execution state of a notebook cell. - */ - export enum NotebookCellExecutionState { - /** - * The cell is idle. - */ - Idle = 1, - /** - * Execution for the cell is pending. - */ - Pending = 2, - /** - * The cell is currently executing. - */ - Executing = 3, - } - - /** - * An event describing a cell execution state change. - */ - export interface NotebookCellExecutionStateChangeEvent { - /** - * The {@link NotebookCell cell} for which the execution state has changed. - */ - readonly cell: NotebookCell; - - /** - * The new execution state of the cell. - */ - readonly state: NotebookCellExecutionState; - } - - export namespace notebooks { - - /** - * An {@link Event} which fires when the execution state of a cell has changed. - */ - // todo@API this is an event that is fired for a property that cells don't have and that makes me wonder - // how a correct consumer works, e.g the consumer could have been late and missed an event? - export const onDidChangeNotebookCellExecutionState: Event; - } - - //#endregion - - //#region notebookDeprecated: https://github.com/microsoft/vscode/issues/106744 - - export interface NotebookCellOutput { - id: string; - } - - //#endregion - - //#region notebookEditor: https://github.com/microsoft/vscode/issues/106744 - - /** - * 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}. - */ - export interface NotebookEditor { - /** - * The document associated with this notebook editor. - */ - //todo@api rename to notebook? - readonly document: NotebookDocument; - - /** - * The selections on 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: NotebookRange[]; - - /** - * The current visible ranges in the editor (vertically). - */ - readonly visibleRanges: NotebookRange[]; - - /** - * 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; - - /** - * The column in which this editor shows. - */ - readonly viewColumn?: ViewColumn; - } - - export interface NotebookDocumentMetadataChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the document metadata have changed. - */ - //todo@API rename to notebook? - readonly document: NotebookDocument; - } - - export interface NotebookCellsChangeData { - readonly start: number; - // todo@API end? Use NotebookCellRange instead? - readonly deletedCount: number; - // todo@API removedCells, deletedCells? - readonly deletedItems: NotebookCell[]; - // todo@API addedCells, insertedCells, newCells? - readonly items: NotebookCell[]; - } - - export interface NotebookCellsChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the cells have changed. - */ - //todo@API rename to notebook? - readonly document: NotebookDocument; - readonly changes: ReadonlyArray; - } - - export interface NotebookCellOutputsChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the cell outputs have changed. - */ - //todo@API remove? use cell.notebook instead? - readonly document: NotebookDocument; - // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell - readonly cells: NotebookCell[]; - } - - export interface NotebookCellMetadataChangeEvent { - /** - * The {@link NotebookDocument notebook document} for which the cell metadata have changed. - */ - //todo@API remove? use cell.notebook instead? - readonly document: NotebookDocument; - // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell - readonly cell: NotebookCell; - } - - export interface NotebookEditorSelectionChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the selections have changed. - */ - readonly notebookEditor: NotebookEditor; - readonly selections: ReadonlyArray - } - - export interface NotebookEditorVisibleRangesChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. - */ - readonly notebookEditor: NotebookEditor; - readonly visibleRanges: ReadonlyArray; - } - - - export interface NotebookDocumentShowOptions { - viewColumn?: ViewColumn; - preserveFocus?: boolean; - preview?: boolean; - selections?: NotebookRange[]; - } - - export namespace notebooks { - - - - export const onDidSaveNotebookDocument: Event; - - export const onDidChangeNotebookDocumentMetadata: Event; - export const onDidChangeNotebookCells: Event; - - // todo@API add onDidChangeNotebookCellOutputs - export const onDidChangeCellOutputs: Event; - - // todo@API add onDidChangeNotebookCellMetadata - export const onDidChangeCellMetadata: Event; - } - - export namespace window { - export const visibleNotebookEditors: NotebookEditor[]; - export const onDidChangeVisibleNotebookEditors: Event; - export const activeNotebookEditor: NotebookEditor | undefined; - export const onDidChangeActiveNotebookEditor: Event; - export const onDidChangeNotebookEditorSelection: Event; - export const onDidChangeNotebookEditorVisibleRanges: Event; - - export function showNotebookDocument(uri: Uri, options?: NotebookDocumentShowOptions): Thenable; - export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; - } - - //#endregion - - //#region notebookEditorEdit: https://github.com/microsoft/vscode/issues/106744 - - // todo@API add NotebookEdit-type which handles all these cases? - // export class NotebookEdit { - // range: NotebookRange; - // newCells: NotebookCellData[]; - // newMetadata?: NotebookDocumentMetadata; - // constructor(range: NotebookRange, newCells: NotebookCellData) - // } - - // export class NotebookCellEdit { - // newMetadata?: NotebookCellMetadata; - // } - - // export interface WorkspaceEdit { - // set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void - // } - - export interface WorkspaceEdit { - // todo@API add NotebookEdit-type which handles all these cases? - replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; - replaceNotebookCells(uri: Uri, range: NotebookRange, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; - replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: { [key: string]: any }, metadata?: WorkspaceEditEntryMetadata): void; - } - - export interface NotebookEditorEdit { - replaceMetadata(value: { [key: string]: any }): void; - replaceCells(start: number, end: number, cells: NotebookCellData[]): void; - replaceCellMetadata(index: number, metadata: { [key: string]: any }): void; - } - - export interface NotebookEditor { - /** - * Perform an edit on the notebook associated with this notebook editor. - * - * The given callback-function is invoked with an {@link NotebookEditorEdit 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 NotebookEditorEdit edit-builder}. - * @return A promise that resolves with a value indicating if the edits could be applied. - */ - // @jrieken REMOVE maybe - edit(callback: (editBuilder: NotebookEditorEdit) => void): Thenable; - } - - //#endregion - - //#region notebookEditorDecorationType: https://github.com/microsoft/vscode/issues/106744 - - export interface NotebookEditor { - setDecorations(decorationType: NotebookEditorDecorationType, range: NotebookRange): void; - } - - export interface NotebookDecorationRenderOptions { - backgroundColor?: string | ThemeColor; - borderColor?: string | ThemeColor; - top?: ThemableDecorationAttachmentRenderOptions; - } - - export interface NotebookEditorDecorationType { - readonly key: string; - dispose(): void; - } - - export namespace notebooks { - export function createNotebookEditorDecorationType(options: NotebookDecorationRenderOptions): NotebookEditorDecorationType; - } - - //#endregion - - //#region notebookConcatTextDocument: https://github.com/microsoft/vscode/issues/106744 - - export namespace notebooks { - /** - * Create a document that is the concatenation of all notebook cells. By default all code-cells are included - * but a selector can be provided to narrow to down the set of cells. - * - * @param notebook - * @param selector - */ - // todo@API really needed? we didn't find a user here - export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; - } - - export interface NotebookConcatTextDocument { - readonly uri: Uri; - readonly isClosed: boolean; - dispose(): void; - readonly onDidChange: Event; - readonly version: number; - getText(): string; - getText(range: Range): string; - - offsetAt(position: Position): number; - positionAt(offset: number): Position; - validateRange(range: Range): Range; - validatePosition(position: Position): Position; - - locationAt(positionOrRange: Position | Range): Location; - positionAt(location: Location): Position; - contains(uri: Uri): boolean; - } - - //#endregion - - //#region notebookContentProvider: https://github.com/microsoft/vscode/issues/106744 - - interface NotebookDocumentBackup { - /** - * Unique identifier for the backup. - * - * This id is passed back to your extension in `openNotebook` when opening a notebook 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; - } - - interface NotebookDocumentBackupContext { - readonly destination: Uri; - } - - interface NotebookDocumentOpenContext { - readonly backupId?: string; - readonly untitledDocumentData?: Uint8Array; - } - - // todo@API use openNotebookDOCUMENT to align with openCustomDocument etc? - // todo@API rename to NotebookDocumentContentProvider - export interface NotebookContentProvider { - - readonly options?: NotebookDocumentContentOptions; - readonly onDidChangeNotebookContentOptions?: Event; - - /** - * Content providers should always use {@link FileSystemProvider file system providers} to - * resolve the raw content for `uri` as the resouce is not necessarily a file on disk. - */ - openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext, token: CancellationToken): NotebookData | Thenable; - - // todo@API use NotebookData instead - saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable; - - // todo@API use NotebookData instead - saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable; - - // todo@API use NotebookData instead - backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable; - } - - export namespace workspace { - - // TODO@api use NotebookDocumentFilter instead of just notebookType:string? - // TODO@API options duplicates the more powerful variant on NotebookContentProvider - export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable; - } - - //#endregion - - //#region notebookLiveShare: https://github.com/microsoft/vscode/issues/106744 - - export interface NotebookRegistrationData { - displayName: string; - filenamePattern: (GlobPattern | { include: GlobPattern; exclude: GlobPattern; })[]; - exclusive?: boolean; - } - - export namespace workspace { - // SPECIAL overload with NotebookRegistrationData - export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions, registrationData?: NotebookRegistrationData): Disposable; - // SPECIAL overload with NotebookRegistrationData - export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions, registration?: NotebookRegistrationData): Disposable; - } - - //#endregion - - //#region notebookMessaging: https://github.com/microsoft/vscode/issues/123601 - - /** - * Represents a script that is loaded into the notebook renderer before rendering output. This allows - * to provide and share functionality for notebook markup and notebook output renderers. - */ - export class NotebookRendererScript { - - /** - * APIs that the preload provides to the renderer. These are matched - * against the `dependencies` and `optionalDependencies` arrays in the - * notebook renderer contribution point. - */ - provides: string[]; - - /** - * URI of the JavaScript module to preload. - * - * This module must export an `activate` function that takes a context object that contains the notebook API. - */ - uri: Uri; - - /** - * @param uri URI of the JavaScript module to preload - * @param provides Value for the `provides` property - */ - constructor(uri: Uri, provides?: string | string[]); - } - - export interface NotebookController { - /** - * The human-readable label used to categorise controllers. - */ - kind?: string; - - // todo@API allow add, not remove - readonly rendererScripts: NotebookRendererScript[]; - - /** - * An event that fires when a {@link NotebookController.rendererScripts renderer script} has send a message to - * the controller. - */ - readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>; - - /** - * Send a message to the renderer of notebook editors. - * - * Note that only editors showing documents that are bound to this controller - * are receiving the message. - * - * @param message The message to send. - * @param editor A specific editor to send the message to. When `undefined` all applicable editors are receiving the message. - * @returns A promise that resolves to a boolean indicating if the message has been send or not. - */ - postMessage(message: any, editor?: NotebookEditor): Thenable; - - //todo@API validate this works - asWebviewUri(localResource: Uri): Uri; - } - - export namespace notebooks { - - export function createNotebookController(id: string, viewType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable, rendererScripts?: NotebookRendererScript[]): NotebookController; - } - - //#endregion - - //#region timeline: https://github.com/microsoft/vscode/issues/84297 - - export class TimelineItem { - /** - * A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred. - */ - timestamp: number; - - /** - * A human-readable string describing the timeline item. - */ - label: string; - - /** - * Optional id for the timeline item. It must be unique across all the timeline items provided by this source. - * - * If not provided, an id is generated using the timeline item's timestamp. - */ - id?: string; - - /** - * The icon path or {@link ThemeIcon} for the timeline item. - */ - iconPath?: Uri | { light: Uri; dark: Uri; } | ThemeIcon; - - /** - * A human readable string describing less prominent details of the timeline item. - */ - description?: string; - - /** - * The tooltip text when you hover over the timeline item. - */ - detail?: string; - - /** - * The {@link Command} that should be executed when the timeline item is selected. - */ - command?: Command; - - /** - * Context value of the timeline item. This can be used to contribute specific actions to the item. - * For example, a timeline item is given a context value as `commit`. When contributing actions to `timeline/item/context` - * using `menus` extension point, you can specify context value for key `timelineItem` in `when` expression like `timelineItem == commit`. - * ``` - * "contributes": { - * "menus": { - * "timeline/item/context": [ - * { - * "command": "extension.copyCommitId", - * "when": "timelineItem == commit" - * } - * ] - * } - * } - * ``` - * This will show the `extension.copyCommitId` action only for items where `contextValue` is `commit`. - */ - contextValue?: string; - - /** - * Accessibility information used when screen reader interacts with this timeline item. - */ - accessibilityInformation?: AccessibilityInformation; - - /** - * @param label A human-readable string describing the timeline item - * @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred - */ - constructor(label: string, timestamp: number); - } - - export interface TimelineChangeEvent { - /** - * The {@link Uri} of the resource for which the timeline changed. - */ - uri: Uri; - - /** - * A flag which indicates whether the entire timeline should be reset. - */ - reset?: boolean; - } - - export interface Timeline { - readonly paging?: { - /** - * A provider-defined cursor specifying the starting point of timeline items which are after the ones returned. - * Use `undefined` to signal that there are no more items to be returned. - */ - readonly cursor: string | undefined; - }; - - /** - * An array of {@link TimelineItem timeline items}. - */ - readonly items: readonly TimelineItem[]; - } - - export interface TimelineOptions { - /** - * A provider-defined cursor specifying the starting point of the timeline items that should be returned. - */ - cursor?: string; - - /** - * An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned. - * If `undefined` all timeline items should be returned. - */ - limit?: number | { timestamp: number; id?: string; }; - } - - export interface TimelineProvider { - /** - * An optional event to signal that the timeline for a source has changed. - * To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`. - */ - onDidChange?: Event; - - /** - * An identifier of the source of the timeline items. This can be used to filter sources. - */ - readonly id: string; - - /** - * A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources. - */ - readonly label: string; - - /** - * Provide {@link TimelineItem timeline items} for a {@link Uri}. - * - * @param uri The {@link Uri} of the file to provide the timeline for. - * @param options A set of options to determine how results should be returned. - * @param token A cancellation token. - * @return The {@link TimelineResult timeline result} or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * Register a timeline provider. - * - * Multiple providers can be registered. In that case, providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param scheme A scheme or schemes that defines which documents this provider is applicable to. Can be `*` to target all documents. - * @param provider A timeline provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerTimelineProvider(scheme: string | string[], provider: TimelineProvider): Disposable; - } - - //#endregion - - //#region tokenInformation: https://github.com/microsoft/vscode/issues/91555 - - export enum StandardTokenType { - Other = 0, - Comment = 1, - String = 2, - RegEx = 4 - } - - export interface TokenInformation { - type: StandardTokenType; - range: Range; - } - - export namespace languages { - export function getTokenInformationAtPosition(document: TextDocument, position: Position): Thenable; - } - - //#endregion - - //#region inlayHints: https://github.com/microsoft/vscode/issues/16221 - - // todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range) - // todo@API add "mini-markdown" for links and styles - // (done) remove description - // (done) rename to InlayHint - // (done) add InlayHintKind with type, argument, etc - - export namespace languages { - /** - * 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; - } - - export enum InlayHintKind { - Other = 0, - Type = 1, - Parameter = 2, - } - - /** - * Inlay hint information. - */ - export class InlayHint { - /** - * The text of the hint. - */ - // todo@API label? - text: string; - /** - * The position of this hint. - */ - position: Position; - /** - * The kind of this hint. - */ - kind?: InlayHintKind; - /** - * Whitespace before the hint. - */ - whitespaceBefore?: boolean; - /** - * Whitespace after the hint. - */ - whitespaceAfter?: boolean; - - // todo@API make range first argument - constructor(text: string, position: Position, 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 have changed. - * @see {@link EventEmitter} - */ - //todo@API needs proper doc (like others) - onDidChangeInlayHints?: Event; - - /** - * - * @param model 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 A list of inlay hints or a thenable that resolves to such. - */ - provideInlayHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; - } - //#endregion - - //#region extensionRuntime: https://github.com/microsoft/vscode/issues/104436 - - export enum ExtensionRuntime { - /** - * The extension is running in a NodeJS extension host. Runtime access to NodeJS APIs is available. - */ - Node = 1, - /** - * The extension is running in a Webworker extension host. Runtime access is limited to Webworker APIs. - */ - Webworker = 2 - } - - export interface ExtensionContext { - readonly extensionRuntime: ExtensionRuntime; - } - - //#endregion - - //#region textDocumentNotebook: https://github.com/microsoft/vscode/issues/102091 - - export interface TextDocument { - - /** - * The {@link NotebookDocument notebook} that contains this document as a notebook cell or `undefined` when - * the document is not contained by a notebook (this should be the more frequent case). - */ - notebook: NotebookDocument | undefined; - } - //#endregion - - //#region testObserver: https://github.com/microsoft/vscode/issues/107467 - export namespace tests { - /** - * Requests that tests be run by their controller. - * @param run Run options to use. - * @param token Cancellation token for the test run - */ - export function runTests(run: TestRunRequest, token?: CancellationToken): Thenable; - - /** - * Returns an observer that watches and can request tests. - */ - export function createTestObserver(): TestObserver; - /** - * List of test results stored by the editor, sorted in descending - * order by their `completedAt` time. - */ - export const testResults: ReadonlyArray; - - /** - * Event that fires when the {@link testResults} array is updated. - */ - export const onDidChangeTestResults: Event; - } - - export interface TestObserver { - /** - * List of tests returned by test provider for files in the workspace. - */ - readonly tests: ReadonlyArray; - - /** - * An event that fires when an existing test in the collection changes, or - * null if a top-level test was added or removed. When fired, the consumer - * should check the test item and all its children for changes. - */ - readonly onDidChangeTest: Event; - - /** - * Dispose of the observer, allowing the editor to eventually tell test - * providers that they no longer need to update tests. - */ - dispose(): void; - } - - export interface TestsChangeEvent { - /** - * List of all tests that are newly added. - */ - readonly added: ReadonlyArray; - - /** - * List of existing tests that have updated. - */ - readonly updated: ReadonlyArray; - - /** - * List of existing tests that have been removed. - */ - readonly removed: ReadonlyArray; - } - - /** - * A test item is an item shown in the "test explorer" view. It encompasses - * both a suite and a test, since they have almost or identical capabilities. - */ - export interface TestItem { - /** - * Marks the test as outdated. This can happen as a result of file changes, - * for example. In "auto run" mode, tests that are outdated will be - * automatically rerun after a short delay. Invoking this on a - * test with children will mark the entire subtree as outdated. - * - * Extensions should generally not override this method. - */ - // todo@api still unsure about this - invalidateResults(): void; - } - - - /** - * TestResults can be provided to the editor in {@link tests.publishTestResult}, - * or read from it in {@link tests.testResults}. - * - * The results contain a 'snapshot' of the tests at the point when the test - * run is complete. Therefore, information such as its {@link Range} may be - * out of date. If the test still exists in the workspace, consumers can use - * its `id` to correlate the result instance with the living test. - */ - export interface TestRunResult { - /** - * Unix milliseconds timestamp at which the test run was completed. - */ - readonly completedAt: number; - - /** - * Optional raw output from the test run. - */ - readonly output?: string; - - /** - * List of test results. The items in this array are the items that - * were passed in the {@link tests.runTests} method. - */ - readonly results: ReadonlyArray>; - } - - /** - * A {@link TestItem}-like interface with an associated result, which appear - * or can be provided in {@link TestResult} interfaces. - */ - export interface TestResultSnapshot { - /** - * Unique identifier that matches that of the associated TestItem. - * This is used to correlate test results and tests in the document with - * those in the workspace (test explorer). - */ - readonly id: string; - - /** - * Parent of this item. - */ - readonly parent?: TestResultSnapshot; - - /** - * URI this TestItem is associated with. May be a file or file. - */ - readonly uri?: Uri; - - /** - * Display name describing the test case. - */ - readonly label: string; - - /** - * Optional description that appears next to the label. - */ - readonly description?: string; - - /** - * Location of the test item in its `uri`. This is only meaningful if the - * `uri` points to a file. - */ - readonly range?: Range; - - /** - * State of the test in each task. In the common case, a test will only - * be executed in a single task and the length of this array will be 1. - */ - readonly taskStates: ReadonlyArray; - - /** - * Optional list of nested tests for this item. - */ - readonly children: Readonly[]; - } - - export interface TestSnapshotTaskState { - /** - * Current result of the test. - */ - readonly state: TestResultState; - - /** - * The number of milliseconds the test took to run. This is set once the - * `state` is `Passed`, `Failed`, or `Errored`. - */ - readonly duration?: number; - - /** - * Associated test run message. Can, for example, contain assertion - * failure information if the test fails. - */ - readonly messages: ReadonlyArray; - } - - /** - * Possible states of tests in a test run. - */ - export enum TestResultState { - // Test will be run, but is not currently running. - Queued = 1, - // Test is currently running - Running = 2, - // Test run has passed - Passed = 3, - // Test run has failed (on an assertion) - Failed = 4, - // Test run has been skipped - Skipped = 5, - // Test run failed for some other reason (compilation error, timeout, etc) - Errored = 6 - } - - //#endregion - - //#region externalUriOpener: https://github.com/microsoft/vscode/issues/109277 - - /** - * Details if an `ExternalUriOpener` can open a uri. - * - * The priority is also used to rank multiple openers against each other and determine - * if an opener should be selected automatically or if the user should be prompted to - * select an opener. - * - * The editor will try to use the best available opener, as sorted by `ExternalUriOpenerPriority`. - * If there are multiple potential "best" openers for a URI, then the user will be prompted - * to select an opener. - */ - export enum ExternalUriOpenerPriority { - /** - * The opener is disabled and will never be shown to users. - * - * Note that the opener can still be used if the user specifically - * configures it in their settings. - */ - None = 0, - - /** - * The opener can open the uri but will not cause a prompt on its own - * since the editor always contributes a built-in `Default` opener. - */ - Option = 1, - - /** - * The opener can open the uri. - * - * The editor's built-in opener has `Default` priority. This means that any additional `Default` - * openers will cause the user to be prompted to select from a list of all potential openers. - */ - Default = 2, - - /** - * The opener can open the uri and should be automatically selected over any - * default openers, include the built-in one from the editor. - * - * A preferred opener will be automatically selected if no other preferred openers - * are available. If multiple preferred openers are available, then the user - * is shown a prompt with all potential openers (not just preferred openers). - */ - Preferred = 3, - } - - /** - * Handles opening uris to external resources, such as http(s) links. - * - * Extensions can implement an `ExternalUriOpener` to open `http` links to a webserver - * inside of the editor instead of having the link be opened by the web browser. - * - * Currently openers may only be registered for `http` and `https` uris. - */ - export interface ExternalUriOpener { - - /** - * Check if the opener can open a uri. - * - * @param uri The uri being opened. This is the uri that the user clicked on. It has - * not yet gone through port forwarding. - * @param token Cancellation token indicating that the result is no longer needed. - * - * @return Priority indicating if the opener can open the external uri. - */ - canOpenExternalUri(uri: Uri, token: CancellationToken): ExternalUriOpenerPriority | Thenable; - - /** - * Open a uri. - * - * This is invoked when: - * - * - The user clicks a link which does not have an assigned opener. In this case, first `canOpenExternalUri` - * is called and if the user selects this opener, then `openExternalUri` is called. - * - The user sets the default opener for a link in their settings and then visits a link. - * - * @param resolvedUri The uri to open. This uri may have been transformed by port forwarding, so it - * may not match the original uri passed to `canOpenExternalUri`. Use `ctx.originalUri` to check the - * original uri. - * @param ctx Additional information about the uri being opened. - * @param token Cancellation token indicating that opening has been canceled. - * - * @return Thenable indicating that the opening has completed. - */ - openExternalUri(resolvedUri: Uri, ctx: OpenExternalUriContext, token: CancellationToken): Thenable | void; - } - - /** - * Additional information about the uri being opened. - */ - interface OpenExternalUriContext { - /** - * The uri that triggered the open. - * - * This is the original uri that the user clicked on or that was passed to `openExternal.` - * Due to port forwarding, this may not match the `resolvedUri` passed to `openExternalUri`. - */ - readonly sourceUri: Uri; - } - - /** - * Additional metadata about a registered `ExternalUriOpener`. - */ - interface ExternalUriOpenerMetadata { - - /** - * List of uri schemes the opener is triggered for. - * - * Currently only `http` and `https` are supported. - */ - readonly schemes: readonly string[] - - /** - * Text displayed to the user that explains what the opener does. - * - * For example, 'Open in browser preview' - */ - readonly label: string; - } - - namespace window { - /** - * Register a new `ExternalUriOpener`. - * - * When a uri is about to be opened, an `onOpenExternalUri:SCHEME` activation event is fired. - * - * @param id Unique id of the opener, such as `myExtension.browserPreview`. This is used in settings - * and commands to identify the opener. - * @param opener Opener to register. - * @param metadata Additional information about the opener. - * - * @returns Disposable that unregisters the opener. - */ - export function registerExternalUriOpener(id: string, opener: ExternalUriOpener, metadata: ExternalUriOpenerMetadata): Disposable; - } - - interface OpenExternalOptions { - /** - * Allows using openers contributed by extensions through `registerExternalUriOpener` - * when opening the resource. - * - * If `true`, the editor will check if any contributed openers can handle the - * uri, and fallback to the default opener behavior. - * - * If it is string, this specifies the id of the `ExternalUriOpener` - * that should be used if it is available. Use `'default'` to force the editor's - * standard external opener to be used. - */ - readonly allowContributedOpeners?: boolean | string; - } - - namespace env { - export function openExternal(target: Uri, options?: OpenExternalOptions): Thenable; - } - - //#endregion - - //#region tabs: https://github.com/Microsoft/vscode/issues/15178 - - /** - * Represents a tab within the window - */ - export interface Tab { - /** - * The text displayed on the tab - */ - readonly label: string; - - /** - * The index of the tab within the column - */ - readonly index: number; - - /** - * The column which the tab belongs to - */ - readonly viewColumn: ViewColumn; - - /** - * The resource represented by the tab if available. - * Note: Not all tabs have a resource associated with them. - */ - readonly resource: Uri | undefined; - - /** - * The identifier of the view contained in the tab - * This is equivalent to `viewType` for custom editors and `notebookType` for notebooks. - * The built-in text editor has an id of 'default' for all configurations. - */ - readonly viewId: string | undefined; - - /** - * All the resources and viewIds represented by a tab - * {@link Tab.resource resource} and {@link Tab.viewId viewId} will - * always be at index 0. - */ - readonly additionalResourcesAndViewIds: readonly { - readonly resource: Uri | undefined, - readonly viewId: string | undefined - }[]; - - /** - * Whether or not the tab is currently active - * Dictated by being the selected tab in the active group - */ - readonly isActive: boolean; - - /** - * Moves a tab to the given index within the column. - * If the index is out of range, the tab will be moved to the end of the column. - * If the column is out of range, a new one will be created after the last existing column. - * @param index The index to move the tab to - * @param viewColumn The column to move the tab into - */ - move(index: number, viewColumn: ViewColumn): Thenable; - - /** - * Closes the tab. This makes the tab object invalid and the tab - * should no longer be used for further actions. - */ - close(): Thenable; - } - - export namespace window { - /** - * A list of all opened tabs - * Ordered from left to right - */ - export const tabs: readonly Tab[]; - - /** - * The currently active tab - * Undefined if no tabs are currently opened - */ - export const activeTab: Tab | undefined; - - /** - * An {@link Event} which fires when the array of {@link window.tabs tabs} - * has changed. - */ - export const onDidChangeTabs: Event; - - /** - * An {@link Event} which fires when the {@link window.activeTab activeTab} - * has changed. - */ - export const onDidChangeActiveTab: Event; - - } - - //#endregion - - //#region workspaceTrust: https://github.com/microsoft/vscode/issues/120173 - /** - * The object describing the properties of the workspace trust request - */ - export interface WorkspaceTrustRequestOptions { - /** - * Custom message describing the user action that requires workspace - * trust. If omitted, a generic message will be displayed in the workspace - * trust request dialog. - */ - readonly message?: string; - } - - export namespace workspace { - /** - * Prompt the user to chose whether to trust the current workspace - * @param options Optional object describing the properties of the - * workspace trust request. - */ - export function requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Thenable; - } - - //#endregion - - //#region portAttributesProvider: https://github.com/microsoft/vscode/issues/115616 @alexr00 - export enum PortAutoForwardAction { - Notify = 1, - OpenBrowser = 2, - OpenPreview = 3, - Silent = 4, - Ignore = 5, - OpenBrowserOnce = 6 - } - - export class PortAttributes { - /** - * The port number associated with this this set of attributes. - */ - port: number; - - /** - * The action to be taken when this port is detected for auto forwarding. - */ - autoForwardAction: PortAutoForwardAction; - - /** - * Creates a new PortAttributes object - * @param port the port number - * @param autoForwardAction the action to take when this port is detected - */ - constructor(port: number, autoForwardAction: PortAutoForwardAction); - } - - export interface PortAttributesProvider { - /** - * Provides attributes for the given port. For ports that your extension doesn't know about, simply - * return undefined. For example, if `providePortAttributes` is called with ports 3000 but your - * extension doesn't know anything about 3000 you should return undefined. - */ - providePortAttributes(port: number, pid: number | undefined, commandLine: string | undefined, token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * If your extension listens on ports, consider registering a PortAttributesProvider to provide information - * about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing - * this information with a PortAttributesProvider the extension can tell the editor that these ports should be - * ignored, since they don't need to be user facing. - * - * @param portSelector If registerPortAttributesProvider is called after you start your process then you may already - * know the range of ports or the pid of your process. All properties of a the portSelector must be true for your - * provider to get called. - * The `portRange` is start inclusive and end exclusive. - * @param provider The PortAttributesProvider - */ - export function registerPortAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: PortAttributesProvider): Disposable; - } - //#endregion - - //#region inlineCompletionProvider: https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima - - export namespace languages { - /** - * Registers an inline completion provider. - */ - export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; - } - - 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 asks for the next or previous inline completion. - * Use `context.triggerKind` to distinguish between these scenarios. - */ - provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult | T[]>; - } - - export interface InlineCompletionContext { - /** - * How the 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. - * - * The user must configure `"editor.suggest.preview": true` for this feature. - */ - readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; - } - - export interface SelectedCompletionInfo { - range: Range; - text: string; - } - - /** - * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. - */ - export enum InlineCompletionTriggerKind { - /** - * Completion was triggered automatically while editing. - * It is sufficient to return a single completion item in this case. - */ - Automatic = 0, - - /** - * Completion was triggered explicitly by a user gesture. - * Return multiple completion items to enable cycling through them. - */ - Explicit = 1, - } - - export class InlineCompletionList { - items: T[]; - - constructor(items: T[]); - } - - export class InlineCompletionItem { - /** - * The text to replace the range with. - * - * The text the range refers to should be a prefix of this value and must be a subword (`AB` and `BEF` are subwords of `ABCDEF`, but `Ab` is not). - */ - text: string; - - /** - * The range to replace. - * Must begin and end on the same line. - * - * Prefer replacements over insertions to avoid cache invalidation: - * Instead of reporting a completion that inserts an extension at the end of a word, - * the whole word should be replaced with the extended word. - */ - range?: Range; - - /** - * An optional {@link Command} that is executed *after* inserting this completion. - */ - command?: Command; - - constructor(text: string, range?: Range, command?: Command); - } - - - /** - * Be aware that this API will not ever be finalized. - */ - export namespace window { - export function getInlineCompletionItemController(provider: InlineCompletionItemProvider): InlineCompletionController; - } - - /** - * Be aware that this API will not ever be finalized. - */ - export interface InlineCompletionController { - /** - * Is fired when an inline completion item is shown to the user. - */ - // eslint-disable-next-line vscode-dts-event-naming - readonly onDidShowCompletionItem: Event>; - } - - /** - * Be aware that this API will not ever be finalized. - */ - export interface InlineCompletionItemDidShowEvent { - completionItem: T; - } - - //#endregion - - //#region notebookMime: https://github.com/microsoft/vscode/issues/126280 @mjbvz - - export interface NotebookCellData { - /** - * Mime type determines how the cell's `value` is interpreted. - * - * The mime selects which notebook renders is used to render the cell. - * - * If not set, internally the cell is treated as having a mime type of `text/plain`. - * Cells that set `language` to `markdown` instead are treated as `text/markdown`. - */ - mime?: string; - } - - export interface NotebookCell { - /** - * Mime type determines how the markup cell's `value` is interpreted. - * - * The mime selects which notebook renders is used to render the cell. - * - * If not set, internally the cell is treated as having a mime type of `text/plain`. - * Cells that set `language` to `markdown` instead are treated as `text/markdown`. - */ - mime: string | undefined; - } - - //#endregion - - //#region testCoverage: https://github.com/microsoft/vscode/issues/123713 @connor4312 - export interface TestRun { - /** - * Test coverage provider for this result. An extension can defer setting - * this until after a run is complete and coverage is available. - */ - coverageProvider?: TestCoverageProvider - // ... - } - - /** - * Provides information about test coverage for a test result. - * Methods on the provider will not be called until the test run is complete - */ - export interface TestCoverageProvider { - /** - * Returns coverage information for all files involved in the test run. - * @param token A cancellation token. - * @return Coverage metadata for all files involved in the test. - */ - provideFileCoverage(token: CancellationToken): ProviderResult; - - /** - * Give a FileCoverage to fill in more data, namely {@link FileCoverage.detailedCoverage}. - * The editor will only resolve a FileCoverage once, and onyl if detailedCoverage - * is undefined. - * - * @param coverage A coverage object obtained from {@link provideFileCoverage} - * @param token A cancellation token. - * @return The resolved file coverage, or a thenable that resolves to one. It - * is OK to return the given `coverage`. When no result is returned, the - * given `coverage` will be used. - */ - resolveFileCoverage?(coverage: T, token: CancellationToken): ProviderResult; - } - - /** - * A class that contains information about a covered resource. A count can - * be give for lines, branches, and functions in a file. - */ - export class CoveredCount { - /** - * Number of items covered in the file. - */ - covered: number; - /** - * Total number of covered items in the file. - */ - total: number; - - /** - * @param covered Value for {@link CovereredCount.covered} - * @param total Value for {@link CovereredCount.total} - */ - constructor(covered: number, total: number); - } - - /** - * Contains coverage metadata for a file. - */ - export class FileCoverage { - /** - * File URI. - */ - readonly uri: Uri; - - /** - * Statement coverage information. If the reporter does not provide statement - * coverage information, this can instead be used to represent line coverage. - */ - statementCoverage: CoveredCount; - - /** - * Branch coverage information. - */ - branchCoverage?: CoveredCount; - - /** - * Function coverage information. - */ - functionCoverage?: CoveredCount; - - /** - * Detailed, per-statement coverage. If this is undefined, the editor will - * call {@link TestCoverageProvider.resolveFileCoverage} when necessary. - */ - detailedCoverage?: DetailedCoverage[]; - - /** - * Creates a {@link FileCoverage} instance with counts filled in from - * the coverage details. - * @param uri Covered file URI - * @param detailed Detailed coverage information - */ - static fromDetails(uri: Uri, details: readonly DetailedCoverage[]): FileCoverage; - - /** - * @param uri Covered file URI - * @param statementCoverage Statement coverage information. If the reporter - * does not provide statement coverage information, this can instead be - * used to represent line coverage. - * @param branchCoverage Branch coverage information - * @param functionCoverage Function coverage information - */ - constructor( - uri: Uri, - statementCoverage: CoveredCount, - branchCoverage?: CoveredCount, - functionCoverage?: CoveredCount, - ); - } - - /** - * Contains coverage information for a single statement or line. - */ - export class StatementCoverage { - /** - * The number of times this statement was executed. If zero, the - * statement will be marked as un-covered. - */ - executionCount: number; - - /** - * Statement location. - */ - location: Position | Range; - - /** - * Coverage from branches of this line or statement. If it's not a - * conditional, this will be empty. - */ - branches: BranchCoverage[]; - - /** - * @param location The statement position. - * @param executionCount The number of times this statement was - * executed. If zero, the statement will be marked as un-covered. - * @param branches Coverage from branches of this line. If it's not a - * conditional, this should be omitted. - */ - constructor(executionCount: number, location: Position | Range, branches?: BranchCoverage[]); - } - - /** - * Contains coverage information for a branch of a {@link StatementCoverage}. - */ - export class BranchCoverage { - /** - * The number of times this branch was executed. If zero, the - * branch will be marked as un-covered. - */ - executionCount: number; - - /** - * Branch location. - */ - location?: Position | Range; - - /** - * @param executionCount The number of times this branch was executed. - * @param location The branch position. - */ - constructor(executionCount: number, location?: Position | Range); - } - - /** - * Contains coverage information for a function or method. - */ - export class FunctionCoverage { - /** - * The number of times this function was executed. If zero, the - * function will be marked as un-covered. - */ - executionCount: number; - - /** - * Function location. - */ - location: Position | Range; - - /** - * @param executionCount The number of times this function was executed. - * @param location The function position. - */ - constructor(executionCount: number, location: Position | Range); - } - - export type DetailedCoverage = StatementCoverage | FunctionCoverage; - - //#endregion - - - - //#region scmActionButton: https://github.com/microsoft/vscode/issues/133935 - - export interface SourceControl { - actionButton?: Command; - } - - //#endregion - -} diff --git a/src/vscode-dts/vscode.proposed.diffCommand.d.ts b/src/vscode-dts/vscode.proposed.diffCommand.d.ts new file mode 100644 index 00000000000..84f4328e077 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.diffCommand.d.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * 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/84899 + + /** + * The contiguous set of modified lines in a diff. + */ + export interface LineChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; + } + + export namespace commands { + + /** + * Registers a diff information command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Diff information commands are different from ordinary {@link commands.registerCommand commands} as + * they only execute when there is an active diff editor when the command is called, and the diff + * information has been computed. Also, the command handler of an editor command has access to + * the diff information. + * + * @param command A unique identifier for the command. + * @param callback A command handler function with access to the {@link LineChange diff information}. + * @param thisArg The `this` context used when invoking the handler function. + * @return Disposable which unregisters this command on disposal. + */ + export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts b/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts new file mode 100644 index 00000000000..7d033796355 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + // todo@jrieken add issue reference + + export interface DocumentFilter { + readonly exclusive?: boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts b/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts new file mode 100644 index 00000000000..356587ebc95 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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/104436 + + export enum ExtensionRuntime { + /** + * The extension is running in a NodeJS extension host. Runtime access to NodeJS APIs is available. + */ + Node = 1, + /** + * The extension is running in a Webworker extension host. Runtime access is limited to Webworker APIs. + */ + Webworker = 2 + } + + export interface ExtensionContext { + readonly extensionRuntime: ExtensionRuntime; + } +} diff --git a/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts b/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts new file mode 100644 index 00000000000..2b5432404f7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * 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/109277 + + /** + * Details if an `ExternalUriOpener` can open a uri. + * + * The priority is also used to rank multiple openers against each other and determine + * if an opener should be selected automatically or if the user should be prompted to + * select an opener. + * + * The editor will try to use the best available opener, as sorted by `ExternalUriOpenerPriority`. + * If there are multiple potential "best" openers for a URI, then the user will be prompted + * to select an opener. + */ + export enum ExternalUriOpenerPriority { + /** + * The opener is disabled and will never be shown to users. + * + * Note that the opener can still be used if the user specifically + * configures it in their settings. + */ + None = 0, + + /** + * The opener can open the uri but will not cause a prompt on its own + * since the editor always contributes a built-in `Default` opener. + */ + Option = 1, + + /** + * The opener can open the uri. + * + * The editor's built-in opener has `Default` priority. This means that any additional `Default` + * openers will cause the user to be prompted to select from a list of all potential openers. + */ + Default = 2, + + /** + * The opener can open the uri and should be automatically selected over any + * default openers, include the built-in one from the editor. + * + * A preferred opener will be automatically selected if no other preferred openers + * are available. If multiple preferred openers are available, then the user + * is shown a prompt with all potential openers (not just preferred openers). + */ + Preferred = 3, + } + + /** + * Handles opening uris to external resources, such as http(s) links. + * + * Extensions can implement an `ExternalUriOpener` to open `http` links to a webserver + * inside of the editor instead of having the link be opened by the web browser. + * + * Currently openers may only be registered for `http` and `https` uris. + */ + export interface ExternalUriOpener { + + /** + * Check if the opener can open a uri. + * + * @param uri The uri being opened. This is the uri that the user clicked on. It has + * not yet gone through port forwarding. + * @param token Cancellation token indicating that the result is no longer needed. + * + * @return Priority indicating if the opener can open the external uri. + */ + canOpenExternalUri(uri: Uri, token: CancellationToken): ExternalUriOpenerPriority | Thenable; + + /** + * Open a uri. + * + * This is invoked when: + * + * - The user clicks a link which does not have an assigned opener. In this case, first `canOpenExternalUri` + * is called and if the user selects this opener, then `openExternalUri` is called. + * - The user sets the default opener for a link in their settings and then visits a link. + * + * @param resolvedUri The uri to open. This uri may have been transformed by port forwarding, so it + * may not match the original uri passed to `canOpenExternalUri`. Use `ctx.originalUri` to check the + * original uri. + * @param ctx Additional information about the uri being opened. + * @param token Cancellation token indicating that opening has been canceled. + * + * @return Thenable indicating that the opening has completed. + */ + openExternalUri(resolvedUri: Uri, ctx: OpenExternalUriContext, token: CancellationToken): Thenable | void; + } + + /** + * Additional information about the uri being opened. + */ + interface OpenExternalUriContext { + /** + * The uri that triggered the open. + * + * This is the original uri that the user clicked on or that was passed to `openExternal.` + * Due to port forwarding, this may not match the `resolvedUri` passed to `openExternalUri`. + */ + readonly sourceUri: Uri; + } + + /** + * Additional metadata about a registered `ExternalUriOpener`. + */ + interface ExternalUriOpenerMetadata { + + /** + * List of uri schemes the opener is triggered for. + * + * Currently only `http` and `https` are supported. + */ + readonly schemes: readonly string[] + + /** + * Text displayed to the user that explains what the opener does. + * + * For example, 'Open in browser preview' + */ + readonly label: string; + } + + namespace window { + /** + * Register a new `ExternalUriOpener`. + * + * When a uri is about to be opened, an `onOpenExternalUri:SCHEME` activation event is fired. + * + * @param id Unique id of the opener, such as `myExtension.browserPreview`. This is used in settings + * and commands to identify the opener. + * @param opener Opener to register. + * @param metadata Additional information about the opener. + * + * @returns Disposable that unregisters the opener. + */ + export function registerExternalUriOpener(id: string, opener: ExternalUriOpener, metadata: ExternalUriOpenerMetadata): Disposable; + } + + interface OpenExternalOptions { + /** + * Allows using openers contributed by extensions through `registerExternalUriOpener` + * when opening the resource. + * + * If `true`, the editor will check if any contributed openers can handle the + * uri, and fallback to the default opener behavior. + * + * If it is string, this specifies the id of the `ExternalUriOpener` + * that should be used if it is available. Use `'default'` to force the editor's + * standard external opener to be used. + */ + readonly allowContributedOpeners?: boolean | string; + } + + namespace env { + export function openExternal(target: Uri, options?: OpenExternalOptions): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts b/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts new file mode 100644 index 00000000000..bf7bc5ecba3 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * 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/73524 + + /** + * The parameters of a query for file search. + */ + export interface FileSearchQuery { + /** + * The search pattern to match against file paths. + */ + pattern: string; + } + + /** + * Options that apply to file search. + */ + export interface FileSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults?: number; + + /** + * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, + * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. + */ + session?: CancellationToken; + } + + /** + * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. + * + * A FileSearchProvider is the more powerful of two ways to implement file search in the editor. Use a FileSearchProvider if you wish to search within a folder for + * all files that match the user's query. + * + * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, + * and in that case, every file in the folder should be returned. + */ + export interface FileSearchProvider { + /** + * Provide the set of files that match a certain file path pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching files. + * @param token A cancellation token. + */ + provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts b/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts new file mode 100644 index 00000000000..14cd44d2048 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * 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/59924 + + /** + * Options that can be set on a findTextInFiles search. + */ + export interface FindTextInFilesOptions { + /** + * A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern + * will be matched against the file paths of files relative to their workspace. Use a {@link RelativePattern relative pattern} + * to restrict the search results to a {@link WorkspaceFolder workspace folder}. + */ + include?: GlobPattern; + + /** + * 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 excludes will + * apply. + */ + exclude?: GlobPattern; + + /** + * Whether to use the default and user-configured excludes. Defaults to true. + */ + useDefaultExcludes?: boolean; + + /** + * The maximum number of results to search for + */ + maxResults?: number; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles?: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles?: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks?: boolean; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + export namespace workspace { + /** + * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + + /** + * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.inlayHints.d.ts b/src/vscode-dts/vscode.proposed.inlayHints.d.ts new file mode 100644 index 00000000000..9b3a30a7e3f --- /dev/null +++ b/src/vscode-dts/vscode.proposed.inlayHints.d.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * 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/16221 + + // todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range) + // todo@API add "mini-markdown" for links and styles + // (done) remove description + // (done) rename to InlayHint + // (done) add InlayHintKind with type, argument, etc + + export namespace languages { + /** + * 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; + } + + export enum InlayHintKind { + Other = 0, + Type = 1, + Parameter = 2, + } + + /** + * Inlay hint information. + */ + export class InlayHint { + /** + * The text of the hint. + */ + // todo@API label? + text: string; + /** + * The position of this hint. + */ + position: Position; + /** + * The kind of this hint. + */ + kind?: InlayHintKind; + /** + * Whitespace before the hint. + */ + whitespaceBefore?: boolean; + /** + * Whitespace after the hint. + */ + whitespaceAfter?: boolean; + + // todo@API make range first argument + constructor(text: string, position: Position, 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 have changed. + * @see {@link EventEmitter} + */ + //todo@API needs proper doc (like others) + onDidChangeInlayHints?: Event; + + /** + * + * @param model 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 A list of inlay hints or a thenable that resolves to such. + */ + provideInlayHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult; + } +} diff --git a/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts new file mode 100644 index 00000000000..a232a6d07e2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * 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/124024 @hediet @alexdima + + export namespace languages { + /** + * Registers an inline completion provider. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; + } + + 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 asks for the next or previous inline completion. + * Use `context.triggerKind` to distinguish between these scenarios. + */ + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult | T[]>; + } + + export interface InlineCompletionContext { + /** + * How the 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. + * + * The user must configure `"editor.suggest.preview": true` for this feature. + */ + readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; + } + + export interface SelectedCompletionInfo { + range: Range; + text: string; + } + + /** + * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1, + } + + export class InlineCompletionList { + items: T[]; + + constructor(items: T[]); + } + + export class InlineCompletionItem { + /** + * The text to replace the range with. + * + * The text the range refers to should be a prefix of this value and must be a subword (`AB` and `BEF` are subwords of `ABCDEF`, but `Ab` is not). + */ + text: string; + + /** + * The range to replace. + * Must begin and end on the same line. + * + * Prefer replacements over insertions to avoid cache invalidation: + * Instead of reporting a completion that inserts an extension at the end of a word, + * the whole word should be replaced with the extended word. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; + + constructor(text: string, range?: Range, command?: Command); + } + + + /** + * Be aware that this API will not ever be finalized. + */ + export namespace window { + export function getInlineCompletionItemController(provider: InlineCompletionItemProvider): InlineCompletionController; + } + + /** + * Be aware that this API will not ever be finalized. + */ + export interface InlineCompletionController { + /** + * Is fired when an inline completion item is shown to the user. + */ + // eslint-disable-next-line vscode-dts-event-naming + readonly onDidShowCompletionItem: Event>; + } + + /** + * Be aware that this API will not ever be finalized. + */ + export interface InlineCompletionItemDidShowEvent { + completionItem: T; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts b/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts new file mode 100644 index 00000000000..67e5c4b0fbf --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * 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/124970 + + /** + * The execution state of a notebook cell. + */ + export enum NotebookCellExecutionState { + /** + * The cell is idle. + */ + Idle = 1, + /** + * Execution for the cell is pending. + */ + Pending = 2, + /** + * The cell is currently executing. + */ + Executing = 3, + } + + /** + * An event describing a cell execution state change. + */ + export interface NotebookCellExecutionStateChangeEvent { + /** + * The {@link NotebookCell cell} for which the execution state has changed. + */ + readonly cell: NotebookCell; + + /** + * The new execution state of the cell. + */ + readonly state: NotebookCellExecutionState; + } + + export namespace notebooks { + + /** + * An {@link Event} which fires when the execution state of a cell has changed. + */ + // todo@API this is an event that is fired for a property that cells don't have and that makes me wonder + // how a correct consumer works, e.g the consumer could have been late and missed an event? + export const onDidChangeNotebookCellExecutionState: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts b/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts new file mode 100644 index 00000000000..fd4e0e0b001 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + export namespace notebooks { + /** + * Create a document that is the concatenation of all notebook cells. By default all code-cells are included + * but a selector can be provided to narrow to down the set of cells. + * + * @param notebook + * @param selector + */ + // todo@API really needed? we didn't find a user here + export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; + } + + export interface NotebookConcatTextDocument { + readonly uri: Uri; + readonly isClosed: boolean; + dispose(): void; + readonly onDidChange: Event; + readonly version: number; + getText(): string; + getText(range: Range): string; + + offsetAt(position: Position): number; + positionAt(offset: number): Position; + validateRange(range: Range): Range; + validatePosition(position: Position): Position; + + locationAt(positionOrRange: Position | Range): Location; + positionAt(location: Location): Position; + contains(uri: Uri): boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts new file mode 100644 index 00000000000..d30fdb167f4 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + interface NotebookDocumentBackup { + /** + * Unique identifier for the backup. + * + * This id is passed back to your extension in `openNotebook` when opening a notebook 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; + } + + interface NotebookDocumentBackupContext { + readonly destination: Uri; + } + + interface NotebookDocumentOpenContext { + readonly backupId?: string; + readonly untitledDocumentData?: Uint8Array; + } + + // todo@API use openNotebookDOCUMENT to align with openCustomDocument etc? + // todo@API rename to NotebookDocumentContentProvider + export interface NotebookContentProvider { + + readonly options?: NotebookDocumentContentOptions; + readonly onDidChangeNotebookContentOptions?: Event; + + /** + * Content providers should always use {@link FileSystemProvider file system providers} to + * resolve the raw content for `uri` as the resouce is not necessarily a file on disk. + */ + openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext, token: CancellationToken): NotebookData | Thenable; + + // todo@API use NotebookData instead + saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable; + + // todo@API use NotebookData instead + saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable; + + // todo@API use NotebookData instead + backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable; + } + + export namespace workspace { + + // TODO@api use NotebookDocumentFilter instead of just notebookType:string? + // TODO@API options duplicates the more powerful variant on NotebookContentProvider + export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts b/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts new file mode 100644 index 00000000000..6bd35115a81 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + // eslint-disable-next-line vscode-dts-region-comments + // @roblourens: new debug session option for simple UI 'managedByParent' (see https://github.com/microsoft/vscode/issues/128588) + + /** + * Options for {@link debug.startDebugging starting a debug session}. + */ + export interface DebugSessionOptions { + + debugUI?: { + /** + * When true, the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed. + */ + simple?: 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; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts b/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts new file mode 100644 index 00000000000..c9ac1073772 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + export interface NotebookCellOutput { + id: string; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookEditor.d.ts b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts new file mode 100644 index 00000000000..0181b8f7ef2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + /** + * 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}. + */ + export interface NotebookEditor { + /** + * The document associated with this notebook editor. + */ + //todo@api rename to notebook? + readonly document: NotebookDocument; + + /** + * The selections on 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: NotebookRange[]; + + /** + * The current visible ranges in the editor (vertically). + */ + readonly visibleRanges: NotebookRange[]; + + /** + * 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; + + /** + * The column in which this editor shows. + */ + readonly viewColumn?: ViewColumn; + } + + export interface NotebookDocumentMetadataChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the document metadata have changed. + */ + //todo@API rename to notebook? + readonly document: NotebookDocument; + } + + export interface NotebookCellsChangeData { + readonly start: number; + // todo@API end? Use NotebookCellRange instead? + readonly deletedCount: number; + // todo@API removedCells, deletedCells? + readonly deletedItems: NotebookCell[]; + // todo@API addedCells, insertedCells, newCells? + readonly items: NotebookCell[]; + } + + export interface NotebookCellsChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the cells have changed. + */ + //todo@API rename to notebook? + readonly document: NotebookDocument; + readonly changes: ReadonlyArray; + } + + export interface NotebookCellOutputsChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the cell outputs have changed. + */ + //todo@API remove? use cell.notebook instead? + readonly document: NotebookDocument; + // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell + readonly cells: NotebookCell[]; + } + + export interface NotebookCellMetadataChangeEvent { + /** + * The {@link NotebookDocument notebook document} for which the cell metadata have changed. + */ + //todo@API remove? use cell.notebook instead? + readonly document: NotebookDocument; + // NotebookCellOutputsChangeEvent.cells vs NotebookCellMetadataChangeEvent.cell + readonly cell: NotebookCell; + } + + export interface NotebookEditorSelectionChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the selections have changed. + */ + readonly notebookEditor: NotebookEditor; + readonly selections: ReadonlyArray + } + + export interface NotebookEditorVisibleRangesChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. + */ + readonly notebookEditor: NotebookEditor; + readonly visibleRanges: ReadonlyArray; + } + + + export interface NotebookDocumentShowOptions { + viewColumn?: ViewColumn; + preserveFocus?: boolean; + preview?: boolean; + selections?: NotebookRange[]; + } + + export namespace notebooks { + + + + export const onDidSaveNotebookDocument: Event; + + export const onDidChangeNotebookDocumentMetadata: Event; + export const onDidChangeNotebookCells: Event; + + // todo@API add onDidChangeNotebookCellOutputs + export const onDidChangeCellOutputs: Event; + + // todo@API add onDidChangeNotebookCellMetadata + export const onDidChangeCellMetadata: Event; + } + + export namespace window { + export const visibleNotebookEditors: NotebookEditor[]; + export const onDidChangeVisibleNotebookEditors: Event; + export const activeNotebookEditor: NotebookEditor | undefined; + export const onDidChangeActiveNotebookEditor: Event; + export const onDidChangeNotebookEditorSelection: Event; + export const onDidChangeNotebookEditorVisibleRanges: Event; + + export function showNotebookDocument(uri: Uri, options?: NotebookDocumentShowOptions): Thenable; + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts b/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts new file mode 100644 index 00000000000..5a07e05ef78 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + export interface NotebookEditor { + setDecorations(decorationType: NotebookEditorDecorationType, range: NotebookRange): void; + } + + export interface NotebookDecorationRenderOptions { + backgroundColor?: string | ThemeColor; + borderColor?: string | ThemeColor; + top?: ThemableDecorationAttachmentRenderOptions; + } + + export interface NotebookEditorDecorationType { + readonly key: string; + dispose(): void; + } + + export namespace notebooks { + export function createNotebookEditorDecorationType(options: NotebookDecorationRenderOptions): NotebookEditorDecorationType; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts b/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts new file mode 100644 index 00000000000..6c954b5f76c --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + // todo@API add NotebookEdit-type which handles all these cases? + // export class NotebookEdit { + // range: NotebookRange; + // newCells: NotebookCellData[]; + // newMetadata?: NotebookDocumentMetadata; + // constructor(range: NotebookRange, newCells: NotebookCellData) + // } + + // export class NotebookCellEdit { + // newMetadata?: NotebookCellMetadata; + // } + + // export interface WorkspaceEdit { + // set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void + // } + + export interface WorkspaceEdit { + // todo@API add NotebookEdit-type which handles all these cases? + replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; + replaceNotebookCells(uri: Uri, range: NotebookRange, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; + replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: { [key: string]: any }, metadata?: WorkspaceEditEntryMetadata): void; + } + + export interface NotebookEditorEdit { + replaceMetadata(value: { [key: string]: any }): void; + replaceCells(start: number, end: number, cells: NotebookCellData[]): void; + replaceCellMetadata(index: number, metadata: { [key: string]: any }): void; + } + + export interface NotebookEditor { + /** + * Perform an edit on the notebook associated with this notebook editor. + * + * The given callback-function is invoked with an {@link NotebookEditorEdit 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 NotebookEditorEdit edit-builder}. + * @return A promise that resolves with a value indicating if the edits could be applied. + */ + // @jrieken REMOVE maybe + edit(callback: (editBuilder: NotebookEditorEdit) => void): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts b/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts new file mode 100644 index 00000000000..de2966db16c --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * 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/106744 + + export interface NotebookRegistrationData { + displayName: string; + filenamePattern: (GlobPattern | { include: GlobPattern; exclude: GlobPattern; })[]; + exclusive?: boolean; + } + + export namespace workspace { + // SPECIAL overload with NotebookRegistrationData + export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions, registrationData?: NotebookRegistrationData): Disposable; + // SPECIAL overload with NotebookRegistrationData + export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions, registration?: NotebookRegistrationData): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts b/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts new file mode 100644 index 00000000000..b76ac58a2c0 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * 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/123601 + + /** + * Represents a script that is loaded into the notebook renderer before rendering output. This allows + * to provide and share functionality for notebook markup and notebook output renderers. + */ + export class NotebookRendererScript { + + /** + * APIs that the preload provides to the renderer. These are matched + * against the `dependencies` and `optionalDependencies` arrays in the + * notebook renderer contribution point. + */ + provides: string[]; + + /** + * URI of the JavaScript module to preload. + * + * This module must export an `activate` function that takes a context object that contains the notebook API. + */ + uri: Uri; + + /** + * @param uri URI of the JavaScript module to preload + * @param provides Value for the `provides` property + */ + constructor(uri: Uri, provides?: string | string[]); + } + + export interface NotebookController { + /** + * The human-readable label used to categorise controllers. + */ + kind?: string; + + // todo@API allow add, not remove + readonly rendererScripts: NotebookRendererScript[]; + + /** + * An event that fires when a {@link NotebookController.rendererScripts renderer script} has send a message to + * the controller. + */ + readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>; + + /** + * Send a message to the renderer of notebook editors. + * + * Note that only editors showing documents that are bound to this controller + * are receiving the message. + * + * @param message The message to send. + * @param editor A specific editor to send the message to. When `undefined` all applicable editors are receiving the message. + * @returns A promise that resolves to a boolean indicating if the message has been send or not. + */ + postMessage(message: any, editor?: NotebookEditor): Thenable; + + //todo@API validate this works + asWebviewUri(localResource: Uri): Uri; + } + + export namespace notebooks { + + export function createNotebookController(id: string, viewType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable, rendererScripts?: NotebookRendererScript[]): NotebookController; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookMime.d.ts b/src/vscode-dts/vscode.proposed.notebookMime.d.ts new file mode 100644 index 00000000000..fb1cf089dcc --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookMime.d.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * 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/126280 @mjbvz + + export interface NotebookCellData { + /** + * Mime type determines how the cell's `value` is interpreted. + * + * The mime selects which notebook renders is used to render the cell. + * + * If not set, internally the cell is treated as having a mime type of `text/plain`. + * Cells that set `language` to `markdown` instead are treated as `text/markdown`. + */ + mime?: string; + } + + export interface NotebookCell { + /** + * Mime type determines how the markup cell's `value` is interpreted. + * + * The mime selects which notebook renders is used to render the cell. + * + * If not set, internally the cell is treated as having a mime type of `text/plain`. + * Cells that set `language` to `markdown` instead are treated as `text/markdown`. + */ + mime: string | undefined; + } +} diff --git a/src/vscode-dts/vscode.proposed.portsAttributes.d.ts b/src/vscode-dts/vscode.proposed.portsAttributes.d.ts new file mode 100644 index 00000000000..1842605a5e7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.portsAttributes.d.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * 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/115616 @alexr00 + + export enum PortAutoForwardAction { + Notify = 1, + OpenBrowser = 2, + OpenPreview = 3, + Silent = 4, + Ignore = 5, + OpenBrowserOnce = 6 + } + + export class PortAttributes { + /** + * The port number associated with this this set of attributes. + */ + port: number; + + /** + * The action to be taken when this port is detected for auto forwarding. + */ + autoForwardAction: PortAutoForwardAction; + + /** + * Creates a new PortAttributes object + * @param port the port number + * @param autoForwardAction the action to take when this port is detected + */ + constructor(port: number, autoForwardAction: PortAutoForwardAction); + } + + export interface PortAttributesProvider { + /** + * Provides attributes for the given port. For ports that your extension doesn't know about, simply + * return undefined. For example, if `providePortAttributes` is called with ports 3000 but your + * extension doesn't know anything about 3000 you should return undefined. + */ + providePortAttributes(port: number, pid: number | undefined, commandLine: string | undefined, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * If your extension listens on ports, consider registering a PortAttributesProvider to provide information + * about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing + * this information with a PortAttributesProvider the extension can tell the editor that these ports should be + * ignored, since they don't need to be user facing. + * + * @param portSelector If registerPortAttributesProvider is called after you start your process then you may already + * know the range of ports or the pid of your process. All properties of a the portSelector must be true for your + * provider to get called. + * The `portRange` is start inclusive and end exclusive. + * @param provider The PortAttributesProvider + */ + export function registerPortAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: PortAttributesProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts b/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts new file mode 100644 index 00000000000..405d67671d7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * 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/73904 + + export interface QuickPick extends QuickInput { + /** + * An optional flag to sort the final results by index of first query match in label. Defaults to true. + */ + sortByLabel: boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts new file mode 100644 index 00000000000..a73316c11a8 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + //resolvers: @alexdima + + export interface MessageOptions { + /** + * Do not render a native message box. + */ + useCustom?: boolean; + } + + export interface RemoteAuthorityResolverContext { + resolveAttempt: number; + } + + export class ResolvedAuthority { + readonly host: string; + readonly port: number; + readonly connectionToken: string | undefined; + + constructor(host: string, port: number, connectionToken?: string); + } + + export interface ResolvedOptions { + extensionHostEnv?: { [key: string]: string | null; }; + + isTrusted?: boolean; + } + + export interface TunnelPrivacy { + themeIcon: string; + id: string; + label: string; + } + + export interface TunnelOptions { + remoteAddress: { port: number, host: string; }; + // The desired local port. If this port can't be used, then another will be chosen. + localAddressPort?: number; + label?: string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; + privacy?: string; + protocol?: string; + } + + export interface TunnelDescription { + remoteAddress: { port: number, host: string; }; + //The complete local address(ex. localhost:1234) + localAddress: { port: number, host: string; } | string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; + privacy?: string; + // If protocol is not provided it is assumed to be http, regardless of the localAddress. + protocol?: string; + } + + export interface Tunnel extends TunnelDescription { + // Implementers of Tunnel should fire onDidDispose when dispose is called. + onDidDispose: Event; + dispose(): void | Thenable; + } + + /** + * Used as part of the ResolverResult if the extension has any candidate, + * published, or forwarded ports. + */ + export interface TunnelInformation { + /** + * Tunnels that are detected by the extension. The remotePort is used for display purposes. + * The localAddress should be the complete local address (ex. localhost:1234) for connecting to the port. Tunnels provided through + * detected are read-only from the forwarded ports UI. + */ + environmentTunnels?: TunnelDescription[]; + + } + + export interface TunnelCreationOptions { + /** + * True when the local operating system will require elevation to use the requested local port. + */ + elevationRequired?: boolean; + } + + export enum CandidatePortSource { + None = 0, + Process = 1, + Output = 2 + } + + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; + + export class RemoteAuthorityResolverError extends Error { + static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; + static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError; + + constructor(message?: string); + } + + export interface RemoteAuthorityResolver { + /** + * Resolve the authority part of the current opened `vscode-remote://` URI. + * + * This method will be invoked once during the startup of the editor and again each time + * the editor detects a disconnection. + * + * @param authority The authority part of the current opened `vscode-remote://` URI. + * @param context A context indicating if this is the first call or a subsequent call. + */ + resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable; + + /** + * Get the canonical URI (if applicable) for a `vscode-remote://` URI. + * + * @returns The canonical URI or undefined if the uri is already canonical. + */ + getCanonicalURI?(uri: Uri): ProviderResult; + + /** + * Can be optionally implemented if the extension can forward ports better than the core. + * When not implemented, the core will use its default forwarding logic. + * When implemented, the core will use this to forward ports. + * + * To enable the "Change Local Port" action on forwarded ports, make sure to set the `localAddress` of + * the returned `Tunnel` to a `{ port: number, host: string; }` and not a string. + */ + tunnelFactory?: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined; + + /**p + * Provides filtering for candidate ports. + */ + showCandidatePort?: (host: string, port: number, detail: string) => Thenable; + + /** + * Lets the resolver declare which tunnel factory features it supports. + * UNDER DISCUSSION! MAY CHANGE SOON. + */ + tunnelFeatures?: { + elevation: boolean; + /** + * @deprecated Use privacy instead + */ + public: boolean; + /** + * One of the the options must have the ID "private". + */ + privacyOptions: TunnelPrivacy[]; + }; + + candidatePortSource?: CandidatePortSource; + } + + /** + * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. + */ + export interface AuthenticationGetSessionOptions { + /** + * 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. + * + * Defaults to false. + */ + forceNewSession?: boolean | { detail: string }; + } + + 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 & { forceNewSession: true | { detail: string } }): Thenable; + export function hasSession(providerId: string, scopes: readonly string[]): Thenable; + } + + export namespace workspace { + /** + * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. + * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. + * + * @throws When run in an environment without a remote. + * + * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. + */ + export function openTunnel(tunnelOptions: TunnelOptions): Thenable; + + /** + * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user. + * Note that these are of type TunnelDescription and cannot be disposed. + */ + export let tunnels: Thenable; + + /** + * Fired when the list of tunnels has changed. + */ + export const onDidChangeTunnels: Event; + } + + export interface ResourceLabelFormatter { + scheme: string; + authority?: string; + formatting: ResourceLabelFormatting; + } + + export interface ResourceLabelFormatting { + label: string; // myLabel:/${path} + // For historic reasons we use an or string here. Once we finalize this API we should start using enums instead and adopt it in extensions. + // eslint-disable-next-line vscode-dts-literal-or-types + separator: '/' | '\\' | ''; + tildify?: boolean; + normalizeDriveLetter?: boolean; + workspaceSuffix?: string; + workspaceTooltip?: string; + authorityPrefix?: string; + stripPathStartingSeparator?: boolean; + } + + export namespace workspace { + export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; + export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable; + } + + export namespace env { + + /** + * The authority part of the current opened `vscode-remote://` URI. + * Defined by extensions, e.g. `ssh-remote+${host}` 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 remoteAuthority: string | undefined; + + } +} diff --git a/src/vscode-dts/vscode.proposed.scmActionButton.d.ts b/src/vscode-dts/vscode.proposed.scmActionButton.d.ts new file mode 100644 index 00000000000..f60dc3047c7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.scmActionButton.d.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * 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/133935 + + export interface SourceControl { + actionButton?: Command; + } +} diff --git a/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts b/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts new file mode 100644 index 00000000000..bced264e5a5 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + // todo@joaomoreno add issue reference + + export interface SourceControl { + + /** + * Whether the source control is selected. + */ + readonly selected: boolean; + + /** + * An event signaling when the selection state changes. + */ + readonly onDidChangeSelection: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.scmValidation.d.ts b/src/vscode-dts/vscode.proposed.scmValidation.d.ts new file mode 100644 index 00000000000..a5fad56ebc2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.scmValidation.d.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + // todo@joaomoreno add issue reference + + /** + * Represents the validation type of the Source Control input. + */ + export enum SourceControlInputBoxValidationType { + + /** + * Something not allowed by the rules of a language or other means. + */ + Error = 0, + + /** + * Something suspicious but allowed. + */ + Warning = 1, + + /** + * Something to inform about but not a problem. + */ + Information = 2 + } + + export interface SourceControlInputBoxValidation { + + /** + * The validation message to display. + */ + readonly message: string | MarkdownString; + + /** + * The validation type. + */ + readonly type: SourceControlInputBoxValidationType; + } + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Shows a transient contextual message on the input. + */ + showValidationMessage(message: string | MarkdownString, type: SourceControlInputBoxValidationType): void; + + /** + * A validation function for the input box. It's possible to change + * the validation provider simply by setting this property to a different function. + */ + validateInput?(value: string, cursorPosition: number): ProviderResult; + } +} diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts new file mode 100644 index 00000000000..691cfd8f07c --- /dev/null +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * 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/15178 + + /** + * Represents a tab within the window + */ + export interface Tab { + /** + * The text displayed on the tab + */ + readonly label: string; + + /** + * The index of the tab within the column + */ + readonly index: number; + + /** + * The column which the tab belongs to + */ + readonly viewColumn: ViewColumn; + + /** + * The resource represented by the tab if available. + * Note: Not all tabs have a resource associated with them. + */ + readonly resource: Uri | undefined; + + /** + * The identifier of the view contained in the tab + * This is equivalent to `viewType` for custom editors and `notebookType` for notebooks. + * The built-in text editor has an id of 'default' for all configurations. + */ + readonly viewId: string | undefined; + + /** + * All the resources and viewIds represented by a tab + * {@link Tab.resource resource} and {@link Tab.viewId viewId} will + * always be at index 0. + */ + readonly additionalResourcesAndViewIds: readonly { + readonly resource: Uri | undefined, + readonly viewId: string | undefined + }[]; + + /** + * Whether or not the tab is currently active + * Dictated by being the selected tab in the active group + */ + readonly isActive: boolean; + + /** + * Moves a tab to the given index within the column. + * If the index is out of range, the tab will be moved to the end of the column. + * If the column is out of range, a new one will be created after the last existing column. + * @param index The index to move the tab to + * @param viewColumn The column to move the tab into + */ + move(index: number, viewColumn: ViewColumn): Thenable; + + /** + * Closes the tab. This makes the tab object invalid and the tab + * should no longer be used for further actions. + */ + close(): Thenable; + } + + export namespace window { + /** + * A list of all opened tabs + * Ordered from left to right + */ + export const tabs: readonly Tab[]; + + /** + * The currently active tab + * Undefined if no tabs are currently opened + */ + export const activeTab: Tab | undefined; + + /** + * An {@link Event} which fires when the array of {@link window.tabs tabs} + * has changed. + */ + export const onDidChangeTabs: Event; + + /** + * An {@link Event} which fires when the {@link window.activeTab activeTab} + * has changed. + */ + export const onDidChangeActiveTab: Event; + + } +} diff --git a/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts b/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts new file mode 100644 index 00000000000..52e631a0495 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * 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/47265 + + export interface TaskPresentationOptions { + /** + * Controls whether the task is executed in a specific terminal group using split panes. + */ + group?: string; + + /** + * Controls whether the terminal is closed after executing the task. + */ + close?: boolean; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts new file mode 100644 index 00000000000..c9c4c0e99af --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * 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/78502 + + export interface TerminalDataWriteEvent { + /** + * The {@link Terminal} for which the data was written. + */ + readonly terminal: Terminal; + /** + * The data being written. + */ + readonly data: string; + } + + namespace window { + /** + * An event which fires when the terminal's child pseudo-device is written to (the shell). + * In other words, this provides access to the raw data stream from the process running + * within the terminal, including VT sequences. + */ + export const onDidWriteTerminalData: Event; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts b/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts new file mode 100644 index 00000000000..a0d28379d60 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * 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/55718 + + /** + * An {@link Event} which fires when a {@link Terminal}'s dimensions change. + */ + export interface TerminalDimensionsChangeEvent { + /** + * The {@link Terminal} for which the dimensions have changed. + */ + readonly terminal: Terminal; + /** + * The new value for the {@link Terminal.dimensions terminal's dimensions}. + */ + readonly dimensions: TerminalDimensions; + } + + export namespace window { + /** + * An event which fires when the {@link Terminal.dimensions dimensions} of the terminal change. + */ + export const onDidChangeTerminalDimensions: Event; + } + + export interface Terminal { + /** + * The current dimensions of the terminal. This will be `undefined` immediately after the + * terminal is created as the dimensions are not known until shortly after the terminal is + * created. + */ + readonly dimensions: TerminalDimensions | undefined; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalLocation.d.ts b/src/vscode-dts/vscode.proposed.terminalLocation.d.ts new file mode 100644 index 00000000000..9c2a10a3feb --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalLocation.d.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * 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/45407 + + export interface TerminalOptions { + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + } + + export interface ExtensionTerminalOptions { + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + } + + export enum TerminalLocation { + Panel = 1, + Editor = 2, + } + + export interface TerminalEditorLocationOptions { + /** + * A view column in which the {@link Terminal terminal} should be shown in the editor area. + * Use {@link ViewColumn.Active active} to open in the active editor group, other values are + * adjusted to be `Min(column, columnCount + 1)`, the + * {@link ViewColumn.Active active}-column is not adjusted. 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; + } + + 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; + } +} diff --git a/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts new file mode 100644 index 00000000000..e72aeca9b82 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + // todo@API,@jrieken is this needed? This is also in vscode.d.ts... + // https://github.com/microsoft/vscode/issues/114898 + + export interface Pseudoterminal { + /** + * An event that when fired allows changing the name of the terminal. + * + * **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; + } +} diff --git a/src/vscode-dts/vscode.proposed.testCoverage.d.ts b/src/vscode-dts/vscode.proposed.testCoverage.d.ts new file mode 100644 index 00000000000..3f0ec8f1d67 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.testCoverage.d.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * 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/123713 + + export interface TestRun { + /** + * Test coverage provider for this result. An extension can defer setting + * this until after a run is complete and coverage is available. + */ + coverageProvider?: TestCoverageProvider + // ... + } + + /** + * Provides information about test coverage for a test result. + * Methods on the provider will not be called until the test run is complete + */ + export interface TestCoverageProvider { + /** + * Returns coverage information for all files involved in the test run. + * @param token A cancellation token. + * @return Coverage metadata for all files involved in the test. + */ + provideFileCoverage(token: CancellationToken): ProviderResult; + + /** + * Give a FileCoverage to fill in more data, namely {@link FileCoverage.detailedCoverage}. + * The editor will only resolve a FileCoverage once, and onyl if detailedCoverage + * is undefined. + * + * @param coverage A coverage object obtained from {@link provideFileCoverage} + * @param token A cancellation token. + * @return The resolved file coverage, or a thenable that resolves to one. It + * is OK to return the given `coverage`. When no result is returned, the + * given `coverage` will be used. + */ + resolveFileCoverage?(coverage: T, token: CancellationToken): ProviderResult; + } + + /** + * A class that contains information about a covered resource. A count can + * be give for lines, branches, and functions in a file. + */ + export class CoveredCount { + /** + * Number of items covered in the file. + */ + covered: number; + /** + * Total number of covered items in the file. + */ + total: number; + + /** + * @param covered Value for {@link CovereredCount.covered} + * @param total Value for {@link CovereredCount.total} + */ + constructor(covered: number, total: number); + } + + /** + * Contains coverage metadata for a file. + */ + export class FileCoverage { + /** + * File URI. + */ + readonly uri: Uri; + + /** + * Statement coverage information. If the reporter does not provide statement + * coverage information, this can instead be used to represent line coverage. + */ + statementCoverage: CoveredCount; + + /** + * Branch coverage information. + */ + branchCoverage?: CoveredCount; + + /** + * Function coverage information. + */ + functionCoverage?: CoveredCount; + + /** + * Detailed, per-statement coverage. If this is undefined, the editor will + * call {@link TestCoverageProvider.resolveFileCoverage} when necessary. + */ + detailedCoverage?: DetailedCoverage[]; + + /** + * Creates a {@link FileCoverage} instance with counts filled in from + * the coverage details. + * @param uri Covered file URI + * @param detailed Detailed coverage information + */ + static fromDetails(uri: Uri, details: readonly DetailedCoverage[]): FileCoverage; + + /** + * @param uri Covered file URI + * @param statementCoverage Statement coverage information. If the reporter + * does not provide statement coverage information, this can instead be + * used to represent line coverage. + * @param branchCoverage Branch coverage information + * @param functionCoverage Function coverage information + */ + constructor( + uri: Uri, + statementCoverage: CoveredCount, + branchCoverage?: CoveredCount, + functionCoverage?: CoveredCount, + ); + } + + /** + * Contains coverage information for a single statement or line. + */ + export class StatementCoverage { + /** + * The number of times this statement was executed. If zero, the + * statement will be marked as un-covered. + */ + executionCount: number; + + /** + * Statement location. + */ + location: Position | Range; + + /** + * Coverage from branches of this line or statement. If it's not a + * conditional, this will be empty. + */ + branches: BranchCoverage[]; + + /** + * @param location The statement position. + * @param executionCount The number of times this statement was + * executed. If zero, the statement will be marked as un-covered. + * @param branches Coverage from branches of this line. If it's not a + * conditional, this should be omitted. + */ + constructor(executionCount: number, location: Position | Range, branches?: BranchCoverage[]); + } + + /** + * Contains coverage information for a branch of a {@link StatementCoverage}. + */ + export class BranchCoverage { + /** + * The number of times this branch was executed. If zero, the + * branch will be marked as un-covered. + */ + executionCount: number; + + /** + * Branch location. + */ + location?: Position | Range; + + /** + * @param executionCount The number of times this branch was executed. + * @param location The branch position. + */ + constructor(executionCount: number, location?: Position | Range); + } + + /** + * Contains coverage information for a function or method. + */ + export class FunctionCoverage { + /** + * The number of times this function was executed. If zero, the + * function will be marked as un-covered. + */ + executionCount: number; + + /** + * Function location. + */ + location: Position | Range; + + /** + * @param executionCount The number of times this function was executed. + * @param location The function position. + */ + constructor(executionCount: number, location: Position | Range); + } + + export type DetailedCoverage = StatementCoverage | FunctionCoverage; + +} diff --git a/src/vscode-dts/vscode.proposed.testObserver.d.ts b/src/vscode-dts/vscode.proposed.testObserver.d.ts new file mode 100644 index 00000000000..2bdb21d7473 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.testObserver.d.ts @@ -0,0 +1,202 @@ +/*--------------------------------------------------------------------------------------------- + * 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/107467 + + export namespace tests { + /** + * Requests that tests be run by their controller. + * @param run Run options to use. + * @param token Cancellation token for the test run + */ + export function runTests(run: TestRunRequest, token?: CancellationToken): Thenable; + + /** + * Returns an observer that watches and can request tests. + */ + export function createTestObserver(): TestObserver; + /** + * List of test results stored by the editor, sorted in descending + * order by their `completedAt` time. + */ + export const testResults: ReadonlyArray; + + /** + * Event that fires when the {@link testResults} array is updated. + */ + export const onDidChangeTestResults: Event; + } + + export interface TestObserver { + /** + * List of tests returned by test provider for files in the workspace. + */ + readonly tests: ReadonlyArray; + + /** + * An event that fires when an existing test in the collection changes, or + * null if a top-level test was added or removed. When fired, the consumer + * should check the test item and all its children for changes. + */ + readonly onDidChangeTest: Event; + + /** + * Dispose of the observer, allowing the editor to eventually tell test + * providers that they no longer need to update tests. + */ + dispose(): void; + } + + export interface TestsChangeEvent { + /** + * List of all tests that are newly added. + */ + readonly added: ReadonlyArray; + + /** + * List of existing tests that have updated. + */ + readonly updated: ReadonlyArray; + + /** + * List of existing tests that have been removed. + */ + readonly removed: ReadonlyArray; + } + + /** + * A test item is an item shown in the "test explorer" view. It encompasses + * both a suite and a test, since they have almost or identical capabilities. + */ + export interface TestItem { + /** + * Marks the test as outdated. This can happen as a result of file changes, + * for example. In "auto run" mode, tests that are outdated will be + * automatically rerun after a short delay. Invoking this on a + * test with children will mark the entire subtree as outdated. + * + * Extensions should generally not override this method. + */ + // todo@api still unsure about this + invalidateResults(): void; + } + + + /** + * TestResults can be provided to the editor in {@link tests.publishTestResult}, + * or read from it in {@link tests.testResults}. + * + * The results contain a 'snapshot' of the tests at the point when the test + * run is complete. Therefore, information such as its {@link Range} may be + * out of date. If the test still exists in the workspace, consumers can use + * its `id` to correlate the result instance with the living test. + */ + export interface TestRunResult { + /** + * Unix milliseconds timestamp at which the test run was completed. + */ + readonly completedAt: number; + + /** + * Optional raw output from the test run. + */ + readonly output?: string; + + /** + * List of test results. The items in this array are the items that + * were passed in the {@link tests.runTests} method. + */ + readonly results: ReadonlyArray>; + } + + /** + * A {@link TestItem}-like interface with an associated result, which appear + * or can be provided in {@link TestResult} interfaces. + */ + export interface TestResultSnapshot { + /** + * Unique identifier that matches that of the associated TestItem. + * This is used to correlate test results and tests in the document with + * those in the workspace (test explorer). + */ + readonly id: string; + + /** + * Parent of this item. + */ + readonly parent?: TestResultSnapshot; + + /** + * URI this TestItem is associated with. May be a file or file. + */ + readonly uri?: Uri; + + /** + * Display name describing the test case. + */ + readonly label: string; + + /** + * Optional description that appears next to the label. + */ + readonly description?: string; + + /** + * Location of the test item in its `uri`. This is only meaningful if the + * `uri` points to a file. + */ + readonly range?: Range; + + /** + * State of the test in each task. In the common case, a test will only + * be executed in a single task and the length of this array will be 1. + */ + readonly taskStates: ReadonlyArray; + + /** + * Optional list of nested tests for this item. + */ + readonly children: Readonly[]; + } + + export interface TestSnapshotTaskState { + /** + * Current result of the test. + */ + readonly state: TestResultState; + + /** + * The number of milliseconds the test took to run. This is set once the + * `state` is `Passed`, `Failed`, or `Errored`. + */ + readonly duration?: number; + + /** + * Associated test run message. Can, for example, contain assertion + * failure information if the test fails. + */ + readonly messages: ReadonlyArray; + } + + /** + * Possible states of tests in a test run. + */ + export enum TestResultState { + // Test will be run, but is not currently running. + Queued = 1, + // Test is currently running + Running = 2, + // Test run has passed + Passed = 3, + // Test run has failed (on an assertion) + Failed = 4, + // Test run has been skipped + Skipped = 5, + // Test run failed for some other reason (compilation error, timeout, etc) + Errored = 6 + } +} diff --git a/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts b/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts new file mode 100644 index 00000000000..0b7e1f7b391 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * 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/102091 + + export interface TextDocument { + + /** + * The {@link NotebookDocument notebook} that contains this document as a notebook cell or `undefined` when + * the document is not contained by a notebook (this should be the more frequent case). + */ + notebook: NotebookDocument | undefined; + } +} diff --git a/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts b/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts new file mode 100644 index 00000000000..9eba555d007 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts @@ -0,0 +1,275 @@ +/*--------------------------------------------------------------------------------------------- + * 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/59921 + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * A file glob pattern to match file paths against. + * TODO@roblourens merge this with the GlobPattern docs/definition in vscode.d.ts. + * @see {@link GlobPattern} + */ + export type GlobString = string; + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to text search. + */ + export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + /** + * Represents the severiry of a TextSearchComplete message. + */ + export enum TextSearchCompleteMessageType { + Information = 1, + Warning = 2, + } + + /** + * A message regarding a completed search. + */ + export interface TextSearchCompleteMessage { + /** + * Markdown text of the message. + */ + text: string, + /** + * Whether the source of the message is trusted, command links are disabled for untrusted message sources. + * Messaged are untrusted by default. + */ + trusted?: boolean, + /** + * The message type, this affects how the message will be rendered. + */ + type: TextSearchCompleteMessageType, + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + + /** + * Additional information regarding the state of the completed search. + * + * Messages with "Information" style support links in markdown syntax: + * - Click to [run a command](command:workbench.action.OpenQuickPick) + * - Click to [open a website](https://aka.ms) + * + * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. + */ + message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; + } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.timeline.d.ts b/src/vscode-dts/vscode.proposed.timeline.d.ts new file mode 100644 index 00000000000..625bc8c9bed --- /dev/null +++ b/src/vscode-dts/vscode.proposed.timeline.d.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * 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/84297 + + export class TimelineItem { + /** + * A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred. + */ + timestamp: number; + + /** + * A human-readable string describing the timeline item. + */ + label: string; + + /** + * Optional id for the timeline item. It must be unique across all the timeline items provided by this source. + * + * If not provided, an id is generated using the timeline item's timestamp. + */ + id?: string; + + /** + * The icon path or {@link ThemeIcon} for the timeline item. + */ + iconPath?: Uri | { light: Uri; dark: Uri; } | ThemeIcon; + + /** + * A human readable string describing less prominent details of the timeline item. + */ + description?: string; + + /** + * The tooltip text when you hover over the timeline item. + */ + detail?: string; + + /** + * The {@link Command} that should be executed when the timeline item is selected. + */ + command?: Command; + + /** + * Context value of the timeline item. This can be used to contribute specific actions to the item. + * For example, a timeline item is given a context value as `commit`. When contributing actions to `timeline/item/context` + * using `menus` extension point, you can specify context value for key `timelineItem` in `when` expression like `timelineItem == commit`. + * ``` + * "contributes": { + * "menus": { + * "timeline/item/context": [ + * { + * "command": "extension.copyCommitId", + * "when": "timelineItem == commit" + * } + * ] + * } + * } + * ``` + * This will show the `extension.copyCommitId` action only for items where `contextValue` is `commit`. + */ + contextValue?: string; + + /** + * Accessibility information used when screen reader interacts with this timeline item. + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * @param label A human-readable string describing the timeline item + * @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred + */ + constructor(label: string, timestamp: number); + } + + export interface TimelineChangeEvent { + /** + * The {@link Uri} of the resource for which the timeline changed. + */ + uri: Uri; + + /** + * A flag which indicates whether the entire timeline should be reset. + */ + reset?: boolean; + } + + export interface Timeline { + readonly paging?: { + /** + * A provider-defined cursor specifying the starting point of timeline items which are after the ones returned. + * Use `undefined` to signal that there are no more items to be returned. + */ + readonly cursor: string | undefined; + }; + + /** + * An array of {@link TimelineItem timeline items}. + */ + readonly items: readonly TimelineItem[]; + } + + export interface TimelineOptions { + /** + * A provider-defined cursor specifying the starting point of the timeline items that should be returned. + */ + cursor?: string; + + /** + * An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned. + * If `undefined` all timeline items should be returned. + */ + limit?: number | { timestamp: number; id?: string; }; + } + + export interface TimelineProvider { + /** + * An optional event to signal that the timeline for a source has changed. + * To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`. + */ + onDidChange?: Event; + + /** + * An identifier of the source of the timeline items. This can be used to filter sources. + */ + readonly id: string; + + /** + * A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources. + */ + readonly label: string; + + /** + * Provide {@link TimelineItem timeline items} for a {@link Uri}. + * + * @param uri The {@link Uri} of the file to provide the timeline for. + * @param options A set of options to determine how results should be returned. + * @param token A cancellation token. + * @return The {@link TimelineResult timeline result} or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a timeline provider. + * + * Multiple providers can be registered. In that case, providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param scheme A scheme or schemes that defines which documents this provider is applicable to. Can be `*` to target all documents. + * @param provider A timeline provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTimelineProvider(scheme: string | string[], provider: TimelineProvider): Disposable; + } +} diff --git a/src/vscode-dts/vscode.proposed.tokenInformation.d.ts b/src/vscode-dts/vscode.proposed.tokenInformation.d.ts new file mode 100644 index 00000000000..538788818d1 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.tokenInformation.d.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * 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/91555 + + export enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 4 + } + + export interface TokenInformation { + type: StandardTokenType; + range: Range; + } + + export namespace languages { + /** @deprecated */ + export function getTokenInformationAtPosition(document: TextDocument, position: Position): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts new file mode 100644 index 00000000000..70fd7dc8b5a --- /dev/null +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * 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/32592 + + /** + * 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`. + */ + onDidChangeTreeData2?: Event; + } + + export interface TreeViewOptions { + /** + * An optional interface to implement drag and drop in the tree view. + */ + dragAndDropController?: DragAndDropController; + } + + export interface TreeDataTransferItem { + asString(): Thenable; + } + + export interface TreeDataTransfer { + /** + * A map containing a mapping of the mime type of the corresponding data. + * The type for tree elements is text/treeitem. + * For example, you can reconstruct the your tree elements: + * ```ts + * JSON.parse(await (items.get('text/treeitem')!.asString())) + * ``` + */ + items: { get: (mimeType: string) => TreeDataTransferItem | undefined }; + } + + export interface DragAndDropController extends Disposable { + readonly supportedTypes: string[]; + + /** + * todo@API maybe + * + * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, + * `onWillDrop` will be called with the dropped tree item. This is the DragAndDropController's opportunity to + * package the data from the dropped tree item into whatever format they want the target tree item to receive. + * + * The returned `TreeDataTransfer` will be merged with the original`TreeDataTransfer` for the operation. + * + * Note for implementation later: This means that the `text/treeItem` mime type will go away. + * + * @param source + */ + // onWillDrop?(source: T): Thenable; + + /** + * Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed. + * + * @param source + * @param target + */ + onDrop(source: TreeDataTransfer, target: T): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts b/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts new file mode 100644 index 00000000000..e71c2f71879 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * 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/61313 @alexr00 + + export interface TreeView extends Disposable { + reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number; }): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts b/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts new file mode 100644 index 00000000000..d48071af2cc --- /dev/null +++ b/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * 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/120173 + + /** + * The object describing the properties of the workspace trust request + */ + export interface WorkspaceTrustRequestOptions { + /** + * Custom message describing the user action that requires workspace + * trust. If omitted, a generic message will be displayed in the workspace + * trust request dialog. + */ + readonly message?: string; + } + + export namespace workspace { + /** + * Prompt the user to chose whether to trust the current workspace + * @param options Optional object describing the properties of the + * workspace trust request. + */ + export function requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Thenable; + } +} From 317ae334899de42a77ae0f65c2c8feb9b46c89dc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 12 Nov 2021 09:52:33 +0100 Subject: [PATCH 017/330] Fix handling of integer parse errors --- src/vs/server/main.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/server/main.js b/src/vs/server/main.js index 7c07af3ef5e..7125928414f 100644 --- a/src/vs/server/main.js +++ b/src/vs/server/main.js @@ -146,24 +146,24 @@ async function start() { async function parsePort(strPort, strPickPort) { let specificPort = -1; - try { - if (strPort) { - specificPort = parseInt(strPort); + if (strPort) { + const port = parseInt(strPort, 10); + if (!isNaN(port)) { + specificPort = port; + } else { + console.log('Port is not a number, will default to 8000 if no pick-port is given.'); } - } catch (e) { - console.log('Port is not a number, will default to 8000 if no pick-port is given.'); } if (strPickPort) { - try { - let [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr); }); - + const [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr, 10); }); + if (!isNaN(start) && !isNaN(end)) { if (specificPort !== -1 && specificPort >= start && specificPort <= end) { return specificPort; } else { return await findFreePort(start, start, end); } - } catch { + } else { console.log('Port range are not numbers, using 8000 instead.'); } } From 20382c5743de6db7eba0cf7408b8344c8e0ceef5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 09:54:38 +0100 Subject: [PATCH 018/330] extract notebook controller kind into its own proposal, https://github.com/microsoft/vscode/issues/131165 --- .../api/common/extHostNotebookKernels.ts | 4 ++-- .../extensions/common/extensionsApiProposals.ts | 1 + .../vscode.proposed.notebookControllerKind.d.ts | 16 ++++++++++++++++ .../vscode.proposed.notebookMessaging.d.ts | 4 ---- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 9d6e323212a..75895d19dcf 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -167,11 +167,11 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { _update(); }, get kind() { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookControllerKind'); return data.kind ?? ''; }, set kind(value) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookControllerKind'); data.kind = value; _update(); }, diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 04349afa658..8f8a4ee8d24 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -21,6 +21,7 @@ export const allApiProposals = Object.freeze({ notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookConcatTextDocument: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts', notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', + notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', notebookDebugOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts', notebookDeprecated: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts', notebookEditor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditor.d.ts', diff --git a/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts b/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts new file mode 100644 index 00000000000..b8a4a5376e8 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * 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-jupyter/issues/7373 + + export interface NotebookController { + /** + * The human-readable label used to categorise controllers. + */ + kind?: string; + } +} diff --git a/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts b/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts index b76ac58a2c0..88660ded2e6 100644 --- a/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts @@ -35,10 +35,6 @@ declare module 'vscode' { } export interface NotebookController { - /** - * The human-readable label used to categorise controllers. - */ - kind?: string; // todo@API allow add, not remove readonly rendererScripts: NotebookRendererScript[]; From 8d3536c7f7c572d89bdbff68a9f30a167a09914a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 10:03:53 +0100 Subject: [PATCH 019/330] extract auth#session into its own proposal file, make isProposedApiEnabled and checkProposedApiEnabled more strict by forcing them to be called with undefined or a proposal name, https://github.com/microsoft/vscode/issues/131165 --- .../api/browser/viewsExtensionPoint.ts | 2 +- .../workbench/api/common/extHost.api.impl.ts | 18 ++++----- .../api/common/extHostMessageService.ts | 2 +- .../api/common/extHostNotebookRenderers.ts | 2 +- src/vs/workbench/api/common/extHostSCM.ts | 2 +- .../api/common/menusExtensionPoint.ts | 4 +- .../contrib/remote/browser/remote.ts | 2 +- .../common/viewsWelcomeContribution.ts | 2 +- .../services/extensions/common/extensions.ts | 4 +- .../common/extensionsApiProposals.ts | 1 + .../services/extensions/node/proxyResolver.ts | 2 +- .../services/label/common/labelService.ts | 2 +- .../themes/common/iconExtensionPoint.ts | 4 +- .../vscode.proposed.authSession.d.ts | 40 +++++++++++++++++++ src/vscode-dts/vscode.proposed.resolvers.d.ts | 33 --------------- 15 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.authSession.d.ts diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index c90faec496b..4ad9fee8a91 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -405,7 +405,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return; } - if (entry.key === 'remote' && !isProposedApiEnabled(extension.description)) { + if (entry.key === 'remote' && !isProposedApiEnabled(extension.description, undefined)) { collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key)); return; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 96e3f393275..6f343668646 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -228,13 +228,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const authentication: typeof vscode.authentication = { getSession(providerId: string, scopes: readonly string[], options?: vscode.AuthenticationGetSessionOptions) { if (options?.forceNewSession) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'authSession'); } return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, // TODO: remove this after GHPR and Codespaces move off of it async hasSession(providerId: string, scopes: readonly string[]) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'authSession'); return !!(await extHostAuthentication.getSession(extension, providerId, scopes, { silent: true } as any)); }, get onDidChangeSessions(): Event { @@ -600,7 +600,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return >extHostMessageService.showMessage(extension, Severity.Error, message, rest[0], >rest.slice(1)); }, showQuickPick(items: any, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { - return extHostQuickOpen.showQuickPick(items, isProposedApiEnabled(extension), options, token); + return extHostQuickOpen.showQuickPick(items, isProposedApiEnabled(extension, undefined), options, token); }, showWorkspaceFolderPick(options?: vscode.WorkspaceFolderPickOptions) { return extHostQuickOpen.showWorkspaceFolderPick(options); @@ -689,7 +689,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostUrls.registerUriHandler(extension.identifier, handler); }, createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.identifier, isProposedApiEnabled(extension)); + return extHostQuickOpen.createQuickPick(extension.identifier, isProposedApiEnabled(extension, undefined)); }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); @@ -1102,7 +1102,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, notebookType, provider); }, get onDidSaveNotebookDocument(): Event { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookDocuments.onDidSaveNotebookDocument; }, createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType { @@ -1113,11 +1113,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebookRenderers.createRendererMessaging(extension, rendererId); }, onDidChangeNotebookDocumentMetadata(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookDocuments.onDidChangeNotebookDocumentMetadata(listener, thisArgs, disposables); }, onDidChangeNotebookCells(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables); }, onDidChangeNotebookCellExecutionState(listener, thisArgs?, disposables?) { @@ -1125,11 +1125,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables); }, onDidChangeCellOutputs(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables); }, onDidChangeCellMetadata(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables); }, createConcatTextDocument(notebook, selector) { diff --git a/src/vs/workbench/api/common/extHostMessageService.ts b/src/vs/workbench/api/common/extHostMessageService.ts index 9f36d8dd3ac..be719110986 100644 --- a/src/vs/workbench/api/common/extHostMessageService.ts +++ b/src/vs/workbench/api/common/extHostMessageService.ts @@ -44,7 +44,7 @@ export class ExtHostMessageService { } if (options.useCustom) { - checkProposedApiEnabled(extension); + checkProposedApiEnabled(extension, 'resolvers'); } const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = []; diff --git a/src/vs/workbench/api/common/extHostNotebookRenderers.ts b/src/vs/workbench/api/common/extHostNotebookRenderers.ts index c217759d9bd..3cc008e1010 100644 --- a/src/vs/workbench/api/common/extHostNotebookRenderers.ts +++ b/src/vs/workbench/api/common/extHostNotebookRenderers.ts @@ -32,7 +32,7 @@ export class ExtHostNotebookRenderers implements ExtHostNotebookRenderersShape { // In the stable API, the editor is given as an empty object, and this map // is used to maintain references. This can be removed after editor finalization. - const notebookEditorVisible = isProposedApiEnabled(manifest); + const notebookEditorVisible = isProposedApiEnabled(manifest, 'notebookEditor'); const notebookEditorAliases = new WeakMap<{}, vscode.NotebookEditor>(); const messaging: vscode.NotebookRendererMessaging = { diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 306e7e373f8..574976beb09 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -502,7 +502,7 @@ class ExtHostSourceControl implements vscode.SourceControl { private _actionButtonDisposables = new MutableDisposable(); private _actionButton: vscode.Command | undefined; get actionButton(): vscode.Command | undefined { - checkProposedApiEnabled(this._extension); + checkProposedApiEnabled(this._extension, 'scmActionButton'); return this._actionButton; } set actionButton(actionButton: vscode.Command | undefined) { diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 82999144a2e..5ee2881e6f8 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -634,7 +634,7 @@ commandsExtensionPoint.setHandler(extensions => { title, source: extension.description.displayName ?? extension.description.name, shortTitle, - tooltip: isProposedApiEnabled(extension.description) ? title : undefined, + tooltip: isProposedApiEnabled(extension.description, undefined) ? title : undefined, category, precondition: ContextKeyExpr.deserialize(enablement), icon: absoluteIcon @@ -764,7 +764,7 @@ menusExtensionPoint.setHandler(extensions => { return; } - if (menu.proposed && !isProposedApiEnabled(extension.description)) { + if (menu.proposed && !isProposedApiEnabled(extension.description, undefined)) { collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value)); return; } diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 4d48e2e58ca..dff2881e612 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -498,7 +498,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements } private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { - if (!isProposedApiEnabled(extension.description)) { + if (!isProposedApiEnabled(extension.description, undefined)) { return; } diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index da5323be01f..b9a713343c4 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -73,7 +73,7 @@ function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointU let group: string | undefined; let order: number | undefined; if (welcome.group) { - if (!isProposedApiEnabled(contribution.description)) { + if (!isProposedApiEnabled(contribution.description, undefined)) { contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled.", contribution.description.identifier.value)); return { group, order }; } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 912c8b55818..02ab47e2c30 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -134,7 +134,7 @@ export interface IExtensionHost { dispose(): void; } -export function isProposedApiEnabled(extension: IExtensionDescription, proposal?: ApiProposalName): boolean { +export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName | undefined): boolean { if (!proposal) { return Boolean(extension.enableProposedApi); } @@ -144,7 +144,7 @@ export function isProposedApiEnabled(extension: IExtensionDescription, proposal? return Boolean(extension.enableProposedApi); } -export function checkProposedApiEnabled(extension: IExtensionDescription, proposal?: ApiProposalName): void { +export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName | undefined): void { if (!isProposedApiEnabled(extension, proposal)) { throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`); } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 8f8a4ee8d24..f60c1461dee 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -6,6 +6,7 @@ // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. export const allApiProposals = Object.freeze({ + authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 9c29c7d2b55..ac2ea9c022c 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -130,7 +130,7 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku } if (!cache[request]) { let mod = modules.default; - if (ext && isProposedApiEnabled(ext)) { + if (ext && isProposedApiEnabled(ext, undefined)) { mod = (modules as any)[(ext).proxySupport] || modules.onRequest; } cache[request] = { ...mod }; // Copy to work around #93167. diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 15252f0546e..509be770cf7 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -84,7 +84,7 @@ class ResourceLabelFormattersHandler implements IWorkbenchContribution { constructor(@ILabelService labelService: ILabelService) { resourceLabelFormattersExtPoint.setHandler((extensions, delta) => { delta.added.forEach(added => added.value.forEach(formatter => { - if (!isProposedApiEnabled(added.description) && formatter.formatting.workspaceTooltip) { + if (!isProposedApiEnabled(added.description, undefined) && formatter.formatting.workspaceTooltip) { // workspaceTooltip is only proposed formatter.formatting.workspaceTooltip = undefined; } diff --git a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts index cfee6d8370b..addcda618e8 100644 --- a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts @@ -126,7 +126,7 @@ export class IconExtensionPoint { const extensionValue = extension.value; const collector = extension.collector; - if (!isProposedApiEnabled(extension.description)) { + if (!isProposedApiEnabled(extension.description, undefined)) { collector.error(nls.localize('invalid.icons.proposedAPI', "'configuration.icons is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } @@ -180,7 +180,7 @@ export class IconFontExtensionPoint { const extensionValue = extension.value; const collector = extension.collector; - if (!isProposedApiEnabled(extension.description)) { + if (!isProposedApiEnabled(extension.description, undefined)) { collector.error(nls.localize('invalid.iconFonts.proposedAPI', "'configuration.iconFonts is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } diff --git a/src/vscode-dts/vscode.proposed.authSession.d.ts b/src/vscode-dts/vscode.proposed.authSession.d.ts new file mode 100644 index 00000000000..a3e593d4224 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.authSession.d.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + /** + * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. + */ + export interface AuthenticationGetSessionOptions { + /** + * 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. + * + * Defaults to false. + */ + forceNewSession?: boolean | { detail: string }; + } + + 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 & { forceNewSession: true | { detail: string } }): Thenable; + export function hasSession(providerId: string, scopes: readonly string[]): Thenable; + } +} diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts index a73316c11a8..9847b67bac3 100644 --- a/src/vscode-dts/vscode.proposed.resolvers.d.ts +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -159,39 +159,6 @@ declare module 'vscode' { candidatePortSource?: CandidatePortSource; } - /** - * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. - */ - export interface AuthenticationGetSessionOptions { - /** - * 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. - * - * Defaults to false. - */ - forceNewSession?: boolean | { detail: string }; - } - - 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 & { forceNewSession: true | { detail: string } }): Thenable; - export function hasSession(providerId: string, scopes: readonly string[]): Thenable; - } - export namespace workspace { /** * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. From 81cf536582dcfd31371b48d610613917a875a804 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 12 Nov 2021 10:52:40 +0100 Subject: [PATCH 020/330] watcher - stop sending out delete events on workspace root (#136673) --- src/vs/platform/files/common/watcher.ts | 20 +++++----- .../node/watcher/nodejs/watcherService.ts | 12 +++--- .../node/watcher/nsfw/nsfwWatcherService.ts | 4 +- .../watcher/parcel/parcelWatcherService.ts | 39 ++++++++++++++----- .../node/recursiveWatcher.integrationTest.ts | 33 +++++++++++----- ...lizer.test.ts => watcherCoalescer.test.ts} | 20 +++++----- .../contrib/files/browser/workspaceWatcher.ts | 2 +- 7 files changed, 82 insertions(+), 48 deletions(-) rename src/vs/platform/files/test/node/{watcherNormalizer.test.ts => watcherCoalescer.test.ts} (92%) diff --git a/src/vs/platform/files/common/watcher.ts b/src/vs/platform/files/common/watcher.ts index 4314712e66b..eb1d60df636 100644 --- a/src/vs/platform/files/common/watcher.ts +++ b/src/vs/platform/files/common/watcher.ts @@ -185,20 +185,20 @@ export function toFileChanges(changes: IDiskFileChange[]): IFileChange[] { })); } -export function normalizeFileChanges(changes: IDiskFileChange[]): IDiskFileChange[] { +export function coalesceEvents(changes: IDiskFileChange[]): IDiskFileChange[] { // Build deltas - const normalizer = new EventNormalizer(); + const coalescer = new EventCoalescer(); for (const event of changes) { - normalizer.processEvent(event); + coalescer.processEvent(event); } - return normalizer.normalize(); + return coalescer.coalesce(); } -class EventNormalizer { +class EventCoalescer { - private readonly normalized = new Set(); + private readonly coalesced = new Set(); private readonly mapPathToChange = new Map(); private toKey(event: IDiskFileChange): string { @@ -232,7 +232,7 @@ class EventNormalizer { // Ignore CREATE followed by DELETE in one go else if (currentChangeType === FileChangeType.ADDED && newChangeType === FileChangeType.DELETED) { this.mapPathToChange.delete(this.toKey(event)); - this.normalized.delete(existingEvent); + this.coalesced.delete(existingEvent); } // Flatten DELETE followed by CREATE into CHANGE @@ -255,12 +255,12 @@ class EventNormalizer { } if (keepEvent) { - this.normalized.add(event); + this.coalesced.add(event); this.mapPathToChange.set(this.toKey(event), event); } } - normalize(): IDiskFileChange[] { + coalesce(): IDiskFileChange[] { const addOrChangeEvents: IDiskFileChange[] = []; const deletedPaths: string[] = []; @@ -271,7 +271,7 @@ class EventNormalizer { // 1.) split ADD/CHANGE and DELETED events // 2.) sort short deleted paths to the top // 3.) for each DELETE, check if there is a deleted parent and ignore the event in that case - return Array.from(this.normalized).filter(e => { + return Array.from(this.coalesced).filter(e => { if (e.type !== FileChangeType.DELETED) { addOrChangeEvents.push(e); diff --git a/src/vs/platform/files/node/watcher/nodejs/watcherService.ts b/src/vs/platform/files/node/watcher/nodejs/watcherService.ts index 346cdb0b656..983b291e229 100644 --- a/src/vs/platform/files/node/watcher/nodejs/watcherService.ts +++ b/src/vs/platform/files/node/watcher/nodejs/watcherService.ts @@ -10,7 +10,7 @@ import { realpath } from 'vs/base/node/extpath'; import { SymlinkSupport } from 'vs/base/node/pfs'; import { CHANGE_BUFFER_DELAY, watchFile, watchFolder } from 'vs/base/node/watcher'; import { FileChangeType } from 'vs/platform/files/common/files'; -import { IDiskFileChange, ILogMessage, normalizeFileChanges } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, ILogMessage, coalesceEvents } from 'vs/platform/files/common/watcher'; export class FileWatcher extends Disposable { private isDisposed: boolean | undefined; @@ -95,19 +95,19 @@ export class FileWatcher extends Disposable { const fileChanges = this.fileChangesBuffer; this.fileChangesBuffer = []; - // Event normalization - const normalizedFileChanges = normalizeFileChanges(fileChanges); + // Event coalsecer + const coalescedFileChanges = coalesceEvents(fileChanges); // Logging if (this.verboseLogging) { - for (const event of normalizedFileChanges) { + for (const event of coalescedFileChanges) { this.onVerbose(`>> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`); } } // Fire - if (normalizedFileChanges.length > 0) { - this.onDidFilesChange(normalizedFileChanges); + if (coalescedFileChanges.length > 0) { + this.onDidFilesChange(coalescedFileChanges); } }); } diff --git a/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts b/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts index 0b7bd2a804a..7b415d8b902 100644 --- a/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts +++ b/src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts @@ -18,7 +18,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { realcaseSync, realpathSync } from 'vs/base/node/extpath'; import { FileChangeType } from 'vs/platform/files/common/files'; import { IWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcher'; -import { IDiskFileChange, ILogMessage, normalizeFileChanges, IWatchRequest } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest } from 'vs/platform/files/common/watcher'; import { watchFolder } from 'vs/base/node/watcher'; interface IWatcher extends IDisposable { @@ -192,7 +192,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService { undeliveredFileEvents = []; // Broadcast to clients normalized - const normalizedEvents = normalizeFileChanges(this.normalizeEvents(undeliveredFileEventsToEmit, request, realBasePathDiffers, realBasePathLength)); + const normalizedEvents = coalesceEvents(this.normalizeEvents(undeliveredFileEventsToEmit, request, realBasePathDiffers, realBasePathLength)); this.emitEvents(normalizedEvents); }, this.getOptions(watcher)).then(async nsfwWatcher => { diff --git a/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts b/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts index a731395f51b..63a859ec83d 100644 --- a/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts +++ b/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts @@ -22,7 +22,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { realcaseSync, realpathSync } from 'vs/base/node/extpath'; import { watchFolder } from 'vs/base/node/watcher'; import { FileChangeType } from 'vs/platform/files/common/files'; -import { IDiskFileChange, ILogMessage, normalizeFileChanges, IWatchRequest, IWatcherService } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest, IWatcherService } from 'vs/platform/files/common/watcher'; export interface IWatcher { @@ -377,14 +377,19 @@ export class ParcelWatcherService extends Disposable implements IWatcherService // Check for excludes const rawEvents = this.handleExcludes(parcelEvents, excludes); - // Normalize and detect root path deletes + // Normalize events: handle NFC normalization and symlinks const { events: normalizedEvents, rootDeleted } = this.normalizeEvents(rawEvents, watcher.request, realPathDiffers, realPathLength); - // Broadcast to clients coalesced - const coalescedEvents = normalizeFileChanges(normalizedEvents); - this.emitEvents(coalescedEvents); + // Coalesce events: merge events of same kind + const coalescedEvents = coalesceEvents(normalizedEvents); - // Handle root path delete if confirmed from coalseced events + // Filter events: check for specific events we want to exclude + const filteredEvents = this.filterEvents(coalescedEvents, watcher.request, rootDeleted); + + // Broadcast to clients + this.emitEvents(filteredEvents); + + // Handle root path delete if confirmed from coalesced events if (rootDeleted && coalescedEvents.some(event => event.path === watcher.request.path && event.type === FileChangeType.DELETED)) { this.onWatchedPathDeleted(watcher); } @@ -485,6 +490,25 @@ export class ParcelWatcherService extends Disposable implements IWatcherService return { events, rootDeleted }; } + private filterEvents(events: IDiskFileChange[], request: IWatchRequest, rootDeleted: boolean): IDiskFileChange[] { + if (!rootDeleted) { + return events; + } + + return events.filter(event => { + if (event.path === request.path && event.type === FileChangeType.DELETED) { + // Explicitly exclude changes to root if we have any + // to avoid VS Code closing all opened editors which + // can happen e.g. in case of network connectivity + // issues + // (https://github.com/microsoft/vscode/issues/136673) + return false; + } + + return true; + }); + } + private onWatchedPathDeleted(watcher: IWatcher): void { this.warn('Watcher shutdown because watched path got deleted', watcher); @@ -502,9 +526,6 @@ export class ParcelWatcherService extends Disposable implements IWatcherService // Stop watching that parent folder disposable.dispose(); - // Send a manual event given we know the root got added again - this.emitEvents([{ path: watcher.request.path, type: FileChangeType.ADDED }]); - // Restart the file watching this.restartWatching(watcher); } diff --git a/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts b/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts index ee26d0ec595..9237d7b6351 100644 --- a/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts @@ -101,13 +101,13 @@ flakySuite('Recursive Watcher (parcel)', () => { } } - async function awaitEvent(service: TestParcelWatcherService, path: string, type: FileChangeType, failOnEventReason?: string): Promise { + function awaitEvent(service: TestParcelWatcherService, path: string, type: FileChangeType, failOnEventReason?: string): Promise { if (loggingEnabled) { console.log(`Awaiting change type '${toMsg(type)}' on file '${path}'`); } // Await the event - await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const disposable = service.onDidChangeFile(events => { for (const event of events) { if (event.path === path && event.type === type) { @@ -124,6 +124,22 @@ flakySuite('Recursive Watcher (parcel)', () => { }); } + function awaitMessage(service: TestParcelWatcherService, type: 'trace' | 'warn' | 'error' | 'info' | 'debug'): Promise { + if (loggingEnabled) { + console.log(`Awaiting message of type ${type}`); + } + + // Await the message + return new Promise(resolve => { + const disposable = service.onDidLogMessage(msg => { + if (msg.type === type) { + disposable.dispose(); + resolve(); + } + }); + }); + } + test('basics', async function () { await service.watch([{ path: testDir, excludes: [] }]); @@ -447,22 +463,19 @@ flakySuite('Recursive Watcher (parcel)', () => { await service.watch([{ path: watchedPath, excludes: [] }]); - // Delete watched path - let changeFuture: Promise = awaitEvent(service, watchedPath, FileChangeType.DELETED); + // Delete watched path and await + const warnFuture = awaitMessage(service, 'warn'); await Promises.rm(watchedPath, RimRafMode.UNLINK); - await changeFuture; + await warnFuture; // Restore watched path - changeFuture = awaitEvent(service, watchedPath, FileChangeType.ADDED); await Promises.mkdir(watchedPath); - await changeFuture; - - await timeout(20); // restart is delayed + await timeout(200); // restart is delayed await service.whenReady(); // Verify events come in again const newFilePath = join(watchedPath, 'newFile.txt'); - changeFuture = awaitEvent(service, newFilePath, FileChangeType.ADDED); + const changeFuture = awaitEvent(service, newFilePath, FileChangeType.ADDED); await Promises.writeFile(newFilePath, 'Hello World'); await changeFuture; }); diff --git a/src/vs/platform/files/test/node/watcherNormalizer.test.ts b/src/vs/platform/files/test/node/watcherCoalescer.test.ts similarity index 92% rename from src/vs/platform/files/test/node/watcherNormalizer.test.ts rename to src/vs/platform/files/test/node/watcherCoalescer.test.ts index 988be68c599..7853f6ce565 100644 --- a/src/vs/platform/files/test/node/watcherNormalizer.test.ts +++ b/src/vs/platform/files/test/node/watcherCoalescer.test.ts @@ -9,7 +9,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform'; import { isEqual } from 'vs/base/common/resources'; import { URI as uri } from 'vs/base/common/uri'; import { FileChangesEvent, FileChangeType, IFileChange } from 'vs/platform/files/common/files'; -import { IDiskFileChange, normalizeFileChanges, toFileChanges } from 'vs/platform/files/common/watcher'; +import { IDiskFileChange, coalesceEvents, toFileChanges } from 'vs/platform/files/common/watcher'; class TestFileWatcher { private readonly _onDidFilesChange: Emitter<{ raw: IFileChange[], event: FileChangesEvent }>; @@ -28,12 +28,12 @@ class TestFileWatcher { private onRawFileEvents(events: IDiskFileChange[]): void { - // Normalize - let normalizedEvents = normalizeFileChanges(events); + // Coalesce + let coalescedEvents = coalesceEvents(events); // Emit through event emitter - if (normalizedEvents.length > 0) { - this._onDidFilesChange.fire({ raw: toFileChanges(normalizedEvents), event: this.toFileChangesEvent(normalizedEvents) }); + if (coalescedEvents.length > 0) { + this._onDidFilesChange.fire({ raw: toFileChanges(coalescedEvents), event: this.toFileChangesEvent(coalescedEvents) }); } } @@ -118,7 +118,7 @@ suite('Watcher Events Normalizer', () => { }); }); - test('event normalization: ignore CREATE followed by DELETE', done => { + test('event coalescer: ignore CREATE followed by DELETE', done => { const watch = new TestFileWatcher(); const created = uri.file('/users/data/src/related'); @@ -143,7 +143,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: flatten DELETE followed by CREATE into CHANGE', done => { + test('event coalescer: flatten DELETE followed by CREATE into CHANGE', done => { const watch = new TestFileWatcher(); const deleted = uri.file('/users/data/src/related'); @@ -169,7 +169,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: ignore UPDATE when CREATE received', done => { + test('event coalescer: ignore UPDATE when CREATE received', done => { const watch = new TestFileWatcher(); const created = uri.file('/users/data/src/related'); @@ -196,7 +196,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: apply DELETE', done => { + test('event coalescer: apply DELETE', done => { const watch = new TestFileWatcher(); const updated = uri.file('/users/data/src/related'); @@ -225,7 +225,7 @@ suite('Watcher Events Normalizer', () => { watch.report(raw); }); - test('event normalization: track case renames', done => { + test('event coalescer: track case renames', done => { const watch = new TestFileWatcher(); const oldPath = uri.file('/users/data/src/added'); diff --git a/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts b/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts index 90f7bba43b4..68fc90c3a16 100644 --- a/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts +++ b/src/vs/workbench/contrib/files/browser/workspaceWatcher.ts @@ -90,7 +90,7 @@ export class WorkspaceWatcher extends Disposable { else if (msg.indexOf('EUNKNOWN') >= 0) { this.notificationService.prompt( Severity.Warning, - localize('eshutdownError', "File changes watcher stopped unexpectedly. Please reload the window to enable the watcher again."), + localize('eshutdownError', "File changes watcher stopped unexpectedly. A reload of the window may enable the watcher again unless the workspace cannot be watched for file changes."), [{ label: localize('reload', "Reload"), run: () => this.hostService.reload() From 4549bd26c7b799284e0ebd8dc1e0310e6a8707a1 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 12 Nov 2021 10:59:52 +0100 Subject: [PATCH 021/330] Extract diff language into its own extension (#136967) --- extensions/diff/.vscodeignore | 2 + extensions/diff/cgmanifest.json | 32 ++++++++++++++++ .../language-configuration.json} | 2 +- extensions/diff/package.json | 38 +++++++++++++++++++ extensions/diff/package.nls.json | 4 ++ .../syntaxes/diff.tmLanguage.json | 0 extensions/git/build/update-grammars.js | 6 --- extensions/git/cgmanifest.json | 29 +------------- extensions/git/package.json | 18 --------- .../test/colorize-fixtures/test.diff} | 0 .../test/colorize-results/test_diff.json} | 0 11 files changed, 78 insertions(+), 53 deletions(-) create mode 100644 extensions/diff/.vscodeignore create mode 100644 extensions/diff/cgmanifest.json rename extensions/{git/languages/diff.language-configuration.json => diff/language-configuration.json} (98%) create mode 100644 extensions/diff/package.json create mode 100644 extensions/diff/package.nls.json rename extensions/{git => diff}/syntaxes/diff.tmLanguage.json (100%) rename extensions/{git/test/colorize-fixtures/example.diff => vscode-colorize-tests/test/colorize-fixtures/test.diff} (100%) rename extensions/{git/test/colorize-results/example_diff.json => vscode-colorize-tests/test/colorize-results/test_diff.json} (100%) diff --git a/extensions/diff/.vscodeignore b/extensions/diff/.vscodeignore new file mode 100644 index 00000000000..d9011becfb6 --- /dev/null +++ b/extensions/diff/.vscodeignore @@ -0,0 +1,2 @@ +build/** +cgmanifest.json diff --git a/extensions/diff/cgmanifest.json b/extensions/diff/cgmanifest.json new file mode 100644 index 00000000000..04d6573c95e --- /dev/null +++ b/extensions/diff/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/diff.tmbundle", + "repositoryUrl": "https://github.com/textmate/diff.tmbundle", + "commitHash": "0593bb775eab1824af97ef2172fd38822abd97d7" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-diff.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/git/languages/diff.language-configuration.json b/extensions/diff/language-configuration.json similarity index 98% rename from extensions/git/languages/diff.language-configuration.json rename to extensions/diff/language-configuration.json index b61fbe63d34..395aff60535 100644 --- a/extensions/git/languages/diff.language-configuration.json +++ b/extensions/diff/language-configuration.json @@ -8,4 +8,4 @@ ["[", "]"], ["(", ")"] ] -} \ No newline at end of file +} diff --git a/extensions/diff/package.json b/extensions/diff/package.json new file mode 100644 index 00000000000..7d23e24ac6b --- /dev/null +++ b/extensions/diff/package.json @@ -0,0 +1,38 @@ +{ + "name": "diff", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "engines": { + "vscode": "0.10.x" + }, + "scripts": { + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/diff.tmbundle Syntaxes/Diff.plist ./syntaxes/diff.tmLanguage.json" + }, + "contributes": { + "languages": [ + { + "id": "diff", + "aliases": [ + "Diff", + "diff" + ], + "extensions": [ + ".diff", + ".patch", + ".rej" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "diff", + "scopeName": "source.diff", + "path": "./syntaxes/diff.tmLanguage.json" + } + ] + } +} diff --git a/extensions/diff/package.nls.json b/extensions/diff/package.nls.json new file mode 100644 index 00000000000..c869050177e --- /dev/null +++ b/extensions/diff/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Diff Language Basics", + "description": "Provides syntax highlighting & bracket matching in Diff files." +} diff --git a/extensions/git/syntaxes/diff.tmLanguage.json b/extensions/diff/syntaxes/diff.tmLanguage.json similarity index 100% rename from extensions/git/syntaxes/diff.tmLanguage.json rename to extensions/diff/syntaxes/diff.tmLanguage.json diff --git a/extensions/git/build/update-grammars.js b/extensions/git/build/update-grammars.js index a5dae9f6850..ad326bff374 100644 --- a/extensions/git/build/update-grammars.js +++ b/extensions/git/build/update-grammars.js @@ -8,9 +8,3 @@ var updateGrammar = require('vscode-grammar-updater'); updateGrammar.update('textmate/git.tmbundle', 'Syntaxes/Git%20Commit%20Message.tmLanguage', './syntaxes/git-commit.tmLanguage.json'); updateGrammar.update('textmate/git.tmbundle', 'Syntaxes/Git%20Rebase%20Message.tmLanguage', './syntaxes/git-rebase.tmLanguage.json'); -updateGrammar.update('textmate/diff.tmbundle', 'Syntaxes/Diff.plist', './syntaxes/diff.tmLanguage.json'); - - - - - diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json index e8081d6472e..256966aba20 100644 --- a/extensions/git/cgmanifest.json +++ b/extensions/git/cgmanifest.json @@ -33,34 +33,7 @@ ], "license": "MIT", "version": "0.0.0" - }, - { - "component": { - "type": "git", - "git": { - "name": "textmate/diff.tmbundle", - "repositoryUrl": "https://github.com/textmate/diff.tmbundle", - "commitHash": "0593bb775eab1824af97ef2172fd38822abd97d7" - } - }, - "licenseDetail": [ - "Copyright (c) textmate-diff.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ], - "license": "TextMate Bundle License", - "version": "0.0.0" } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/git/package.json b/extensions/git/package.json index b778540aa91..adacad15ae9 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2314,19 +2314,6 @@ ], "configuration": "./languages/git-rebase.language-configuration.json" }, - { - "id": "diff", - "aliases": [ - "Diff", - "diff" - ], - "extensions": [ - ".diff", - ".patch", - ".rej" - ], - "configuration": "./languages/diff.language-configuration.json" - }, { "id": "ignore", "aliases": [ @@ -2351,11 +2338,6 @@ "scopeName": "text.git-rebase", "path": "./syntaxes/git-rebase.tmLanguage.json" }, - { - "language": "diff", - "scopeName": "source.diff", - "path": "./syntaxes/diff.tmLanguage.json" - }, { "language": "ignore", "scopeName": "source.ignore", diff --git a/extensions/git/test/colorize-fixtures/example.diff b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.diff similarity index 100% rename from extensions/git/test/colorize-fixtures/example.diff rename to extensions/vscode-colorize-tests/test/colorize-fixtures/test.diff diff --git a/extensions/git/test/colorize-results/example_diff.json b/extensions/vscode-colorize-tests/test/colorize-results/test_diff.json similarity index 100% rename from extensions/git/test/colorize-results/example_diff.json rename to extensions/vscode-colorize-tests/test/colorize-results/test_diff.json From 7ac327281abe7bb460aaf01adeda47bff7f3305b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 10:13:32 +0100 Subject: [PATCH 022/330] make sure proposal name generation is sorted, https://github.com/microsoft/vscode/issues/131165 --- build/lib/compilation.js | 2 +- build/lib/compilation.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 5a882c3f88a..b81e714fa41 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -183,7 +183,7 @@ function apiProposalNamesGenerator() { try { const t1 = Date.now(); const proposalNames = []; - for (let file of fs.readdirSync(dtsFolder)) { + for (let file of fs.readdirSync(dtsFolder).sort()) { const match = pattern.exec(file); if (match) { proposalNames.push([match[1], `https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/${file}`]); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 120c0403755..892acaa4216 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -224,7 +224,7 @@ function apiProposalNamesGenerator() { const t1 = Date.now(); const proposalNames: [name: string, url: string][] = []; - for (let file of fs.readdirSync(dtsFolder)) { + for (let file of fs.readdirSync(dtsFolder).sort()) { const match = pattern.exec(file); if (match) { proposalNames.push([match[1], `https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/${file}`]); From 09c9d4c29377ae5cead4e4399c1c203e5385b547 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 11:05:30 +0100 Subject: [PATCH 023/330] use `enabledApiProposals` instead of wildcard property, also update compilation units to only include d.ts-files that are needed, https://github.com/microsoft/vscode/issues/131165 --- .../client/tsconfig.json | 3 +- extensions/css-language-features/package.json | 1 - extensions/debug-server-ready/package.json | 4 +- extensions/debug-server-ready/tsconfig.json | 2 +- extensions/git/package.json | 8 ++- extensions/git/tsconfig.json | 6 ++- extensions/github-authentication/package.json | 1 - .../github-authentication/tsconfig.json | 3 +- extensions/github/package.json | 1 - extensions/github/tsconfig.json | 3 +- .../client/tsconfig.json | 3 +- .../html-language-features/package.json | 1 - extensions/image-preview/package.json | 1 - extensions/image-preview/tsconfig.json | 3 +- extensions/ipynb/package.json | 41 +++++++++------- extensions/ipynb/tsconfig.json | 3 +- .../client/tsconfig.json | 3 +- .../json-language-features/package.json | 1 - .../markdown-language-features/package.json | 1 - .../markdown-language-features/tsconfig.json | 3 +- .../microsoft-authentication/package.json | 1 - .../microsoft-authentication/tsconfig.json | 3 +- extensions/npm/package.json | 1 - extensions/npm/tsconfig.json | 3 +- extensions/php-language-features/package.json | 1 - .../php-language-features/tsconfig.json | 3 +- extensions/search-result/package.json | 1 - extensions/search-result/tsconfig.json | 3 +- extensions/simple-browser/package.json | 4 +- extensions/simple-browser/tsconfig.json | 2 +- .../typescript-language-features/package.json | 6 ++- .../tsconfig.json | 4 +- extensions/vscode-api-tests/package.json | 49 ++++++++++++++++++- extensions/vscode-colorize-tests/package.json | 1 - .../vscode-colorize-tests/tsconfig.json | 3 +- .../vscode-custom-editor-tests/package.json | 1 - extensions/vscode-notebook-tests/package.json | 15 +++++- .../vscode-notebook-tests/tsconfig.json | 2 +- extensions/vscode-test-resolver/package.json | 4 +- extensions/vscode-test-resolver/tsconfig.json | 2 +- 40 files changed, 130 insertions(+), 71 deletions(-) diff --git a/extensions/css-language-features/client/tsconfig.json b/extensions/css-language-features/client/tsconfig.json index e6a92d451d8..573b24b4aa6 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -5,7 +5,6 @@ }, "include": [ "src/**/*", - "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.*.d.ts", + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 7c3b8e75472..88994a67f44 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -17,7 +17,6 @@ ], "main": "./client/out/node/cssClientMain", "browser": "./client/dist/browser/cssClientMain", - "enableProposedApi": true, "capabilities": { "virtualWorkspaces": true, "untrustedWorkspaces": { diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index e5c69df006b..64aa227aa53 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -18,7 +18,9 @@ "supported": true } }, - "enableProposedApi": true, + "enabledApiProposals": [ + "terminalDataWriteEvent" + ], "main": "./out/extension", "scripts": { "compile": "gulp compile-extension:debug-server-ready", diff --git a/extensions/debug-server-ready/tsconfig.json b/extensions/debug-server-ready/tsconfig.json index ea25ee33fda..9bf747283ca 100644 --- a/extensions/debug-server-ready/tsconfig.json +++ b/extensions/debug-server-ready/tsconfig.json @@ -10,6 +10,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts", + "../../src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts", ] } diff --git a/extensions/git/package.json b/extensions/git/package.json index adacad15ae9..e14ea0dc12a 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -9,7 +9,13 @@ "vscode": "^1.5.0" }, "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "enableProposedApi": true, + "enabledApiProposals": [ + "diffCommand", + "scmActionButton", + "scmSelectedProvider", + "scmValidation", + "timeline" + ], "categories": [ "Other" ], diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index c64e35be794..99129079e48 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -10,7 +10,11 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts", + "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", + "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", + "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", + "../../src/vscode-dts/vscode.proposed.timeline.d.ts", "../types/lib.textEncoder.d.ts" ] } diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 45c6a4b5175..2dbf469fd26 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -9,7 +9,6 @@ "vscode": "^1.41.0" }, "icon": "images/icon.png", - "enableProposedApi": true, "categories": [ "Other" ], diff --git a/extensions/github-authentication/tsconfig.json b/extensions/github-authentication/tsconfig.json index 4d6c63c3a86..d7aed1836ee 100644 --- a/extensions/github-authentication/tsconfig.json +++ b/extensions/github-authentication/tsconfig.json @@ -9,7 +9,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/github/package.json b/extensions/github/package.json index f21080ab6f1..5e3e72a7b1c 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -9,7 +9,6 @@ "vscode": "^1.41.0" }, "icon": "images/icon.png", - "enableProposedApi": true, "categories": [ "Other" ], diff --git a/extensions/github/tsconfig.json b/extensions/github/tsconfig.json index 4d6c63c3a86..d7aed1836ee 100644 --- a/extensions/github/tsconfig.json +++ b/extensions/github/tsconfig.json @@ -9,7 +9,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/html-language-features/client/tsconfig.json b/extensions/html-language-features/client/tsconfig.json index f71a7ae175e..573b24b4aa6 100644 --- a/extensions/html-language-features/client/tsconfig.json +++ b/extensions/html-language-features/client/tsconfig.json @@ -5,7 +5,6 @@ }, "include": [ "src/**/*", - "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index ddaf33bcb65..07bbfe29e9a 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -1,5 +1,4 @@ { - "enableProposedApi": true, "name": "html-language-features", "displayName": "%displayName%", "description": "%description%", diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index c9aa3d23a85..8b730de9790 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -9,7 +9,6 @@ "version": "1.0.0", "publisher": "vscode", "icon": "icon.png", - "enableProposedApi": true, "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { diff --git a/extensions/image-preview/tsconfig.json b/extensions/image-preview/tsconfig.json index deb0d3842a3..c5194e2e33c 100644 --- a/extensions/image-preview/tsconfig.json +++ b/extensions/image-preview/tsconfig.json @@ -6,7 +6,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index cffba92357d..b31a322dff3 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -8,7 +8,10 @@ "engines": { "vscode": "^1.57.0" }, - "enableProposedApi": true, + "enabledApiProposals": [ + "notebookEditor", + "notebookEditorEdit" + ], "activationEvents": [ "onNotebook:jupyter-notebook" ], @@ -36,16 +39,16 @@ ] } ], - "grammars": [ - { - "language": "jupyter", - "scopeName": "source.jupyter", - "path": "./syntaxes/jupyter.tmLanguage.json", - "embeddedLanguages": { - "source.json": "json" - } - } - ], + "grammars": [ + { + "language": "jupyter", + "scopeName": "source.jupyter", + "path": "./syntaxes/jupyter.tmLanguage.json", + "embeddedLanguages": { + "source.json": "json" + } + } + ], "notebooks": [ { "type": "jupyter-notebook", @@ -59,10 +62,10 @@ } ] }, - "scripts": { - "compile": "npx gulp compile-extension:ipynb", - "watch": "npx gulp watch-extension:ipynb" - }, + "scripts": { + "compile": "npx gulp compile-extension:ipynb", + "watch": "npx gulp watch-extension:ipynb" + }, "dependencies": { "@enonic/fnv-plus": "^1.3.0", "detect-indent": "^6.0.0", @@ -72,8 +75,8 @@ "@jupyterlab/coreutils": "^3.1.0", "@types/uuid": "^8.3.1" }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } + "repository": { + "type": "git", + "url": "https://github.com/microsoft/vscode.git" + } } diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 1e72bb74947..2032bf87b0d 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -9,6 +9,7 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts", + "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts", ] } diff --git a/extensions/json-language-features/client/tsconfig.json b/extensions/json-language-features/client/tsconfig.json index f71a7ae175e..573b24b4aa6 100644 --- a/extensions/json-language-features/client/tsconfig.json +++ b/extensions/json-language-features/client/tsconfig.json @@ -5,7 +5,6 @@ }, "include": [ "src/**/*", - "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 91dcbb8d554..6a6fb61c9a6 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -16,7 +16,6 @@ ], "main": "./client/out/node/jsonClientMain", "browser": "./client/dist/browser/jsonClientMain", - "enableProposedApi": true, "capabilities": { "virtualWorkspaces": true, "untrustedWorkspaces": { diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index ba2157fde37..3734ba11aaf 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -5,7 +5,6 @@ "version": "1.0.0", "icon": "icon.png", "publisher": "vscode", - "enableProposedApi": true, "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 5bede697077..fcd79775de5 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -5,7 +5,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index ef97ae07a9b..33e5eb749fc 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -12,7 +12,6 @@ "categories": [ "Other" ], - "enableProposedApi": true, "activationEvents": [ "onAuthenticationRequest:microsoft" ], diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json index d8764400622..51edc522bc6 100644 --- a/extensions/microsoft-authentication/tsconfig.json +++ b/extensions/microsoft-authentication/tsconfig.json @@ -18,7 +18,6 @@ ], "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 3e85fc13ae1..fbc75dd0fb5 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -8,7 +8,6 @@ "engines": { "vscode": "0.10.x" }, - "enableProposedApi": true, "icon": "images/npm_icon.png", "categories": [ "Other" diff --git a/extensions/npm/tsconfig.json b/extensions/npm/tsconfig.json index 3ef85d919ec..7234fdfeb97 100644 --- a/extensions/npm/tsconfig.json +++ b/extensions/npm/tsconfig.json @@ -8,7 +8,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index 8447f019786..03176a05573 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -9,7 +9,6 @@ "engines": { "vscode": "0.10.x" }, - "enableProposedApi": true, "activationEvents": [ "onLanguage:php" ], diff --git a/extensions/php-language-features/tsconfig.json b/extensions/php-language-features/tsconfig.json index 3ef85d919ec..7234fdfeb97 100644 --- a/extensions/php-language-features/tsconfig.json +++ b/extensions/php-language-features/tsconfig.json @@ -8,7 +8,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index d7f9aa4a138..fe64cfa8fe0 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -3,7 +3,6 @@ "displayName": "%displayName%", "description": "%description%", "version": "1.0.0", - "enableProposedApi": true, "publisher": "vscode", "license": "MIT", "icon": "images/icon.png", diff --git a/extensions/search-result/tsconfig.json b/extensions/search-result/tsconfig.json index ab6639e3a9d..f0f7c00adf5 100644 --- a/extensions/search-result/tsconfig.json +++ b/extensions/search-result/tsconfig.json @@ -5,7 +5,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index a1b2560bbc9..ea0fd90265b 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -2,7 +2,9 @@ "name": "simple-browser", "displayName": "%displayName%", "description": "%description%", - "enableProposedApi": true, + "enabledApiProposals": [ + "externalUriOpener" + ], "version": "1.0.0", "icon": "media/icon.png", "publisher": "vscode", diff --git a/extensions/simple-browser/tsconfig.json b/extensions/simple-browser/tsconfig.json index deb0d3842a3..bd370826678 100644 --- a/extensions/simple-browser/tsconfig.json +++ b/extensions/simple-browser/tsconfig.json @@ -7,6 +7,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.proposed.externalUriOpener.d.ts", ] } diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 280a5fe744e..3eaf31c0c5d 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -7,9 +7,11 @@ "publisher": "vscode", "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "enableProposedApi": true, "enabledApiProposals": [ - "languageStatus" + "inlayHints", + "languageStatus", + "resolvers", + "workspaceTrust" ], "capabilities": { "virtualWorkspaces": { diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 2d5e8c31a3b..07d4066f89b 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -10,7 +10,9 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.inlayHints.d.ts", "../../src/vscode-dts/vscode.proposed.languageStatus.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", + "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", ] } diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index dcdd8b608a9..a4b8c32909a 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -4,7 +4,54 @@ "version": "0.0.1", "publisher": "vscode", "license": "MIT", - "enableProposedApi": true, + "enabledApiProposals": [ + "authSession", + "customEditorMove", + "diffCommand", + "documentFiltersExclusive", + "editorInsets", + "extensionRuntime", + "externalUriOpener", + "fileSearchProvider", + "findTextInFiles", + "fsChunks", + "inlayHints", + "inlineCompletions", + "languageStatus", + "notebookCellExecutionState", + "notebookConcatTextDocument", + "notebookContentProvider", + "notebookControllerKind", + "notebookDebugOptions", + "notebookDeprecated", + "notebookEditor", + "notebookEditorDecorationType", + "notebookEditorEdit", + "notebookLiveShare", + "notebookMessaging", + "notebookMime", + "portsAttributes", + "quickPickSortByLabel", + "resolvers", + "scmActionButton", + "scmSelectedProvider", + "scmValidation", + "tabs", + "taskPresentationGroup", + "terminalDataWriteEvent", + "terminalDimensions", + "terminalLocation", + "terminalNameChangeEvent", + "testCoverage", + "testObserver", + "textDocumentNotebook", + "textSearchProvider", + "timeline", + "tokenInformation", + "treeViewDragAndDrop", + "treeViewReveal", + "workspaceTrust" + ], "private": true, "activationEvents": [], "main": "./out/extension", diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index b2ce2e9d9cf..b27439bf8b6 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -9,7 +9,6 @@ "onLanguage:json" ], "main": "./out/colorizerTestMain", - "enableProposedApi": true, "engines": { "vscode": "*" }, diff --git a/extensions/vscode-colorize-tests/tsconfig.json b/extensions/vscode-colorize-tests/tsconfig.json index 3ef85d919ec..7234fdfeb97 100644 --- a/extensions/vscode-colorize-tests/tsconfig.json +++ b/extensions/vscode-colorize-tests/tsconfig.json @@ -8,7 +8,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/vscode-custom-editor-tests/package.json b/extensions/vscode-custom-editor-tests/package.json index acdd4adcb9e..778f5c56cf0 100644 --- a/extensions/vscode-custom-editor-tests/package.json +++ b/extensions/vscode-custom-editor-tests/package.json @@ -9,7 +9,6 @@ "onCustomEditor:testWebviewEditor.abc" ], "main": "./out/extension", - "enableProposedApi": true, "engines": { "vscode": "^1.48.0" }, diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index cc0264bb1a3..3cf93067634 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -9,7 +9,20 @@ "*" ], "main": "./out/extension", - "enableProposedApi": true, + "enabledApiProposals": [ + "notebookCellExecutionState", + "notebookConcatTextDocument", + "notebookContentProvider", + "notebookControllerKind", + "notebookDebugOptions", + "notebookDeprecated", + "notebookEditor", + "notebookEditorDecorationType", + "notebookEditorEdit", + "notebookLiveShare", + "notebookMessaging", + "notebookMime" + ], "engines": { "vscode": "^1.25.0" }, diff --git a/extensions/vscode-notebook-tests/tsconfig.json b/extensions/vscode-notebook-tests/tsconfig.json index 3ef85d919ec..c9c35c63299 100644 --- a/extensions/vscode-notebook-tests/tsconfig.json +++ b/extensions/vscode-notebook-tests/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.proposed.notebook*.d.ts" ] } diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 99a836fd6c2..76946671bda 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -4,7 +4,9 @@ "version": "0.0.1", "publisher": "vscode", "license": "MIT", - "enableProposedApi": true, + "enabledApiProposals": [ + "resolvers" + ], "private": true, "engines": { "vscode": "^1.25.0" diff --git a/extensions/vscode-test-resolver/tsconfig.json b/extensions/vscode-test-resolver/tsconfig.json index 3ef85d919ec..f9a183ef9e1 100644 --- a/extensions/vscode-test-resolver/tsconfig.json +++ b/extensions/vscode-test-resolver/tsconfig.json @@ -9,6 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.*.d.ts" + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts" ] } From e95c74c4c7af876e79ec58df262464467c06df28 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 12 Nov 2021 11:35:35 +0100 Subject: [PATCH 024/330] Extract git static contributions into a separate extension --- extensions/git-base/.vscodeignore | 2 + .../build/update-grammars.js | 0 extensions/git-base/cgmanifest.json | 39 +++++++++++ .../git-commit.language-configuration.json | 0 .../git-rebase.language-configuration.json | 0 .../ignore.language-configuration.json | 0 extensions/git-base/package.json | 70 +++++++++++++++++++ extensions/git-base/package.nls.json | 4 ++ .../syntaxes/git-commit.tmLanguage.json | 0 .../syntaxes/git-rebase.tmLanguage.json | 0 .../syntaxes/ignore.tmLanguage.json | 0 extensions/git/package.json | 55 --------------- .../test/colorize-fixtures/git-rebase-todo | 15 ---- extensions/git/test/mocha.opts | 1 - .../test/colorize-fixtures/COMMIT_EDITMSG | 0 .../test/colorize-fixtures/git-rebase-todo | 0 .../test/colorize-results/COMMIT_EDITMSG.json | 0 .../colorize-results/git-rebase-todo.json | 0 18 files changed, 115 insertions(+), 71 deletions(-) create mode 100644 extensions/git-base/.vscodeignore rename extensions/{git => git-base}/build/update-grammars.js (100%) create mode 100644 extensions/git-base/cgmanifest.json rename extensions/{git => git-base}/languages/git-commit.language-configuration.json (100%) rename extensions/{git => git-base}/languages/git-rebase.language-configuration.json (100%) rename extensions/{git => git-base}/languages/ignore.language-configuration.json (100%) create mode 100644 extensions/git-base/package.json create mode 100644 extensions/git-base/package.nls.json rename extensions/{git => git-base}/syntaxes/git-commit.tmLanguage.json (100%) rename extensions/{git => git-base}/syntaxes/git-rebase.tmLanguage.json (100%) rename extensions/{git => git-base}/syntaxes/ignore.tmLanguage.json (100%) delete mode 100644 extensions/git/test/colorize-fixtures/git-rebase-todo delete mode 100644 extensions/git/test/mocha.opts rename extensions/{git => vscode-colorize-tests}/test/colorize-fixtures/COMMIT_EDITMSG (100%) create mode 100644 extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo rename extensions/{git => vscode-colorize-tests}/test/colorize-results/COMMIT_EDITMSG.json (100%) rename extensions/{git => vscode-colorize-tests}/test/colorize-results/git-rebase-todo.json (100%) diff --git a/extensions/git-base/.vscodeignore b/extensions/git-base/.vscodeignore new file mode 100644 index 00000000000..d9011becfb6 --- /dev/null +++ b/extensions/git-base/.vscodeignore @@ -0,0 +1,2 @@ +build/** +cgmanifest.json diff --git a/extensions/git/build/update-grammars.js b/extensions/git-base/build/update-grammars.js similarity index 100% rename from extensions/git/build/update-grammars.js rename to extensions/git-base/build/update-grammars.js diff --git a/extensions/git-base/cgmanifest.json b/extensions/git-base/cgmanifest.json new file mode 100644 index 00000000000..256966aba20 --- /dev/null +++ b/extensions/git-base/cgmanifest.json @@ -0,0 +1,39 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/git.tmbundle", + "repositoryUrl": "https://github.com/textmate/git.tmbundle", + "commitHash": "5870cf3f8abad3a6637bdf69250b5d2ded427dc4" + } + }, + "licenseDetail": [ + "Copyright (c) 2008 Tim Harper", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of this software and associated documentation files (the\"", + "Software\"), to deal in the Software without restriction, including", + "without limitation the rights to use, copy, modify, merge, publish,", + "distribute, sublicense, and/or sell copies of the Software, and to", + "permit persons to whom the Software is furnished to do so, subject to", + "the following conditions:", + "", + "The above copyright notice and this permission notice shall be", + "included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/git/languages/git-commit.language-configuration.json b/extensions/git-base/languages/git-commit.language-configuration.json similarity index 100% rename from extensions/git/languages/git-commit.language-configuration.json rename to extensions/git-base/languages/git-commit.language-configuration.json diff --git a/extensions/git/languages/git-rebase.language-configuration.json b/extensions/git-base/languages/git-rebase.language-configuration.json similarity index 100% rename from extensions/git/languages/git-rebase.language-configuration.json rename to extensions/git-base/languages/git-rebase.language-configuration.json diff --git a/extensions/git/languages/ignore.language-configuration.json b/extensions/git-base/languages/ignore.language-configuration.json similarity index 100% rename from extensions/git/languages/ignore.language-configuration.json rename to extensions/git-base/languages/ignore.language-configuration.json diff --git a/extensions/git-base/package.json b/extensions/git-base/package.json new file mode 100644 index 00000000000..db3cbb7a812 --- /dev/null +++ b/extensions/git-base/package.json @@ -0,0 +1,70 @@ +{ + "name": "git-base", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "engines": { + "vscode": "0.10.x" + }, + "scripts": { + "update-grammar": "node ./build/update-grammars.js" + }, + "contributes": { + "languages": [ + { + "id": "git-commit", + "aliases": [ + "Git Commit Message", + "git-commit" + ], + "filenames": [ + "COMMIT_EDITMSG", + "MERGE_MSG" + ], + "configuration": "./languages/git-commit.language-configuration.json" + }, + { + "id": "git-rebase", + "aliases": [ + "Git Rebase Message", + "git-rebase" + ], + "filenames": [ + "git-rebase-todo" + ], + "configuration": "./languages/git-rebase.language-configuration.json" + }, + { + "id": "ignore", + "aliases": [ + "Ignore", + "ignore" + ], + "extensions": [ + ".gitignore_global", + ".gitignore" + ], + "configuration": "./languages/ignore.language-configuration.json" + } + ], + "grammars": [ + { + "language": "git-commit", + "scopeName": "text.git-commit", + "path": "./syntaxes/git-commit.tmLanguage.json" + }, + { + "language": "git-rebase", + "scopeName": "text.git-rebase", + "path": "./syntaxes/git-rebase.tmLanguage.json" + }, + { + "language": "ignore", + "scopeName": "source.ignore", + "path": "./syntaxes/ignore.tmLanguage.json" + } + ] + } +} diff --git a/extensions/git-base/package.nls.json b/extensions/git-base/package.nls.json new file mode 100644 index 00000000000..4c1acedb648 --- /dev/null +++ b/extensions/git-base/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Git Base", + "description": "Git static contributions and pickers." +} diff --git a/extensions/git/syntaxes/git-commit.tmLanguage.json b/extensions/git-base/syntaxes/git-commit.tmLanguage.json similarity index 100% rename from extensions/git/syntaxes/git-commit.tmLanguage.json rename to extensions/git-base/syntaxes/git-commit.tmLanguage.json diff --git a/extensions/git/syntaxes/git-rebase.tmLanguage.json b/extensions/git-base/syntaxes/git-rebase.tmLanguage.json similarity index 100% rename from extensions/git/syntaxes/git-rebase.tmLanguage.json rename to extensions/git-base/syntaxes/git-rebase.tmLanguage.json diff --git a/extensions/git/syntaxes/ignore.tmLanguage.json b/extensions/git-base/syntaxes/ignore.tmLanguage.json similarity index 100% rename from extensions/git/syntaxes/ignore.tmLanguage.json rename to extensions/git-base/syntaxes/ignore.tmLanguage.json diff --git a/extensions/git/package.json b/extensions/git/package.json index adacad15ae9..7c36fa9da16 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -23,7 +23,6 @@ "compile": "gulp compile-extension:git", "watch": "gulp watch-extension:git", "update-emoji": "node ./build/update-emoji.js", - "update-grammar": "node ./build/update-grammars.js", "test": "node ../../node_modules/mocha/bin/mocha" }, "capabilities": { @@ -2290,60 +2289,6 @@ } } ], - "languages": [ - { - "id": "git-commit", - "aliases": [ - "Git Commit Message", - "git-commit" - ], - "filenames": [ - "COMMIT_EDITMSG", - "MERGE_MSG" - ], - "configuration": "./languages/git-commit.language-configuration.json" - }, - { - "id": "git-rebase", - "aliases": [ - "Git Rebase Message", - "git-rebase" - ], - "filenames": [ - "git-rebase-todo" - ], - "configuration": "./languages/git-rebase.language-configuration.json" - }, - { - "id": "ignore", - "aliases": [ - "Ignore", - "ignore" - ], - "extensions": [ - ".gitignore_global", - ".gitignore" - ], - "configuration": "./languages/ignore.language-configuration.json" - } - ], - "grammars": [ - { - "language": "git-commit", - "scopeName": "text.git-commit", - "path": "./syntaxes/git-commit.tmLanguage.json" - }, - { - "language": "git-rebase", - "scopeName": "text.git-rebase", - "path": "./syntaxes/git-rebase.tmLanguage.json" - }, - { - "language": "ignore", - "scopeName": "source.ignore", - "path": "./syntaxes/ignore.tmLanguage.json" - } - ], "configurationDefaults": { "[git-commit]": { "editor.rulers": [ diff --git a/extensions/git/test/colorize-fixtures/git-rebase-todo b/extensions/git/test/colorize-fixtures/git-rebase-todo deleted file mode 100644 index 3b6df1cd4f7..00000000000 --- a/extensions/git/test/colorize-fixtures/git-rebase-todo +++ /dev/null @@ -1,15 +0,0 @@ -pick 1fc6c95 Patch A -squash fa39187 Something to add to patch A -pick 7b36971 Something to move before patch B -pick 6b2481b Patch B -fixup c619268 A fix for Patch B -edit dd1475d Something I want to split -reword 4ca2acc i cant' typ goods - -# Commands: -# p, pick = use commit -# r, reword = use commit, but edit the commit message -# e, edit = use commit, but stop for amending -# s, squash = use commit, but meld into previous commit -# f, fixup = like "squash", but discard this commit's log message -# x, exec = run command (the rest of the line) using shell \ No newline at end of file diff --git a/extensions/git/test/mocha.opts b/extensions/git/test/mocha.opts deleted file mode 100644 index 93c2e8fffb6..00000000000 --- a/extensions/git/test/mocha.opts +++ /dev/null @@ -1 +0,0 @@ ---ui tdd out/test \ No newline at end of file diff --git a/extensions/git/test/colorize-fixtures/COMMIT_EDITMSG b/extensions/vscode-colorize-tests/test/colorize-fixtures/COMMIT_EDITMSG similarity index 100% rename from extensions/git/test/colorize-fixtures/COMMIT_EDITMSG rename to extensions/vscode-colorize-tests/test/colorize-fixtures/COMMIT_EDITMSG diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo b/extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/git/test/colorize-results/COMMIT_EDITMSG.json b/extensions/vscode-colorize-tests/test/colorize-results/COMMIT_EDITMSG.json similarity index 100% rename from extensions/git/test/colorize-results/COMMIT_EDITMSG.json rename to extensions/vscode-colorize-tests/test/colorize-results/COMMIT_EDITMSG.json diff --git a/extensions/git/test/colorize-results/git-rebase-todo.json b/extensions/vscode-colorize-tests/test/colorize-results/git-rebase-todo.json similarity index 100% rename from extensions/git/test/colorize-results/git-rebase-todo.json rename to extensions/vscode-colorize-tests/test/colorize-results/git-rebase-todo.json From 7bbae2d44dd16903530f8927f9cb502243c7ae30 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 12 Nov 2021 11:43:25 +0100 Subject: [PATCH 025/330] Do not brutally kill extension hosts in the abnormal shutdown case to unblock the build --- .../directMainProcessExtensionHostStarter.ts | 13 +++++++++---- .../workerMainProcessExtensionHostStarter.ts | 19 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts index a238332b86d..8c9bfd32a48 100644 --- a/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts @@ -15,10 +15,15 @@ export class DirectMainProcessExtensionHostStarter extends ExtensionHostStarter ) { super(logService); - // Abnormal shutdown: terminate extension hosts asap - lifecycleMainService.onWillKill(() => { - this.killAllNow(); - }); + // TODO: in the remote integration tests, this leads to the extension + // host processes getting killed brutally, which leads to the + // test resolver not having a chance to deactivate and kill the server processes + // it launches + + // // Abnormal shutdown: terminate extension hosts asap + // lifecycleMainService.onWillKill(() => { + // this.killAllNow(); + // }); // Normal shutdown: gracefully await extension host shutdowns lifecycleMainService.onWillShutdown((e) => { diff --git a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts index 2abb2eb9dfd..f98915bfdb6 100644 --- a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts @@ -80,13 +80,18 @@ export class WorkerMainProcessExtensionHostStarter implements IDisposable, IExte ); this._initialize(); - // Abnormal shutdown: terminate extension hosts asap - lifecycleMainService.onWillKill(async () => { - this._shutdown = true; - if (this._proxy) { - this._proxy.killAllNow(); - } - }); + // TODO: in the remote integration tests, this leads to the extension + // host processes getting killed brutally, which leads to the + // test resolver not having a chance to deactivate and kill the server processes + // it launches + + // // Abnormal shutdown: terminate extension hosts asap + // lifecycleMainService.onWillKill(async () => { + // this._shutdown = true; + // if (this._proxy) { + // this._proxy.killAllNow(); + // } + // }); // Normal shutdown: gracefully await extension host shutdowns lifecycleMainService.onWillShutdown((e) => { From 22b53c1acfc905cc91be28e46812d3b73890df76 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 12 Nov 2021 11:57:15 +0100 Subject: [PATCH 026/330] Add contextualTitle for NPM Scripts Fixes #111994 --- extensions/npm/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/npm/package.json b/extensions/npm/package.json index fbc75dd0fb5..ce506759aa4 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -72,7 +72,8 @@ "name": "%view.name%", "when": "npm:showScriptExplorer", "icon": "$(json)", - "visibility": "hidden" + "visibility": "hidden", + "contextualTitle": "%view.name%" } ] }, From e6a106f5242a0e303269ff7da33a91ef96da27a0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 12:24:16 +0100 Subject: [PATCH 027/330] fix https://github.com/microsoft/monaco-editor/issues/2366 --- src/vs/editor/contrib/suggest/suggestWidgetDetails.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts index b5e9cad6a46..e12163c1035 100644 --- a/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/suggestWidgetDetails.ts @@ -363,7 +363,7 @@ export class SuggestDetailsOverlay implements IOverlayWidget { } placeAtAnchor(anchor: HTMLElement, preferAlignAtTop: boolean) { - const anchorBox = dom.getDomNodePagePosition(anchor); + const anchorBox = anchor.getBoundingClientRect(); this._anchorBox = anchorBox; this._preferAlignAtTop = preferAlignAtTop; this._placeAtAnchor(this._anchorBox, this._userSize ?? this.widget.size, preferAlignAtTop); From d775c7346cc5260e7fa0c3155f61c1cb2b7d02bc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 12:31:56 +0100 Subject: [PATCH 028/330] create stand-in declaration for contribViewsWelcome and adopt for git-extension, https://github.com/microsoft/vscode/issues/131165 --- extensions/git/package.json | 1 + .../contrib/welcome/common/viewsWelcomeContribution.ts | 2 +- .../services/extensions/common/extensionsApiProposals.ts | 1 + src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts | 6 ++++++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index e14ea0dc12a..d6f792d939e 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -11,6 +11,7 @@ "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "enabledApiProposals": [ "diffCommand", + "contribViewsWelcome", "scmActionButton", "scmSelectedProvider", "scmValidation", diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index b9a713343c4..41f202b54d4 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -73,7 +73,7 @@ function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointU let group: string | undefined; let order: number | undefined; if (welcome.group) { - if (!isProposedApiEnabled(contribution.description, undefined)) { + if (!isProposedApiEnabled(contribution.description, 'contribViewsWelcome')) { contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled.", contribution.description.identifier.value)); return { group, order }; } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index f60c1461dee..974d2a3bdab 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -7,6 +7,7 @@ export const allApiProposals = Object.freeze({ authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', + contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', diff --git a/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts b/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts new file mode 100644 index 00000000000..035b8f1f8b5 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty place holder declaration for the `viewsWelcome`-contribution point From 3bd29c1d6c6ef5b6265614def99b7257b671ae19 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 12:46:37 +0100 Subject: [PATCH 029/330] temp restore all enabled proposed API for test resolver, depends on some contrib-extensions, https://github.com/microsoft/vscode/issues/131165 --- extensions/vscode-test-resolver/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 76946671bda..28cd1f1faef 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -4,6 +4,7 @@ "version": "0.0.1", "publisher": "vscode", "license": "MIT", + "enableProposedApi": true, "enabledApiProposals": [ "resolvers" ], From aee006ecb8431ac6322e9ad90e9c144296f575c8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 12 Nov 2021 13:09:13 +0100 Subject: [PATCH 030/330] lifecycle - emit `onWillShutdown` even when using `app.exit` --- .../directMainProcessExtensionHostStarter.ts | 12 +------ .../workerMainProcessExtensionHostStarter.ts | 15 +------- .../electron-main/lifecycleMainService.ts | 36 +++++++------------ .../electron-main/storageMainService.test.ts | 1 - 4 files changed, 15 insertions(+), 49 deletions(-) diff --git a/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts index 8c9bfd32a48..c503cf3ed02 100644 --- a/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts @@ -15,17 +15,7 @@ export class DirectMainProcessExtensionHostStarter extends ExtensionHostStarter ) { super(logService); - // TODO: in the remote integration tests, this leads to the extension - // host processes getting killed brutally, which leads to the - // test resolver not having a chance to deactivate and kill the server processes - // it launches - - // // Abnormal shutdown: terminate extension hosts asap - // lifecycleMainService.onWillKill(() => { - // this.killAllNow(); - // }); - - // Normal shutdown: gracefully await extension host shutdowns + // On shutdown: gracefully await extension host shutdowns lifecycleMainService.onWillShutdown((e) => { e.join(this.waitForAllExit(6000)); }); diff --git a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts index f98915bfdb6..c59eb65e452 100644 --- a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts @@ -80,20 +80,7 @@ export class WorkerMainProcessExtensionHostStarter implements IDisposable, IExte ); this._initialize(); - // TODO: in the remote integration tests, this leads to the extension - // host processes getting killed brutally, which leads to the - // test resolver not having a chance to deactivate and kill the server processes - // it launches - - // // Abnormal shutdown: terminate extension hosts asap - // lifecycleMainService.onWillKill(async () => { - // this._shutdown = true; - // if (this._proxy) { - // this._proxy.killAllNow(); - // } - // }); - - // Normal shutdown: gracefully await extension host shutdowns + // On shutdown: gracefully await extension host shutdowns lifecycleMainService.onWillShutdown((e) => { this._shutdown = true; if (this._proxy) { diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index bfe95f931ef..81a02b51d86 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -20,13 +20,13 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platf export const ILifecycleMainService = createDecorator('lifecycleMainService'); -export interface IWindowLoadEvent { +export interface WindowLoadEvent { window: ICodeWindow; workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined; reason: LoadReason; } -export interface IWindowUnloadEvent { +export interface WindowUnloadEvent { window: ICodeWindow; reason: UnloadReason; veto(value: boolean | Promise): void; @@ -77,13 +77,13 @@ export interface ILifecycleMainService { * An event that fires when a window is loading. This can either be a window opening for the * first time or a window reloading or changing to another URL. */ - readonly onWillLoadWindow: Event; + readonly onWillLoadWindow: Event; /** * An event that fires before a window is about to unload. Listeners can veto this event to prevent * the window from unloading. */ - readonly onBeforeUnloadWindow: Event; + readonly onBeforeUnloadWindow: Event; /** * An event that fires before a window closes. This event is fired after any veto has been dealt @@ -91,13 +91,6 @@ export interface ILifecycleMainService { */ readonly onBeforeCloseWindow: Event; - /** - * An event that fires in the rare cases where `app.exit` is triggered - * and thus we Forcefully shutdown the application. No other lifecycle - * event handlers are triggered. - */ - readonly onWillKill: Event; - /** * Make a `ICodeWindow` known to the lifecycle main service. */ @@ -125,7 +118,7 @@ export interface ILifecycleMainService { /** * Forcefully shutdown the application. The only lifecycle event handler - * that is triggered is `onWillKill`. + * that is triggered is `onWillShutdown`. */ kill(code?: number): Promise; @@ -168,18 +161,15 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private readonly _onWillShutdown = this._register(new Emitter()); readonly onWillShutdown = this._onWillShutdown.event; - private readonly _onWillLoadWindow = this._register(new Emitter()); + private readonly _onWillLoadWindow = this._register(new Emitter()); readonly onWillLoadWindow = this._onWillLoadWindow.event; private readonly _onBeforeCloseWindow = this._register(new Emitter()); readonly onBeforeCloseWindow = this._onBeforeCloseWindow.event; - private readonly _onBeforeUnloadWindow = this._register(new Emitter()); + private readonly _onBeforeUnloadWindow = this._register(new Emitter()); readonly onBeforeUnloadWindow = this._onBeforeUnloadWindow.event; - private readonly _onWillKill = this._register(new Emitter()); - readonly onWillKill = this._onWillKill.event; - private _quitRequested = false; get quitRequested(): boolean { return this._quitRequested; } @@ -239,7 +229,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // the onWillShutdown() event directly because there is no veto // to be expected. if (isMacintosh && this.windowCounter === 0) { - this.beginOnWillShutdown(); + this.fireOnWillShutdown(); } }; app.addListener('before-quit', beforeQuitListener); @@ -267,7 +257,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe e.preventDefault(); // Start shutdown sequence - const shutdownPromise = this.beginOnWillShutdown(); + const shutdownPromise = this.fireOnWillShutdown(); // Wait until shutdown is signaled to be complete shutdownPromise.finally(() => { @@ -285,7 +275,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe }); } - private beginOnWillShutdown(): Promise { + private fireOnWillShutdown(): Promise { if (this.pendingWillShutdownPromise) { return this.pendingWillShutdownPromise; // shutdown is already running } @@ -411,7 +401,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // we are on macOS where it is perfectly fine to close the last window and // the application continues running (unless quit was actually requested) if (this.windowCounter === 0 && (!isMacintosh || this._quitRequested)) { - this.beginOnWillShutdown(); + this.fireOnWillShutdown(); } }); } @@ -600,8 +590,8 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe async kill(code?: number): Promise { this.logService.trace('Lifecycle#kill()'); - // Event - this._onWillKill.fire(); + // Start shutdown sequence + await this.fireOnWillShutdown(); // The kill() method is only used in 2 situations: // - when an instance fails to start at all diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 110a60eb77e..459ce917e13 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -57,7 +57,6 @@ suite('StorageMainService', function () { onWillLoadWindow = Event.None; onBeforeCloseWindow = Event.None; onBeforeUnloadWindow = Event.None; - onWillKill = Event.None; wasRestarted = false; quitRequested = false; From 23f79187b90e03edb930aad4c456b67b98598518 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 12 Nov 2021 14:34:36 +0100 Subject: [PATCH 031/330] Include workspace files tasks in recent tasks Fixes #109454 --- .../tasks/browser/abstractTaskService.ts | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index add862a216a..a6fa4499a8d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -771,9 +771,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this._recentlyUsedTasks; } - private getFolderFromTaskKey(key: string): string | undefined { - const keyValue: { folder: string | undefined } = JSON.parse(key); - return keyValue.folder; + private getFolderFromTaskKey(key: string): { folder: string | undefined, isWorkspaceFile: boolean | undefined } { + const keyValue: { folder: string | undefined, id: string | undefined } = JSON.parse(key); + return { + folder: keyValue.folder, isWorkspaceFile: keyValue.id?.endsWith(TaskSourceKind.WorkspaceFile) + }; } public async readRecentTasks(): Promise<(Task | ConfiguringTask)[]> { @@ -782,40 +784,55 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer folderMap[folder.uri.toString()] = folder; }); const folderToTasksMap: Map = new Map(); + const workspaceToTaskMap: Map = new Map(); const recentlyUsedTasks = this.getRecentlyUsedTasks(); const tasks: (Task | ConfiguringTask)[] = []; + + function addTaskToMap(map: Map, folder: string | undefined, task: any) { + if (folder && !map.has(folder)) { + map.set(folder, []); + } + if (folder && (folderMap[folder] || (folder === USER_TASKS_GROUP_KEY)) && task) { + map.get(folder).push(task); + } + } + for (const entry of recentlyUsedTasks.entries()) { const key = entry[0]; const task = JSON.parse(entry[1]); - const folder = this.getFolderFromTaskKey(key); - if (folder && !folderToTasksMap.has(folder)) { - folderToTasksMap.set(folder, []); - } - if (folder && (folderMap[folder] || (folder === USER_TASKS_GROUP_KEY)) && task) { - folderToTasksMap.get(folder).push(task); - } + const folderInfo = this.getFolderFromTaskKey(key); + addTaskToMap(folderInfo.isWorkspaceFile ? workspaceToTaskMap : folderToTasksMap, folderInfo.folder, task); } + const readTasksMap: Map = new Map(); - for (const key of folderToTasksMap.keys()) { - let custom: CustomTask[] = []; - let customized: IStringDictionary = Object.create(null); - await this.computeTasksForSingleConfig(folderMap[key] ?? await this.getAFolder(), { - version: '2.0.0', - tasks: folderToTasksMap.get(key) - }, TaskRunSource.System, custom, customized, folderMap[key] ? TaskConfig.TaskConfigSource.TasksJson : TaskConfig.TaskConfigSource.User, true); - custom.forEach(task => { - const taskKey = task.getRecentlyUsedKey(); - if (taskKey) { - readTasksMap.set(taskKey, task); - } - }); - for (const configuration in customized) { - const taskKey = customized[configuration].getRecentlyUsedKey(); - if (taskKey) { - readTasksMap.set(taskKey, customized[configuration]); + async function readTasks(that: AbstractTaskService, map: Map, isWorkspaceFile: boolean) { + for (const key of map.keys()) { + let custom: CustomTask[] = []; + let customized: IStringDictionary = Object.create(null); + const taskConfigSource = (folderMap[key] + ? (isWorkspaceFile + ? TaskConfig.TaskConfigSource.WorkspaceFile : TaskConfig.TaskConfigSource.TasksJson) + : TaskConfig.TaskConfigSource.User); + await that.computeTasksForSingleConfig(folderMap[key] ?? await that.getAFolder(), { + version: '2.0.0', + tasks: map.get(key) + }, TaskRunSource.System, custom, customized, taskConfigSource, true); + custom.forEach(task => { + const taskKey = task.getRecentlyUsedKey(); + if (taskKey) { + readTasksMap.set(taskKey, task); + } + }); + for (const configuration in customized) { + const taskKey = customized[configuration].getRecentlyUsedKey(); + if (taskKey) { + readTasksMap.set(taskKey, customized[configuration]); + } } } } + await readTasks(this, folderToTasksMap, false); + await readTasks(this, workspaceToTaskMap, true); for (const key of recentlyUsedTasks.keys()) { if (readTasksMap.has(key)) { From e79a9c811aeb40e37cc469bbfcd2aa373ff12eba Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 14:46:33 +0100 Subject: [PATCH 032/330] `checkProposedApiEnabled` and `isProposedApiEnabled` must be called with proposal name, add proposals for `package.json`-based API, https://github.com/microsoft/vscode/issues/131165 --- .../api/browser/viewsExtensionPoint.ts | 2 +- .../workbench/api/common/extHost.api.impl.ts | 4 +-- .../workbench/api/common/extHostQuickOpen.ts | 20 +++++------ .../api/common/menusExtensionPoint.ts | 35 ++++++------------- .../contrib/remote/browser/remote.ts | 2 +- .../services/extensions/common/extensions.ts | 7 ++-- .../common/extensionsApiProposals.ts | 6 ++++ .../services/extensions/node/proxyResolver.ts | 4 --- .../services/label/common/labelService.ts | 2 +- .../themes/common/iconExtensionPoint.ts | 4 +-- .../vscode.proposed.contribIconFonts.d.ts | 6 ++++ .../vscode.proposed.contribIcons.d.ts | 6 ++++ ...contribLabelFormatterWorkspaceTooltip.d.ts | 6 ++++ .../vscode.proposed.contribMenuBarHome.d.ts | 6 ++++ .../vscode.proposed.contribRemoteHelp.d.ts | 6 ++++ .../vscode.proposed.contribViewsRemote.d.ts | 6 ++++ .../vscode.proposed.contribViewsWelcome.d.ts | 2 +- 17 files changed, 72 insertions(+), 52 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribIconFonts.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribIcons.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 4ad9fee8a91..fda5c018445 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -405,7 +405,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return; } - if (entry.key === 'remote' && !isProposedApiEnabled(extension.description, undefined)) { + if (entry.key === 'remote' && !isProposedApiEnabled(extension.description, 'contribViewsRemote')) { collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key)); return; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6f343668646..00e0d4f9e3a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -600,7 +600,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return >extHostMessageService.showMessage(extension, Severity.Error, message, rest[0], >rest.slice(1)); }, showQuickPick(items: any, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { - return extHostQuickOpen.showQuickPick(items, isProposedApiEnabled(extension, undefined), options, token); + return extHostQuickOpen.showQuickPick(items, options, token); }, showWorkspaceFolderPick(options?: vscode.WorkspaceFolderPickOptions) { return extHostQuickOpen.showWorkspaceFolderPick(options); @@ -689,7 +689,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostUrls.registerUriHandler(extension.identifier, handler); }, createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.identifier, isProposedApiEnabled(extension, undefined)); + return extHostQuickOpen.createQuickPick(extension.identifier); }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index f5ec6a46edc..eb1c05c36a6 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -22,16 +22,16 @@ import { ThemeIcon as ThemeIconUtils } from 'vs/platform/theme/common/themeServi export type Item = string | QuickPickItem; export interface ExtHostQuickOpen { - showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, enableProposedApi: boolean, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: string[] | Promise, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: Item[] | Promise, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: QuickPickOptions, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, options?: QuickPickOptions, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: QuickPickOptions, token?: CancellationToken): Promise; showInput(options?: InputBoxOptions, token?: CancellationToken): Promise; showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token?: CancellationToken): Promise - createQuickPick(extensionId: ExtensionIdentifier, enableProposedApi: boolean): QuickPick; + createQuickPick(extensionId: ExtensionIdentifier): QuickPick; createInputBox(extensionId: ExtensionIdentifier): InputBox; } @@ -56,10 +56,10 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx this._commands = commands; } - showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, enableProposedApi: boolean, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: string[] | Promise, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: Item[] | Promise, enableProposedApi: boolean, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Promise { + showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: QuickPickOptions, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise, options?: QuickPickOptions, token?: CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Promise { // clear state from last invocation this._onDidSelectItem = undefined; @@ -192,7 +192,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx // ---- QuickInput - createQuickPick(extensionId: ExtensionIdentifier, enableProposedApi: boolean): QuickPick { + createQuickPick(extensionId: ExtensionIdentifier): QuickPick { const session: ExtHostQuickPick = new ExtHostQuickPick(extensionId, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 5ee2881e6f8..36a97b077cb 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -17,14 +17,14 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { Iterable } from 'vs/base/common/iterator'; import { index } from 'vs/base/common/arrays'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { ApiProposalName } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; interface IAPIMenu { readonly key: string; readonly id: MenuId; readonly description: string; - readonly proposed?: boolean; // defaults to false + readonly proposed?: ApiProposalName; readonly supportsSubmenus?: boolean; // defaults to true - readonly deprecationMessage?: string; } const apiMenus: IAPIMenu[] = [ @@ -85,17 +85,11 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.DebugToolBar, description: localize('menus.debugToolBar', "The debug toolbar menu") }, - { - key: 'menuBar/file', - id: MenuId.MenubarFileMenu, - description: localize('menus.file', "The top level file menu"), - proposed: true - }, { key: 'menuBar/home', id: MenuId.MenubarHomeMenu, description: localize('menus.home', "The home indicator context menu (web only)"), - proposed: true, + proposed: 'contribMenuBarHome', supportsSubmenus: false }, { @@ -133,14 +127,6 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.SCMChangeContext, description: localize('menus.changeTitle', "The Source Control inline change menu") }, - { - key: 'statusBar/windowIndicator', - id: MenuId.StatusBarWindowIndicatorMenu, - description: localize('menus.statusBarWindowIndicator', "The window indicator menu in the status bar"), - proposed: true, - supportsSubmenus: false, - deprecationMessage: localize('menus.statusBarWindowIndicator.deprecated', "Use menu 'statusBar/remoteIndicator' instead."), - }, { key: 'statusBar/remoteIndicator', id: MenuId.StatusBarRemoteIndicatorMenu, @@ -198,19 +184,19 @@ const apiMenus: IAPIMenu[] = [ key: 'notebook/cell/executePrimary', id: MenuId.NotebookCellExecutePrimary, description: localize('notebook.cell.executePrimary', "The contributed primary notebook cell execution button"), - proposed: true + proposed: 'notebookEditor' }, { key: 'interactive/toolbar', id: MenuId.InteractiveToolbar, description: localize('interactive.toolbar', "The contributed interactive toolbar menu"), - proposed: true + proposed: 'notebookEditor' }, { key: 'interactive/cell/title', id: MenuId.InteractiveCellTitle, description: localize('interactive.cell.title', "The contributed interactive cell title menu"), - proposed: true + proposed: 'notebookEditor' }, { key: 'testing/item/context', @@ -263,7 +249,7 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.InlineCompletionsActions, description: localize('inlineCompletions.actions', "The actions shown when hovering on an inline completion"), supportsSubmenus: false, - proposed: true + proposed: 'inlineCompletions' }, ]; @@ -451,8 +437,7 @@ namespace schema { description: localize('vscode.extension.contributes.menus', "Contributes menu items to the editor"), type: 'object', properties: index(apiMenus, menu => menu.key, menu => ({ - description: menu.proposed ? `(${localize('proposed', "Proposed API")}) ${menu.description}` : menu.description, - deprecationMessage: menu.deprecationMessage, + markdownDescription: menu.proposed ? localize('proposed', "Proposed API, requires `enabledApiProposal: [\"{0}\"]` - {1}", menu.proposed, menu.description) : menu.description, type: 'array', items: menu.supportsSubmenus === false ? menuItem : { oneOf: [menuItem, submenuItem] } })), @@ -634,7 +619,7 @@ commandsExtensionPoint.setHandler(extensions => { title, source: extension.description.displayName ?? extension.description.name, shortTitle, - tooltip: isProposedApiEnabled(extension.description, undefined) ? title : undefined, + tooltip: title, category, precondition: ContextKeyExpr.deserialize(enablement), icon: absoluteIcon @@ -764,7 +749,7 @@ menusExtensionPoint.setHandler(extensions => { return; } - if (menu.proposed && !isProposedApiEnabled(extension.description, undefined)) { + if (menu.proposed && !isProposedApiEnabled(extension.description, menu.proposed)) { collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value)); return; } diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index dff2881e612..488f5665aa9 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -498,7 +498,7 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements } private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { - if (!isProposedApiEnabled(extension.description, undefined)) { + if (!isProposedApiEnabled(extension.description, 'contribRemoteHelp')) { return; } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 02ab47e2c30..3ef6cdd288e 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -134,17 +134,14 @@ export interface IExtensionHost { dispose(): void; } -export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName | undefined): boolean { - if (!proposal) { - return Boolean(extension.enableProposedApi); - } +export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean { if (extension.enabledApiProposals?.includes(proposal)) { return true; } return Boolean(extension.enableProposedApi); } -export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName | undefined): void { +export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void { if (!isProposedApiEnabled(extension, proposal)) { throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`); } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 974d2a3bdab..1ccca4a1ffc 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -7,6 +7,12 @@ export const allApiProposals = Object.freeze({ authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', + contribIconFonts: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribIconFonts.d.ts', + contribIcons: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribIcons.d.ts', + contribLabelFormatterWorkspaceTooltip: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts', + contribMenuBarHome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts', + contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts', + contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index ac2ea9c022c..8b6e10d7ae1 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -15,7 +15,6 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { LogLevel, createHttpPatch, ProxyResolveEvent, createProxyResolver, createTlsPatch, ProxySupportSetting } from 'vscode-proxy-agent'; -import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export function connectProxyResolver( extHostWorkspace: IExtHostWorkspaceProvider, @@ -130,9 +129,6 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku } if (!cache[request]) { let mod = modules.default; - if (ext && isProposedApiEnabled(ext, undefined)) { - mod = (modules as any)[(ext).proxySupport] || modules.onRequest; - } cache[request] = { ...mod }; // Copy to work around #93167. } return cache[request]; diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 509be770cf7..05b4c9776c2 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -84,7 +84,7 @@ class ResourceLabelFormattersHandler implements IWorkbenchContribution { constructor(@ILabelService labelService: ILabelService) { resourceLabelFormattersExtPoint.setHandler((extensions, delta) => { delta.added.forEach(added => added.value.forEach(formatter => { - if (!isProposedApiEnabled(added.description, undefined) && formatter.formatting.workspaceTooltip) { + if (!isProposedApiEnabled(added.description, 'contribLabelFormatterWorkspaceTooltip') && formatter.formatting.workspaceTooltip) { // workspaceTooltip is only proposed formatter.formatting.workspaceTooltip = undefined; } diff --git a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts index addcda618e8..67bd0f3f868 100644 --- a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts @@ -126,7 +126,7 @@ export class IconExtensionPoint { const extensionValue = extension.value; const collector = extension.collector; - if (!isProposedApiEnabled(extension.description, undefined)) { + if (!isProposedApiEnabled(extension.description, 'contribIcons')) { collector.error(nls.localize('invalid.icons.proposedAPI', "'configuration.icons is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } @@ -180,7 +180,7 @@ export class IconFontExtensionPoint { const extensionValue = extension.value; const collector = extension.collector; - if (!isProposedApiEnabled(extension.description, undefined)) { + if (!isProposedApiEnabled(extension.description, 'contribIconFonts')) { collector.error(nls.localize('invalid.iconFonts.proposedAPI', "'configuration.iconFonts is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } diff --git a/src/vscode-dts/vscode.proposed.contribIconFonts.d.ts b/src/vscode-dts/vscode.proposed.contribIconFonts.d.ts new file mode 100644 index 00000000000..dbfa93e4200 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribIconFonts.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `iconFonts`-contribution point diff --git a/src/vscode-dts/vscode.proposed.contribIcons.d.ts b/src/vscode-dts/vscode.proposed.contribIcons.d.ts new file mode 100644 index 00000000000..04f4fdc41e0 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribIcons.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `icons`-contribution point diff --git a/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts b/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts new file mode 100644 index 00000000000..90814eb2211 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `workspaceTooltip`-property of the `resourceLabelFormatters` contribution poain diff --git a/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts b/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts new file mode 100644 index 00000000000..caa0ff228da --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `menuBar/home` menu diff --git a/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts b/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts new file mode 100644 index 00000000000..f577e0daa2e --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `remoteHelp`-contribution point diff --git a/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts b/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts new file mode 100644 index 00000000000..b1e6ca3d77b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `remote`-property of the `views`-contribution diff --git a/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts b/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts index 035b8f1f8b5..616694ebb7d 100644 --- a/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts +++ b/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts @@ -3,4 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// empty place holder declaration for the `viewsWelcome`-contribution point +// empty placeholder declaration for the `viewsWelcome`-contribution point From b78b7273475290511dc68184cd5393da51337b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 12 Nov 2021 14:49:30 +0100 Subject: [PATCH 033/330] docs: :memo: better docs for views welcome content, group property Related-to: https://github.com/microsoft/vscode/commit/d775c7346cc5260e7fa0c3155f61c1cb2b7d02bc#r60165388 --- .../contrib/welcome/common/viewsWelcomeContribution.ts | 2 +- .../contrib/welcome/common/viewsWelcomeExtensionPoint.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index 41f202b54d4..adc14db0b19 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -74,7 +74,7 @@ function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointU let order: number | undefined; if (welcome.group) { if (!isProposedApiEnabled(contribution.description, 'contribViewsWelcome')) { - contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled.", contribution.description.identifier.value)); + contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled, in order to use the 'group' proposed property.", contribution.description.identifier.value)); return { group, order }; } diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts index c6629c19a87..39a48446c2c 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts @@ -65,7 +65,7 @@ const viewsWelcomeExtensionPointSchema = Object.freeze Date: Fri, 12 Nov 2021 14:54:05 +0100 Subject: [PATCH 034/330] Add link to task when contexts documentation Fixes #104000 --- src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts index a3b89c01dc8..8d612f6d9a6 100644 --- a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts @@ -40,7 +40,7 @@ const taskDefinitionSchema: IJSONSchema = { }, when: { type: 'string', - markdownDescription: nls.localize('TaskDefinition.when', 'Condition which must be true to enable this type of task. Consider using `shellExecutionSupported`, `processExecutionSupported`, and `customExecutionSupported` as appropriate for this task definition.'), + markdownDescription: nls.localize('TaskDefinition.when', 'Condition which must be true to enable this type of task. Consider using `shellExecutionSupported`, `processExecutionSupported`, and `customExecutionSupported` as appropriate for this task definition. See the [API documentation](https://code.visualstudio.com/api/extension-guides/task-provider#when-clause) for more information.'), default: '' } } From 112709a721bdad02bf4a658f686be3bb5af4e8c6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 15:03:11 +0100 Subject: [PATCH 035/330] tweak error messages when API proposal isn't available, https://github.com/microsoft/vscode/issues/131165 --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 2 +- src/vs/workbench/api/common/menusExtensionPoint.ts | 2 +- .../contrib/welcome/common/viewsWelcomeContribution.ts | 2 +- src/vs/workbench/services/extensions/common/extensions.ts | 2 +- src/vs/workbench/services/themes/common/iconExtensionPoint.ts | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index fda5c018445..fbeeb50326f 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -406,7 +406,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } if (entry.key === 'remote' && !isProposedApiEnabled(extension.description, 'contribViewsRemote')) { - collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key)); + collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enabledApiProposals: [\"contribViewsRemote\"]' to be added to 'Remote'.", entry.key)); return; } diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 36a97b077cb..6f7fb8389ca 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -750,7 +750,7 @@ menusExtensionPoint.setHandler(extensions => { } if (menu.proposed && !isProposedApiEnabled(extension.description, menu.proposed)) { - collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value)); + collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier. It requires 'package.json#enabledApiProposals: [\"{1}\"]' and is only available when running out of dev or with the following command line switch: --enable-proposed-api {2}", entry.key, menu.proposed, extension.description.identifier.value)); return; } diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index adc14db0b19..91f6b79e2e5 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -74,7 +74,7 @@ function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointU let order: number | undefined; if (welcome.group) { if (!isProposedApiEnabled(contribution.description, 'contribViewsWelcome')) { - contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled, in order to use the 'group' proposed property.", contribution.description.identifier.value)); + contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enabledApiProposals: [\"contribViewsWelcome\"]' in order to use the 'group' proposed property.", contribution.description.identifier.value)); return { group, order }; } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 3ef6cdd288e..5eccd39dc01 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -143,7 +143,7 @@ export function isProposedApiEnabled(extension: IExtensionDescription, proposal: export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void { if (!isProposedApiEnabled(extension, proposal)) { - throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`); + throw new Error(`Extension '${extension.identifier.value}' CANNOT use API proposal: ${proposal}.\nAccording to its package.json#enabledApiProposals-property it wants: ${extension.enabledApiProposals?.join(', ') ?? ''}.\n You MUST start in extension development mode or use the following command line switch: --enable-proposed-api ${extension.identifier.value}`); } } diff --git a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts index 67bd0f3f868..b7d60683e0b 100644 --- a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts @@ -127,7 +127,7 @@ export class IconExtensionPoint { const collector = extension.collector; if (!isProposedApiEnabled(extension.description, 'contribIcons')) { - collector.error(nls.localize('invalid.icons.proposedAPI', "'configuration.icons is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); + collector.error(nls.localize('invalid.icons.proposedAPI', "'configuration.icons is a proposed contribution point. It requires 'package.json#enabledApiProposals: [\"contribIcons\"]' and is only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } @@ -181,7 +181,7 @@ export class IconFontExtensionPoint { const collector = extension.collector; if (!isProposedApiEnabled(extension.description, 'contribIconFonts')) { - collector.error(nls.localize('invalid.iconFonts.proposedAPI', "'configuration.iconFonts is a proposed contribution point and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); + collector.error(nls.localize('invalid.iconFonts.proposedAPI', "'configuration.iconFonts is a proposed contribution point. It requires 'package.json#enabledApiProposals: [\"contribIconFonts\"]' and is and only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value)); return; } From daa8095ccf6a81c6808f0a8306e4e3874bee87c0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 06:05:52 -0800 Subject: [PATCH 036/330] Fix xterm layering problem --- src/vs/platform/driver/browser/baseDriver.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index 2b84d12e975..832ee351654 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -8,7 +8,6 @@ import { coalesce } from 'vs/base/common/arrays'; import { language, locale } from 'vs/base/common/platform'; import { IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver } from 'vs/platform/driver/common/driver'; import localizedStrings from 'vs/platform/localizations/common/localizedStrings'; -import type { Terminal } from 'xterm'; // eslint-disable-line code-import-patterns export abstract class BaseWindowDriver implements IWindowDriver { @@ -133,7 +132,7 @@ export abstract class BaseWindowDriver implements IWindowDriver { throw new Error(`Terminal not found: ${selector}`); } - const xterm = (element as any).xterm as Terminal; + const xterm = (element as any).xterm; if (!xterm) { throw new Error(`Xterm not found: ${selector}`); @@ -154,13 +153,13 @@ export abstract class BaseWindowDriver implements IWindowDriver { throw new Error(`Element not found: ${selector}`); } - const xterm = (element as any).xterm as Terminal; + const xterm = (element as any).xterm; if (!xterm) { throw new Error(`Xterm not found: ${selector}`); } - (xterm as any)._core._coreService.triggerDataEvent(text); + xterm._core._coreService.triggerDataEvent(text); } getLocaleInfo(): Promise { From c65c8738f96abd0262e561b67dbe5d58dc4d5047 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 06:11:18 -0800 Subject: [PATCH 037/330] Copy and use IWindowDriver in PlaywrightDriver --- .eslintrc.json | 1 - src/vs/platform/driver/common/driver.ts | 8 ++++---- test/automation/src/playwrightDriver.ts | 19 +------------------ 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 08f5bfd644b..70012db4e74 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -952,7 +952,6 @@ "@vscode/*", "@parcel/*", "playwright-core/**", - "**/vs/platform/driver/common/**", "*" // node modules ] }, diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts index e447cee2532..41b7809f448 100644 --- a/src/vs/platform/driver/common/driver.ts +++ b/src/vs/platform/driver/common/driver.ts @@ -57,10 +57,6 @@ export interface IDriver { getLocaleInfo(windowId: number): Promise; getLocalizedStrings(windowId: number): Promise; } -//*END - -export const ID = 'driverService'; -export const IDriver = createDecorator(ID); export interface IWindowDriver { click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; @@ -76,6 +72,10 @@ export interface IWindowDriver { getLocaleInfo(): Promise; getLocalizedStrings(): Promise } +//*END + +export const ID = 'driverService'; +export const IDriver = createDecorator(ID); export interface IDriverOptions { verbose: boolean; diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index e406b740f85..4ea07cb4cc4 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -8,27 +8,10 @@ import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; import { mkdir } from 'fs'; import { promisify } from 'util'; -import { IDriver, IDisposable } from './driver'; +import { IDriver, IDisposable, IWindowDriver } from './driver'; import { URI } from 'vscode-uri'; import * as kill from 'tree-kill'; import { PageFunction } from 'playwright-core/types/structs'; -import { IElement, ILocaleInfo, ILocalizedStrings } from '.'; - -// TODO: Copy driver over to ./driver.d.ts? -export interface IWindowDriver { - click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; - doubleClick(selector: string): Promise; - setValue(selector: string, text: string): Promise; - getTitle(): Promise; - isActiveElement(selector: string): Promise; - getElements(selector: string, recursive: boolean): Promise; - getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>; - typeInEditor(selector: string, text: string): Promise; - getTerminalBuffer(selector: string): Promise; - writeInTerminal(selector: string, text: string): Promise; - getLocaleInfo(): Promise; - getLocalizedStrings(): Promise -} const width = 1200; const height = 800; From 3c9b18e13535161673b04ac5ff32e19fa3270313 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 06:16:17 -0800 Subject: [PATCH 038/330] Remove test only --- test/smoke/src/areas/terminal/terminal-profiles.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts index 6d5a9757cfe..c4fe5e9d789 100644 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ b/test/smoke/src/areas/terminal/terminal-profiles.test.ts @@ -9,7 +9,7 @@ import { Application } from '../../../../automation'; import { afterSuite, beforeSuite } from '../../utils'; export function setup(opts: ParsedArgs) { - describe.only('Terminal Profiles', () => { + describe('Terminal Profiles', () => { let app: Application; beforeSuite(opts); From fff34ad86b7f1b651323d6f709ebaa7456058d80 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 12 Nov 2021 15:27:40 +0100 Subject: [PATCH 039/330] Fix test input --- .../test/colorize-fixtures/git-rebase-todo | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo b/extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo index e69de29bb2d..3efe501eb98 100644 --- a/extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/git-rebase-todo @@ -0,0 +1,15 @@ +pick 1fc6c95 Patch A +squash fa39187 Something to add to patch A +pick 7b36971 Something to move before patch B +pick 6b2481b Patch B +fixup c619268 A fix for Patch B +edit dd1475d Something I want to split +reword 4ca2acc i cant' typ goods + +# Commands: +# p, pick = use commit +# r, reword = use commit, but edit the commit message +# e, edit = use commit, but stop for amending +# s, squash = use commit, but meld into previous commit +# f, fixup = like "squash", but discard this commit's log message +# x, exec = run command (the rest of the line) using shell From 2dcc58217a1da34301ed3dee473eb26210db5ec0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 15:29:58 +0100 Subject: [PATCH 040/330] reduce `enableProposedApi` usage further, https://github.com/microsoft/vscode/issues/131165 --- .../workbench/test/browser/api/extHostAuthentication.test.ts | 4 +--- src/vs/workbench/test/browser/api/extHostTreeViews.test.ts | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/test/browser/api/extHostAuthentication.test.ts b/src/vs/workbench/test/browser/api/extHostAuthentication.test.ts index 87b04169218..aa5e63baa4d 100644 --- a/src/vs/workbench/test/browser/api/extHostAuthentication.test.ts +++ b/src/vs/workbench/test/browser/api/extHostAuthentication.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; @@ -20,7 +19,7 @@ import { ExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.pro import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; import { IActivityService } from 'vs/workbench/services/activity/common/activity'; import { AuthenticationService, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; -import { IExtensionService, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, nullExtensionDescription as extensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TestRemoteAgentService } from 'vs/workbench/services/remote/test/common/testServices'; import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; @@ -89,7 +88,6 @@ class TestAuthProvider implements AuthenticationProvider { suite('ExtHostAuthentication', () => { let disposables: DisposableStore; - let extensionDescription: IExtensionDescription = { ...nullExtensionDescription, enableProposedApi: true }; let extHostAuthentication: ExtHostAuthentication; let instantiationService: TestInstantiationService; diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 46fc654a936..db66293a7e9 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -17,9 +17,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { mock } from 'vs/base/test/common/mock'; import { TreeItemCollapsibleState, ITreeItem, IRevealOptions } from 'vs/workbench/common/views'; import { NullLogService } from 'vs/platform/log/common/log'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import type { IDisposable } from 'vs/base/common/lifecycle'; -import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { nullExtensionDescription as extensionsDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('ExtHostTreeView', function () { @@ -42,8 +41,6 @@ suite('ExtHostTreeView', function () { } - const extensionsDescription: IExtensionDescription = { ...nullExtensionDescription, enableProposedApi: true }; - let testObject: ExtHostTreeViews; let target: RecordingShape; let onDidChangeTreeNode: Emitter<{ key: string } | undefined>; From 5f4830bc79c37890fea600f9cff309de8a28d0dc Mon Sep 17 00:00:00 2001 From: John Murray Date: Fri, 12 Nov 2021 14:39:17 +0000 Subject: [PATCH 041/330] Add progress indicator to SCM in activity bar while QuickDiff is preparing (#136968) (#136969) --- .../contrib/scm/browser/dirtydiffDecorator.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 8f2cd3f4a7a..1d04a740c71 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -51,6 +51,7 @@ import { gotoNextLocation, gotoPreviousLocation } from 'vs/platform/theme/common import { Codicon } from 'vs/base/common/codicons'; import { onUnexpectedError } from 'vs/base/common/errors'; import { TextCompareEditorActiveContext } from 'vs/workbench/common/editor'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; class DiffActionRunner extends ActionRunner { @@ -1088,7 +1089,8 @@ export class DirtyDiffModel extends Disposable { @ISCMService private readonly scmService: ISCMService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, @IConfigurationService private readonly configurationService: IConfigurationService, - @ITextModelService private readonly textModelResolverService: ITextModelService + @ITextModelService private readonly textModelResolverService: ITextModelService, + @IProgressService private readonly progressService: IProgressService, ) { super(); this._model = textFileModel; @@ -1160,21 +1162,23 @@ export class DirtyDiffModel extends Disposable { } private diff(): Promise { - return this.getOriginalURIPromise().then(originalURI => { - if (this._disposed || this._model.isDisposed() || !originalURI) { - return Promise.resolve([]); // disposed - } + return this.progressService.withProgress({ location: ProgressLocation.Scm }, async () => { + return this.getOriginalURIPromise().then(originalURI => { + if (this._disposed || this._model.isDisposed() || !originalURI) { + return Promise.resolve([]); // disposed + } - if (!this.editorWorkerService.canComputeDirtyDiff(originalURI, this._model.resource)) { - return Promise.resolve([]); // Files too large - } + if (!this.editorWorkerService.canComputeDirtyDiff(originalURI, this._model.resource)) { + return Promise.resolve([]); // Files too large + } - const ignoreTrimWhitespaceSetting = this.configurationService.getValue<'true' | 'false' | 'inherit'>('scm.diffDecorationsIgnoreTrimWhitespace'); - const ignoreTrimWhitespace = ignoreTrimWhitespaceSetting === 'inherit' - ? this.configurationService.getValue('diffEditor.ignoreTrimWhitespace') - : ignoreTrimWhitespaceSetting !== 'false'; + const ignoreTrimWhitespaceSetting = this.configurationService.getValue<'true' | 'false' | 'inherit'>('scm.diffDecorationsIgnoreTrimWhitespace'); + const ignoreTrimWhitespace = ignoreTrimWhitespaceSetting === 'inherit' + ? this.configurationService.getValue('diffEditor.ignoreTrimWhitespace') + : ignoreTrimWhitespaceSetting !== 'false'; - return this.editorWorkerService.computeDirtyDiff(originalURI, this._model.resource, ignoreTrimWhitespace); + return this.editorWorkerService.computeDirtyDiff(originalURI, this._model.resource, ignoreTrimWhitespace); + }); }); } From b1dc0b0ebf7d4f4c20464601878fb8e2c2549b32 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 12 Nov 2021 16:02:25 +0100 Subject: [PATCH 042/330] Fix task arguments with Windows remote Fixes #93437 --- src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 3f12b29d53c..3fd75cfb6db 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1084,7 +1084,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let windowsShellArgs: boolean = false; if (platform === Platform.Platform.Windows) { windowsShellArgs = true; - let basename = path.basename(shellLaunchConfig.executable!).toLowerCase(); + let basename = path.posix.basename(URI.file(shellLaunchConfig.executable!).path).toLowerCase(); // If we don't have a cwd, then the terminal uses the home dir. const userHome = await this.pathService.userHome(); if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { From 2be31eebb1a55cd8c24be48e97488931cd62eeb0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 12 Nov 2021 16:17:14 +0100 Subject: [PATCH 043/330] Improve task numbering for task quick picks Fixes #91440 --- .../tasks/browser/abstractTaskService.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index a6fa4499a8d..d0fb9c98ba9 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2277,19 +2277,22 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private async createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, includeRecents: boolean = true): Promise { - let count: { [key: string]: number; } = {}; + let encounteredTasks: { [key: string]: TaskQuickPickEntry[] } = {}; if (tasks === undefined || tasks === null || tasks.length === 0) { return []; } const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => { - let entryLabel = task._label; - if (count[task._id]) { - entryLabel = entryLabel + ' (' + count[task._id].toString() + ')'; - count[task._id]++; + const newEntry = { label: task._label, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; + if (encounteredTasks[task._id]) { + if (encounteredTasks[task._id].length === 1) { + encounteredTasks[task._id][0].label += ' (1)'; + } + newEntry.label = newEntry.label + ' (' + (encounteredTasks[task._id].length + 1).toString() + ')'; } else { - count[task._id] = 1; + encounteredTasks[task._id] = []; } - return { label: entryLabel, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; + encounteredTasks[task._id].push(newEntry); + return newEntry; }; function fillEntries(entries: QuickPickInput[], tasks: Task[], groupLabel: string): void { @@ -2360,7 +2363,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } entries = tasks.map(task => TaskQuickPickEntry(task)); } - count = {}; + encounteredTasks = {}; return entries; } From cd8f11af583a30f80b732114c6a33367c78afc26 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 12 Nov 2021 16:22:08 +0100 Subject: [PATCH 044/330] Remove cgmanifest.json from git extension --- extensions/git/.vscodeignore | 1 - extensions/git/cgmanifest.json | 39 ---------------------------------- 2 files changed, 40 deletions(-) delete mode 100644 extensions/git/cgmanifest.json diff --git a/extensions/git/.vscodeignore b/extensions/git/.vscodeignore index 7462f7448d3..94d2dc921e0 100644 --- a/extensions/git/.vscodeignore +++ b/extensions/git/.vscodeignore @@ -4,5 +4,4 @@ out/** tsconfig.json build/** extension.webpack.config.js -cgmanifest.json yarn.lock diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json deleted file mode 100644 index 256966aba20..00000000000 --- a/extensions/git/cgmanifest.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "textmate/git.tmbundle", - "repositoryUrl": "https://github.com/textmate/git.tmbundle", - "commitHash": "5870cf3f8abad3a6637bdf69250b5d2ded427dc4" - } - }, - "licenseDetail": [ - "Copyright (c) 2008 Tim Harper", - "", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of this software and associated documentation files (the\"", - "Software\"), to deal in the Software without restriction, including", - "without limitation the rights to use, copy, modify, merge, publish,", - "distribute, sublicense, and/or sell copies of the Software, and to", - "permit persons to whom the Software is furnished to do so, subject to", - "the following conditions:", - "", - "The above copyright notice and this permission notice shall be", - "included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", - "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", - "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", - "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ], - "license": "MIT", - "version": "0.0.0" - } - ], - "version": 1 -} From 81b5ea2f377651a2b8150d335edd9a85120fdc5b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 12 Nov 2021 15:40:38 +0100 Subject: [PATCH 045/330] [json] update service (for #137054) --- extensions/json-language-features/server/package.json | 2 +- extensions/json-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index cfbbe5e5abb..5c8bb31f68b 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,7 +14,7 @@ "dependencies": { "jsonc-parser": "^3.0.0", "request-light": "^0.5.4", - "vscode-json-languageservice": "^4.1.9", + "vscode-json-languageservice": "^4.1.10", "vscode-languageserver": "^7.0.0", "vscode-uri": "^3.0.2" }, diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 06fd41ef116..25e3ca55845 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -22,10 +22,10 @@ request-light@^0.5.4: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.4.tgz#497a98c6d8ae49536417a5e2d7f383b934f3e38c" integrity sha512-t3566CMweOFlUk7Y1DJMu5OrtpoZEb6aSTsLQVT3wtrIEJ5NhcY9G/Oqxvjllzl4a15zXfFlcr9q40LbLVQJqw== -vscode-json-languageservice@^4.1.9: - version "4.1.9" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.1.9.tgz#fb48edc69e37167c3cafd447c3fa898052d87b61" - integrity sha512-kxNHitUy2fCxmP6vAp0SRLrUSuecUYzzxlC+85cC3jJlFHWmvtCJOzikC+kcUnIdls9fQSB8n0yHs8Sl6taxJw== +vscode-json-languageservice@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.1.10.tgz#5d5729fc4f3e02f41599e0104523a1877c25f0fb" + integrity sha512-IHliMEEYSY0tJjJt0ECb8ESx/nRXpoy9kN42WVQXgaqGyizFAf3jibSiezDQTrrY7f3kywXggCU+kkJEM+OLZQ== dependencies: jsonc-parser "^3.0.0" vscode-languageserver-textdocument "^1.0.1" From 4df57580d323ab2bb9dfaaafb347d24be018a49a Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 12 Nov 2021 16:20:35 +0100 Subject: [PATCH 046/330] remove unnecessary escapes for '-' in regexes that go into schemas (for #137054) --- .../schemas/attachContainer.schema.json | 6 ++-- .../devContainer.schema.generated.json | 30 +++++++++---------- .../schemas/devContainer.schema.src.json | 6 ++-- src/vs/base/common/codicons.ts | 4 +-- .../browser/localizations.contribution.ts | 2 +- .../contrib/remote/browser/remoteIndicator.ts | 2 +- .../remote/common/remote.contribution.ts | 2 +- .../themes/common/productIconThemeSchema.ts | 4 +-- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index 48d51d78f05..6d9d794265b 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -24,7 +24,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -32,7 +32,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { @@ -179,7 +179,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json index 19e769e95b0..b8a6d3cf5a3 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.generated.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -116,7 +116,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, @@ -141,7 +141,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -149,7 +149,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { @@ -519,7 +519,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, @@ -544,7 +544,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -552,7 +552,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { @@ -888,7 +888,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, @@ -913,7 +913,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -921,7 +921,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { @@ -1231,7 +1231,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, @@ -1256,7 +1256,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -1264,7 +1264,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { @@ -1539,7 +1539,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, @@ -1564,7 +1564,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -1572,7 +1572,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { diff --git a/extensions/configuration-editing/schemas/devContainer.schema.src.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json index e42d3655d2a..7a11e21a01b 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.src.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -16,7 +16,7 @@ "description": "An array of extensions that should be installed into the container.", "items": { "type": "string", - "pattern": "^([a-z0-9A-Z][a-z0-9\\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\\-A-Z]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." } }, @@ -41,7 +41,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9\\-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9-]+):(\\d{1,5})$" } ] } @@ -49,7 +49,7 @@ "portsAttributes": { "type": "object", "patternProperties": { - "(^\\d+(\\-\\d+)?$)|(.+)": { + "(^\\d+(-\\d+)?$)|(.+)": { "type": "object", "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 100ec15f577..26792a1bd03 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -598,9 +598,9 @@ export interface CSSIcon { export namespace CSSIcon { export const iconNameSegment = '[A-Za-z0-9]+'; - export const iconNameExpression = '[A-Za-z0-9\\-]+'; + export const iconNameExpression = '[A-Za-z0-9-]+'; export const iconModifierExpression = '~[A-Za-z]+'; - export const iconNameCharacter = '[A-Za-z0-9\\-~]'; + export const iconNameCharacter = '[A-Za-z0-9~-]'; const cssIconIdRegex = new RegExp(`^(${iconNameExpression})(${iconModifierExpression})?$`); diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index c91e9c0a29d..92c5e9c0348 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -253,7 +253,7 @@ ExtensionsRegistry.registerExtensionPoint({ id: { type: 'string', description: localize('vscode.extension.contributes.localizations.translations.id', "Id of VS Code or Extension for which this translation is contributed to. Id of VS Code is always `vscode` and of extension should be in format `publisherId.extensionName`."), - pattern: '^((vscode)|([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*))$', + pattern: '^((vscode)|([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*))$', patternErrorMessage: localize('vscode.extension.contributes.localizations.translations.id.pattern', "Id should be `vscode` or in format `publisherId.extensionName` for translating VS code or an extension respectively.") }, path: { diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 359a8a983f9..d4a8b77576f 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -254,7 +254,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr } private validatedGroup(group: string) { - if (!group.match(/^(remote|virtualfs)_(\d\d)_(([a-z][a-z0-9+\-.]*)_(.*))$/)) { + if (!group.match(/^(remote|virtualfs)_(\d\d)_(([a-z][a-z0-9+.-]*)_(.*))$/)) { if (!this.loggedInvalidGroupNames[group]) { this.loggedInvalidGroupNames[group] = true; this.logService.warn(`Invalid group name used in "statusBar/remoteIndicator" menu contribution: ${group}. Entries ignored. Expected format: 'remote_$ORDER_$REMOTENAME_$GROUPING or 'virtualfs_$ORDER_$FILESCHEME_$GROUPING.`); diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index c1b3d14da98..a75b4c1198c 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -218,7 +218,7 @@ Registry.as(ConfigurationExtensions.Configuration) 'remote.portsAttributes': { type: 'object', patternProperties: { - '(^\\d+(\\-\\d+)?$)|(.+)': { + '(^\\d+(-\\d+)?$)|(.+)': { type: 'object', description: localize('remote.portsAttributes.port', "A port, range of ports (ex. \"40000-55000\"), host and port (ex. \"db:1234\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression."), properties: { diff --git a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts index 5b77311e497..fa85dec3cdc 100644 --- a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts @@ -9,10 +9,10 @@ import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/plat import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { iconsSchemaId } from 'vs/platform/theme/common/iconRegistry'; -export const fontIdRegex = '^([\\w-_]+)$'; +export const fontIdRegex = '^([\\w_-]+)$'; export const fontStyleRegex = '^(normal|italic|(oblique[ \\w\\s-]+))$'; export const fontWeightRegex = '^(normal|bold|lighter|bolder|(\\d{0-1000}))$'; -export const fontSizeRegex = '^([\\w .%-_]+)$'; +export const fontSizeRegex = '^([\\w .%_-]+)$'; const schemaId = 'vscode://schemas/product-icon-theme'; const schema: IJSONSchema = { From 8215e1855749526378261b4cec24060bd89d2459 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 12 Nov 2021 16:22:32 +0100 Subject: [PATCH 047/330] regexp: Unnecessary escapes of `-` --- src/vs/workbench/services/themes/common/colorThemeData.ts | 4 ++-- .../test/electron-browser/colorRegistry.releaseTest.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 8cbe7940658..13a0a850882 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -678,8 +678,8 @@ function toCSSSelector(extensionId: string, path: string) { let str = `${extensionId}-${path}`; //remove all characters that are not allowed in css - str = str.replace(/[^_\-a-zA-Z0-9]/g, '-'); - if (str.charAt(0).match(/[0-9\-]/)) { + str = str.replace(/[^_a-zA-Z0-9-]/g, '-'); + if (str.charAt(0).match(/[0-9-]/)) { str = '_' + str; } return str; diff --git a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts index eff9650b2cc..f176e90a9b8 100644 --- a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts @@ -40,7 +40,7 @@ suite('Color Registry', function () { const reqContext = await new RequestService(new TestConfigurationService(), environmentService, new NullLogService()).request({ url: 'https://raw.githubusercontent.com/microsoft/vscode-docs/vnext/api/references/theme-color.md' }, CancellationToken.None); const content = (await asText(reqContext))!; - const expression = /\-\s*\`([\w\.]+)\`: (.*)/g; + const expression = /-\s*\`([\w\.]+)\`: (.*)/g; let m: RegExpExecArray | null; let colorsInDoc: { [id: string]: ColorInfo } = Object.create(null); From a34113483b96899037c95b03b52b32584521898a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 12 Nov 2021 16:26:45 +0100 Subject: [PATCH 048/330] Task description: "User settings" -> "User" Fixes #89448 --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index d0fb9c98ba9..2b3f346c6d8 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -3009,7 +3009,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer public getTaskDescription(task: Task | ConfiguringTask): string | undefined { let description: string | undefined; if (task._source.kind === TaskSourceKind.User) { - description = nls.localize('taskQuickPick.userSettings', 'User Settings'); + description = nls.localize('taskQuickPick.userSettings', 'User'); } else if (task._source.kind === TaskSourceKind.WorkspaceFile) { description = task.getWorkspaceFileName(); } else if (this.needsFolderQualification()) { From 9b1d8197452f97629ed64f3429993549c40fd6af Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 12 Nov 2021 16:25:47 +0100 Subject: [PATCH 049/330] #136424 Initialize remote with user synced extensions --- .../remote/common/remoteAuthorityResolver.ts | 1 + .../common/abstractSynchronizer.ts | 9 +- .../userDataSync/common/extensionsSync.ts | 3 +- .../extensions.contribution.ts | 2 + .../electron-sandbox/remoteExtensionsInit.ts | 132 ++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 3 + src/vs/workbench/workbench.web.main.ts | 4 +- 7 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index ecf80672720..6c43f9252d3 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -19,6 +19,7 @@ export interface ResolvedAuthority { export interface ResolvedOptions { readonly extensionHostEnv?: { [key: string]: string | null }; readonly isTrusted?: boolean; + readonly initializeUsingAccount?: { providerId: string, sessionId: string }; } export interface TunnelDescription { diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index d6159097603..47af2647f89 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -21,6 +21,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileChangesEvent, FileOperationError, FileOperationResult, IFileContent, IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -835,7 +836,7 @@ export abstract class AbstractInitializer implements IUserDataInitializer { constructor( readonly resource: SyncResource, @IEnvironmentService protected readonly environmentService: IEnvironmentService, - @IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService, + @ILogService protected readonly logService: ILogService, @IFileService protected readonly fileService: IFileService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { @@ -854,12 +855,6 @@ export abstract class AbstractInitializer implements IUserDataInitializer { return; } - const isPreviouslySynced = await this.fileService.exists(this.lastSyncResource); - if (isPreviouslySynced) { - this.logService.info('Remote content does not exist.', this.resource); - return; - } - try { await this.doInitialize({ ref, syncData }); } catch (error) { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 1bdce0eb1b6..3e56cc3875a 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -18,6 +18,7 @@ import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtension import { areSameExtensions, getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -506,7 +507,7 @@ export abstract class AbstractExtensionsInitializer extends AbstractInitializer @IIgnoredExtensionsManagementService private readonly ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, - @IUserDataSyncLogService logService: IUserDataSyncLogService, + @ILogService logService: ILogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { super(SyncResource.Extensions, environmentService, logService, fileService, uriIdentityService); diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts index 61ded27f026..55cb5746c3c 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts @@ -23,6 +23,7 @@ import { IExtensionRecommendationNotificationService } from 'vs/platform/extensi import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; import { Codicon } from 'vs/base/common/codicons'; +import { RemoteExtensionsInitializerContribution } from 'vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit'; // Running Extensions Editor Registry.as(EditorExtensions.EditorPane).registerEditorPane( @@ -60,6 +61,7 @@ class ExtensionsContributions implements IWorkbenchContribution { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored); // Register Commands diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts new file mode 100644 index 00000000000..746deed82ea --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { AbstractExtensionsInitializer } from 'vs/platform/userDataSync/common/extensionsSync'; +import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; +import { IRemoteUserData, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + +export class RemoteExtensionsInitializerContribution implements IWorkbenchContribution { + constructor( + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IStorageService private readonly storageService: IStorageService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + ) { + this.initializeRemoteExtensions(); + } + + private async initializeRemoteExtensions(): Promise { + const connection = this.remoteAgentService.getConnection(); + const localExtensionManagementServer = this.extensionManagementServerService.localExtensionManagementServer; + const remoteExtensionManagementServer = this.extensionManagementServerService.remoteExtensionManagementServer; + // Skip: Not a remote window + if (!connection || !remoteExtensionManagementServer) { + return; + } + // Skip: Not a native window + if (!localExtensionManagementServer) { + return; + } + // Skip: No UserdataSyncStore is configured + if (!this.userDataSyncStoreManagementService.userDataSyncStore) { + return; + } + const newRemoteConnectionKey = `${IS_NEW_KEY}.${connection.remoteAuthority}`; + // Skip: Not a new remote connection + if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.GLOBAL, true)) { + this.logService.trace(`Skipping initializing remote extensions because the window with this remote authority was opened before.`); + return; + } + this.storageService.store(newRemoteConnectionKey, false, StorageScope.GLOBAL, StorageTarget.MACHINE); + // Skip: Not a new workspace + if (!this.storageService.isNew(StorageScope.WORKSPACE)) { + this.logService.trace(`Skipping initializing remote extensions because this workspace was opened before.`); + return; + } + // Skip: No account is provided to initialize + const resolvedAuthority = await this.remoteAuthorityResolverService.resolveAuthority(connection.remoteAuthority); + if (!resolvedAuthority.options?.initializeUsingAccount) { + return; + } + + const sessions = await this.authenticationService.getSessions(resolvedAuthority.options?.initializeUsingAccount.providerId); + const session = sessions.find(s => s.id === resolvedAuthority.options?.initializeUsingAccount?.sessionId); + // Skip: Session is not found + if (!session) { + this.logService.info('Skipping initializing remote extensions because the account with given session id is not found', resolvedAuthority.options.initializeUsingAccount.sessionId); + return; + } + + const userDataSyncStoreClient = this.instantiationService.createInstance(UserDataSyncStoreClient, this.userDataSyncStoreManagementService.userDataSyncStore.url); + userDataSyncStoreClient.setAuthToken(session.accessToken, resolvedAuthority.options.initializeUsingAccount.providerId); + const userData = await userDataSyncStoreClient.read(SyncResource.Extensions, null); + + const serviceCollection = new ServiceCollection(); + serviceCollection.set(IExtensionManagementService, remoteExtensionManagementServer.extensionManagementService); + const instantiationService = this.instantiationService.createChild(serviceCollection); + const extensionsToInstallInitializer = instantiationService.createInstance(RemoteExtensionsInitializer); + + await extensionsToInstallInitializer.initialize(userData); + } +} + +class RemoteExtensionsInitializer extends AbstractExtensionsInitializer { + + constructor( + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IIgnoredExtensionsManagementService ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, + @IFileService fileService: IFileService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService, + @IUriIdentityService uriIdentityService: IUriIdentityService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, + ) { + super(extensionManagementService, ignoredExtensionsManagementService, fileService, environmentService, logService, uriIdentityService); + } + + protected override async doInitialize(remoteUserData: IRemoteUserData): Promise { + const remoteExtensions = await this.parseExtensions(remoteUserData); + if (!remoteExtensions) { + this.logService.info('No synced extensions exist while initializing remote extensions.'); + return; + } + const installedExtensions = await this.extensionManagementService.getInstalled(); + const { newExtensions } = this.generatePreview(remoteExtensions, installedExtensions); + if (!newExtensions.length) { + this.logService.trace('No new remote extensions to install.'); + return; + } + const extensionsToInstall = await this.extensionGalleryService.getExtensions(newExtensions, CancellationToken.None); + if (extensionsToInstall.length) { + await Promise.allSettled(extensionsToInstall.map(async e => { + const manifest = await this.extensionGalleryService.getManifest(e, CancellationToken.None); + if (manifest && this.extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) { + await this.extensionManagementService.installFromGallery(e); + } + })); + } + } +} diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 39cf934c361..dd1dd6a6864 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -122,7 +122,10 @@ import { OpenerService } from 'vs/editor/browser/services/openerService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; +import { IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; +registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManagementService); registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService); registerSingleton(IExtensionsStorageSyncService, ExtensionsStorageSyncService); diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index a595a059d78..ea8d00c999b 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -72,8 +72,7 @@ import { ExtensionManagementService } from 'vs/workbench/services/extensionManag import { ILoggerService } from 'vs/platform/log/common/log'; import { FileLoggerService } from 'vs/platform/log/common/fileLog'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; -import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; +import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; @@ -94,7 +93,6 @@ registerSingleton(IWorkbenchExtensionManagementService, ExtensionManagementServi registerSingleton(IAccessibilityService, AccessibilityService, true); registerSingleton(IContextMenuService, ContextMenuService); registerSingleton(ILoggerService, FileLoggerService); -registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService); registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService); From 88ad4c094d07d49329689c2a642c4aee601c059b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 16:45:33 +0100 Subject: [PATCH 050/330] add mini readme for how to consume/add proposals, https://github.com/microsoft/vscode/issues/131165 --- src/vscode-dts/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/vscode-dts/README.md diff --git a/src/vscode-dts/README.md b/src/vscode-dts/README.md new file mode 100644 index 00000000000..37bf94a0b28 --- /dev/null +++ b/src/vscode-dts/README.md @@ -0,0 +1,20 @@ + +## vscode-dts + +This is the place for the stable API and for API proposals. + + +### Consume a proposal + +1. find a proposal you are interested in +1. add its name to your extensions `package.json#enabledApiProposals` property +1. run `npx vscode-dts dev` to download the `d.ts` files into your project +1. don't forget that extension using proposed API cannot be published +1. learn more here: https://code.visualstudio.com/api/advanced-topics/using-proposed-api + +### Add a new proposal + +1. create a _new_ file in this directory, its name must follow this pattern `vscode.proposed.[a-zA-Z]+.d.ts` +1. creating the proposal-file will automatically update `src/vs/workbench/services/extensions/common/extensionsApiProposals.ts` (make to run `yarn watch`) +1. declare and implement your proposal +1. make sure to use the `checkProposedApiEnabled` and/or `isProposedApiEnabled`-utils to enforce the API being proposed. Make sure to invoke them with your proposal's name which got generated into `extensionsApiProposals.ts` From ca9042324fa60c2c76f889f7338487236b499841 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Nov 2021 17:10:21 +0100 Subject: [PATCH 051/330] typo --- src/vscode-dts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/README.md b/src/vscode-dts/README.md index 37bf94a0b28..86de5ab0a0e 100644 --- a/src/vscode-dts/README.md +++ b/src/vscode-dts/README.md @@ -15,6 +15,6 @@ This is the place for the stable API and for API proposals. ### Add a new proposal 1. create a _new_ file in this directory, its name must follow this pattern `vscode.proposed.[a-zA-Z]+.d.ts` -1. creating the proposal-file will automatically update `src/vs/workbench/services/extensions/common/extensionsApiProposals.ts` (make to run `yarn watch`) +1. creating the proposal-file will automatically update `src/vs/workbench/services/extensions/common/extensionsApiProposals.ts` (make sure to run `yarn watch`) 1. declare and implement your proposal 1. make sure to use the `checkProposedApiEnabled` and/or `isProposedApiEnabled`-utils to enforce the API being proposed. Make sure to invoke them with your proposal's name which got generated into `extensionsApiProposals.ts` From 231fe61ab5787ac1cc50052a3099ad861bd2960a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 12 Nov 2021 17:14:05 +0100 Subject: [PATCH 052/330] Add setting to exclude scripts from NPM scripts view Fixes #71635 --- extensions/npm/package.json | 9 +++++++++ extensions/npm/package.nls.json | 1 + extensions/npm/src/npmMain.ts | 2 +- extensions/npm/src/npmView.ts | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/extensions/npm/package.json b/extensions/npm/package.json index ce506759aa4..e3baafd8bfe 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -281,6 +281,15 @@ "scope": "window", "default": "open" }, + "npm.scriptExplorerExclude": { + "type": "array", + "items": { + "type": "string" + }, + "markdownDescription": "%config.npm.scriptExplorerExclude%", + "scope": "resource", + "default": [] + }, "npm.fetchOnlinePackageInfo": { "type": "boolean", "description": "%config.npm.fetchOnlinePackageInfo%", diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index b3ff9a4d229..b0a4f06c01f 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -12,6 +12,7 @@ "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", "config.npm.scriptExplorerAction": "The default click action used in the npm scripts explorer: `open` or `run`, the default is `open`.", + "config.npm.scriptExplorerExclude": "An array of regular expressions that indicate which scripts should be excluded from the NPM Scripts view.", "config.npm.enableRunFromFolder": "Enable running npm scripts contained in a folder from the Explorer context menu.", "config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.", "npm.parseError": "Npm task detection: failed to parse the file {0}", diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 6969b7d63a6..3788c94ea45 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -38,7 +38,7 @@ export async function activate(context: vscode.ExtensionContext): Promise treeDataProvider = registerExplorer(context); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect')) { + if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect') || e.affectsConfiguration('npm.scriptExplorerExclude')) { invalidateTasksCache(); if (treeDataProvider) { treeDataProvider.refresh(); diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 7f06a994f70..47b43c8f74e 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -291,7 +291,14 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { let folder = null; let packageJson = null; + const regularExpressionsSetting = workspace.getConfiguration('npm').get('scriptExplorerExclude', []); + const regularExpressions = regularExpressionsSetting?.map(value => RegExp(value)); + tasks.forEach(each => { + if (regularExpressions.some((regularExpression) => (each.task.definition).script.match(regularExpression))) { + return; + } + if (isWorkspaceFolder(each.task.scope) && !this.isInstallTask(each.task)) { folder = folders.get(each.task.scope.name); if (!folder) { From 1bf65f4bfb3d855d50b8519ce37d744f9998ea28 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 12 Nov 2021 11:19:54 -0500 Subject: [PATCH 053/330] Bump distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 368265d0742..6755ff90e58 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "cc8976e5470edb06e4b3abd29841ffe7bf5042d2", + "distro": "4402f943794631d3a9a9f3dbdfaa5d855ddfe575", "author": { "name": "Microsoft Corporation" }, From 654f5ae36b81eaefd482c3c27c760f4876c32240 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 11 Nov 2021 21:59:10 -0800 Subject: [PATCH 054/330] focus cell list container when active element is removed from DOM. --- .../contrib/notebook/browser/notebook.layout.md | 3 ++- .../contrib/notebook/browser/notebookBrowser.ts | 7 ++++++- .../contrib/notebook/browser/notebookEditorWidget.ts | 12 ++++++++++++ .../notebook/browser/view/notebookCellList.ts | 4 ++++ .../notebook/browser/view/notebookRenderingCommon.ts | 1 + .../notebook/browser/view/renderers/codeCell.ts | 5 +++++ .../notebook/browser/view/renderers/markdownCell.ts | 5 +++++ .../notebook/browser/viewModel/baseCellViewModel.ts | 6 ++++-- 8 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index d12d4cc249b..7ef92d5bbc9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -94,4 +94,5 @@ CSS * if focused element is find widget / notebookToolbar, don't change focusMode * if focused element is webview/iframe, don't change focusMode * if focused element is outside of the notebook, don't change focusMode -* cell editor focused and cell moves out of view (no blur event) / `CodeCell.dispose` +* cell editor focused and cell moves out of view (no blur event) + * When disposing the cell view, we will focus the cell list container, otherwise the focus goes to body as the active element is removed from the DOM. diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 19d614d3908..451f08fc484 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -434,10 +434,15 @@ export interface INotebookEditor { getSelectionViewModels(): ICellViewModel[]; /** - * Focus the notebook editor cell list + * Focus the active cell in notebook cell list */ focus(): void; + /** + * Focus the notebook cell list container + */ + focusContainer(): void; + hasEditorFocus(): boolean; hasWebviewFocus(): boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 994640eb7fa..03ffe4f6a49 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1446,6 +1446,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private _lastCellWithEditorFocus: ICellViewModel | null = null; private _validateCellFocusMode(cell: ICellViewModel) { + if (cell.focusMode !== CellFocusMode.Editor) { + return; + } + if (this._lastCellWithEditorFocus && this._lastCellWithEditorFocus !== cell) { this._lastCellWithEditorFocus.focusMode = CellFocusMode.Container; } @@ -1731,6 +1735,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._onDidFocusEditorWidget.fire(); } + focusContainer() { + if (this._webviewFocused) { + this._webview?.focusWebview(); + } else { + this._list.focusContainer(); + } + } + onWillHide() { this._isVisible = false; this._editorFocus.set(false); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 953bb39eb64..b371fac872d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -924,6 +924,10 @@ export class NotebookCellList extends WorkbenchList implements ID super.domFocus(); } + focusContainer() { + super.domFocus(); + } + getViewScrollTop() { return this.view.getScrollTop(); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 9ce7173a360..1abcf561309 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -80,6 +80,7 @@ export interface INotebookCellList { triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent): void; updateElementHeight2(element: ICellViewModel, size: number): void; domFocus(): void; + focusContainer(): void; setCellSelection(element: ICellViewModel, range: Range): void; style(styles: IListStyles): void; getRenderHeight(): number; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index cce8bfde294..6587aa78ec3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -442,6 +442,11 @@ export class CodeCell extends Disposable { override dispose() { this._isDisposed = true; + // move focus back to the cell list otherwise the focus goes to body + if (this.notebookEditor.getActiveCell() === this.viewCell && this.viewCell.focusMode === CellFocusMode.Editor) { + this.notebookEditor.focusContainer(); + } + this.viewCell.detachTextEditor(); this._removeInputCollapsePreview(); this._outputContainerRenderer.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 86620b5fc48..86aac5bb371 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -152,6 +152,11 @@ export class StatefulMarkdownCell extends Disposable { } override dispose() { + // move focus back to the cell list otherwise the focus goes to body + if (this.notebookEditor.getActiveCell() === this.viewCell && this.viewCell.focusMode === CellFocusMode.Editor) { + this.notebookEditor.focusContainer(); + } + this.viewCell.detachTextEditor(); super.dispose(); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index fabc8219d80..9c022a93c24 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -104,8 +104,10 @@ export abstract class BaseCellViewModel extends Disposable { return this._focusMode; } set focusMode(newMode: CellFocusMode) { - this._focusMode = newMode; - this._onDidChangeState.fire({ focusModeChanged: true }); + if (this._focusMode !== newMode) { + this._focusMode = newMode; + this._onDidChangeState.fire({ focusModeChanged: true }); + } } protected _textEditor?: ICodeEditor; From 48a7916767ae117449a38862b62f5c21869e9bcf Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 12 Nov 2021 10:01:36 -0800 Subject: [PATCH 055/330] update markdown cell listener --- .../browser/view/renderers/markdownCell.ts | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 86aac5bb371..30093cc6abf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -31,7 +31,7 @@ export class StatefulMarkdownCell extends Disposable { private editor: CodeEditorWidget | null = null; - private markdownAccessibilityContainer: HTMLElement; + private markdownAccessibilityContainer!: HTMLElement; private editorPart: HTMLElement; private readonly localDisposables = this._register(new DisposableStore()); @@ -52,9 +52,30 @@ export class StatefulMarkdownCell extends Disposable { ) { super(); + this.constructDOM(); + this.editorPart = templateData.editorPart; + this._register(toDisposable(() => renderedEditors.delete(this.viewCell))); + this.registerListeners(); + + // update for init state + this.updateForFocusModeChange(); + this.foldingState = viewCell.foldingState; + this.setFoldingIndicator(); + this.updateFoldingIconShowClass(); + + // the markdown preview's height might already be updated after the renderer calls `element.getHeight()` + if (this.viewCell.layoutInfo.totalHeight > 0) { + this.relayoutCell(); + } + + this.applyDecorations(); + this.viewUpdate(); + } + + private constructDOM() { // Create an element that is only used to announce markup cell content to screen readers const id = `aria-markup-cell-${this.viewCell.id}`; - this.markdownAccessibilityContainer = templateData.cellContainer; + this.markdownAccessibilityContainer = this.templateData.cellContainer; this.markdownAccessibilityContainer.id = id; // Hide the element from non-screen readers this.markdownAccessibilityContainer.style.height = '1px'; @@ -63,36 +84,25 @@ export class StatefulMarkdownCell extends Disposable { this.markdownAccessibilityContainer.ariaHidden = 'false'; this.templateData.rootContainer.setAttribute('aria-describedby', id); - - this.editorPart = templateData.editorPart; - this.templateData.container.classList.toggle('webview-backed-markdown-cell', true); + } - this._register(toDisposable(() => renderedEditors.delete(this.viewCell))); - - this._register(viewCell.model.onDidChangeMetadata(() => { + private registerListeners() { + this._register(this.viewCell.model.onDidChangeMetadata(() => { this.viewUpdate(); })); - const updateForFocusMode = () => { - if (viewCell.focusMode === CellFocusMode.Editor) { - this.focusEditorIfNeeded(); - } - - templateData.container.classList.toggle('cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); - }; - - this._register(viewCell.onDidChangeState((e) => { + this._register(this.viewCell.onDidChangeState((e) => { if (e.editStateChanged || e.contentChanged) { this.viewUpdate(); } if (e.focusModeChanged) { - updateForFocusMode(); + this.updateForFocusModeChange(); } if (e.foldingStateChanged) { - const foldingState = viewCell.foldingState; + const foldingState = this.viewCell.foldingState; if (foldingState !== this.foldingState) { this.foldingState = foldingState; @@ -101,34 +111,33 @@ export class StatefulMarkdownCell extends Disposable { } })); - updateForFocusMode(); - this.foldingState = viewCell.foldingState; - this.setFoldingIndicator(); - this.updateFoldingIconShowClass(); - this._register(this.notebookEditor.notebookOptions.onDidChangeOptions(e => { if (e.showFoldingControls) { this.updateFoldingIconShowClass(); } })); - this._register(viewCell.onDidChangeLayout((e) => { + this._register(this.viewCell.onDidChangeLayout((e) => { const layoutInfo = this.editor?.getLayoutInfo(); - if (e.outerWidth && this.viewCell.getEditState() === CellEditState.Editing && layoutInfo && layoutInfo.width !== viewCell.layoutInfo.editorWidth) { + if (e.outerWidth && this.viewCell.getEditState() === CellEditState.Editing && layoutInfo && layoutInfo.width !== this.viewCell.layoutInfo.editorWidth) { this.onCellEditorWidthChange(); } else if (e.totalHeight || e.outerWidth) { this.relayoutCell(); } })); + } - // the markdown preview's height might already be updated after the renderer calls `element.getHeight()` - if (this.viewCell.layoutInfo.totalHeight > 0) { - this.relayoutCell(); + private updateForFocusModeChange() { + if (this.viewCell.focusMode === CellFocusMode.Editor) { + this.focusEditorIfNeeded(); } - // apply decorations + this.templateData.container.classList.toggle('cell-editor-focus', this.viewCell.focusMode === CellFocusMode.Editor); + } - this._register(viewCell.onCellDecorationsChanged((e) => { + private applyDecorations() { + // apply decorations + this._register(this.viewCell.onCellDecorationsChanged((e) => { e.added.forEach(options => { if (options.className) { this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.className], []); @@ -142,13 +151,11 @@ export class StatefulMarkdownCell extends Disposable { }); })); - viewCell.getCellDecorations().forEach(options => { + this.viewCell.getCellDecorations().forEach(options => { if (options.className) { this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.className], []); } }); - - this.viewUpdate(); } override dispose() { From 694097a63a2940146e3caa22a29755f0d1d36a7f Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 12 Nov 2021 10:40:25 -0800 Subject: [PATCH 056/330] Instrument accepted suggestion contributions (#136922) * Instrument accepted suggestion contributions * Only log once per resource/provider * Keep track of logged events for real * Move types closer to use * Address feedback * Tidy up --- .../contrib/suggest/suggestController.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 6de2eb0900c..ff9075b5a93 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -43,6 +43,9 @@ import { CommitCharacterController } from './suggestCommitCharacters'; import { State, SuggestModel } from './suggestModel'; import { OvertypingCapturer } from './suggestOvertypingCapturer'; import { ISelectedSuggestion, SuggestWidget } from './suggestWidget'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { basename, extname } from 'vs/base/common/resources'; +import { hash } from 'vs/base/common/hash'; // sticky suggest widget which doesn't disappear on focus out and such let _sticky = false; @@ -122,6 +125,7 @@ export class SuggestController implements IEditorContribution { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, ) { this.editor = editor; this.model = _instantiationService.createInstance(SuggestModel, this.editor,); @@ -427,11 +431,35 @@ export class SuggestController implements IEditorContribution { // clear only now - after all tasks are done Promise.all(tasks).finally(() => { + this._reportSuggestionAcceptedTelemetry(model, event); + this.model.clear(); cts.dispose(); }); } + private _telemetryGate: number = 0; + private _reportSuggestionAcceptedTelemetry(model: ITextModel, acceptedSuggestion: ISelectedSuggestion) { + if (this._telemetryGate++ % 100 !== 0) { + return; + } + + type AcceptedSuggestion = { providerId: string; fileExtension: string; languageId: string; basenameHash: string; }; + type AcceptedSuggestionClassification = { + providerId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; }; + basenameHash: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; }; + fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; }; + languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; }; + }; + + this._telemetryService.publicLog2('suggest.acceptedSuggestion', { + providerId: acceptedSuggestion.item.provider._debugDisplayName ?? 'unknown', + basenameHash: hash(basename(model.uri)).toString(16), + languageId: model.getLanguageId(), + fileExtension: extname(model.uri), + }); + } + getOverwriteInfo(item: CompletionItem, toggleMode: boolean): { overwriteBefore: number, overwriteAfter: number } { assertType(this.editor.hasModel()); From 596c835be231972bd1d86d243cdb4e7f78cac807 Mon Sep 17 00:00:00 2001 From: John Murray Date: Fri, 12 Nov 2021 18:46:53 +0000 Subject: [PATCH 057/330] Word missing from setting description (fix #137027) (#137028) --- .../gettingStarted/browser/gettingStarted.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts index 848cd9cd251..957e3b14898 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts @@ -295,7 +295,7 @@ configurationRegistry.registerConfiguration({ scope: ConfigurationScope.MACHINE, type: 'boolean', default: true, - description: localize('workbench.welcomePage.walkthroughs.openOnInstall', "When enabled, an extension's walkthrough will open upon install the extension.") + description: localize('workbench.welcomePage.walkthroughs.openOnInstall', "When enabled, an extension's walkthrough will open upon install of the extension.") } } }); From 5248915eadd04a8723f7d15789e61e0ea49d2f3a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 12 Nov 2021 19:29:56 +0000 Subject: [PATCH 058/330] fix #134418 --- src/vs/platform/terminal/node/terminalProfiles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 5fa0a84441c..d9aab34bc5f 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -236,7 +236,7 @@ async function getWslProfiles(wslPath: string, defaultProfileName: string | unde const profiles: ITerminalProfile[] = []; const distroOutput = await new Promise((resolve, reject) => { // wsl.exe output is encoded in utf16le (ie. A -> 0x4100) - cp.exec('wsl.exe -l -q', { encoding: 'utf16le' }, (err, stdout) => { + cp.exec('wsl.exe -l -q', { encoding: 'utf16le', timeout: 1000 }, (err, stdout) => { if (err) { return reject('Problem occurred when getting wsl distros'); } From 0687b17f54ea0916da9d0e813e265fbdefa47d15 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 12:50:40 -0800 Subject: [PATCH 059/330] Hide terminal panel-specific commands from cmd palette Part of #136549 --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index b8e42781552..01c66be16e9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -760,7 +760,7 @@ export function registerTerminalActions() { super({ id: TerminalCommandId.ChangeIconPanel, title: terminalStrings.changeIcon, - f1: true, + f1: false, category, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated) }); @@ -802,7 +802,7 @@ export function registerTerminalActions() { super({ id: TerminalCommandId.ChangeColorPanel, title: terminalStrings.changeColor, - f1: true, + f1: false, category, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated) }); From 9607a0029ac2b5de29904e5b046d6802674adc69 Mon Sep 17 00:00:00 2001 From: tanhakabir Date: Fri, 12 Nov 2021 13:04:16 -0800 Subject: [PATCH 060/330] Ensure `--pick-port` range is "-" --- src/vs/server/main.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/server/main.js b/src/vs/server/main.js index 7125928414f..b35e734f5e7 100644 --- a/src/vs/server/main.js +++ b/src/vs/server/main.js @@ -156,15 +156,20 @@ async function parsePort(strPort, strPickPort) { } if (strPickPort) { - const [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr, 10); }); - if (!isNaN(start) && !isNaN(end)) { - if (specificPort !== -1 && specificPort >= start && specificPort <= end) { - return specificPort; + if (strPickPort.match(/[\d]+-[\d]+/)) { + const [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr, 10); }); + + if (!isNaN(start) && !isNaN(end)) { + if (specificPort !== -1 && specificPort >= start && specificPort <= end) { + return specificPort; + } else { + return await findFreePort(start, start, end); + } } else { - return await findFreePort(start, start, end); + console.log('Port range are not numbers, using 8000 instead.'); } } else { - console.log('Port range are not numbers, using 8000 instead.'); + console.log(`Port range: "${strPickPort}" is not properly formatted, using 8000 instead.`); } } From e011ec2b7de16881f60134615a9e08e2f3857d20 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 12 Nov 2021 16:14:47 -0500 Subject: [PATCH 061/330] Remove instance id common property --- .../platform/storage/electron-main/storageMain.ts | 9 +-------- .../test/electron-main/storageMainService.test.ts | 4 +--- src/vs/platform/telemetry/common/telemetry.ts | 2 -- .../platform/telemetry/common/telemetryService.ts | 3 +-- .../telemetry/test/browser/telemetryService.test.ts | 2 -- .../contrib/update/browser/releaseNotesEditor.ts | 13 ++++++------- .../electron-sandbox/workbenchCommonProperties.ts | 5 +---- .../test/electron-browser/commonProperties.test.ts | 1 - 8 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index d81f4d0f096..e82e328647e 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -6,14 +6,13 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { join } from 'vs/base/common/path'; -import { generateUuid } from 'vs/base/common/uuid'; import { Promises } from 'vs/base/node/pfs'; import { InMemoryStorageDatabase, IStorage, Storage, StorageHint } from 'vs/base/parts/storage/common/storage'; import { ISQLiteStorageDatabaseLoggingOptions, SQLiteStorageDatabase } from 'vs/base/parts/storage/node/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; -import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { currentSessionDateStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export interface IStorageMainOptions { @@ -214,12 +213,6 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { private updateTelemetryState(storage: IStorage): void { - // Instance UUID (once) - const instanceId = storage.get(instanceStorageKey, undefined); - if (instanceId === undefined) { - storage.set(instanceStorageKey, generateUuid()); - } - // First session date (once) const firstSessionDate = storage.get(firstSessionDateStorageKey, undefined); if (firstSessionDate === undefined) { diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 459ce917e13..782140d4e99 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -17,7 +17,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { IStorageChangeEvent, IStorageMain, IStorageMainOptions } from 'vs/platform/storage/electron-main/storageMain'; import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; -import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { currentSessionDateStorageKey, firstSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { ICodeWindow, UnloadReason } from 'vs/platform/windows/electron-main/windows'; suite('StorageMainService', function () { @@ -77,9 +77,7 @@ suite('StorageMainService', function () { // Telemetry: added after init if (isGlobal) { strictEqual(storage.items.size, 0); - strictEqual(storage.get(instanceStorageKey), undefined); await storage.init(); - strictEqual(typeof storage.get(instanceStorageKey), 'string'); strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); strictEqual(typeof storage.get(currentSessionDateStorageKey), 'string'); } else { diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index 4f0dfa36049..be381cd790c 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -11,7 +11,6 @@ export const ITelemetryService = createDecorator('telemetrySe export interface ITelemetryInfo { sessionId: string; machineId: string; - instanceId: string; firstSessionDate: string; msftInternal?: boolean; } @@ -66,7 +65,6 @@ export interface ICustomEndpointTelemetryService { } // Keys -export const instanceStorageKey = 'telemetry.instanceId'; export const currentSessionDateStorageKey = 'telemetry.currentSessionDate'; export const firstSessionDateStorageKey = 'telemetry.firstSessionDate'; export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index a3b17f5a821..c66501394a0 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -95,12 +95,11 @@ export class TelemetryService implements ITelemetryService { // well known properties let sessionId = values['sessionID']; - let instanceId = values['common.instanceId']; let machineId = values['common.machineId']; let firstSessionDate = values['common.firstSessionDate']; let msftInternal = values['common.msftInternal']; - return { sessionId, instanceId, machineId, firstSessionDate, msftInternal }; + return { sessionId, machineId, firstSessionDate, msftInternal }; } dispose(): void { diff --git a/src/vs/platform/telemetry/test/browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/browser/telemetryService.test.ts index a930891c50e..66cccfc61d7 100644 --- a/src/vs/platform/telemetry/test/browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/browser/telemetryService.test.ts @@ -185,14 +185,12 @@ suite('TelemetryService', () => { appenders: [NullAppender], commonProperties: Promise.resolve({ sessionID: 'one', - ['common.instanceId']: 'two', ['common.machineId']: 'three', }) }, new TestConfigurationService()); return service.getTelemetryInfo().then(info => { assert.strictEqual(info.sessionId, 'one'); - assert.strictEqual(info.instanceId, 'two'); assert.strictEqual(info.machineId, 'three'); service.dispose(); diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index fa95f119d62..a3cdd412cc1 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -22,14 +22,15 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { asText, IRequestService } from 'vs/platform/request/common/request'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DEFAULT_MARKDOWN_STYLES, renderMarkdownDocument } from 'vs/workbench/contrib/markdown/browser/markdownDocumentRenderer'; import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput'; import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; export class ReleaseNotesManager { @@ -44,7 +45,7 @@ export class ReleaseNotesManager { @IModeService private readonly _modeService: IModeService, @IOpenerService private readonly _openerService: IOpenerService, @IRequestService private readonly _requestService: IRequestService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @@ -195,11 +196,9 @@ export class ReleaseNotesManager { } private async addGAParameters(uri: URI, origin: string, experiment = '1'): Promise { - if (supportsTelemetry(this._productService, this._environmentService)) { + if (supportsTelemetry(this._productService, this._environmentService) && getTelemetryLevel(this._configurationService) === TelemetryLevel.USAGE) { if (uri.scheme === 'https' && uri.authority === 'code.visualstudio.com') { - const info = await this._telemetryService.getTelemetryInfo(); - - return uri.with({ query: `${uri.query ? uri.query + '&' : ''}utm_source=VsCode&utm_medium=${encodeURIComponent(origin)}&utm_campaign=${encodeURIComponent(info.instanceId)}&utm_content=${encodeURIComponent(experiment)}` }); + return uri.with({ query: `${uri.query ? uri.query + '&' : ''}utm_source=VsCode&utm_medium=${encodeURIComponent(origin)}&utm_content=${encodeURIComponent(experiment)}` }); } } return uri; diff --git a/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts b/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts index d81230c34f2..ff36a5231e6 100644 --- a/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts +++ b/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts @@ -5,7 +5,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; -import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IFileService } from 'vs/platform/files/common/files'; @@ -23,7 +23,6 @@ export async function resolveWorkbenchCommonProperties( remoteAuthority?: string ): Promise<{ [name: string]: string | boolean | undefined }> { const result = await resolveCommonProperties(fileService, release, hostname, process.arch, commit, version, machineId, msftInternalDomains, installSourcePath); - const instanceId = storageService.get(instanceStorageKey, StorageScope.GLOBAL)!; const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; @@ -37,8 +36,6 @@ export async function resolveWorkbenchCommonProperties( result['common.lastSessionDate'] = lastSessionDate || ''; // __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.isNewSession'] = !lastSessionDate ? '1' : '0'; - // __GDPR__COMMON__ "common.instanceId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - result['common.instanceId'] = instanceId; // __GDPR__COMMON__ "common.remoteAuthority" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.remoteAuthority'] = cleanRemoteAuthority(remoteAuthority); diff --git a/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts b/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts index 26d36e27ba0..fed7bd8c4ff 100644 --- a/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts @@ -64,7 +64,6 @@ suite('Telemetry - common properties', function () { assert.ok('common.lastSessionDate' in props, 'lastSessionDate'); // conditional, see below, 'lastSessionDate'ow assert.ok('common.isNewSession' in props, 'isNewSession'); // machine id et al - assert.ok('common.instanceId' in props, 'instanceId'); assert.ok('common.machineId' in props, 'machineId'); fs.unlinkSync(installSource); const props_1 = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), hostname(), commit, version, 'someMachineId', undefined, installSource); From 97ba0f840f8dcf8c2123d5ece7f6ab41314a1848 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 12 Nov 2021 13:32:16 -0800 Subject: [PATCH 062/330] ensure notebook toolbar takes focus --- .../notebook/browser/notebook.layout.md | 35 ++++------- .../notebook/browser/notebookEditorWidget.ts | 58 +++++++++++++++++-- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index 7ef92d5bbc9..f48565ab373 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -71,28 +71,17 @@ However, we **don't** warmup the previous viewport as the cell height change of ## Focus Tracking -* DOM focus tracker of notebook editor container - * focus on cell container - * focus on cell editor - * focus on cell status bar - * focus on cell/execution/output toolbar - * focus on find widget - * focus on an element in the webview/iframe - - [ ] Focus on notebook toolbar -* hasWebviewFocus +Due to the nature of virtualization (list view) and two layers architecture, the focus tracking is more complex compared to file explorer or monaco editor. When a notebook is *focused*, the `document.activeElement` can be +* Monaco editor, when users focus on a cell editor + * `textarea` when users focus the text + * Widgets +* Webview/iframe, when users focus on markdown cell or rich outputs rendered rendered in iframe +* List view container, when users focus on cell container +* Focusable element inside the notebook editor + * Builtin output (e.g., text output) + * Find Widget + * Cell statusbar + * Toolbars -CSS -* `monaco-list.focus-within` for cell container/editor borders - - -* in `codecell` we update the `cell.focusMode` when rendering it and if it's not the focused element anymore, the focusMode is reverted to container - - -* cell editor focused and editor blur event happens - * if focused element is another cell, then change focusMode to container - * if focused element is find widget / notebookToolbar, don't change focusMode - * if focused element is webview/iframe, don't change focusMode - * if focused element is outside of the notebook, don't change focusMode -* cell editor focused and cell moves out of view (no blur event) - * When disposing the cell view, we will focus the cell list container, otherwise the focus goes to body as the active element is removed from the DOM. +The catch here is if the focus is on a monaco editor, instead of the list view container, when the cell is moved out of view, the list view removes the cell row from the DOM tree. The `document.activeElement` will fall back `document.body` when that happens. To ensure that the notebook editor doesn't blur, we need to move focus back to list view container when the focused cell is moved out of view. More importantly, focus the cell editor again when the cell is visible again (if the cell is still the *active* cell). diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 03ffe4f6a49..545f7eec1ff 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -222,6 +222,41 @@ export function getDefaultNotebookCreationOptions() { }; } +class NotebookEditorWidgetFocusTracker extends Disposable { + + private _hasFocus: boolean; + private readonly _domFocusTracker: DOM.IFocusTracker; + + private readonly _onChange: Emitter = this._register(new Emitter()); + public readonly onChange: Event = this._onChange.event; + + constructor(domElement: HTMLElement) { + super(); + + this._hasFocus = false; + this._domFocusTracker = this._register(DOM.trackFocus(domElement)); + + this._register(this._domFocusTracker.onDidFocus(() => { + this._hasFocus = true; + this._onChange.fire(undefined); + })); + this._register(this._domFocusTracker.onDidBlur(() => { + this._hasFocus = false; + this._onChange.fire(undefined); + })); + } + + public hasFocus(): boolean { + return this._hasFocus; + } + + public refreshState(): void { + if (this._domFocusTracker.refreshState) { + this._domFocusTracker.refreshState(); + } + } +} + export class NotebookEditorWidget extends Disposable implements INotebookEditorDelegate { //#region Eventing private readonly _onDidChangeCellState = this._register(new Emitter()); @@ -293,6 +328,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private _cellContextKeyManager: CellContextKeyManager | null = null; private _isVisible = false; private readonly _uuid = generateUuid(); + private _widgetFocusTracker!: NotebookEditorWidgetFocusTracker; private _webviewFocused: boolean = false; private _isDisposed: boolean = false; @@ -581,6 +617,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private _createBody(parent: HTMLElement): void { this._notebookTopToolbarContainer = document.createElement('div'); this._notebookTopToolbarContainer.classList.add('notebook-toolbar-container'); + this._notebookTopToolbarContainer.tabIndex = 0; this._notebookTopToolbarContainer.style.display = 'none'; DOM.append(parent, this._notebookTopToolbarContainer); this._body = document.createElement('div'); @@ -1000,10 +1037,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } })); - const widgetFocusTracker = DOM.trackFocus(this.getDomNode()); - this._register(widgetFocusTracker); - this._register(widgetFocusTracker.onDidFocus(() => this._onDidFocusEmitter.fire())); - this._register(widgetFocusTracker.onDidBlur(() => this._onDidBlurEmitter.fire())); + this._widgetFocusTracker = this._register(new NotebookEditorWidgetFocusTracker(this.getDomNode())); + this._register(this._widgetFocusTracker.onChange(() => { + const focused = this._widgetFocusTracker.hasFocus(); + this._editorFocus.set(focused); + this.viewModel?.setEditorFocus(focused); + + if (focused) { + this._onDidFocusEmitter.fire(); + } else { + this._onDidBlurEmitter.fire(); + } + })); this._registerNotebookActionsToolbar(); } @@ -1754,7 +1799,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD updateEditorFocus() { // Note - focus going to the webview will fire 'blur', but the webview element will be // a descendent of the notebook editor root. - const focused = this._overlayContainer.contains(document.activeElement); + this._widgetFocusTracker.refreshState(); + const focused = this._widgetFocusTracker.hasFocus(); this._editorFocus.set(focused); this.viewModel?.setEditorFocus(focused); } @@ -1763,7 +1809,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD // _editorFocus is driven by the FocusTracker, which is only guaranteed to _eventually_ fire blur. // If we need to know whether we have focus at this instant, we need to check the DOM manually. this.updateEditorFocus(); - return this._editorFocus.get() || false; + return this._widgetFocusTracker.hasFocus(); } hasWebviewFocus() { From c528aef31b6c10121c8961cbd6ee26633c88d515 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 13:56:43 -0800 Subject: [PATCH 063/330] Move exit code message handling into a tested func Part of #136061 --- .../terminal/browser/terminalInstance.ts | 141 ++++++++++-------- .../test/browser/terminalInstance.test.ts | 113 +++++++++++++- 2 files changed, 188 insertions(+), 66 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 08ffd2a1e3a..f278281d29f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1163,64 +1163,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { await this._flushXtermData(); this._logService.debug(`Terminal process exit (instanceId: ${this.instanceId}) with code ${this._exitCode}`); - let exitCodeMessage: string | undefined; - - // Create exit code message - switch (typeof exitCodeOrError) { - case 'number': - // Only show the error if the exit code is non-zero - this._exitCode = exitCodeOrError; - if (this._exitCode === 0) { - break; - } - - let commandLine: string | undefined = undefined; - if (this._shellLaunchConfig.executable) { - commandLine = this._shellLaunchConfig.executable; - if (typeof this._shellLaunchConfig.args === 'string') { - commandLine += ` ${this._shellLaunchConfig.args}`; - } else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) { - commandLine += this._shellLaunchConfig.args.map(a => ` '${a}'`).join(); - } - } - - if (this._processManager.processState === ProcessState.KilledDuringLaunch) { - if (commandLine) { - exitCodeMessage = nls.localize('launchFailed.exitCodeAndCommandLine', "The terminal process \"{0}\" failed to launch (exit code: {1}).", commandLine, this._exitCode); - break; - } - exitCodeMessage = nls.localize('launchFailed.exitCodeOnly', "The terminal process failed to launch (exit code: {0}).", this._exitCode); - break; - } - if (commandLine) { - exitCodeMessage = nls.localize('terminated.exitCodeAndCommandLine', "The terminal process \"{0}\" terminated with exit code: {1}.", commandLine, this._exitCode); - break; - } - exitCodeMessage = nls.localize('terminated.exitCodeOnly', "The terminal process terminated with exit code: {0}.", this._exitCode); - break; - case 'object': - if (exitCodeOrError.message.toString().includes('Could not find pty with id')) { - break; - } - this._exitCode = exitCodeOrError.code; - const conptyError = exitCodeOrError.message.match(/.*error code:\s*(\d+).*$/); - if (conptyError) { - const errorCode = conptyError.length > 1 ? parseInt(conptyError[1]) : undefined; - switch (errorCode) { - case 5: - exitCodeOrError.message = `Access was denied to the path containing your executable ${this.shellLaunchConfig.executable}. Manage and change your permissions to get this to work.`; - break; - case 267: - exitCodeOrError.message = `Invalid starting directory ${this.initialCwd}, review your terminal.integrated.cwd setting`; - break; - case 1260: - exitCodeOrError.message = `Windows cannot open this program because it has been prevented by a software restriction policy. For more information, open Event Viewer or contact your system Administrator`; - break; - } - } - exitCodeMessage = nls.localize('launchFailed.errorMessage', "The terminal process failed to launch: {0}.", exitCodeOrError.message); - break; - } + const parsedExitResult = parseExitResult(exitCodeOrError, this.shellLaunchConfig, this._processManager.processState, this._initialCwd); + this._exitCode = parsedExitResult?.code; + const exitMessage = parsedExitResult?.message; this._logService.debug(`Terminal process exit (instanceId: ${this.instanceId}) state ${this._processManager.processState}`); @@ -1228,8 +1173,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // user (via the `workbench.action.terminal.kill` command). if (this._shellLaunchConfig.waitOnExit && this._processManager.processState !== ProcessState.KilledByUser) { this._xtermReadyPromise.then(xterm => { - if (exitCodeMessage) { - xterm.raw.writeln(exitCodeMessage); + if (exitMessage) { + xterm.raw.writeln(exitMessage); } if (typeof this._shellLaunchConfig.waitOnExit === 'string') { xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit)); @@ -1242,19 +1187,19 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); } else { this.dispose(); - if (exitCodeMessage) { + if (exitMessage) { const failedDuringLaunch = this._processManager.processState === ProcessState.KilledDuringLaunch; if (failedDuringLaunch || this._configHelper.config.showExitAlert) { // Always show launch failures this._notificationService.notify({ - message: exitCodeMessage, + message: exitMessage, severity: Severity.Error, actions: { primary: [this._instantiationService.createInstance(TerminalLaunchHelpAction)] } }); } else { // Log to help surface the error in case users report issues with showExitAlert // disabled - this._logService.warn(exitCodeMessage); + this._logService.warn(exitMessage); } } } @@ -2151,3 +2096,73 @@ export class TerminalLabelComputer extends Disposable { return true; } } + +export function parseExitResult( + exitCodeOrError: ITerminalLaunchError | number | undefined, + shellLaunchConfig: IShellLaunchConfig, + processState: ProcessState, + initialCwd: string | undefined +): { code: number | undefined, message: string | undefined } | undefined { + // Only return a message if the exit code is non-zero + if (exitCodeOrError === undefined || exitCodeOrError === 0) { + return { code: exitCodeOrError, message: undefined }; + } + + const code = typeof exitCodeOrError === 'number' ? exitCodeOrError : exitCodeOrError.code; + + // Create exit code message + let message: string | undefined = undefined; + switch (typeof exitCodeOrError) { + case 'number': + let commandLine: string | undefined = undefined; + if (shellLaunchConfig.executable) { + commandLine = shellLaunchConfig.executable; + if (typeof shellLaunchConfig.args === 'string') { + commandLine += ` ${shellLaunchConfig.args}`; + } else if (shellLaunchConfig.args && shellLaunchConfig.args.length) { + commandLine += shellLaunchConfig.args.map(a => ` '${a}'`).join(); + } + } + + if (processState === ProcessState.KilledDuringLaunch) { + if (commandLine) { + message = nls.localize('launchFailed.exitCodeAndCommandLine', "The terminal process \"{0}\" failed to launch (exit code: {1}).", commandLine, code); + } else { + message = nls.localize('launchFailed.exitCodeOnly', "The terminal process failed to launch (exit code: {0}).", code); + } + } else { + if (commandLine) { + message = nls.localize('terminated.exitCodeAndCommandLine', "The terminal process \"{0}\" terminated with exit code: {1}.", commandLine, code); + } else { + message = nls.localize('terminated.exitCodeOnly', "The terminal process terminated with exit code: {0}.", code); + } + } + break; + case 'object': + // Ignore internal errors + if (exitCodeOrError.message.toString().includes('Could not find pty with id')) { + break; + } + // Convert conpty code-based failures into human friendly messages + let innerMessage = exitCodeOrError.message; + const conptyError = exitCodeOrError.message.match(/.*error code:\s*(\d+).*$/); + if (conptyError) { + const errorCode = conptyError.length > 1 ? parseInt(conptyError[1]) : undefined; + switch (errorCode) { + case 5: + innerMessage = `Access was denied to the path containing your executable ${shellLaunchConfig.executable}. Manage and change your permissions to get this to work`; + break; + case 267: + innerMessage = `Invalid starting directory ${initialCwd}, review your terminal.integrated.cwd setting`; + break; + case 1260: + innerMessage = `Windows cannot open this program because it has been prevented by a software restriction policy. For more information, open Event Viewer or contact your system Administrator`; + break; + } + } + message = nls.localize('launchFailed.errorMessage', "The terminal process failed to launch: {0}.", innerMessage); + break; + } + + return { code, message }; +} diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts index 36b9db9382f..79f5a8cdd68 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { strictEqual } from 'assert'; +import { deepStrictEqual, strictEqual } from 'assert'; import { isWindows } from 'vs/base/common/platform'; -import { TerminalLabelComputer } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; +import { TerminalLabelComputer, parseExitResult } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -15,6 +15,7 @@ import { fixPath, getUri } from 'vs/workbench/contrib/search/test/browser/queryB import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ProcessState } from 'vs/workbench/contrib/terminal/common/terminal'; import { basename } from 'vs/base/common/path'; function createInstance(partial?: Partial): Pick { @@ -40,7 +41,113 @@ const ROOT_2 = fixPath(root2); const emptyRoot = '/foo'; const ROOT_EMPTY = fixPath(emptyRoot); suite('Workbench - TerminalInstance', () => { - suite('refreshLabel', () => { + suite.only('parseExitResult', () => { + test('should return no message for exit code = undefined', () => { + deepStrictEqual( + parseExitResult(undefined, {}, ProcessState.KilledDuringLaunch, undefined), + { code: undefined, message: undefined } + ); + deepStrictEqual( + parseExitResult(undefined, {}, ProcessState.KilledByUser, undefined), + { code: undefined, message: undefined } + ); + deepStrictEqual( + parseExitResult(undefined, {}, ProcessState.KilledByProcess, undefined), + { code: undefined, message: undefined } + ); + }); + test('should return no message for exit code = 0', () => { + deepStrictEqual( + parseExitResult(0, {}, ProcessState.KilledDuringLaunch, undefined), + { code: 0, message: undefined } + ); + deepStrictEqual( + parseExitResult(0, {}, ProcessState.KilledByUser, undefined), + { code: 0, message: undefined } + ); + deepStrictEqual( + parseExitResult(0, {}, ProcessState.KilledDuringLaunch, undefined), + { code: 0, message: undefined } + ); + }); + test('should return friendly message when executable is specified for non-zero exit codes', () => { + deepStrictEqual( + parseExitResult(1, { executable: 'foo' }, ProcessState.KilledDuringLaunch, undefined), + { code: 1, message: 'The terminal process "foo" failed to launch (exit code: 1).' } + ); + deepStrictEqual( + parseExitResult(1, { executable: 'foo' }, ProcessState.KilledByUser, undefined), + { code: 1, message: 'The terminal process "foo" terminated with exit code: 1.' } + ); + deepStrictEqual( + parseExitResult(1, { executable: 'foo' }, ProcessState.KilledByProcess, undefined), + { code: 1, message: 'The terminal process "foo" terminated with exit code: 1.' } + ); + }); + test('should return friendly message when executable and args are specified for non-zero exit codes', () => { + deepStrictEqual( + parseExitResult(1, { executable: 'foo', args: ['bar', 'baz'] }, ProcessState.KilledDuringLaunch, undefined), + { code: 1, message: `The terminal process "foo 'bar', 'baz'" failed to launch (exit code: 1).` } + ); + deepStrictEqual( + parseExitResult(1, { executable: 'foo', args: ['bar', 'baz'] }, ProcessState.KilledByUser, undefined), + { code: 1, message: `The terminal process "foo 'bar', 'baz'" terminated with exit code: 1.` } + ); + deepStrictEqual( + parseExitResult(1, { executable: 'foo', args: ['bar', 'baz'] }, ProcessState.KilledByProcess, undefined), + { code: 1, message: `The terminal process "foo 'bar', 'baz'" terminated with exit code: 1.` } + ); + }); + test('should return friendly message when executable and arguments are omitted for non-zero exit codes', () => { + deepStrictEqual( + parseExitResult(1, {}, ProcessState.KilledDuringLaunch, undefined), + { code: 1, message: `The terminal process failed to launch (exit code: 1).` } + ); + deepStrictEqual( + parseExitResult(1, {}, ProcessState.KilledByUser, undefined), + { code: 1, message: `The terminal process terminated with exit code: 1.` } + ); + deepStrictEqual( + parseExitResult(1, {}, ProcessState.KilledByProcess, undefined), + { code: 1, message: `The terminal process terminated with exit code: 1.` } + ); + }); + test('should ignore pty host-related errors', () => { + deepStrictEqual( + parseExitResult({ message: 'Could not find pty with id 16' }, {}, ProcessState.KilledDuringLaunch, undefined), + { code: undefined, message: undefined } + ); + }); + test('should format conpty failure code 5', () => { + deepStrictEqual( + parseExitResult({ code: 5, message: 'A native exception occurred during launch (Cannot create process, error code: 5)' }, { executable: 'foo' }, ProcessState.KilledDuringLaunch, undefined), + { code: 5, message: `The terminal process failed to launch: Access was denied to the path containing your executable foo. Manage and change your permissions to get this to work.` } + ); + }); + test('should format conpty failure code 267', () => { + deepStrictEqual( + parseExitResult({ code: 267, message: 'A native exception occurred during launch (Cannot create process, error code: 267)' }, {}, ProcessState.KilledDuringLaunch, '/foo'), + { code: 267, message: `The terminal process failed to launch: Invalid starting directory /foo, review your terminal.integrated.cwd setting.` } + ); + }); + test('should format conpty failure code 1260', () => { + deepStrictEqual( + parseExitResult({ code: 1260, message: 'A native exception occurred during launch (Cannot create process, error code: 1260)' }, { executable: 'foo' }, ProcessState.KilledDuringLaunch, undefined), + { code: 1260, message: `The terminal process failed to launch: Windows cannot open this program because it has been prevented by a software restriction policy. For more information, open Event Viewer or contact your system Administrator.` } + ); + }); + test('should format generic failures', () => { + deepStrictEqual( + parseExitResult({ code: 123, message: 'A native exception occurred during launch (Cannot create process, error code: 123)' }, {}, ProcessState.KilledDuringLaunch, undefined), + { code: 123, message: `The terminal process failed to launch: A native exception occurred during launch (Cannot create process, error code: 123).` } + ); + deepStrictEqual( + parseExitResult({ code: 123, message: 'foo' }, {}, ProcessState.KilledDuringLaunch, undefined), + { code: 123, message: `The terminal process failed to launch: foo.` } + ); + }); + }); + suite('TerminalLabelComputer', () => { let configurationService: TestConfigurationService; let terminalLabelComputer: TerminalLabelComputer; let instantiationService: TestInstantiationService; From 547da02d14091e639a99488a7cef1e0fc056edb8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 13:57:17 -0800 Subject: [PATCH 064/330] Polish exit code messages Fixes #136061 --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 4 ++-- .../contrib/terminal/test/browser/terminalInstance.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index f278281d29f..1d88836d643 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -2150,10 +2150,10 @@ export function parseExitResult( const errorCode = conptyError.length > 1 ? parseInt(conptyError[1]) : undefined; switch (errorCode) { case 5: - innerMessage = `Access was denied to the path containing your executable ${shellLaunchConfig.executable}. Manage and change your permissions to get this to work`; + innerMessage = `Access was denied to the path containing your executable "${shellLaunchConfig.executable}". Manage and change your permissions to get this to work`; break; case 267: - innerMessage = `Invalid starting directory ${initialCwd}, review your terminal.integrated.cwd setting`; + innerMessage = `Invalid starting directory "${initialCwd}", review your terminal.integrated.cwd setting`; break; case 1260: innerMessage = `Windows cannot open this program because it has been prevented by a software restriction policy. For more information, open Event Viewer or contact your system Administrator`; diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts index 79f5a8cdd68..5c89110fd99 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -121,13 +121,13 @@ suite('Workbench - TerminalInstance', () => { test('should format conpty failure code 5', () => { deepStrictEqual( parseExitResult({ code: 5, message: 'A native exception occurred during launch (Cannot create process, error code: 5)' }, { executable: 'foo' }, ProcessState.KilledDuringLaunch, undefined), - { code: 5, message: `The terminal process failed to launch: Access was denied to the path containing your executable foo. Manage and change your permissions to get this to work.` } + { code: 5, message: `The terminal process failed to launch: Access was denied to the path containing your executable "foo". Manage and change your permissions to get this to work.` } ); }); test('should format conpty failure code 267', () => { deepStrictEqual( parseExitResult({ code: 267, message: 'A native exception occurred during launch (Cannot create process, error code: 267)' }, {}, ProcessState.KilledDuringLaunch, '/foo'), - { code: 267, message: `The terminal process failed to launch: Invalid starting directory /foo, review your terminal.integrated.cwd setting.` } + { code: 267, message: `The terminal process failed to launch: Invalid starting directory "/foo", review your terminal.integrated.cwd setting.` } ); }); test('should format conpty failure code 1260', () => { From 08fed964638d5e1723c5acac92983aa288c4aacb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:01:27 -0800 Subject: [PATCH 065/330] Remove .only --- .../contrib/terminal/test/browser/terminalInstance.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts index 5c89110fd99..1db307b4eb5 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -41,7 +41,7 @@ const ROOT_2 = fixPath(root2); const emptyRoot = '/foo'; const ROOT_EMPTY = fixPath(emptyRoot); suite('Workbench - TerminalInstance', () => { - suite.only('parseExitResult', () => { + suite('parseExitResult', () => { test('should return no message for exit code = undefined', () => { deepStrictEqual( parseExitResult(undefined, {}, ProcessState.KilledDuringLaunch, undefined), From e0563d8dd9afe110e3a1451a4cef741bcce5b233 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 12 Nov 2021 14:02:26 -0800 Subject: [PATCH 066/330] bump node-pty versions --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 6755ff90e58..93cb37521a2 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "native-is-elevated": "0.4.3", "native-keymap": "3.0.1", "native-watchdog": "1.3.0", - "node-pty": "0.11.0-beta7", + "node-pty": "0.11.0-beta11", "spdlog": "^0.13.0", "sudo-prompt": "9.2.1", "tas-client-umd": "0.1.4", diff --git a/remote/package.json b/remote/package.json index 501827c3f79..fa4023a632f 100644 --- a/remote/package.json +++ b/remote/package.json @@ -15,7 +15,7 @@ "jschardet": "3.0.0", "minimist": "^1.2.5", "native-watchdog": "1.3.0", - "node-pty": "0.11.0-beta7", + "node-pty": "0.11.0-beta11", "spdlog": "^0.13.0", "tas-client-umd": "0.1.4", "vscode-nsfw": "2.1.8", diff --git a/remote/yarn.lock b/remote/yarn.lock index 6eb9ea16e1b..739ac1d8d47 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -394,10 +394,10 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -node-pty@0.11.0-beta7: - version "0.11.0-beta7" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta7.tgz#aed0888b5032d96c54d8473455e6adfae3bbebbe" - integrity sha512-uApPGLglZRiHQcUMWakbZOrBo8HVWvhzIqNnrWvBGJOvc6m/S5lCdbbg93BURyJqHFmBS0GV+4hwiMNDuGRbSA== +node-pty@0.11.0-beta11: + version "0.11.0-beta11" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta11.tgz#10843516868129c26a97253903c46fe0e4520eb0" + integrity sha512-Gw58duqHle4k/BunssCE1CUKKWipRQZTUFhaTegkKC19fw3IXsvillblLUuD2bQL42+3mQCAFSgTDo+OsJzYCQ== dependencies: nan "^2.14.0" diff --git a/yarn.lock b/yarn.lock index 06debad293e..e982c9bfd6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6977,10 +6977,10 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-pty@0.11.0-beta7: - version "0.11.0-beta7" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta7.tgz#aed0888b5032d96c54d8473455e6adfae3bbebbe" - integrity sha512-uApPGLglZRiHQcUMWakbZOrBo8HVWvhzIqNnrWvBGJOvc6m/S5lCdbbg93BURyJqHFmBS0GV+4hwiMNDuGRbSA== +node-pty@0.11.0-beta11: + version "0.11.0-beta11" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta11.tgz#10843516868129c26a97253903c46fe0e4520eb0" + integrity sha512-Gw58duqHle4k/BunssCE1CUKKWipRQZTUFhaTegkKC19fw3IXsvillblLUuD2bQL42+3mQCAFSgTDo+OsJzYCQ== dependencies: nan "^2.14.0" From 0a4fa73a96db37fce128cc705a0598d7abae126c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 12 Nov 2021 14:09:38 -0800 Subject: [PATCH 067/330] webview focus --- .../contrib/notebook/browser/notebookEditorWidget.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 545f7eec1ff..03f5b88df01 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1296,11 +1296,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._localStore.add(this._webview.webview.onDidFocus(() => { this._outputFocus.set(true); this.updateEditorFocus(); - this._onDidFocusEmitter.fire(); - - if (this._overlayContainer.contains(document.activeElement)) { - this._webviewFocused = true; - } + this._webviewFocused = true; })); this._localStore.add(this._webview.onMessage(e => { From 2730c200e62ae2407e1b18d8c39ca56bba7b6a74 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 12 Nov 2021 14:18:39 -0800 Subject: [PATCH 068/330] bump vscode-windows-registry version --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 93cb37521a2..87f09e9fe95 100644 --- a/package.json +++ b/package.json @@ -220,7 +220,7 @@ "url": "https://github.com/microsoft/vscode/issues" }, "optionalDependencies": { - "vscode-windows-registry": "1.0.3", + "vscode-windows-registry": "1.0.4", "windows-foreground-love": "0.4.0", "windows-mutex": "0.4.1", "windows-process-tree": "^0.3.2" diff --git a/remote/package.json b/remote/package.json index fa4023a632f..0f1775b847f 100644 --- a/remote/package.json +++ b/remote/package.json @@ -34,7 +34,7 @@ "yazl": "^2.4.3" }, "optionalDependencies": { - "vscode-windows-registry": "1.0.3", + "vscode-windows-registry": "1.0.4", "windows-process-tree": "^0.3.2" } } diff --git a/remote/yarn.lock b/remote/yarn.lock index 739ac1d8d47..491da3f73ae 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -524,10 +524,10 @@ vscode-windows-ca-certs@^0.3.0: dependencies: node-addon-api "^3.0.2" -vscode-windows-registry@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.3.tgz#377e9a8bf75c0acac81a188282a4f16f748ecd47" - integrity sha512-IXCwNAm+H5yPCn6JBz89T9AAMgy5xEN2LxbxrvHPlErmyQqCYtpCCjvisfgT2dCuaJ2T9FfiqIeIrOpDm2Jc4Q== +vscode-windows-registry@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.4.tgz#9e565a497c84b6b82d200f88930baeff12912079" + integrity sha512-vjYaMzEygZrb8bN6I33XTajpF/gtDOk5CtQPPSaxanXg2kkrerEM9qovY6t6FtBGl3oLq6YHytYdYw4IpXgJdA== windows-process-tree@^0.3.2: version "0.3.2" diff --git a/yarn.lock b/yarn.lock index e982c9bfd6a..9a633edb20c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10488,10 +10488,10 @@ vscode-windows-ca-certs@^0.3.0: dependencies: node-addon-api "^3.0.2" -vscode-windows-registry@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.3.tgz#377e9a8bf75c0acac81a188282a4f16f748ecd47" - integrity sha512-IXCwNAm+H5yPCn6JBz89T9AAMgy5xEN2LxbxrvHPlErmyQqCYtpCCjvisfgT2dCuaJ2T9FfiqIeIrOpDm2Jc4Q== +vscode-windows-registry@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.4.tgz#9e565a497c84b6b82d200f88930baeff12912079" + integrity sha512-vjYaMzEygZrb8bN6I33XTajpF/gtDOk5CtQPPSaxanXg2kkrerEM9qovY6t6FtBGl3oLq6YHytYdYw4IpXgJdA== watchpack-chokidar2@^2.0.1: version "2.0.1" From d30cabb5314d52a2748fedde346de68b5652af8e Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 12 Nov 2021 14:22:47 -0800 Subject: [PATCH 069/330] reduce redundant focus update. --- src/vs/workbench/contrib/notebook/browser/notebookEditor.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index b030a953c2f..7c72213a626 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -117,10 +117,6 @@ export class NotebookEditor extends EditorPane { protected createEditor(parent: HTMLElement): void { this._rootElement = DOM.append(parent, DOM.$('.notebook-editor')); - - // this._widget.createEditor(); - this._register(this.onDidFocus(() => this._widget.value?.updateEditorFocus())); - this._register(this.onDidBlur(() => this._widget.value?.updateEditorFocus())); } getDomNode() { From b9777fbaab78fbc2ce523dabc1810e80ed7bb3c1 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 12 Nov 2021 14:24:28 -0800 Subject: [PATCH 070/330] bump distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 87f09e9fe95..baed6e0bbb4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "4402f943794631d3a9a9f3dbdfaa5d855ddfe575", + "distro": "2b5b37909854357e3218c1c9677e8eb81f1a898b", "author": { "name": "Microsoft Corporation" }, From bf08f79130e34c475fac7dd3146f8a2923db215d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:29:41 -0800 Subject: [PATCH 071/330] Move terminal label register into a contribution Fixes #136057 --- .../terminal/browser/terminal.contribution.ts | 2 ++ .../browser/terminalMainContribution.ts | 28 +++++++++++++++++++ .../terminal/browser/terminalService.ts | 11 -------- 3 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 79f80459435..e5653c210f0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -48,6 +48,7 @@ import { TerminalProfileService } from 'vs/workbench/contrib/terminal/browser/te import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { RemoteTerminalBackendContribution } from 'vs/workbench/contrib/terminal/browser/remoteTerminalBackend'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { TerminalMainContribution } from 'vs/workbench/contrib/terminal/browser/terminalMainContribution'; // Register services registerSingleton(ITerminalService, TerminalService, true); @@ -73,6 +74,7 @@ CommandsRegistry.registerCommand({ id: quickAccessNavigatePreviousInTerminalPick // Register workbench contributions const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(TerminalMainContribution, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(RemoteTerminalBackendContribution, LifecyclePhase.Starting); // Register configurations diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts new file mode 100644 index 00000000000..1cb3227614d --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Schemas } from 'vs/base/common/network'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; + +/** + * The main contribution for the terminal contrib. This contains calls to other components necessary + * to set up the terminal but don't need to be tracked in the long term (where TerminalService would + * be more relevant). + */ +export class TerminalMainContribution implements IWorkbenchContribution { + constructor( + @ILabelService labelService: ILabelService + ) { + // Register a resource formatter for terminal URIs + labelService.registerFormatter({ + scheme: Schemas.vscodeTerminal, + formatting: { + label: '${path}', + separator: '' + } + }); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index ef444259b8a..2f2336157f4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -17,7 +17,6 @@ import * as nls from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ILabelService } from 'vs/platform/label/common/label'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString } from 'vs/platform/terminal/common/terminal'; import { iconForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -138,7 +137,6 @@ export class TerminalService implements ITerminalService { constructor( @IContextKeyService private _contextKeyService: IContextKeyService, - @ILabelService labelService: ILabelService, @ILifecycleService lifecycleService: ILifecycleService, @IDialogService private _dialogService: IDialogService, @IInstantiationService private _instantiationService: IInstantiationService, @@ -223,15 +221,6 @@ export class TerminalService implements ITerminalService { lifecycleService.onBeforeShutdown(async e => e.veto(this._onBeforeShutdown(e.reason), 'veto.terminal')); lifecycleService.onWillShutdown(e => this._onWillShutdown(e)); - // Register a resource formatter for terminal URIs - labelService.registerFormatter({ - scheme: Schemas.vscodeTerminal, - formatting: { - label: '${path}', - separator: '' - } - }); - // Create async as the class depends on `this` timeout(0).then(() => this._instantiationService.createInstance(TerminalEditorStyle, document.head)); } From ad88550b9e6f27259bece5cf70570ddb9aa8693c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 12 Nov 2021 14:34:20 -0800 Subject: [PATCH 072/330] main thread nb listen to nbEditor.onDidFocus. --- .../api/browser/mainThreadNotebookDocumentsAndEditors.ts | 2 +- src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts | 3 ++- .../contrib/notebook/browser/notebookEditorWidget.ts | 5 ----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts index ad86a0d4ea6..49f628b43ca 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts @@ -131,7 +131,7 @@ export class MainThreadNotebooksAndEditors { private _handleEditorAdd(editor: INotebookEditor): void { this._editorListeners.set(editor.getId(), combinedDisposable( editor.onDidChangeModel(() => this._updateState()), - editor.onDidFocusEditorWidget(() => this._updateState(editor)), + editor.onDidFocus(() => this._updateState(editor)), )); this._updateState(); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 451f08fc484..e8bd95e1c50 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -398,7 +398,8 @@ export interface INotebookEditor { * An event emitted when the model of this editor has changed. */ readonly onDidChangeModel: Event; - readonly onDidFocusEditorWidget: Event; + readonly onDidFocus: Event; + readonly onDidBlur: Event; readonly onDidScroll: Event; readonly onDidChangeActiveCell: Event; readonly onDidChangeActiveKernel: Event; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 03f5b88df01..dd10b1b3892 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -275,8 +275,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; private readonly _onDidChangeVisibleRanges = this._register(new Emitter()); readonly onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; - private readonly _onDidFocusEditorWidget = this._register(new Emitter()); - readonly onDidFocusEditorWidget = this._onDidFocusEditorWidget.event; private readonly _onDidFocusEmitter = this._register(new Emitter()); readonly onDidFocus = this._onDidFocusEmitter.event; private readonly _onDidBlurEmitter = this._register(new Emitter()); @@ -1765,15 +1763,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD if (element && element.focusMode === CellFocusMode.Editor) { element.updateEditState(CellEditState.Editing, 'editorWidget.focus'); element.focusMode = CellFocusMode.Editor; - this._onDidFocusEditorWidget.fire(); return; } } this._list.domFocus(); } - - this._onDidFocusEditorWidget.fire(); } focusContainer() { From 8fdb5bc4e67aeba3587f2c7017663a7dcfdca69c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 12 Nov 2021 14:35:50 -0800 Subject: [PATCH 073/330] differenciate onFocusWidget and onFocusWebview. --- .../api/browser/mainThreadNotebookDocumentsAndEditors.ts | 2 +- .../contrib/interactive/browser/interactiveEditor.ts | 2 +- src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts | 4 ++-- src/vs/workbench/contrib/notebook/browser/notebookEditor.ts | 4 ++-- .../contrib/notebook/browser/notebookEditorWidget.ts | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts index 49f628b43ca..67ace55d95a 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts @@ -131,7 +131,7 @@ export class MainThreadNotebooksAndEditors { private _handleEditorAdd(editor: INotebookEditor): void { this._editorListeners.set(editor.getId(), combinedDisposable( editor.onDidChangeModel(() => this._updateState()), - editor.onDidFocus(() => this._updateState(editor)), + editor.onDidFocusWidget(() => this._updateState(editor)), )); this._updateState(); } diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index 3117b72ae95..e5ec850f34f 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -322,7 +322,7 @@ export class InteractiveEditor extends EditorPane { this.#notebookWidget.value!.setOptions({ isReadOnly: true }); - this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidFocus(() => this.#onDidFocusWidget.fire())); + this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidFocusWidget(() => this.#onDidFocusWidget.fire())); this.#widgetDisposableStore.add(model.notebook.onDidChangeContent(() => { (model as ComplexNotebookEditorModel).setDirty(false); })); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index e8bd95e1c50..8e0c1b86f36 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -398,8 +398,8 @@ export interface INotebookEditor { * An event emitted when the model of this editor has changed. */ readonly onDidChangeModel: Event; - readonly onDidFocus: Event; - readonly onDidBlur: Event; + readonly onDidFocusWidget: Event; + readonly onDidBlurWidget: Event; readonly onDidScroll: Event; readonly onDidChangeActiveCell: Event; readonly onDidChangeActiveKernel: Event; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 7c72213a626..31e939f76cb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -225,8 +225,8 @@ export class NotebookEditor extends EditorPane { await this._widget.value!.setModel(model.notebook, viewState); const isReadOnly = input.hasCapability(EditorInputCapabilities.Readonly); await this._widget.value!.setOptions({ ...options, isReadOnly }); - this._widgetDisposableStore.add(this._widget.value!.onDidFocus(() => this._onDidFocusWidget.fire())); - this._widgetDisposableStore.add(this._widget.value!.onDidBlur(() => this._onDidBlurWidget.fire())); + this._widgetDisposableStore.add(this._widget.value!.onDidFocusWidget(() => this._onDidFocusWidget.fire())); + this._widgetDisposableStore.add(this._widget.value!.onDidBlurWidget(() => this._onDidBlurWidget.fire())); this._widgetDisposableStore.add(this._editorDropService.createEditorDropTarget(this._widget.value!.getDomNode(), { containsGroup: (group) => this.group?.id === group.id diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index dd10b1b3892..8d165b28b81 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -276,9 +276,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private readonly _onDidChangeVisibleRanges = this._register(new Emitter()); readonly onDidChangeVisibleRanges: Event = this._onDidChangeVisibleRanges.event; private readonly _onDidFocusEmitter = this._register(new Emitter()); - readonly onDidFocus = this._onDidFocusEmitter.event; + readonly onDidFocusWidget = this._onDidFocusEmitter.event; private readonly _onDidBlurEmitter = this._register(new Emitter()); - readonly onDidBlur = this._onDidBlurEmitter.event; + readonly onDidBlurWidget = this._onDidBlurEmitter.event; private readonly _onDidChangeActiveEditor = this._register(new Emitter()); readonly onDidChangeActiveEditor: Event = this._onDidChangeActiveEditor.event; private readonly _onDidChangeActiveKernel = this._register(new Emitter()); From de69c40e1e8e05ccb8b09b478ac58cd0c2524c6e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:37:22 -0800 Subject: [PATCH 074/330] Move terminal editor registration to contribution Fixes #136058 --- .../browser/terminalMainContribution.ts | 45 ++++++++++++++++++- .../terminal/browser/terminalService.ts | 37 +-------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts index 1cb3227614d..d58f8bf21ba 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -6,6 +6,10 @@ import { Schemas } from 'vs/base/common/network'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { ITerminalEditorService, ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalEditor } from 'vs/workbench/contrib/terminal/browser/terminalEditor'; +import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; +import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; /** * The main contribution for the terminal contrib. This contains calls to other components necessary @@ -14,8 +18,47 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; */ export class TerminalMainContribution implements IWorkbenchContribution { constructor( - @ILabelService labelService: ILabelService + @IEditorResolverService editorResolverService: IEditorResolverService, + @ILabelService labelService: ILabelService, + @ITerminalService terminalService: ITerminalService, + @ITerminalEditorService terminalEditorService: ITerminalEditorService, + @ITerminalGroupService terminalGroupService: ITerminalGroupService ) { + // Register terminal editors + editorResolverService.registerEditor( + `${Schemas.vscodeTerminal}:/**`, + { + id: TerminalEditor.ID, + label: terminalStrings.terminal, + priority: RegisteredEditorPriority.exclusive + }, + { + canHandleDiff: false, + canSupportResource: uri => uri.scheme === Schemas.vscodeTerminal, + singlePerResource: true + }, + ({ resource, options }) => { + let instance = terminalService.getInstanceFromResource(resource); + if (instance) { + const sourceGroup = terminalGroupService.getGroupForInstance(instance); + if (sourceGroup) { + sourceGroup.removeInstance(instance); + } + } + const resolvedResource = terminalEditorService.resolveResource(instance || resource); + const editor = terminalEditorService.getInputFromResource(resolvedResource) || { editor: resolvedResource }; + return { + editor, + options: { + ...options, + pinned: true, + forceReload: true, + override: TerminalEditor.ID + } + }; + } + ); + // Register a resource formatter for terminal URIs labelService.registerFormatter({ scheme: Schemas.vscodeTerminal, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 2f2336157f4..d3a06a22d08 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -27,14 +27,12 @@ import { VirtualWorkspaceContext } from 'vs/workbench/browser/contextkeys'; import { IEditableData, IViewsService } from 'vs/workbench/common/views'; import { ICreateTerminalOptions, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalExternalLinkProvider, ITerminalFindHost, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; -import { TerminalEditor } from 'vs/workbench/contrib/terminal/browser/terminalEditor'; import { getColorStyleContent, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { getInstanceFromResource, getTerminalUri, parseTerminalUri } from 'vs/workbench/contrib/terminal/browser/terminalUri'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalBackend, ITerminalProcessExtHostProxy, ITerminalProfileService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { formatMessageForTerminal, terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; -import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ILifecycleService, ShutdownReason, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -147,7 +145,6 @@ export class TerminalService implements ITerminalService { @ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService, @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, - @IEditorResolverService editorResolverService: IEditorResolverService, @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, @IExtensionService private readonly _extensionService: IExtensionService, @@ -156,38 +153,6 @@ export class TerminalService implements ITerminalService { this._isShuttingDown = false; this._findState = new FindReplaceState(); this._configHelper = _instantiationService.createInstance(TerminalConfigHelper); - editorResolverService.registerEditor( - `${Schemas.vscodeTerminal}:/**`, - { - id: TerminalEditor.ID, - label: terminalStrings.terminal, - priority: RegisteredEditorPriority.exclusive - }, - { - canHandleDiff: false, - canSupportResource: uri => uri.scheme === Schemas.vscodeTerminal, - singlePerResource: true - }, - ({ resource, options }) => { - let instance = this.getInstanceFromResource(resource); - if (instance) { - const sourceGroup = this._terminalGroupService.getGroupForInstance(instance); - if (sourceGroup) { - sourceGroup.removeInstance(instance); - } - } - const resolvedResource = this._terminalEditorService.resolveResource(instance || resource); - const editor = this._terminalEditorService.getInputFromResource(resolvedResource) || { editor: resolvedResource }; - return { - editor, - options: { - ...options, - pinned: true, - forceReload: true, - override: TerminalEditor.ID - } - }; - }); // the below avoids having to poll routinely. // we update detected profiles when an instance is created so that, // for example, we detect if you've installed a pwsh From 3496200d272c98b95d0ab0832f17b59f26032e39 Mon Sep 17 00:00:00 2001 From: tanhakabir Date: Fri, 12 Nov 2021 14:48:11 -0800 Subject: [PATCH 075/330] Clean up port range regex --- src/vs/server/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/server/main.js b/src/vs/server/main.js index b35e734f5e7..37c7dab5a78 100644 --- a/src/vs/server/main.js +++ b/src/vs/server/main.js @@ -156,7 +156,7 @@ async function parsePort(strPort, strPickPort) { } if (strPickPort) { - if (strPickPort.match(/[\d]+-[\d]+/)) { + if (strPickPort.match(/^\d+-\d+$/)) { const [start, end] = strPickPort.split('-').map(numStr => { return parseInt(numStr, 10); }); if (!isNaN(start) && !isNaN(end)) { From ca58cb12bc0ddc525dea35bce886fdf6aa4c8662 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Sat, 13 Nov 2021 15:02:41 -0800 Subject: [PATCH 076/330] remove unnecessary check for monaco-editor class in qp focus handling --- src/vs/base/parts/quickinput/browser/quickInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 6b84d0aa6c3..4a9a2db8cd2 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1687,7 +1687,7 @@ export class QuickInputController extends Disposable { while (currentElement && !currentElement.offsetParent) { currentElement = withNullAsUndefined(currentElement.parentElement); } - if (currentElement?.offsetParent && !currentElement.parentElement?.classList.contains('monaco-editor')) { + if (currentElement?.offsetParent) { currentElement.focus(); this.previousFocusElement = undefined; } else { From 049e0eafa31513b21095d0ed6220522e78911911 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 14 Nov 2021 13:58:04 -0800 Subject: [PATCH 077/330] Fix running "execute above/below" commands from command palette Fix #136766 --- .../contrib/notebook/browser/controller/executeActions.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index c55e7d9928c..cc3c11bfc70 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { maxIndex, minIndex } from 'vs/base/common/arrays'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -17,7 +16,7 @@ import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cel import { cellExecutionArgs, CellToolbarOrder, CELL_TITLE_CELL_GROUP_ID, executeNotebookCondition, getContextFromActiveEditor, getContextFromUri, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NotebookAction, NotebookCellAction, NotebookMultiCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, parseMultiCellExecutionArgs } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_MISSING_KERNEL_EXTENSION } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { CellKind, NotebookSetting, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellExecutionState, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -231,7 +230,7 @@ registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction { if (context.ui) { endCellIdx = context.notebookEditor.getCellIndex(context.cell); } else { - endCellIdx = maxIndex(context.selectedCells, cell => context.notebookEditor.getCellIndex(cell)); + endCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell))); } if (typeof endCellIdx === 'number') { @@ -277,7 +276,7 @@ registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction { if (context.ui) { startCellIdx = context.notebookEditor.getCellIndex(context.cell); } else { - startCellIdx = minIndex(context.selectedCells, cell => context.notebookEditor.getCellIndex(cell)); + startCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell))); } if (typeof startCellIdx === 'number') { From db3763b5f4bd06f163b78a8456f860cc68682655 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 14 Nov 2021 21:59:58 -0800 Subject: [PATCH 078/330] update focus tracking doc. --- .../workbench/contrib/notebook/browser/notebook.layout.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index f48565ab373..256d646208a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -85,3 +85,10 @@ Due to the nature of virtualization (list view) and two layers architecture, the * Toolbars The catch here is if the focus is on a monaco editor, instead of the list view container, when the cell is moved out of view, the list view removes the cell row from the DOM tree. The `document.activeElement` will fall back `document.body` when that happens. To ensure that the notebook editor doesn't blur, we need to move focus back to list view container when the focused cell is moved out of view. More importantly, focus the cell editor again when the cell is visible again (if the cell is still the *active* cell). + +Copy in Notebook depends on the focus tracking + +* Send `document.executeCommand('copy')` if users select text in output rendered in main frame by builtin renderer +* Request webview copy if the focus is inside the webview +* Copy cells if the focus is on notebook cell list +* Copy text if the focus is in cell editor (monaco editor) From eecabfd9aada5c1347869a762b3a7d0fa8ad5f6a Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 14 Nov 2021 22:08:37 -0800 Subject: [PATCH 079/330] update diagram --- src/vs/workbench/contrib/notebook/browser/notebook.layout.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index 256d646208a..8737513ebaa 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -92,3 +92,5 @@ Copy in Notebook depends on the focus tracking * Request webview copy if the focus is inside the webview * Copy cells if the focus is on notebook cell list * Copy text if the focus is in cell editor (monaco editor) + +![diagram](https://user-images.githubusercontent.com/876920/141730905-2818043e-1a84-45d3-ad27-83bd89235ca5.png) From 4fb384ee8530661fc4ae50780eba49e0a4be7d39 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Nov 2021 07:43:55 +0100 Subject: [PATCH 080/330] :up: @parcel/watcher@2.0.2 --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index baed6e0bbb4..aac27a97165 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ }, "dependencies": { "@microsoft/applicationinsights-web": "^2.6.4", - "@parcel/watcher": "2.0.1", + "@parcel/watcher": "2.0.2", "@vscode/sqlite3": "4.0.12", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", diff --git a/remote/package.json b/remote/package.json index 0f1775b847f..052e6fe6407 100644 --- a/remote/package.json +++ b/remote/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@microsoft/applicationinsights-web": "^2.6.4", - "@parcel/watcher": "2.0.1", + "@parcel/watcher": "2.0.2", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", "cookie": "^0.4.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index 491da3f73ae..9b8fc183fc1 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -83,10 +83,10 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.4.tgz#40e1c0ad20743fcee1604a7df2c57faf0aa1af87" integrity sha512-Ot53G927ykMF8cQ3/zq4foZtdk+Tt1YpX7aUTHxBU7UHNdkEiBvBfZSq+rnlUmKCJ19VatwPG4mNzvcGpBj4og== -"@parcel/watcher@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.1.tgz#ec4bb6c43d9588a1ffd3d2abe6df5b501463c62d" - integrity sha512-XegFF4L8sFn1RzU5KKOZxXUuzgOSwd6+X2ez3Cy6MVhYMbiLZ1moceMTqDhuT3N8DNbdumK3zP1wojsIsnX40w== +"@parcel/watcher@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.2.tgz#46bef14584497147bad5247cfb41f80b24d24dfb" + integrity sha512-WGJY55/mTAGse2C9VVi2oo+p05oJ0kiSHmOjV33+ywgKgUkUh6B/qFQ5kBO/9mH686qqtV3k2zH1QNm+XX4+lw== dependencies: node-addon-api "^3.2.1" node-gyp-build "^4.3.0" diff --git a/yarn.lock b/yarn.lock index 9a633edb20c..b4cd859cd2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -417,10 +417,10 @@ dependencies: "@octokit/openapi-types" "^10.2.2" -"@parcel/watcher@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.1.tgz#ec4bb6c43d9588a1ffd3d2abe6df5b501463c62d" - integrity sha512-XegFF4L8sFn1RzU5KKOZxXUuzgOSwd6+X2ez3Cy6MVhYMbiLZ1moceMTqDhuT3N8DNbdumK3zP1wojsIsnX40w== +"@parcel/watcher@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.2.tgz#46bef14584497147bad5247cfb41f80b24d24dfb" + integrity sha512-WGJY55/mTAGse2C9VVi2oo+p05oJ0kiSHmOjV33+ywgKgUkUh6B/qFQ5kBO/9mH686qqtV3k2zH1QNm+XX4+lw== dependencies: node-addon-api "^3.2.1" node-gyp-build "^4.3.0" From 6f28593438fceb1da65e4b92e1cea8f84b777a07 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Nov 2021 07:52:12 +0100 Subject: [PATCH 081/330] smoke - retry quick open search (fix #136499) --- .../node/recursiveWatcher.integrationTest.ts | 2 +- .../electron-main/lifecycleMainService.ts | 24 +++++++----- test/automation/src/quickaccess.ts | 37 +++++++++++++++++-- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts b/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts index 9237d7b6351..86252eb4c13 100644 --- a/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/recursiveWatcher.integrationTest.ts @@ -470,7 +470,7 @@ flakySuite('Recursive Watcher (parcel)', () => { // Restore watched path await Promises.mkdir(watchedPath); - await timeout(200); // restart is delayed + await timeout(1500); // restart is delayed await service.whenReady(); // Verify events come in again diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 81a02b51d86..fc9c0fe9e9a 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -117,8 +117,16 @@ export interface ILifecycleMainService { quit(willRestart?: boolean): Promise; /** - * Forcefully shutdown the application. The only lifecycle event handler - * that is triggered is `onWillShutdown`. + * Forcefully shutdown the application and optionally set an exit code. + * + * This method should only be used in rare situations where it is important + * to set an exit code (e.g. running tests) or when the application is + * not in a healthy state and should terminate asap. + * + * This method does not fire the normal lifecycle events to the windows, + * that normally can be vetoed. Windows are destroyed without a chance + * of components to participate. The only lifecycle event handler that + * is triggered is `onWillShutdown` in the main process. */ kill(code?: number): Promise; @@ -590,17 +598,13 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe async kill(code?: number): Promise { this.logService.trace('Lifecycle#kill()'); - // Start shutdown sequence + // Give main process participants a chance to oderly shutdown await this.fireOnWillShutdown(); - // The kill() method is only used in 2 situations: - // - when an instance fails to start at all - // - when extension tests run from CLI to report proper exit code - // // From extension tests we have seen issues where calling app.exit() - // with an opened window can lead to native crashes (Linux) when webviews - // are involved. As such, we should make sure to destroy any opened - // window before calling app.exit(). + // with an opened window can lead to native crashes (Linux). As such, + // we should make sure to destroy any opened window before calling + // `app.exit()`. // // Note: Electron implements a similar logic here: // https://github.com/electron/electron/blob/fe5318d753637c3903e23fc1ed1b263025887b6a/spec-main/window-helpers.ts#L5 diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts index 918d213050b..ff8131f5f81 100644 --- a/test/automation/src/quickaccess.ts +++ b/test/automation/src/quickaccess.ts @@ -40,9 +40,40 @@ export class QuickAccess { } async openFile(fileName: string): Promise { - await this.openQuickAccess(fileName); + let retries = 0; + let fileFound = false; + while (++retries < 10) { + let retry = false; + + await this.openQuickAccess(fileName); + + await this.quickInput.waitForQuickInputElements(names => { + const name = names[0]; + if (name === fileName) { + fileFound = true; + return true; + } + + if (name === 'No matching results') { + retry = true; + return true; + } + + return false; + }); + + if (!retry) { + break; + } + + await this.quickInput.closeQuickInput(); + await new Promise(c => setTimeout(c, 500)); + } + + if (!fileFound) { + throw new Error(`Quick open file search was unable to find '${fileName}' after 10 attempts, giving up.`); + } - await this.quickInput.waitForQuickInputElements(names => names[0] === fileName); await this.code.dispatchKeybinding('enter'); await this.editors.waitForActiveTab(fileName); await this.editors.waitForEditorFocus(fileName); @@ -75,7 +106,7 @@ export class QuickAccess { } await this.quickInput.closeQuickInput(); - await new Promise(c => setTimeout(c, 250)); + await new Promise(c => setTimeout(c, 500)); } } } From 4bb7826d02e0b873d180f00c68643bb0bf26966a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Nov 2021 07:55:36 +0100 Subject: [PATCH 082/330] extension dev - do not delete empty window sqlite DB --- .../contrib/storageDataCleaner.ts | 21 ++++++++++++------- .../electron-sandbox/shared.desktop.main.ts | 6 ++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index 553304dc4cc..56eb3683002 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -17,6 +17,9 @@ export class StorageDataCleaner extends Disposable { // Workspace/Folder storage names are MD5 hashes (128bits / 4 due to hex presentation) private static readonly NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; + // Reserved empty window workspace storage name when in extension development + private static readonly EXTENSION_DEV_EMPTY_WINDOW_ID = 'ext-dev'; + constructor( private readonly backupWorkspacesPath: string, @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @@ -42,18 +45,20 @@ export class StorageDataCleaner extends Disposable { const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat; const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(emptyWorkspace => emptyWorkspace.backupFolder); - // Read all workspace storage folders that exist - const storageFolders = await Promises.readdir(this.environmentService.workspaceStorageHome.fsPath); - await Promise.all(storageFolders.map(async storageFolder => { - if (storageFolder.length === StorageDataCleaner.NON_EMPTY_WORKSPACE_ID_LENGTH) { + // Read all workspace storage folders that exist & cleanup unused + const workspaceStorageFolders = await Promises.readdir(this.environmentService.workspaceStorageHome.fsPath); + await Promise.all(workspaceStorageFolders.map(async workspaceStorageFolder => { + if ( + workspaceStorageFolder.length === StorageDataCleaner.NON_EMPTY_WORKSPACE_ID_LENGTH || // keep non-empty workspaces + workspaceStorageFolder === StorageDataCleaner.EXTENSION_DEV_EMPTY_WINDOW_ID || // keep empty extension dev workspaces + emptyWorkspaces.indexOf(workspaceStorageFolder) >= 0 // keep empty workspaces that are in use + ) { return; } - if (emptyWorkspaces.indexOf(storageFolder) === -1) { - this.logService.trace(`[storage cleanup]: Deleting storage folder ${storageFolder}.`); + this.logService.trace(`[storage cleanup]: Deleting workspace storage folder ${workspaceStorageFolder}.`); - await Promises.rm(join(this.environmentService.workspaceStorageHome.fsPath, storageFolder)); - } + await Promises.rm(join(this.environmentService.workspaceStorageHome.fsPath, workspaceStorageFolder)); })); } catch (error) { onUnexpectedError(error); diff --git a/src/vs/workbench/electron-sandbox/shared.desktop.main.ts b/src/vs/workbench/electron-sandbox/shared.desktop.main.ts index 9bfb3976893..f4a7ba21f27 100644 --- a/src/vs/workbench/electron-sandbox/shared.desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/shared.desktop.main.ts @@ -317,9 +317,11 @@ export abstract class SharedDesktopMain extends Disposable { if (!workspaceInitializationPayload) { let id: string; if (this.configuration.backupPath) { - id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + // we know the backupPath must be a unique path so we leverage its name as workspace ID + id = basename(this.configuration.backupPath); } else if (environmentService.isExtensionDevelopment) { - id = 'ext-dev'; // extension development window never stores backups and is a singleton + // fallback to a reserved identifier when in extension development where backups are not stored + id = 'ext-dev'; } else { throw new Error('Unexpected window configuration without backupPath'); } From 03a844a2e5517ae69d8df8764c3e402cf8c1e7e7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 14 Nov 2021 23:39:46 -0800 Subject: [PATCH 083/330] Use async await in pty terminal test Part of #137155 --- .../src/singlefolder-tests/terminal.test.ts | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index c88fa9619d6..dafd6ca3010 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -28,7 +28,7 @@ import { assertNoRpc } from '../utils'; await config.update('environmentChangesRelaunch', false, ConfigurationTarget.Global); }); - suite('Terminal', () => { + suite.only('Terminal', () => { let disposables: Disposable[] = []; teardown(() => { @@ -491,31 +491,7 @@ import { assertNoRpc } from '../utils'; // const terminal = window.createTerminal({ name: 'foo', pty }); // }); - test('should respect dimension overrides', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - } catch (e) { - done(e); - return; - } - term.show(); - disposables.push(window.onDidChangeTerminalDimensions(e => { - // The default pty dimensions have a chance to appear here since override - // dimensions happens after the terminal is created. If so just ignore and - // wait for the right dimensions - if (e.dimensions.columns === 10 || e.dimensions.rows === 5) { - try { - equal(e.terminal, terminal); - } catch (e) { - done(e); - return; - } - disposables.push(window.onDidCloseTerminal(() => done())); - terminal.dispose(); - } - })); - })); + test('should respect dimension overrides', async () => { const writeEmitter = new EventEmitter(); const overrideDimensionsEmitter = new EventEmitter(); const pty: Pseudoterminal = { @@ -525,6 +501,27 @@ import { assertNoRpc } from '../utils'; close: () => { } }; const terminal = window.createTerminal({ name: 'foo', pty }); + await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t => { + if (t === terminal) { + r(); + } + })); + }); + await new Promise(r => { + disposables.push(window.onDidChangeTerminalDimensions(e => { + strictEqual(e.terminal, terminal); + // The default pty dimensions have a chance to appear here since override + // dimensions happens after the terminal is created. If so just ignore and + // wait for the right dimensions + console.log('onDidChangeTerminalDimensions', e); + if (e.dimensions.columns === 10 || e.dimensions.rows === 5) { + disposables.push(window.onDidCloseTerminal(() => r())); + terminal.dispose(); + } + })); + terminal.show(); + }); }); test('should change terminal name', (done) => { From b934d0934672fe975b5376551342f20eff67ca6d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 14 Nov 2021 23:53:12 -0800 Subject: [PATCH 084/330] Move other extension terminal tests to async await Part of #137155 --- .../src/singlefolder-tests/terminal.test.ts | 210 ++++++++---------- 1 file changed, 90 insertions(+), 120 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index dafd6ca3010..d7139de67a4 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -28,7 +28,7 @@ import { assertNoRpc } from '../utils'; await config.update('environmentChangesRelaunch', false, ConfigurationTarget.Global); }); - suite.only('Terminal', () => { + suite('Terminal', () => { let disposables: Disposable[] = []; teardown(() => { @@ -425,23 +425,24 @@ import { assertNoRpc } from '../utils'; }); suite('Extension pty terminals', () => { - test('should fire onDidOpenTerminal and onDidCloseTerminal', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(term.name, 'c'); - } catch (e) { - done(e); - return; - } - disposables.push(window.onDidCloseTerminal(() => done())); - term.dispose(); - })); + test('should fire onDidOpenTerminal and onDidCloseTerminal', async () => { const pty: Pseudoterminal = { onDidWrite: new EventEmitter().event, open: () => { }, close: () => { } }; - window.createTerminal({ name: 'c', pty }); + const terminal = await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t => { + if (t.name === 'c') { + r(t); + } + })); + window.createTerminal({ name: 'c', pty }); + }); + await new Promise(r => { + disposables.push(window.onDidCloseTerminal(() => r())); + terminal.dispose(); + }); }); // The below tests depend on global UI state and each other @@ -500,13 +501,13 @@ import { assertNoRpc } from '../utils'; open: () => overrideDimensionsEmitter.fire({ columns: 10, rows: 5 }), close: () => { } }; - const terminal = window.createTerminal({ name: 'foo', pty }); - await new Promise(r => { + const terminal = await new Promise(r => { disposables.push(window.onDidOpenTerminal(t => { - if (t === terminal) { - r(); + if (t === created) { + r(t); } })); + const created = window.createTerminal({ name: 'foo', pty }); }); await new Promise(r => { disposables.push(window.onDidChangeTerminalDimensions(e => { @@ -524,26 +525,7 @@ import { assertNoRpc } from '../utils'; }); }); - test('should change terminal name', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - equal(terminal.name, 'foo'); - } catch (e) { - done(e); - return; - } - disposables.push(window.onDidCloseTerminal(t => { - try { - equal(terminal, t); - equal(terminal.name, 'bar'); - } catch (e) { - done(e); - return; - } - done(); - })); - })); + test('should change terminal name', async () => { const changeNameEmitter = new EventEmitter(); const closeEmitter = new EventEmitter(); const pty: Pseudoterminal = { @@ -556,29 +538,22 @@ import { assertNoRpc } from '../utils'; }, close: () => { } }; - const terminal = window.createTerminal({ name: 'foo', pty }); + await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t1 => { + if (t1 === created) { + disposables.push(window.onDidCloseTerminal(t2 => { + if (t2 === created) { + strictEqual(t1.name, 'bar'); + r(); + } + })); + } + })); + const created = window.createTerminal({ name: 'foo', pty }); + }); }); - test('exitStatus.code should be set to the exit code (undefined)', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - equal(terminal.exitStatus, undefined); - } catch (e) { - done(e); - return; - } - disposables.push(window.onDidCloseTerminal(t => { - try { - equal(terminal, t); - deepEqual(terminal.exitStatus, { code: undefined }); - } catch (e) { - done(e); - return; - } - done(); - })); - })); + test('exitStatus.code should be set to the exit code (undefined)', async () => { const writeEmitter = new EventEmitter(); const closeEmitter = new EventEmitter(); const pty: Pseudoterminal = { @@ -587,29 +562,23 @@ import { assertNoRpc } from '../utils'; open: () => closeEmitter.fire(undefined), close: () => { } }; - const terminal = window.createTerminal({ name: 'foo', pty }); + await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t1 => { + if (t1 === created) { + strictEqual(created.exitStatus, undefined); + disposables.push(window.onDidCloseTerminal(t2 => { + if (t2 === created) { + deepStrictEqual(created.exitStatus, { code: undefined }); + r(); + } + })); + } + })); + const created = window.createTerminal({ name: 'foo', pty }); + }); }); - test('exitStatus.code should be set to the exit code (zero)', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - equal(terminal.exitStatus, undefined); - } catch (e) { - done(e); - return; - } - disposables.push(window.onDidCloseTerminal(t => { - try { - equal(terminal, t); - deepEqual(terminal.exitStatus, { code: 0 }); - } catch (e) { - done(e); - return; - } - done(); - })); - })); + test('exitStatus.code should be set to the exit code (zero)', async () => { const writeEmitter = new EventEmitter(); const closeEmitter = new EventEmitter(); const pty: Pseudoterminal = { @@ -618,29 +587,23 @@ import { assertNoRpc } from '../utils'; open: () => closeEmitter.fire(0), close: () => { } }; - const terminal = window.createTerminal({ name: 'foo', pty }); + await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t1 => { + if (t1 === created) { + strictEqual(created.exitStatus, undefined); + disposables.push(window.onDidCloseTerminal(t2 => { + if (t2 === created) { + deepStrictEqual(created.exitStatus, { code: 0 }); + r(); + } + })); + } + })); + const created = window.createTerminal({ name: 'foo', pty }); + }); }); - test('exitStatus.code should be set to the exit code (non-zero)', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - equal(terminal.exitStatus, undefined); - } catch (e) { - done(e); - return; - } - disposables.push(window.onDidCloseTerminal(t => { - try { - equal(terminal, t); - deepEqual(terminal.exitStatus, { code: 22 }); - } catch (e) { - done(e); - return; - } - done(); - })); - })); + test('exitStatus.code should be set to the exit code (non-zero)', async () => { const writeEmitter = new EventEmitter(); const closeEmitter = new EventEmitter(); const pty: Pseudoterminal = { @@ -654,20 +617,23 @@ import { assertNoRpc } from '../utils'; }, close: () => { } }; - const terminal = window.createTerminal({ name: 'foo', pty }); + await new Promise(r => { + disposables.push(window.onDidOpenTerminal(t1 => { + if (t1 === created) { + strictEqual(created.exitStatus, undefined); + disposables.push(window.onDidCloseTerminal(t2 => { + if (t2 === created) { + deepStrictEqual(created.exitStatus, { code: 22 }); + r(); + } + })); + } + })); + const created = window.createTerminal({ name: 'foo', pty }); + }); }); - test('creationOptions should be set and readonly for ExtensionTerminalOptions terminals', (done) => { - disposables.push(window.onDidOpenTerminal(term => { - try { - equal(terminal, term); - } catch (e) { - done(e); - return; - } - terminal.dispose(); - disposables.push(window.onDidCloseTerminal(() => done())); - })); + test('creationOptions should be set and readonly for ExtensionTerminalOptions terminals', async () => { const writeEmitter = new EventEmitter(); const pty: Pseudoterminal = { onDidWrite: writeEmitter.event, @@ -675,16 +641,20 @@ import { assertNoRpc } from '../utils'; close: () => { } }; const options = { name: 'foo', pty }; - const terminal = window.createTerminal(options); - try { - equal(terminal.name, 'foo'); + await new Promise(r => { + disposables.push(window.onDidOpenTerminal(term => { + if (term === terminal) { + terminal.dispose(); + disposables.push(window.onDidCloseTerminal(() => r())); + } + })); + const terminal = window.createTerminal(options); + strictEqual(terminal.name, 'foo'); const terminalOptions = terminal.creationOptions as ExtensionTerminalOptions; - equal(terminalOptions.name, 'foo'); - equal(terminalOptions.pty, pty); + strictEqual(terminalOptions.name, 'foo'); + strictEqual(terminalOptions.pty, pty); throws(() => terminalOptions.name = 'bad', 'creationOptions should be readonly at runtime'); - } catch (e) { - done(e); - } + }); }); }); From 9366786ec63e80d6132b257e0a2cb44c6cb583e1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 14 Nov 2021 23:55:29 -0800 Subject: [PATCH 085/330] Use optional chaining in ExtHostPseudoterminal --- .../api/common/extHostTerminalService.ts | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 6eb760ba348..15e4b33753a 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -275,15 +275,11 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { } input(data: string): void { - if (this._pty.handleInput) { - this._pty.handleInput(data); - } + this._pty.handleInput?.(data); } resize(cols: number, rows: number): void { - if (this._pty.setDimensions) { - this._pty.setDimensions({ columns: cols, rows }); - } + this._pty.setDimensions?.({ columns: cols, rows }); } async processBinary(data: string): Promise { @@ -314,28 +310,22 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { startSendingEvents(initialDimensions: ITerminalDimensionsDto | undefined): void { // Attach the listeners this._pty.onDidWrite(e => this._onProcessData.fire(e)); - if (this._pty.onDidClose) { - this._pty.onDidClose((e: number | void = undefined) => { - this._onProcessExit.fire(e === void 0 ? undefined : e); - }); - } - if (this._pty.onDidOverrideDimensions) { - this._pty.onDidOverrideDimensions(e => { - if (e) { - this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: { cols: e.columns, rows: e.rows } }); - } - }); - } - if (this._pty.onDidChangeName) { - this._pty.onDidChangeName(title => { - this._onDidChangeProperty.fire({ type: ProcessPropertyType.Title, value: title }); - }); - } + this._pty.onDidClose?.((e: number | void = undefined) => { + this._onProcessExit.fire(e === void 0 ? undefined : e); + }); + this._pty.onDidOverrideDimensions?.(e => { + if (e) { + this._onDidChangeProperty.fire({ type: ProcessPropertyType.OverrideDimensions, value: { cols: e.columns, rows: e.rows } }); + } + }); + this._pty.onDidChangeName?.(title => { + this._onDidChangeProperty.fire({ type: ProcessPropertyType.Title, value: title }); + }); this._pty.open(initialDimensions ? initialDimensions : undefined); - if (this._pty.setDimensions && initialDimensions) { - this._pty.setDimensions(initialDimensions); + if (initialDimensions) { + this._pty.setDimensions?.(initialDimensions); } this._onProcessReady.fire({ pid: -1, cwd: '', capabilities: this._capabilities }); From f88820646b3d4e7f37f870461f7139b4f8d51cc1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Nov 2021 00:14:06 -0800 Subject: [PATCH 086/330] Tidy up --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 1 - src/vs/workbench/api/common/extHostTerminalService.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index d7139de67a4..b27f729f46d 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -515,7 +515,6 @@ import { assertNoRpc } from '../utils'; // The default pty dimensions have a chance to appear here since override // dimensions happens after the terminal is created. If so just ignore and // wait for the right dimensions - console.log('onDidChangeTerminalDimensions', e); if (e.dimensions.columns === 10 || e.dimensions.rows === 5) { disposables.push(window.onDidCloseTerminal(() => r())); terminal.dispose(); diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 15e4b33753a..47452b0060b 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -576,7 +576,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): IDisposable { const disposables = new DisposableStore(); - disposables.add(p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd))); + disposables.add(p.onProcessReady(e => this._proxy.$sendProcessReady(id, e.pid, e.cwd))); disposables.add(p.onDidChangeProperty(property => this._proxy.$sendProcessProperty(id, property))); // Buffer data events to reduce the amount of messages going to the renderer From 4a7cd9416f37e8f7095e714fdcbc4f16cff114fc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Nov 2021 09:11:42 +0100 Subject: [PATCH 087/330] tweak for https://github.com/microsoft/vscode/pull/135736#issuecomment-968528741 --- .../workbench/contrib/markers/browser/markers.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 469c0cf0e64..073988a4dcf 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -104,7 +104,7 @@ Registry.as(Extensions.Configuration).registerConfigurat 'problems.compareOrder': { 'description': Messages.PROBLEMS_PANEL_CONFIGURATION_COMPARE_ORDER, 'type': 'string', - 'default': ['severity'], + 'default': 'severity', 'enum': ['severity', 'position'], 'enumDescriptions': [ Messages.PROBLEMS_PANEL_CONFIGURATION_COMPARE_ORDER_SEVERITY, From f76b133e692ca6fe400314a7804f0f0c73fd20d1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Nov 2021 00:43:22 -0800 Subject: [PATCH 088/330] Don't wait for 4s orphan check for just revived processes Fixes #133964 --- src/vs/platform/terminal/node/ptyService.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 2c3294935ab..abd7906c9e8 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -342,9 +342,10 @@ export class PtyService extends Disposable implements IPtyService { private async _expandTerminalInstance(t: ITerminalInstanceLayoutInfoById): Promise> { try { - const persistentProcessId = this._revivedPtyIdMap.get(t.terminal)?.newId ?? t.terminal; + const revivedPtyId = this._revivedPtyIdMap.get(t.terminal)?.newId; + const persistentProcessId = revivedPtyId ?? t.terminal; const persistentProcess = this._throwIfNoPty(persistentProcessId); - const processDetails = persistentProcess && await this._buildProcessDetails(t.terminal, persistentProcess); + const processDetails = persistentProcess && await this._buildProcessDetails(t.terminal, persistentProcess, revivedPtyId !== undefined); return { terminal: { ...processDetails, id: persistentProcessId } ?? null, relativeSize: t.relativeSize @@ -359,8 +360,10 @@ export class PtyService extends Disposable implements IPtyService { } } - private async _buildProcessDetails(id: number, persistentProcess: PersistentTerminalProcess): Promise { - const [cwd, isOrphan] = await Promise.all([persistentProcess.getCwd(), persistentProcess.isOrphaned()]); + private async _buildProcessDetails(id: number, persistentProcess: PersistentTerminalProcess, wasRevived: boolean = false): Promise { + // If the process was just revived, don't do the orphan check as it will + // take some time + const [cwd, isOrphan] = await Promise.all([persistentProcess.getCwd(), wasRevived ? true : persistentProcess.isOrphaned()]); return { id, title: persistentProcess.title, From 5243c4021afe78be9df476c73d6ef0c360d11028 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 15 Nov 2021 09:52:06 +0100 Subject: [PATCH 089/330] #136424 add to proposed api --- .../platform/remote/common/remoteAuthorityResolver.ts | 2 +- src/vs/workbench/api/common/extHostExtensionService.ts | 3 ++- .../electron-sandbox/remoteExtensionsInit.ts | 10 +++++----- src/vscode-dts/vscode.proposed.resolvers.d.ts | 7 +++++++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index 6c43f9252d3..d5b97ca9121 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -19,7 +19,7 @@ export interface ResolvedAuthority { export interface ResolvedOptions { readonly extensionHostEnv?: { [key: string]: string | null }; readonly isTrusted?: boolean; - readonly initializeUsingAccount?: { providerId: string, sessionId: string }; + readonly authenticationSession?: { id: string, providerId: string }; } export interface TunnelDescription { diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 7be1359c2ec..4d412233d10 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -698,7 +698,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme }; const options: ResolvedOptions = { extensionHostEnv: result.extensionHostEnv, - isTrusted: result.isTrusted + isTrusted: result.isTrusted, + authenticationSession: result.authenticationSession ? { id: result.authenticationSession.id, providerId: result.authenticationSession.providerId } : undefined }; return { diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index 746deed82ea..d1d29adc444 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -67,20 +67,20 @@ export class RemoteExtensionsInitializerContribution implements IWorkbenchContri } // Skip: No account is provided to initialize const resolvedAuthority = await this.remoteAuthorityResolverService.resolveAuthority(connection.remoteAuthority); - if (!resolvedAuthority.options?.initializeUsingAccount) { + if (!resolvedAuthority.options?.authenticationSession) { return; } - const sessions = await this.authenticationService.getSessions(resolvedAuthority.options?.initializeUsingAccount.providerId); - const session = sessions.find(s => s.id === resolvedAuthority.options?.initializeUsingAccount?.sessionId); + const sessions = await this.authenticationService.getSessions(resolvedAuthority.options?.authenticationSession.providerId); + const session = sessions.find(s => s.id === resolvedAuthority.options?.authenticationSession?.id); // Skip: Session is not found if (!session) { - this.logService.info('Skipping initializing remote extensions because the account with given session id is not found', resolvedAuthority.options.initializeUsingAccount.sessionId); + this.logService.info('Skipping initializing remote extensions because the account with given session id is not found', resolvedAuthority.options.authenticationSession.id); return; } const userDataSyncStoreClient = this.instantiationService.createInstance(UserDataSyncStoreClient, this.userDataSyncStoreManagementService.userDataSyncStore.url); - userDataSyncStoreClient.setAuthToken(session.accessToken, resolvedAuthority.options.initializeUsingAccount.providerId); + userDataSyncStoreClient.setAuthToken(session.accessToken, resolvedAuthority.options.authenticationSession.providerId); const userData = await userDataSyncStoreClient.read(SyncResource.Extensions, null); const serviceCollection = new ServiceCollection(); diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts index 9847b67bac3..8936603d970 100644 --- a/src/vscode-dts/vscode.proposed.resolvers.d.ts +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -30,6 +30,13 @@ declare module 'vscode' { extensionHostEnv?: { [key: string]: string | null; }; isTrusted?: boolean; + + /** + * When provided, remote server will be initialized with the data synced using given user account. + * **Note:** Initialization happens only when VSCode is opened in Desktop and only extensions are initialized as of now. + */ + // authenticationSession(ForInitialization|ForInitializingExtensions)? + authenticationSession?: AuthenticationSession & { providerId: string }; } export interface TunnelPrivacy { From ddd2f5901ec52551f740f697ad2ca0ee3daa3d39 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Nov 2021 01:17:52 -0800 Subject: [PATCH 090/330] Fix terminal detach session Fixes #137204 --- src/vs/platform/terminal/node/ptyService.ts | 2 +- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 3 +++ .../contrib/terminal/browser/terminalProcessManager.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index abd7906c9e8..8ddc2331a2c 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -234,7 +234,7 @@ export class PtyService extends Disposable implements IPtyService { } async detachFromProcess(id: number): Promise { - this._throwIfNoPty(id).detach(); + return this._throwIfNoPty(id).detach(); } async reduceConnectionGraceTime(): Promise { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 1d88836d643..00673a5ad45 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -916,7 +916,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } async detachFromProcess(): Promise { + // Detach the process and dispose the instance, without the instance dispose the terminal + // won't go away await this._processManager.detachFromProcess(); + this.dispose(); } focus(force?: boolean): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index a63d043a0b8..ed69c1fdb3a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -167,6 +167,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce async detachFromProcess(): Promise { await this._process?.detach?.(); + this._process = null; } async createProcess( From c33965ab7510ed8cd0ae8759f6b74d623155699a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Nov 2021 01:21:45 -0800 Subject: [PATCH 091/330] Fix terminal dnd feedback Fixes #137205 --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index bd605002ca4..e2f0e2be9b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -388,6 +388,7 @@ pointer-events: none; opacity: 0; /* hidden initially */ transition: left 70ms ease-out, right 70ms ease-out, top 70ms ease-out, bottom 70ms ease-out, opacity 150ms ease-out; + z-index: 33; } .monaco-workbench .pane-body.integrated-terminal .terminal-group > .monaco-split-view2.horizontal .terminal-drop-overlay.drop-before { right: 50%; From bacf6ed2c1929bba386ddd0286fb9f86a7c3b83b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Nov 2021 12:15:25 +0100 Subject: [PATCH 092/330] smoke - properly create screenshots and stop instances (#137220) --- .../areas/workbench/data-migration.test.ts | 20 ++++++++++++++----- test/smoke/src/areas/workbench/launch.test.ts | 20 ++++--------------- test/smoke/src/main.ts | 2 +- test/smoke/src/utils.ts | 8 ++++++-- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/test/smoke/src/areas/workbench/data-migration.test.ts b/test/smoke/src/areas/workbench/data-migration.test.ts index e852e2e30ad..11786a1d62a 100644 --- a/test/smoke/src/areas/workbench/data-migration.test.ts +++ b/test/smoke/src/areas/workbench/data-migration.test.ts @@ -6,11 +6,17 @@ import { Application, ApplicationOptions, Quality } from '../../../../automation'; import { join } from 'path'; import { ParsedArgs } from 'minimist'; -import { timeout } from '../../utils'; +import { afterSuite, timeout } from '../../utils'; export function setup(opts: ParsedArgs, testDataPath: string) { describe('Datamigration', () => { + + let insidersApp: Application | undefined = undefined; + let stableApp: Application | undefined = undefined; + + afterSuite(opts, () => insidersApp, async () => stableApp?.stop()); + it(`verifies opened editors are restored`, async function () { const stableCodePath = opts['stable-build']; if (!stableCodePath) { @@ -31,7 +37,7 @@ export function setup(opts: ParsedArgs, testDataPath: string) { stableOptions.userDataDir = userDataDir; stableOptions.quality = Quality.Stable; - const stableApp = new Application(stableOptions); + stableApp = new Application(stableOptions); await stableApp.start(); // Open 3 editors and pin 2 of them @@ -44,11 +50,12 @@ export function setup(opts: ParsedArgs, testDataPath: string) { await stableApp.workbench.editors.newUntitledFile(); await stableApp.stop(); + stableApp = undefined; const insiderOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); insiderOptions.userDataDir = userDataDir; - const insidersApp = new Application(insiderOptions); + insidersApp = new Application(insiderOptions); await insidersApp.start(); // Verify 3 editors are open @@ -57,6 +64,7 @@ export function setup(opts: ParsedArgs, testDataPath: string) { await insidersApp.workbench.editors.selectTab('www'); await insidersApp.stop(); + insidersApp = undefined; }); it(`verifies that 'hot exit' works for dirty files`, async function () { @@ -72,7 +80,7 @@ export function setup(opts: ParsedArgs, testDataPath: string) { stableOptions.userDataDir = userDataDir; stableOptions.quality = Quality.Stable; - const stableApp = new Application(stableOptions); + stableApp = new Application(stableOptions); await stableApp.start(); await stableApp.workbench.editors.newUntitledFile(); @@ -89,11 +97,12 @@ export function setup(opts: ParsedArgs, testDataPath: string) { await timeout(2000); // give time to store the backup before stopping the app await stableApp.stop(); + stableApp = undefined; const insiderOptions: ApplicationOptions = Object.assign({}, this.defaultOptions); insiderOptions.userDataDir = userDataDir; - const insidersApp = new Application(insiderOptions); + insidersApp = new Application(insiderOptions); await insidersApp.start(); await insidersApp.workbench.editors.waitForTab(readmeMd, true); @@ -105,6 +114,7 @@ export function setup(opts: ParsedArgs, testDataPath: string) { await insidersApp.workbench.editor.waitForEditorContents(untitled, c => c.indexOf(textToTypeInUntitled) > -1); await insidersApp.stop(); + insidersApp = undefined; }); }); } diff --git a/test/smoke/src/areas/workbench/launch.test.ts b/test/smoke/src/areas/workbench/launch.test.ts index 0365098ff24..93074dec346 100644 --- a/test/smoke/src/areas/workbench/launch.test.ts +++ b/test/smoke/src/areas/workbench/launch.test.ts @@ -3,29 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import minimist = require('minimist'); import * as path from 'path'; import { Application, ApplicationOptions } from '../../../../automation'; +import { afterSuite } from '../../utils'; -export function setup() { +export function setup(opts: minimist.ParsedArgs) { describe('Launch', () => { let app: Application; - after(async function () { - if (app) { - await app.stop(); - } - }); - - afterEach(async function () { - if (app) { - if (this.currentTest!.state === 'failed') { - const name = this.currentTest!.fullTitle().replace(/[^a-z0-9\-]/ig, '_'); - await app.captureScreenshot(name); - } - } - }); + afterSuite(opts, () => app); it(`verifies that application launches when user data directory has non-ascii characters`, async function () { const defaultOptions = this.defaultOptions as ApplicationOptions; @@ -33,6 +22,5 @@ export function setup() { app = new Application(options); await app.start(); }); - }); } diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index bae2e43cc51..1cf54e68e77 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -356,7 +356,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { setupExtensionTests(opts); if (!opts.web) { setupMultirootTests(opts); } if (!opts.web) { setupLocalizationTests(opts); } - if (!opts.web) { setupLaunchTests(); } + if (!opts.web) { setupLaunchTests(opts); } // TODO: Enable terminal tests for non-web if (opts.web) { setupTerminalProfileTests(opts); } diff --git a/test/smoke/src/utils.ts b/test/smoke/src/utils.ts index 727a994e6de..8eb977a3681 100644 --- a/test/smoke/src/utils.ts +++ b/test/smoke/src/utils.ts @@ -42,9 +42,9 @@ export function beforeSuite(opts: minimist.ParsedArgs, optionsTransform?: (opts: }); } -export function afterSuite(opts: minimist.ParsedArgs) { +export function afterSuite(opts: minimist.ParsedArgs, appFn?: () => Application | undefined, joinFn?: () => Promise) { after(async function () { - const app = this.app as Application; + const app: Application = appFn?.() ?? this.app; if (this.currentTest?.state === 'failed' && opts.screenshots) { const name = this.currentTest!.fullTitle().replace(/[^a-z0-9\-]/ig, '_'); @@ -58,6 +58,10 @@ export function afterSuite(opts: minimist.ParsedArgs) { if (app) { await app.stop(); } + + if (joinFn) { + await joinFn(); + } }); } From 5be35751b91c4807756ada25f0dff609c387c014 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 15 Nov 2021 13:04:40 +0100 Subject: [PATCH 093/330] Remove unnecessary regexp escape (for #137166) --- src/vs/platform/theme/common/tokenClassificationRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts index ee9c65f4cfd..b74f64dc33b 100644 --- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts +++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts @@ -22,7 +22,7 @@ export type TokenClassificationString = string; export const idPattern = '\\w+[-_\\w+]*'; export const typeAndModifierIdPattern = `^${idPattern}$`; -export const selectorPattern = `^(${idPattern}|\\*)(\\${CLASSIFIER_MODIFIER_SEPARATOR}${idPattern})*(\\${TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR}${idPattern})?$`; +export const selectorPattern = `^(${idPattern}|\\*)(\\${CLASSIFIER_MODIFIER_SEPARATOR}${idPattern})*(${TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR}${idPattern})?$`; export const fontStylePattern = '^(\\s*(italic|bold|underline))*\\s*$'; From ccdd1bf1a19b961abfcf02240d1ac6607045ca17 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Nov 2021 13:51:24 +0100 Subject: [PATCH 094/330] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aac27a97165..ff58899897f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "2b5b37909854357e3218c1c9677e8eb81f1a898b", + "distro": "462411ad58e3777e58712ca3d3f79b657219f590", "author": { "name": "Microsoft Corporation" }, From 466d0418a2ce7d29d7e3fb36e1087c7a6f09c0a1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Nov 2021 15:00:54 +0100 Subject: [PATCH 095/330] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff58899897f..769de7d9af5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "462411ad58e3777e58712ca3d3f79b657219f590", + "distro": "36fc578790f6eade7c7dadd8e19aad867102d14f", "author": { "name": "Microsoft Corporation" }, From bdc489b38d0fa9b76f60d3e54de16c975262d72e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 15 Nov 2021 17:10:19 +0100 Subject: [PATCH 096/330] #51935 fix configuration model to consider an override identifier can exists in more than one overrides --- .../common/configurationModels.ts | 29 +++++++++++++++---- .../test/common/configurationModels.test.ts | 24 +++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index ae155ae126f..6f5f2d3b07c 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as arrays from 'vs/base/common/arrays'; +import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter, Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -58,12 +59,13 @@ export class ConfigurationModel implements IConfigurationModel { } getKeysForOverrideIdentifier(identifier: string): string[] { + const keys: string[] = []; for (const override of this.overrides) { - if (override.identifiers.indexOf(identifier) !== -1) { - return override.keys; + if (override.identifiers.includes(identifier)) { + keys.push(...override.keys); } } - return []; + return arrays.distinct(keys); } override(identifier: string): ConfigurationModel { @@ -156,12 +158,27 @@ export class ConfigurationModel implements IConfigurationModel { } private getContentsForOverrideIdentifer(identifier: string): any { + let contentsForIdentifierOnly: IStringDictionary | null = null; + let contents: IStringDictionary | null = null; + const mergeContents = (contentsToMerge: any) => { + if (contentsToMerge) { + if (contents) { + this.mergeContents(contents, contentsToMerge); + } else { + contents = contentsToMerge; + } + } + }; for (const override of this.overrides) { - if (override.identifiers.indexOf(identifier) !== -1) { - return override.contents; + if (arrays.equals(override.identifiers, [identifier])) { + contentsForIdentifierOnly = override.contents; + } else if (override.identifiers.includes(identifier)) { + mergeContents(override.contents); } } - return null; + // Merge contents of the identifier only at the end to take precedence. + mergeContents(contentsForIdentifierOnly); + return contents; } toJSON(): IConfigurationModel { diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index 0032a1e5b9f..4abda18f8a1 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -236,6 +236,30 @@ suite('ConfigurationModel', () => { assert.deepStrictEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 }); }); + + test('Test override when an override has multiple identifiers', () => { + const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, ['a', 'c'], [{ identifiers: ['x', 'y'], contents: { 'a': 2 }, keys: ['a'] }]); + + let actual = testObject.override('x'); + assert.deepStrictEqual(actual.contents, { 'a': 2, 'c': 1 }); + assert.deepStrictEqual(actual.keys, ['a', 'c']); + assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('x'), ['a']); + + actual = testObject.override('y'); + assert.deepStrictEqual(actual.contents, { 'a': 2, 'c': 1 }); + assert.deepStrictEqual(actual.keys, ['a', 'c']); + assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('y'), ['a']); + }); + + test('Test override when an identifier is defined in multiple overrides', () => { + const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, ['a', 'c'], [{ identifiers: ['x'], contents: { 'a': 3, 'b': 1 }, keys: ['a', 'b'] }, { identifiers: ['x', 'y'], contents: { 'a': 2 }, keys: ['a'] }]); + + const actual = testObject.override('x'); + assert.deepStrictEqual(actual.contents, { 'a': 3, 'c': 1, 'b': 1 }); + assert.deepStrictEqual(actual.keys, ['a', 'c']); + + assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('x'), ['a', 'b']); + }); }); suite('CustomConfigurationModel', () => { From 7a9cf6dbbaebbbfc0a1e0209ac3127728ff76c47 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 14 Nov 2021 22:17:12 -0800 Subject: [PATCH 097/330] Move decoration update to common. --- .../notebook/browser/notebookBrowser.ts | 1 + .../browser/view/renderers/cellRenderer.ts | 93 +++++++------------ 2 files changed, 32 insertions(+), 62 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 8e0c1b86f36..ca715fe2511 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -252,6 +252,7 @@ export interface ICellViewModel extends IGenericCellViewModel { readonly layoutInfo: { totalHeight: number; }; readonly onDidChangeLayout: Event<{ totalHeight?: boolean | number; outerWidth?: number; }>; readonly onDidChangeCellStatusBarItems: Event; + readonly onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[], removed: INotebookCellDecorationOptions[] }>; readonly editStateSource: string; readonly editorAttached: boolean; isInputCollapsed: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 85f42fee93b..bb0f8657014 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -251,6 +251,37 @@ abstract class AbstractCellRenderer { } protected commonRenderElement(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + const removedClassNames: string[] = []; + templateData.rootContainer.classList.forEach(className => { + if (/^nb\-.*$/.test(className)) { + removedClassNames.push(className); + } + }); + + removedClassNames.forEach(className => { + templateData.rootContainer.classList.remove(className); + }); + + templateData.decorationContainer.innerText = ''; + + const generateCellTopDecorations = () => { + templateData.decorationContainer.innerText = ''; + + element.getCellDecorations().filter(options => options.topClassName !== undefined).forEach(options => { + templateData.decorationContainer.append(DOM.$(`.${options.topClassName!}`)); + }); + }; + + templateData.elementDisposables.add(element.onCellDecorationsChanged((e) => { + const modified = e.added.find(e => e.topClassName) || e.removed.find(e => e.topClassName); + + if (modified) { + generateCellTopDecorations(); + } + })); + + generateCellTopDecorations(); + if (element.dragging) { templateData.container.classList.add(DRAGGING_CLASS); } else { @@ -349,19 +380,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen throw new Error('The notebook editor is not attached with view model yet.'); } - const removedClassNames: string[] = []; - templateData.rootContainer.classList.forEach(className => { - if (/^nb\-.*$/.test(className)) { - removedClassNames.push(className); - } - }); - - removedClassNames.forEach(className => { - templateData.rootContainer.classList.remove(className); - }); - - templateData.decorationContainer.innerText = ''; - this.commonRenderElement(element, templateData); templateData.currentRenderedCell = element; @@ -374,23 +392,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } const elementDisposables = templateData.elementDisposables; - - const generateCellTopDecorations = () => { - templateData.decorationContainer.innerText = ''; - - element.getCellDecorations().filter(options => options.topClassName !== undefined).forEach(options => { - templateData.decorationContainer.append(DOM.$(`.${options.topClassName!}`)); - }); - }; - - elementDisposables.add(element.onCellDecorationsChanged((e) => { - const modified = e.added.find(e => e.topClassName) || e.removed.find(e => e.topClassName); - - if (modified) { - generateCellTopDecorations(); - } - })); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); this.updateForLayout(element, templateData); @@ -937,19 +938,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende throw new Error('The notebook editor is not attached with view model yet.'); } - const removedClassNames: string[] = []; - templateData.rootContainer.classList.forEach(className => { - if (/^nb\-.*$/.test(className)) { - removedClassNames.push(className); - } - }); - - removedClassNames.forEach(className => { - templateData.rootContainer.classList.remove(className); - }); - - templateData.decorationContainer.innerText = ''; - this.commonRenderElement(element, templateData); templateData.currentRenderedCell = element; @@ -964,25 +952,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.setupOutputCollapsedPart(templateData, cellOutputCollapsedContainer, element); const elementDisposables = templateData.elementDisposables; - - const generateCellTopDecorations = () => { - templateData.decorationContainer.innerText = ''; - - element.getCellDecorations().filter(options => options.topClassName !== undefined).forEach(options => { - templateData.decorationContainer.append(DOM.$(`.${options.topClassName!}`)); - }); - }; - - elementDisposables.add(element.onCellDecorationsChanged((e) => { - const modified = e.added.find(e => e.topClassName) || e.removed.find(e => e.topClassName); - - if (modified) { - generateCellTopDecorations(); - } - })); - - generateCellTopDecorations(); - const child = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); elementDisposables.add(child.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); From 8f37bcaf6e4b3436751da4b085ec78aff13ac06c Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 14 Nov 2021 22:38:06 -0800 Subject: [PATCH 098/330] Simplify markup cell renderElement. --- .../notebook/browser/notebookBrowser.ts | 1 + .../browser/view/renderers/cellContextKeys.ts | 6 +-- .../browser/view/renderers/cellRenderer.ts | 42 ++----------------- .../browser/view/renderers/markdownCell.ts | 40 +++++++++++++++++- 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index ca715fe2511..9b17ab9f044 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -253,6 +253,7 @@ export interface ICellViewModel extends IGenericCellViewModel { readonly onDidChangeLayout: Event<{ totalHeight?: boolean | number; outerWidth?: number; }>; readonly onDidChangeCellStatusBarItems: Event; readonly onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[], removed: INotebookCellDecorationOptions[] }>; + readonly onDidChangeState: Event; readonly editStateSource: string; readonly editorAttached: boolean; isInputCollapsed: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts index f4bcf8830b8..d66e823e72f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { CellEditState, CellFocusMode, CellViewModelStateChangeEvent, INotebookEditorDelegate, NotebookCellExecutionStateContext, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, CellViewModelStateChangeEvent, ICellViewModel, INotebookEditorDelegate, NotebookCellExecutionStateContext, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -30,7 +30,7 @@ export class CellContextKeyManager extends Disposable { constructor( private readonly contextKeyService: IContextKeyService, private readonly notebookEditor: INotebookEditorDelegate, - private element: CodeCellViewModel | MarkupCellViewModel + private element: ICellViewModel ) { super(); @@ -51,7 +51,7 @@ export class CellContextKeyManager extends Disposable { }); } - public updateForElement(element: MarkupCellViewModel | CodeCellViewModel) { + public updateForElement(element: ICellViewModel) { this.elementDisposables.clear(); this.elementDisposables.add(element.onDidChangeState(e => this.onDidChangeState(e))); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index bb0f8657014..6b0b8181729 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -287,6 +287,8 @@ abstract class AbstractCellRenderer { } else { templateData.container.classList.remove(DRAGGING_CLASS); } + + templateData.elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); } } @@ -298,7 +300,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen dndController: CellDragAndDropController, private renderedEditors: Map, contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService, - @IConfigurationService private configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService, @IMenuService menuService: IMenuService, @@ -392,32 +394,12 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } const elementDisposables = templateData.elementDisposables; - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { this.updateForLayout(element, templateData); })); - this.updateForHover(element, templateData); - const cellEditorOptions = new CellEditorOptions(this.notebookEditor, this.notebookEditor.notebookOptions, this.configurationService, element.language); - cellEditorOptions.setLineNumbers(element.lineNumbers); - elementDisposables.add(cellEditorOptions); - - elementDisposables.add(element.onDidChangeState(e => { - if (e.cellIsHoveredChanged) { - this.updateForHover(element, templateData); - } - - if (e.inputCollapsedChanged) { - this.updateCollapsedState(element); - } - - if (e.cellLineNumberChanged) { - cellEditorOptions.setLineNumbers(element.lineNumbers); - } - })); - // render toolbar first this.setupCellToolbarActions(templateData, elementDisposables); @@ -433,10 +415,8 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.setBetweenCellToolbarContext(templateData, element, toolbarContext); const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); - const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, cellEditorOptions.getValue(element.internalMetadata), this.renderedEditors,); + const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); elementDisposables.add(markdownCell); - elementDisposables.add(cellEditorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(cellEditorOptions.getUpdatedValue(element.internalMetadata)))); - templateData.statusBar.update(toolbarContext); } @@ -450,18 +430,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen templateData.container.classList.toggle('cell-statusbar-hidden', this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(element.internalMetadata) === 0); } - private updateForHover(element: MarkupCellViewModel, templateData: MarkdownCellRenderTemplate): void { - templateData.container.classList.toggle('markdown-cell-hover', element.cellIsHovered); - } - - private updateCollapsedState(element: MarkupCellViewModel) { - if (element.isInputCollapsed) { - this.notebookEditor.hideMarkupPreviews([element]); - } else { - this.notebookEditor.unhideMarkupPreviews([element]); - } - } - disposeTemplate(templateData: MarkdownCellRenderTemplate): void { templateData.disposables.clear(); } @@ -961,8 +929,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(cellEditorOptions.onDidChange(() => templateData.editor.updateOptions(cellEditorOptions.getUpdatedValue(element.internalMetadata)))); templateData.editor.updateOptions(cellEditorOptions.getUpdatedValue(element.internalMetadata)); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); - this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { this.updateForLayout(element, templateData); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 30093cc6abf..2d29c5f05a1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -25,6 +25,8 @@ import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { TokenizationRegistry } from 'vs/editor/common/modes'; import { MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; +import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class StatefulMarkdownCell extends Disposable { @@ -38,26 +40,34 @@ export class StatefulMarkdownCell extends Disposable { private readonly focusSwitchDisposable = this._register(new MutableDisposable()); private readonly editorDisposables = this._register(new DisposableStore()); private foldingState: CellFoldingState; + private cellEditorOptions: CellEditorOptions; + private editorOptions: IEditorOptions; constructor( private readonly notebookEditor: IActiveNotebookEditorDelegate, private readonly viewCell: MarkupCellViewModel, private readonly templateData: MarkdownCellRenderTemplate, - private editorOptions: IEditorOptions, private readonly renderedEditors: Map, @IContextKeyService private readonly contextKeyService: IContextKeyService, @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IModeService private readonly modeService: IModeService, + @IConfigurationService private configurationService: IConfigurationService, ) { super(); this.constructDOM(); this.editorPart = templateData.editorPart; + + this.cellEditorOptions = this._register(new CellEditorOptions(this.notebookEditor, this.notebookEditor.notebookOptions, this.configurationService, this.viewCell.language)); + this.cellEditorOptions.setLineNumbers(this.viewCell.lineNumbers); + this.editorOptions = this.cellEditorOptions.getValue(this.viewCell.internalMetadata); + this._register(toDisposable(() => renderedEditors.delete(this.viewCell))); this.registerListeners(); // update for init state + this.updateForHover(); this.updateForFocusModeChange(); this.foldingState = viewCell.foldingState; this.setFoldingIndicator(); @@ -109,6 +119,18 @@ export class StatefulMarkdownCell extends Disposable { this.setFoldingIndicator(); } } + + if (e.cellIsHoveredChanged) { + this.updateForHover(); + } + + if (e.inputCollapsedChanged) { + this.updateCollapsedState(); + } + + if (e.cellLineNumberChanged) { + this.cellEditorOptions.setLineNumbers(this.viewCell.lineNumbers); + } })); this._register(this.notebookEditor.notebookOptions.onDidChangeOptions(e => { @@ -125,6 +147,22 @@ export class StatefulMarkdownCell extends Disposable { this.relayoutCell(); } })); + + this._register(this.cellEditorOptions.onDidChange(() => { + this.updateEditorOptions(this.cellEditorOptions.getUpdatedValue(this.viewCell.internalMetadata)); + })); + } + + private updateCollapsedState() { + if (this.viewCell.isInputCollapsed) { + this.notebookEditor.hideMarkupPreviews([this.viewCell]); + } else { + this.notebookEditor.unhideMarkupPreviews([this.viewCell]); + } + } + + private updateForHover(): void { + this.templateData.container.classList.toggle('markdown-cell-hover', this.viewCell.cellIsHovered); } private updateForFocusModeChange() { From 4dde912b9e6b9600b1e1f1a6b9390d499e554634 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 15 Nov 2021 08:17:37 -0800 Subject: [PATCH 099/330] Update cell rendering. --- .../notebook/browser/notebook.layout.md | 76 +++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index 8737513ebaa..ef9472a4561 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -1,6 +1,50 @@ -# Notebook Layout +The notebook editor is a virtualized list view rendered in two contexts (mainframe and webview/iframe). It's on top of the builtin list/tree view renderer but its experience is different from traditional list views like File Explorer and Settings Editor. This doc covers the architecture of the notebook editor and layout optimiziations we experimented. -The notebook editor is a virtualized list view rendered in two contexts (mainframe and webview/iframe). Since most elements' positions are absoulte and there is latency between the two frames, we have multiple optimizations to ensure smooth (we try our best) perceived user experience. The optimizations are mostly around: +# Archtecture + +## Cell rendering + +The rendering of cells in the notebook editor consists of following steps: + +* Update reused DOM nodes in the template and cell decorations +* Set up context for the cell and toolbars +* Update cell toolbar, run toolbar and insertion toolbar between cells +* Render cell +* Register listeners for: + * Notebook layout change + * Cell layout change + * Cell state change: Folding, Collapse, Focus + +## Focus Tracking + +Due to the nature of virtualization (list view) and two layers architecture, the focus tracking is more complex compared to file explorer or monaco editor. When a notebook is *focused*, the `document.activeElement` can be + +* Monaco editor, when users focus on a cell editor + * `textarea` when users focus the text + * Widgets +* Webview/iframe, when users focus on markdown cell or rich outputs rendered rendered in iframe +* List view container, when users focus on cell container +* Focusable element inside the notebook editor + * Builtin output (e.g., text output) + * Find Widget + * Cell statusbar + * Toolbars + +The catch here is if the focus is on a monaco editor, instead of the list view container, when the cell is moved out of view, the list view removes the cell row from the DOM tree. The `document.activeElement` will fall back `document.body` when that happens. To ensure that the notebook editor doesn't blur, we need to move focus back to list view container when the focused cell is moved out of view. More importantly, focus the cell editor again when the cell is visible again (if the cell is still the *active* cell). + +Copy in Notebook depends on the focus tracking + +* Send `document.executeCommand('copy')` if users select text in output rendered in main frame by builtin renderer +* Request webview copy if the focus is inside the webview +* Copy cells if the focus is on notebook cell list +* Copy text if the focus is in cell editor (monaco editor) + +![diagram](https://user-images.githubusercontent.com/876920/141730905-2818043e-1a84-45d3-ad27-83bd89235ca5.png) + + +# Optimizations + +Since most elements' positions are absoulte and there is latency between the two frames, we have multiple optimizations to ensure smooth (we try our best) perceived user experience. The optimizations are mostly around: * Ensure the elements in curent viewport are stable when other elements dimensions update * Fewer layout messages between the main and iframe @@ -66,31 +110,3 @@ If the new output is rendered within 200ms, users won't see the UI movement. Code cell outputs and markdown cells are rendered in the webview, which are async in nature. In order to have the cell outputs and markdown previews rendered when users scroll to them, we send rendering requests of cells in the next viewport when it's idle. Thus scrolling downwards is smoother. However, we **don't** warmup the previous viewport as the cell height change of previous viewport might trigger the flickering of markdown cells in current viewport. Before we optimize this, do not do any warmup of cells before current viewport. - - - -## Focus Tracking - -Due to the nature of virtualization (list view) and two layers architecture, the focus tracking is more complex compared to file explorer or monaco editor. When a notebook is *focused*, the `document.activeElement` can be - -* Monaco editor, when users focus on a cell editor - * `textarea` when users focus the text - * Widgets -* Webview/iframe, when users focus on markdown cell or rich outputs rendered rendered in iframe -* List view container, when users focus on cell container -* Focusable element inside the notebook editor - * Builtin output (e.g., text output) - * Find Widget - * Cell statusbar - * Toolbars - -The catch here is if the focus is on a monaco editor, instead of the list view container, when the cell is moved out of view, the list view removes the cell row from the DOM tree. The `document.activeElement` will fall back `document.body` when that happens. To ensure that the notebook editor doesn't blur, we need to move focus back to list view container when the focused cell is moved out of view. More importantly, focus the cell editor again when the cell is visible again (if the cell is still the *active* cell). - -Copy in Notebook depends on the focus tracking - -* Send `document.executeCommand('copy')` if users select text in output rendered in main frame by builtin renderer -* Request webview copy if the focus is inside the webview -* Copy cells if the focus is on notebook cell list -* Copy text if the focus is in cell editor (monaco editor) - -![diagram](https://user-images.githubusercontent.com/876920/141730905-2818043e-1a84-45d3-ad27-83bd89235ca5.png) From de269bbbf2bb5e3e5a8d2f5851a14ba46c64a22b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 15 Nov 2021 17:32:08 +0100 Subject: [PATCH 100/330] #51935 fix overrides keys in configuration model while merging --- .../configuration/common/configurationModels.ts | 2 ++ .../test/common/configurationModels.test.ts | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 6f5f2d3b07c..cda053874c9 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -89,6 +89,8 @@ export class ConfigurationModel implements IConfigurationModel { const [override] = overrides.filter(o => arrays.equals(o.identifiers, otherOverride.identifiers)); if (override) { this.mergeContents(override.contents, otherOverride.contents); + override.keys.push(...otherOverride.keys); + override.keys = arrays.distinct(override.keys); } else { overrides.push(objects.deepClone(otherOverride)); } diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index 4abda18f8a1..ab8ae1dc741 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -190,7 +190,7 @@ suite('ConfigurationModel', () => { let result = base.merge(add); assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 } }); - assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a'] }]); + assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a', 'b'] }]); assert.deepStrictEqual(result.override('c').contents, { 'a': 2, 'b': 2 }); assert.deepStrictEqual(result.keys, ['a.b']); }); @@ -260,6 +260,21 @@ suite('ConfigurationModel', () => { assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('x'), ['a', 'b']); }); + + test('Test merge when configuration models have multiple identifiers', () => { + const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, ['a', 'c'], [{ identifiers: ['y'], contents: { 'c': 1 }, keys: ['c'] }, { identifiers: ['x', 'y'], contents: { 'a': 2 }, keys: ['a'] }]); + const target = new ConfigurationModel({ 'a': 2, 'b': 1 }, ['a', 'b'], [{ identifiers: ['x'], contents: { 'a': 3, 'b': 2 }, keys: ['a', 'b'] }, { identifiers: ['x', 'y'], contents: { 'b': 3 }, keys: ['b'] }]); + + const actual = testObject.merge(target); + + assert.deepStrictEqual(actual.contents, { 'a': 2, 'c': 1, 'b': 1 }); + assert.deepStrictEqual(actual.keys, ['a', 'c', 'b']); + assert.deepStrictEqual(actual.overrides, [ + { identifiers: ['y'], contents: { 'c': 1 }, keys: ['c'] }, + { identifiers: ['x', 'y'], contents: { 'a': 2, 'b': 3 }, keys: ['a', 'b'] }, + { identifiers: ['x'], contents: { 'a': 3, 'b': 2 }, keys: ['a', 'b'] }, + ]); + }); }); suite('CustomConfigurationModel', () => { From 90ae587a5586fc6c933b58c166683d77937082a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tron=C3=AD=C4=8Dek?= Date: Mon, 15 Nov 2021 16:49:26 +0000 Subject: [PATCH 101/330] Fix indentation --- src/vs/server/serverEnvironmentService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/server/serverEnvironmentService.ts b/src/vs/server/serverEnvironmentService.ts index e82cb341d82..c3992fba47f 100644 --- a/src/vs/server/serverEnvironmentService.ts +++ b/src/vs/server/serverEnvironmentService.ts @@ -12,7 +12,7 @@ import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/envi export const serverOptions: OptionDescriptions = { 'port': { type: 'string' }, 'pick-port': { type: 'string' }, - 'connection-token': { type: 'string' }, + 'connection-token': { type: 'string' }, 'connection-secret': { type: 'string', description: nls.localize('connection-secret', "Path to file that contains the connection token. This will require that all incoming connections know the secret.") }, 'host': { type: 'string' }, 'socket-path': { type: 'string' }, @@ -64,7 +64,7 @@ export interface ServerParsedArgs { * @deprecated use `connection-token` instead */ connectionToken?: string; - 'pick-port'?: string; + 'pick-port'?: string; /** * A path to a filename which will be read on startup. * Consider placing this file in a folder readable only by the same user (a `chmod 0700` directory). From 4e29d28afb6d9b81069833978096625364800205 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 15 Nov 2021 18:58:05 +0100 Subject: [PATCH 102/330] #51935 use overrides from the model --- .../configuration/common/configuration.ts | 67 -------------- .../common/configurationModels.ts | 87 +++++++++++++++++-- .../common/configurationModels.ts | 11 +-- 3 files changed, 87 insertions(+), 78 deletions(-) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 2711e370a19..6160cb333d0 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IStringDictionary } from 'vs/base/common/collections'; import { Event } from 'vs/base/common/event'; -import * as objects from 'vs/base/common/objects'; import * as types from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Extensions, IConfigurationRegistry, overrideIdentifierFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; @@ -151,71 +149,6 @@ export interface IConfigurationCompareResult { overrides: [string, string[]][]; } -export function compare(from: IConfigurationModel | undefined, to: IConfigurationModel | undefined): IConfigurationCompareResult { - const added = to - ? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys] - : []; - const removed = from - ? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys] - : []; - const updated: string[] = []; - - if (to && from) { - for (const key of from.keys) { - if (to.keys.indexOf(key) !== -1) { - const value1 = getConfigurationValue(from.contents, key); - const value2 = getConfigurationValue(to.contents, key); - if (!objects.equals(value1, value2)) { - updated.push(key); - } - } - } - } - - const overrides: [string, string[]][] = []; - const byOverrideIdentifier = (overrides: IOverrides[]): IStringDictionary => { - const result: IStringDictionary = {}; - for (const override of overrides) { - for (const identifier of override.identifiers) { - result[keyFromOverrideIdentifier(identifier)] = override; - } - } - return result; - }; - const toOverridesByIdentifier: IStringDictionary = to ? byOverrideIdentifier(to.overrides) : {}; - const fromOverridesByIdentifier: IStringDictionary = from ? byOverrideIdentifier(from.overrides) : {}; - - if (Object.keys(toOverridesByIdentifier).length) { - for (const key of added) { - const override = toOverridesByIdentifier[key]; - if (override) { - overrides.push([overrideIdentifierFromKey(key), override.keys]); - } - } - } - if (Object.keys(fromOverridesByIdentifier).length) { - for (const key of removed) { - const override = fromOverridesByIdentifier[key]; - if (override) { - overrides.push([overrideIdentifierFromKey(key), override.keys]); - } - } - } - - if (Object.keys(toOverridesByIdentifier).length && Object.keys(fromOverridesByIdentifier).length) { - for (const key of updated) { - const fromOverride = fromOverridesByIdentifier[key]; - const toOverride = toOverridesByIdentifier[key]; - if (fromOverride && toOverride) { - const result = compare({ contents: fromOverride.contents, keys: fromOverride.keys, overrides: [] }, { contents: toOverride.contents, keys: toOverride.keys, overrides: [] }); - overrides.push([overrideIdentifierFromKey(key), [...result.added, ...result.removed, ...result.updated]]); - } - } - } - - return { added, removed, updated, overrides }; -} - export function toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] { const overrides: IOverrides[] = []; for (const key of Object.keys(raw)) { diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index cda053874c9..58aa8f2effb 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -13,7 +13,7 @@ import * as objects from 'vs/base/common/objects'; import { IExtUri } from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { addToValueTree, compare, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; +import { addToValueTree, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifierFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; import { IFileService } from 'vs/platform/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -68,6 +68,14 @@ export class ConfigurationModel implements IConfigurationModel { return arrays.distinct(keys); } + getAllOverrideIdentifiers(): string[] { + const result: string[] = []; + for (const override of this.overrides) { + result.push(...override.identifiers); + } + return arrays.distinct(result); + } + override(identifier: string): ConfigurationModel { let overrideConfigurationModel = this.overrideConfigurations.get(identifier); if (!overrideConfigurationModel) { @@ -562,9 +570,9 @@ export class Configuration { } compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys: string[]): IConfigurationChange { - const overrides: [string, string[]][] = keys - .filter(key => OVERRIDE_PROPERTY_PATTERN.test(key)) - .map(key => { + const overrides: [string, string[]][] = []; + for (const key of keys) { + if (OVERRIDE_PROPERTY_PATTERN.test(key)) { const overrideIdentifier = overrideIdentifierFromKey(key); const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier); const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier); @@ -573,8 +581,9 @@ export class Configuration { ...fromKeys.filter(key => toKeys.indexOf(key) === -1), ...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key))) ]; - return [overrideIdentifier, keys]; - }); + overrides.push([overrideIdentifier, keys]); + } + } this.updateDefaultConfiguration(defaults); return { keys, overrides }; } @@ -751,6 +760,15 @@ export class Configuration { return [...keys.values()]; } + protected allOverrideIdentifiers(): string[] { + const keys: Set = new Set(); + this._defaultConfiguration.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key)); + this.userConfiguration.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key)); + this._workspaceConfiguration.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key)); + this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key))); + return [...keys.values()]; + } + protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] { const keys: Set = new Set(); this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)); @@ -858,3 +876,60 @@ export class AllKeysConfigurationChangeEvent extends ConfigurationChangeEvent { this.sourceConfig = sourceConfig; } } + +function compare(from: ConfigurationModel | undefined, to: ConfigurationModel | undefined): IConfigurationCompareResult { + const { added, removed, updated } = compareConfigurationContents(to, from); + const overrides: [string, string[]][] = []; + + const fromOverrideIdentifiers = from?.getAllOverrideIdentifiers() || []; + const toOverrideIdentifiers = to?.getAllOverrideIdentifiers() || []; + + if (to) { + const addedOverrideIdentifiers = toOverrideIdentifiers.filter(key => !fromOverrideIdentifiers.includes(key)); + for (const identifier of addedOverrideIdentifiers) { + overrides.push([identifier, to.getKeysForOverrideIdentifier(identifier)]); + } + } + + if (from) { + const removedOverrideIdentifiers = fromOverrideIdentifiers.filter(key => !toOverrideIdentifiers.includes(key)); + for (const identifier of removedOverrideIdentifiers) { + overrides.push([identifier, from.getKeysForOverrideIdentifier(identifier)]); + } + } + + if (to && from) { + for (const identifier of fromOverrideIdentifiers) { + if (toOverrideIdentifiers.includes(identifier)) { + const result = compareConfigurationContents({ contents: from.getOverrideValue(undefined, identifier) || {}, keys: from.getKeysForOverrideIdentifier(identifier) }, { contents: to.getOverrideValue(undefined, identifier) || {}, keys: to.getKeysForOverrideIdentifier(identifier) }); + overrides.push([identifier, [...result.added, ...result.removed, ...result.updated]]); + } + } + } + + return { added, removed, updated, overrides }; +} + +function compareConfigurationContents(to: { keys: string[], contents: any } | undefined, from: { keys: string[], contents: any } | undefined) { + const added = to + ? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys] + : []; + const removed = from + ? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys] + : []; + const updated: string[] = []; + + if (to && from) { + for (const key of from.keys) { + if (to.keys.indexOf(key) !== -1) { + const value1 = getConfigurationValue(from.contents, key); + const value2 = getConfigurationValue(to.contents, key); + if (!objects.equals(value1, value2)) { + updated.push(key); + } + } + } + } + return { added, removed, updated }; +} + diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index 412065dd3b2..95a1d40ad77 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -10,8 +10,8 @@ import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces import { Workspace } from 'vs/platform/workspace/common/workspace'; import { ResourceMap } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; -import { OVERRIDE_PROPERTY_PATTERN, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry'; import { isBoolean } from 'vs/base/common/types'; +import { distinct } from 'vs/base/common/arrays'; export class WorkspaceConfigurationModelParser extends ConfigurationModelParser { @@ -154,10 +154,11 @@ export class Configuration extends BaseConfiguration { }; const keys = compare(this.allKeys(), other.allKeys()); const overrides: [string, string[]][] = []; - for (const key of keys) { - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { - const overrideIdentifier = overrideIdentifierFromKey(key); - overrides.push([overrideIdentifier, compare(this.getAllKeysForOverrideIdentifier(overrideIdentifier), other.getAllKeysForOverrideIdentifier(overrideIdentifier), overrideIdentifier)]); + const allOverrideIdentifiers = distinct([...this.allOverrideIdentifiers(), ...other.allOverrideIdentifiers()]); + for (const overrideIdentifier of allOverrideIdentifiers) { + const keys = compare(this.getAllKeysForOverrideIdentifier(overrideIdentifier), other.getAllKeysForOverrideIdentifier(overrideIdentifier), overrideIdentifier); + if (keys.length) { + overrides.push([overrideIdentifier, keys]); } } return { keys, overrides }; From c157ab781b058ebd25b9d21299f06b7458f39059 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 15 Nov 2021 12:01:10 -0800 Subject: [PATCH 103/330] diagram for notebook model resolution. --- .../workbench/contrib/notebook/browser/notebook.layout.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index ef9472a4561..d1908fd19e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -2,6 +2,12 @@ The notebook editor is a virtualized list view rendered in two contexts (mainfra # Archtecture +## Notebook model resolution + + +![arch](https://user-images.githubusercontent.com/876920/141845889-abe0384e-0093-4b08-831a-04424a4b8101.png) + + ## Cell rendering The rendering of cells in the notebook editor consists of following steps: From a2772026524263c592c12634d61700c87d86cc53 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 15 Nov 2021 12:28:35 -0800 Subject: [PATCH 104/330] Fix #136482 --- src/vs/base/browser/ui/inputbox/inputBox.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index a6ba042ad66..f04858f1545 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -363,11 +363,8 @@ export class InputBox extends Widget { } public set paddingRight(paddingRight: number) { - if (this.options.flexibleHeight && this.options.flexibleWidth) { - this.input.style.width = `calc(100% - ${paddingRight}px)`; - } else { - this.input.style.paddingRight = paddingRight + 'px'; - } + // Set width to avoid hint text overlapping buttons + this.input.style.width = `calc(100% - ${paddingRight}px)`; if (this.mirror) { this.mirror.style.paddingRight = paddingRight + 'px'; From e1e648fe6e4440db0159d17f4ca33fe841daf204 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 15 Nov 2021 12:36:31 -0800 Subject: [PATCH 105/330] Revert back to onStartupFinished activation event Fixes #137012 --- extensions/emmet/package.json | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 6c1f0eb8eda..3dfc882b432 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -41,24 +41,7 @@ "onCommand:editor.emmet.action.decrementNumberByTen", "onCommand:editor.emmet.action.reflectCSSValue", "onCommand:workbench.action.showEmmetCommands", - "onLanguage:html", - "onLanguage:haml", - "onLanguage:blade", - "onLanguage:css", - "onLanguage:sass", - "onLanguage:scss", - "onLanguage:less", - "onLanguage:stylus", - "onLanguage:slim", - "onLanguage:stylus", - "onLanguage:jade", - "onLanguage:pug", - "onLanguage:javascriptreact", - "onLanguage:typescriptreact", - "onLanguage:php", - "onLanguage:vue", - "onLanguage:xsl", - "onLanguage:xml" + "onStartupFinished" ], "main": "./out/node/emmetNodeMain", "browser": "./dist/browser/emmetBrowserMain", From b256d9bc74e56e315fa2ce858f68500b65869198 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 15 Nov 2021 12:43:10 -0800 Subject: [PATCH 106/330] Initial cut of QuickPick separators (#137244) * initial cut of QuickPick separators * add proposed gate --- .../api/browser/mainThreadQuickOpen.ts | 14 ++- .../workbench/api/common/extHost.api.impl.ts | 7 +- .../workbench/api/common/extHost.protocol.ts | 5 +- .../workbench/api/common/extHostQuickOpen.ts | 99 ++++++++++--------- src/vs/workbench/api/common/extHostTypes.ts | 5 + .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.quickPickSeparators.d.ts | 18 ++++ 7 files changed, 95 insertions(+), 54 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts diff --git a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts index c5aec867363..9a47764e962 100644 --- a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItem, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItem, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions, TransferQuickPickItemOrSeparator } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -27,7 +27,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private readonly _proxy: ExtHostQuickOpenShape; private readonly _quickInputService: IQuickInputService; private readonly _items: Record = {}; @@ -43,7 +43,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } $show(instance: number, options: IPickOptions, token: CancellationToken): Promise { - const contents = new Promise((resolve, reject) => { + const contents = new Promise((resolve, reject) => { this._items[instance] = { resolve, reject }; }); @@ -73,7 +73,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } - $setItems(instance: number, items: TransferQuickPickItem[]): Promise { + $setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise { if (this._items[instance]) { this._items[instance].resolve(items); delete this._items[instance]; @@ -169,7 +169,11 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } else if (param === 'items') { handlesToItems.clear(); - params[param].forEach((item: TransferQuickPickItem) => { + params[param].forEach((item: TransferQuickPickItemOrSeparator) => { + if (item.type === 'separator') { + return; + } + if (item.buttons) { item.buttons = item.buttons.map((button: TransferQuickInputButton) => { if (button.iconPath) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 00e0d4f9e3a..1d7ab23720b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -600,6 +600,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return >extHostMessageService.showMessage(extension, Severity.Error, message, rest[0], >rest.slice(1)); }, showQuickPick(items: any, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { + // TODO: remove this once quickPickSeparators has been finalized. + if (Array.isArray(items) && items.some((item) => item.kind !== undefined)) { + checkProposedApiEnabled(extension, 'quickPickSeparators'); + } return extHostQuickOpen.showQuickPick(items, options, token); }, showWorkspaceFolderPick(options?: vscode.WorkspaceFolderPickOptions) { @@ -689,7 +693,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostUrls.registerUriHandler(extension.identifier, handler); }, createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.identifier); + return extHostQuickOpen.createQuickPick(extension); }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); @@ -1307,6 +1311,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I FunctionCoverage: extHostTypes.FunctionCoverage, WorkspaceTrustState: extHostTypes.WorkspaceTrustState, LanguageStatusSeverity: extHostTypes.LanguageStatusSeverity, + QuickPickItemKind: extHostTypes.QuickPickItemKind, }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d935e9ab65a..2144f6df3e6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -510,6 +510,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $sendProcessExit(terminalId: number, exitCode: number | undefined): void; } +export type TransferQuickPickItemOrSeparator = TransferQuickPickItem | quickInput.IQuickPickSeparator; export interface TransferQuickPickItem extends quickInput.IQuickPickItem { handle: number; buttons?: TransferQuickInputButton[]; @@ -548,7 +549,7 @@ export interface TransferQuickPick extends BaseTransferQuickInput { buttons?: TransferQuickInputButton[]; - items?: TransferQuickPickItem[]; + items?: TransferQuickPickItemOrSeparator[]; activeItems?: number[]; @@ -594,7 +595,7 @@ export interface IInputBoxOptions { export interface MainThreadQuickOpenShape extends IDisposable { $show(instance: number, options: quickInput.IPickOptions, token: CancellationToken): Promise; - $setItems(instance: number, items: TransferQuickPickItem[]): Promise; + $setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise; $setError(instance: number, error: Error): Promise; $input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise; $createOrUpdate(params: TransferQuickInput): Promise; diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index eb1c05c36a6..177d0649181 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -9,15 +9,16 @@ import { Emitter } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace'; -import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; -import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickPickItem, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol'; +import type { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; +import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItemOrSeparator } from './extHost.protocol'; import { URI } from 'vs/base/common/uri'; -import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/common/extHostTypes'; +import { ThemeIcon, QuickInputButtons, QuickPickItemKind } from 'vs/workbench/api/common/extHostTypes'; import { isPromiseCanceledError } from 'vs/base/common/errors'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; import Severity from 'vs/base/common/severity'; import { ThemeIcon as ThemeIconUtils } from 'vs/platform/theme/common/themeService'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export type Item = string | QuickPickItem; @@ -31,7 +32,7 @@ export interface ExtHostQuickOpen { showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token?: CancellationToken): Promise - createQuickPick(extensionId: ExtensionIdentifier): QuickPick; + createQuickPick(extensionId: IExtensionDescription): QuickPick; createInputBox(extensionId: ExtensionIdentifier): InputBox; } @@ -87,33 +88,23 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx return itemsPromise.then(items => { - const pickItems: TransferQuickPickItem[] = []; + const pickItems: TransferQuickPickItemOrSeparator[] = []; for (let handle = 0; handle < items.length; handle++) { - const item = items[handle]; - let label: string; - let description: string | undefined; - let detail: string | undefined; - let picked: boolean | undefined; - let alwaysShow: boolean | undefined; - if (typeof item === 'string') { - label = item; + pickItems.push({ label: item, handle }); + } else if (item.kind === QuickPickItemKind.Separator) { + pickItems.push({ type: 'separator', label: item.label }); } else { - label = item.label; - description = item.description; - detail = item.detail; - picked = item.picked; - alwaysShow = item.alwaysShow; + pickItems.push({ + label: item.label, + description: item.description, + detail: item.detail, + picked: item.picked, + alwaysShow: item.alwaysShow, + handle + }); } - pickItems.push({ - label, - description, - handle, - detail, - picked, - alwaysShow - }); } // handle selection changes @@ -192,8 +183,8 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx // ---- QuickInput - createQuickPick(extensionId: ExtensionIdentifier): QuickPick { - const session: ExtHostQuickPick = new ExtHostQuickPick(extensionId, () => this._sessions.delete(session._id)); + createQuickPick(extension: IExtensionDescription): QuickPick { + const session: ExtHostQuickPick = new ExtHostQuickPick(extension, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; } @@ -531,8 +522,9 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx private readonly _onDidChangeSelectionEmitter = new Emitter(); private readonly _onDidTriggerItemButtonEmitter = new Emitter>(); - constructor(extensionId: ExtensionIdentifier, onDispose: () => void) { - super(extensionId, onDispose); + // TODO: revert this change once quickPickSeparators has been finalized. + constructor(private readonly extension: IExtensionDescription, onDispose: () => void) { + super(extension.identifier, onDispose); this._disposables.push( this._onDidChangeActiveEmitter, this._onDidChangeSelectionEmitter, @@ -546,6 +538,10 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx } set items(items: T[]) { + if (items.some((item) => item.kind !== undefined)) { + checkProposedApiEnabled(this.extension, 'quickPickSeparators'); + } + this._items = items.slice(); this._handlesToItems.clear(); this._itemsToHandles.clear(); @@ -553,22 +549,33 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx this._handlesToItems.set(i, item); this._itemsToHandles.set(item, i); }); + + const pickItems: TransferQuickPickItemOrSeparator[] = []; + for (let handle = 0; handle < items.length; handle++) { + const item = items[handle]; + if (item.kind === QuickPickItemKind.Separator) { + pickItems.push({ type: 'separator', label: item.label }); + } else { + pickItems.push({ + handle, + label: item.label, + description: item.description, + detail: item.detail, + picked: item.picked, + alwaysShow: item.alwaysShow, + buttons: item.buttons?.map((button, i) => { + return { + ...getIconPathOrClass(button), + tooltip: button.tooltip, + handle: i + }; + }), + }); + } + } + this.update({ - items: items.map((item, i) => ({ - label: item.label, - description: item.description, - handle: i, - detail: item.detail, - picked: item.picked, - alwaysShow: item.alwaysShow, - buttons: item.buttons?.map((button, i) => { - return { - ...getIconPathOrClass(button), - tooltip: button.tooltip, - handle: i - }; - }), - })) + items: pickItems, }); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4f193198645..4623fe4de8d 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2904,6 +2904,11 @@ export class QuickInputButtons { private constructor() { } } +export enum QuickPickItemKind { + Default = 1, + Separator = 2, +} + export enum ExtensionKind { UI = 1, Workspace = 2 diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 1ccca4a1ffc..d413ce4beab 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -39,6 +39,7 @@ export const allApiProposals = Object.freeze({ notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', + quickPickSeparators: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts', quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', diff --git a/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts b/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts new file mode 100644 index 00000000000..c73c37b2095 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * 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/74967 + + export enum QuickPickItemKind { + Default = 1, + Separator = 2, + } + + export interface QuickPickItem { + kind?: QuickPickItemKind + } +} From 2fe3c26ff74daebeb651a988141d35ba8dd21a9a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 15 Nov 2021 13:05:26 -0800 Subject: [PATCH 107/330] Update unsupported debug type warning on context key updates Fix #137219 --- .../debug/browser/debugAdapterManager.ts | 108 +++++++++--------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 6a05a0af677..2bc4947ea77 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -65,6 +65,7 @@ export class AdapterManager extends Disposable implements IAdapterManager { this._register(this.contextKeyService.onDidChangeContext(e => { if (e.affectsSome(this.debuggerWhenKeys)) { this.debuggersAvailable.set(this.hasEnabledDebuggers()); + this.updateDebugAdapterSchema(); } })); this.debugExtensionsAvailable = CONTEXT_DEBUG_EXTENSION_AVAILABLE.bindTo(contextKeyService); @@ -111,58 +112,7 @@ export class AdapterManager extends Disposable implements IAdapterManager { this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1); }); - // update the schema to include all attributes, snippets and types from extensions. - const items = (launchSchema.properties!['configurations'].items); - const taskSchema = TaskDefinitionRegistry.getJsonSchema(); - const definitions: IJSONSchemaMap = { - 'common': { - properties: { - 'name': { - type: 'string', - description: nls.localize('debugName', "Name of configuration; appears in the launch configuration dropdown menu."), - default: 'Launch' - }, - 'debugServer': { - type: 'number', - description: nls.localize('debugServer', "For debug extension development only: if a port is specified VS Code tries to connect to a debug adapter running in server mode"), - default: 4711 - }, - 'preLaunchTask': { - anyOf: [taskSchema, { - type: ['string'] - }], - default: '', - defaultSnippets: [{ body: { task: '', type: '' } }], - description: nls.localize('debugPrelaunchTask', "Task to run before debug session starts.") - }, - 'postDebugTask': { - anyOf: [taskSchema, { - type: ['string'], - }], - default: '', - defaultSnippets: [{ body: { task: '', type: '' } }], - description: nls.localize('debugPostDebugTask', "Task to run after debug session ends.") - }, - 'presentation': presentationSchema, - 'internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA, - } - } - }; - launchSchema.definitions = definitions; - items.oneOf = []; - items.defaultSnippets = []; - this.debuggers.forEach(adapter => { - const schemaAttributes = adapter.getSchemaAttributes(definitions); - if (schemaAttributes && items.oneOf) { - items.oneOf.push(...schemaAttributes); - } - const configurationSnippets = adapter.configurationSnippets; - if (configurationSnippets && items.defaultSnippets) { - items.defaultSnippets.push(...configurationSnippets); - } - }); - jsonRegistry.registerSchema(launchSchemaId, launchSchema); - + this.updateDebugAdapterSchema(); this._onDidDebuggersExtPointRead.fire(); }); @@ -176,6 +126,60 @@ export class AdapterManager extends Disposable implements IAdapterManager { }); } + private updateDebugAdapterSchema(): void { + // update the schema to include all attributes, snippets and types from extensions. + const items = (launchSchema.properties!['configurations'].items); + const taskSchema = TaskDefinitionRegistry.getJsonSchema(); + const definitions: IJSONSchemaMap = { + 'common': { + properties: { + 'name': { + type: 'string', + description: nls.localize('debugName', "Name of configuration; appears in the launch configuration dropdown menu."), + default: 'Launch' + }, + 'debugServer': { + type: 'number', + description: nls.localize('debugServer', "For debug extension development only: if a port is specified VS Code tries to connect to a debug adapter running in server mode"), + default: 4711 + }, + 'preLaunchTask': { + anyOf: [taskSchema, { + type: ['string'] + }], + default: '', + defaultSnippets: [{ body: { task: '', type: '' } }], + description: nls.localize('debugPrelaunchTask', "Task to run before debug session starts.") + }, + 'postDebugTask': { + anyOf: [taskSchema, { + type: ['string'], + }], + default: '', + defaultSnippets: [{ body: { task: '', type: '' } }], + description: nls.localize('debugPostDebugTask', "Task to run after debug session ends.") + }, + 'presentation': presentationSchema, + 'internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA, + } + } + }; + launchSchema.definitions = definitions; + items.oneOf = []; + items.defaultSnippets = []; + this.debuggers.forEach(adapter => { + const schemaAttributes = adapter.getSchemaAttributes(definitions); + if (schemaAttributes && items.oneOf) { + items.oneOf.push(...schemaAttributes); + } + const configurationSnippets = adapter.configurationSnippets; + if (configurationSnippets && items.defaultSnippets) { + items.defaultSnippets.push(...configurationSnippets); + } + }); + jsonRegistry.registerSchema(launchSchemaId, launchSchema); + } + registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); this.debuggersAvailable.set(this.hasEnabledDebuggers()); From 9fa52c75d642e75471387dc60be2689f3fb45ae7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 11 Nov 2021 18:12:01 -0800 Subject: [PATCH 108/330] Use unique id for webview iframe Switch from using the passed in id (which is generally but not always unique) to instead use a generated id for identifying the webview's iframe. This prevents two iframes with the same id from being created --- .../contrib/webview/browser/webviewElement.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index b645e966772..6797ecaec0b 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -14,6 +14,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import { localize } from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -93,6 +94,9 @@ namespace WebviewState { export class WebviewElement extends Disposable implements IWebview, WebviewFindDelegate { + public readonly id: string; + private readonly iframeId: string; + protected get platform(): string { return 'browser'; } private readonly _expectedServiceWorkerVersion = 2; // Keep this in sync with the version in service-worker.js @@ -136,7 +140,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD public readonly checkImeCompletionState = true; constructor( - public readonly id: string, + id: string, private readonly options: WebviewOptions, contentOptions: WebviewContentOptions, public extension: WebviewExtensionDescription | undefined, @@ -155,6 +159,9 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD ) { super(); + this.id = id; + this.iframeId = generateUuid(); + this.content = { html: '', options: contentOptions, @@ -312,7 +319,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD })); this._register(addDisposableListener(window, 'message', e => { - if (e?.data?.target === this.id) { + if (e?.data?.target === this.iframeId) { if (e.origin !== this.webviewContentOrigin) { console.log(`Skipped renderer receiving message due to mismatched origins: ${e.origin} ${this.webviewContentOrigin}`); return; @@ -412,7 +419,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD private initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions) { // The extensionId and purpose in the URL are used for filtering in js-debug: const params: { [key: string]: string } = { - id: this.id, + id: this.iframeId, swVersion: String(this._expectedServiceWorkerVersion), extensionId: extension?.id.value ?? '', platform: this.platform, From c8a321ae17aa6ca5e7b4e626b692d9a6e20c2380 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 13:26:25 -0800 Subject: [PATCH 109/330] Skip document link test on web Fixes #136738 Cant figure out why these tests sometimes timeout in safari on web. Only in ci too it seems :/ --- .../src/test/documentLink.test.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/extensions/markdown-language-features/src/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts index 8919f6a1ecf..7c280a82bdf 100644 --- a/extensions/markdown-language-features/src/test/documentLink.test.ts +++ b/extensions/markdown-language-features/src/test/documentLink.test.ts @@ -10,18 +10,26 @@ import { joinLines } from './util'; const testFileA = workspaceFile('a.md'); +const debug = false; + +function debugLog(...args: any[]) { + if (debug) { + console.log(...args); + } +} + function workspaceFile(...segments: string[]) { return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); } async function getLinksForFile(file: vscode.Uri): Promise { - console.log('getting links', file.toString(), Date.now()); + debugLog('getting links', file.toString(), Date.now()); const r = (await vscode.commands.executeCommand('vscode.executeLinkProvider', file))!; - console.log('got links', file.toString(), Date.now()); + debugLog('got links', file.toString(), Date.now()); return r; } -suite('Markdown Document links', () => { +(vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('Markdown Document links', () => { setup(async () => { // the tests make the assumption that link providers are already registered @@ -149,21 +157,21 @@ function assertActiveDocumentUri(expectedUri: vscode.Uri) { } async function withFileContents(file: vscode.Uri, contents: string): Promise { - console.log('openTextDocument', file.toString(), Date.now()); + debugLog('openTextDocument', file.toString(), Date.now()); const document = await vscode.workspace.openTextDocument(file); - console.log('showTextDocument', file.toString(), Date.now()); + debugLog('showTextDocument', file.toString(), Date.now()); const editor = await vscode.window.showTextDocument(document); - console.log('editTextDocument', file.toString(), Date.now()); + debugLog('editTextDocument', file.toString(), Date.now()); await editor.edit(edit => { edit.replace(new vscode.Range(0, 0, 1000, 0), contents); }); - console.log('opened done', vscode.window.activeTextEditor?.document.toString(), Date.now()); + debugLog('opened done', vscode.window.activeTextEditor?.document.toString(), Date.now()); } async function executeLink(link: vscode.DocumentLink) { - console.log('executeingLink', link.target?.toString(), Date.now()); + debugLog('executeingLink', link.target?.toString(), Date.now()); const args = JSON.parse(decodeURIComponent(link.target!.query)); await vscode.commands.executeCommand(link.target!.path, args); - console.log('executedLink', vscode.window.activeTextEditor?.document.toString(), Date.now()); + debugLog('executedLink', vscode.window.activeTextEditor?.document.toString(), Date.now()); } From bb764173a3e43f7365b937b342fcab6458944bbf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 14:19:12 -0800 Subject: [PATCH 110/330] Require more explicit strict null typing for `executeCommand` Fixes #136906 Some commands never return undefined, so the current typings are incorrect. Instead we should require the caller to pass in `| undefined` if undefined can be returned --- src/vscode-dts/vscode.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 84b27f81c62..a482b0f7c00 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -8598,10 +8598,10 @@ declare module 'vscode' { * * @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. `undefined` when + * @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; + export function executeCommand(command: string, ...rest: any[]): Thenable; /** * Retrieve the list of all available commands. Commands starting with an underscore are From 99739e607b39d4454fd4b4e9ca400c035fc54e7f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 14:48:48 -0800 Subject: [PATCH 111/330] Try avoiding bad calls to columnToEditorGroup Fixes #132421 --- .../api/browser/mainThreadWebviewPanels.ts | 16 +++----- .../browser/webviewWorkbenchService.ts | 40 ++++++++----------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index d355e0876c4..5e00e96f690 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -17,7 +17,7 @@ import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewI import { ICreateWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; import { columnToEditorGroup, editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; /** @@ -154,11 +154,10 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc initData: extHostProtocol.IWebviewInitData, showOptions: extHostProtocol.WebviewPanelShowOptions, ): void { - const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); - if (showOptions) { - mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus; - mainThreadShowOptions.group = columnToEditorGroup(this._editorGroupService, showOptions.viewColumn); - } + const mainThreadShowOptions: ICreateWebViewShowOptions = showOptions ? { + preserveFocus: !!showOptions.preserveFocus, + group: columnToEditorGroup(this._editorGroupService, showOptions.viewColumn) + } : {}; const extension = reviveWebviewExtension(extensionData); @@ -198,10 +197,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc return; } - const targetGroup = this._editorGroupService.getGroup(columnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0); - if (targetGroup) { - this._webviewWorkbenchService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus); - } + this._webviewWorkbenchService.revealWebview(webview, showOptions.viewColumn ?? ACTIVE_GROUP, !!showOptions.preserveFocus); } public $registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void { diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts index 28657c3f0a6..b7d52c8e8cc 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts @@ -13,19 +13,19 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { EditorActivation } from 'vs/platform/editor/common/editor'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { IWebviewService, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions, IOverlayWebview } from 'vs/workbench/contrib/webview/browser/webview'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { IOverlayWebview, IWebviewService, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewIconManager, WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager'; -import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { WebviewInput } from './webviewEditorInput'; export const IWebviewWorkbenchService = createDecorator('webviewEditorService'); export interface ICreateWebViewShowOptions { - group: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE; - preserveFocus: boolean; + readonly group?: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE; + readonly preserveFocus?: boolean; } export interface IWebviewWorkbenchService { @@ -57,7 +57,7 @@ export interface IWebviewWorkbenchService { revealWebview( webview: WebviewInput, - group: IEditorGroup, + group: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE, preserveFocus: boolean ): void; @@ -168,7 +168,6 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc private readonly _iconManager: WebviewIconManager; constructor( - @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorService private readonly _editorService: IEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IWebviewService private readonly _webviewService: IWebviewService, @@ -241,27 +240,20 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc public revealWebview( webview: WebviewInput, - group: IEditorGroup, + group: IEditorGroup | GroupIdentifier | ACTIVE_GROUP_TYPE | SIDE_GROUP_TYPE, preserveFocus: boolean ): void { const topLevelEditor = this.findTopLevelEditorForWebview(webview); - if (webview.group === group.id) { - if (this._editorService.activeEditor === topLevelEditor) { - return; - } - - this._editorService.openEditor(topLevelEditor, { - preserveFocus, - // preserve pre 1.38 behaviour to not make group active when preserveFocus: true - // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 - activation: preserveFocus ? EditorActivation.RESTORE : undefined - }, webview.group); - } else { - const groupView = this._editorGroupService.getGroup(webview.group!); - if (groupView) { - groupView.moveEditor(topLevelEditor, group, { preserveFocus }); - } + if (this._editorService.activeEditor === topLevelEditor) { + return; } + + this._editorService.openEditor(topLevelEditor, { + preserveFocus, + // preserve pre 1.38 behaviour to not make group active when preserveFocus: true + // but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633 + activation: preserveFocus ? EditorActivation.RESTORE : undefined + }, group); } private findTopLevelEditorForWebview(webview: WebviewInput): EditorInput { From 6e6ca5f46a00fa94fbe32f558936891840fe72df Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 15 Nov 2021 15:03:19 -0800 Subject: [PATCH 112/330] Add proposedApi manifest to searchresult extension --- extensions/search-result/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index fe64cfa8fe0..747cde8e648 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -27,6 +27,9 @@ "supported": true } }, + "enabledApiProposals": [ + "documentFiltersExclusive" + ], "contributes": { "configurationDefaults": { "[search-result]": { From e38fbdae27d350d764b92a431f1b5430b8ec856f Mon Sep 17 00:00:00 2001 From: Afrish Khan S <9781909+RyanAfrish7@users.noreply.github.com> Date: Tue, 16 Nov 2021 06:26:39 +0530 Subject: [PATCH 113/330] Change img hrefs in markdown from file to vscode-file (#136687) * Change img hrefs in markdown from file to vscode-file * Transform all img uris after markdown rendering --- src/vs/base/browser/markdownRenderer.ts | 39 +++++++++++++------ .../test/browser/markdownRenderer.test.ts | 25 +++++++++++- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 70995984352..5d885f37ea3 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -71,20 +71,23 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const _href = function (href: string, isDomUri: boolean): string { const data = markdown.uris && markdown.uris[href]; - if (!data) { - return href; // no uri exists - } let uri = URI.revive(data); if (isDomUri) { if (href.startsWith(Schemas.data + ':')) { return href; } + if (!uri) { + uri = URI.parse(href); + } // this URI will end up as "src"-attribute of a dom node // and because of that special rewriting needs to be done // so that the URI uses a protocol that's understood by // browsers (like http or https) return FileAccess.asBrowserUri(uri).toString(true); } + if (!uri) { + return href; + } if (URI.parse(href).toString() === uri.toString()) { return href; // no transformation performed } @@ -100,19 +103,12 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const withInnerHTML = new Promise(c => signalInnerHTML = c); const renderer = new marked.Renderer(); + renderer.image = (href: string, title: string, text: string) => { let dimensions: string[] = []; let attributes: string[] = []; if (href) { ({ href, dimensions } = parseHrefAndDimensions(href)); - href = _href(href, true); - try { - const hrefAsUri = URI.parse(href); - if (options.baseUrl && hrefAsUri.scheme === Schemas.file) { // absolute or relative local path, or file: uri - href = resolvePath(options.baseUrl, href).toString(); - } - } catch (err) { } - attributes.push(`src="${href}"`); } if (text) { @@ -250,7 +246,26 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende renderedMarkdown = elements.map(e => typeof e === 'string' ? e : e.outerHTML).join(''); } - element.innerHTML = sanitizeRenderedMarkdown(markdown, renderedMarkdown) as unknown as string; + const htmlParser = new DOMParser(); + const markdownHtmlDoc = htmlParser.parseFromString(sanitizeRenderedMarkdown(markdown, renderedMarkdown) as unknown as string, 'text/html'); + + markdownHtmlDoc.body.querySelectorAll('img') + .forEach(img => { + if (img.src) { + let href = _href(img.src, true); + + try { + const hrefAsUri = URI.parse(href); + if (options.baseUrl && hrefAsUri.scheme === Schemas.file) { // absolute or relative local path, or file: uri + href = resolvePath(options.baseUrl, href).toString(); + } + } catch (err) { } + + img.src = href; + } + }); + + element.innerHTML = sanitizeRenderedMarkdown(markdown, markdownHtmlDoc.body.innerHTML) as unknown as string; // signal that async code blocks can be now be inserted signalInnerHTML!(); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index befafc099b4..de58e25197c 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -56,6 +56,11 @@ suite('MarkdownRenderer', () => { const result: HTMLElement = renderMarkdown({ value: `![image](http://example.com/cat.gif|height=200,width=100 'caption')` }).element; assertNodeEquals(result, `

image

`); }); + + test('image with file uri should render as same origin uri', () => { + const result: HTMLElement = renderMarkdown({ value: `![image](file:///images/cat.gif)` }).element; + assertNodeEquals(result, '

image

'); + }); }); suite('Code block renderer', () => { @@ -139,7 +144,7 @@ suite('MarkdownRenderer', () => { mds.appendMarkdown(`[$(zap)-link](#link)`); let result: HTMLElement = renderMarkdown(mds).element; - assert.strictEqual(result.innerHTML, `

-link

`); + assert.strictEqual(result.innerHTML, `

-link

`); }); test('render icon in table', () => { @@ -159,7 +164,7 @@ suite('MarkdownRenderer', () => { --link +-link `); @@ -251,5 +256,21 @@ suite('MarkdownRenderer', () => { const result = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, `

a<b>b</b>c

`); }); + + test('Should render html images', () => { + const mds = new MarkdownString(undefined, { supportHtml: true }); + mds.appendMarkdown(``); + + const result = renderMarkdown(mds).element; + assert.strictEqual(result.innerHTML, ``); + }); + + test('Should render html images with file uri as same origin uri', () => { + const mds = new MarkdownString(undefined, { supportHtml: true }); + mds.appendMarkdown(``); + + const result = renderMarkdown(mds).element; + assert.strictEqual(result.innerHTML, ``); + }); }); }); From c775da5f0f5398de907e453796396e7f5b268e75 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 15 Nov 2021 17:04:24 -0800 Subject: [PATCH 114/330] fix #133928 --- .../browser/diff/notebookTextDiffEditor.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index de1e6d09378..eef7e7fff9a 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -408,11 +408,14 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD const diffResult = await this.notebookEditorWorkerService.computeDiff(this._model.original.resource, this._model.modified.resource); NotebookTextDiffEditor.prettyChanges(this._model, diffResult.cellsDiff); const { viewModels, firstChangeIndex } = NotebookTextDiffEditor.computeDiff(this.instantiationService, this.configurationService, this._model, this._eventDispatcher!, diffResult); + const isSame = this._isViewModelTheSame(viewModels); - this._originalWebview?.removeInsets([...this._originalWebview?.insetMapping.keys()]); - this._modifiedWebview?.removeInsets([...this._modifiedWebview?.insetMapping.keys()]); + if (!isSame) { + this._originalWebview?.removeInsets([...this._originalWebview?.insetMapping.keys()]); + this._modifiedWebview?.removeInsets([...this._modifiedWebview?.insetMapping.keys()]); + this._setViewModel(viewModels); + } - this._setViewModel(viewModels); // this._diffElementViewModels = viewModels; // this._list.splice(0, this._list.length, this._diffElementViewModels); @@ -423,7 +426,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } } - private _setViewModel(viewModels: DiffElementViewModelBase[]) { + private _isViewModelTheSame(viewModels: DiffElementViewModelBase[]) { let isSame = true; if (this._diffElementViewModels.length === viewModels.length) { for (let i = 0; i < viewModels.length; i++) { @@ -440,13 +443,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD isSame = false; } - if (isSame) { - return; - } + return isSame; + } + private _setViewModel(viewModels: DiffElementViewModelBase[]) { this._diffElementViewModels = viewModels; this._list.splice(0, this._list.length, this._diffElementViewModels); - } /** From db96db93a5c6036386365312d51edaef59a55adf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 17:15:52 -0800 Subject: [PATCH 115/330] Add file to exception list --- src/tsec.exemptions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tsec.exemptions.json b/src/tsec.exemptions.json index fbb798ca0e3..7e7edeefd22 100644 --- a/src/tsec.exemptions.json +++ b/src/tsec.exemptions.json @@ -32,6 +32,7 @@ "vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService.ts" ], "ban-domparser-parsefromstring": [ + "vs/base/browser/markdownRenderer.ts", "vs/base/test/browser/markdownRenderer.test.ts" ] } From f50ca77882c8824b89148288c0482286f9b364e1 Mon Sep 17 00:00:00 2001 From: chrisdias Date: Mon, 15 Nov 2021 17:26:14 -0800 Subject: [PATCH 116/330] update png files --- .../vscode-api-tests/testWorkspace/image%.png | Bin 25587 -> 43431 bytes .../testWorkspace/image%02.png | Bin 25587 -> 43431 bytes .../vscode-api-tests/testWorkspace/image.png | Bin 25587 -> 43431 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/extensions/vscode-api-tests/testWorkspace/image%.png b/extensions/vscode-api-tests/testWorkspace/image%.png index 15b462975bee12491b059444010eeb347dc35fbd..761522a3347ff62191c92b62780eaa9f41997f95 100644 GIT binary patch literal 43431 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 diff --git a/extensions/vscode-api-tests/testWorkspace/image%02.png b/extensions/vscode-api-tests/testWorkspace/image%02.png index 15b462975bee12491b059444010eeb347dc35fbd..761522a3347ff62191c92b62780eaa9f41997f95 100644 GIT binary patch literal 43431 zcmdqIWl&sQ6fFoz2o@j^B)D5+!QH*F0F7I48h3XOZjDOmzP9A!b5_AfkBa$5>tYKd1nm+1DlHQ?(GaW zsrt;@0PCP6DFRbAMzH^O@WE7AP8bHJG8*|wAMWiK(N;>s0R{%O``;I~-=@F_2Bwft zT1*(^s(bvb1Nx%Tb{Bc8`CPJ^e#tw|ZSlhDkZ0=NG!@3-XpmZu4JY!qFDdZjAPEUl z=^FkViAVs)&@h6(Tg)$-U~3`PFnSU41)4e$;!iRmcZpLKEXb2%Mh ztrpaUMW8q&#L&Cv60goO%OU{8$mgJW^?(8QZEm`B=iB>24K1`fgN@pDp(Q0v31V5)DkEp6u_4Z8S=rR;HW$7flDNajO zchDJ#US%8>x)dc%(ZhU@&Cxg68HyQD&{C|}7q}?^s0i1GW;eOt4D`&mn(7|nw5FCxsXdAb2)u{x22z#=y5Mat~)PM!(eDwvk zepXx5;k=S#8HMYj{`p`XU^N**5b(ti4}V}a>$8ZSj*;v4LWAwvLM0P(8LxNCYlwA> zW84ze<fR+KmK#Uw zmE=vV1rhQNg_bs_CY;M-!~OLw$My%Chv)2Uha?pX^xJC-{VSjY5e-~=NSsuCRpyQf zT!EuL4B)!!gfzExeVL6@3-g}R@DoRYS4~-e@~S1xJ~6xgZvpzjTyboi4S?LM10$=B z<7MFKBmDNg`}e{uSAOk4Pq!zGhk6>?#oKHjVxY?{8T@uI>DB;wR^L<$_0HyI+7}2} zcD4|-;|K=gPWly~VoBUX#C}bHkfVhVb`&A1adp}oTmU2=<)V$4rPmylWcgnLp;w^r|EGoPW&!@VhBo2gR9 zpe7){Tycw8-&7N$v_a@#^F?m;5Hi0MW^ePfd3ZQ(g~)3I^!DW6?%Ymh*`9pwbV#&z zf!}OD=OHM*di|FeAhLh+a-Els#$i<25*eLlFUhnjgrsjEg5q>Q>>fYRo)fn~oO&11 zz1FIcwso~7JqX`=P)EP+wG&)Y@G`tta;AmPE88Oh}IU8gkrm*;HoDr{T^WDuvwhofh;Wt`A zw=(i90!OqqYthK43{E`d^&R?Tve7QBNhFN?gKW!a)16RtUoDGg$Q5sU>wFWL3`1oIyW2^}1+{F( zJdFTybA1QU{J2gZtn?jJq^P{JT{*p3pg0H z#Gwb;OSgX}DgmZhjWf!=XfJTbtZ>hB{R~=II(Dg`Qv-7xax_y%0!f5u#b_TMCg9{6 zr!bb)3oJ{bT9?Bk;#~WTIa26fHcxGFkFC5wR{COE!xHq3!xN2THrT3#&NZC(^2#8q zT0Ae;8QtT6;mUlZoO40lD5_N!`gV`jH0{O5+S>=t)M3bOg!Dj<)fhQre46axLz;m+ z7Qg~IXAFNCHKfzSV+nnAHp_k-bJ6*8Mq?O)iq80b<&cBwYBxTow_3;;U(s-`*PpCF z`RMXV<9u1MU5Q=v%Cu63FqIl##~4hjhG21aC&lXDMrWjD8&=C@JcK#R{DGjJJ-Ypw zo^TEjQv>Mn;t&4OS2NQ;0>|v|U_{v+&fPz+*;9_2Iq&044;CP@y=eB__CRV>=MefM{p2_XYYymFz#|{UQ2oc-;im84|$-2m>(dF z^E~Nxe$or=(L3npI?_1FHC0HdPUtlsSCBalHFtlx9SdQhDrA|ILh&i8rmkFp5&d}? zHGxKnnR`Om(g%yft;gC;F08Yf#DDP>Q{?g(*rEqYb)!$rtkt-}^dTDx2~Sd?Ta4vK zqiRzdPahj>^j61Ijki`GZ{pf}?+A#amP{K-Jeo7@oBjD|@+#!R>yj_cu&0rBJ-8)F zD@@0f_k;I{|CB`lSD5%LQy9Zu_Tj}SWM!%kePc(-3o@-v*VMoZ67P$0j@9h66Xxd` z5tNwWE5OO<@;c?qR>JSwKzx&?^vD8OkLU_(5I*J9B{hf_Dn@>K?xu)VS z$=ByXB7XC$%Q#xjkeApsx}dSC6Z1)&VhwWYbw1V4ld;bHH9ueN zgBF%Gg1ulkiiJk=29)nM}33Ook3eKsm!Z?w*7UO0ZxA5^oEup{DeSWy_IoBrY-)SGXsT>d_N!4O@G?xsOdU}-}O@S7KiFjR3f^nAy!VDj0S9F z3v!6t(eDReY~nY_erRV1vUK$3YQcsigR$e4 z+M?7Vyj%#0FYz{)+da?GSL1am^_qnWCdq|a)b5FBi-qy~c{a@wyJp(+GP}hG`1y=6 z0PqvVI?u*z!}fcCFla-qU~IqaqjAxAvP=XV7BYV;_yu7@3(eadS0Cpf2Sy6ZMWn%?~1wQUV(hBoa$ko}fn- z0CSjPRrTsQbWfWv;Cq1(Lp4NZYe6_WS(NR0KGpFY9`?Fg%7!85PkDcS_Q1QbpZZrQR!o~nzskaSalPF$Z| z+FO6wNcIE?e+O>~=VUscH^n6b@2Q#QqUU-sRrcFXT1OoH=9x@geK~e{oW-l$GuA|3 z`5b1+L8qH#qgK046B>Mel`(a4#5soQl*lU7pQ9T>0<8_-8|!eR?3)3Ygw}ugD1Ua#^q?#6Dd0O5X>o-p5|>_$%d(X;HfsuiyR-mGl>9ajplp#F zw7-jWS3-1Kb_h_L*$Fas3kS9qf}WOtdf$KVfvVK7bNA~)m(X$dw!YM5VkwY0>o0oS z9k*xU!|R7)HN1{VmjTjBAH*XDL`!b6GJu1{8-t{2wOj)?^Br`Ixo^`WJu}=W#S?88 z8F|95(E_fElqTuv#i6-=^=t?zEeJdVp;nuerOk#O4LL?M=<5@59?m?oUtB>cJ@}j5 zVV(VZU$eF9S>Byj==^M3+WD=9L2VP~Ao=#&5ds|)fm^?@L7K#&6yQXJ#U9GSzq~8i zlQPq6Op(8?Aw#!CVn*aY+H#cKIEglL1v(@9?xQH~RB6*71xnP4=lYQh6jJOLTESl3 zcp_gRRp6ZOBe`i)d>mn$tm_RFMN0ahA*$JTPGQRptJaw+2iTA}%$z(Mq1osV$@{O) zY^#c3y#3DFS%QspNJs2fL;zNE@FpfRIEF2B;kr`kd@IxO2jPuEedfS^euR(>Jds;>=nzaRL+VQIIv>usCx_YLOzFNy2W@ri1QmXeG@!s-#0UsEMe6%{M*{otm8_!j&+Yb8Bhca3B--s_3{cHOy@bqYk%p)jVI z?GfX_UjEYt3Cc2v@30+Se+&_{A2m2NhDw#It02X38~C>dZ} zcyAh4>uDT~K9v7DWtC;n{uV9xXgp5J7al;&TInDjSj{e=v5xRUV{~9k!++=jVf%x`QCU6EEpN7#rri0~VI=x;1UI@blGN8gm2aDO-6EDALuXKYN|16j#4 z)rnW6JfKSq-P2i&#g9wsLH;{(A(wBC?$Z^k&+at$Fb{;Xs#8bOpR+oX1oQf)QcA=wtwJnrdWCNtfNt-Mu=*rnO4?~=}7?BmA zshjT`Xzez9BD}||en`{wWM~&`*z1fM(}2X;S@XROQ{F>{YGx-Hzv1SG7*;6+o4ORz zjbCcbr(XhXs^CDixAl%J3w-tHWGv!yuq~?ZdEc}1ji=AC;f)f1-}#~nmDmY7Uw8Dg zBgyw@x<^Ek&QfEyhyOjsYj1UB<+xl<{VKEzDSJWn;d^p};%7^xL~@=T$tqj|-`=T) z{xVg9m#v1lN%)3a*-TXJ-LRdT)6c_xQgLV%N|1RKff(6|Gefkn&8WK@KavVJ;CW5z zOPs|}!0clHlL-fZ*8?ZYNc$(DA03#7ZEu|M@S)aWw#5N zM3_@mZX}maN>rcsigGhB>4D&id_Quo3OHD7E%?uZMRq5}4I-QQa2d^F zLcMVX(QPu)PAf@!9U(b?p_8L7_!Qj|^QA>e)1(B0iSH1s3ueyDb}-6@J%AYd1Q|qBwJ?F- z##+`q*sKPQk+TV1zZ+94j?y&A1G`7C&5MF!9%-WnEjyvMb&*08jgNF;cK%K!oB0{@($9zMlO_SK^sD%jp{Vv?+w6MfGc58^jbFN$9RaF$4m*CPm~9aL$v%eEEJ#3g$0gqY`q1NJxK@b`yg;yc@D^XYI4A$P+fa4OI{&s3Y*DYPIWx+>=mHp5Nr z57Gc}Tc%jV(=ax<=0Z#rORQPsl>M$W>V|g6d{sBFuDPV%c?gqYB2ErV{=m9CR(4tkLhVUxXnNx5 z_{fzVmw$Z$Ci%8)=0~2Nf%%t25bT>j=L|0L=O)bea7AC~w8N`3#EWR_WFYY$*eQZQ znQYRaH+HszPW1PYJE%+c^Z1-pX@E`wWQw}C*3Lg!mQQqET6x69d6H+qj;A+qQD5iLKqD;P;oDKd0bq5JZq49 zn{SC*o-guW8^-lKanvIy0>$!!X>g9wJf-iPD|ab}K_0J<#6E9R1n4@+Vf z;t(`btd&xl;Vt+~Yb!9)KOThX)l+4DTK;-1 ztXDs5YMRW`_x<(q*UN1Mo7c-OVl!`Rgwgf{>#rSyY*rs2ZNPURTCXw#`%+v%2Id@w zGJ8;K@9`LKi==KIJQV$!hHTZ`3MwECMz7Iw<3t|JpXQZQm_CxG^f(rMQlS#2396mH(V3WBT_1Bud-$e zmaTKJ=9}S5bsW`h90H#5>8A22R@5elx*~na4!A-HGlAaeWc0JkB8NEugyi5We z<3i~&)&%+eaY(VS)jQE_GPbvR|JnkHRY>y}OJ|c4qZtj0ESn>?BOmL9*~6IBR999- zkSsgl+A!7jhvJJqrsd2P0LYW9U9zQ65EW47jpQW&&&tRzX}OgCyRsqX=}hYs7|B2Z zhZKYPM2obsZ@7D*86!Kv7VE`J#J#1lyC~GP@fkIX7?JPr66F_zjg0skf z;t|X5#F2B5hqF%cye!81ny+Tz;a@wX0-wF;6C5Gd{N4_cu@n0?eMVUkW5n`(%kkZ@ zziU32NCy>RSp*+1XluCZ&4Lvk7v!LCy*LJ4eE5SX^hkp}jr$hn{*E{@fROExXEOxI z^lf38aXpBzF@JYCR>N zt_Z%V;DkKzILzZ+nqao5W;s5KVNuU|jT-GOY7@%ke)47-b5O5h%0oO}Y6cpwdPjpE zh;Ie+H5flb-o0!nLf7Z@3ghaj3cf6TL6XG@l_Tfu+n?NU?8D~d?sZa+1-;l`v*)pf=>&6j3&15<2#`w1)y2MpGRfo zxfRQHYLFbq= z<>>UTwbeucP#5>~?x0AOc3%Rgyr7&7FmoRe=9K2P9|CmKDrn_;rBUs(QX6(&?H3s` z$&r-|vz9UPV1xT00~Hwaqlkl(42QTv;J&{NLobo-=AmT(8dj^a2FLgIup_z;^w=;1toYeouHk2cT{kvv}`okJ!FC_m|j_q0XR zmsojUuB4Z94AVjUq_i*cwdd_c2zPPK4o&+jaC>36449D!o6A)9uUF}(7>kQiNY$q6 z3fFUk01?1>icrU0Ip~Wxw81Y4m1_e9Vt*0xh{hMyzV>JRfPhkyL9p*o0_pS#7ko4_ zt(w~Z7iXjvxSXvNccIq{>LXztSETje`p3BhEn0Q76oK9-nL%z7*E+@h=FKlQt?74I zw~s^OWs_zg`&3vMy}RF&6?=1V-)a+h6Z-tvxCxuW0$7QArKo8XLUPOhnryg)hq2fI zj8Ccgt|623{tl&>sYHCJ!+bNe22(ROugBX8uN=C->P&T+Mfo=#gY_QQM^em*smS%z zLS6;<09=+gRBg#F_)FKRyq{#}dUs;WWn&bxSSF=c$f2+xwCScqyQt>w1D$n(pYH@7 z)}6J%x2ZIR(-Yr4^JaI4foeIaY^$GoBHh)Cabf~N5FWlJzy=l*al`vVzlm8MAFimz zxF^2`(#L3{Jy$_S=ec9Z?=Ni8-?vN=b_~o>Tt$XQN(tGbf8=K%?4|@3ep%9Npe#yg zm|w||swVnnh>)Mx$8+1biXV^OViGR=fS?QVKmN*r#@=*|fb(*oSD<-+;vyhHM|5{S zG7{b=Q?KZ{aKZuPc)(DlHBBv~--2P@Xn+VR`v3ac!Mr^HGm1otPJOE)?&T*pXJ5=E zBXQ<@Np}@!_2R-$-{S@?QF9IcjO$FNBQ=tjAuk6Isaul$^0XPpv^nmv$R{?a<3D zWMi|hT~HRB;``F|F^P3En8?uEqJD+LEvPcH8?|~`g+~IfOd!fB$kZQo4V>iKwI_O^ zx3r&c#~W;`RXNY08R8t2Ul0{DpTe4#>AAu*%U+%!E9fMvI6d1$rZU?ynG)jUTDNBx zncBAXF#}`482^_?^)j6uaUqCb$Y*$?_H2Prl9nq+dKs!yUMW-AhiajXf~jax)M6@7L{ zj%YVVM%%jKXJEGt;jb(gperguRiM*VF}6zf)bAmz^q2p>+cLTTZd0gfLX+nqq6BZ< zST~vmnHc~PueUldVo4pLQ-uZx+UUg!0yuC%sWF(;Ut$B8=4zoW>~il|2dwL=`w5*xhOi ztiY;^Nm;Kw(~xZ#zS`XGz7v-2i?#79_Cdc(s zgenjE3;PGfi_LuMqN#GZicZ~Iv8~K;UqPU%heUjk`}3{_mp@1Bn1(s8(l7hjn!jNs zN9hxf+iogwFF`EP69fDyzaDPSS@@fr8C?p-=Aj*~CEW?!V{|tE7^&NPERG|Ngaxaq zeK%>AW2jP^*4jY!F%8L1_jZaYN*+~H23-5Wf=cJP`5WJz21IW>mvCl4P_lp8RWhVA zHQ73p8wf5FqhQTVmv64Cl4(@(dxSInt<*CVe<0Bk1?vvJ%xci-VlX^h-Cc7uE5+O5l!r-YU^vAOo^L>^fAxj-O^8Df*D5zRnKmpld!Lc@B{Jc5+6nOgM)%_ zAgZV_rjHRvH+MgpXR)Uhs{j{~t9Hb~yMDLjXwSq?9bat5Dc-^SkpI8KT;#05tKf6# zGBVP&H1DU13LKS4HqP6aelxV&$#hQ8@4-goO<{L3SH(hE>}>fcM~`3kWNWPxmG!zW z*Ga2ozukx{6o%f?06b%af$0=80O4h8<7QQSTmjE-h^+s`(9AeAr-(io|A|+=4;BtC8{F2 zysAutY#x_oY3&rXAg5kYp0RojVI%qYLRO4gim6H->TQTf$QDY1CL!9d9jYjgy~{E} z=_lbC7L_YI3&>{YEKVk$4Z$-rf7apVgTLLZJl8%L`;>d3%wSt(?^q>&)xIStWq;fZ z*KwhB;MSBNx<%r&tWN9hmK&JI>j+`|imt+=}phFO@ zAg9qn=Aefx&-{2PVHjwZd>%KgjeC(nOsxl$U2QO@lX5)MWU}&QO@j;#@38MEYIl$| zuTm=nz$zBD&N*;0)Xlo9)&5-nwv{is(vAX!r|+AytmS%e3ibGq-1~iMO*Jb=H!`+Y3?4Fhwet=F zMTmsUMiOI$Aj3fqrNd($e$3iiLO0A{Q!KTf-V%;i9@lPXF3$OZK#KY1`0Ihg#3Dt0 zf7_}`O@wTsQ}fJCjX_6hz|D^uRlXi1oEF}G@atTt{Ly-JR5wb=_%ApD0$l~YDv&;B zc+r*Z3=MZr_n>ySko>J9E5~-Vzo)eWr{YB`*l|7*Egw0eU~Y=lEc{GY7>N|}UJSg+ z@SJf!n&DJ8_@?F9qWBJ8A{F&!l@FnhACNZqPqWr^P_@jn5C+7HMvWq))T`71=E8jw zgWi4;9l?4;0I2?D^&fsi?EI%Ao2inOByaz|guzcU*6$~(3#qdnEf5L`jE46-BKCU1 z+&^lbq4*ttM`h^1C?s}X#NtU$Y?F1hxjNDMj|=if$y>NS^m)0O89rV8twGdp^CQ;4 zI&=l)9n3eaCnZ|{TCj-a6&Ct%cr(Ul1{)G|c{kjrGVV&|YPVzJXrgD30 zFrcf>BlV=H9b{21Ao((NZq9~EL4vIez9zzVmf$I$kwF^&j69Ibb{1D}SR>f}qq>ap z{EK(HHUvpA7Wu-#8gJAgW~F2JV-Fbsh5s2d<1(eV^i9gqZ|b`Kn=*9=ovq2LWvfZx z&V8(s$1Dzoi?i|BfL6O-69zXPrY0YSy-_UJu zqpG98co?cwG9{A+RO(6G81$5d!pDgkcJLi5RhuAFwPatE6KNbtO!i9yu-Tad4Qh|;{z+hsC8*A!3IysUi(<>g@|qyorIorb?E;|EgqY~ zNP`Y;ePG`d!9*%}v6m_$L#hUH6M-OrPta&f32->t?W=;vyTb!nJ$U<1)H(oheVToJ zO2-6NCPrELvRzQsgpoD$6#xlRm5b{e!8xwW7tcuA;6Cma$t_O6xkMa@-P=_lPU)jK8pg);(eTvn~Z~3MO2O#4!DpoZhKUfA$MkVfOw#BBGz?u z9joJ(;;gz$u+;lm6J=kzO1uxhJ& zZAjdFPsR}nZ2giwwmPBU5Ax7sSq_fPRw`7a#rcPPCEres3OAv2o@ji9#Pw+5o-g9t zY*OO9DIcZpk_E}tD8c_>J`(l+0s4YiYyXA-e_AfUGfk9GSJ_rdD7X5D&CI@o!%zYK zkX#B?r?7paPkE96#vcB?kHFibU-u+M34|Ah%x40TKME2m>da(mZTH!Xhw#_if4sSzXL{1XUB?F^XEwXd zJE|r`7~fwcDp1tuvM}|p!|)H^3+ar)UQ=SQ~|JHop`7ULhJm0^iQB377|JN$YQtVgR2T$N}EI~Of?(ph=^ zXAe01)v|%xk31(EnqCLi{Q|cnKO?YGD-*dS`v6KVXzMphuPQ~O1$-SAqWb+q7pD22 zAuJ4|WwDEn^5NSx7JKn~68(AeO&x^ioJWwF}xxRRdB75S6=VVJ5XI z{;K9XC(3Lwt9ywU>*R9xfLJ_yL{3@Xq=^_Sow-Sv#cip3VZCrBxCHtzLN!hT)mXZn zc6w7h1Rs*s0E$o_`RX}P8UgH88Kz#q_;H285B@M2plu7LZ>Uo;y7bXD;k7l(^uF>K-sjDsI}|U>5f8${TLnPltMEfUR` z3UOsm7WVV&{gHbs<=_TUT8PEPJfF-Z@wiE9SvlP-1^;qvFS(l-B`v#MJViLM_4fURYm?}IY)!|qjGpGb+*;}NK{!{ss0exX^^x9O zCDq#~s^IBpQ>v__EZbt$Pq})3rv57ZK(G3x688Q0c~mg3LJlSSOJt$stA!cw@|s`y z19e^=)kCrV2PT*gX`Jd%3wU!=F2Zyg+)}7zb8%Abdq4335j9$o+UI(ow~9U#K5yc5 zh4|ss$tsUKdP~QXkDdJnh3({>(3x8`zH9Zlp?>7Z}s05XFF@G00wV8m*>_hpQn|pi;Tp_%&Z&G zU$qX|U1uyNsN26NSJ9XSwTG8ZpH_!+;#0d!EHa_rmX9)|tgXY^Z7`W8{~2s9gN-03 zue-cBuP>R4n{5p=2|Cfh7`bMoU(kOIR#(HAqRHDLy*7t)n@pm>J+WfEOn@K2r(OjZ(R0B-k(gPHcRY37k(n}8CkP?+q~aCx~3FGj*E|yFV|yl*-O;% z@lmOiZH%G&y|JNf8?tu+ao{m*dpBbHPV$}%6Ts4{eihi$HW^sG7z>mf@ACZm7QTielnvB+ z{X^yUq5Mdug~-OQG2J9+Km{)nD9`$DzOz(vMX2`(9i((GmBu)a^-!FcBB!X8*{}ap zeyL#g-?s4<#6#{hhpx+2VF(NZh6`59B`uWqhYH4_P`#PEU|_;O6#^&~g3nt&mRo8J z>jBRmKBcfR^`JxK`A-;g)Bc65IZ0Fj&*Sx9?se%$-Gu_+2_Q?Ss_x#vhA~(8G{=(e zc*}q8tF_v$Bw&(dd=O51mgDQPjVH&MdDJsH=kZF^^$a3l7EM1MmA&g7%3Em1Mn@9R z@bop4e)z1puDyNtcmn)4$K~%LKYVU-d6~SdoZOXKCxY%<=#N;EH%(xp(l1$T#TM}0 z!vW~@t-mk{ss4Q?-oGC=Jznn3e?~AE5~Tly)9b9@KSvl$?*HeVWB*TWZ{^77 zDf)O1)G+iE|I`dE*k)1B6C67B?a$?_(}2D^)Fvi zH*>gS!C8#8t5G^fiiLb^GPq6FxTB-om!1v1;W&_)^2QEJCrT|@dM{WQqbigMm$Lko zLS6Z4n9tuLgHuxIL?hYyh5z@N$+*|pKY|W|8Wv8Pe%r)>89xh1|8>E)j5pc(n*Mca zPUz(&@bkj~S$6;si>_P_!lJBo@MZ6#LWPsv7;~x@zs5I!+~87B?QDgc;4(?Dwo@%4 zZjG&Nbe?d19;p_mI%NKg)N7*i!qC|Giz!+(;Y5b8v16)?v4;m|^S-mLK-;!ek}|Pg zbop-$c)w{ATKHLkn>;0)p`jmz31Ur{`Rd=YkQBLW>z>!RV1>JhaEzAylXZd&(N zLnfD72ZS5-5x6e~^3T~$f8O8S?Yzl`3TVF5e&ZE_>LLUKu@>PgCFS_xyY{gC$#^%v zV?TRTk`_%$v7$;Vx8-Ws#56VNJN&BuR-PD?tCZ!LskZmHY8clS0~`ji9mf8f5D3Kx zA(Z}JD3Qro@{EUNSm(S@3)$UXZ;_8gyR=HTR#O8m?G%speJT7OJW@-;x66&L_+8VI z_%{Of3aTbCcj5lrH1U$qKRb~R@f^?PiLV98>*Wj>c8%3gXxjPx?SB(s&C*pjVr{S7 z_0P^`En+yYBs|@@zX|i0jfbG{2Q&N~+nmnZ6}R4DZHI+|puoxF=R-MeV#@(dH+*8w z?I_(gKg_P4K)i#$f>%_rK9ZERf^6q#Z|8n^8a9QI2>s=E01_$S9^v9K1EGhA{TSYn!k>1>NGJ33 zN3D$doZT62-u%w_$x&W{;3KONR{7XeJG&k`VatadOJVInZ^kVZM>y-_G3L;M8a}va zj)O}t+|R#F&7)-hQhUi)Gj3&ByE)zr zVnREWFkUCB*tI^l_a_Buy!qNZyS`l>H#Pec!>S7m1b3vzC%%TyAGkUC z;>4&9&j!FPSV4Ktp1U|*BK~nYZJtp*VJps?)Ot%REmqj{fAnSkgAuXH|CqJCLyXvB zwlt@-<#-9o&%ZFUF}WZE)5Ykbpl0o-?B@M z$rG2h_q2C8k&8jR3u&JDv_*BAkpg@c(9=VOM$<4W-q0~^@xY?8-6!>J z?&8z+Nc{F9Ouc4||>{CAeS z&uZs&)@m716DLF>0D~)tqs5y-qbO;-)_k5`X24t3X7l5c#Da!tnwYUHe~E~8g@H3B zD-oeVv>7nBTt>N&$49c7w0-`2h{B6L;XQu7C~l4Kdv~rKeVq`}_S!V5aOOQ7)6Au4 zLc>4E^xMJNB_CJ$&zLY-TNWFs?H6=z&dVy>vS(nQu#*QFcXZ{7W7D_JtUW)F2{tcp zEkq5;W*%~4qio0waYvSrg_jzet(noe>qQm!@Dl_tFNVS%_X}tSto-N~;tjX~mvc+W z{9tM44tc>@uKM(wm6ND8LQnszr`(+ySFIO3s}7Ilo$rpROb;dhg@Fn8XjNbxVK;uX zJ*K)YD0u0tz+~}$gdL?xVce&3%Kl(LOb4T#)c5#F^x7MhF)_ulHV@O7VSY)0u4bLi zSxsx~%{aWh9Z^f-ft9W&r)MH<_(+a_t2_FLd2Cwoa+X8&q~}>b(S7bbo!~I?stH_J zPdu5)zKxoq+~RoawLhm?|0Gr>qI010or3KYPgbBPx(Zn0GqwssQgMIwNXinP);#5L za0(pUDhbq(V%DCemCGY3a2%Xjk3g24eDPw74m|}nb>u=+{*;9OU^@e7h93>&j3Zre zGkgp?xO=j?vKc&L=&YjMJRR~ z@Y3U5R!HmsSbGBg1F{#Ws7@d&duKH7t?8q z=^AyaS~gdrqv-uPiZ+%1YN7}Sk04Pd^3N?zvtdIam208eSiUUsIoZlYdvu)d1|hPqP>A#fK~ijz?x4@rz%)tW$4s> z9#viqLz(tfRi=V?!iD-``|+1-dn^IjKD^;bVfr5h)BV5cGU}SxMJ|68j|U{%JQdUnzjlK5QSBR2fiD^=_Hayc zP#%KN=2hK4_2e5}Bn502zZ4akU3H{op-=HRN0LVdVMUI5x4I@x9Y;y5d_rjsgw13c zt@3BTeKH#l(Ti^FW_Ri?oA&54JJh8X^P(r1OX7lxzJy9HiHg)$`c;*s2RpJsaDLhT zX{Sy8=SZpr?T0}u?*ik@A!qO2gh8jUaRF-u37<$XWKLc&0t-N#5g~Xgh~9xj$@v8d zZ0Qe5g$>rb-=SG*Yt`;c&1J>f8fiF*+YEr~Jf(9N>W6Jn^d!}ZnJoejh`r?h!QNX2 z#TBk?x+F*vG!P_sfMAWgLlV4$LqqT+xVu|$cbeet?ykYz-QC^&EVB1}wa?k}P0gH| zAKz5XFH)pyb+7Ku%6gyozOGw4B~p6YQIXew+S}x=qw>ksl)#Hbaru#lvNq4SY8Quz z6s>gD%@#yn96;%7Jh}Q|P7k)YCTvqCljgj7`m3a4RDQ$>;jiqR@qDm&c?l*o0%39V z1M4yA_6M@6;NKO8AqOiV5vf96q8tY^J8II)vN{-ML1;7x07mC$y3qT&z0H>V)-v8# zLp-5De2~zy@EQYW(hZhTKtmyto3WfL~W|_!hk31R*U$XIzra zZNNvfkV{?`S#uV}aL$h!04wB1=3JL_Vq;|B-O#l){XiiBGEo(>RJx-B21NKBC$Z*g z7?4~>kWQqLJ9mfaycx$3$R-dh9w0l$k+Rp-mhOzq6L*xu8hpMyYlLbs2woNof`bOA zOPusDQdWI{E!%^}V4vwGLk>;WDBQ)iHT4kpx?c{oSRqR*j-+zjv~gb8Ub zneZkdgT405&IZOD&FFI5>}52MjB7I_2j;xk$zTD_kGWfC7& zlR%3uOTGzrSIP2va3r&M;GlzUCyHg<28>M(PcO;O{nOGD94vlpZp|AX>P6xw-GT%_ zwnW`orrZ~ir^?72eId&>#INE{UAWBP%t{mtMLKS^(0E>cF6CPZIF97RLJ(a-iOY|e#5+J3aQV4Uw)p}-(h=)1TABoyZGdKBW<4OHScqI z=~*0WQP>vwnOAjE(h#%~dj~x>{v@6sq{|WuUU|UD-KImB;u^-~0k*~SY5a_{OJIHA znJP$sz)Qy)6Elvv$}*a5MXvX@4SJ9YChojm1l?->y;^t^6Ir4dKYpxzSNj> z#$<|D`^r*M+w!oNP636>A=@i;l8YM=59x_V?8dEHw1$Uf4+snoWU^>)n1@WW#)TVZ|z*R(NC0D`98HR&g>OOSjNEw^F*LMK1PIWfq=d?c7}Avgdf8-e$wWl_nk_h9s+g4| z%J92hiqBT;yLgfG8`$2A6#WGD2q-SzHV~@n?3wAOL816Bgpv_6{a=*qzb;DV$}Y(P zrwm1d$rKl8G!Oj#FG2Qy1VMHUx;txsHiRAHrN|_?DFm6Q(3XYK9s86dRvhym z-gs={pgxPjG|(`KE9VzoiC&Iz;9TVLbZ6M{j2gK9(-NiuyMAF`vpU@4Jy{b7C)qGBT8swj*PiYs`${$O?=`RRVH#io{HvXidEL(X@c8jsA1A=>NWlIM zNF`e&H(%42ycd7%FNGpYKTCWaaeVM|=xcQD<>QsWAy_`6VDxj+ZxZ|zc=*+8=&r6_iaX4;_hh$}Z{On1IU$@A zzS1|PKM~CIZGIg3EdZqOkla#kYDrk1AQ5Mc8e<}_}t-Oll-Y+lrtY-L(vld$%v@wpatKX73z9z5dL(we+QwM&3So@%wnHxQCTWK8i;Mv_&qk{ zV?xZ*?Ur0`#@y7C?YdwCvHWEX{48~x|KJ(R>o5}GUW=3;E7TpZ)``3A9c3?N-#EZ1 z+y($lTB*9YSSIw$Zf}DD2rtVgUX$MrKBcem0dw@0utg;xqL(RGEKAugY>}LIk0>@_ zb6#0PaqNEvHL$t5TT0oM4`OEc%|?4XzgTWg_oOi#5Aw@;ElP#Tz4w? z@m;F0LY|)iQ72BjuGD72)16>+^)TUWST>LK!2v=BU%v2l&6QT<0Sg13q_2VEWfz39 z0BcfD5;@NLn-D%+@C$xM&uv8dMRj9rOAG4(XW)GR7! zX!gdO_CRu`E*gbWLQvR6OJyM<^b3bV;dmHHcx62aK?=9o*!LXbU*U#gQ71Sx|A>EN zggS3Bca+ghu_yju9!BSh9YC4-qf{#pph`A`FJ(x{V^B@L=|+8(DHpq%*!%>`2d3_G$8}^t+qp z$^Jpx2A77+=66_qqL+l_nz28SZ#h+9A|cE^eA;E_&=lrpHhcf;+3=gi1rO4voM5_1 zb*~W4>$f};lZ7h;<{gh~Hv?JNx|;zVDYe(ffI{p}#!gn?woQjw4uNn3#LqMbIpvV5 zlTc|P#;kKax797077eLNv*Q>JK@DC?jBb*8o5LzM0?VlJ^Z+d0trF&l)hww25^ z$a9soW#>&rV@a2l@!+-kh~uZ($&G3+y^7p(TkYbEdzW*FM;XRZ7u}3v*Vs$i{T?29 zxu!e2PqVRon8ce)qS@giyg|KQTk3OOT}%t1{#%c;STx|1+%#qjhYE?fkwd}EGJ$zF zufq>+H*^E1ocv-(zp@Y>8sT3q%KU3iuXcY|gq?gPB@nP)X~#wDCTX@Yv)H$450V%B z2z0m8dK-)0RU8mmS`ibiJ2lz8iN2n3i5eoz=oB)mSHyTpzkcEQ5j=?&SXqpa?tb;1 zc=Bdid1`Ki9WP8Ww)G&vt{-BM@rYdu`_>T?U=fdhbuKB46}(Q(PA1i2v$Z^2#`@hW zOS?^!XDNHGNJ176{l*78Q*Ov^b@<}+L;&v7)BU%s>}5n`La9161~f z{5!Y)AgQ9exSrwaTJAV8+>Yo}Ltc6~Z*C4vV%Cwv- z1MFf4XVpnQ%owSzQ}}j>{ifm`#&~HZU)qOh5|d zMuX`!WHk*G3eH|(?XjPYqCroqWSlG>WebAlilPsiLcOB~L}EiQ7oxi_^$}l3gIyAC zY%$bOMXjW%d2MF4s75RE372$7H;vzYd#8Zd&+^EA>_ersI&-@XvH5*SY8oGFOuXN3 z#-G}U9tvg(fD3L?71)eAtZCvJlFSLfx|1d#v_z$L{E4@UMon7SwN8G;+7SPI=Ah|q zsb$cG`3E#m;U0-(eShJ?Do6+4-eu71$1B`YlLq6x!)+LbLFGBs&puDAq2H{KVdEG! zxk2RaW%d}>xun=O3cKoJ1((jbQG5yZmP(tvxlsujXROyfvO#oOXPTK!B6BZ(30!B6 zdNcaj-F@)%vM7S)!zWwbemo2t`Q$MktsB~=)5hEv;3F1h$kpg!YL)ZMeTl!_8H@)% z1cEJK83@hW@j)BwXjjDc1a7UVZ4+GJ3KiF&sbI{-89?s3WhbXjQ?IMU`3~lIC2hBp*hZvJnhA!bFM-aYUrS3e!-SYR^K~& zSf3tA7&8g8>mD|da2aS|>eiCx5yheQ2n`_3T*KCnjs(zN6RG=OpO3M`9UG6V;jy{&pm38{^s#@s(ydoo<(JEK z{Ph^!VYE^SKxxQ^RSOaP#h4K8`W{vG3u5f)^9Pc?XT8DDNFnQydlo4VBAjN$5HABL zJzh=UY>M=003a?(GVp1I(>eW&TRe@Er|@ndB%6%5ZhhE9-3Lx8hRdi`mNEEFckA>@>vLRXwm*FhM6%dF6+nTebK^bQtg%W3V3apY zO{8uL#>_daB-BzAq^~qfjK0ntJJwcKA+J4r#LPo{9APSEEq#l{Q(IE8d-G88xQH^IU3AN9H#Tsj~#056eB+M4M6w#Q=svp_-M((EC}{j$Dcd(rH8w|WY?4GcZ-S%(R;on;=Xu4+z~#J zrpUE;k7acPl9#~3ppK`z2|Og(iMra@{4_L7Yep0~eklj6f_W|b+%B&0-^RQQeAm{Y zDMfg;BFl|@VCvi4M*(Zb{J-wsUcOj1uj_)q2aLH$9Rsm1RNzTk{`jh^EY{V-QA ztO9+3EVnz~>~{K_-?v1LNV}p2I3s%gp}+r7CyyqTp6oth;j+q5}(G?OMdir#CV32eRYQnGeRH&TRfe zvQvFDS^(Gd@Myxz-P1)j{{rSU^Z!ei^2n0DMVLmKP!Me%D`7FMVw5fIYTBeA*;k_f z*SRD>Qp`M*Cqa0K=8f8ePgTiH^l3nn;A(hFohRbjvp^?rdu#)dqtL4Z) z)pY^CR+MNyI!3!_RiWO|n?F34FV(zZkHj*TJi@$P;S_!}#7X23yr^zceZ25SYA)7r z2=NfZ&q&=sMYQ@%7Sl)qShmvHc5?eC?+?HpWxJFJUW(h?whZY_TU80raq`xuGKTv~ zAmDGkIPw)z04JFof)V%@21VoNxy?iPXVyw%ORe3OTi$u#tt$>15np-4Rmt!h+=t{-NbS=a*9S8uS|_09~qlvkg~ z#X&vtPMS3UJV~I*HFVPfP{@U>beZ#S-x!{-dBe8Qbo`6)q*rMMIc6lz^gWf!_4B>; zgTDcp<=l2?EyevShVijW#TCaVhqYhi3+DCT;hZ`+=ldpt)6k!$ynB*q>deNNrIVoX z2&2*!MYG$dLi&&7){roPbJDrQ9xrY$0O;kp z=F|h8MCIPp)BR52@1NK#-MA%(4G&bf(6}(KYSQj8}xsw_$A@|v96Z!;VpJ=m2clp@& zgRC_Qe`6+*V26nVK>)KZeK*%uUG=Z4l=rT+AgnY}ob|1F*0`+?jqblu{Dc+o9B-+) zw^)=^4T!=+hmQXUeq|jX)3=_p@3C zCt09@_4t7I+evunwowzd9 z&DowR()81QOZ^u;sd6}B2?~MLjCy9Hn))whvAFA~Umn{YM8z_nRWRhIgG*sx;(MPZ z{z00lW1d2qM#rM8?y=b~a@J3dt#Oq92cGTvAfl_yD-FU#yNnk;@F zudFtj!4^Zr7v{Qb`O_;mEVJL94i2;`+;&dFA`D~vs!;|GzYgrpxfZ~;HzmNFNCmOh za`F~_8}l{AOv3Vup)iM<_SN}TA@L5Ft`mh5=+EOxm&cf66x_`*Nz@4!2PLY1K7%WE z$xMQ%Wa@>biJaaXVt!KKc8%gVht`;EYJ>}N)2JM!3Rc)1)4!|_cT)WLT301*LB)ux z-R9efTcXj1cf&K|6QPUV9Dlo~%0T=uI|LJvGk)~+5-6GA&0{6q%J+&YY>!KOldJHE z81JAAjV3;0OuB@doQQ2gU4JiCrx5T*X0#2wO{J%y7L%Yhl&hWIGY@`(fRGB~J_g4! z%$i6Cax-*T&H8$aDN!?Qz6--Qv%XN{9wo?)Qlrw1z?tE?{mEtKl^2D>{S{b1Z~MZ# z{^^&?ENe%l*{O0KWwYJWw~AFU4u8c2MdT<8Se24PvP6XZo*{47xjc-==!vrBBEx80pyRCOO`a_1x?TwxzlAzd3MDqDC&T4lHfh(P06`n@Ont8RFW3lrqy|Yw##4(ebND+AS@0#yZ$v+o;dWN}Z zP>h)hG>tT6Y3-Gt!C8**ScS##h(IQ>C8P$uRBRc9Ai#tyn-Iid_McEK+Ut> z^>f(;uT_Tnhsj8cOu|o)&K=m-j%U2n);!<}s$s1TXE%Q*-&6G7rBF8LMx@iG3IlVRe-{SEKF{%Yrmr1d)(n9rZ{z=TBVSfz~7Xy>VN%JhVo zZSjSvh?2{dH9pJkxkG`y`jZ?)kh}l^(wNEEczzG5PsYcF6WaCC#7;^(sHdQ6%8SUX zm%Lu@8#U|PVg0EW;`BO>Gvv5Es%QGS^Z-Tbgb7@+5a7nOneV8)9bX7>IsA-)OT(%~ zXAu$v$v7nXbCCI%uMjWm@N3HFPY85lNQwlcqY|f1R=tye-Dfa0OgZwS+8&N!i}%EM zBu{&>?kgh7oW{UJ;pIM81&wO8kHxxIv9ym;^u=ijoy@+o7U2kKWFX}`cOWENUShy(Z1Y`!Q25^=ao+z#HB}*3~GD%nV2`QR%(nQ%iJ|4FM(sz6e(*-Ao(6}@# z=|8FOG+tTWC9yB9A@%@H5Ge#cjiV|mdZ+YHdJe06ybdGA54ee*7U!3ssdE%;=C&@` z_lc^72h_zZ_9hQ{>j#{h&_^|J`TUz=ZJ)jVK$a`lbEaa2H1N}U!Gv)BMfav`1~KcB)2FNX3Wk1}3uVfOIyI#LM8|8y2z+~i0Ke<&i4a!H zPlkG?r_-)ECcU*6{oVBSXX`h(d)iWOV!d`r4GMRt)qnd&#=WAqKd-a57k!rrQhs+YjaI_KEE4gXkJZ)FsvpDOH=ZRy@Tfs4agsH{{stI7d_v!dV ze~NtsLasy@ngJiB>;tAKM?-@nKQ~bEp{u9)u^&(3?RM^0#2hF-(k}*x!BtcrV)GD6 zYgdH=nq!hL)}p8=%90@@QuY>^tz-T__h3bdSa(e#&)dzJw92}J2;pNf&Zab7X|{81 zMFqc1ziKYJIxmduVaGoznc__}1?q%2ui5tXPu`W4Qv1^G$@AD59lu0EEo~Zw_cUZs zZt2b^(}@F6{Q-5u5XtK6b4Cj$RF(Ra z#*m-)GGU00Ge^niv=QHgmOry)zc{coLBWkrcYzE`OpW(03lCR~v{j^$)L+JIh=-g1 zqCf+!dI~D{O^3HmI#1V)I~zQ7S9}5oxjDtZVh0aE8sM|%;~x8bs4PBz@O*OIS`}Da zV1Zj5OT3zDhdp+W>$i@rl<(-ZB`u8bHro(gOCY;FZ@9dmuOkPkH*nP63{H`uNcQNIi!baL)Sd7 zxgKL$*Pbulz)=Uq6n3hvLhttyfp?Ft{_!#IXRIau!oK^vSh38jC)%nESfS?*s}@qh zG_Xz?x$ly|ap;n|v%lAF`B+rW_++Dp?(@t0j=rX;aC;Xif25<~W@f^)u>az(z6F5z zq&0Wm_`77j(W@qdh9r64Fg_m+A7I_89#bVxUu(?#w&z=XX~i{w2t?lTpb5Ib-@`1k ziKc-c01#GngiW?rLT@sVUrYqzr1dg%DX~VjIQ74QV7nz`4d&mwqA;P@Zo2XGm71aG zZXfX_e4bwuHm)25oP@mQgrYH17Tp}Re1Xespy5XbN_3o87vp$-NXuT3HguJ;?f$Is zTinGj1T;=i?NZ_SeUy3P2`c-}H($z`=MDB2BX2-cG(&;J;3}~5Sif{_ygWg0M`k+P zNVr${PN9ex;5H4t;PHTm{_1f53pVm8N3YnKxVa6@rH|!^9_3U!p^7nSswc+8#cdaR zpf@}pmGrZm!md^3r&!uF`?2GTiQGj#P~<_w^>@Qv(^tG~&tL9utYQZ=@W3BnK4JbR zKAaWV=MM-5<}Zs6{QPH5;Q!zWLEezMvR zt=fsWJ0Kq6J@%3P4?^??zGGEpr|Nir^?2Qc^;Q3?|3$#8MTTnbQLbC!i23)>$r~Ds zpVkIUhVTR6)Ix(GUo=%$k69ESJ*P#NmFJAD85Ix`uv0Sg&SV9}WH7m!KJ-=E5`6rH+KE@(X%@de^=c z(JsruocTYU1Wd=&EYE|dEzQz6hF^-qR z1(*#vtm;V?%n2EsE+qHXo(sR~3xz@@6Zm*-h3pWZBL?))gM)?rMKI09+E|{? z$kob7r&?Sx3e9D5AujXL@JK<}a^ARi8S^&~dAO+#-vB&{%+5XR|JI7Lt68Vqc}6b+ zhxIZ0xGS&oP=>~5QiItz=7CPIey)S<=M{7p&!rC_zJQJuaJg?!f(r4JjQ-B#^vdgqld)u_wt&{XQp-}*#6%rEb> zi`^oA&2XLOWznj7uNIGWBYKciav_6yG=~$Rm4jbiZjzx3=)}3C?a^QVsF&giRF91{ zu8v{0PI%bg?L;SOPQ!WpFvEOku)mL}@<(``B(hb+V=$Id7f77+DlGD`*XNg(-*eE3 zXlpd>ePK$=HW}8#L7B05ai>2*?_FmT6P3(66)ufh_e!%mRiw+->1$U~SVB3%-Me!J zb*)6atw+VJgY8 zqLXe-559HQL=f7JjU#(^A)Y)|fOM0sN|eZVnj<#N_d8mh(W0ur?%ev$VTa z8y!XJP~~3Mjq(GuXmd_>ZtJW+XQqvD+Xuz+K3PCQM5nW7`ySX3(ak@}ia3K+#n2S2Qy);#mT8PV@DLWK-@*AKIwH&MPd>|g zt5x*lgWa;;TI809E;|VN|K-)DiS1O<@R6Xw6Q$Ko;j$P*HT@Kk9d7r8^y!U-p?P#c z`bIN;H4>)Gg?nZJT#%dsx*%>dYaRlGqXP?3XHB!;#a!!fGD{FceAXzcKLy;D(}>in5ZA zPG#mQ0E_*ZuqET1Hp-`xko(}VRAiNw7kmU~B}HvvOS6(09q-SJSfmfT1ipJ}e5dBX z)M2|mbazXQSy>q53m-cy3$+Wrz^X)Du5OXAWx6s{310u`Yqv)B_GadQV#-!Q_f)O9 zRR67}+@OIrma;LOAS_^mzOz&#KU`tlUlc*s7q}e3J4fI_n8mG-22?dFFC%XTxXzom zqjg><)}G|(+#|Lv8~thilUqYwY(4#U`+cO}SI6~^e#qwJWj% zRcEnAc-F~DPd~Fa#>4&VKDr9R%-#6zJ`kwUvg{a2QunHq)g~+Iw<+xxRV9oR8ua$z z8hyS5aW=;_^+$AGin1Rz#$8d*4t;-E5tY1dzBWzwT|3C5-impujH+m(TPPGhBFz5U zZ31lN-?1%V2xRdpZYsiL!+HcV(g3%vDGZ$h?^tPKkqK|aiONzOzn5*Kt>LJ|57p0* zgAVWnL#$l|{AK8}9Xe_z6y7S-Y44eL!c!PL@yCg_+mWk(AMFL{PYS;Ult7n!T|{hF zwh?9~BItQfpala*Uh+<@F)5WV%wMIgKLmcfIM`UUrC+FzH7Z2g4bJJO7{iVeT~{m? zeKCK4D9ThIw@-OWMiNoq5W%J?iNvhz9DIFwabOG)d2BRexKRxnI$4RRx6+UPbZN`C zVGHjhBk>~<+k|Orgf`{~vffRLIKtO6g&8&|3FoxzQ_M9JjK^8A6 z!#B6sT0B}2jG!PoSG-z7RbHf{Am!|H0O!Cm3C zXRk18-K8ZqBi!tMM4G)}3eKPQH9{DP-b_H4lJ-%F++(lHUnnAucD~TX+)8EHdKh;m zJup}ELwfKBD;>wJ@Cs$}-)fRulD^A5e@w2mV{l#G`Tdn-X_qFbQoGseX}|h5`V2p4 z86xkV&&q}udPEqEoayl8i#8n)d@?dEREbc)JGGFw$E$P<=H?fn_9N7<-tHeOsAQEs zo>tnmQ34FW|5}8Wv36Wa9iS!75kt|$C|Jlt3_#1%$7UZ%P>$|k8R}u`;%?Gs(%#1|IYxKZ#sG=If&#Wu!TGKsU zl%3MGfXIr&XbrhBT9T~l*zQi%GwQiV<>AdjVH1NfbOjN{V$X$b=E#I|dHz`($urtN zgI`DY9c=Z+d|9PG)MFsb@IN;rB|*S%3^jAz-a=>V7M!F$0;M*9(Ke2}<{{^1LSY$H zs5#bS%x4nqo9$yh8sN#kKDBMG((V2cD$@DmCLD7Qd*ocvA1;Lm0(=Orso!!f_dz6` z2fmwr6b6b>3E$?w&wVU8KdtIZQhN^v)m<;VFZzA3Y)#Gt+^`5u9MaM3bw1^0!u zxSB({wH11PauHxwV}Mgkxcm*zxW?lEK_L6SohfOvdKriJz0$Haok_By=n+B)M~-QE{XzDK-iHGfKs zRD-aqUN}9fm-Sx;XY^X7Z1gHWXM(LM135<_z_?85$f2{ zoHNezd5;Eq1o7_{X>k_KJT>KQ7kBZ%4?ZIlKQ5GGr;*K+N?UEiQw6eD>($72J`W*? z>2mBQm;tfYR&iY-7S0p|zl9*rZtr!K6sc1Dt2qbYTubz@SpSBtxTZj37i%lB{62&O zb_#JKj^+n!zMOg;G{90k^CynHGjqQ`ox8RF3CTNx8Fq~{$Wd?w^Nnw>mXlamFN((R z0)Vh}&SCYFrN)OtN|%ec-?NSa{uv;B%hX)Gu-98sLuY+%Nh5d3)YMcZ*%3rh%PbTI zJltc}i_%8XAe6<|8Y7EBFN~Gae4FHEzv4*zqpqx!j>-PI=sRyX0~F(nq3|owvt9RfIAC0LFe^_&yjl*(k@1oMX}bfulwjx2Ne6rgi8Hp zc9R`4oJ@GDheKwtun_`t>R00V?m-s_jisE)!OXQ%&nl@5Z*CU^QMUc=v^`Tb@HQo@ zUqxd{A_T}NEx_+8JS9ZUlBg^&r&<80|79e_<#v4UOvEzt`Dhi<%Sl<9&xTNB?v;4s0K^tq3z%q?tA2ftBd;4R{-FP=kUVu8e!@PX|wZ@f2 z3H1^Wjvi_rcp&GS+y0NO534#~jZ`CK++E!uVAy$^?1Uwlt|EJodR2oEV<khlySAA3mjH zw@%D~lNY1&*VmmEi$Oo=_YLC~Jck?QcsqJNg59WrB3x@qkAP|4}8q44sI0XqfXa}^Z7Rlm_8R#Ob0NwZJN zuhgOoNn$9bw|L;p$Wf?Dm64406l-57+F;r)RAwj^O$CW&5)&AFv6OT$S6+Y+%Btq@ zLb6F&jO1NYteouccM#C2H+{a5c^myv1R}#E&FRJP(orJe2{dwO)6^3JAd+YC3mhRDCJ$*b2_6>d&3T}Z~bv!U)M`|e;&e7$nS0z z9By&4&k+jLzGcWMe~cabvV?OhGu$&>U_VI3qEgX!!zCt5T_;o%iN)~BeNpAFF}_2$ zP1OqorS6p%Ypfj=tlYxU{?eVk$vjeGd9?*EhWzffaX+9;(m@qr&muJU`y$e*(Hzrg zq>AVk^pdDmWMzmZQ>PPj@~1p$6NTBVGKrn)=7u@3CR=+>9{8le+} z2)n17R0u}3$E9Qv(pSVtfNc6{=m`6D%S-DQil*~rcYqOTN_{?|$*P@FETL9~;mIVV zYjLn{Gh9`2@J8$P-|x;SfSb_HS&o&)pr;!(WC8t*6J9z?<9N83NomXm*`kmcVUkrk z3_OR&<4s#zrOS06qwteQjsii*>kehJ$M@%90+!KJ z84h+7)oIg0qm7{#qVUHu--><+L>(!v>U7T>P486@a$3q3jz)o(Gxj zQc|N=5-jY4!eJS5a90?y$3%mL{Cj0h*goE2diwKQhA?p-p^Hh(HT~r!ApL9Eb6-u5o|_vI{C=B-@edc;IQ+IO+<#&`dj{YBd#KvKM)UqU!jMd3 z@nJqa^J?~V9|9Mm=FOh`HUW)0U`o^VwKNd9AC)BCvx04*9qHUKC+-;(v?3jAV#Luz zVLlzE)!DB5PCL)w@4!F5+7Sru(W+DL8B1?==XfU``iKA{@Yd;^U3aS8iw3Wz46`1+%1K}kJ7uECh zMx=&Mck|-$`oBM8MYRup0d*uPs;W2Lj@OPuf(!R8*f_%Bf9X=X{5dlKI+X>E&853& zBI@e?9}QK%T)rtrnW<~d>ql78lAPHpBqHMRAYPbF8B+tz&DGC5q{5xuo%ARj9&))E zt%*hIpqmBn?Onja&9>R8*Kw@xeRc&s zUOOc2aZ63NZhQ01(iK|o*+q(+z=>nc>zh;ilP)QFxvC@KC;q6ziywTwD1zPLqWoAc zpJ;s@?ykQmLLo6k&VhW0K<<$(MUDYzISM%NoKDY5f}QV*>BF4C^)1_83Pb_i;c*sX zai`=1j-KNkDDkk3-VT+tAECD&b3z}Jl0z9z`dS00Q|$0j*Y|mu#dS%hQJ4Toe64SBKLNxSYw>55KSB^UD)oJtiPuI7%R;qsn<0FVQr5((< zvLtHHKP}X~s=4nhBDh?X9e}+-NpnB0W1*#|c2^Gx6lF*Y6HNnidVT zr5-PA%eY^!4RN*{=DV&#VdJivQ{}$&T2Ek`PEK*#!P^_>ysG9l6CEnFvX$~I&y2U@ zM4p-Zrk6mcxP9O>Hx zUG;8(h12c*a8cnuhqV>^9{%Mzjq&=?CC+Y}(Ik!OO2xJ2+bLPR^L)E1x3wYi1-aiD zc_@V6wpj4IRz(ekm>7Jc#%R>p@oN42xim-lF&5WD&I>6`7P8zPa-s<8j;Q4blkaV;+aSEe`i*u|w8=1+9jOlll> z_LXKMrk@CCgitIo(>6Ob#;eWURbJh4#Pvb9g2cPqO$N#6BjY9ZZsUJSVS$X0(g*Pu zPyySV(>(gpRI=p|=A7-@#M*$@5cQ#((q1dYr8ayHS0&#;-n_mHm*HeDsnFt4ooorA zEs-ts()dEU07%6z>o81{8XsXc8`<(y?PHRnRg;AxZo>2hJ}-s#ZJM1)fQnOKNH&@% zQ;rUMC|K-7sR^zqA7uCMjdA?uUGu$TpGudPyPaqxN=);2mYmIFh_0Y(c`9o1eH%A` zAo?=vzj$?S-rrV-D(Gs&5ss}jTtCQLOU%=mg7d8xMOYc7$!S@WO4q*HN_xGbet%6} z%SE^l_L`0{QoX-1PLYhOXJz5~wUDcAeNQDJG?sVDj25r`ca#;LvM{!4-*C%_;&~H+ zlD`uta(y*A>};q@1wvzMh`TMq!I*|sSagfFs}<@4L!A!p7d=y6L)_*+jMs6;#pZ^0 zsJeB}Gf@^W8U0Q;4ojawGJ)5p7sVJJI^|O{D)mhXGdIj(^`OEi)x6EJh=7?u%t_&Bm z^#wdHfR59Zr`7skqLAwRtOTvcDY;S9YU^t3M!N)hl4}3_sDml%ulF&h-(po?B%r>g z|C6ufp8KDxx&G0fbQ%6)YD$qurlX5(BFJf+m=?S^O!>3A^}~DJaLE8 z(n7OD)=nDJ!&6zM#rz6%s$5+L3L#%3z=HfC{+j8E#j;&pRPd#+Us(Kp4nK?A`^qkN z&yZLIw(5dsFaOQTHEMA^joDF}f!`Zyw#*}LgWk`T zsxN$F46q~RnnILTf)aiN$yb(1t*naFxZZ-@Jr7$WypA3qyiWOXEb% z6?*eX%mL%*y+T^XJd04#O8FB%AmGyC%bK&r=J6qO1rvMW|XUU{pkX zwR%{MbAI4g!}uB$%&R{>Mz8VL3$_k_hh86(23nYE{2fcrc(;L!HoU3URV1cZ{LXnh zS?|!%Yu9DIvi6{Xo;#3|5qq?1N6Q*HUpsXdZWaA1Z213a?!2Sfd>B2hlgnMt^3|qE)37HG;&bQfgLOgxWI*O6`%LrKA)oir8w$Ua`l0^u6c2?>+b4Ki>DA zbMO5-C*gTcJd*G8{eC_!&Y8u6`FGg#XDRtvw_je)t8CeT37gj2^0266MNb_$VsF>3ECgMCIT5`PWIeq(T8Tsyne7oth z3e%==TKL}+PrC|+3qvX_u+~tiFpyPNQ>(R8Z_XoR^ctCxCd%3roE(Y*;9%xn4fb)IRZ7c{r8o9q|V9zT^l{9PFvTx%l2KHg)KdnbvJ~fJc=$qO=X3n$h=I zJ$o00gHbft0{WpIKsupQp+Vpk(Lq@Q8&)8l%%!pM=;Zq6vQFs`0L9g(mGx0aHXrUx zk8@~t*D*nzZAMEd7wIwXO=C($fBY6&`V+59IH(bu;>*t)b9$wE-@GW5|0XXO#3fE= z0`eAbHn0!C44Zz2q_xo{u(mTo8B(T%vdb}Cr0dCMRaDcAansiWo z1%3wisRA2{Ee5xQHX;grY3wa6{q%l2i>l;o1v)?aSnt!7|B!7Scn72ZmOobWP*G3Z zjON=!8Qm+mgn)RYrrQBN!Y*#HkXshH9!-#36x(}1qMjSot<6`^QXAZ@d&jowbQv%_ zB6|jX`&<4=DA%%m*QI|A%B70x{gB&By*u(A4DmBB=Cm8(XaAalXZjj-@#gU&q@c;Z zTE+){NOzEX6)`D&eRmU-{h&IOYPQj<`tz2mj8`8zBOY&CYXMZ259J8)Yd_#zus0|p zNzed?A3;7GHq<+uz-^K_VT0@?^BEJEm;F*Em<~=97NzaXd1tgU9T@Gi)RNE+^qhHw z$$S)d>LzOVp?2p>GuI^bgdd3^?jAnF-%*Ns!y#GbsToostzz-};tBtMiZaD(yMww3 zfEvZZO3h9&lub-1JnQBqLD;a};p?QGs9?Ba1~-J=n}MEbQBQl_tmpJ{9@yu-;L-Z~ z7mv62+LiaXRDHyt6k3t)eczSezu=7Xgt%roeQ#If>4dKCX9}%rXtgAe?0GG^*_npB zSwK4G*4R72gL9Z~;@`t1&H3I5ZAg0GeybP^DY-gp-}-hemKH0W%HuNh>~*rlkF?}R z4DOBB*<$$kyuWxse@}gS{eRO!;9?%OAXT@IedWw;m3qZh>ExQ<_xm)<7kOm1<}?B{T`= zux1pB6w*rgRDu-q${IZbAXWryITS=H-Jb7whjgsics+N)^ZA=~G6ymqy`&iq0LRro zVWl-SgBKj5(d=->201wRCPSEokR6woWU`ju`~XQjq{n+!c^9|#Y|ZxblyHR6BRgd1 z3)?+tB5Fsfl&Bh{mu5-)CbI?GZ@BdDmk3ac6k!UvoUS1A7ftN!T{@_tuG9zs0wKk#`962(7D1 zldaLus@tO(oR*YPD22Wk>cOQ!z+8_X>d(f=Sy|dX+m)Y-NTP+QU#7H{%<5{d{hZBcr2$<4lDx11FcWJ(c00@n=}D zL7RQ6r&dx=wpBM?EfU0cz3|1{c+1tHq)ZBpYgIDcVr_k+nusELA+ZpDSsQlPx87P()0vPJLfly?e!XCvBIucisIljYZcFJBfxGKmursi*=G_J2xX| z<|;o-XEUu(z>VaT*lANp5a+~1HtY8VQ;jb-e_YeEtgnp?Bc+>TwNd8*2+xV(jW>*= zcZz;Tv-W$M31pYdKA_gr`o{dF0#qmKcSj!gLTK6!dlekFlCa8#8Eq1d{0NSF@f&7; zG@2=H@i_*uvu=1L9jEhO1(#inde^wrpER&v=PFMP=utbVzYnaI)ny!lcJ7>%|D|Rs zppr57aAjLP3l~|;Ce}reV^oU;(~j!eHDfsS`&>bqHI6q2uOtzh|JPw#U)!#iM>hD; zU3vgq0yNHYM63jTM~OC1=XOa+uxg?*x%EqM*9~(Q^>oVyPTK?rYe8e0y?ZZONne6u(3;2X$B$UNXVNtz>Yxb&fR6c<+> z8w0cP-ocbv2R)!Q`2yqd{MD0+q7f-c&sOAHpFT#|GW%3Qk~nEOOBALG#K$qp2kz&` zCaDb3`Mz5>Gg)2%{Y#4=#BNoAdH=DqTEjCNn@gK05a6Hyme;CuTgAOyVdbLRxe;cW zR}%gxCse#q^eSB3sy+eg zI)Xy$wY9Vr6FRFyiPk7k>H6+ppNw@X;*F@z#-ot{-mC2REYj(Pto>amuv3r_% z`(|u#^phF0C!Xz*hy}rrm7$vUS0RqKr4tgqyl-Js#-!O_N)|CG4jXx*Ry_$_IMt19 zA1K0Gqx1YQRbE$w)xEuZ@aKlgi=Sc+wF#ajGBxNM#HCla^NgT4r?wzR|7$6pGUU6s zgm$~lDsY8BghokoU_34)%~9tYb6covKq-Z<@o#CeT%v05{&8UIP9Hwcl^Y3*W2Rr5jT-g&0$ z`8yl=l-iNRC{Hp|$5&-}heW2G%+89->e2!5nENZ=+28}+?5pitk^}AAO4}64Tpoid zc`3xQ^nP!j;*1wad+K=0jH(KbjN|#{l3sI47!7q>|t>-FV`iQlAa*@I2?bJLd!!sIBdi%q`@CA9?Nm49&wAvXpn3 zTjA3jBiX<#iCQ6e0Skj|7CTcH8%x9`b-;u!@$TN2zn6yRu!VO$f?S(rZj(4 zvbCBmx-LMOWI*z$zQ}try|y0c`1hPs+ZxC@eXm?G`n#rJ)PAL3rIyvNP>lI>dUgTt zC|_A5Haz(#tA1Vw^@9-iYuq)`m0Q2hs*SV|e%EAOnF+PvsVMj&%9bL)LLq6QlAuL; zGp5;fChu%;p^Nyb^b5$<=jd3GW|ISB4OWy=OSBmz`!jZ#q5nvL;+WX`KIJjF2&Wl< zT{eDTLlIQ44X;?)4+X+pE)~wQZdP`~gqJRD$p8}IvD%t=&6qUIRme>en!QzHNPX7kdNTmhqhV~lvb{B?JA|# z4>Ue*s(bMh8%?%1Ob*l_r5fDfrIvnM~U+34fdhb$~!le@M+PEnCA%2eQv?=H= zsm8zAp2iMh>QE;er$ao>#>5I6w-x+aly<5DX|*E;FBTU?(+X_p>R4Kv*KYc;w(%rw z!2FW_ z-UWaHdG`gcKUm^t#z~lK)*R@YL!HSFv_9#Czw>@+z_=m^fXt@e`Fa1u+JyMC-at_2 z#~=(NrJQ99rtR@xb){J3tOF%-A8u=|SfuAuED2tViq!oQW{lIS8%zID$UD*#(B5yB z{7m4mM{X;-tnaB{Lm&vb&PFlrmRq>J+9z9sLdFuROfs3xP$e|RWP&nT83{F3T8dCPiCPd+ zSQg6t`^M_Tc7QSTIN_Q5YJQtmiPw`D7D}0R{RY>8#Md%)Br{A!X?$LtG4TT5TBeh$ zUlc-3+h|eW#kgBo`);;mmo;#2wAk@4ARx<-=22K)sCc-S2^g!F7(ckjw0w&2;=p&+ zf9jyYh6-R$>@+5c*I#pnpe2yrsCri&Y8op;^C9OWxY?bd>GL3H60w(yb zonL16a(U8SYwDUc5`djaz^TEJAv(j5*sITOA72jg3jcXcds?{5XwC$6Yb+srOADu( zzckF!e{@H4CVVPl!ha~S$+-MXy1I>|5plN26uZf9yLe% zBIb$eR;yqHn5{Vl2?nq}&VHNAJ3=uv8F~Td>Y-mv(Srr?4X#s;wTTQK>V;sgE@M=u zNk^j86*BgVvEZ?6qgc9Ww+T=pCEAE%&jWdb0~A1MD1earG^KRONPFoXk@vhH>&iGrUnZug(- z8;;u{%Nt_Z55UC!G+m_ad8>+x9%lE~E7llMpQFgHZ!9j=nW2l7C`(rd_++#`M(z3< z3$dmKBH6nG!evfUQ?$0ehHN7*``wt2u7YfkS*rPMj-umN1YM-q@+=(h8>6Q*cqOfz zmKeNEXg3wwP9iU?$0Y4g9xa`-_9KU&odZDZ-JXIY(q;@BJz-_GIULlF3OyxLuSm#z zlr;-CsIZ_1k>t!|W)D5Ixh&jFnQxj^C!Mr&4wifiO`B^IQtk=D7rtxqFiw5WRXw*y zmPEF`cQ#&+ivt7)D4Dny-kLK(IWoPF`V7~}OoEG==)2{eSEjouJu3Rj`%;3silJ+rnU**dRc9 z>gmurhh*o-3`=2FGLYTZcOBsORTPb;=G(vk0)TdIOL3&D!%KRapo{I|rIpo)Mhq1v zM6|-5r@+K8y}1NG`8ywxZM4%F#_ZtNcTR{MTNHmWB1NgXi+8o(YfM2aYV6ZuQ|^d9 zZ&rQWo@O;XH$C3@7Lx$xCryjR`t$aLrI8yVaoL}Li-1a!QnZU!haieb; z4QIrk0PzBp=C9MdwQp4Cbb#vVxhU>cxX43GBih5`XSjOZ!MIguaS3A@gyrTiAs&Vh zi42L7c4dfp&<2YF)BgFAKVvZST`*dIB9)Q1&y}Mx$z{woO3x9XkYNbTlF^Qx@p?5| zz|k$Z*h$^k6dBGRZ#b4LUJ>Po68cRi;a;-9$EN$^s+1*5Wn{xHeK4lmD=4Fs1*t&O zX&SU@KCka6ub)mx0aSra^k>W6f{SW$tXF&yFy-60Kalb?0<9?@8%{E@SHD8lFm08U z)&-}lEzqq3ph%k*vc8U;re3~(e<<;%@_W3`{I_|?Jz5=RC5ff|F6l>&iy2=A(M^|! zea;D7!;*de+|xGxtwl(A&^LGd>hKoWw9Ch>|Fky$z3az^bNg-*BmrNn=)zn1yvhrr zP%&w!$Evy_8ZU?xh<6DT*me)(9r2`{INv!kPcG!Fiuz(pHd1+hXgk-Sc$Ev}g_>BS zex7`LD?#k)O_A2m+c1P_nBcv=$0J&c^WE18I}!U96^>S zMU5SL4Gz)_mg(^4>r#JQ==Lqg`7%F<$MBa%AW5lev1{wM9cnIO-?`H6=_;I#jt{sY zVE4=_gmZg_)VbAqbdJ*XJA%4<3HYVlfM{Zp8eVI$=DzFyw`VGfs(i$2>2h+@ljD8e nB*=uWp2Zjcde#7F5>M|wShoGKb&-SN?1gkR4KzybJr4diMgZ%i!ElbWLab}GgFIN%*@Pev1GAivBk`6F@wd-%*@OG2S>6v^LmDS= zevTpJn5JDofHA_?+)IF+r?oaO5d+lcE&$mi;F+*eeGmZ=5u&&-%XcBf%bcxrMHQp{ z{q5z0#zZ&nHzEM@rz4sC=$PmeC;pjsjwB?2O!P*a_G=G3y^w}V5K5@y8FZ11!wWQ} z+|}4n>YzPIf-jt(;Fi58HQ-ms6fa*yTEKlyb}I}!B(a{~ADm(0Rb>>^J?#WDGgJDs zO%bXzig%@~gw8vegoj;w#a-U|WGS>CPlg7tW^*4NgdbWdc+*F2xVb`7STH~3Px1I_ zmv2qF9kIWTqh^@$+x_v0Q!4ORm>I=->6K@R*&*N^o4%!*XLS-~v3f#x-DQnP3Lbeu zWZn-Me4(t_OaFQw@q6F6L%=JwR*BrGkSl_O&o{O22(6oijsLn$5}!VcOJkQ|!>G{R zl%3veUng#Muqy@6aPQ_a?#J7%hfqSBF4a}i1Ey)v*k~3S=|n^=*{&7EA&GVB45yF* zi+9iN0hBr^q-c&1oIw}CB7|92!qSbyj0?(#$l9HZY_-V$&xb44iW7=!440)PL4+l31j z+yW6M015;lB@rt8*#*TwOv)%KLN1dNlTwF-&M?P6i3QsyNpG>Ak;oZgRD)d%l-yB9 zgEZ_J3E>%jDsArBv!|ol`HyT`K7L+B$_lLNb~C_xMIaUe2#e36LG>aSknu}Wp~rkB zCo?8e3{4=LlO&)1=}gv$hQ}QAqi2(m6^Y0w*x@gQhJo+pIT8X zkt-2aJsEv2JJ0sy?!3B)c42MT@dESV(_? zO*T=sPX1llwlZF+^&IOQ+1%e!ozlOSG?plqQKH6h#}qU6*dut#?B}O6P+jzi5HsvE=D1&6Do-L0kB?vv5nS9(G8c%Zl@@NMrDtfT zcq%xGl^D8>y2fNMTaw* z)AL28v$r#+Q?0WNPId0DT&GS=7m`lt2TfBof3M2oYvTt?rYt6x7P-1TVw_Nw67l+1 zN83&VT&`S)94y?Pc8_;y9Qcl(i_#LcTD00%y0rUO^PA!J@0$r)oL0+LO^BUipkqk* zj5A>~2`4Nk%yh+dd8_cNaK1aK;)BOo-x$?6_!9I`98)%-nN`~nYUgK{W0#y2u9u+K zyrJYP@`3pQ`>_QW@SB4$gR1fG_FsYWT5PeVZ;j?7+$F4IKNM!9;3R9}ebfjQ3#H1p zA?9T;Uyt^PIY=AD8MT9l$vK@Q%8~EEH5mEpqv5CZxgp!I`^J9dl5HyktuC|x?GBBE ztew13oIjj7B2L^zlvbP}yfT6~>MDvlI<{{-OozaWPL3y3{dp8HY zt2TFvw(hSF`P=z8L+3+%Lr~P`)I5r?N_?E+r|qJQj9I z5~^bJ16WhE(G|+N)aoAIiG8KAWxB>MW*>F_8+`)E_Fowh5d`O&GJ_IhcB2mZaOcP zjXNzK3hjd1ddkGu#R|mq*qu1^-n#EY{*d%HeziMVKCW}wU|(hLZtz;+Yh1vl#WBNq zIQ!k0*Bqp)sDABpFrA?WBm&a9#kxwl4GjwqYq}9!tT((!dssfLAzXwP@^O2ypW&^I ztaw>vEp+q)*M@V4Cj_p2+w`zNbJ{kVIxQrP0#+Y4P*ZT+zJ~A0YI!6bU{dEF8`(M&OH z#2j252heAt1}w8}r5;Tb^#>1c@qqlt~?>P_K=zRieah9^DljiF;+r=>T?7pw(Z$|~ln25qO7 z`5$Xn&tZB*zI;=AvvWWGK8c^mCa2k~Ikn1fT(`NLH(Z=ayDhp^u4VW(zUa=jzwkwP z8b5eEz!4ksy5BIKY%S)k4lE7?%O_+e@XvW@zNJ0c$ud(emFRA)pG=Tt@*VHow?FH= zz9hfD9?TvTFx=|RA}=D>p+2Lo|7gB6`Jmk@tH}uvNcdR1Sw6kH5@>&K<0s@<{Sd!3 zeLdTE)4V|GaC6^aLTA6e71u?!$Ra{!_KDx6%RQ>&2VN z#ZSK*${DW;5Q37EgWWt60u(y{CP5oAiodT1;sXkK-@f{P^ePd1Di%AHOu&$$M}Zgs zrIUyCqr)`tJ~Xo-@iEgXE`OCGlj#N%v!Wuw)o(oGID7PG87O^6GrLV5L`4$}moms=jVo=~KhR8H=QOlyu%x zfkLebBDyq{kf}TI$Lre$%0b0~)BTG&SA|~E+n&SQ+9j?^o%;OiqekXiDt+Qi#fgK_ z;0>>aeYJ7ShPFU-!q}y@`yGczhaml`N9&bC-ogTM{lhzx@7)J(XU~hMurYDr{+qi9 zay+T1^fRN$<4CT4_}hJ*dBy{ck-IUWOL2p@B_(?_IyN9BJ65VIY zh|`IrV;t$Km!ID2k7yYi5?H4K9z`-1XAe=GNFP}-A0MJk_L@%zid26FnCdm-G&4JQ zST;Q8a_IB&Pj>p}ziPeabso{?3%t-Olob4s6NQ)d`L^mM@JLFwll4|^m_T;rL^geS zl9m=$M@E7{;WFLZNoVt??rBP|Uh-vHK2DPQ$%$<48_n6ym<}zqCCOVP1I>#OS)6^s zYafoxSB?9F>GFizu_fT|JL%ua?FIc__Mdth3sTZ4B;VWtdI`^c5^ag*J~hRM^le)^ zFn%fd^3w8iMAjedQVv&RR_`bytuP=}6XmL+>(U+pQG5`#~~u~o^k^Y`m!WMtw+qBSN0J43I|F5Bc7 zbvY61gFL+nir);nrIw0__D(o$+#DtjU0*^=RvP_FMkQ{Sn0!_T^2yY%;fr|6dg*%tL%R0mpTvSKoy*aZQl^(h0zAw2~jM}(SUg|`&9+*sA?63y5r)@bb?)Jp# zHH>oO5jWdyTz2E*?icyGg+#K!F^byyZPSDs^S=gXp(B4ivYubv8{gugsy-j1lrTkq zru@y0oS7kL(?bHUmyhxF$FH^^j8j5bjS^_uY1P3tXx1VR&fl4dUeFZeTaITq(?KYv z6B*Ineip?@#ET)NW`paY4T^A2mBa>$UtmyX(}S>b>3)j!L@H9UO-mu#8kS~%3mSsX zN@qpeFkVKW7zce4AIg}uNU%T-F2EtLMdVVfE_Ghc?($FWQAFIL4|;kx3QMIL5qVb6 z6_u}ts~0*R%Z|FVdeCB~iJi)TIz^9;WL4~OZ_O!0s+AeSwVZUXVLRV47jan>X0`b| z-A1Mc3xApuC!kVnk`B)ltoamGUw`9unHF-~0Fvr=rAcVB`d&+<%cH#dnW}<2Wr7XG zF5W6>-aUH&mwV(`eO@YTJdWFBM#^8#kGt{^vG~jfa*QOvU>93F%z(-aXwK*!sl=D= z@JbrM5*JXCckcMx1U4To97Q*6t%uh{d|&RH&Or>cjw7%lBv=nys%h{Mro6n5?R+u+ z@3$xRDG`2MgS3z}*t=qboxpYum!*JOAIcFTWBWhLSBZhrq0>ALkl7LA8m zssohYSgjF@-(+ML_mhY$@qL~DtxzgKS^XldH-CU8(!oIxX? zwF#WnISE%*KP?%Ewrat%uOn|p<3Z`Wo%Cf>KAY$kD znL78IHr0-B7r$XO;x(vyw^Lvaa`H_sJWhdvba}=g~X_xw|p&SRGi2bUqN5t|GgrX7g3K%l6X;?LP6eGHK2;-jpnLG;X0oT{9 zuJRK!py)3V4xw3l^iz$8HG&$ z1Y2>dJJH(4zTlCY7gb;DJKzOoHW17Hi#w=zR2V(Mc$Tdgr5vm0Q_X$A-{II(#SGF~ z32Ezx#gNZOke~F#a#YVw?Fk(Qb-So2)Z3I5tCwk9qNf2V{q)6Af8ABNM3{XTQM@Z?ju7-H@>L78kpGD+Z z5{Ho!`?jSSSCV2{EG9QOfo;q2CxnB|@<$c}0is8G0-2imo1)cMrodwB;LQ5-p?L-? zD_A=pBJXMT8)a1PNSVo5DT}a*(P6q4V&(?Q!e=p5EX3db2}pCN;Fy< zigXO9B83V5DA|wxLNypkUfdahnK*!xBlTO5Afa_Dy_R0-mrgsrmc{h2sr4--*(DLxWHUuDF+qF z(*9;ZEMV5Zi=IuyR@ox4iKax?MCp`Q`B{*N=%TQeqryyfzKwZDWoar9wv}D5l`Zm4 zr(|T=tP14a7wqy_t|Wx?j6GQIZrr@!Jz%Of;i+Qx$*h;a5xE(nF`IvBlI;*`@*J5) zpl`Yh{WF}b3N`m}KBk>XLn{*e#oR9tvK>3&)Z%LcH)CYJ@LK(T{MX_NhB*9kk~MZUM9Yx+^-a)*4D1>K)} z^8|sX1=8^*6%p4yMDf3(l+3#F-YEaYZKLcsnl~HG``6ZL0kAmO!d?Ayxv&Fg1<$D4O#@;qlyqU2Du0;VMu9AgKkmOL&5=}<&F z;15Yth8Qd@IYE%lo$=o<62Bf{Qpz<3(l6l{tf&&W9r~f^_Hntv$J1Z%2C|n(+mrBA z*;+Q-_q8b+2Der@C+bMdtS^SGb$%}}8=C_^i2$*uD%k5ZCP$EcDX1LfrIXv=II#Fm zuei4~*Wkt_-u@t_)HAqZM4&M~YExzI6+>N|T4#?i9%47}V0bI(*RU}%>k(WoPYz;d zC4Au|S&R?-zS;K;B3snB>$mxLMPD>|#(FzJ412ss0^_yAb*n1&Sa=tV#WPTre2}w& zKuv_it+2Di$Y$Ikp`@d~U^dYt{;AH2N^)5YA`*EpuJRCf9SWO1${%-$AygL1bWC8M z{}>M2ACpI2jL3ZJwI#QVs?n>rk;9V+EY9+WWLUV?^Gua|L+AA8M*Ug}y z%A(-WFb1LL%fK*F>k_i}yAR`LvVLQ+CJ@19kiEai)opDpZGG#pnwQYtbo5%Nob&Sa zs%S6kgoz{lwCw>y1r06qOBk)!Nz{JYZ(_g~7AM^8GWd+!5H+~P_c*>?udQ{#$F0ig znH%}g1;In#ydI$UJko1#&DUsvvk@)ymOLI6LR8hNO~S+DaJEFXUsJ;}F#p4{%~gCO zv3+{!*|hQ1^#Qu0*8b}Bo`bT%`o5tyrSI0=>9|bE&N_3E+0in_i?Vg`y{htnT&!ax&<=)Lzo&9w&M&Yuyc7D zGn6S~(*xj0-!@4$|x*{hN_o_~Cnqs5E9@(Ebgv=^nu40XCc`>y1*%9Bym zx}PY$o>^sTO0UalPo+=?cN4PecywA5!J<^@tIzyY#=}ycx0Kl532BM$yNiTb=4;f$ zQL5_qR@zpUbw;@Hw&ur|@@{PXqu4)RG}xZ)MztN6uK^QaRZY)<4472+qoRN%Hkt?% zt`AugU`5Iz8PAY9Xh>Lnu2m@B?ApKkJV1-FH|AZi+x_~bvCX65w&ihW#-@XKBOY}%f!#rkUH@)1npbVi7Gs&gzBO!D+;0fOqrzB0A1HXqPB^0p|N#ji(=6r;0kvgiIuN}oV~(R`FYcwE$E+do8@fX; zjPFW+^;K;=qW8e_ofI~^&+!{mBQLrXyzbR7h1s}O2VI?$+ znr**-8^7<~YC~MB#p&>C{ZfjR+pXZ5!KVSf5}`w+U(dJ!Gf=>uJmaNW^$LFslu=v&~UvT7Nw<$XZ#&@3ow&5WFV zd{;({Uz)2*rd+>FsdOWYCHMg4PsMA>Wk5!oUVYnT-e3g*q>iP|*5pTk1K{HLSJ1NQ zk*Ps^r%%m<0A0Z>y3L@Nd0Sh#=;pn#VjTG&)01au*!xI{BHxE>8**jaowmCLOp$-AaNX_@I79J3QIn*YuSrU zasRxksolnP(NHA7dH248p~c>^Hy!le@X(*8u;YFf$v*cf#%Slln*f#H?T7in)8pz4*lGCgZBB3U;p9>W zP1L=+P={wDPgR}6)Y4LK$-o3t;K{&rZBtQb0v|~#1BMF{Ru##E!+nri2gZPOHW*^H z)R21eazWa%I6S|oKb!tsRTPqyPwh}Sqy@C>K^y+6Aw}}rJ?&v(_F!Iafdpzrlc&@8 zg{5>N^8}^~tqbEe&hiEyqiE)GT99W#gbx|z9C$3J{$GoN<0P%}ot{F|vVis&X`7L( zahi5)G^nTg!(Ej~J10MVI|;-St%uX zk(^w4p0!v-gqRvmk_oxoM4H*S8%ymmv5(O?s)Wlj1VFLpT(Kq4lo5+z(R6Ff2MwY% zr3iEsHW@DZ^+_I6V%Ibm#9s&@iy2-^11L_l#Z{q*YmRRGVo_^z?bfCe!1EIuTRX8> zk+$-ZW>Svb5CRI&cgqq!^W;dYNQIbyX^!^O{DoPT{_(vgSV3)6Ppwf)i)AZkOq+F#5~*`#Lykw37k@Bu9v zBlo~ZHow$Cc=Etebqg<^IOtZ9obpOS1U^7PE0qnmik~Hm{JUdM2bkR1_Ng={FoRYc zi+= z%~Y8YDNw9L8BhuHWH}i>C9i1gk7IWh`dR)&H1ogsYV`s3ng)u)ek#+!mRr!P%(Ea0)0ru9F~kKJW_bK(HUPOGHJX@na(pqk3IRhm|?Nq*t4RPlRNn&6X=7Wg6T?K;QN?d?R&AgV^he#og{r;sKXJD$A9sB z-^aMn#LtwJa<8IuDU&GF&mW!zlYry|CRw1?0-rK^)GR6y(I%u%fMK69Oi+fodzDn z=2qcmrYQKfv);H$t~(x2TV=kvyd9o6+%=xLU(m?MSOwu5CEhPJ5#!I*)zu-?>};hh z{Zt~D5;6uBb7}`8B%?QLgkjRWP9d0m=b$y!u6==(BkFfnHvk&qj=+2;55hm8q;wHe1SO|;G3ntdUk>XtnkU%n0?u=!-` z`7+uNb)-4B-KUhcEzaKVL=k_ft1&0F--p}c` z^8AgzHa|SVuLY$0I^B4#UZ-0a{b~r83k&w9=5LGZs5)%pOC7g?Q*%{0!Mg9x)BW+} zOAzv0eRuX?bu_}feI{c#eO1PzL+Rt^&SS?~S&T+)a^7vyyCVO_-`X|LQt;qsHxTY6 zwJ}2O6TNw>yHlm7L^zKwTo@47=+)W*P6wcC@I|}&;^kK*b3O&Q`+Nx|`xJl~PWoSs zB2@tCr+p^p<6?BiCXFf`o`v&fbL~+0XGi~TFg=0pBEGXLdM7t;p65-kqQXe| zqeic%{9y>w~j8stHC+N;cLjT*2C} z;_!P(3o}A+Lk^ARtDk<2nxuJe{G4b-_nf_&Ga$w7!{bcq`YNw+A9dJ5}ea z56A0A4OSTugv>ubtUo-hHz?aNf$6-kJRQ-p9Hw(baA)IKg6qrN%iA_=`kPGgT(|I- z*X1Iz8N7xo5@r__X7z~|O1hitbU&a34?@wIE(yN-@GHi8xmA8Jk%f0DKv>H835WXV z)naEtWOGB1v8SM9i$$Av0lJEidVuA@m~6X`oho zT5;V?kA%SYP2caKFMS2~I1ltX1742!NAuXMtYprj3(mzoxyDtnneCa?w`!dI*&x$p z3~u3gh{|x>J&e5+&|@e+X?FTCjJVo5#R1Xf$lX!0IMU9u@qvrsbqxWeV~b~lF5zHr zR4y>BErl#iA$p7vSYNLIDZ+Ywr}tkoXjk5y%`5@|Rdcq#(r+-gJ;tSWXs}eM7^Is1 zy~xNL^9GOJ-BSvPNBE@wIu~frDQtJxOWnf6>gvp0dmglLHbR5x(`aWtni73oUY2!R za{XB3>%VpoNG_p2AjICT-?>r&%DtO6KiO$`C^%XP^CrDA+y_0p^9yj0I2sukJl`%f z*pLYDblMit-T$*R@$>3KgV<6uPt$wn3;Ig|-Fo4iEbh@ihjsO?7nQ4oK;Tgt!l7I- z^W3hyCs#dcDM&17tIy!THP4_bTKw-$uG`@F1u?-#J?-9GUR9*G1Gl4I>bv#9V_6u- z?evry85XT-amG-=jS!~gfK)Hc#UMrRlcfTRi^8O`uSZ#1XO;liSUOX5Chm^k`S-Za z*9_*TCNU_Ex7Ky47YX^GsTbzU|2pHPK+=p% z7C-{1*sAQH<5Y@DXGXbgxIK5zBmMIVn-ybC9x*hiN%ZXuj?5pavLD8cu{&ZSQKN{m zIw%T|pn#G1jG<1%%{!i<$1i`lGV^v)Hc}8V=qI&m+l>E z8|~$m^WosUpU-T`0{LJ`)4QE0Q#tBni=0`TOvyC^k+92!9rMt>!bXyx@aG6OV`Pyp zV_C`Qf^z~PYxy((cvB&*1)G{N-g{(|gW&~u4_-Al9GGkKDa1sbxe>=;P^I%PUa8j( zcE1)9*u8o$x-rNDsmHT|9(|}T#9vz9Q2*m9Ta2ENY={8L@{_l|T%~Q4;^29o8dtD0 zI-SdC-Z@Mq7?>xK0DJS>5o~bxtrJ>{7}}^9mz1MA3i{Sr1K$4972PX7b&-ZOc~@CU zE4g6=ic%oWRlh(u8k~uz^q4U1ymUO(ig1lP9FL(1DPR;d|+=sUCLI`4?<@wxD zIq8IA!SU*!h6Vla-hXSTF@-)uQ_tQwH*T{WjX1fq?#nyrH8gZ2x0~G+1?PdCc#LoU zk@zbi7DCGP>MB{JHq0bhObaEr>ahNlI&)YGZjWSR7w>}K^Y!X}^#Tg5R}<^F`GHy@ z3$G9Z>3N&&B|(~+>C%SbAyU>9cO0XGufhexO4;IFQ_38Lw9*X>}GI;D0`; z08Qk{a~W>(bf~WdX~Sh*OOiVh8fBtGD0%VB@zD}zy;ns)SMs%MGLX0hVAA}>xT2P* zgev|Sco<>|8#aZ`-oEI@vt!_wt1$CN&Zcl7L6OW1!LO7i(K2sLE{wBP?pkRaAtII9v_O_Rzm>`z<0;1&;3t=sGs^q60nlE=tbI0hJ=r`=4eQA8!a#- zyHY;cU8PuYRHCFaCFOLs&Q#5|_D6&5PxPR>=|1-#>D|K0e$!)dOWSz6==b8g0XdG5 zXe;wKpSrsv<+SBE6V4j(;5hl-BHn8*NYL+#1#}j@T`a%SF|^nfSU+r0t_TFIZy=zx zCe5MT_Hl%8XcyD+TcRkkpq)K6MR->O*+M)q{~k<+#WSyV^8U=%xO>6PA!0mwO9(XbTj|S^)l=q+# zms0{BvCzuU2PwfY#vioeDv14*fe1Cz@0z_F`jnDXjuHb@f|#URKg(0!g-l;aDaOd9 z7VTmMF-r@imxM*=szEHO2Gn;l^YJ*+wkz(2_1Pqzn_l=c^vqUL1DEuGYVCqA<0_Q0`yXZO;%?xyfzy6QS(8TmBG4G zvD&Q<_ktASL&4FfiU=WMVm5u|p>?s^PPg5d!>Rci66IEj576MKDArvCG3(gR`Ap-d zTXF1}=wg$*N2ScIKwx9LEw(Pml_4bUkAN80TOsRK?vGV^mME0oaH~LtIhIY$?Nkha zQ!Bn@U~#<|tY{m@F!3ZSJyH)_pXMH(v{^+SlY-q}dsE}(%0~{N8{02nA2TX1Xi?4U!)lex?A=Qn~ z;Tb>{7ltSr&Xm>h%}_l2$3(c5e=EX7r%);Qlk;##ik0o);)V%hrR53SKh!mJyfo?W zJQNXteMNM``_#QpJlMY3gFS`$_XcT8;yEWuDstNyuzlNdjqo%JaxJz3J^ zBeOjG>0nM!iSu%C5kW+_Z9f0r6D8drFNtvgjb_`BS>cL(bM)!qZ_R!p{@MXG^XT=R z!Xi^}j2d|^G$Q;39@{?6g~sH0YgC7g+3IB=!Y&^oLlrz>{!=&i^cxN>6P#X2tb2~a z3xd&Ny<#Qf>DG*{GbS9$rO3Y%;PYcz7Wb~ji}}gJ+Wi@LGqn9im}81gfy~zGm}tJJ z(S#B7-bZEgKohbxy}!=H6_Bfc&(n!a`lj{avFY%x`4zmR3~jzUzr(?vDHE=S**QBsf&1M{r8gpt4@hc70rM&H%1p5-LMglh?`n zgbsamig~2>(SbtH0a4 z20&Gq-2@&&KGw0W(|qmvGT-&G^S}&`ZPwh$-$Wy`)_1bWR$^0$&(K$*n3m@sr;j&> zFF379Lszd7;?cap^6H95s!-rZYbG|LR}UQ>MS>@`=>HCr{}mzs6DI%r68%4`^FK$` z|1E#7feeU;{lWjv(gbP?WUyX9^P-x4Q_4Dh+g@wGZ}rNUSy=d3l`XPdd~`~~#>Lg7 zb3bArU?S9-uvzSDGy;7TIPDk=oX{@}jLT_zv4?wDpN`hBW#Lz4Ou3mT6G%^)<7v(x@_gn1nLLcXP#QDbenh2 zo-9rGw}YWV(8atMeFy21$BA1xS@xdczQuR8u>9pt)vJF)oOwe~!3OtO;>n zc3tVy;Pm6c4eH+bIUdY=U{PkxP7aDeab+ou$9;!21xT-QZ2#}ri74@Z4Lgmx*30Io zvOl~~VSaoCTWC5zCqFAY_xG{o_z1`VA9UvNQ^=ne4kxJ$Y|F zsqP9m(&FUv);%>!ubV>giZh4h`4#_>FU?XqjPH|w*4>T7+ z1sVBXm%5^mrO4a?HT=7HmAn~MB<%NZ)yNrFyAP%PjFyb&S*3u(Bd>5-?8eMCH`yYD z>;p?hIj|xZAtX{Y?~Dbw75}RY$Tkd4KA5F(uw$h>M}UnAx8-uZjaDYmzK4IQxAb0Y zac1D?t-Xs4gDR0(O?@8pHu^Zr7kVLbYi%X$aNN!d12d=O)$4v1IQsRWS&jGmF|eKl zmgjtzv!H$M7b`EbTj}_yu9h{t^R(!j?b@77wJgZcB(yIZZIB2Ac$z2O%Js604R(sw zNt{N=V-q&9sgkWSHh$+KQc>fSv+bx|e4U@N+<0Y*D!Lr=mlIqmwJ^D=QWI5UIK0_<|q`nFu?sQ+j8x9lRYNn%b zdr{*+vm5`BcqGU_@((o+WwqQ3Ztp#(*?Jg%BZh9o#<3f3Jv%~`YyY_KO^O0j%bd=$ z_AAp_PFhVaBIdhpvTq33d*7bu)Oma0DiYSeZmwX$kkg*n;|u$q53|gPXoY~ zpq^6f`LM@QrZ3n{!lzcgHBx7Hs#TO;5uLIQ?^=k*_@Xw`hhmKIoA7VH;QtqPvNUrS zY2HP;#_nk<*r!%@n?&!EBDg{V$q*y*P0Y-|7j}lsmZVh z4AJ9AMx;P7Wo=>$bario9F9-&-bwwYCuwVRFUb+5yrU&AJ^Z8l%Bke%h502#IiRVc zjB`oq-~n|lIxN}$2|3XyYcZ(wzkL7isHs?t7Cm-XK!GqeEll|7w=0_h)p#i&+p@(# zIZ<+-d;MqrSi{i9l`8x>uBN)ZN-nUXsi#bk$5NaP5*84CWQJLEv!|L5!WLxvWu;hb zA74H-4?t3w83wnoU?f@(b%te18x4`{ zpD1Js2cH00bV!tI;n-W#rb}JDvBGhv6BH9NL`6*cR5Cx-7avSfwaop z=Pw5!Tux818FJQ&hYW$87`@;8Yvm$D601(QnB7xjad4~Dx zI97nEYON@8*PvQWAw+gsy>(TP9#nByIwSL7WMaeK<=5_W5LFp%h%l<)5j`+X4l8?k zG`=&vx(bx@OUprpz5r>qfsjTo=lbt@i)ICC8W(|wnD)(ZffI|`c!IQ#TzxRhJ&%-a zJ#QAz&D4f6FsYH-vDnEWtu}0O(>P9qZxC-KqWiVSF+{lC!CZ_>*)F)H7V-#9X!2AK zGP}w&$zig}1pT#c`S88A+p&1fXlF$`!4dvK+^hcJWJiQYevD3vx^FdD>0o98$)SUv zzm7wY+My)u;iXa}I80=Q1p-sPyvUq`6;Oa}OXDzcCDsBiAS2T~LlOHDrqCfgBo}<0 z=pH5Yi1fS%>ZyV#*2O3gohUcP1`^JP>JoE`r`hX&rf;kr&7kvU zz5y0lRP?W`n^|kj95sgbf``Ib)iimP=w(kG>?9y}Oj!X8hZK$4mU`nGdr2I6x`NbK z*O{@S+zHy^&28>ebaowWp{Sop${U3pq=QG}%wyOPFa%4uASn#>OTZ+A1u?JwJVsZP zI=Dn|496SO(&GHFqd4_NpSvYr;aH%YfEFWnNM`OI)+&Y2?(FLfBLGfB4x!3E*S0M_`+9Gtqk}ZCM}uNrdh%q z3)?>e-5;8K_I4X*si_4|%@SMA?V$H+S$OB{_od3@cqDZU@vrXZ^;-$)is}ZM7z-i= z)NNy4iJb5r`ho-j-O1y>bMb-e#83L&E+Z5Za(X26)&nZ)E3PZoJ;u*v&KStxpdW@} z3v`=L!dW#Jv7cEnYp{`7D8**TT*l=1bCf}w5_EC>lD6h}q`YEN;BFy5+w@T6%wXqX z>g!ein`R*+_Fd5l2Zl12rETD4FMaT87XdD1DT)3>wuiVEGSP*x3yL| z(wybSn9$iO{b&xN@W^c>IcV%4J{YZIlGG9_>h0;kB+-@HP;*`Llzu4@gqB-XfqzUoy zJ(r(&M=pDgM)Ara>JO5uU)M}`;=gV*pS*h>;5hgA%c6(~CKK$xjvziyzP*Sg1MbFK^k&UTcON)B-PGTLml-{0>%^?ACl}gL-uw4Pq=>A&yu7ad7>o)IKAO=31Jc*i*OmzVAC4 z^tO1e&Dg2>wmvB>p%;#r3<-d#DY` zo2e_`LKz)JeJqOlI>x)>YU|(fyc|m4?BDiQ{TeRMPl}Zl8X-r_%oI02LhcEHCfbjt z*Mrn4CIICqn7gkYHD57b^rklO%Oz>3IYM7NbkKUHFTs(@4YvJB3;0Z z0i=Z@B_LHmI+5N%dhfmW-u{JW@4L@A_nw)5=FXjA21xSFN*3Q*tnYoEcLgVGXG7;l z`y$VY94|lGPk90w$>zgWfPLa&flI;N9TWDvF{MrJ4CI3&FWV@KR_j4uvg>F1 z+b(1QCPuo52;7=&o|bqfl3*)|_(azeMVARL$0vx`n(zZOx(V8v*pX?1GXE=_$Ygh}QjN%Tb z=yIsqYnci5JK4{G&tJX@*$LjaFGWqwglsJH*y2EV{g;Vy+M}~{{bT6{|d?e$HY@u*@Ic+bDfY@ zxy?1#iJ8rY&zO%UOowlLT;TXrHo?BBS0(KeV{bQ0Lz@POWgqFIdgq5qCt^M)$rXCN zr-<0w{tBEXdap$8i|1*iA$(Gt595sl>QmVaN|Y<%L>M#2&b_0iL32WL7ig73M(}Gc zC4omHw|j?LdHKq^^2o=#0Ihf$d?a)Zr^TvU2p>{xbK7N%;-YDNvCK%wURJI)RWiS~ zcC(J$86Ws|wC}|5GL1usFvWRMz~7oL68Yq6u58&XyJSQw7QcTPV~mUe?FFPVlC5CTIa$y_7blo+}LEQuPl8zCPp%nN9% zrR>+OZL{n%mU+&WSEF=bo316B6+y#0vbrfsWjQRjj6of8Cgs~fn?ef;JbTOX;y~H} z4LMFhSZN2c6o;1}p+@MLO<9@mx1SZ%K|b82_&R0yY7lgY6{nhh*Ou8?cx~=BxzFmw zkdm6S`7~-mlfJ7!0@t~JUe#`E_`w8XcQ4Wr^wSmnBh`7Tbn+Z7AcxV?FyzVgHnCm` zh~V6p3`mel11lvsoKPW3jlF?DiXQou|3_{pk$ zeHY0^0-Bni5($%L{ppn)D)2Nx;*J6aUCCi%d_u*l8;QRio%RLuKnX42b4nw$aY>uy zx-;U%M;3|%s6T0#o*;yC7Gs{(U`B4%<$zX|T2E|oy8A9JwnVNyeOebB+c=W6HSmuz z0Hl~jS1yp;+IU963XlgP=A4TUsXpDfWD@SY90yN|l;nr~dqA0V=i z?I>rrSZ=Y-!$5Bik)<@UoK#OrUFQZhP=hY^eGG=W7of^}P$#~Xq=VG_RWV>?fTr-S zScML&>wDs(F|cIfGm=yIiYvW3ayL!64l6Z_U>UwTlFqR7)O@()KHYg%`k?PehxDdY zG!P!E>Ec!*VK_}-gV=^%G9kUcwKt#!*2+zA>N_jnP7z?8$*3{=C6tJS@B7zA?NWir zcYi?Ci}}w@V9GWd)loP+Z(&t?z`4S5gW7I~_jZ1(AsH6cB5R@rLJyM0nZ_@S_ga|+ zV$(F35*~^m%gLm;;lr8^9|{&9ebKSDL3llMLh|U}0@hdg_2MR7pkEx7S_5)DdT#0I zIsJWco^#J%P-1g?ZK69fAHaV`QpkOg%Z~7_0?N@c>uLyCKTWmAB5rDWbE2<#6<*v% zTV!O8+2kCjk8j}es<6Gc^(`pNa}a1jyM1p3`v}afx{1{Z_ZB=+q}X)H?ta2g@dWBT z+9_Buk;`rjStu!4Ss{CnKJyZ|kxFDsTGnY6B)WKe{Tzil%aKIN`;8p;{@FzQs)q%y9Ul z(c7I76K1ZB$7-oi$PqR)?&9Y2n^gt>CsE)x0eM`YQ@BNSw!#+a{+3obiQ~$6fZos3##7x=Y>P@N7Vv74l9`k3N z()Gr5!(MY2$c#B&lrx~^A(Zgl;0wk9{DpSPuT$%hk50gon2kmO5Vd8VmS+h68) zA86bW7aN8EI4F~;(37pu%b9pR$Z~)|84dI+r;GOPd=5mt?+S|33O_G8+aj>Tet|^y zHwK3>18+emZJXOdcq4y`wouVSsCMD_b*+dVzx$63HuTyJBX{6qC`+X+U-w3eM%BTJ zZQ?0(zrf)CDRugXAM*Dm{z72PPx!2U zt}KWK;4}-Knvz{#7pdN`7W?ue=R3G}I(R703gVFV+QVFo zJIx&g6JIMt@n+Wv9JK(o658zJit_n@PMpscKXy>lN3rLyE0VzobDJYlyLIkqhYVuI z!LMrrt+fwY2?(MhcIwz??=?@BZ?Ao0QB7|^Ya)SOlFFsA0}9Hc%c0mTv5#wadQC=2 zBgOqj1yG|`0x-4qDVL@AL#M2|z9|={ZY}R-AR~=_{DwFCI0fKfYra$5g?jtPzTBKH zdkya-L(_%7hyv)sB1%6g-Wljvv>Q&v8&P(*OxkQx2L%Lc@qhY7Z7KlRYk%>m7K?xI zsAwXR1`Vj;^IKv44c_n)P<$c7xu4ne(`1uh1$kU_5gaskF!}0t1LI#SP+I4;x&+Gv zKy9N$zqN3}p(^oMbXBRs{J2`WoAnm1`I%^7+^YmL8Ge=6QwC;f%UZw!pgG=}j=c6C z7=C^`W;W@o*J^DE7TNSM4n_NxI~Q!%{HEItQNCJYJzAmq;J$kB9v+&W zH5%08x$mQ4@`(GH5(xMH5*u>oD<%=dHVQ+Kx6#q`jdjVTy7@khtQ00qXgc#o=*OdY1YNe2e5`p?-n#I>{6%axsWqeYSJli`@SiO* zA%9qSD93NE>K99}C{j~UlL?Ak3D2KwR|9`UYH`NQ8n)ew_9*~W2++wlJgw>|c+WK8xi)e0SdJYjD*pIfx_{qJ^1i?IN}K z11%L(xUol3`h`j0yyoTW;J+bMMR41nN1=uz*=cMfeMZK~7JMLh4 zCtWwG$fj6s7H+UoXwaJFg8krNdQO zm4GTz;A1$EL$~1}fdZ>6tjN<{t$pakP`0Mlb4fy2r@E4#e;G((6I#vm_{#Ga*Mnm- z0r!7(FMLJ6FbP78m{4^thq*o2Tkg_|$6qWFx~0#TIW`2(3<^+dv9jj#;*K^>d&@Nw zi0j$G3kPM}F`(=7;Cy15+(9N{p^WpqttktbV2Y~_N;Snc?z11?QD2ZyoM+86m`;ny zWqlO0^|lr=6Oi2j|MvovahB)=|8+;b__W_5u4XAs0F9;Op?xj-8~3uFcraL=;|tio zSw*fCzALJS86(eFnJEJ%xw;z`sOtQ09Ew>Y%fJ1FE$-l9hE?UeIzUuWdV&@Do*fij zg45gftjlr(46);nbj$_On_u&6DyWQB{{Ler!pax}#;iFO+>0oSp?VUz4o2bJ3##W7 z&YiSHQ~YSmIi+=2}2 zO-^tBsI6Z~wb_lcN=z`no8f5iSdyGe(-fSGyGY{dNDF$Cy7Q#x@k>m#ER(|m^|aU| ztp#h=bBhAGwCDcoU`~aiKSpaX?{4oJX^%Mb7vA9_~yb?2h>wx zaafc<&d*!+<+aM&m8#85C=r`d2Dps>A%y<{^I&p3(p3Ih@lcOU{+it|YM+=%Pb*=K zC}s>JaZ1rjL$E@EF&Op7H+AGBMHRMIhKeyMg`_dSn)+B4D`e2fL5W98m^yHksU2e^)q zOsToCNbTMjXIo2<9LuEoJYpf-^%b&Sx@Md*Od{G~nHj#R4lxYHF`wgHjg!2G!bLs`UMM2Y~hTnYh|?`pAa}B8OvI zET!*7diRe3_+)~Pp=Kxl>sKM#BcON%Nv}=;l=P8WF-fwJ*pCoU7$`99+vkFco?J&`TKQtbD$-->@i^z{~<5d3xZ2 ziT0fnXEVs)7f|gTtV=?5-jXJN)%hi1+IYaK{^;zrm##bQ>qSWYM8uN_?6$x@7@QpV zBdUJ8FH!UpI7^{*b?cu>O)9!krxq}!+!{8K3)auC^hM9T^{(s$r)8xR5V6DeuncEv zg=}@=hqo$@x_FYtLp+GT5HaCvr8Q(q*=y1Sf?=f%k2|~NDi+~?HkwLBrl_{L%A;4m zs0`;mUdtlFQW~%CiqwXn{pL-YNU)|xKUowGW9F{nOJ%0#BtISJiK6KWHFFgX@vCA^>%TGDBq%4pxkVcT}VLsV}U8puG-Gah~rA5Dl|xKCrS#NH&f826UV;vX~o(W z=LAodjYc@BvRxSY*kMnMLW9TI6(Fy;=5GB^ttn-&p1e@2iYpf?LDlLg#BB6*U2jG` z4kQa5auIvWzR}n*S135Fa3JF_Eyk3@VfWSJlS8eLPD8bRoQvqrn+ivm$31Gi+-J&% z*9(^|BMb0&9xSVGtMKMHLtjriKf^vRAFkvH{DRHgVdl*M+1k_n8y@^4qm7B3k^VTK ztQ6mrv7qHqrF03}bDy@qCAqq;>)2*ZEpO8?el@-%povQMg?hDcXF>usvljaEK8AadVl zG$c*gxA+EC?#sT!blox70@0-GuRPgYRI9TWSz;FNdToL|H}$y!=)3Hx`*s)#ooo*Q zifFIM?JEHB1fve^_{FFb^lS!u4pvw$A3xA4n?u=d+bw zGn~hahVLZ&PPmAz{d_xoDm+M8N28Vb&)}bxy?Tf)fTv;8zn{W(+}XYlx!o?XaafOU zhsvFLFqP};ROl$KHwjU1OLk3g#9>tk_w7(%yr@-*vwhB|*F;hD->9&RYik?3F;6}1?z~xmZ^sDLpCvSocoXX#-S@8mBB+f= zGiw9uq6@wc4%-`9r!s1p2Bo-0KjayofZ>^6kF z>A#+6JZuxL+rwbJ`6QA^<{ zkNGPg`&rOCjeN2oqeodIO0#Sy^#znWGeolUi+%8>y_kMHGznv$b66%CM- zN@D&x?fg_ntM;?2lBmF)d2q`FO6_j%T6=6T-KEFy6e{k=x{ySg@}4im;RbXx!}AJc z@FuV!z9nd6eJ8mhBJSAEGs4bu>sx&FoI^p%75w+F5my(jBaLY9TnsNeZTjkO_Hcd} zU3m0~ouLdbqZV&Gl4q_2tTR&gs8>*vC)H6XC-*?btCP66b0)a)ewT$Hd!(wdq?TAd zCZvHv-bAQJdQ%SWq_MyOw)?8{Z4H$Ax~rcAR3t>GBuy!%qM^Y(PXEkVO>3d4d-rn{ zPNITVLu2fS$1ZkR7!hy(Q(4O;CgSC;pUdqt5?28fm2D~U!b(*ffyVealBv4Wxy|52 z18)3`dd>J)Oauv!%Iz8vOC2&cujBVcai-I~)#RZII#D1geQ=fV&Jb@+3(w{Jx10k* zThFcVoNCpM%y{Loppm3_UD5gc2RpSb*H@j?Ks&YGuEyN$SxdBF+9w;oM=zBZ7gJ4U?P^I!O8-IQaO8HfUN7_s=(bK6d1%p zMF3g~`uaio@6}Pmh&(aSz)%@?tN%NW?(4wj_=h?NMC5lyEz&Rao&dEzEa;JDHuQ3r zEvC_8DHS}lkiiT^fFq;l0NsgZA3MrKVySk zJ8T+?=vzCaA?i{U)11i-6VATao$k8#%skZc8j`txCrK~}49a>5rXvya0K^TGE*m*U zel>9;C!ZShH%_=qKj$9}-xaduQbqde?kqt{oIpR9>Tn4BNuYLMoSvj1T8^m43hUVAWKHWPAOhKlP<>AtN0Dw&` z>A*k=boV4m_i2HW=zmb6t|C;a&9$iU^PqtYW?`DSBwzUYcxKd@Xwj-5-?zk>iKHu7p5}hf+WGMu1Var(N{4-wNMZ)zuZ0|IQ}doS?`#<5@nu|HvPbndvMeKN0~TDX|eD8hV-v7 nre-cC&Q^}DRu1+cDJvI8TO%)0@|X(XC7@T*N>T+9Z{Ghu*{_wj diff --git a/extensions/vscode-api-tests/testWorkspace/image.png b/extensions/vscode-api-tests/testWorkspace/image.png index 15b462975bee12491b059444010eeb347dc35fbd..761522a3347ff62191c92b62780eaa9f41997f95 100644 GIT binary patch literal 43431 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 From 99226399fc8a0df12f04600b8c7a9706e3b31269 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 17:47:58 -0800 Subject: [PATCH 117/330] Skip html image tests on web Need to figure out how to handle file uris on web --- src/vs/base/test/browser/markdownRenderer.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index de58e25197c..310963142ca 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { renderMarkdown, renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { parse } from 'vs/base/common/marshalling'; +import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; function strToNode(str: string): HTMLElement { @@ -258,6 +259,10 @@ suite('MarkdownRenderer', () => { }); test('Should render html images', () => { + if (isWeb) { + return; + } + const mds = new MarkdownString(undefined, { supportHtml: true }); mds.appendMarkdown(``); @@ -266,6 +271,10 @@ suite('MarkdownRenderer', () => { }); test('Should render html images with file uri as same origin uri', () => { + if (isWeb) { + return; + } + const mds = new MarkdownString(undefined, { supportHtml: true }); mds.appendMarkdown(``); From fc4e141811d873f697ad21edf43985ad1b0234a3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 18:15:15 -0800 Subject: [PATCH 118/330] Skip one more test on web --- src/vs/base/test/browser/markdownRenderer.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 310963142ca..fc154b0f8d3 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -59,6 +59,9 @@ suite('MarkdownRenderer', () => { }); test('image with file uri should render as same origin uri', () => { + if (isWeb) { + return; + } const result: HTMLElement = renderMarkdown({ value: `![image](file:///images/cat.gif)` }).element; assertNodeEquals(result, '

image

'); }); From 4415c0f1054b3947b253f386bd45337ef584335f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 18:16:01 -0800 Subject: [PATCH 119/330] Update ts@next for building VS Code --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build/package.json b/build/package.json index 337cac1406a..1d566ae9797 100644 --- a/build/package.json +++ b/build/package.json @@ -62,7 +62,7 @@ "plist": "^3.0.1", "source-map": "0.6.1", "tmp": "^0.2.1", - "typescript": "^4.6.0-dev.20211108", + "typescript": "^4.6.0-dev.20211115", "vsce": "^1.100.0", "vscode-universal-bundler": "^0.0.2" }, diff --git a/build/yarn.lock b/build/yarn.lock index c2ca84ea946..f30d26a6f71 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2557,10 +2557,10 @@ typed-rest-client@^1.8.4: tunnel "0.0.6" underscore "^1.12.1" -typescript@^4.6.0-dev.20211108: - version "4.6.0-dev.20211108" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.0-dev.20211108.tgz#d9e65b39f0876ba9d5e82b7955d1183c38d1e40b" - integrity sha512-5a0mWJq05zNPSb0vF4s6OJbCxT0Cz6XIjgkaiYy5pkSXlu2E8mYRnYlo9IKKn34eUFO5FiO0uwm0Z3dsbcEgDw== +typescript@^4.6.0-dev.20211115: + version "4.6.0-dev.20211115" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.0-dev.20211115.tgz#215e5d032e77cb83f382dc88e901a0757c02cc53" + integrity sha512-rYdYp/j8OhCRFs97l7GNOX9xGHndwwgY8AcL7LDzmFXgBOXC2VLoQP48nCg8FgVzjK6s0M5V4nijTYHRlwiqGQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" diff --git a/package.json b/package.json index 769de7d9af5..952860119a7 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,7 @@ "style-loader": "^1.0.0", "ts-loader": "^9.2.3", "tsec": "0.1.4", - "typescript": "^4.6.0-dev.20211108", + "typescript": "^4.6.0-dev.20211115", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index b4cd859cd2e..f525f917925 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10033,10 +10033,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.6.0-dev.20211108: - version "4.6.0-dev.20211108" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.0-dev.20211108.tgz#d9e65b39f0876ba9d5e82b7955d1183c38d1e40b" - integrity sha512-5a0mWJq05zNPSb0vF4s6OJbCxT0Cz6XIjgkaiYy5pkSXlu2E8mYRnYlo9IKKn34eUFO5FiO0uwm0Z3dsbcEgDw== +typescript@^4.6.0-dev.20211115: + version "4.6.0-dev.20211115" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.0-dev.20211115.tgz#215e5d032e77cb83f382dc88e901a0757c02cc53" + integrity sha512-rYdYp/j8OhCRFs97l7GNOX9xGHndwwgY8AcL7LDzmFXgBOXC2VLoQP48nCg8FgVzjK6s0M5V4nijTYHRlwiqGQ== typical@^4.0.0: version "4.0.0" From 8a7324fd2ed68a540a82b54a69ccaa1e47cee164 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Nov 2021 18:43:28 -0800 Subject: [PATCH 120/330] Allow relative file links inside of markdown notebook cells Fixes #126097 --- .../browser/view/renderers/backLayerWebView.ts | 12 +++++++++++- .../browser/view/renderers/webviewMessages.ts | 6 ++++++ .../browser/view/renderers/webviewPreloads.ts | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index c50030657da..fb24a88f2c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -27,7 +27,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IFileService } from 'vs/platform/files/common/files'; -import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; +import { IOpenerService, matchesScheme, matchesSomeScheme } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; @@ -615,6 +615,16 @@ var requirejs = (function() { this._onDidClickDataLink(data); break; } + case 'clicked-link': + { + if (matchesSomeScheme(data.href, Schemas.http, Schemas.https, Schemas.mailto)) { + this.openerService.open(data.href, { fromUserGesture: true }); + } else if (!/^[\w\-]+:/.test(data.href)) { + const path = URI.joinPath(dirname(this.documentUri), data.href); + this.openerService.open(path, { fromUserGesture: true }); + } + break; + } case 'customKernelMessage': { this._onMessage.fire({ message: data.message }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 52430aabb78..ed62d87c052 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -83,6 +83,11 @@ export interface IClickMarkupCellMessage extends BaseToWebviewMessage { readonly shiftKey: boolean; } +export interface IClickedLinkMessage extends BaseToWebviewMessage { + readonly type: 'clicked-link'; + readonly href: string; +} + export interface IContextMenuMarkupCellMessage extends BaseToWebviewMessage { readonly type: 'contextMenuMarkupCell'; readonly cellId: string; @@ -373,6 +378,7 @@ export type FromWebviewMessage = WebviewInitialized | ICustomRendererMessage | IClickedDataUrlMessage | IClickMarkupCellMessage | + IClickedLinkMessage | IContextMenuMarkupCellMessage | IMouseEnterMarkupCellMessage | IMouseLeaveMarkupCellMessage | diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index df12a174b99..46434821fab 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -96,9 +96,15 @@ async function webviewPreloads(ctx: PreloadContext) { postNotebookMessage('scroll-to-reveal', { scrollTop }); return; } + } else { + const href = node.getAttribute('href'); + if (href) { + postNotebookMessage('clicked-link', { href }); + } } event.preventDefault(); + event.stopPropagation(); return; } } From 6a25ae3a24929f35aabf8a051531c0716032e0a0 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 15 Nov 2021 19:06:03 -0800 Subject: [PATCH 121/330] enable browser and remote integration tests (#137254) --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bccbb3fef51..81394730d69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,9 +75,20 @@ jobs: - name: Run Unit Tests (Browser) run: yarn test-browser --browser chromium + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + - name: Run Integration Tests (Electron) run: .\scripts\test-integration.bat + - name: Run Integration Tests (Browser) + timeout-minutes: 10 + run: .\resources\server\test\test-web-integration.bat --browser firefox + + - name: Run Remote Integration Tests (Electron) + timeout-minutes: 10 + run: .\resources\server\test\test-remote-integration.bat + linux: name: Linux runs-on: ubuntu-latest @@ -140,10 +151,22 @@ jobs: id: browser-unit-tests run: DISPLAY=:10 yarn test-browser --browser chromium + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + - name: Run Integration Tests (Electron) id: electron-integration-tests run: DISPLAY=:10 ./scripts/test-integration.sh + - name: Run Integration Tests (Browser) + id: browser-integration-tests + run: DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium + + - name: Run Remote Integration Tests (Electron) + id: electron-remote-integration-tests + timeout-minutes: 7 + run: DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + darwin: name: macOS runs-on: macos-latest @@ -202,9 +225,19 @@ jobs: - name: Run Unit Tests (Browser) run: DISPLAY=:10 yarn test-browser --browser chromium + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + - name: Run Integration Tests (Electron) run: DISPLAY=:10 ./scripts/test-integration.sh + - name: Run Integration Tests (Browser) + run: DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser webkit + + - name: Run Remote Integration Tests (Electron) + timeout-minutes: 7 + run: DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + hygiene: name: Hygiene, Layering and Monaco Editor runs-on: ubuntu-latest From 7f3fb9cf2df6f8b909b8d6441832b15393b5d163 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 15 Nov 2021 19:33:14 +0100 Subject: [PATCH 122/330] #51935 clone override contents --- src/vs/platform/configuration/common/configurationModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 58aa8f2effb..0cb02c481bd 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -175,7 +175,7 @@ export class ConfigurationModel implements IConfigurationModel { if (contents) { this.mergeContents(contents, contentsToMerge); } else { - contents = contentsToMerge; + contents = objects.deepClone(contentsToMerge); } } }; From c0d8e78359bb062221f42b4401cc021c9402929d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Nov 2021 09:35:57 +0100 Subject: [PATCH 123/330] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 952860119a7..85d6e73c481 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "36fc578790f6eade7c7dadd8e19aad867102d14f", + "distro": "c1103ffc0e43a5e04fff74ede20733c5c68ed756", "author": { "name": "Microsoft Corporation" }, From 5c52ca4dace154777b197740e3da58cb04e3f06a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Nov 2021 09:58:04 +0100 Subject: [PATCH 124/330] api - update docs around `onDidChangeWorkspaceFolders` (fix #137263) --- src/vscode-dts/vscode.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index a482b0f7c00..4007777baa7 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -10784,6 +10784,11 @@ declare module 'vscode' { /** * 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; From 409041b7ff707baf57076c6a2faa842122ca1cbc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Nov 2021 14:07:03 +0100 Subject: [PATCH 125/330] dedupe workspace symbols results --- .../api/browser/mainThreadLanguageFeatures.ts | 37 ++-- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostApiCommands.ts | 10 +- .../api/common/extHostLanguageFeatures.ts | 2 +- .../search/browser/search.contribution.ts | 7 +- .../search/browser/symbolsQuickAccess.ts | 179 +++++++++--------- .../workbench/contrib/search/common/search.ts | 63 +++++- .../browser/api/extHostApiCommands.test.ts | 2 +- .../api/extHostLanguageFeatures.test.ts | 38 +++- 9 files changed, 205 insertions(+), 135 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 5e6151d2875..3229f48b2aa 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -384,27 +384,26 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- navigate type - $registerNavigateTypeSupport(handle: number): void { + $registerNavigateTypeSupport(handle: number, supportsResolve: boolean): void { let lastResultId: number | undefined; - this._registrations.set(handle, search.WorkspaceSymbolProviderRegistry.register({ - provideWorkspaceSymbols: (search: string, token: CancellationToken): Promise => { - return this._proxy.$provideWorkspaceSymbols(handle, search, token).then(result => { - if (lastResultId !== undefined) { - this._proxy.$releaseWorkspaceSymbols(handle, lastResultId); - } - lastResultId = result._id; - return MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(result.symbols); - }); - }, - resolveWorkspaceSymbol: (item: search.IWorkspaceSymbol, token: CancellationToken): Promise => { - return this._proxy.$resolveWorkspaceSymbol(handle, item, token).then(i => { - if (i) { - return MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(i); - } - return undefined; - }); + + const provider: search.IWorkspaceSymbolProvider = { + provideWorkspaceSymbols: async (search: string, token: CancellationToken): Promise => { + const result = await this._proxy.$provideWorkspaceSymbols(handle, search, token); + if (lastResultId !== undefined) { + this._proxy.$releaseWorkspaceSymbols(handle, lastResultId); + } + lastResultId = result._id; + return MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(result.symbols); } - })); + }; + if (supportsResolve) { + provider.resolveWorkspaceSymbol = async (item: search.IWorkspaceSymbol, token: CancellationToken): Promise => { + const resolvedItem = await this._proxy.$resolveWorkspaceSymbol(handle, item, token); + return resolvedItem && MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(resolvedItem); + }; + } + this._registrations.set(handle, search.WorkspaceSymbolProviderRegistry.register(provider)); } // --- rename diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2144f6df3e6..b8565df3724 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -405,7 +405,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void; - $registerNavigateTypeSupport(handle: number): void; + $registerNavigateTypeSupport(handle: number, supportsResolve: boolean): void; $registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportsResolveInitialValues: boolean): void; $registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticTokensLegend, eventHandle: number | undefined): void; $emitDocumentSemanticTokensEvent(eventHandle: number): void; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 1a46b7661de..eeb38e33bf8 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -129,14 +129,8 @@ const newCommands: ApiCommand[] = [ new ApiCommand( 'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol providers.', [ApiCommandArgument.String.with('query', 'Search string')], - new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => { - const result: types.SymbolInformation[] = []; - if (Array.isArray(value)) { - for (let tuple of value) { - result.push(...tuple[1].map(typeConverters.WorkspaceSymbol.to)); - } - } - return result; + new ApiCommandResult('A promise that resolves to an array of SymbolInformation-instances.', value => { + return value.map(typeConverters.WorkspaceSymbol.to); }) ), // --- call hierarchy diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 667289e83ad..7809c705277 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1845,7 +1845,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { const handle = this._addNewAdapter(new NavigateTypeAdapter(provider, this._logService), extension); - this._proxy.$registerNavigateTypeSupport(handle); + this._proxy.$registerNavigateTypeSupport(handle, typeof provider.resolveWorkspaceSymbol === 'function'); return this._createDisposable(handle); } diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 9e368be9a08..cdc625287a5 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -46,7 +46,7 @@ import { registerContributions as searchWidgetContributions } from 'vs/workbench import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { resolveResourcesForSearchIncludes } from 'vs/workbench/contrib/search/common/queryBuilder'; -import { getWorkspaceSymbols, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; +import { getWorkspaceSymbols, IWorkspaceSymbol, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { FileMatch, FileMatchOrMatch, FolderMatch, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; @@ -1025,10 +1025,11 @@ configurationRegistry.registerConfiguration({ } }); -CommandsRegistry.registerCommand('_executeWorkspaceSymbolProvider', function (accessor, ...args) { +CommandsRegistry.registerCommand('_executeWorkspaceSymbolProvider', async function (accessor, ...args): Promise { const [query] = args; assertType(typeof query === 'string'); - return getWorkspaceSymbols(query); + const result = await getWorkspaceSymbols(query); + return result.map(item => item.symbol); }); // Go to menu diff --git a/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts b/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts index eaa56c70ef2..478712d763e 100644 --- a/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts @@ -119,102 +119,101 @@ export class SymbolsQuickAccessProvider extends PickerQuickAccessProvider 0) { + + // First: try to score on the entire query, it is possible that + // the symbol matches perfectly (e.g. searching for "change log" + // can be a match on a markdown symbol "change log"). In that + // case we want to skip the container query altogether. + if (symbolQuery !== query) { + [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, { ...query, values: undefined /* disable multi-query support */ }, 0, symbolLabelIconOffset); + if (typeof symbolScore === 'number') { + skipContainerQuery = true; // since we consumed the query, skip any container matching + } + } + + // Otherwise: score on the symbol query and match on the container later + if (typeof symbolScore !== 'number') { + [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, symbolQuery, 0, symbolLabelIconOffset); + if (typeof symbolScore !== 'number') { + continue; + } + } + } + + const symbolUri = symbol.location.uri; + let containerLabel: string | undefined = undefined; + if (symbolUri) { + const containerPath = this.labelService.getUriLabel(symbolUri, { relative: true }); + if (symbol.containerName) { + containerLabel = `${symbol.containerName} • ${containerPath}`; + } else { + containerLabel = containerPath; + } + } + + // Score by container if specified and searching + let containerScore: number | undefined = undefined; + let containerMatches: IMatch[] | undefined = undefined; + if (!skipContainerQuery && containerQuery && containerQuery.original.length > 0) { + if (containerLabel) { + [containerScore, containerMatches] = scoreFuzzy2(containerLabel, containerQuery); + } + + if (typeof containerScore !== 'number') { continue; } - const symbolLabel = symbol.name; - const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`; - const symbolLabelIconOffset = symbolLabelWithIcon.length - symbolLabel.length; - - // Score by symbol label if searching - let symbolScore: number | undefined = undefined; - let symbolMatches: IMatch[] | undefined = undefined; - let skipContainerQuery = false; - if (symbolQuery.original.length > 0) { - - // First: try to score on the entire query, it is possible that - // the symbol matches perfectly (e.g. searching for "change log" - // can be a match on a markdown symbol "change log"). In that - // case we want to skip the container query altogether. - if (symbolQuery !== query) { - [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, { ...query, values: undefined /* disable multi-query support */ }, 0, symbolLabelIconOffset); - if (typeof symbolScore === 'number') { - skipContainerQuery = true; // since we consumed the query, skip any container matching - } - } - - // Otherwise: score on the symbol query and match on the container later - if (typeof symbolScore !== 'number') { - [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, symbolQuery, 0, symbolLabelIconOffset); - if (typeof symbolScore !== 'number') { - continue; - } - } + if (typeof symbolScore === 'number') { + symbolScore += containerScore; // boost symbolScore by containerScore } - - const symbolUri = symbol.location.uri; - let containerLabel: string | undefined = undefined; - if (symbolUri) { - const containerPath = this.labelService.getUriLabel(symbolUri, { relative: true }); - if (symbol.containerName) { - containerLabel = `${symbol.containerName} • ${containerPath}`; - } else { - containerLabel = containerPath; - } - } - - // Score by container if specified and searching - let containerScore: number | undefined = undefined; - let containerMatches: IMatch[] | undefined = undefined; - if (!skipContainerQuery && containerQuery && containerQuery.original.length > 0) { - if (containerLabel) { - [containerScore, containerMatches] = scoreFuzzy2(containerLabel, containerQuery); - } - - if (typeof containerScore !== 'number') { - continue; - } - - if (typeof symbolScore === 'number') { - symbolScore += containerScore; // boost symbolScore by containerScore - } - } - - const deprecated = symbol.tags ? symbol.tags.indexOf(SymbolTag.Deprecated) >= 0 : false; - - symbolPicks.push({ - symbol, - resource: symbolUri, - score: symbolScore, - label: symbolLabelWithIcon, - ariaLabel: symbolLabel, - highlights: deprecated ? undefined : { - label: symbolMatches, - description: containerMatches - }, - description: containerLabel, - strikethrough: deprecated, - buttons: [ - { - iconClass: openSideBySideDirection === 'right' ? Codicon.splitHorizontal.classNames : Codicon.splitVertical.classNames, - tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom") - } - ], - trigger: (buttonIndex, keyMods) => { - this.openSymbol(provider, symbol, token, { keyMods, forceOpenSideBySide: true }); - - return TriggerAction.CLOSE_PICKER; - }, - accept: async (keyMods, event) => this.openSymbol(provider, symbol, token, { keyMods, preserveFocus: event.inBackground, forcePinned: event.inBackground }), - }); } + + const deprecated = symbol.tags ? symbol.tags.indexOf(SymbolTag.Deprecated) >= 0 : false; + + symbolPicks.push({ + symbol, + resource: symbolUri, + score: symbolScore, + label: symbolLabelWithIcon, + ariaLabel: symbolLabel, + highlights: deprecated ? undefined : { + label: symbolMatches, + description: containerMatches + }, + description: containerLabel, + strikethrough: deprecated, + buttons: [ + { + iconClass: openSideBySideDirection === 'right' ? Codicon.splitHorizontal.classNames : Codicon.splitVertical.classNames, + tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom") + } + ], + trigger: (buttonIndex, keyMods) => { + this.openSymbol(provider, symbol, token, { keyMods, forceOpenSideBySide: true }); + + return TriggerAction.CLOSE_PICKER; + }, + accept: async (keyMods, event) => this.openSymbol(provider, symbol, token, { keyMods, preserveFocus: event.inBackground, forcePinned: event.inBackground }), + }); + } // Sort picks (unless disabled) diff --git a/src/vs/workbench/contrib/search/common/search.ts b/src/vs/workbench/contrib/search/common/search.ts index 69581fc213f..03eaa7595de 100644 --- a/src/vs/workbench/contrib/search/common/search.ts +++ b/src/vs/workbench/contrib/search/common/search.ts @@ -14,9 +14,11 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { CancellationToken } from 'vs/base/common/cancellation'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IFileService } from 'vs/platform/files/common/files'; -import { IRange } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { isNumber } from 'vs/base/common/types'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { compare } from 'vs/base/common/strings'; +import { groupBy } from 'vs/base/common/arrays'; export interface IWorkspaceSymbol { name: string; @@ -59,19 +61,62 @@ export namespace WorkspaceSymbolProviderRegistry { } } -export function getWorkspaceSymbols(query: string, token: CancellationToken = CancellationToken.None): Promise<[IWorkspaceSymbolProvider, IWorkspaceSymbol[]][]> { +export class WorkspaceSymbolItem { + constructor(readonly symbol: IWorkspaceSymbol, readonly provider: IWorkspaceSymbolProvider) { } +} - const result: [IWorkspaceSymbolProvider, IWorkspaceSymbol[]][] = []; +export async function getWorkspaceSymbols(query: string, token: CancellationToken = CancellationToken.None): Promise { - const promises = WorkspaceSymbolProviderRegistry.all().map(support => { - return Promise.resolve(support.provideWorkspaceSymbols(query, token)).then(value => { - if (Array.isArray(value)) { - result.push([support, value]); + const all: WorkspaceSymbolItem[] = []; + + const promises = WorkspaceSymbolProviderRegistry.all().map(async provider => { + try { + const value = await provider.provideWorkspaceSymbols(query, token); + if (!value) { + return; } - }, onUnexpectedError); + for (let symbol of value) { + all.push(new WorkspaceSymbolItem(symbol, provider)); + } + } catch (err) { + onUnexpectedError(err); + } }); - return Promise.all(promises).then(_ => result); + await Promise.all(promises); + + if (token.isCancellationRequested) { + return []; + } + + // de-duplicate entries + + function compareItems(a: WorkspaceSymbolItem, b: WorkspaceSymbolItem): number { + let res = compare(a.symbol.name, b.symbol.name); + if (res === 0) { + res = a.symbol.kind - b.symbol.kind; + } + if (res === 0) { + res = compare(a.symbol.location.uri.toString(), b.symbol.location.uri.toString()); + } + if (res === 0) { + if (a.symbol.location.range && b.symbol.location.range) { + if (!Range.areIntersecting(a.symbol.location.range, b.symbol.location.range)) { + res = Range.compareRangesUsingStarts(a.symbol.location.range, b.symbol.location.range); + } + } else if (a.provider.resolveWorkspaceSymbol && !b.provider.resolveWorkspaceSymbol) { + res = -1; + } else if (!a.provider.resolveWorkspaceSymbol && b.provider.resolveWorkspaceSymbol) { + res = 1; + } + } + if (res === 0) { + res = compare(a.symbol.containerName ?? '', b.symbol.containerName ?? ''); + } + return res; + } + + return groupBy(all, compareItems).map(group => group[0]).flat(); } export interface IWorkbenchSearchConfigurationProperties extends ISearchConfigurationProperties { diff --git a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts index 141d2420fa2..334d6dbf96c 100644 --- a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts @@ -201,12 +201,12 @@ suite('ExtHostLanguageFeatureCommands', function () { return rpcProtocol.sync().then(() => { return commands.executeCommand('vscode.executeWorkspaceSymbolProvider', 'testing').then(value => { + assert.strictEqual(value.length, 2); // de-duped for (let info of value) { assert.strictEqual(info instanceof types.SymbolInformation, true); assert.strictEqual(info.name, 'testing'); assert.strictEqual(info.kind, types.SymbolKind.Array); } - assert.strictEqual(value.length, 3); }); }); }); diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index 0871f46573c..52f13c584d7 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -681,9 +681,41 @@ suite('ExtHostLanguageFeatures', function () { let value = await getWorkspaceSymbols(''); assert.strictEqual(value.length, 1); const [first] = value; - const [, symbols] = first; - assert.strictEqual(symbols.length, 1); - assert.strictEqual(symbols[0].name, 'testing'); + assert.strictEqual(first.symbol.name, 'testing'); + }); + + test('Navigate types, de-duplicate results', async () => { + const uri = URI.from({ scheme: 'foo', path: '/some/path' }); + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider { + provideWorkspaceSymbols(): any { + return [new types.SymbolInformation('ONE', types.SymbolKind.Array, undefined, new types.Location(uri, new types.Range(0, 0, 1, 1)))]; + } + })); + + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider { + provideWorkspaceSymbols(): any { + return [new types.SymbolInformation('ONE', types.SymbolKind.Array, undefined, new types.Location(uri, new types.Range(0, 0, 1, 1)))]; // get de-duped + } + })); + + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider { + provideWorkspaceSymbols(): any { + return [new types.SymbolInformation('ONE', types.SymbolKind.Array, undefined, new types.Location(uri, undefined!))]; // NO dedupe because of resolve + } + resolveWorkspaceSymbol(a: vscode.SymbolInformation) { + return a; + } + })); + + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider { + provideWorkspaceSymbols(): any { + return [new types.SymbolInformation('ONE', types.SymbolKind.Struct, undefined, new types.Location(uri, new types.Range(0, 0, 1, 1)))]; // NO dedupe because of kind + } + })); + + await rpcProtocol.sync(); + let value = await getWorkspaceSymbols(''); + assert.strictEqual(value.length, 3); }); // --- rename From dd72b79db6565bb5b1c24a47954147282500e330 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Nov 2021 14:32:35 +0100 Subject: [PATCH 126/330] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 85d6e73c481..df347738880 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "c1103ffc0e43a5e04fff74ede20733c5c68ed756", + "distro": "a9033b9207d753215ad13618e125ddb770066bd1", "author": { "name": "Microsoft Corporation" }, From 9583050514e01b1d9bd0d6b9c40156d90081da52 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Nov 2021 14:36:36 +0100 Subject: [PATCH 127/330] fix compiler --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0ec2973e8b3..5a41a2e0ba3 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -921,7 +921,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, registerFileSystemProvider(scheme, provider, options) { return combinedDisposable( - extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options), + extHostFileSystem.registerFileSystemProvider(extension, scheme, provider, options), extHostConsumerFileSystem.addFileSystemProvider(scheme, provider) ); }, From ff04fe28ed1651e51254a4c26c639a443e5882ed Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Nov 2021 14:39:18 +0100 Subject: [PATCH 128/330] don't freeze copied object --- src/vs/workbench/api/common/extHostFileSystemConsumer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts index 313692e3978..cc8805a700b 100644 --- a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts +++ b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts @@ -39,13 +39,13 @@ export class ExtHostConsumerFileSystem { // use shortcut await that._proxy.$ensureActivation(uri.scheme); const stat = await provider.stat(uri); - return Object.freeze({ + return { type: stat.type, ctime: stat.ctime, mtime: stat.mtime, size: stat.size, permissions: stat.permissions - }); + }; } catch (err) { ExtHostConsumerFileSystem._handleError(err); } From 56681eda57ae62cb3e90a06a7e7441ad3f1e3d23 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 16 Nov 2021 14:43:34 +0100 Subject: [PATCH 129/330] Update Publish Branch label --- extensions/git/src/repository.ts | 4 ++-- extensions/git/src/statusbar.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 54f5dc55b57..8f6d5c60d88 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1944,8 +1944,8 @@ export class Repository implements Disposable { } else { actionButton = { command: 'git.publish', - title: localize('scm button publish title', "$(cloud-upload) Publish Changes"), - tooltip: localize('scm button publish tooltip', "Publish Changes"), + title: localize('scm button publish title', "$(cloud-upload) Publish Branch"), + tooltip: localize('scm button publish tooltip', "Publish Branch"), arguments: [this._sourceControl], }; } diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts index fd556024f83..1de71ad94a0 100644 --- a/extensions/git/src/statusbar.ts +++ b/extensions/git/src/statusbar.ts @@ -154,7 +154,7 @@ class SyncStatusBar { } else { icon = '$(cloud-upload)'; command = 'git.publish'; - tooltip = localize('publish changes', "Publish Changes"); + tooltip = localize('publish branch', "Publish Branch"); } } else { command = ''; From adcda6af72cdaca96e2d357f395b4b23698970dd Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 16 Nov 2021 14:51:31 +0100 Subject: [PATCH 130/330] Add onWillDrop to tree dnd proposal Part of #32592 --- .../api/browser/mainThreadTreeViews.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 41 +++++++++++++------ .../workbench/browser/parts/views/treeView.ts | 37 +++++++++++++---- src/vs/workbench/common/views.ts | 3 +- .../vscode.proposed.treeViewDragAndDrop.d.ts | 18 ++++---- 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 1df23985f9c..faed925d709 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -169,8 +169,8 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { constructor(private readonly treeViewId: string, private readonly _proxy: ExtHostTreeViewsShape) { } - async onDrop(dataTransfer: ITreeDataTransfer, targetTreeItem: ITreeItem): Promise { - return this._proxy.$onDrop(this.treeViewId, await TreeDataTransferConverter.toTreeDataTransferDTO(dataTransfer), targetTreeItem.handle); + async onDrop(dataTransfer: ITreeDataTransfer, targetTreeItem: ITreeItem, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise { + return this._proxy.$onDrop(this.treeViewId, await TreeDataTransferConverter.toTreeDataTransferDTO(dataTransfer), targetTreeItem.handle, sourceTreeId, sourceTreeItemHandles); } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b8565df3724..f953182df55 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1271,7 +1271,7 @@ export interface ExtHostDocumentsAndEditorsShape { export interface ExtHostTreeViewsShape { $getChildren(treeViewId: string, treeItemHandle?: string): Promise; - $onDrop(treeViewId: string, treeDataTransfer: TreeDataTransferDTO, newParentTreeItemHandle: string): Promise; + $onDrop(destinationViewId: string, treeDataTransfer: TreeDataTransferDTO, newParentTreeItemHandle: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise; $setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void; $setSelection(treeViewId: string, treeItemHandles: string[]): void; $setVisible(treeViewId: string, visible: boolean): void; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index e4730e17821..f5e63bdc8cc 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; -import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions, TREE_ITEM_DATA_TRANSFER_TYPE } from 'vs/workbench/common/views'; +import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { asPromise } from 'vs/base/common/async'; import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringType } from 'vs/workbench/api/common/extHostTypes'; @@ -129,22 +129,22 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return treeView.getChildren(treeItemHandle); } - async $onDrop(treeViewId: string, treeDataTransferDTO: TreeDataTransferDTO, newParentItemHandle: string): Promise { - const treeView = this.treeViews.get(treeViewId); + async $onDrop(destinationViewId: string, treeDataTransferDTO: TreeDataTransferDTO, newParentItemHandle: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise { + const treeView = this.treeViews.get(destinationViewId); if (!treeView) { - return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId))); + return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', destinationViewId))); } const treeDataTransfer = TreeDataTransferConverter.toITreeDataTransfer(treeDataTransferDTO); - if (treeDataTransfer.items.has(TREE_ITEM_DATA_TRANSFER_TYPE)) { - const sourceHandles: string[] = JSON.parse(await treeDataTransfer.items.get(TREE_ITEM_DATA_TRANSFER_TYPE)!.asString()); - const sourceElements = sourceHandles.map(handle => treeView.getExtensionElement(handle)).filter(element => !!element); - if (sourceElements.length > 0) { - treeDataTransfer.items.set(TREE_ITEM_DATA_TRANSFER_TYPE, { - asString: async () => JSON.stringify(sourceElements) - }); - } else { - treeDataTransfer.items.delete(TREE_ITEM_DATA_TRANSFER_TYPE); + if ((sourceViewId === destinationViewId) && sourceTreeItemHandles) { + const additionalTransferItems = await treeView.onWillDrop(sourceTreeItemHandles); + if (additionalTransferItems) { + for (const key of additionalTransferItems.items.keys()) { + const item = additionalTransferItems.items.get(key); + if (item) { + treeDataTransfer.items.set(key, item); + } + } } } return treeView.onDrop(treeDataTransfer, newParentItemHandle); @@ -404,6 +404,21 @@ class ExtHostTreeView extends Disposable { } } + async onWillDrop(sourceTreeItemHandles: TreeItemHandle[]): Promise { + const extensionTreeItems: T[] = []; + for (const sourceHandle of sourceTreeItemHandles) { + const extensionItem = this.getExtensionElement(sourceHandle); + if (extensionItem) { + extensionTreeItems.push(extensionItem); + } + } + + if (!this.dndController?.onWillDrop || (extensionTreeItems.length === 0)) { + return; + } + return this.dndController.onWillDrop(extensionTreeItems); + } + async onDrop(treeDataTransfer: vscode.TreeDataTransfer, targetHandleOrNode: TreeItemHandle): Promise { const target = this.getExtensionElement(targetHandleOrNode); if (!target) { diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index e84fe69e9a1..c40d64a1475 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -10,7 +10,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { MenuId, IMenuService, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, ViewContainer, ViewContainerLocation, ResolvableTreeItem, ITreeViewDragAndDropController, ITreeDataTransfer, TREE_ITEM_DATA_TRANSFER_TYPE } from 'vs/workbench/common/views'; +import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, ViewContainer, ViewContainerLocation, ResolvableTreeItem, ITreeViewDragAndDropController, ITreeDataTransfer } from 'vs/workbench/common/views'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IThemeService, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -216,7 +216,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this.collapseAllToggleContext = this.collapseAllToggleContextKey.bindTo(contextKeyService); this.refreshContextKey = new RawContextKey(`treeView.${this.id}.enableRefresh`, false, localize('treeView.enableRefresh', "Whether the tree view with id {0} enables refresh.", this.id)); this.refreshContext = this.refreshContextKey.bindTo(contextKeyService); - this.treeViewDnd = this.instantiationService.createInstance(CustomTreeViewDragAndDrop); + this.treeViewDnd = this.instantiationService.createInstance(CustomTreeViewDragAndDrop, this.id); this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.themeService.onDidColorThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); @@ -1215,8 +1215,16 @@ export class TreeView extends AbstractTreeView { } } +const TREE_DRAG_SOURCE_INFO_MIME_TYPE = 'tree/internalsourceinfo'; +interface TreeDragSourceInfo { + id: string, + itemHandles: string[]; +} + export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { - constructor(@ILabelService private readonly labelService: ILabelService) { } + constructor( + private readonly treeId: string, + @ILabelService private readonly labelService: ILabelService) { } private dndController: ITreeViewDragAndDropController | undefined; set controller(controller: ITreeViewDragAndDropController | undefined) { @@ -1225,8 +1233,13 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { if (originalEvent.dataTransfer) { - originalEvent.dataTransfer.setData(TREE_ITEM_DATA_TRANSFER_TYPE, - JSON.stringify((data as ElementsDragAndDropData).getData().map(treeItem => treeItem.handle))); + const treeItemsData = (data as ElementsDragAndDropData).getData(); + const sourceInfo: TreeDragSourceInfo = { + id: this.treeId, + itemHandles: treeItemsData.map(item => item.handle) + }; + originalEvent.dataTransfer.setData(TREE_DRAG_SOURCE_INFO_MIME_TYPE, + JSON.stringify(sourceInfo)); } } @@ -1268,6 +1281,8 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } return previous; }, 0); + + let treeSourceInfo: TreeDragSourceInfo | undefined; await new Promise(resolve => { if (!originalEvent.dataTransfer || !this.dndController || !targetNode) { return; @@ -1276,9 +1291,13 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { if (dataItem.kind === 'string') { const type = dataItem.type; dataItem.getAsString(dataValue => { - treeDataTransfer.items.set(type, { - asString: () => Promise.resolve(dataValue) - }); + if (type === TREE_DRAG_SOURCE_INFO_MIME_TYPE) { + treeSourceInfo = JSON.parse(dataValue); + } else { + treeDataTransfer.items.set(type, { + asString: () => Promise.resolve(dataValue) + }); + } stringCount--; if (stringCount === 0) { resolve(); @@ -1287,6 +1306,6 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } } }); - return this.dndController.onDrop(treeDataTransfer, targetNode); + return this.dndController.onDrop(treeDataTransfer, targetNode, treeSourceInfo?.id, treeSourceInfo?.itemHandles); } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index d837af0438b..7f72eedc88b 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -838,9 +838,8 @@ export interface ITreeViewDataProvider { getChildren(element?: ITreeItem): Promise; } -export const TREE_ITEM_DATA_TRANSFER_TYPE = 'text/treeitems'; export interface ITreeViewDragAndDropController { - onDrop(elements: ITreeDataTransfer, target: ITreeItem): Promise; + onDrop(elements: ITreeDataTransfer, target: ITreeItem, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; } export interface IEditableData { diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts index 70fd7dc8b5a..b2ae05a46a2 100644 --- a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -33,32 +33,28 @@ declare module 'vscode' { export interface TreeDataTransfer { /** * A map containing a mapping of the mime type of the corresponding data. - * The type for tree elements is text/treeitem. - * For example, you can reconstruct the your tree elements: - * ```ts - * JSON.parse(await (items.get('text/treeitem')!.asString())) - * ``` + * Trees that support drag and drop can implement `DragAndDropController.onWillDrop` to add additional mime types + * when the drop occurs on an item in the same tree. */ - items: { get: (mimeType: string) => TreeDataTransferItem | undefined }; + items: { + get: (mimeType: string) => TreeDataTransferItem | undefined + keys: () => IterableIterator; + }; } export interface DragAndDropController extends Disposable { readonly supportedTypes: string[]; /** - * todo@API maybe - * * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, * `onWillDrop` will be called with the dropped tree item. This is the DragAndDropController's opportunity to * package the data from the dropped tree item into whatever format they want the target tree item to receive. * * The returned `TreeDataTransfer` will be merged with the original`TreeDataTransfer` for the operation. * - * Note for implementation later: This means that the `text/treeItem` mime type will go away. - * * @param source */ - // onWillDrop?(source: T): Thenable; + onWillDrop(source: T[]): Thenable; /** * Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed. From 340338b3b3b4d05958895aa73fecfe7391ff8ba5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Nov 2021 15:03:22 +0100 Subject: [PATCH 131/330] #51935 support multi lang specific settings --- .../configuration/common/configuration.ts | 20 +------ .../common/configurationModels.ts | 30 +++++++--- .../common/configurationRegistry.ts | 38 ++++++++++-- .../test/common/configurationModels.test.ts | 60 ++++++++++++++++++- .../api/common/configurationExtensionPoint.ts | 4 +- .../browser/preferencesRenderers.ts | 4 +- 6 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 6160cb333d0..c1fe7191f49 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { Extensions, IConfigurationRegistry, overrideIdentifierFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -149,24 +149,6 @@ export interface IConfigurationCompareResult { overrides: [string, string[]][]; } -export function toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] { - const overrides: IOverrides[] = []; - for (const key of Object.keys(raw)) { - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { - const overrideRaw: any = {}; - for (const keyInOverrideRaw in raw[key]) { - overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw]; - } - overrides.push({ - identifiers: [overrideIdentifierFromKey(key).trim()], - keys: Object.keys(overrideRaw), - contents: toValuesTree(overrideRaw, conflictReporter) - }); - } - } - return overrides; -} - export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any { const root = Object.create(null); diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 0cb02c481bd..4ec58398615 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -13,8 +13,8 @@ import * as objects from 'vs/base/common/objects'; import { IExtUri } from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { addToValueTree, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifierFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { addToValueTree, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toValuesTree } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; import { IFileService } from 'vs/platform/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; import { Workspace } from 'vs/platform/workspace/common/workspace'; @@ -241,7 +241,7 @@ export class DefaultConfigurationModel extends ConfigurationModel { for (const key of Object.keys(contents)) { if (OVERRIDE_PROPERTY_PATTERN.test(key)) { overrides.push({ - identifiers: [overrideIdentifierFromKey(key).trim()], + identifiers: overrideIdentifiersFromKey(key), keys: Object.keys(contents[key]), contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)), }); @@ -360,7 +360,7 @@ export class ConfigurationModelParser { raw = filtered.raw; const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`)); const keys = Object.keys(raw); - const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`)); + const overrides = this.toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`)); return { contents, keys, overrides, restricted: filtered.restricted }; } @@ -392,6 +392,24 @@ export class ConfigurationModelParser { return { raw, restricted }; } + private toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] { + const overrides: IOverrides[] = []; + for (const key of Object.keys(raw)) { + if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + const overrideRaw: any = {}; + for (const keyInOverrideRaw in raw[key]) { + overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw]; + } + overrides.push({ + identifiers: overrideIdentifiersFromKey(key), + keys: Object.keys(overrideRaw), + contents: toValuesTree(overrideRaw, conflictReporter) + }); + } + } + return overrides; + } + } export class UserSettings extends Disposable { @@ -572,8 +590,7 @@ export class Configuration { compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys: string[]): IConfigurationChange { const overrides: [string, string[]][] = []; for (const key of keys) { - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { - const overrideIdentifier = overrideIdentifierFromKey(key); + for (const overrideIdentifier of overrideIdentifiersFromKey(key)) { const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier); const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier); const keys = [ @@ -932,4 +949,3 @@ function compareConfigurationContents(to: { keys: string[], contents: any } | un } return { added, removed, updated }; } - diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 16ae308adf1..5d43dd41a65 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -217,6 +217,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.excludedConfigurationProperties = {}; contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema); + this.registerOverridePropertyPatternKey(); } public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void { @@ -265,7 +266,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0} language.", key), $ref: resourceLanguageSettingsSchemaId }; - overrideIdentifiers.push(overrideIdentifierFromKey(key)); + overrideIdentifiers.push(...overrideIdentifiersFromKey(key)); this.configurationProperties[key] = property; this.defaultLanguageConfigurationOverridesNode.properties![key] = property; } else { @@ -499,6 +500,22 @@ class ConfigurationRegistry implements IConfigurationRegistry { this._onDidSchemaChange.fire(); } + private registerOverridePropertyPatternKey(): void { + const resourceLanguagePropertiesSchema: IJSONSchema = { + type: 'object', + description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."), + errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."), + $ref: resourceLanguageSettingsSchemaId, + }; + allSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + applicationSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + machineSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + windowSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + resourceSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + this._onDidSchemaChange.fire(); + } + private updatePropertyDefaultValue(key: string, property: IConfigurationPropertySchema): void { let defaultValue = this.defaultValues[key]; if (types.isUndefined(defaultValue)) { @@ -511,11 +528,24 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } -const OVERRIDE_PROPERTY = '\\[.*\\]$'; +const OVERRIDE_IDENTIFIER = `\\[([^\\]]+)\\]`; +const OVERRIDE_IDENTIFIER_PATTERN = new RegExp(OVERRIDE_IDENTIFIER, 'g'); +export const OVERRIDE_PROPERTY = `^(${OVERRIDE_IDENTIFIER})+$`; export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY); -export function overrideIdentifierFromKey(key: string): string { - return key.substring(1, key.length - 1); +export function overrideIdentifiersFromKey(key: string): string[] { + const identifiers: string[] = []; + if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + let matches = OVERRIDE_IDENTIFIER_PATTERN.exec(key); + while (matches?.length) { + const identifier = matches[1].trim(); + if (identifier) { + identifiers.push(identifier); + } + matches = OVERRIDE_IDENTIFIER_PATTERN.exec(key); + } + } + return distinct(identifiers); } export function getDefaultValue(type: string | string[] | undefined): any { diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index ab8ae1dc741..406f48b8092 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -12,6 +12,35 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; +suite('ConfigurationModelParser', () => { + + test('parse configuration model with single override identifier', () => { + const testObject = new ConfigurationModelParser(''); + + testObject.parse(JSON.stringify({ '[x]': { 'a': 1 } })); + + assert.deepStrictEqual(JSON.stringify(testObject.configurationModel.overrides), JSON.stringify([{ identifiers: ['x'], keys: ['a'], contents: { 'a': 1 } }])); + }); + + test('parse configuration model with multiple override identifiers', () => { + const testObject = new ConfigurationModelParser(''); + + testObject.parse(JSON.stringify({ '[x][y]': { 'a': 1 } })); + + assert.deepStrictEqual(JSON.stringify(testObject.configurationModel.overrides), JSON.stringify([{ identifiers: ['x', 'y'], keys: ['a'], contents: { 'a': 1 } }])); + }); + + test('parse configuration model with multiple duplicate override identifiers', () => { + const testObject = new ConfigurationModelParser(''); + + testObject.parse(JSON.stringify({ '[x][y][x][z]': { 'a': 1 } })); + + assert.deepStrictEqual(JSON.stringify(testObject.configurationModel.overrides), JSON.stringify([{ identifiers: ['x', 'y', 'z'], keys: ['a'], contents: { 'a': 1 } }])); + }); + + +}); + suite('ConfigurationModel', () => { test('setValue for a key that has no sections and not defined', () => { @@ -621,11 +650,14 @@ suite('ConfigurationChangeEvent', () => { 'files.autoSave': 'off', '[markdown]': { 'editor.wordWrap': 'off' + }, + '[typescript][jsonc]': { + 'editor.lineNumbers': 'off' } })); let testObject = new ConfigurationChangeEvent(change, undefined, configuration); - assert.deepStrictEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', 'editor.wordWrap']); + assert.deepStrictEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', '[typescript][jsonc]', 'editor.wordWrap', 'editor.lineNumbers']); assert.ok(testObject.affectsConfiguration('files')); assert.ok(testObject.affectsConfiguration('files.autoSave')); @@ -637,8 +669,16 @@ suite('ConfigurationChangeEvent', () => { assert.ok(testObject.affectsConfiguration('editor')); assert.ok(testObject.affectsConfiguration('editor.wordWrap')); + assert.ok(testObject.affectsConfiguration('editor.lineNumbers')); assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' })); + assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'jsonc' })); + assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'typescript' })); assert.ok(testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' })); + assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'jsonc' })); + assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'typescript' })); + assert.ok(!testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'markdown' })); + assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'typescript' })); + assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'jsonc' })); assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' })); assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' })); @@ -654,6 +694,10 @@ suite('ConfigurationChangeEvent', () => { 'editor.fontSize': 12, 'editor.wordWrap': 'off' }, + '[css][scss]': { + 'editor.lineNumbers': 'off', + 'css.lint.emptyRules': 'error' + }, 'files.autoSave': 'off', })); const data = configuration.toData(); @@ -663,11 +707,15 @@ suite('ConfigurationChangeEvent', () => { 'editor.fontSize': 13, 'editor.wordWrap': 'off' }, + '[css][scss]': { + 'editor.lineNumbers': 'relative', + 'css.lint.emptyRules': 'error' + }, 'window.zoomLevel': 1, })); let testObject = new ConfigurationChangeEvent(change, { data }, configuration); - assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', 'workbench.editor.enablePreview', 'editor.fontSize']); + assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', '[css][scss]', 'workbench.editor.enablePreview', 'editor.fontSize', 'editor.lineNumbers']); assert.ok(!testObject.affectsConfiguration('files')); @@ -676,10 +724,18 @@ suite('ConfigurationChangeEvent', () => { assert.ok(!testObject.affectsConfiguration('[markdown].editor.fontSize')); assert.ok(!testObject.affectsConfiguration('[markdown].editor.wordWrap')); assert.ok(!testObject.affectsConfiguration('[markdown].workbench')); + assert.ok(testObject.affectsConfiguration('[css][scss]')); assert.ok(testObject.affectsConfiguration('editor')); assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' })); + assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'css' })); + assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'scss' })); assert.ok(testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' })); + assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'css' })); + assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'scss' })); + assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'scss' })); + assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'css' })); + assert.ok(!testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'markdown' })); assert.ok(!testObject.affectsConfiguration('editor.wordWrap')); assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' })); assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' })); diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 5d483b337aa..c0d23a31be9 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN, OVERRIDE_PROPERTY } from 'vs/platform/configuration/common/configurationRegistry'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { isObject } from 'vs/base/common/types'; @@ -110,7 +110,7 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint this.onSettingUpdated(source)); From 7131e48daf1d9da544ffb80df65d5dab0991c088 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 16 Nov 2021 16:14:49 +0100 Subject: [PATCH 132/330] Remove IterableIterator from dnd API proposal Part of #32592 --- src/vs/workbench/api/common/extHostTreeViews.ts | 9 ++++----- src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index f5e63bdc8cc..86ac1db54ea 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -139,12 +139,11 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if ((sourceViewId === destinationViewId) && sourceTreeItemHandles) { const additionalTransferItems = await treeView.onWillDrop(sourceTreeItemHandles); if (additionalTransferItems) { - for (const key of additionalTransferItems.items.keys()) { - const item = additionalTransferItems.items.get(key); - if (item) { - treeDataTransfer.items.set(key, item); + additionalTransferItems.items.forEach((value, key) => { + if (value) { + treeDataTransfer.items.set(key, value); } - } + }); } } return treeView.onDrop(treeDataTransfer, newParentItemHandle); diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts index b2ae05a46a2..293666e1296 100644 --- a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -38,7 +38,7 @@ declare module 'vscode' { */ items: { get: (mimeType: string) => TreeDataTransferItem | undefined - keys: () => IterableIterator; + forEach: (callbackfn: (value: TreeDataTransferItem, key: string) => void) => void; }; } From f6470973e7331e82563311c1f969ebeef0fd9d73 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Nov 2021 17:12:21 +0100 Subject: [PATCH 133/330] #51935 support updating configuration for multiple language identifiers in configuration service --- .../configuration/common/configuration.ts | 18 +++++--- .../common/configurationModels.ts | 12 +++--- .../common/configurationRegistry.ts | 39 +++++++++-------- .../api/common/configurationExtensionPoint.ts | 6 +-- .../api/common/extHostConfiguration.ts | 4 +- .../browser/preferencesRenderers.ts | 8 ++-- .../browser/configurationService.ts | 26 +++++++---- .../common/configurationEditingService.ts | 24 +++++------ .../test/browser/configurationService.test.ts | 43 ++++++++++++++----- .../preferences/browser/preferencesService.ts | 4 +- .../preferences/common/preferencesModels.ts | 6 +-- 11 files changed, 115 insertions(+), 75 deletions(-) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index c1fe7191f49..7716e2b73dd 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -25,6 +25,16 @@ export interface IConfigurationOverrides { resource?: URI | null; } +export function isConfigurationUpdateOverrides(thing: any): thing is IConfigurationUpdateOverrides { + return thing + && typeof thing === 'object' + && (!thing.overrideIdentifiers || types.isArray(thing.overrideIdentifiers)) + && !thing.overrideIdentifier + && (!thing.resource || thing.resource instanceof URI); +} + +export type IConfigurationUpdateOverrides = Omit & { overrideIdentifiers?: string[] | null; }; + export const enum ConfigurationTarget { USER = 1, USER_LOCAL, @@ -106,9 +116,9 @@ export interface IConfigurationService { getValue(section: string, overrides: IConfigurationOverrides): T; updateValue(key: string, value: any): Promise; - updateValue(key: string, value: any, overrides: IConfigurationOverrides): Promise; + updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise; updateValue(key: string, value: any, target: ConfigurationTarget): Promise; - updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise; + updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise; inspect(key: string, overrides?: IConfigurationOverrides): IConfigurationValue>; @@ -269,10 +279,6 @@ export function getDefaultValues(): any { return valueTreeRoot; } -export function keyFromOverrideIdentifier(overrideIdentifier: string): string { - return `[${overrideIdentifier}]`; -} - export function getMigratedSettingValue(configurationService: IConfigurationService, currentSettingName: string, legacySettingName: string): T { const setting = configurationService.inspect(currentSettingName); const legacySetting = configurationService.inspect(legacySettingName); diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 4ec58398615..f41d440c4f9 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -13,8 +13,8 @@ import * as objects from 'vs/base/common/objects'; import { IExtUri } from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { addToValueTree, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toValuesTree } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { addToValueTree, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationUpdateOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toValuesTree } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { IFileService } from 'vs/platform/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; import { Workspace } from 'vs/platform/workspace/common/workspace'; @@ -239,7 +239,7 @@ export class DefaultConfigurationModel extends ConfigurationModel { const keys = getConfigurationKeys(); const overrides: IOverrides[] = []; for (const key of Object.keys(contents)) { - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + if (OVERRIDE_PROPERTY_REGEX.test(key)) { overrides.push({ identifiers: overrideIdentifiersFromKey(key), keys: Object.keys(contents[key]), @@ -371,7 +371,7 @@ export class ConfigurationModelParser { const raw: any = {}; const restricted: string[] = []; for (let key in properties) { - if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) { + if (OVERRIDE_PROPERTY_REGEX.test(key) && filterOverriddenProperties) { const result = this.filter(properties[key], configurationProperties, false, options); raw[key] = result.raw; restricted.push(...result.restricted); @@ -395,7 +395,7 @@ export class ConfigurationModelParser { private toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] { const overrides: IOverrides[] = []; for (const key of Object.keys(raw)) { - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + if (OVERRIDE_PROPERTY_REGEX.test(key)) { const overrideRaw: any = {}; for (const keyInOverrideRaw in raw[key]) { overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw]; @@ -476,7 +476,7 @@ export class Configuration { return consolidateConfigurationModel.getValue(section); } - updateValue(key: string, value: any, overrides: IConfigurationOverrides = {}): void { + updateValue(key: string, value: any, overrides: IConfigurationUpdateOverrides = {}): void { let memoryConfiguration: ConfigurationModel | undefined; if (overrides.resource) { memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource); diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 5d43dd41a65..6dc55505e20 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -258,7 +258,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { for (const key in defaultConfiguration) { properties.push(key); - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + if (OVERRIDE_PROPERTY_REGEX.test(key)) { this.defaultValues[key] = { ...(this.defaultValues[key] || {}), ...defaultConfiguration[key] }; const property: IConfigurationPropertySchema = { type: 'object', @@ -291,7 +291,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { for (const key in defaultConfiguration) { properties.push(key); delete this.defaultValues[key]; - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + if (OVERRIDE_PROPERTY_REGEX.test(key)) { delete this.configurationProperties[key]; delete this.defaultLanguageConfigurationOverridesNode.properties![key]; } else { @@ -371,7 +371,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.updatePropertyDefaultValue(key, property); // update scope - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { + if (OVERRIDE_PROPERTY_REGEX.test(key)) { property.scope = undefined; // No scope for overridable properties `[${identifier}]` } else { property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope; @@ -507,12 +507,12 @@ class ConfigurationRegistry implements IConfigurationRegistry { errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."), $ref: resourceLanguageSettingsSchemaId, }; - allSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; - applicationSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; - machineSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; - machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; - windowSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; - resourceSettings.patternProperties[OVERRIDE_PROPERTY] = resourceLanguagePropertiesSchema; + allSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema; + applicationSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema; + machineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema; + machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema; + windowSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema; + resourceSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema; this._onDidSchemaChange.fire(); } @@ -528,26 +528,30 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } -const OVERRIDE_IDENTIFIER = `\\[([^\\]]+)\\]`; -const OVERRIDE_IDENTIFIER_PATTERN = new RegExp(OVERRIDE_IDENTIFIER, 'g'); -export const OVERRIDE_PROPERTY = `^(${OVERRIDE_IDENTIFIER})+$`; -export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY); +const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`; +const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g'); +export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`; +export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN); export function overrideIdentifiersFromKey(key: string): string[] { const identifiers: string[] = []; - if (OVERRIDE_PROPERTY_PATTERN.test(key)) { - let matches = OVERRIDE_IDENTIFIER_PATTERN.exec(key); + if (OVERRIDE_PROPERTY_REGEX.test(key)) { + let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key); while (matches?.length) { const identifier = matches[1].trim(); if (identifier) { identifiers.push(identifier); } - matches = OVERRIDE_IDENTIFIER_PATTERN.exec(key); + matches = OVERRIDE_IDENTIFIER_REGEX.exec(key); } } return distinct(identifiers); } +export function keyFromOverrideIdentifiers(overrideIdentifiers: string[]): string { + return overrideIdentifiers.reduce((result, overrideIdentifier) => `${result}[${overrideIdentifier}]`, ''); +} + export function getDefaultValue(type: string | string[] | undefined): any { const t = Array.isArray(type) ? (type)[0] : type; switch (t) { @@ -567,7 +571,6 @@ export function getDefaultValue(type: string | string[] | undefined): any { } } - const configurationRegistry = new ConfigurationRegistry(); Registry.add(Extensions.Configuration, configurationRegistry); @@ -575,7 +578,7 @@ export function validateProperty(property: string): string | null { if (!property.trim()) { return nls.localize('config.property.empty', "Cannot register an empty property"); } - if (OVERRIDE_PROPERTY_PATTERN.test(property)) { + if (OVERRIDE_PROPERTY_REGEX.test(property)) { return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property); } if (configurationRegistry.getConfigurationProperties()[property] !== undefined) { diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index c0d23a31be9..5f7b9e0cd93 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN, OVERRIDE_PROPERTY } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { isObject } from 'vs/base/common/types'; @@ -110,7 +110,7 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint { const addedDefaultConfigurations = added.map>(extension => { const defaults: IStringDictionary = objects.deepClone(extension.value); for (const key of Object.keys(defaults)) { - if (!OVERRIDE_PROPERTY_PATTERN.test(key) || typeof defaults[key] !== 'object') { + if (!OVERRIDE_PROPERTY_REGEX.test(key) || typeof defaults[key] !== 'object') { extension.collector.warn(nls.localize('config.property.defaultConfiguration.warning', "Cannot register configuration defaults for '{0}'. Only defaults for language specific settings are supported.", key)); delete defaults[key]; } diff --git a/src/vs/workbench/api/common/extHostConfiguration.ts b/src/vs/workbench/api/common/extHostConfiguration.ts index 5c90db67dbf..e8033334ac6 100644 --- a/src/vs/workbench/api/common/extHostConfiguration.ts +++ b/src/vs/workbench/api/common/extHostConfiguration.ts @@ -11,7 +11,7 @@ import { ExtHostConfigurationShape, MainThreadConfigurationShape, IConfiguration import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes'; import { ConfigurationTarget, IConfigurationChange, IConfigurationData, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; -import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { isObject } from 'vs/base/common/types'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Barrier } from 'vs/base/common/async'; @@ -297,7 +297,7 @@ export class ExtHostConfigProvider { } private _validateConfigurationAccess(key: string, overrides?: IConfigurationOverrides, extensionId?: ExtensionIdentifier): void { - const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes.get(key); + const scope = OVERRIDE_PROPERTY_REGEX.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes.get(key); const extensionIdText = extensionId ? `[${extensionId.value}] ` : ''; if (ConfigurationScope.RESOURCE === scope) { if (typeof overrides?.resource === 'undefined') { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index ccacfefc8b1..7374a24b8a5 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -24,7 +24,7 @@ import * as modes from 'vs/editor/common/modes'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMarkerData, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; @@ -76,9 +76,9 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend } updatePreference(key: string, value: any, source: IIndexedSetting): void { - const overrideIdentifier = source.overrideOf ? source.overrideOf.key.substring(1, source.overrideOf.key.length - 1) : null; + const overrideIdentifiers = source.overrideOf ? overrideIdentifiersFromKey(source.overrideOf.key) : null; const resource = this.preferencesModel.uri; - this.configurationService.updateValue(key, value, { overrideIdentifier, resource }, this.preferencesModel.configurationTarget) + this.configurationService.updateValue(key, value, { overrideIdentifiers, resource }, this.preferencesModel.configurationTarget) .then(() => this.onSettingUpdated(source)); } @@ -533,7 +533,7 @@ class UnsupportedSettingsRenderer extends Disposable implements modes.CodeAction this.handleWorkspaceFolderConfiguration(setting, configuration, markerData); break; } - } else if (!OVERRIDE_PROPERTY_PATTERN.test(setting.key)) { // Ignore override settings (language specific settings) + } else if (!OVERRIDE_PROPERTY_REGEX.test(setting.key)) { // Ignore override settings (language specific settings) markerData.push({ severity: MarkerSeverity.Hint, tags: [MarkerTag.Unnecessary], diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index f24e3a93f19..5c0ae2f19c6 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -12,11 +12,11 @@ import { Queue, Barrier, runWhenIdle, Promises } from 'vs/base/common/async'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent } from 'vs/platform/workspace/common/workspace'; import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels'; -import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides } from 'vs/platform/configuration/common/configuration'; import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceIdentifier, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditingService, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService'; @@ -305,18 +305,26 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat } updateValue(key: string, value: any): Promise; - updateValue(key: string, value: any, overrides: IConfigurationOverrides): Promise; + updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise; updateValue(key: string, value: any, target: ConfigurationTarget): Promise; - updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): Promise; - updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError: boolean): Promise; + updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise; async updateValue(key: string, value: any, arg3?: any, arg4?: any, donotNotifyError?: any): Promise { await this.cyclicDependency; - const overrides = isConfigurationOverrides(arg3) ? arg3 : undefined; + const overrides: IConfigurationUpdateOverrides | undefined = isConfigurationUpdateOverrides(arg3) ? arg3 + : isConfigurationOverrides(arg3) ? { resource: arg3.resource, overrideIdentifiers: arg3.overrideIdentifier ? [arg3.overrideIdentifier] : undefined } : undefined; const target: ConfigurationTarget | undefined = overrides ? arg4 : arg3; const targets: ConfigurationTarget[] = target ? [target] : []; + if (overrides?.overrideIdentifiers) { + overrides.overrideIdentifiers = distinct(overrides.overrideIdentifiers); + overrides.overrideIdentifiers = overrides.overrideIdentifiers.length ? overrides.overrideIdentifiers : undefined; + } + if (!targets.length) { - const inspect = this.inspect(key, overrides); + if (overrides?.overrideIdentifiers && overrides.overrideIdentifiers.length > 1) { + throw new Error('Configuration Target is required while updating the value for multiple override identifiers'); + } + const inspect = this.inspect(key, { resource: overrides?.resource, overrideIdentifier: overrides?.overrideIdentifiers ? overrides.overrideIdentifiers[0] : undefined }); targets.push(...this.deriveConfigurationTargets(key, value, inspect)); // Remove the setting, if the value is same as default value and is updated only in user target @@ -856,7 +864,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat return validWorkspaceFolders; } - private async writeConfigurationValue(key: string, value: any, target: ConfigurationTarget, overrides: IConfigurationOverrides | undefined, donotNotifyError: boolean): Promise { + private async writeConfigurationValue(key: string, value: any, target: ConfigurationTarget, overrides: IConfigurationUpdateOverrides | undefined, donotNotifyError: boolean): Promise { if (target === ConfigurationTarget.DEFAULT) { throw new Error('Invalid configuration target'); } @@ -864,7 +872,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat if (target === ConfigurationTarget.MEMORY) { const previous = { data: this._configuration.toData(), workspace: this.workspace }; this._configuration.updateValue(key, value, overrides); - this.triggerConfigurationChange({ keys: overrides?.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier), key] : [key], overrides: overrides?.overrideIdentifier ? [[overrides?.overrideIdentifier, [key]]] : [] }, previous, target); + this.triggerConfigurationChange({ keys: overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key], overrides: overrides?.overrideIdentifiers?.length ? overrides.overrideIdentifiers.map(overrideIdentifier => ([overrideIdentifier, [key]])) : [] }, previous, target); return; } diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index bfaf5ecd7d6..c18ee164b64 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -13,11 +13,11 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IConfigurationService, IConfigurationOverrides, keyFromOverrideIdentifier } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationUpdateOverrides } from 'vs/platform/configuration/common/configuration'; import { FOLDER_SETTINGS_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY, USER_STANDALONE_CONFIGURATIONS, TASKS_DEFAULT, FOLDER_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; -import { OVERRIDE_PROPERTY_PATTERN, IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IOpenSettingsOptions, IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; @@ -113,7 +113,7 @@ export interface IConfigurationEditingOptions { /** * Scope of configuration to be written into. */ - scopes?: IConfigurationOverrides; + scopes?: IConfigurationUpdateOverrides; } export const enum EditableConfigurationTarget { @@ -243,7 +243,7 @@ export class ConfigurationEditingService { return { insertSpaces, tabSize, eol }; } - private async onError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationOverrides | undefined): Promise { + private async onError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationUpdateOverrides | undefined): Promise { switch (error.code) { case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: this.onInvalidConfigurationError(error, operation); @@ -279,7 +279,7 @@ export class ConfigurationEditingService { } } - private onConfigurationFileDirtyError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationOverrides | undefined): void { + private onConfigurationFileDirtyError(error: ConfigurationEditingError, operation: IConfigurationEditOperation, scopes: IConfigurationUpdateOverrides | undefined): void { const openStandAloneConfigurationActionLabel = operation.workspaceStandAloneConfigurationKey === TASKS_CONFIGURATION_KEY ? nls.localize('openTasksConfiguration', "Open Tasks Configuration") : operation.workspaceStandAloneConfigurationKey === LAUNCH_CONFIGURATION_KEY ? nls.localize('openLaunchConfiguration', "Open Launch Configuration") : null; @@ -475,7 +475,7 @@ export class ConfigurationEditingService { return parseErrors.length > 0; } - private async validate(target: EditableConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationOverrides): Promise { + private async validate(target: EditableConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationUpdateOverrides): Promise { const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); const configurationScope = configurationProperties[operation.key]?.scope; @@ -488,7 +488,7 @@ export class ConfigurationEditingService { */ if (!operation.workspaceStandAloneConfigurationKey) { const validKeys = this.configurationService.keys().default; - if (validKeys.indexOf(operation.key) < 0 && !OVERRIDE_PROPERTY_PATTERN.test(operation.key) && operation.value !== undefined) { + if (validKeys.indexOf(operation.key) < 0 && !OVERRIDE_PROPERTY_REGEX.test(operation.key) && operation.value !== undefined) { throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY, target, operation); } } @@ -506,7 +506,7 @@ export class ConfigurationEditingService { } if (target === EditableConfigurationTarget.WORKSPACE) { - if (!operation.workspaceStandAloneConfigurationKey && !OVERRIDE_PROPERTY_PATTERN.test(operation.key)) { + if (!operation.workspaceStandAloneConfigurationKey && !OVERRIDE_PROPERTY_REGEX.test(operation.key)) { if (configurationScope === ConfigurationScope.APPLICATION) { throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION, target, operation); } @@ -521,14 +521,14 @@ export class ConfigurationEditingService { throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation); } - if (!operation.workspaceStandAloneConfigurationKey && !OVERRIDE_PROPERTY_PATTERN.test(operation.key)) { + if (!operation.workspaceStandAloneConfigurationKey && !OVERRIDE_PROPERTY_REGEX.test(operation.key)) { if (configurationScope !== undefined && !FOLDER_SCOPES.includes(configurationScope)) { throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_CONFIGURATION, target, operation); } } } - if (overrides.overrideIdentifier) { + if (overrides.overrideIdentifiers?.length) { if (configurationScope !== ConfigurationScope.LANGUAGE_OVERRIDABLE) { throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_INVALID_RESOURCE_LANGUAGE_CONFIGURATION, target, operation); } @@ -544,7 +544,7 @@ export class ConfigurationEditingService { } - private getConfigurationEditOperation(target: EditableConfigurationTarget, config: IConfigurationValue, overrides: IConfigurationOverrides): IConfigurationEditOperation { + private getConfigurationEditOperation(target: EditableConfigurationTarget, config: IConfigurationValue, overrides: IConfigurationUpdateOverrides): IConfigurationEditOperation { // Check for standalone workspace configurations if (config.key) { @@ -569,7 +569,7 @@ export class ConfigurationEditingService { } let key = config.key; - let jsonPath = overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier), key] : [key]; + let jsonPath = overrides.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key]; if (target === EditableConfigurationTarget.USER_LOCAL || target === EditableConfigurationTarget.USER_REMOTE) { return { key, jsonPath, value: config.value, resource: withNullAsUndefined(this.getConfigurationFileResource(target, '', null)), target }; } diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 6ea7e0ccaec..17f9dfb4c51 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -8,7 +8,7 @@ import * as sinon from 'sinon'; import { URI } from 'vs/base/common/uri'; import { Registry } from 'vs/platform/registry/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, keyFromOverrideIdentifiers } from 'vs/platform/configuration/common/configurationRegistry'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditingService'; @@ -1012,9 +1012,38 @@ suite('WorkspaceConfigurationService - Folder', () => { .then(() => assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'value')); }); - test('update resource language configuration', () => { - return testObject.updateValue('configurationService.folder.languageSetting', 'value', { resource: workspaceService.getWorkspace().folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER) - .then(() => assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting'), 'value')); + test('update language configuration using configuration overrides', async () => { + await testObject.updateValue('configurationService.folder.languageSetting', 'abcLangValue', { overrideIdentifier: 'abclang' }); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'abclang' }), 'abcLangValue'); + }); + + test('update language configuration using configuration update overrides', async () => { + await testObject.updateValue('configurationService.folder.languageSetting', 'abcLangValue', { overrideIdentifiers: ['abclang'] }); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'abclang' }), 'abcLangValue'); + }); + + test('update language configuration for multiple languages', async () => { + await testObject.updateValue('configurationService.folder.languageSetting', 'multiLangValue', { overrideIdentifiers: ['deflang', 'xyzlang'] }, ConfigurationTarget.USER); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'deflang' }), 'multiLangValue'); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { overrideIdentifier: 'xyzlang' }), 'multiLangValue'); + assert.deepStrictEqual(testObject.getValue(keyFromOverrideIdentifiers(['deflang', 'xyzlang'])), { 'configurationService.folder.languageSetting': 'multiLangValue' }); + }); + + test('update resource language configuration', async () => { + await testObject.updateValue('configurationService.folder.languageSetting', 'value', { resource: workspaceService.getWorkspace().folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting'), 'value'); + }); + + test('update resource language configuration for a language using configuration overrides', async () => { + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValue'); + await testObject.updateValue('configurationService.folder.languageSetting', 'languageValueUpdated', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }, ConfigurationTarget.WORKSPACE_FOLDER); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValueUpdated'); + }); + + test('update resource language configuration for a language using configuration update overrides', async () => { + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValue'); + await testObject.updateValue('configurationService.folder.languageSetting', 'languageValueUpdated', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifiers: ['jsonc'] }, ConfigurationTarget.WORKSPACE_FOLDER); + assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValueUpdated'); }); test('update application setting into workspace configuration in a workspace is not supported', () => { @@ -1058,12 +1087,6 @@ suite('WorkspaceConfigurationService - Folder', () => { .then(() => assert.ok(target.called)); }); - test('resource language configuration', async () => { - assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValue'); - await testObject.updateValue('configurationService.folder.languageSetting', 'languageValueUpdated', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }, ConfigurationTarget.WORKSPACE_FOLDER); - assert.strictEqual(testObject.getValue('configurationService.folder.languageSetting', { resource: workspaceService.getWorkspace().folders[0].uri, overrideIdentifier: 'jsonc' }), 'languageValueUpdated'); - }); - test('remove setting from all targets', async () => { const key = 'configurationService.folder.testSetting'; await testObject.updateValue(key, 'workspaceValue', ConfigurationTarget.WORKSPACE); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 96370d9ee74..7800cc1c21f 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -19,7 +19,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Extensions, getDefaultValue, IConfigurationRegistry, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { Extensions, getDefaultValue, IConfigurationRegistry, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { EditorResolution } from 'vs/platform/editor/common/editor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; @@ -541,7 +541,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } const schema = Registry.as(Extensions.Configuration).getConfigurationProperties()[settingKey]; - const isOverrideProperty = OVERRIDE_PROPERTY_PATTERN.test(settingKey); + const isOverrideProperty = OVERRIDE_PROPERTY_REGEX.test(settingKey); if (!schema && !isOverrideProperty) { return null; } diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 6b5f95fde2c..2993223a628 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -15,7 +15,7 @@ import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/mod import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPropertySchema, IConfigurationRegistry, OVERRIDE_PROPERTY_PATTERN, IConfigurationExtensionInfo } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPropertySchema, IConfigurationRegistry, IConfigurationExtensionInfo, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; @@ -342,7 +342,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, }; if (previousParents.length === settingsPropertyIndex + 1) { settings.push(setting); - if (OVERRIDE_PROPERTY_PATTERN.test(name)) { + if (OVERRIDE_PROPERTY_REGEX.test(name)) { overrideSetting = setting; } } else { @@ -618,7 +618,7 @@ export class DefaultSettings extends Disposable { description = ''; } const descriptionLines = description.split('\n'); - const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; + const overrides = OVERRIDE_PROPERTY_REGEX.test(key) ? this.parseOverrideSettings(prop.default) : []; let listItemType: string | undefined; if (prop.type === 'array' && prop.items && !isArray(prop.items) && prop.items.type) { if (prop.items.enum) { From 68519fc4440cbb8deae954a5070928cb69f43682 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 16 Nov 2021 08:39:29 -0800 Subject: [PATCH 134/330] Fix #137085 --- .../themes/solarized-light-color-theme.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index dfa65c9411a..cfb65d06c78 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -355,6 +355,7 @@ // "scrollbarSlider.hoverBackground": "", // Editor "editor.background": "#FDF6E3", + "notebook.cellEditorBackground": "#F7F0E0", // "editor.foreground": "#6688cc", "editorWidget.background": "#EEE8D5", "editorCursor.foreground": "#657B83", From cd66eec870313c3a4cccc5932a5d9def6b385cb6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Nov 2021 13:58:48 -0800 Subject: [PATCH 135/330] Only serialize terminal sessions if data is written Fixes #135811 --- src/vs/platform/terminal/node/ptyService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 8ddc2331a2c..1273db4f215 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -100,7 +100,8 @@ export class PtyService extends Disposable implements IPtyService { async serializeTerminalState(ids: number[]): Promise { const promises: Promise[] = []; for (const [persistentProcessId, persistentProcess] of this._ptys.entries()) { - if (ids.indexOf(persistentProcessId) !== -1) { + // Only serialize persistent processes that have had data written or performed a replay + if (persistentProcess.hasWrittenData && ids.indexOf(persistentProcessId) !== -1) { promises.push(Promises.withAsyncBody(async r => { r({ id: persistentProcessId, @@ -402,6 +403,7 @@ export class PersistentTerminalProcess extends Disposable { private readonly _pendingCommands = new Map void; reject: (err: any) => void; }>(); private _isStarted: boolean = false; + private _hasWrittenData: boolean = false; private _orphanQuestionBarrier: AutoOpenBarrier | null; private _orphanQuestionReplyTime: number; @@ -432,6 +434,7 @@ export class PersistentTerminalProcess extends Disposable { get pid(): number { return this._pid; } get shellLaunchConfig(): IShellLaunchConfig { return this._terminalProcess.shellLaunchConfig; } + get hasWrittenData(): boolean { return this._hasWrittenData; } get title(): string { return this._title || this._terminalProcess.currentTitle; } get titleSource(): TitleEventSource { return this._titleSource; } get icon(): TerminalIcon | undefined { return this._icon; } @@ -571,6 +574,7 @@ export class PersistentTerminalProcess extends Disposable { return this._terminalProcess.shutdown(immediate); } input(data: string): void { + this._hasWrittenData = true; if (this._inReplay) { return; } @@ -611,6 +615,7 @@ export class PersistentTerminalProcess extends Disposable { } async triggerReplay(): Promise { + this._hasWrittenData = true; const ev = await this._serializer.generateReplayEvent(); let dataLength = 0; for (const e of ev.events) { From 7cb71e89b256d0c9525c24e72c1fb679b4b54004 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Nov 2021 14:25:21 -0800 Subject: [PATCH 136/330] Add unit tests for convertProfileToShellLaunchConfig Part of #136060 --- .../terminal/browser/terminalService.ts | 71 +++++++------- .../test/browser/terminalService.test.ts | 98 +++++++++++++++++++ 2 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index d3a06a22d08..862df6fdf4a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -48,10 +48,10 @@ export class TerminalService implements ITerminalService { private _hostActiveTerminals: Map = new Map(); - private _isShuttingDown: boolean; + private _isShuttingDown: boolean = false; private _backgroundedTerminalInstances: ITerminalInstance[] = []; private _backgroundedTerminalDisposables: Map = new Map(); - private _findState: FindReplaceState; + private _findState: FindReplaceState = new FindReplaceState(); private _linkProviders: Set = new Set(); private _linkProviderDisposables: Map = new Map(); private _processSupportContextKey: IContextKey; @@ -150,9 +150,7 @@ export class TerminalService implements ITerminalService { @IExtensionService private readonly _extensionService: IExtensionService, @INotificationService private readonly _notificationService: INotificationService ) { - this._isShuttingDown = false; - this._findState = new FindReplaceState(); - this._configHelper = _instantiationService.createInstance(TerminalConfigHelper); + this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper); // the below avoids having to poll routinely. // we update detected profiles when an instance is created so that, // for example, we detect if you've installed a pwsh @@ -161,7 +159,7 @@ export class TerminalService implements ITerminalService { this._forwardInstanceHostEvents(this._terminalGroupService); this._forwardInstanceHostEvents(this._terminalEditorService); this._terminalGroupService.onDidChangeActiveGroup(this._onDidChangeActiveGroup.fire, this._onDidChangeActiveGroup); - _terminalInstanceService.onDidCreateInstance(instance => { + this._terminalInstanceService.onDidCreateInstance(instance => { this._initInstanceListeners(instance); this._onDidCreateInstance.fire(instance); }); @@ -189,6 +187,7 @@ export class TerminalService implements ITerminalService { // Create async as the class depends on `this` timeout(0).then(() => this._instantiationService.createInstance(TerminalEditorStyle, document.head)); } + async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise { const quickPick = this._instantiationService.createInstance(TerminalProfileQuickpick); const result = await quickPick.showAndGetResult(type); @@ -864,35 +863,6 @@ export class TerminalService implements ITerminalService { return instance?.target === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService; } - private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { - if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { - const profile = shellLaunchConfigOrProfile; - if (!profile.path) { - return shellLaunchConfigOrProfile; - } - return { - executable: profile.path, - args: profile.args, - env: profile.env, - icon: profile.icon, - color: profile.color, - name: profile.overrideName ? profile.profileName : undefined, - cwd - }; - } - - // A shell launch config was provided - if (shellLaunchConfigOrProfile) { - if (cwd) { - shellLaunchConfigOrProfile.cwd = cwd; - } - return shellLaunchConfigOrProfile; - } - - // Return empty shell launch config - return {}; - } - async createTerminal(options?: ICreateTerminalOptions): Promise { // Await the initialization of available profiles as long as this is not a pty terminal or a // local terminal in a remote workspace as profile won't be used in those cases and these @@ -906,7 +876,7 @@ export class TerminalService implements ITerminalService { } const config = options?.config || this._terminalProfileService.availableProfiles?.find(p => p.profileName === this._terminalProfileService.getDefaultProfileName()); - const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : this._convertProfileToShellLaunchConfig(config || {}); + const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : convertProfileToShellLaunchConfig(config || {}); // Get the contributed profile if it was provided let contributedProfile = config && 'extensionIdentifier' in config ? config : undefined; @@ -1079,6 +1049,35 @@ export class TerminalService implements ITerminalService { } } +export function convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { + if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { + const profile = shellLaunchConfigOrProfile; + if (!profile.path) { + return shellLaunchConfigOrProfile; + } + return { + executable: profile.path, + args: profile.args, + env: profile.env, + icon: profile.icon, + color: profile.color, + name: profile.overrideName ? profile.profileName : undefined, + cwd + }; + } + + // A shell launch config was provided + if (shellLaunchConfigOrProfile) { + if (cwd) { + shellLaunchConfigOrProfile.cwd = cwd; + } + return shellLaunchConfigOrProfile; + } + + // Return empty shell launch config + return {}; +} + class TerminalEditorStyle extends Themable { private _styleElement: HTMLElement; diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts new file mode 100644 index 00000000000..582663a2dcb --- /dev/null +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { deepStrictEqual } from 'assert'; +import { ITerminalProfile } from 'vs/platform/terminal/common/terminal'; +import { convertProfileToShellLaunchConfig } from 'vs/workbench/contrib/terminal/browser/terminalService'; +import { URI } from 'vs/base/common/uri'; + +suite('Workbench - TerminalService', () => { + suite('convertProfileToShellLaunchConfig', () => { + test('should return an empty shell launch config when undefined is provided', () => { + deepStrictEqual(convertProfileToShellLaunchConfig(), {}); + deepStrictEqual(convertProfileToShellLaunchConfig(undefined), {}); + }); + test('should return the same shell launch config when provided', () => { + deepStrictEqual( + convertProfileToShellLaunchConfig({}), + {} + ); + deepStrictEqual( + convertProfileToShellLaunchConfig({ executable: '/foo' }), + { executable: '/foo' } + ); + deepStrictEqual( + convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar', args: ['a', 'b'] }), + { executable: '/foo', cwd: '/bar', args: ['a', 'b'] } + ); + deepStrictEqual( + convertProfileToShellLaunchConfig({ executable: '/foo' }, '/bar'), + { executable: '/foo', cwd: '/bar' } + ); + deepStrictEqual( + convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar' }, '/baz'), + { executable: '/foo', cwd: '/baz' } + ); + }); + test('should convert a provided profile to a shell launch config', () => { + deepStrictEqual( + convertProfileToShellLaunchConfig({ + profileName: 'abc', + path: '/foo', + isDefault: true + }), + { + args: undefined, + color: undefined, + cwd: undefined, + env: undefined, + executable: '/foo', + icon: undefined, + name: undefined + } + ); + const icon = URI.file('/icon'); + deepStrictEqual( + convertProfileToShellLaunchConfig({ + profileName: 'abc', + path: '/foo', + isDefault: true, + args: ['a', 'b'], + color: 'color', + env: { test: 'TEST' }, + icon + } as ITerminalProfile, '/bar'), + { + args: ['a', 'b'], + color: 'color', + cwd: '/bar', + env: { test: 'TEST' }, + executable: '/foo', + icon, + name: undefined + } + ); + }); + test('should respect overrideName in profile', () => { + deepStrictEqual( + convertProfileToShellLaunchConfig({ + profileName: 'abc', + path: '/foo', + isDefault: true, + overrideName: true + }), + { + args: undefined, + color: undefined, + cwd: undefined, + env: undefined, + executable: '/foo', + icon: undefined, + name: 'abc' + } + ); + }); + }); +}); From 5109e2e12a67e763995d8e62847916be7fd0ed69 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Nov 2021 18:01:02 +0100 Subject: [PATCH 137/330] Fix #136424 --- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- src/vscode-dts/vscode.proposed.resolvers.d.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 4d412233d10..4e902bf8333 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -699,7 +699,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme const options: ResolvedOptions = { extensionHostEnv: result.extensionHostEnv, isTrusted: result.isTrusted, - authenticationSession: result.authenticationSession ? { id: result.authenticationSession.id, providerId: result.authenticationSession.providerId } : undefined + authenticationSession: result.authenticationSessionForInitializingExtensions ? { id: result.authenticationSessionForInitializingExtensions.id, providerId: result.authenticationSessionForInitializingExtensions.providerId } : undefined }; return { diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts index 8936603d970..211c201028d 100644 --- a/src/vscode-dts/vscode.proposed.resolvers.d.ts +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -32,11 +32,9 @@ declare module 'vscode' { isTrusted?: boolean; /** - * When provided, remote server will be initialized with the data synced using given user account. - * **Note:** Initialization happens only when VSCode is opened in Desktop and only extensions are initialized as of now. + * When provided, remote server will be initialized with the extensions synced using the given user account. */ - // authenticationSession(ForInitialization|ForInitializingExtensions)? - authenticationSession?: AuthenticationSession & { providerId: string }; + authenticationSessionForInitializingExtensions?: AuthenticationSession & { providerId: string }; } export interface TunnelPrivacy { From 297c52bd80d8dbd18c1bf1900fee25aa8b7a5044 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 16 Nov 2021 09:41:27 -0800 Subject: [PATCH 138/330] finalize forceNewSession API --- .../workbench/api/common/extHost.api.impl.ts | 3 -- src/vscode-dts/vscode.d.ts | 24 +++++++++++++++ .../vscode.proposed.authSession.d.ts | 29 +------------------ 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1d7ab23720b..43d43d5bce1 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -227,9 +227,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const authentication: typeof vscode.authentication = { getSession(providerId: string, scopes: readonly string[], options?: vscode.AuthenticationGetSessionOptions) { - if (options?.forceNewSession) { - checkProposedApiEnabled(extension, 'authSession'); - } return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, // TODO: remove this after GHPR and Codespaces move off of it diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 4007777baa7..ff186b0103c 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -13919,6 +13919,15 @@ declare module 'vscode' { */ 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. + * + * Defaults to false. + */ + forceNewSession?: boolean | { detail: string }; /** * Whether we should show the indication to sign in in the Accounts menu. @@ -14070,6 +14079,21 @@ declare module 'vscode' { */ export function getSession(providerId: string, scopes: readonly string[], options?: AuthenticationGetSessionOptions): 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; + /** * An {@link Event} which fires when the authentication sessions of an authentication provider have * been added, removed, or changed. diff --git a/src/vscode-dts/vscode.proposed.authSession.d.ts b/src/vscode-dts/vscode.proposed.authSession.d.ts index a3e593d4224..5287a5e97c6 100644 --- a/src/vscode-dts/vscode.proposed.authSession.d.ts +++ b/src/vscode-dts/vscode.proposed.authSession.d.ts @@ -4,37 +4,10 @@ *--------------------------------------------------------------------------------------------*/ declare module 'vscode' { - - /** - * More options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. - */ - export interface AuthenticationGetSessionOptions { - /** - * 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. - * - * Defaults to false. - */ - forceNewSession?: boolean | { detail: string }; - } - 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 + * @deprecated Use {@link getSession()} {@link AuthenticationGetSessionOptions.silent} instead. */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | { detail: string } }): Thenable; export function hasSession(providerId: string, scopes: readonly string[]): Thenable; } } From f1051d94c0e2564a04df9137916925d486e71407 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 16 Nov 2021 11:06:11 -0800 Subject: [PATCH 139/330] Fix ToC category and extension names sorting Fixes #137257 --- .../contrib/preferences/browser/settingsTree.ts | 14 ++++++++------ .../services/preferences/common/preferences.ts | 2 +- .../preferences/common/preferencesModels.ts | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 88f090a63ac..f1553b7ac11 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -435,27 +435,26 @@ export async function resolveExtensionsSettings(extensionService: IExtensionServ addEntryToTree(extensionId, extensionName, childEntry); }; - const processPromises = groups - .sort((a, b) => a.title.localeCompare(b.title)) - .map(g => processGroupEntry(g)); - + const processPromises = groups.map(g => processGroupEntry(g)); return Promise.all(processPromises).then(() => { const extGroups: ITOCEntry[] = []; for (const value of extGroupTree.values()) { for (const child of value.children!) { - // Sort the individual settings + // Sort the individual settings of the child. child.settings?.sort((a, b) => { return compareNullableIntegers(a.order, b.order); }); } + if (value.children!.length === 1) { - // Push a flattened setting + // Push a flattened setting. extGroups.push({ id: value.id, label: value.children![0].label, settings: value.children![0].settings }); } else { + // Sort the categories. value.children!.sort((a, b) => { return compareNullableIntegers(a.order, b.order); }); @@ -463,6 +462,9 @@ export async function resolveExtensionsSettings(extensionService: IExtensionServ } } + // Sort the outermost settings. + extGroups.sort((a, b) => a.label.localeCompare(b.label)); + return { id: 'extensions', label: localize('extensions', "Extensions"), diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index bad2c3faee7..a9026ea6131 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -42,8 +42,8 @@ export interface ISettingsGroup { range: IRange; title: string; titleRange: IRange; - order: number; sections: ISettingsSection[]; + order?: number; extensionInfo?: IConfigurationExtensionInfo; } diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 2993223a628..c4f77c2470a 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -567,7 +567,7 @@ export class DefaultSettings extends Disposable { if (!settingsGroup) { settingsGroup = result.find(g => g.title === title && g.extensionInfo?.id === config.extensionInfo?.id); if (!settingsGroup) { - settingsGroup = { sections: [{ settings: [] }], id: config.id || '', title: title || '', titleRange: nullRange, order: config.order ?? 0, range: nullRange, extensionInfo: config.extensionInfo }; + settingsGroup = { sections: [{ settings: [] }], id: config.id || '', title: title || '', titleRange: nullRange, order: config.order, range: nullRange, extensionInfo: config.extensionInfo }; result.push(settingsGroup); } } else { @@ -576,7 +576,7 @@ export class DefaultSettings extends Disposable { } if (config.properties) { if (!settingsGroup) { - settingsGroup = { sections: [{ settings: [] }], id: config.id || '', title: config.id || '', titleRange: nullRange, order: config.order ?? 0, range: nullRange, extensionInfo: config.extensionInfo }; + settingsGroup = { sections: [{ settings: [] }], id: config.id || '', title: config.id || '', titleRange: nullRange, order: config.order, range: nullRange, extensionInfo: config.extensionInfo }; result.push(settingsGroup); } const configurationSettings: ISetting[] = []; From 059cb6f087803e531fc05967195ccd8c4f30d4af Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 16 Nov 2021 12:34:45 -0800 Subject: [PATCH 140/330] Do not show the selected or suggested kernel unless just one (#137320) --- .../notebook/browser/contrib/editorStatusBar/editorStatusBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index 77554cd2187..fdfbd20d35e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -347,7 +347,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { this._kernelInfoElement.clear(); let { selected, suggestions, all } = this._notebookKernelService.getMatchingKernel(notebook); - const suggested = suggestions[0]; + const suggested = suggestions.length === 1 ? suggestions[0] : undefined; let isSuggested = false; if (all.length === 0) { From 3eb535c4abc8231a8543aba90a8ae5f4a1a0ee65 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 16 Nov 2021 12:41:46 -0800 Subject: [PATCH 141/330] Add support for ungrouped settings in the ToC Fixes #137259 --- .../preferences/browser/settingsTree.ts | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index f1553b7ac11..475c52528f2 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -438,27 +438,44 @@ export async function resolveExtensionsSettings(extensionService: IExtensionServ const processPromises = groups.map(g => processGroupEntry(g)); return Promise.all(processPromises).then(() => { const extGroups: ITOCEntry[] = []; - for (const value of extGroupTree.values()) { - for (const child of value.children!) { + for (const extensionRootEntry of extGroupTree.values()) { + for (const child of extensionRootEntry.children!) { // Sort the individual settings of the child. child.settings?.sort((a, b) => { return compareNullableIntegers(a.order, b.order); }); } - if (value.children!.length === 1) { + if (extensionRootEntry.children!.length === 1) { + // There is a single category for this extension. // Push a flattened setting. extGroups.push({ - id: value.id, - label: value.children![0].label, - settings: value.children![0].settings + id: extensionRootEntry.id, + label: extensionRootEntry.children![0].label, + settings: extensionRootEntry.children![0].settings }); } else { // Sort the categories. - value.children!.sort((a, b) => { + extensionRootEntry.children!.sort((a, b) => { return compareNullableIntegers(a.order, b.order); }); - extGroups.push(value); + + // If there is a category that matches the setting name, + // add the settings in manually as "ungrouped" settings. + // https://github.com/microsoft/vscode/issues/137259 + const ungroupedChild = extensionRootEntry.children!.find(child => child.label === extensionRootEntry.label); + if (ungroupedChild && !ungroupedChild.children) { + const groupedChildren = extensionRootEntry.children!.filter(child => child !== ungroupedChild); + extGroups.push({ + id: extensionRootEntry.id, + label: extensionRootEntry.label, + settings: ungroupedChild.settings, + children: groupedChildren + }); + } else { + // Push all the groups as-is. + extGroups.push(extensionRootEntry); + } } } From db217ba0de90692f82d823dd41353ac0a24fc4e6 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 16 Nov 2021 12:55:36 -0800 Subject: [PATCH 142/330] Add enable-render-process-reuse flag (#120952) This PR adds an opt-in enable-render-process-reuse flag so that users can self-host on Insiders using this flag while VS Code is still on Electron 13. After moving to Electron 15, the app.allowRendererProcessReuse property cannot be changed at all (it'll default to true), and the flag will become irrelevant. --- src/main.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main.js b/src/main.js index ad838aa245e..4f3e6ef6930 100644 --- a/src/main.js +++ b/src/main.js @@ -25,8 +25,6 @@ const { getUserDataPath } = require('./vs/platform/environment/node/userDataPath const product = require('../product.json'); const { app, protocol, crashReporter } = require('electron'); -// Disable render process reuse, we still have -// non-context aware native modules in the renderer. app.allowRendererProcessReuse = false; // Enable portable support @@ -175,7 +173,10 @@ function configureCommandlineSwitchesSync(cliArgs) { 'enable-proposed-api', // Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. - 'log-level' + 'log-level', + + // Enables render process reuse. Default value is 'false'. See https://github.com/electron/electron/issues/18397 + 'enable-render-process-reuse' ]; // Read argv config @@ -220,6 +221,12 @@ function configureCommandlineSwitchesSync(cliArgs) { process.argv.push('--log', argvValue); } break; + + case 'enable-render-process-reuse': + if (argvValue === true) { + app.allowRendererProcessReuse = true; + } + break; } } }); From fc24669991cfd6968660255dedb4a2013e6db360 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 13:03:46 -0800 Subject: [PATCH 143/330] Extract CellToolbars. --- .../browser/view/notebookRenderingCommon.ts | 14 +- .../browser/view/renderers/cellDnd.ts | 4 +- .../browser/view/renderers/cellOutput.ts | 4 +- .../browser/view/renderers/cellRenderer.ts | 342 ++++++++++-------- 4 files changed, 214 insertions(+), 150 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 1abcf561309..3052fd2419e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -89,6 +89,14 @@ export interface INotebookCellList { dispose(): void; } +export interface ICellToolbars { + toolbar: ToolBar; + deleteToolbar: ToolBar; + betweenCellToolbar: ToolBar; + updateContext(element: ICellViewModel, elementDisposables: DisposableStore): void; + setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void; +} + export interface BaseCellRenderTemplate { rootContainer: HTMLElement; editorPart: HTMLElement; @@ -97,12 +105,10 @@ export interface BaseCellRenderTemplate { container: HTMLElement; cellContainer: HTMLElement; decorationContainer: HTMLElement; - toolbar: ToolBar; - deleteToolbar: ToolBar; - betweenCellToolbar: ToolBar; + cellToolbars: ICellToolbars; focusIndicatorLeft: FastDomNode; focusIndicatorRight: FastDomNode; - readonly disposables: DisposableStore; + readonly templateDisposables: DisposableStore; readonly elementDisposables: DisposableStore; bottomCellContainer: HTMLElement; currentRenderedCell?: ICellViewModel; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts index c25b8fc76c3..13e42c99cfe 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts @@ -305,7 +305,7 @@ export class CellDragAndDropController extends Disposable { const container = templateData.container; dragHandle.setAttribute('draggable', 'true'); - templateData.disposables.add(DOM.addDisposableListener(dragHandle, DOM.EventType.DRAG_END, () => { + templateData.templateDisposables.add(DOM.addDisposableListener(dragHandle, DOM.EventType.DRAG_END, () => { if (!this.notebookEditor.notebookOptions.getLayoutConfiguration().dragAndDropEnabled || !!this.notebookEditor.isReadOnly) { return; } @@ -315,7 +315,7 @@ export class CellDragAndDropController extends Disposable { this.dragCleanup(); })); - templateData.disposables.add(DOM.addDisposableListener(dragHandle, DOM.EventType.DRAG_START, event => { + templateData.templateDisposables.add(DOM.addDisposableListener(dragHandle, DOM.EventType.DRAG_START, event => { if (!event.dataTransfer) { return; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts index df62bb4b569..841747762c9 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts @@ -630,7 +630,7 @@ export class CellOutputContainer extends Disposable { this.templateData.outputShowMoreContainer.domNode.innerText = ''; if (this.viewCell.outputsViewModels.length > this.options.limit) { - this.templateData.outputShowMoreContainer.domNode.appendChild(this._generateShowMoreElement(this.templateData.disposables)); + this.templateData.outputShowMoreContainer.domNode.appendChild(this._generateShowMoreElement(this.templateData.templateDisposables)); } else { DOM.hide(this.templateData.outputShowMoreContainer.domNode); this.viewCell.updateOutputShowMoreContainerHeight(0); @@ -835,7 +835,7 @@ export class CellOutputContainer extends Disposable { if (this.viewCell.outputsViewModels.length > this.options.limit) { DOM.show(this.templateData.outputShowMoreContainer.domNode); if (!this.templateData.outputShowMoreContainer.domNode.hasChildNodes()) { - this.templateData.outputShowMoreContainer.domNode.appendChild(this._generateShowMoreElement(this.templateData.disposables)); + this.templateData.outputShowMoreContainer.domNode.appendChild(this._generateShowMoreElement(this.templateData.templateDisposables)); } this.viewCell.updateOutputShowMoreContainerHeight(46); } else { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 6b0b8181729..14e572d1233 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -39,7 +39,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { BaseCellRenderTemplate, CodeCellRenderTemplate, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { BaseCellRenderTemplate, CodeCellRenderTemplate, ICellToolbars, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; @@ -88,6 +88,170 @@ export class NotebookCellListDelegate extends Disposable implements IListVirtual } } +class CellToolbars extends Disposable implements ICellToolbars { + toolbar: ToolBar; + deleteToolbar: ToolBar; + betweenCellToolbar!: ToolBar; + cellDisposable: Disposable | null = null; + + constructor( + readonly notebookEditor: INotebookEditorDelegate, + readonly contextKeyService: IContextKeyService, + readonly titleToolbarContainer: HTMLElement, + readonly bottomCellContainer: HTMLElement, + @IInstantiationService readonly instantiationService: IInstantiationService, + @IContextMenuService readonly contextMenuService: IContextMenuService, + @IKeybindingService readonly keybindingService: IKeybindingService, + @IMenuService readonly menuService: IMenuService, + ) { + super(); + + this.toolbar = this._register(this.createToolbar(this.titleToolbarContainer)); + this.deleteToolbar = this._register(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); + if (!this.notebookEditor.creationOptions.isReadOnly) { + this.deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); + } + + this.createBetweenCellToolbar(); + } + + createToolbar(container: HTMLElement, elementClass?: string): ToolBar { + const toolbar = new ToolBar(container, this.contextMenuService, { + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), + actionViewItemProvider: action => { + return createActionViewItem(this.instantiationService, action); + }, + renderDropdownAsChildElement: true + }); + + if (elementClass) { + toolbar.getElement().classList.add(elementClass); + } + + return toolbar; + } + + createBetweenCellToolbar() { + this.betweenCellToolbar = this._register(new ToolBar(this.bottomCellContainer, this.contextMenuService, { + actionViewItemProvider: action => { + if (action instanceof MenuItemAction) { + if (this.notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { + return this.instantiationService.createInstance(CodiconActionViewItem, action); + } else { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + } + } + + return undefined; + } + })); + + const menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellInsertToolbar, this.contextKeyService)); + const updateActions = () => { + const actions = this.getCellToolbarActions(menu); + this.betweenCellToolbar.setActions(actions.primary, actions.secondary); + }; + + this._register(menu.onDidChange(() => updateActions())); + this._register(this.notebookEditor.notebookOptions.onDidChangeOptions((e) => { + if (e.insertToolbarAlignment) { + updateActions(); + } + })); + updateActions(); + } + + getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; } { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + return result; + } + + updateContext(element: CodeCellViewModel | MarkupCellViewModel, elementDisposables: DisposableStore) { + const toolbarContext = { + ui: true, + cell: element, + notebookEditor: this.notebookEditor, + $mid: MarshalledId.NotebookCellActionContext + }; + + this.toolbar.context = toolbarContext; + this.deleteToolbar.context = toolbarContext; + this.betweenCellToolbar.context = toolbarContext; + + const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; + this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + + elementDisposables.add(element.onDidChangeLayout(() => { + const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; + this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + })); + } + + setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { + const updateActions = () => { + const actions = this.getCellToolbarActions(templateData.titleMenu); + + const hadFocus = DOM.isAncestor(document.activeElement, this.toolbar.getElement()); + this.toolbar.setActions(actions.primary, actions.secondary); + if (hadFocus) { + this.notebookEditor.focus(); + } + + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); + if (actions.primary.length || actions.secondary.length) { + templateData.container.classList.add('cell-has-toolbar-actions'); + if (isCodeCellRenderTemplate(templateData)) { + templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; + templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; + } + } else { + templateData.container.classList.remove('cell-has-toolbar-actions'); + if (isCodeCellRenderTemplate(templateData)) { + templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; + templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; + } + } + }; + + // #103926 + let dropdownIsVisible = false; + let deferredUpdate: (() => void) | undefined; + + updateActions(); + disposables.add(templateData.titleMenu.onDidChange(() => { + if (this.notebookEditor.isDisposed) { + return; + } + + if (dropdownIsVisible) { + deferredUpdate = () => updateActions(); + return; + } + + updateActions(); + })); + templateData.container.classList.toggle('cell-toolbar-dropdown-active', false); + disposables.add(this.toolbar.onDidChangeDropdownVisibility(visible => { + dropdownIsVisible = visible; + templateData.container.classList.toggle('cell-toolbar-dropdown-active', visible); + + if (deferredUpdate && !visible) { + setTimeout(() => { + if (deferredUpdate) { + deferredUpdate(); + } + }, 0); + deferredUpdate = undefined; + } + })); + } +} + abstract class AbstractCellRenderer { protected readonly editorOptions: CellEditorOptions; @@ -144,35 +308,6 @@ abstract class AbstractCellRenderer { return toolbar; } - protected setBetweenCellToolbarContext(templateData: BaseCellRenderTemplate, element: CodeCellViewModel | MarkupCellViewModel, context: INotebookCellActionContext): void { - templateData.betweenCellToolbar.context = context; - - const container = templateData.bottomCellContainer; - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - container.style.transform = `translateY(${bottomToolbarOffset}px)`; - - templateData.elementDisposables.add(element.onDidChangeLayout(() => { - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - container.style.transform = `translateY(${bottomToolbarOffset}px)`; - })); - } - - protected createToolbar(container: HTMLElement, elementClass?: string): ToolBar { - const toolbar = new ToolBar(container, this.contextMenuService, { - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), - actionViewItemProvider: action => { - return createActionViewItem(this.instantiationService, action); - }, - renderDropdownAsChildElement: true - }); - - if (elementClass) { - toolbar.getElement().classList.add(elementClass); - } - - return toolbar; - } - protected getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; } { const primary: IAction[] = []; const secondary: IAction[] = []; @@ -183,67 +318,8 @@ abstract class AbstractCellRenderer { return result; } - protected setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { - const updateActions = () => { - const actions = this.getCellToolbarActions(templateData.titleMenu); - - const hadFocus = DOM.isAncestor(document.activeElement, templateData.toolbar.getElement()); - templateData.toolbar.setActions(actions.primary, actions.secondary); - if (hadFocus) { - this.notebookEditor.focus(); - } - - const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); - if (actions.primary.length || actions.secondary.length) { - templateData.container.classList.add('cell-has-toolbar-actions'); - if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; - templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; - } - } else { - templateData.container.classList.remove('cell-has-toolbar-actions'); - if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; - templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; - } - } - }; - - // #103926 - let dropdownIsVisible = false; - let deferredUpdate: (() => void) | undefined; - - updateActions(); - disposables.add(templateData.titleMenu.onDidChange(() => { - if (this.notebookEditor.isDisposed) { - return; - } - - if (dropdownIsVisible) { - deferredUpdate = () => updateActions(); - return; - } - - updateActions(); - })); - templateData.container.classList.toggle('cell-toolbar-dropdown-active', false); - disposables.add(templateData.toolbar.onDidChangeDropdownVisibility(visible => { - dropdownIsVisible = visible; - templateData.container.classList.toggle('cell-toolbar-dropdown-active', visible); - - if (deferredUpdate && !visible) { - setTimeout(() => { - if (deferredUpdate) { - deferredUpdate(); - } - }, 0); - deferredUpdate = undefined; - } - })); - } - protected commonRenderTemplate(templateData: BaseCellRenderTemplate): void { - templateData.disposables.add(DOM.addDisposableListener(templateData.container, DOM.EventType.FOCUS, () => { + templateData.templateDisposables.add(DOM.addDisposableListener(templateData.container, DOM.EventType.FOCUS, () => { if (templateData.currentRenderedCell) { this.notebookEditor.focusElement(templateData.currentRenderedCell); } @@ -317,15 +393,10 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen renderTemplate(rootContainer: HTMLElement): MarkdownCellRenderTemplate { rootContainer.classList.add('markdown-cell-row'); const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); - const disposables = new DisposableStore(); - const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); + const templateDisposables = new DisposableStore(); + const contextKeyService = templateDisposables.add(this.contextKeyServiceProvider(container)); const decorationContainer = DOM.append(rootContainer, $('.cell-decoration')); const titleToolbarContainer = DOM.append(container, $('.cell-title-toolbar')); - const toolbar = disposables.add(this.createToolbar(titleToolbarContainer)); - const deleteToolbar = disposables.add(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); - if (!this.notebookEditor.creationOptions.isReadOnly) { - deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); - } DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); const focusIndicatorLeft = new FastDomNode(DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left'))); @@ -341,12 +412,13 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen const foldingIndicator = DOM.append(focusIndicatorLeft.domNode, DOM.$('.notebook-folding-indicator')); const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); - const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService, this.notebookEditor.notebookOptions)); + const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); + const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')); - const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); + const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); - const titleMenu = disposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); + const titleMenu = templateDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); const templateData: MarkdownCellRenderTemplate = { rootContainer, @@ -361,11 +433,9 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen focusIndicatorBottom, focusIndicatorRight, foldingIndicator, - disposables, + templateDisposables, elementDisposables: new DisposableStore(), - toolbar, - deleteToolbar, - betweenCellToolbar, + cellToolbars, bottomCellContainer, titleMenu, statusBar, @@ -401,7 +471,8 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen })); // render toolbar first - this.setupCellToolbarActions(templateData, elementDisposables); + templateData.cellToolbars.setupCellToolbarActions(templateData, elementDisposables); + // this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { ui: true, @@ -409,10 +480,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen notebookEditor: this.notebookEditor, $mid: MarshalledId.NotebookCellActionContext }; - templateData.toolbar.context = toolbarContext; - templateData.deleteToolbar.context = toolbarContext; - - this.setBetweenCellToolbarContext(templateData, element, toolbarContext); + templateData.cellToolbars.updateContext(element, templateData.elementDisposables); const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); @@ -431,7 +499,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { - templateData.disposables.clear(); + templateData.templateDisposables.clear(); } disposeElement(element: ICellViewModel, _index: number, templateData: MarkdownCellRenderTemplate): void { @@ -579,16 +647,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende renderTemplate(rootContainer: HTMLElement): CodeCellRenderTemplate { rootContainer.classList.add('code-cell-row'); const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); - const disposables = new DisposableStore(); - const contextKeyService = disposables.add(this.contextKeyServiceProvider(container)); + const templateDisposables = new DisposableStore(); + const contextKeyService = templateDisposables.add(this.contextKeyServiceProvider(container)); const decorationContainer = DOM.append(rootContainer, $('.cell-decoration')); DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); const titleToolbarContainer = DOM.append(container, $('.cell-title-toolbar')); - const toolbar = disposables.add(this.createToolbar(titleToolbarContainer)); - const deleteToolbar = disposables.add(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); - if (!this.notebookEditor.creationOptions.isReadOnly) { - deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); - } const focusIndicator = new FastDomNode(DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left'))); const dragHandle = new FastDomNode(DOM.append(container, DOM.$('.cell-drag-handle'))); @@ -596,7 +659,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const runButtonContainer = DOM.append(cellContainer, $('.run-button-container')); const cellInputCollapsedContainer = DOM.append(cellContainer, $('.input-collapse-container')); - const runToolbar = this.setupRunToolbar(runButtonContainer, container, contextKeyService, disposables); + const runToolbar = this.setupRunToolbar(runButtonContainer, container, contextKeyService, templateDisposables); const executionOrderLabel = DOM.append(cellContainer, $('div.execution-count-label')); executionOrderLabel.title = localize('cellExecutionOrderCountLabel', 'Execution Order'); @@ -604,7 +667,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const editorContainer = DOM.append(editorPart, $('.cell-editor-container')); // create a special context key service that set the inCompositeEditor-contextkey - const editorContextKeyService = disposables.add(this.contextKeyServiceProvider(editorPart)); + const editorContextKeyService = templateDisposables.add(this.contextKeyServiceProvider(editorPart)); const editorInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); EditorContextKeys.inCompositeEditor.bindTo(editorContextKeyService).set(true); @@ -619,17 +682,17 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende contributions: this.notebookEditor.creationOptions.cellEditorContributions }); - disposables.add(editor); + templateDisposables.add(editor); const progressBar = new ProgressBar(editorPart); progressBar.hide(); - disposables.add(progressBar); + templateDisposables.add(progressBar); const collapsedProgressBar = new ProgressBar(cellInputCollapsedContainer); collapsedProgressBar.hide(); - disposables.add(collapsedProgressBar); + templateDisposables.add(collapsedProgressBar); - const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); + const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); const outputContainer = new FastDomNode(DOM.append(container, $('.output'))); const cellOutputCollapsedContainer = DOM.append(outputContainer.domNode, $('.output-collapse-container')); @@ -641,9 +704,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende focusSinkElement.setAttribute('tabindex', '0'); const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); - const betweenCellToolbar = this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService, this.notebookEditor.notebookOptions); - const titleMenu = disposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); + const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); + const titleMenu = templateDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); const templateData: CodeCellRenderTemplate = { rootContainer, @@ -660,9 +723,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende focusIndicatorLeft: focusIndicator, focusIndicatorRight, focusIndicatorBottom, - toolbar, - deleteToolbar, - betweenCellToolbar, + cellToolbars, focusSinkElement, runToolbar, runButtonContainer, @@ -670,7 +731,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende outputContainer, outputShowMoreContainer, editor, - disposables, + templateDisposables, elementDisposables: new DisposableStore(), bottomCellContainer, titleMenu, @@ -680,14 +741,14 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.dndController?.registerDragHandle(templateData, rootContainer, dragHandle.domNode, () => new CodeCellDragImageRenderer().getDragImage(templateData, templateData.editor, 'code')); - disposables.add(this.addCollapseClickCollapseHandler(templateData)); - disposables.add(DOM.addDisposableListener(focusSinkElement, DOM.EventType.FOCUS, () => { + templateDisposables.add(this.addCollapseClickCollapseHandler(templateData)); + templateDisposables.add(DOM.addDisposableListener(focusSinkElement, DOM.EventType.FOCUS, () => { if (templateData.currentRenderedCell && (templateData.currentRenderedCell as CodeCellViewModel).outputsViewModels.length) { this.notebookEditor.focusNotebookCell(templateData.currentRenderedCell, 'output'); } })); - disposables.add(this.notebookEditor.onDidChangeActiveKernel(() => { + templateDisposables.add(this.notebookEditor.onDidChangeActiveKernel(() => { if (templateData.currentRenderedCell) { this.updateForKernel(templateData.currentRenderedCell as CodeCellViewModel, templateData); } @@ -727,11 +788,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.currentRenderedCell.isOutputCollapsed = !templateData.currentRenderedCell.isOutputCollapsed; }; - templateData.disposables.add(DOM.addDisposableListener(expandIcon, DOM.EventType.CLICK, () => { + templateData.templateDisposables.add(DOM.addDisposableListener(expandIcon, DOM.EventType.CLICK, () => { expand(); })); - templateData.disposables.add(DOM.addDisposableListener(cellOutputCollapseContainer, DOM.EventType.DBLCLICK, () => { + templateData.templateDisposables.add(DOM.addDisposableListener(cellOutputCollapseContainer, DOM.EventType.DBLCLICK, () => { expand(); })); } @@ -887,7 +948,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { - templateData.disposables.add(DOM.scheduleAtNextAnimationFrame(() => { + templateData.templateDisposables.add(DOM.scheduleAtNextAnimationFrame(() => { const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); const bottomToolbarDimensions = this.notebookEditor.notebookOptions.computeBottomToolbarDimensions(this.notebookEditor.textModel?.viewType); templateData.focusIndicatorLeft.setHeight(element.layoutInfo.indicatorHeight); @@ -970,7 +1031,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForKernel(element, templateData); - this.setupCellToolbarActions(templateData, elementDisposables); + templateData.cellToolbars.setupCellToolbarActions(templateData, elementDisposables); + // this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { ui: true, @@ -979,17 +1041,13 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende notebookEditor: this.notebookEditor, $mid: MarshalledId.NotebookCellActionContext }; - templateData.toolbar.context = toolbarContext; + templateData.cellToolbars.updateContext(element, templateData.elementDisposables); templateData.runToolbar.context = toolbarContext; - templateData.deleteToolbar.context = toolbarContext; - - this.setBetweenCellToolbarContext(templateData, element, toolbarContext); - templateData.statusBar.update(toolbarContext); } disposeTemplate(templateData: CodeCellRenderTemplate): void { - templateData.disposables.clear(); + templateData.templateDisposables.clear(); } disposeElement(element: ICellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { From ee96da10b2474ae782bff29621c72080de92f877 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 13:07:01 -0800 Subject: [PATCH 144/330] :lipstick: --- .../browser/view/renderers/cellRenderer.ts | 170 +--------------- .../browser/view/renderers/cellToolbars.ts | 187 ++++++++++++++++++ 2 files changed, 190 insertions(+), 167 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 14e572d1233..2396bb35a9a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -26,7 +26,7 @@ import * as modes from 'vs/editor/common/modes'; import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { localize } from 'vs/nls'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; -import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -37,13 +37,13 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { BaseCellRenderTemplate, CodeCellRenderTemplate, ICellToolbars, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions'; +import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/renderers/CellToolbars'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; @@ -88,170 +88,6 @@ export class NotebookCellListDelegate extends Disposable implements IListVirtual } } -class CellToolbars extends Disposable implements ICellToolbars { - toolbar: ToolBar; - deleteToolbar: ToolBar; - betweenCellToolbar!: ToolBar; - cellDisposable: Disposable | null = null; - - constructor( - readonly notebookEditor: INotebookEditorDelegate, - readonly contextKeyService: IContextKeyService, - readonly titleToolbarContainer: HTMLElement, - readonly bottomCellContainer: HTMLElement, - @IInstantiationService readonly instantiationService: IInstantiationService, - @IContextMenuService readonly contextMenuService: IContextMenuService, - @IKeybindingService readonly keybindingService: IKeybindingService, - @IMenuService readonly menuService: IMenuService, - ) { - super(); - - this.toolbar = this._register(this.createToolbar(this.titleToolbarContainer)); - this.deleteToolbar = this._register(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); - if (!this.notebookEditor.creationOptions.isReadOnly) { - this.deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); - } - - this.createBetweenCellToolbar(); - } - - createToolbar(container: HTMLElement, elementClass?: string): ToolBar { - const toolbar = new ToolBar(container, this.contextMenuService, { - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), - actionViewItemProvider: action => { - return createActionViewItem(this.instantiationService, action); - }, - renderDropdownAsChildElement: true - }); - - if (elementClass) { - toolbar.getElement().classList.add(elementClass); - } - - return toolbar; - } - - createBetweenCellToolbar() { - this.betweenCellToolbar = this._register(new ToolBar(this.bottomCellContainer, this.contextMenuService, { - actionViewItemProvider: action => { - if (action instanceof MenuItemAction) { - if (this.notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { - return this.instantiationService.createInstance(CodiconActionViewItem, action); - } else { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); - } - } - - return undefined; - } - })); - - const menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellInsertToolbar, this.contextKeyService)); - const updateActions = () => { - const actions = this.getCellToolbarActions(menu); - this.betweenCellToolbar.setActions(actions.primary, actions.secondary); - }; - - this._register(menu.onDidChange(() => updateActions())); - this._register(this.notebookEditor.notebookOptions.onDidChangeOptions((e) => { - if (e.insertToolbarAlignment) { - updateActions(); - } - })); - updateActions(); - } - - getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; - } - - updateContext(element: CodeCellViewModel | MarkupCellViewModel, elementDisposables: DisposableStore) { - const toolbarContext = { - ui: true, - cell: element, - notebookEditor: this.notebookEditor, - $mid: MarshalledId.NotebookCellActionContext - }; - - this.toolbar.context = toolbarContext; - this.deleteToolbar.context = toolbarContext; - this.betweenCellToolbar.context = toolbarContext; - - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; - - elementDisposables.add(element.onDidChangeLayout(() => { - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; - })); - } - - setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { - const updateActions = () => { - const actions = this.getCellToolbarActions(templateData.titleMenu); - - const hadFocus = DOM.isAncestor(document.activeElement, this.toolbar.getElement()); - this.toolbar.setActions(actions.primary, actions.secondary); - if (hadFocus) { - this.notebookEditor.focus(); - } - - const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); - if (actions.primary.length || actions.secondary.length) { - templateData.container.classList.add('cell-has-toolbar-actions'); - if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; - templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; - } - } else { - templateData.container.classList.remove('cell-has-toolbar-actions'); - if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; - templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; - } - } - }; - - // #103926 - let dropdownIsVisible = false; - let deferredUpdate: (() => void) | undefined; - - updateActions(); - disposables.add(templateData.titleMenu.onDidChange(() => { - if (this.notebookEditor.isDisposed) { - return; - } - - if (dropdownIsVisible) { - deferredUpdate = () => updateActions(); - return; - } - - updateActions(); - })); - templateData.container.classList.toggle('cell-toolbar-dropdown-active', false); - disposables.add(this.toolbar.onDidChangeDropdownVisibility(visible => { - dropdownIsVisible = visible; - templateData.container.classList.toggle('cell-toolbar-dropdown-active', visible); - - if (deferredUpdate && !visible) { - setTimeout(() => { - if (deferredUpdate) { - deferredUpdate(); - } - }, 0); - deferredUpdate = undefined; - } - })); - } -} - abstract class AbstractCellRenderer { protected readonly editorOptions: CellEditorOptions; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts new file mode 100644 index 00000000000..9ec7f86103d --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IAction } from 'vs/base/common/actions'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshalling'; +import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; +import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, ICellToolbars, isCodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; +import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; + +export class CellToolbars extends Disposable implements ICellToolbars { + toolbar: ToolBar; + deleteToolbar: ToolBar; + betweenCellToolbar!: ToolBar; + cellDisposable: Disposable | null = null; + + constructor( + readonly notebookEditor: INotebookEditorDelegate, + readonly contextKeyService: IContextKeyService, + readonly titleToolbarContainer: HTMLElement, + readonly bottomCellContainer: HTMLElement, + @IInstantiationService readonly instantiationService: IInstantiationService, + @IContextMenuService readonly contextMenuService: IContextMenuService, + @IKeybindingService readonly keybindingService: IKeybindingService, + @IMenuService readonly menuService: IMenuService + ) { + super(); + + this.toolbar = this._register(this.createToolbar(this.titleToolbarContainer)); + this.deleteToolbar = this._register(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); + if (!this.notebookEditor.creationOptions.isReadOnly) { + this.deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); + } + + this.createBetweenCellToolbar(); + } + + createToolbar(container: HTMLElement, elementClass?: string): ToolBar { + const toolbar = new ToolBar(container, this.contextMenuService, { + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), + actionViewItemProvider: action => { + return createActionViewItem(this.instantiationService, action); + }, + renderDropdownAsChildElement: true + }); + + if (elementClass) { + toolbar.getElement().classList.add(elementClass); + } + + return toolbar; + } + + createBetweenCellToolbar() { + this.betweenCellToolbar = this._register(new ToolBar(this.bottomCellContainer, this.contextMenuService, { + actionViewItemProvider: action => { + if (action instanceof MenuItemAction) { + if (this.notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { + return this.instantiationService.createInstance(CodiconActionViewItem, action); + } else { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + } + } + + return undefined; + } + })); + + const menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellInsertToolbar, this.contextKeyService)); + const updateActions = () => { + const actions = this.getCellToolbarActions(menu); + this.betweenCellToolbar.setActions(actions.primary, actions.secondary); + }; + + this._register(menu.onDidChange(() => updateActions())); + this._register(this.notebookEditor.notebookOptions.onDidChangeOptions((e) => { + if (e.insertToolbarAlignment) { + updateActions(); + } + })); + updateActions(); + } + + getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[]; } { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + return result; + } + + updateContext(element: CodeCellViewModel | MarkupCellViewModel, elementDisposables: DisposableStore) { + const toolbarContext = { + ui: true, + cell: element, + notebookEditor: this.notebookEditor, + $mid: MarshalledId.NotebookCellActionContext + }; + + this.toolbar.context = toolbarContext; + this.deleteToolbar.context = toolbarContext; + this.betweenCellToolbar.context = toolbarContext; + + const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; + this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + + elementDisposables.add(element.onDidChangeLayout(() => { + const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; + this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + })); + } + + setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { + const updateActions = () => { + const actions = this.getCellToolbarActions(templateData.titleMenu); + + const hadFocus = DOM.isAncestor(document.activeElement, this.toolbar.getElement()); + this.toolbar.setActions(actions.primary, actions.secondary); + if (hadFocus) { + this.notebookEditor.focus(); + } + + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); + if (actions.primary.length || actions.secondary.length) { + templateData.container.classList.add('cell-has-toolbar-actions'); + if (isCodeCellRenderTemplate(templateData)) { + templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; + templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; + } + } else { + templateData.container.classList.remove('cell-has-toolbar-actions'); + if (isCodeCellRenderTemplate(templateData)) { + templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; + templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; + } + } + }; + + // #103926 + let dropdownIsVisible = false; + let deferredUpdate: (() => void) | undefined; + + updateActions(); + disposables.add(templateData.titleMenu.onDidChange(() => { + if (this.notebookEditor.isDisposed) { + return; + } + + if (dropdownIsVisible) { + deferredUpdate = () => updateActions(); + return; + } + + updateActions(); + })); + templateData.container.classList.toggle('cell-toolbar-dropdown-active', false); + disposables.add(this.toolbar.onDidChangeDropdownVisibility(visible => { + dropdownIsVisible = visible; + templateData.container.classList.toggle('cell-toolbar-dropdown-active', visible); + + if (deferredUpdate && !visible) { + setTimeout(() => { + if (deferredUpdate) { + deferredUpdate(); + } + }, 0); + deferredUpdate = undefined; + } + })); + } +} From cdb14d806c1ba26bc0d461085bc1beeaaabcd862 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 13:11:20 -0800 Subject: [PATCH 145/330] remove title menu from template. --- .../notebook/browser/view/notebookRenderingCommon.ts | 2 -- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 5 ----- .../contrib/notebook/browser/view/renderers/cellToolbars.ts | 6 ++++-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 3052fd2419e..5ff3d37262e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -19,7 +19,6 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { IMenu } from 'vs/platform/actions/common/actions'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -113,7 +112,6 @@ export interface BaseCellRenderTemplate { bottomCellContainer: HTMLElement; currentRenderedCell?: ICellViewModel; statusBar: CellEditorStatusBar; - titleMenu: IMenu; toJSON: () => object; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 2396bb35a9a..4217bca86a7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -254,8 +254,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); - const titleMenu = templateDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); - const templateData: MarkdownCellRenderTemplate = { rootContainer, cellInputCollapsedContainer, @@ -273,7 +271,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen elementDisposables: new DisposableStore(), cellToolbars, bottomCellContainer, - titleMenu, statusBar, toJSON: () => { return {}; } }; @@ -542,7 +539,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); - const titleMenu = templateDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); const templateData: CodeCellRenderTemplate = { rootContainer, @@ -570,7 +566,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateDisposables, elementDisposables: new DisposableStore(), bottomCellContainer, - titleMenu, dragHandle, toJSON: () => { return {}; } }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts index 9ec7f86103d..51ca43882cb 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts @@ -26,6 +26,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { toolbar: ToolBar; deleteToolbar: ToolBar; betweenCellToolbar!: ToolBar; + titleMenu: IMenu; cellDisposable: Disposable | null = null; constructor( @@ -47,6 +48,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { } this.createBetweenCellToolbar(); + this.titleMenu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); } createToolbar(container: HTMLElement, elementClass?: string): ToolBar { @@ -128,7 +130,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { const updateActions = () => { - const actions = this.getCellToolbarActions(templateData.titleMenu); + const actions = this.getCellToolbarActions(this.titleMenu); const hadFocus = DOM.isAncestor(document.activeElement, this.toolbar.getElement()); this.toolbar.setActions(actions.primary, actions.secondary); @@ -157,7 +159,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { let deferredUpdate: (() => void) | undefined; updateActions(); - disposables.add(templateData.titleMenu.onDidChange(() => { + disposables.add(this.titleMenu.onDidChange(() => { if (this.notebookEditor.isDisposed) { return; } From 0e233cca9c8994377e89bfa8033658b895a1df7b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 16 Nov 2021 22:20:07 +0100 Subject: [PATCH 146/330] Adopt a pattern that works with parcel 2 --- src/vs/base/common/marked/marked.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index b7a725a1bca..3a3f975c922 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -2980,14 +2980,14 @@ // ESM-uncomment-begin // })(); -// export var marked = __marked_exports; -// export var Parser = __marked_exports.Parser; -// export var parser = __marked_exports.parser; -// export var Renderer = __marked_exports.Renderer; -// export var TextRenderer = __marked_exports.TextRenderer; -// export var Lexer = __marked_exports.Lexer; -// export var lexer = __marked_exports.lexer; -// export var Tokenizer = __marked_exports.Tokenizer; -// export var Slugger = __marked_exports.Slugger; -// export var parse = __marked_exports.parse; +// export var marked = (__marked_exports || exports); +// export var Parser = (__marked_exports || exports).Parser; +// export var parser = (__marked_exports || exports).parser; +// export var Renderer = (__marked_exports || exports).Renderer; +// export var TextRenderer = (__marked_exports || exports).TextRenderer; +// export var Lexer = (__marked_exports || exports).Lexer; +// export var lexer = (__marked_exports || exports).lexer; +// export var Tokenizer = (__marked_exports || exports).Tokenizer; +// export var Slugger = (__marked_exports || exports).Slugger; +// export var parse = (__marked_exports || exports).parse; // ESM-uncomment-end From 2673296a8f8bc85b419403abdf74daab97c58f6c Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 13:40:22 -0800 Subject: [PATCH 147/330] fix nb test npe. --- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 2 +- src/vs/workbench/contrib/notebook/test/cellOutput.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 4217bca86a7..8c2a4f74626 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -43,7 +43,7 @@ import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/vie import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions'; -import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/renderers/CellToolbars'; +import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; diff --git a/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts b/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts index 2c4593d6de9..13730127895 100644 --- a/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts +++ b/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts @@ -110,7 +110,7 @@ suite('NotebookViewModel Outputs', async () => { return 100; } }, - disposables: new DisposableStore(), + templateDisposables: new DisposableStore(), } as unknown as CodeCellRenderTemplate, { limit: 5 }, openerService, instantiationService); container.render(100); assert.strictEqual(container.renderedOutputEntries.length, 4); @@ -189,7 +189,7 @@ suite('NotebookViewModel Outputs', async () => { return 100; } }, - disposables: new DisposableStore(), + templateDisposables: new DisposableStore(), } as unknown as CodeCellRenderTemplate, { limit: 5 }, openerService, instantiationService); container.render(100); assert.strictEqual(container.renderedOutputEntries.length, 5); From e303d3eb001861f0871afdb7a78c4908ad1cc21c Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 14:01:36 -0800 Subject: [PATCH 148/330] Update cell renderer naming. --- .../browser/view/renderers/cellRenderer.ts | 53 +++---------------- .../browser/view/renderers/cellToolbars.ts | 8 +-- .../browser/view/renderers/cellWidgets.ts | 4 +- 3 files changed, 13 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 8c2a4f74626..f5d17381b8e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -26,7 +26,7 @@ import * as modes from 'vs/editor/common/modes'; import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { localize } from 'vs/nls'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; -import { createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -51,7 +51,6 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; const $ = DOM.$; @@ -111,39 +110,6 @@ abstract class AbstractCellRenderer { this.dndController = undefined; } - protected createBetweenCellToolbar(container: HTMLElement, disposables: DisposableStore, contextKeyService: IContextKeyService, notebookOptions: NotebookOptions): ToolBar { - const toolbar = new ToolBar(container, this.contextMenuService, { - actionViewItemProvider: action => { - if (action instanceof MenuItemAction) { - if (notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { - return this.instantiationService.createInstance(CodiconActionViewItem, action); - } else { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); - } - } - - return undefined; - } - }); - disposables.add(toolbar); - - const menu = disposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellInsertToolbar, contextKeyService)); - const updateActions = () => { - const actions = this.getCellToolbarActions(menu); - toolbar.setActions(actions.primary, actions.secondary); - }; - - disposables.add(menu.onDidChange(() => updateActions())); - disposables.add(notebookOptions.onDidChangeOptions((e) => { - if (e.insertToolbarAlignment) { - updateActions(); - } - })); - updateActions(); - - return toolbar; - } - protected getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; } { const primary: IAction[] = []; const secondary: IAction[] = []; @@ -236,6 +202,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-top')); const focusIndicatorLeft = new FastDomNode(DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left'))); + const foldingIndicator = DOM.append(focusIndicatorLeft.domNode, DOM.$('.notebook-folding-indicator')); const focusIndicatorRight = new FastDomNode(DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-right'))); const codeInnerContent = DOM.append(container, $('.cell.code')); @@ -245,13 +212,9 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen editorPart.style.display = 'none'; const innerContent = DOM.append(container, $('.cell.markdown')); - const foldingIndicator = DOM.append(focusIndicatorLeft.domNode, DOM.$('.notebook-folding-indicator')); - const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); - const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')); - const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); const templateData: MarkdownCellRenderTemplate = { @@ -305,7 +268,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen // render toolbar first templateData.cellToolbars.setupCellToolbarActions(templateData, elementDisposables); - // this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { ui: true, @@ -318,7 +280,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); elementDisposables.add(markdownCell); - templateData.statusBar.update(toolbarContext); + templateData.statusBar.updateContext(toolbarContext); } private updateForLayout(element: MarkupCellViewModel, templateData: MarkdownCellRenderTemplate): void { @@ -535,10 +497,10 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const focusSinkElement = DOM.append(container, $('.cell-editor-focus-sink')); focusSinkElement.setAttribute('tabindex', '0'); - const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); + const bottomCellToolbarContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); - const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); + const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellToolbarContainer)); const templateData: CodeCellRenderTemplate = { rootContainer, @@ -565,7 +527,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende editor, templateDisposables, elementDisposables: new DisposableStore(), - bottomCellContainer, + bottomCellContainer: bottomCellToolbarContainer, dragHandle, toJSON: () => { return {}; } }; @@ -863,7 +825,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForKernel(element, templateData); templateData.cellToolbars.setupCellToolbarActions(templateData, elementDisposables); - // this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { ui: true, @@ -874,7 +835,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende }; templateData.cellToolbars.updateContext(element, templateData.elementDisposables); templateData.runToolbar.context = toolbarContext; - templateData.statusBar.update(toolbarContext); + templateData.statusBar.updateContext(toolbarContext); } disposeTemplate(templateData: CodeCellRenderTemplate): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts index 51ca43882cb..3397e02b078 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts @@ -33,7 +33,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { readonly notebookEditor: INotebookEditorDelegate, readonly contextKeyService: IContextKeyService, readonly titleToolbarContainer: HTMLElement, - readonly bottomCellContainer: HTMLElement, + readonly bottomCellToolbarContainer: HTMLElement, @IInstantiationService readonly instantiationService: IInstantiationService, @IContextMenuService readonly contextMenuService: IContextMenuService, @IKeybindingService readonly keybindingService: IKeybindingService, @@ -68,7 +68,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { } createBetweenCellToolbar() { - this.betweenCellToolbar = this._register(new ToolBar(this.bottomCellContainer, this.contextMenuService, { + this.betweenCellToolbar = this._register(new ToolBar(this.bottomCellToolbarContainer, this.contextMenuService, { actionViewItemProvider: action => { if (action instanceof MenuItemAction) { if (this.notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { @@ -120,11 +120,11 @@ export class CellToolbars extends Disposable implements ICellToolbars { this.betweenCellToolbar.context = toolbarContext; const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + this.bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; elementDisposables.add(element.onDidChangeLayout(() => { const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + this.bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; })); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts index 1f4546456ad..d2c990038f1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -66,7 +66,7 @@ export class CellEditorStatusBar extends Disposable { this.itemsDisposable = this._register(new DisposableStore()); - this._register(this._themeService.onDidColorThemeChange(() => this.currentContext && this.update(this.currentContext))); + this._register(this._themeService.onDidColorThemeChange(() => this.currentContext && this.updateContext(this.currentContext))); this._register(DOM.addDisposableListener(this.statusBarContainer, DOM.EventType.CLICK, e => { if (e.target === leftItemsContainer || e.target === rightItemsContainer || e.target === this.statusBarContainer) { @@ -116,7 +116,7 @@ export class CellEditorStatusBar extends Disposable { return this.width / 2; } - update(context: INotebookCellActionContext) { + updateContext(context: INotebookCellActionContext) { this.currentContext = context; this.itemsDisposable.clear(); From 5cbefa8178a162d7db216be070aa017476f4bab4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Nov 2021 23:07:03 +0100 Subject: [PATCH 149/330] #51935 suggestons for multi language identifiers --- .../src/settingsDocumentHelper.ts | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index e6afeb22d1d..11282899b81 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -9,6 +9,7 @@ import * as nls from 'vscode-nls'; import { provideInstalledExtensionProposals } from './extensionsProposals'; const localize = nls.loadMessageBundle(); +const OVERRIDE_IDENTIFIER_REGEX = /\[([^\[\]]*)\]/g; export class SettingsDocument { @@ -186,61 +187,60 @@ export class SettingsDocument { .then(languages => languages.map(l => this.newSimpleCompletionItem(formatFunc(l), range))); } - private provideLanguageCompletionItemsForLanguageOverrides(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable { - return vscode.languages.getLanguages().then(languages => { - const completionItems = []; - const configuration = vscode.workspace.getConfiguration(); - for (const language of languages) { - const inspect = configuration.inspect(`[${language}]`); - if (!inspect || !inspect.defaultValue) { - const item = new vscode.CompletionItem(formatFunc(language)); - item.kind = vscode.CompletionItemKind.Property; - item.range = range; - completionItems.push(item); - } - } - return completionItems; - }); + private async provideLanguageCompletionItemsForLanguageOverrides(_location: Location, range: vscode.Range): Promise { + const languages = await vscode.languages.getLanguages(); + const completionItems = []; + for (const language of languages) { + const item = new vscode.CompletionItem(JSON.stringify(language)); + item.kind = vscode.CompletionItemKind.Property; + item.range = range; + completionItems.push(item); + } + return completionItems; } - private provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): vscode.ProviderResult { - - if (location.path.length === 0) { - - let range = this.document.getWordRangeAtPosition(position, /^\s*\[.*]?/) || new vscode.Range(position, position); - let text = this.document.getText(range); - if (text && text.trim().startsWith('[')) { - range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + text.indexOf('[')), range.end); - return this.provideLanguageCompletionItemsForLanguageOverrides(location, range, language => `"[${language}]"`); - } - - range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); - text = this.document.getText(range); - let snippet = '"[${1:language}]": {\n\t"$0"\n}'; - - // Suggestion model word matching includes quotes, - // hence exclude the starting quote from the snippet and the range - // ending quote gets replaced - if (text && text.startsWith('"')) { - range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 1), range.end); - snippet = snippet.substring(1); - } - - return Promise.resolve([this.newSnippetCompletionItem({ - label: localize('languageSpecificEditorSettings', "Language specific editor settings"), - documentation: localize('languageSpecificEditorSettingsDescription', "Override editor settings for language"), - snippet, - range - })]); - } - + private async provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): Promise { if (location.path.length === 1 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) { - // Suggestion model word matching includes closed sqaure bracket and ending quote - // Hence include them in the proposal to replace - const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); - return this.provideLanguageCompletionItemsForLanguageOverrides(location, range, language => `"[${language}]"`); + const startPosition = this.document.positionAt(location.previousNode.offset + 1); + const endPosition = startPosition.translate(undefined, location.previousNode.value.length); + const donotSuggestLanguages: string[] = []; + const languageOverridesRanges: vscode.Range[] = []; + let matches = OVERRIDE_IDENTIFIER_REGEX.exec(location.previousNode.value); + let lastLanguageOverrideRange: vscode.Range | undefined; + while (matches?.length) { + lastLanguageOverrideRange = new vscode.Range(this.document.positionAt(location.previousNode.offset + 1 + matches.index), this.document.positionAt(location.previousNode.offset + 1 + matches.index + matches[0].length)); + languageOverridesRanges.push(lastLanguageOverrideRange); + /* Suggest the configured language if the position is in the match range */ + if (!lastLanguageOverrideRange.contains(position)) { + donotSuggestLanguages.push(matches[1].trim()); + } + matches = OVERRIDE_IDENTIFIER_REGEX.exec(location.previousNode.value); + } + const lastLanguageOverrideEndPosition = lastLanguageOverrideRange ? lastLanguageOverrideRange.end : startPosition; + if (lastLanguageOverrideEndPosition.isBefore(endPosition)) { + languageOverridesRanges.push(new vscode.Range(lastLanguageOverrideEndPosition, endPosition)); + } + const languageOverrideRange = languageOverridesRanges.find(range => range.contains(position)); + + /** + * Skip if suggestsions are for first language override range + * Since VSCode registers language overrides to the schema, JSON language server does suggestions for first language override. + */ + if (languageOverrideRange && !languageOverrideRange.isEqual(languageOverridesRanges[0])) { + const languages = await vscode.languages.getLanguages(); + const completionItems = []; + for (const language of languages) { + if (!donotSuggestLanguages.includes(language)) { + const item = new vscode.CompletionItem(`[${language}]`); + item.kind = vscode.CompletionItemKind.Property; + item.range = languageOverrideRange; + completionItems.push(item); + } + } + return completionItems; + } } - return Promise.resolve([]); + return []; } private providePortsAttributesCompletionItem(range: vscode.Range): vscode.CompletionItem[] { From aa1a68f78a32e8a71068e1d8424dcbf336472923 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 14:42:04 -0800 Subject: [PATCH 150/330] notebook viewparts. --- .../notebook/browser/diff/diffComponents.ts | 2 +- .../notebook/browser/diff/diffElementOutputs.ts | 2 +- .../browser/diff/notebookTextDiffList.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 4 ++-- .../browser/view/notebookRenderingCommon.ts | 2 +- .../browser/view/renderers/cellRenderer.ts | 16 ++++++++-------- .../{renderers => viewParts}/cellActionView.ts | 0 .../{renderers => viewParts}/cellContextKeys.ts | 0 .../view/{renderers => viewParts}/cellDnd.ts | 0 .../cellEditorOptions.ts | 0 .../view/{renderers => viewParts}/cellOutput.ts | 2 +- .../{renderers => viewParts}/cellToolbars.ts | 2 +- .../view/{renderers => viewParts}/cellWidgets.ts | 0 .../view/{renderers => viewParts}/codeCell.ts | 4 ++-- .../{renderers => viewParts}/markdownCell.ts | 2 +- .../browser/viewParts/notebookEditorToolbar.ts | 2 +- .../contrib/notebook/test/cellOutput.test.ts | 2 +- 17 files changed, 21 insertions(+), 21 deletions(-) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellActionView.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellContextKeys.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellDnd.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellEditorOptions.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellOutput.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellToolbars.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/cellWidgets.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/codeCell.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{renderers => viewParts}/markdownCell.ts (99%) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 14c6dc4b5dd..92a411c420e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -23,7 +23,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/diff/diffElementOutputs'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts index e6d0c482d5d..85964aa6ff1 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts @@ -10,7 +10,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { DiffElementViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { DiffSide, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { BUILTIN_RENDERER_ID, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 914d2b81480..57e8b95a2c7 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -24,7 +24,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8d165b28b81..9e8c6c2b96c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -47,8 +47,8 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/no import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { BackLayerWebView, INotebookWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; -import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; -import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd'; import { CodeCellRenderer, ListTopCellToolbar, MarkupCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 5ff3d37262e..33524c04350 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -19,7 +19,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; export interface INotebookCellList { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index f5d17381b8e..2c1144f7e59 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -39,14 +39,14 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; -import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; -import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd'; -import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions'; -import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars'; -import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; -import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; -import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys'; +import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd'; +import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions'; +import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; +import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/viewParts/codeCell'; +import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput.ts index 841747762c9..170cc805509 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput.ts @@ -28,7 +28,7 @@ import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browse import { ICellOutputViewModel, ICellViewModel, IInsetRenderOutput, INotebookEditorDelegate, IRenderOutput, JUPYTER_EXTENSION_ID, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { mimetypeIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { BUILTIN_RENDERER_ID, CellUri, IOrderedMimeType, NotebookCellOutputsSplice, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars.ts index 3397e02b078..9b67e9d84f8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars.ts @@ -18,7 +18,7 @@ import { INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, ICellToolbars, isCodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/codeCell.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/codeCell.ts index 6587aa78ec3..4a01bc5d8db 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/viewParts/codeCell.ts @@ -20,8 +20,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { CellFocusMode, EXPAND_CELL_INPUT_COMMAND_ID, IActiveNotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput'; -import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; +import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput'; +import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts rename to src/vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell.ts index 2d29c5f05a1..a81b160a047 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell.ts @@ -25,7 +25,7 @@ import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { TokenizationRegistry } from 'vs/editor/common/modes'; import { MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions'; +import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 2a32670df98..358bd919051 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -22,7 +22,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { INotebookEditorDelegate, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; -import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; +import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; diff --git a/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts b/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts index 13730127895..ba768810147 100644 --- a/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts +++ b/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts @@ -16,7 +16,7 @@ import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbe import { CodeCellRenderTemplate, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry'; import { getStringValue } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform'; -import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput'; +import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { BUILTIN_RENDERER_ID, CellEditType, CellKind, IOutputDto, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; From 57f6be85d4863ffef1d372669e42545e7e66d3b1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 14:48:56 -0800 Subject: [PATCH 151/330] extract nb top cell toolbar. --- .../notebook/browser/notebookEditorWidget.ts | 3 +- .../browser/view/renderers/cellRenderer.ts | 81 +--------------- .../browser/view/viewParts/topCellToolbar.ts | 92 +++++++++++++++++++ 3 files changed, 95 insertions(+), 81 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 9e8c6c2b96c..14f250171bc 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -49,7 +49,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/outpu import { BackLayerWebView, INotebookWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys'; import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd'; -import { CodeCellRenderer, ListTopCellToolbar, MarkupCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; +import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; @@ -70,6 +70,7 @@ import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { registerZIndex, ZIndex } from 'vs/platform/layout/browser/zIndexRegistry'; import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { notebookDebug } from 'vs/workbench/contrib/notebook/browser/notebookLogger'; +import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar'; const $ = DOM.$; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 2c1144f7e59..433b5e4a024 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -36,10 +36,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions'; @@ -862,81 +861,3 @@ export function getCodeCellExecutionContextKeyService(contextKeyService: IContex return executionContextKeyService; } - -export class ListTopCellToolbar extends Disposable { - private topCellToolbar: HTMLElement; - private menu: IMenu; - private toolbar: ToolBar; - private readonly _modelDisposables = this._register(new DisposableStore()); - constructor( - protected readonly notebookEditor: INotebookEditorDelegate, - - contextKeyService: IContextKeyService, - insertionIndicatorContainer: HTMLElement, - @IInstantiationService protected readonly instantiationService: IInstantiationService, - @IContextMenuService protected readonly contextMenuService: IContextMenuService, - @IMenuService protected readonly menuService: IMenuService - ) { - super(); - - this.topCellToolbar = DOM.append(insertionIndicatorContainer, $('.cell-list-top-cell-toolbar-container')); - - this.toolbar = this._register(new ToolBar(this.topCellToolbar, this.contextMenuService, { - actionViewItemProvider: action => { - if (action instanceof MenuItemAction) { - const item = this.instantiationService.createInstance(CodiconActionViewItem, action); - return item; - } - - return undefined; - } - })); - this.toolbar.context = { - notebookEditor - }; - - this.menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTopInsertToolbar, contextKeyService)); - this._register(this.menu.onDidChange(() => { - this.updateActions(); - })); - this.updateActions(); - - // update toolbar container css based on cell list length - this._register(this.notebookEditor.onDidChangeModel(() => { - this._modelDisposables.clear(); - - if (this.notebookEditor.hasModel()) { - this._modelDisposables.add(this.notebookEditor.onDidChangeViewCells(() => { - this.updateClass(); - })); - - this.updateClass(); - } - })); - - this.updateClass(); - } - - private updateActions() { - const actions = this.getCellToolbarActions(this.menu, false); - this.toolbar.setActions(actions.primary, actions.secondary); - } - - private updateClass() { - if (this.notebookEditor.hasModel() && this.notebookEditor.getLength() === 0) { - this.topCellToolbar.classList.add('emptyNotebook'); - } else { - this.topCellToolbar.classList.remove('emptyNotebook'); - } - } - - private getCellToolbarActions(menu: IMenu, alwaysFillSecondaryActions: boolean): { primary: IAction[], secondary: IAction[]; } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts new file mode 100644 index 00000000000..f6e42d76117 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts @@ -0,0 +1,92 @@ +import * as DOM from 'vs/base/browser/dom'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IAction } from 'vs/base/common/actions'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotebookActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; + +export class ListTopCellToolbar extends Disposable { + private topCellToolbar: HTMLElement; + private menu: IMenu; + private toolbar: ToolBar; + private readonly _modelDisposables = this._register(new DisposableStore()); + constructor( + protected readonly notebookEditor: INotebookEditorDelegate, + + contextKeyService: IContextKeyService, + insertionIndicatorContainer: HTMLElement, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IContextMenuService protected readonly contextMenuService: IContextMenuService, + @IMenuService protected readonly menuService: IMenuService + ) { + super(); + + this.topCellToolbar = DOM.append(insertionIndicatorContainer, DOM.$('.cell-list-top-cell-toolbar-container')); + + this.toolbar = this._register(new ToolBar(this.topCellToolbar, this.contextMenuService, { + actionViewItemProvider: action => { + if (action instanceof MenuItemAction) { + const item = this.instantiationService.createInstance(CodiconActionViewItem, action); + return item; + } + + return undefined; + } + })); + this.toolbar.context = { + notebookEditor + }; + + this.menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTopInsertToolbar, contextKeyService)); + this._register(this.menu.onDidChange(() => { + this.updateActions(); + })); + this.updateActions(); + + // update toolbar container css based on cell list length + this._register(this.notebookEditor.onDidChangeModel(() => { + this._modelDisposables.clear(); + + if (this.notebookEditor.hasModel()) { + this._modelDisposables.add(this.notebookEditor.onDidChangeViewCells(() => { + this.updateClass(); + })); + + this.updateClass(); + } + })); + + this.updateClass(); + } + + private updateActions() { + const actions = this.getCellToolbarActions(this.menu, false); + this.toolbar.setActions(actions.primary, actions.secondary); + } + + private updateClass() { + if (this.notebookEditor.hasModel() && this.notebookEditor.getLength() === 0) { + this.topCellToolbar.classList.add('emptyNotebook'); + } else { + this.topCellToolbar.classList.remove('emptyNotebook'); + } + } + + private getCellToolbarActions(menu: IMenu, alwaysFillSecondaryActions: boolean): { primary: IAction[], secondary: IAction[]; } { + type NewType = IAction; + + const primary: NewType[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + return result; + } +} From cfb2f750c73e38df622c99c08670d06bc46252fc Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 14:56:16 -0800 Subject: [PATCH 152/330] :lipstick: --- .../notebook/browser/view/viewParts/topCellToolbar.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts index f6e42d76117..05958d82354 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + import * as DOM from 'vs/base/browser/dom'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction } from 'vs/base/common/actions'; From 1170cc9837d0d52d9e426c832474efd59e7cb271 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 17:43:23 -0800 Subject: [PATCH 153/330] -bottomCellToolbarContainer. --- .../contrib/notebook/browser/view/notebookRenderingCommon.ts | 1 - .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 33524c04350..b088845805d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -109,7 +109,6 @@ export interface BaseCellRenderTemplate { focusIndicatorRight: FastDomNode; readonly templateDisposables: DisposableStore; readonly elementDisposables: DisposableStore; - bottomCellContainer: HTMLElement; currentRenderedCell?: ICellViewModel; statusBar: CellEditorStatusBar; toJSON: () => object; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 433b5e4a024..01e80ff64a4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -232,7 +232,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen templateDisposables, elementDisposables: new DisposableStore(), cellToolbars, - bottomCellContainer, statusBar, toJSON: () => { return {}; } }; @@ -526,7 +525,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende editor, templateDisposables, elementDisposables: new DisposableStore(), - bottomCellContainer: bottomCellToolbarContainer, dragHandle, toJSON: () => { return {}; } }; From f4aff7557109203446c2338f122af1f0c9d26779 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 17:47:15 -0800 Subject: [PATCH 154/330] cellParts. --- .../notebook/browser/diff/diffComponents.ts | 2 +- .../notebook/browser/diff/diffElementOutputs.ts | 2 +- .../notebook/browser/diff/notebookTextDiffList.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 6 +++--- .../{viewParts => cellParts}/cellActionView.ts | 0 .../{viewParts => cellParts}/cellContextKeys.ts | 0 .../view/{viewParts => cellParts}/cellDnd.ts | 0 .../{viewParts => cellParts}/cellEditorOptions.ts | 0 .../view/{viewParts => cellParts}/cellOutput.ts | 2 +- .../view/{viewParts => cellParts}/cellToolbars.ts | 2 +- .../view/{viewParts => cellParts}/cellWidgets.ts | 0 .../view/{viewParts => cellParts}/codeCell.ts | 4 ++-- .../view/{viewParts => cellParts}/markdownCell.ts | 2 +- .../{viewParts => cellParts}/topCellToolbar.ts | 2 +- .../browser/view/notebookRenderingCommon.ts | 2 +- .../browser/view/renderers/cellRenderer.ts | 14 +++++++------- .../browser/viewParts/notebookEditorToolbar.ts | 2 +- .../contrib/notebook/test/cellOutput.test.ts | 2 +- 18 files changed, 22 insertions(+), 22 deletions(-) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellActionView.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellContextKeys.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellDnd.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellEditorOptions.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellOutput.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellToolbars.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/cellWidgets.ts (100%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/codeCell.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/markdownCell.ts (99%) rename src/vs/workbench/contrib/notebook/browser/view/{viewParts => cellParts}/topCellToolbar.ts (98%) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 92a411c420e..17955e7c261 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -23,7 +23,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/diff/diffElementOutputs'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts index 85964aa6ff1..437372d6e8b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts @@ -10,7 +10,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { DiffElementViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { DiffSide, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; +import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { BUILTIN_RENDERER_ID, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 57e8b95a2c7..6b3b5f56da3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -24,7 +24,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 14f250171bc..ea2e6c4f3d2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -47,8 +47,8 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/no import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { BackLayerWebView, INotebookWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; -import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys'; -import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd'; import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; @@ -70,7 +70,7 @@ import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { registerZIndex, ZIndex } from 'vs/platform/layout/browser/zIndexRegistry'; import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { notebookDebug } from 'vs/workbench/contrib/notebook/browser/notebookLogger'; -import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar'; +import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/topCellToolbar'; const $ = DOM.$; diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index 170cc805509..91bfd890984 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -28,7 +28,7 @@ import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browse import { ICellOutputViewModel, ICellViewModel, IInsetRenderOutput, INotebookEditorDelegate, IRenderOutput, JUPYTER_EXTENSION_ID, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { mimetypeIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; +import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { BUILTIN_RENDERER_ID, CellUri, IOrderedMimeType, NotebookCellOutputsSplice, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 9b67e9d84f8..fc6a91278a8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -18,7 +18,7 @@ import { INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, ICellToolbars, isCodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/codeCell.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index 4a01bc5d8db..a4a838c337a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/viewParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -20,8 +20,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { CellFocusMode, EXPAND_CELL_INPUT_COMMAND_ID, IActiveNotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput'; -import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; +import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput'; +import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts index a81b160a047..2879bfce310 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts @@ -25,7 +25,7 @@ import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { TokenizationRegistry } from 'vs/editor/common/modes'; import { MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions'; +import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/topCellToolbar.ts similarity index 98% rename from src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/topCellToolbar.ts index 05958d82354..33ac46a5319 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/viewParts/topCellToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/topCellToolbar.ts @@ -14,7 +14,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotebookActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; +import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; export class ListTopCellToolbar extends Disposable { private topCellToolbar: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index b088845805d..8aad7f496f0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -19,7 +19,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; export interface INotebookCellList { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 01e80ff64a4..149a49e69e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -39,13 +39,13 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellContextKeys'; -import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellDnd'; -import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellEditorOptions'; -import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellToolbars'; -import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellWidgets'; -import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/viewParts/codeCell'; -import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/viewParts/markdownCell'; +import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; +import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd'; +import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions'; +import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; +import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCell'; +import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 358bd919051..2569ebf1b93 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -22,7 +22,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { INotebookEditorDelegate, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem'; -import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellActionView'; +import { ActionViewWithLabel } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; diff --git a/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts b/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts index ba768810147..5354a020e95 100644 --- a/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts +++ b/src/vs/workbench/contrib/notebook/test/cellOutput.test.ts @@ -16,7 +16,7 @@ import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbe import { CodeCellRenderTemplate, IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry'; import { getStringValue } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform'; -import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/viewParts/cellOutput'; +import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { BUILTIN_RENDERER_ID, CellEditType, CellKind, IOutputDto, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; From 7fa59454935e38ee95c88fb54244392d4db24d64 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 18:04:58 -0800 Subject: [PATCH 155/330] Extract Cell Run Toolbar. --- .../view/cellParts/codeCellRunToolbar.ts | 120 ++++++++++++++++++ .../browser/view/notebookRenderingCommon.ts | 8 +- .../browser/view/renderers/cellRenderer.ts | 87 +------------ 3 files changed, 133 insertions(+), 82 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts new file mode 100644 index 00000000000..7c4a650a46f --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { Action, IAction } from 'vs/base/common/actions'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { localize } from 'vs/nls'; +import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IRunToolbar } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; + +export class RunToolbar extends Disposable implements IRunToolbar { + toolbar!: ToolBar; + + constructor( + readonly notebookEditor: INotebookEditorDelegate, + readonly contextKeyService: IContextKeyService, + readonly cellContainer: HTMLElement, + readonly runButtonContainer: HTMLElement, + @IMenuService readonly menuService: IMenuService, + @IKeybindingService readonly keybindingService: IKeybindingService, + @IContextMenuService readonly contextMenuService: IContextMenuService, + @IInstantiationService readonly instantiationService: IInstantiationService, + ) { + super(); + + const menu = this._register(menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)) + this.createRunCellToolbar(runButtonContainer, cellContainer, contextKeyService); + const updateActions = () => { + const actions = this.getCellToolbarActions(menu); + const primary = actions.primary[0]; // Only allow one primary action + this.toolbar.setActions(primary ? [primary] : []); + }; + updateActions(); + this._register(menu.onDidChange(updateActions)); + this._register(this.notebookEditor.notebookOptions.onDidChangeOptions(updateActions)); + } + + getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; } { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + return result; + } + + private createRunCellToolbar(container: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService) { + const actionViewItemDisposables = this._register(new DisposableStore()); + const dropdownAction = this._register(new Action('notebook.moreRunActions', localize('notebook.moreRunActionsLabel', "More..."), 'codicon-chevron-down', true)); + + const keybindingProvider = (action: IAction) => this.keybindingService.lookupKeybinding(action.id, executionContextKeyService); + const executionContextKeyService = this._register(getCodeCellExecutionContextKeyService(contextKeyService)); + this.toolbar = this._register(new ToolBar(container, this.contextMenuService, { + getKeyBinding: keybindingProvider, + actionViewItemProvider: _action => { + actionViewItemDisposables.clear(); + + const primaryMenu = actionViewItemDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)); + const primary = this.getCellToolbarActions(primaryMenu).primary[0]; + if (!(primary instanceof MenuItemAction)) { + return undefined; + } + + const menu = actionViewItemDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecuteToolbar, contextKeyService)); + const secondary = this.getCellToolbarActions(menu).secondary; + if (!secondary.length) { + return undefined; + } + + const item = this.instantiationService.createInstance(DropdownWithPrimaryActionViewItem, + primary, + dropdownAction, + secondary, + 'notebook-cell-run-toolbar', + this.contextMenuService, + { + getKeyBinding: keybindingProvider + }); + actionViewItemDisposables.add(item.onDidChangeDropdownVisibility(visible => { + cellContainer.classList.toggle('cell-run-toolbar-dropdown-active', visible); + })); + + return item; + }, + renderDropdownAsChildElement: true + })); + } + + updateContext(context: INotebookCellActionContext) { + this.toolbar.context = context; + } +} + +export function getCodeCellExecutionContextKeyService(contextKeyService: IContextKeyService): IContextKeyService { + // Create a fake ContextKeyService, and look up the keybindings within this context. + const executionContextKeyService = contextKeyService.createScoped(document.createElement('div')); + InputFocusedContext.bindTo(executionContextKeyService).set(true); + EditorContextKeys.editorTextFocus.bindTo(executionContextKeyService).set(true); + EditorContextKeys.focus.bindTo(executionContextKeyService).set(true); + EditorContextKeys.textInputFocus.bindTo(executionContextKeyService).set(true); + NOTEBOOK_CELL_EXECUTION_STATE.bindTo(executionContextKeyService).set('idle'); + NOTEBOOK_CELL_LIST_FOCUSED.bindTo(executionContextKeyService).set(true); + NOTEBOOK_EDITOR_FOCUSED.bindTo(executionContextKeyService).set(true); + NOTEBOOK_CELL_TYPE.bindTo(executionContextKeyService).set('code'); + + return executionContextKeyService; +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 8aad7f496f0..e4e9b458a03 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -21,6 +21,7 @@ import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCom import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import type { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; export interface INotebookCellList { isDisposed: boolean; @@ -121,8 +122,13 @@ export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate { currentEditor?: ICodeEditor; } +export interface IRunToolbar { + toolbar: ToolBar; + updateContext(context: INotebookCellActionContext): void; +} + export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { - runToolbar: ToolBar; + runToolbar: IRunToolbar; runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; outputContainer: FastDomNode; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 149a49e69e9..d31f8aa15a3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -8,8 +8,7 @@ import * as DOM from 'vs/base/browser/dom'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { Action, IAction } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { combinedDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; @@ -25,19 +24,17 @@ import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { localize } from 'vs/nls'; -import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd'; @@ -46,6 +43,7 @@ import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/cellPar import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCell'; import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell'; +import { RunToolbar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -452,7 +450,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const runButtonContainer = DOM.append(cellContainer, $('.run-button-container')); const cellInputCollapsedContainer = DOM.append(cellContainer, $('.input-collapse-container')); - const runToolbar = this.setupRunToolbar(runButtonContainer, container, contextKeyService, templateDisposables); + const runToolbar = templateDisposables.add(this.instantiationService.createInstance(RunToolbar, this.notebookEditor, contextKeyService, container, runButtonContainer)); const executionOrderLabel = DOM.append(cellContainer, $('div.execution-count-label')); executionOrderLabel.title = localize('cellExecutionOrderCountLabel', 'Execution Order'); @@ -632,64 +630,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende return combinedDisposable(dragHandleListener, collapsedPartListener, clickHandler); } - private createRunCellToolbar(container: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar { - const actionViewItemDisposables = disposables.add(new DisposableStore()); - const dropdownAction = disposables.add(new Action('notebook.moreRunActions', localize('notebook.moreRunActionsLabel', "More..."), 'codicon-chevron-down', true)); - - const keybindingProvider = (action: IAction) => this.keybindingService.lookupKeybinding(action.id, executionContextKeyService); - const executionContextKeyService = disposables.add(getCodeCellExecutionContextKeyService(contextKeyService)); - const toolbar = disposables.add(new ToolBar(container, this.contextMenuService, { - getKeyBinding: keybindingProvider, - actionViewItemProvider: _action => { - actionViewItemDisposables.clear(); - - const primaryMenu = actionViewItemDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)); - const primary = this.getCellToolbarActions(primaryMenu).primary[0]; - if (!(primary instanceof MenuItemAction)) { - return undefined; - } - - const menu = actionViewItemDisposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecuteToolbar, contextKeyService)); - const secondary = this.getCellToolbarActions(menu).secondary; - if (!secondary.length) { - return undefined; - } - - const item = this.instantiationService.createInstance(DropdownWithPrimaryActionViewItem, - primary, - dropdownAction, - secondary, - 'notebook-cell-run-toolbar', - this.contextMenuService, - { - getKeyBinding: keybindingProvider - }); - actionViewItemDisposables.add(item.onDidChangeDropdownVisibility(visible => { - cellContainer.classList.toggle('cell-run-toolbar-dropdown-active', visible); - })); - - return item; - }, - renderDropdownAsChildElement: true - })); - - return toolbar; - } - - private setupRunToolbar(runButtonContainer: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar { - const menu = disposables.add(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)); - const runToolbar = this.createRunCellToolbar(runButtonContainer, cellContainer, contextKeyService, disposables); - const updateActions = () => { - const actions = this.getCellToolbarActions(menu); - const primary = actions.primary[0]; // Only allow one primary action - runToolbar.setActions(primary ? [primary] : []); - }; - updateActions(); - disposables.add(menu.onDidChange(updateActions)); - disposables.add(this.notebookEditor.notebookOptions.onDidChangeOptions(updateActions)); - return runToolbar; - } - private updateForOutputs(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { if (element.outputsViewModels.length) { DOM.show(templateData.focusSinkElement); @@ -831,7 +771,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende $mid: MarshalledId.NotebookCellActionContext }; templateData.cellToolbars.updateContext(element, templateData.elementDisposables); - templateData.runToolbar.context = toolbarContext; + templateData.runToolbar.updateContext(toolbarContext); templateData.statusBar.updateContext(toolbarContext); } @@ -844,18 +784,3 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.renderedEditors.delete(element); } } - -export function getCodeCellExecutionContextKeyService(contextKeyService: IContextKeyService): IContextKeyService { - // Create a fake ContextKeyService, and look up the keybindings within this context. - const executionContextKeyService = contextKeyService.createScoped(document.createElement('div')); - InputFocusedContext.bindTo(executionContextKeyService).set(true); - EditorContextKeys.editorTextFocus.bindTo(executionContextKeyService).set(true); - EditorContextKeys.focus.bindTo(executionContextKeyService).set(true); - EditorContextKeys.textInputFocus.bindTo(executionContextKeyService).set(true); - NOTEBOOK_CELL_EXECUTION_STATE.bindTo(executionContextKeyService).set('idle'); - NOTEBOOK_CELL_LIST_FOCUSED.bindTo(executionContextKeyService).set(true); - NOTEBOOK_EDITOR_FOCUSED.bindTo(executionContextKeyService).set(true); - NOTEBOOK_CELL_TYPE.bindTo(executionContextKeyService).set('code'); - - return executionContextKeyService; -} From d42cecf56ec65ea3aee8baf36a49afd9ee6e6ba3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 18:12:12 -0800 Subject: [PATCH 156/330] -runButtonContainer --- .../contrib/notebook/browser/view/notebookRenderingCommon.ts | 1 - .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index e4e9b458a03..03d297d6a1e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -129,7 +129,6 @@ export interface IRunToolbar { export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { runToolbar: IRunToolbar; - runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; outputContainer: FastDomNode; cellOutputCollapsedContainer: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index d31f8aa15a3..388d87c6257 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -516,7 +516,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende cellToolbars, focusSinkElement, runToolbar, - runButtonContainer, executionOrderLabel, outputContainer, outputShowMoreContainer, From d450b04c783dfa6f83dc439017767acccd4e9fac Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Nov 2021 18:44:24 -0800 Subject: [PATCH 157/330] :lipstick: --- .../notebook/browser/view/cellParts/codeCellRunToolbar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index 7c4a650a46f..37f35387a72 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -35,7 +35,7 @@ export class RunToolbar extends Disposable implements IRunToolbar { ) { super(); - const menu = this._register(menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)) + const menu = this._register(menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)); this.createRunCellToolbar(runButtonContainer, cellContainer, contextKeyService); const updateActions = () => { const actions = this.getCellToolbarActions(menu); From 119b0ea23d3d541cd3cfddb296a628520e0099a7 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Nov 2021 10:57:39 +0100 Subject: [PATCH 158/330] remove unused method --- src/vs/platform/log/node/spdlogLog.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index 9c7bc2b69a0..e72d4db4a36 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -23,12 +23,6 @@ async function createSpdLogLogger(name: string, logfilePath: string, filesize: n return null; } -export function createRotatingLogger(name: string, filename: string, filesize: number, filecount: number): Promise { - const _spdlog: typeof spdlog = require.__$__nodeRequire('spdlog'); - _spdlog.setFlushOn(LogLevel.Trace); - return _spdlog.createRotatingLogger(name, filename, filesize, filecount); -} - interface ILog { level: LogLevel; message: string; From af67d7303c36b306b60de0e6b03cc668c9dfdafd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Nov 2021 11:00:09 +0100 Subject: [PATCH 159/330] add note why spdlog is used in ext host --- src/vs/workbench/api/node/extHostLoggerService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/node/extHostLoggerService.ts b/src/vs/workbench/api/node/extHostLoggerService.ts index 67d9d52e055..d25dd875169 100644 --- a/src/vs/workbench/api/node/extHostLoggerService.ts +++ b/src/vs/workbench/api/node/extHostLoggerService.ts @@ -14,6 +14,7 @@ export class ExtHostLoggerService extends BaseExtHostLoggerService { protected override doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { if (resource.scheme === Schemas.file) { + /* Create the logger in the Extension Host process to prevent loggers (log, output channels...) traffic over IPC */ return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel); } return super.doCreateLogger(resource, logLevel, options); From daabfff18584ac95a28f4cc4e7ea2ef553c0bd42 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 17 Nov 2021 13:09:18 +0100 Subject: [PATCH 160/330] Incorporate tree dnd API feedback Part of #32592 --- .../api/browser/mainThreadTreeViews.ts | 6 +- .../workbench/api/common/extHost.api.impl.ts | 2 + .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 8 +-- src/vs/workbench/api/common/extHostTypes.ts | 24 +++++++ .../api/common/shared/treeDataTransfer.ts | 8 +-- .../workbench/browser/parts/views/treeView.ts | 66 ++++++++++++------- src/vs/workbench/common/views.ts | 5 +- .../browser/api/mainThreadTreeViews.test.ts | 2 +- .../vscode.proposed.treeViewDragAndDrop.d.ts | 15 +++-- 10 files changed, 92 insertions(+), 46 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index faed925d709..e266d88ca41 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -32,13 +32,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, canDragAndDrop: boolean }): Promise { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, dragAndDropMimeTypes: string[] }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); - const dndController = options.canDragAndDrop ? new TreeViewDragAndDropController(treeViewId, this._proxy) : undefined; + const dndController = (options.dragAndDropMimeTypes.length > 0) + ? new TreeViewDragAndDropController(treeViewId, options.dragAndDropMimeTypes, this._proxy) : undefined; const viewer = this.getTreeView(treeViewId); if (viewer) { // Order is important here. The internal tree isn't created until the dataProvider is set. @@ -167,6 +168,7 @@ type TreeItemHandle = string; class TreeViewDragAndDropController implements ITreeViewDragAndDropController { constructor(private readonly treeViewId: string, + readonly supportedMimeTypes: string[], private readonly _proxy: ExtHostTreeViewsShape) { } async onDrop(dataTransfer: ITreeDataTransfer, targetTreeItem: ITreeItem, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 43d43d5bce1..8d170f3ebee 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1301,6 +1301,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TestTag: extHostTypes.TestTag, TestRunProfileKind: extHostTypes.TestRunProfileKind, TextSearchCompleteMessageType: TextSearchCompleteMessageType, + TreeDataTransfer: extHostTypes.TreeDataTransfer, + TreeDataTransferItem: extHostTypes.TreeDataTransferItem, CoveredCount: extHostTypes.CoveredCount, FileCoverage: extHostTypes.FileCoverage, StatementCoverage: extHostTypes.StatementCoverage, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f953182df55..d0a72e28b18 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -295,7 +295,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, canDragAndDrop: boolean; }): Promise; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, dragAndDropMimeTypes: string[] }): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 86ac1db54ea..efbc0999101 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -85,8 +85,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } - const canDragAndDrop = options.dragAndDropController !== undefined; - const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, canDragAndDrop: canDragAndDrop }); + const dragAndDropMimeTypes = (options.dragAndDropController === undefined) ? [] : options.dragAndDropController.supportedMimeTypes; + const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dragAndDropMimeTypes }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, @@ -139,9 +139,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if ((sourceViewId === destinationViewId) && sourceTreeItemHandles) { const additionalTransferItems = await treeView.onWillDrop(sourceTreeItemHandles); if (additionalTransferItems) { - additionalTransferItems.items.forEach((value, key) => { + additionalTransferItems.forEach((value, key) => { if (value) { - treeDataTransfer.items.set(key, value); + treeDataTransfer.set(key, value); } }); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4623fe4de8d..8c34ee60014 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2296,6 +2296,30 @@ export enum TreeItemCollapsibleState { Expanded = 2 } +@es5ClassCompat +export class TreeDataTransferItem { + async asString(): Promise { + return JSON.stringify(this._value); + } + + constructor(private readonly _value: any) { } +} + +@es5ClassCompat +export class TreeDataTransfer { + private readonly _items: Map = new Map(); + get(mimeType: string): T | undefined { + return this._items.get(mimeType); + } + set(mimeType: string, value: T): void { + this._items.set(mimeType, value); + } + forEach(callbackfn: (value: T, key: string) => void): void { + this._items.forEach(callbackfn); + } +} + + @es5ClassCompat export class ThemeIcon { diff --git a/src/vs/workbench/api/common/shared/treeDataTransfer.ts b/src/vs/workbench/api/common/shared/treeDataTransfer.ts index 123d05adeec..fb0f9acb536 100644 --- a/src/vs/workbench/api/common/shared/treeDataTransfer.ts +++ b/src/vs/workbench/api/common/shared/treeDataTransfer.ts @@ -16,11 +16,9 @@ export interface TreeDataTransferDTO { export namespace TreeDataTransferConverter { export function toITreeDataTransfer(value: TreeDataTransferDTO): ITreeDataTransfer { - const newDataTransfer: ITreeDataTransfer = { - items: new Map() - }; + const newDataTransfer: ITreeDataTransfer = new Map(); value.types.forEach((type, index) => { - newDataTransfer.items.set(type, { + newDataTransfer.set(type, { asString: async () => value.items[index].asString }); }); @@ -32,7 +30,7 @@ export namespace TreeDataTransferConverter { types: [], items: [] }; - const entries = Array.from(value.items.entries()); + const entries = Array.from(value.entries()); for (const entry of entries) { newDTO.types.push(entry[0]); newDTO.items.push({ diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index c40d64a1475..82cc5cb73c4 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -1215,16 +1215,18 @@ export class TreeView extends AbstractTreeView { } } -const TREE_DRAG_SOURCE_INFO_MIME_TYPE = 'tree/internalsourceinfo'; interface TreeDragSourceInfo { id: string, itemHandles: string[]; } export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { + private readonly treeMimeType: string; constructor( private readonly treeId: string, - @ILabelService private readonly labelService: ILabelService) { } + @ILabelService private readonly labelService: ILabelService) { + this.treeMimeType = `tree/${treeId.toLowerCase()}`; + } private dndController: ITreeViewDragAndDropController | undefined; set controller(controller: ITreeViewDragAndDropController | undefined) { @@ -1238,16 +1240,27 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { id: this.treeId, itemHandles: treeItemsData.map(item => item.handle) }; - originalEvent.dataTransfer.setData(TREE_DRAG_SOURCE_INFO_MIME_TYPE, + originalEvent.dataTransfer.setData(this.treeMimeType, JSON.stringify(sourceInfo)); } } onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { - if (!this.dndController) { + const dndController = this.dndController; + if (!dndController || !originalEvent.dataTransfer) { return false; } - return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: true }; + const dragContainersSupportedType = originalEvent.dataTransfer.types.some((value, index) => { + if (value === this.treeMimeType) { + return true; + } else { + return dndController.supportedMimeTypes.indexOf(value) >= 0; + } + }); + if (dragContainersSupportedType) { + return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: true }; + } + return false; } getDragURI(element: ITreeItem): string | null { @@ -1272,9 +1285,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { if (!originalEvent.dataTransfer || !this.dndController || !targetNode) { return; } - const treeDataTransfer: ITreeDataTransfer = { - items: new Map() - }; + const treeDataTransfer: ITreeDataTransfer = new Map(); let stringCount = Array.from(originalEvent.dataTransfer.items).reduce((previous, current) => { if (current.kind === 'string') { return previous + 1; @@ -1284,25 +1295,34 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { let treeSourceInfo: TreeDragSourceInfo | undefined; await new Promise(resolve => { - if (!originalEvent.dataTransfer || !this.dndController || !targetNode) { + function decrementStringCount() { + stringCount--; + if (stringCount === 0) { + resolve(); + } + } + + const dndController = this.dndController; + if (!originalEvent.dataTransfer || !dndController || !targetNode) { return; } for (const dataItem of originalEvent.dataTransfer.items) { + const type = dataItem.type; if (dataItem.kind === 'string') { - const type = dataItem.type; - dataItem.getAsString(dataValue => { - if (type === TREE_DRAG_SOURCE_INFO_MIME_TYPE) { - treeSourceInfo = JSON.parse(dataValue); - } else { - treeDataTransfer.items.set(type, { - asString: () => Promise.resolve(dataValue) - }); - } - stringCount--; - if (stringCount === 0) { - resolve(); - } - }); + if ((type === this.treeMimeType) || (dndController.supportedMimeTypes.indexOf(type) >= 0)) { + dataItem.getAsString(dataValue => { + if (type === this.treeMimeType) { + treeSourceInfo = JSON.parse(dataValue); + } else { + treeDataTransfer.set(type, { + asString: () => Promise.resolve(dataValue) + }); + } + decrementStringCount(); + }); + } else { + decrementStringCount(); + } } } }); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 7f72eedc88b..df43db9d318 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -646,9 +646,7 @@ export interface ITreeDataTransferItem { asString(): Thenable; } -export interface ITreeDataTransfer { - items: Map; -} +export type ITreeDataTransfer = Map; export interface ITreeView extends IDisposable { @@ -839,6 +837,7 @@ export interface ITreeViewDataProvider { } export interface ITreeViewDragAndDropController { + readonly supportedMimeTypes: string[]; onDrop(elements: ITreeDataTransfer, target: ITreeItem, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; } diff --git a/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts b/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts index a18928327b3..9f72491c290 100644 --- a/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts @@ -73,7 +73,7 @@ suite('MainThreadHostTreeView', function () { } drain(): any { return null; } }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); - mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, canDragAndDrop: false }); + mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dragAndDropMimeTypes: [] }); await testExtensionService.whenInstalledExtensionsRegistered(); }); diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts index 293666e1296..81f74dce81a 100644 --- a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -26,24 +26,25 @@ declare module 'vscode' { dragAndDropController?: DragAndDropController; } - export interface TreeDataTransferItem { + export class TreeDataTransferItem { asString(): Thenable; + + constructor(value: any); } - export interface TreeDataTransfer { + export class TreeDataTransfer { /** * A map containing a mapping of the mime type of the corresponding data. * Trees that support drag and drop can implement `DragAndDropController.onWillDrop` to add additional mime types * when the drop occurs on an item in the same tree. */ - items: { - get: (mimeType: string) => TreeDataTransferItem | undefined - forEach: (callbackfn: (value: TreeDataTransferItem, key: string) => void) => void; - }; + get(mimeType: string): T | undefined; + set(mimeType: string, value: T): void; + forEach(callbackfn: (value: T, key: string) => void): void; } export interface DragAndDropController extends Disposable { - readonly supportedTypes: string[]; + readonly supportedMimeTypes: string[]; /** * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, From 6ff75f1af1587a891411d63622f40de42e33e1a8 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 17 Nov 2021 13:28:43 +0100 Subject: [PATCH 161/330] Add comments to tree dnd proposal Part of #32592 --- .../vscode.proposed.treeViewDragAndDrop.d.ts | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts index 81f74dce81a..c4dcf512c5b 100644 --- a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -26,42 +26,85 @@ declare module 'vscode' { dragAndDropController?: DragAndDropController; } + /** + * A class for encapsulating data transferred during a tree drag and drop event. + * + * If your `DragAndDropController` implements `onWillDrop`, you can extend `TreeDataTransferItem` and return + * an instance of your new class for easy access to the source tree items. + * + * ```ts + * class TestViewObjectTransferItem extends vscode.TreeDataTransferItem { + * constructor(private _nodes: Node[]) { + * super(_nodes); + * } + * + * asObject(): Node[] { + * return this._nodes; + * } + * } + * ``` + */ export class TreeDataTransferItem { asString(): Thenable; constructor(value: any); } + /** + * A map containing a mapping of the mime type of the corresponding transferred data. + * Trees that support drag and drop can implement `DragAndDropController.onWillDrop` to add additional mime types + * when the drop occurs on an item in the same tree. + */ export class TreeDataTransfer { /** - * A map containing a mapping of the mime type of the corresponding data. - * Trees that support drag and drop can implement `DragAndDropController.onWillDrop` to add additional mime types - * when the drop occurs on an item in the same tree. + * Retrieves the data transfer item for a given mime type. + * @param mimeType The mime type to get the data transfer item for. */ get(mimeType: string): T | 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: T): void; + + /** + * Allows iteration through the data transfer items. + * @param callbackfn Callback for iteration through the data transfer items. + */ forEach(callbackfn: (value: T, key: string) => void): void; } + /** + * Provides support for drag and drop in `TreeView`. + */ export interface DragAndDropController extends Disposable { + + /** + * The mime types that this `DragAndDropController` supports. This could be well-defined, existing, mime types, + * and also mime types defined by the extension that are returned in the `TreeDataTransfer` from `onWillDrop`. + */ readonly supportedMimeTypes: string[]; /** * When the user drops an item from this DragAndDropController on **another tree item** in **the same tree**, - * `onWillDrop` will be called with the dropped tree item. This is the DragAndDropController's opportunity to + * `onWillDrop` will be called with the dropped tree items. This is the DragAndDropController's opportunity to * package the data from the dropped tree item into whatever format they want the target tree item to receive. * * The returned `TreeDataTransfer` will be merged with the original`TreeDataTransfer` for the operation. * - * @param source + * @param source The source items for the drag and drop operation. */ onWillDrop(source: T[]): Thenable; /** + * Called when a drag and drop action results in a drop on the tree that this `DragAndDropController` belongs too. + * * Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed. * - * @param source - * @param target + * @param source The data transfer items of the source of the drag. + * @param target The target tree element that the drop is occuring on. */ onDrop(source: TreeDataTransfer, target: T): Thenable; } From da099cc71fdb9ef835f7a1bb7995b95cf52fb309 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 04:45:30 -0800 Subject: [PATCH 162/330] Skip should respect dimensions overrides tests in remote only Part of #137155 --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index b27f729f46d..8586d6c1791 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -492,7 +492,8 @@ import { assertNoRpc } from '../utils'; // const terminal = window.createTerminal({ name: 'foo', pty }); // }); - test('should respect dimension overrides', async () => { + // Skip in remote for now as it's flaky https://github.com/microsoft/vscode/issues/137155 + (process.env.REMOTE_VSCODE ? test.skip : test)('should respect dimension overrides', async () => { const writeEmitter = new EventEmitter(); const overrideDimensionsEmitter = new EventEmitter(); const pty: Pseudoterminal = { From 61913650d8fc2bd8b97ac43b5718ab32326fd20b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 17 Nov 2021 13:59:41 +0100 Subject: [PATCH 163/330] Enable file dnd in tree views Part of #32592 --- .../api/browser/mainThreadTreeViews.ts | 4 +-- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 2 +- .../workbench/browser/parts/views/treeView.ts | 31 +++++++++++++++++-- .../vscode.proposed.treeViewDragAndDrop.d.ts | 2 +- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index e266d88ca41..747c2a4ddea 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -32,13 +32,13 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, dragAndDropMimeTypes: string[] }): Promise { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, dragAndDropMimeTypes: string[] | undefined }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); - const dndController = (options.dragAndDropMimeTypes.length > 0) + const dndController = options.dragAndDropMimeTypes ? new TreeViewDragAndDropController(treeViewId, options.dragAndDropMimeTypes, this._proxy) : undefined; const viewer = this.getTreeView(treeViewId); if (viewer) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d0a72e28b18..bba94a57a8f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -295,7 +295,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, dragAndDropMimeTypes: string[] }): Promise; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, dragAndDropMimeTypes: string[] | undefined}): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index efbc0999101..09281009481 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -85,7 +85,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } - const dragAndDropMimeTypes = (options.dragAndDropController === undefined) ? [] : options.dragAndDropController.supportedMimeTypes; + const dragAndDropMimeTypes = options.dragAndDropController?.supportedMimeTypes; const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dragAndDropMimeTypes }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 82cc5cb73c4..fc647e3e4bc 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -57,6 +57,8 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { Command } from 'vs/editor/common/modes'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { CodeDataTransfers, fillEditorsDragData } from 'vs/workbench/browser/dnd'; +import { Schemas } from 'vs/base/common/network'; export class TreeViewPane extends ViewPane { @@ -1224,7 +1226,8 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { private readonly treeMimeType: string; constructor( private readonly treeId: string, - @ILabelService private readonly labelService: ILabelService) { + @ILabelService private readonly labelService: ILabelService, + @IInstantiationService private readonly instantiationService: IInstantiationService) { this.treeMimeType = `tree/${treeId.toLowerCase()}`; } @@ -1233,13 +1236,35 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { this.dndController = controller; } + private addResourceInfoToTransfer(originalEvent: DragEvent, resources: URI[]) { + if (resources.length && originalEvent.dataTransfer) { + // Apply some datatransfer types to allow for dragging the element outside of the application + this.instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, resources, originalEvent)); + + // The only custom data transfer we set from the explorer is a file transfer + // to be able to DND between multiple code file explorers across windows + const fileResources = resources.filter(s => s.scheme === Schemas.file).map(r => r.fsPath); + if (fileResources.length) { + originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); + } + } + } + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { if (originalEvent.dataTransfer) { const treeItemsData = (data as ElementsDragAndDropData).getData(); + const resources: URI[] = []; const sourceInfo: TreeDragSourceInfo = { id: this.treeId, - itemHandles: treeItemsData.map(item => item.handle) + itemHandles: [] }; + treeItemsData.forEach(item => { + sourceInfo.itemHandles.push(item.handle); + if (item.resourceUri) { + resources.push(URI.revive(item.resourceUri)); + } + }); + this.addResourceInfoToTransfer(originalEvent, resources); originalEvent.dataTransfer.setData(this.treeMimeType, JSON.stringify(sourceInfo)); } @@ -1247,7 +1272,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { const dndController = this.dndController; - if (!dndController || !originalEvent.dataTransfer) { + if (!dndController || !originalEvent.dataTransfer || (dndController.supportedMimeTypes.length === 0)) { return false; } const dragContainersSupportedType = originalEvent.dataTransfer.types.some((value, index) => { diff --git a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts index c4dcf512c5b..9db19f3260c 100644 --- a/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.treeViewDragAndDrop.d.ts @@ -96,7 +96,7 @@ declare module 'vscode' { * * @param source The source items for the drag and drop operation. */ - onWillDrop(source: T[]): Thenable; + onWillDrop?(source: T[]): Thenable; /** * Called when a drag and drop action results in a drop on the tree that this `DragAndDropController` belongs too. From b66d2e7bbd57553fde0ab6f17957bc6a2b17411b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 05:28:31 -0800 Subject: [PATCH 164/330] Next attempt to fix should respect dimension overrides in remote Fixes #137155 --- .../src/singlefolder-tests/terminal.test.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 8586d6c1791..fd700eace65 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -492,8 +492,7 @@ import { assertNoRpc } from '../utils'; // const terminal = window.createTerminal({ name: 'foo', pty }); // }); - // Skip in remote for now as it's flaky https://github.com/microsoft/vscode/issues/137155 - (process.env.REMOTE_VSCODE ? test.skip : test)('should respect dimension overrides', async () => { + test('should respect dimension overrides', async () => { const writeEmitter = new EventEmitter(); const overrideDimensionsEmitter = new EventEmitter(); const pty: Pseudoterminal = { @@ -510,17 +509,25 @@ import { assertNoRpc } from '../utils'; })); const created = window.createTerminal({ name: 'foo', pty }); }); + // Exit the test early if dimensions already match which may happen if the exthost + // has high latency + if (terminal.dimensions?.columns === 10 && terminal.dimensions?.rows === 5) { + return; + } + // TODO: Remove logs when the test is verified as non-flaky await new Promise(r => { + // Does this never fire because it's already set to 10x5? disposables.push(window.onDidChangeTerminalDimensions(e => { - strictEqual(e.terminal, terminal); + console.log(`window.onDidChangeTerminalDimensions event, dimensions = ${e.dimensions?.columns}x${e.dimensions?.rows}`); // The default pty dimensions have a chance to appear here since override // dimensions happens after the terminal is created. If so just ignore and // wait for the right dimensions - if (e.dimensions.columns === 10 || e.dimensions.rows === 5) { + if (e.terminal === terminal && e.dimensions.columns === 10 && e.dimensions.rows === 5) { disposables.push(window.onDidCloseTerminal(() => r())); terminal.dispose(); } })); + console.log(`listening for window.onDidChangeTerminalDimensions, current dimensions = ${terminal.dimensions?.columns}x${terminal.dimensions?.rows}`); terminal.show(); }); }); From dc8122c39b1fd950d2cbf3da7b4d48a866231acb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 06:11:02 -0800 Subject: [PATCH 165/330] Setup unit tests for TerminalService, hide convert slc in class again Part of #136060 --- .../terminal/browser/terminalService.ts | 60 ++++++------- .../test/browser/terminalService.test.ts | 86 ++++++++++++++++--- 2 files changed, 104 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 862df6fdf4a..a7eda50668c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -876,7 +876,7 @@ export class TerminalService implements ITerminalService { } const config = options?.config || this._terminalProfileService.availableProfiles?.find(p => p.profileName === this._terminalProfileService.getDefaultProfileName()); - const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : convertProfileToShellLaunchConfig(config || {}); + const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : this._convertProfileToShellLaunchConfig(config || {}); // Get the contributed profile if it was provided let contributedProfile = config && 'extensionIdentifier' in config ? config : undefined; @@ -1043,41 +1043,41 @@ export class TerminalService implements ITerminalService { this._onDidChangeGroups.fire(); } + protected _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { + if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { + const profile = shellLaunchConfigOrProfile; + if (!profile.path) { + return shellLaunchConfigOrProfile; + } + return { + executable: profile.path, + args: profile.args, + env: profile.env, + icon: profile.icon, + color: profile.color, + name: profile.overrideName ? profile.profileName : undefined, + cwd + }; + } + + // A shell launch config was provided + if (shellLaunchConfigOrProfile) { + if (cwd) { + shellLaunchConfigOrProfile.cwd = cwd; + } + return shellLaunchConfigOrProfile; + } + + // Return empty shell launch config + return {}; + } + async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { this._configHelper.panelContainer = panelContainer; this._terminalGroupService.setContainer(terminalContainer); } } -export function convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { - if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { - const profile = shellLaunchConfigOrProfile; - if (!profile.path) { - return shellLaunchConfigOrProfile; - } - return { - executable: profile.path, - args: profile.args, - env: profile.env, - icon: profile.icon, - color: profile.color, - name: profile.overrideName ? profile.profileName : undefined, - cwd - }; - } - - // A shell launch config was provided - if (shellLaunchConfigOrProfile) { - if (cwd) { - shellLaunchConfigOrProfile.cwd = cwd; - } - return shellLaunchConfigOrProfile; - } - - // Return empty shell launch config - return {}; -} - class TerminalEditorStyle extends Themable { private _styleElement: HTMLElement; diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index 582663a2dcb..11a4600274b 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -4,41 +4,103 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual } from 'assert'; -import { ITerminalProfile } from 'vs/platform/terminal/common/terminal'; -import { convertProfileToShellLaunchConfig } from 'vs/workbench/contrib/terminal/browser/terminalService'; +import { IShellLaunchConfig, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; +import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { URI } from 'vs/base/common/uri'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Emitter } from 'vs/base/common/event'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; + +class TestTerminalInstanceHost implements ITerminalInstanceHost { + activeInstance: ITerminalInstance | undefined = undefined; + instances: readonly ITerminalInstance[] = []; + onDidChangeInstances = new Emitter().event; + onDidDisposeInstance = new Emitter().event; + onDidChangeActiveInstance = new Emitter().event; + onDidFocusInstance = new Emitter().event; + setActiveInstance(instance: ITerminalInstance): void { + throw new Error('Method not implemented.'); + } + getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { + throw new Error('Method not implemented.'); + } +} + +class TestTerminalService extends TerminalService { + convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { + return this._convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile, cwd); + } +} suite('Workbench - TerminalService', () => { + let instantiationService: TestInstantiationService; + let terminalService: TestTerminalService; + + setup(async () => { + const configurationService = new TestConfigurationService({ + terminal: { + integrated: { + fontWeight: 'normal' + } + } + }); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IConfigurationService, configurationService); + instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService)); + instantiationService.stub(ILifecycleService, new TestLifecycleService()); + instantiationService.stub(IThemeService, new TestThemeService()); + instantiationService.stub(ITerminalEditorService, new TestTerminalInstanceHost()); + instantiationService.stub(ITerminalGroupService, new TestTerminalInstanceHost()); + instantiationService.stub(ITerminalGroupService, 'onDidChangeActiveGroup', new Emitter().event); + instantiationService.stub(ITerminalInstanceService, {}); + instantiationService.stub(ITerminalInstanceService, 'onDidCreateInstance', new Emitter().event); + instantiationService.stub(ITerminalProfileService, {}); + instantiationService.stub(ITerminalProfileService, 'onDidChangeAvailableProfiles', new Emitter().event); + + terminalService = instantiationService.createInstance(TestTerminalService); + instantiationService.stub(ITerminalService, terminalService); + }); + suite('convertProfileToShellLaunchConfig', () => { test('should return an empty shell launch config when undefined is provided', () => { - deepStrictEqual(convertProfileToShellLaunchConfig(), {}); - deepStrictEqual(convertProfileToShellLaunchConfig(undefined), {}); + deepStrictEqual(terminalService.convertProfileToShellLaunchConfig(), {}); + deepStrictEqual(terminalService.convertProfileToShellLaunchConfig(undefined), {}); }); test('should return the same shell launch config when provided', () => { deepStrictEqual( - convertProfileToShellLaunchConfig({}), + terminalService.convertProfileToShellLaunchConfig({}), {} ); deepStrictEqual( - convertProfileToShellLaunchConfig({ executable: '/foo' }), + terminalService.convertProfileToShellLaunchConfig({ executable: '/foo' }), { executable: '/foo' } ); deepStrictEqual( - convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar', args: ['a', 'b'] }), + terminalService.convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar', args: ['a', 'b'] }), { executable: '/foo', cwd: '/bar', args: ['a', 'b'] } ); deepStrictEqual( - convertProfileToShellLaunchConfig({ executable: '/foo' }, '/bar'), + terminalService.convertProfileToShellLaunchConfig({ executable: '/foo' }, '/bar'), { executable: '/foo', cwd: '/bar' } ); deepStrictEqual( - convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar' }, '/baz'), + terminalService.convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar' }, '/baz'), { executable: '/foo', cwd: '/baz' } ); }); test('should convert a provided profile to a shell launch config', () => { deepStrictEqual( - convertProfileToShellLaunchConfig({ + terminalService.convertProfileToShellLaunchConfig({ profileName: 'abc', path: '/foo', isDefault: true @@ -55,7 +117,7 @@ suite('Workbench - TerminalService', () => { ); const icon = URI.file('/icon'); deepStrictEqual( - convertProfileToShellLaunchConfig({ + terminalService.convertProfileToShellLaunchConfig({ profileName: 'abc', path: '/foo', isDefault: true, @@ -77,7 +139,7 @@ suite('Workbench - TerminalService', () => { }); test('should respect overrideName in profile', () => { deepStrictEqual( - convertProfileToShellLaunchConfig({ + terminalService.convertProfileToShellLaunchConfig({ profileName: 'abc', path: '/foo', isDefault: true, From bdc7b61a662542a044ab6740ad821fc53b499a8e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 06:24:24 -0800 Subject: [PATCH 166/330] Add terminal test services, tidy up terminal service test init Part of #136060 --- .../test/browser/terminalService.test.ts | 39 ++------ .../test/browser/workbenchTestServices.ts | 91 ++++++++++++++++++- 2 files changed, 97 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index 11a4600274b..c5f8dfb6157 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -12,29 +12,13 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { Emitter } from 'vs/base/common/event'; +import { TestLifecycleService, TestTerminalEditorService, TestTerminalGroupService, TestTerminalInstanceService, TestTerminalProfileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; -class TestTerminalInstanceHost implements ITerminalInstanceHost { - activeInstance: ITerminalInstance | undefined = undefined; - instances: readonly ITerminalInstance[] = []; - onDidChangeInstances = new Emitter().event; - onDidDisposeInstance = new Emitter().event; - onDidChangeActiveInstance = new Emitter().event; - onDidFocusInstance = new Emitter().event; - setActiveInstance(instance: ITerminalInstance): void { - throw new Error('Method not implemented.'); - } - getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { - throw new Error('Method not implemented.'); - } -} - class TestTerminalService extends TerminalService { convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { return this._convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile, cwd); @@ -46,26 +30,21 @@ suite('Workbench - TerminalService', () => { let terminalService: TestTerminalService; setup(async () => { - const configurationService = new TestConfigurationService({ + instantiationService = new TestInstantiationService(); + instantiationService.stub(IConfigurationService, new TestConfigurationService({ terminal: { integrated: { fontWeight: 'normal' } } - }); - - instantiationService = new TestInstantiationService(); - instantiationService.stub(IConfigurationService, configurationService); + })); instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IThemeService, new TestThemeService()); - instantiationService.stub(ITerminalEditorService, new TestTerminalInstanceHost()); - instantiationService.stub(ITerminalGroupService, new TestTerminalInstanceHost()); - instantiationService.stub(ITerminalGroupService, 'onDidChangeActiveGroup', new Emitter().event); - instantiationService.stub(ITerminalInstanceService, {}); - instantiationService.stub(ITerminalInstanceService, 'onDidCreateInstance', new Emitter().event); - instantiationService.stub(ITerminalProfileService, {}); - instantiationService.stub(ITerminalProfileService, 'onDidChangeAvailableProfiles', new Emitter().event); + instantiationService.stub(ITerminalEditorService, new TestTerminalEditorService()); + instantiationService.stub(ITerminalGroupService, new TestTerminalGroupService()); + instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); + instantiationService.stub(ITerminalProfileService, new TestTerminalProfileService()); terminalService = instantiationService.createInstance(TestTerminalService); instantiationService.stub(ITerminalService, terminalService); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index d908a25531e..11287b9704a 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -122,10 +122,10 @@ import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEd import { IEnterWorkspaceResult, IRecent, IRecentlyOpened, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { TestWorkspaceTrustManagementService, TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; -import { IShellLaunchConfig, ITerminalProfile, TerminalLocation, TerminalShellType } from 'vs/platform/terminal/common/terminal'; -import { ICreateTerminalOptions, ITerminalInstance, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IExtensionTerminalProfile, IShellLaunchConfig, ITerminalProfile, TerminalLocation, TerminalShellType } from 'vs/platform/terminal/common/terminal'; +import { ICreateTerminalOptions, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { assertIsDefined, isArray } from 'vs/base/common/types'; -import { IShellLaunchConfigResolveOptions, ITerminalBackend, ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRegisterContributedProfileArgs, IShellLaunchConfigResolveOptions, ITerminalBackend, ITerminalProfileProvider, ITerminalProfileResolverService, ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; import { EditorResolverService } from 'vs/workbench/services/editor/browser/editorResolverService'; import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files'; import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; @@ -141,6 +141,9 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { IPaneCompositePart, IPaneCompositeSelectorPart } from 'vs/workbench/browser/parts/paneCompositePart'; import { ILanguageConfigurationService } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; +import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; +import { DeserializedTerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorSerializer'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -1732,6 +1735,88 @@ export class TestTerminalInstanceService implements ITerminalInstanceService { getBackend(remoteAuthority?: string): ITerminalBackend | undefined { throw new Error('Method not implemented.'); } } +export class TestTerminalEditorService implements ITerminalEditorService { + _serviceBrand: undefined; + activeInstance: ITerminalInstance | undefined; + instances: readonly ITerminalInstance[] = []; + onDidDisposeInstance = Event.None; + onDidFocusInstance = Event.None; + onDidChangeActiveInstance = Event.None; + onDidChangeInstances = Event.None; + openEditor(instance: ITerminalInstance, editorOptions?: TerminalEditorLocation): Promise { throw new Error('Method not implemented.'); } + detachActiveEditorInstance(): ITerminalInstance { throw new Error('Method not implemented.'); } + detachInstance(instance: ITerminalInstance): void { throw new Error('Method not implemented.'); } + splitInstance(instanceToSplit: ITerminalInstance, shellLaunchConfig?: IShellLaunchConfig): ITerminalInstance { throw new Error('Method not implemented.'); } + revealActiveEditor(preserveFocus?: boolean): void { throw new Error('Method not implemented.'); } + resolveResource(instance: ITerminalInstance | URI): URI { throw new Error('Method not implemented.'); } + reviveInput(deserializedInput: DeserializedTerminalEditorInput): TerminalEditorInput { throw new Error('Method not implemented.'); } + getInputFromResource(resource: URI): TerminalEditorInput { throw new Error('Method not implemented.'); } + setActiveInstance(instance: ITerminalInstance): void { throw new Error('Method not implemented.'); } + getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { throw new Error('Method not implemented.'); } + focusFindWidget(): void { throw new Error('Method not implemented.'); } + hideFindWidget(): void { throw new Error('Method not implemented.'); } + getFindState(): FindReplaceState { throw new Error('Method not implemented.'); } + findNext(): void { throw new Error('Method not implemented.'); } + findPrevious(): void { throw new Error('Method not implemented.'); } +} + +export class TestTerminalGroupService implements ITerminalGroupService { + _serviceBrand: undefined; + activeInstance: ITerminalInstance | undefined; + instances: readonly ITerminalInstance[] = []; + groups: readonly ITerminalGroup[] = []; + activeGroup: ITerminalGroup | undefined; + activeGroupIndex: number = 0; + onDidChangeActiveGroup = Event.None; + onDidDisposeGroup = Event.None; + onDidChangeGroups = Event.None; + onDidChangePanelOrientation = Event.None; + onDidDisposeInstance = Event.None; + onDidFocusInstance = Event.None; + onDidChangeActiveInstance = Event.None; + onDidChangeInstances = Event.None; + createGroup(instance?: any): ITerminalGroup { throw new Error('Method not implemented.'); } + getGroupForInstance(instance: ITerminalInstance): ITerminalGroup | undefined { throw new Error('Method not implemented.'); } + moveGroup(source: ITerminalInstance, target: ITerminalInstance): void { throw new Error('Method not implemented.'); } + moveGroupToEnd(source: ITerminalInstance): void { throw new Error('Method not implemented.'); } + moveInstance(source: ITerminalInstance, target: ITerminalInstance, side: 'before' | 'after'): void { throw new Error('Method not implemented.'); } + unsplitInstance(instance: ITerminalInstance): void { throw new Error('Method not implemented.'); } + joinInstances(instances: ITerminalInstance[]): void { throw new Error('Method not implemented.'); } + instanceIsSplit(instance: ITerminalInstance): boolean { throw new Error('Method not implemented.'); } + getGroupLabels(): string[] { throw new Error('Method not implemented.'); } + setActiveGroupByIndex(index: number): void { throw new Error('Method not implemented.'); } + setActiveGroupToNext(): void { throw new Error('Method not implemented.'); } + setActiveGroupToPrevious(): void { throw new Error('Method not implemented.'); } + setActiveInstanceByIndex(terminalIndex: number): void { throw new Error('Method not implemented.'); } + setContainer(container: HTMLElement): void { throw new Error('Method not implemented.'); } + showPanel(focus?: boolean): Promise { throw new Error('Method not implemented.'); } + hidePanel(): void { throw new Error('Method not implemented.'); } + focusTabs(): void { throw new Error('Method not implemented.'); } + showTabs(): void { throw new Error('Method not implemented.'); } + setActiveInstance(instance: ITerminalInstance): void { throw new Error('Method not implemented.'); } + getInstanceFromResource(resource: URI | undefined): ITerminalInstance | undefined { throw new Error('Method not implemented.'); } + focusFindWidget(): void { throw new Error('Method not implemented.'); } + hideFindWidget(): void { throw new Error('Method not implemented.'); } + getFindState(): FindReplaceState { throw new Error('Method not implemented.'); } + findNext(): void { throw new Error('Method not implemented.'); } + findPrevious(): void { throw new Error('Method not implemented.'); } +} + +export class TestTerminalProfileService implements ITerminalProfileService { + _serviceBrand: undefined; + availableProfiles: ITerminalProfile[] = []; + contributedProfiles: IExtensionTerminalProfile[] = []; + profilesReady: Promise = Promise.resolve(); + onDidChangeAvailableProfiles = Event.None; + getPlatformKey(): Promise { throw new Error('Method not implemented.'); } + refreshAvailableProfiles(): void { throw new Error('Method not implemented.'); } + getDefaultProfileName(): string | undefined { throw new Error('Method not implemented.'); } + getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise { throw new Error('Method not implemented.'); } + registerContributedProfile(args: IRegisterContributedProfileArgs): Promise { throw new Error('Method not implemented.'); } + getContributedProfileProvider(extensionIdentifier: string, id: string): ITerminalProfileProvider | undefined { throw new Error('Method not implemented.'); } + registerTerminalProfileProvider(extensionIdentifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable { throw new Error('Method not implemented.'); } +} + export class TestTerminalProfileResolverService implements ITerminalProfileResolverService { _serviceBrand: undefined; defaultProfileName = ''; From 35a8a611406d1e2223d7ac7dda06a5d86f3be0f6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Nov 2021 15:43:48 +0100 Subject: [PATCH 167/330] Fix #137239 --- .../workbench/api/browser/viewsExtensionPoint.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index fbeeb50326f..ff95e5d8b02 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -7,6 +7,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as resources from 'vs/base/common/resources'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -310,12 +311,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } for (let descriptor of viewsContainersDescriptors) { - if (typeof descriptor.id !== 'string') { - collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + if (typeof descriptor.id !== 'string' && isFalsyOrWhitespace(descriptor.id)) { + collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); return false; } if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { - collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string` with non-empty value. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); return false; } if (typeof descriptor.title !== 'string') { @@ -326,6 +327,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution { collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); return false; } + if (isFalsyOrWhitespace(descriptor.title)) { + collector.warn(localize('requirenonemptystring', "property `{0}` is mandatory and must be of type `string` with non-empty value", 'title')); + return true; + } } return true; @@ -337,7 +342,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution { const icon = themeIcon || resources.joinPath(extension.extensionLocation, descriptor.icon); const id = `workbench.view.extension.${descriptor.id}`; - const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier, location); + const title = descriptor.title || id; + const viewContainer = this.registerCustomViewContainer(id, title, icon, order++, extension.identifier, location); // Move those views that belongs to this container if (existingViewContainers.length) { From 9f1e991680ee735b35063e1dced1612a4131daa7 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 17 Nov 2021 16:14:48 +0100 Subject: [PATCH 168/330] [json] update diagnostics when schema changes. Fixes #137309 --- extensions/json-language-features/server/src/jsonServer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 2fcaa0a82e6..ffca9f40153 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -255,7 +255,11 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // A schema has changed connection.onNotification(SchemaContentChangeNotification.type, uri => { - languageService.resetSchema(uri); + if (languageService.resetSchema(uri)) { + for (const doc of documents.all()) { + triggerValidation(doc); + } + } }); // Retry schema validation on all open documents From e84d16b74612a5acb9c1588b6247133240dc2bc8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 07:14:49 -0800 Subject: [PATCH 169/330] Add unit tests for safeDisposeTerminal Part of #136060 --- .../terminal/browser/terminalService.ts | 4 +- .../test/browser/terminalService.test.ts | 142 +++++++++++++++++- 2 files changed, 137 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index a7eda50668c..0895d2d7eed 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -335,8 +335,8 @@ export class TerminalService implements ITerminalService { instance.hasChildProcesses && (this.configHelper.config.confirmOnKill === 'panel' || this.configHelper.config.confirmOnKill === 'always')) { - const notConfirmed = await this._showTerminalCloseConfirmation(true); - if (notConfirmed) { + const veto = await this._showTerminalCloseConfirmation(true); + if (veto) { return; } } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index c5f8dfb6157..cc32143ac41 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { deepStrictEqual } from 'assert'; -import { IShellLaunchConfig, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; +import { deepStrictEqual, fail } from 'assert'; +import { IShellLaunchConfig, ITerminalProfile, TerminalLocation } from 'vs/platform/terminal/common/terminal'; import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { URI } from 'vs/base/common/uri'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -13,11 +13,13 @@ import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyServ import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestLifecycleService, TestTerminalEditorService, TestTerminalGroupService, TestTerminalInstanceService, TestTerminalProfileService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { ITerminalEditorService, ITerminalGroupService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; class TestTerminalService extends TerminalService { convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { @@ -25,19 +27,24 @@ class TestTerminalService extends TerminalService { } } -suite('Workbench - TerminalService', () => { +suite.only('Workbench - TerminalService', () => { let instantiationService: TestInstantiationService; let terminalService: TestTerminalService; + let configurationService: TestConfigurationService; + let dialogService: TestDialogService; setup(async () => { - instantiationService = new TestInstantiationService(); - instantiationService.stub(IConfigurationService, new TestConfigurationService({ + dialogService = new TestDialogService(); + configurationService = new TestConfigurationService({ terminal: { integrated: { fontWeight: 'normal' } } - })); + }); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IThemeService, new TestThemeService()); @@ -45,6 +52,7 @@ suite('Workbench - TerminalService', () => { instantiationService.stub(ITerminalGroupService, new TestTerminalGroupService()); instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); instantiationService.stub(ITerminalProfileService, new TestTerminalProfileService()); + instantiationService.stub(IDialogService, dialogService); terminalService = instantiationService.createInstance(TestTerminalService); instantiationService.stub(ITerminalService, terminalService); @@ -136,4 +144,124 @@ suite('Workbench - TerminalService', () => { ); }); }); + + suite('safeDisposeTerminal', () => { + test('should not show prompt when confirmOnKill is never', async () => { + configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'never'); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Editor, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + }); + test('should not show prompt when any terminal editor is closed (handled by editor itself)', async () => { + configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'editor'); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Editor, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'always'); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Editor, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + }); + test('should not show prompt when confirmOnKill is editor and panel terminal is closed', async () => { + configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'editor'); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + }); + test('should show prompt when confirmOnKill is panel and panel terminal is closed', async () => { + configurationService.setUserConfiguration('terminal', { integrated: { confirmOnKill: 'panel' } }); + configurationService.onDidChangeConfigurationEmitter.fire({ affectsConfiguration: () => true } as any); + // No child process cases + dialogService.setConfirmResult({ confirmed: false }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + dispose: () => r() + } as Partial as any); + }); + dialogService.setConfirmResult({ confirmed: true }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + dispose: () => r() + } as Partial as any); + }); + // Child process cases + dialogService.setConfirmResult({ confirmed: false }); + await terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + dispose: () => fail() + } as Partial as any); + dialogService.setConfirmResult({ confirmed: true }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + }); + test('should show prompt when confirmOnKill is always and panel terminal is closed', async () => { + configurationService.setUserConfiguration('terminal', { integrated: { confirmOnKill: 'always' } }); + configurationService.onDidChangeConfigurationEmitter.fire({ affectsConfiguration: () => true } as any); + // No child process cases + dialogService.setConfirmResult({ confirmed: false }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + dispose: () => r() + } as Partial as any); + }); + dialogService.setConfirmResult({ confirmed: true }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + dispose: () => r() + } as Partial as any); + }); + // Child process cases + dialogService.setConfirmResult({ confirmed: false }); + await terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + dispose: () => fail() + } as Partial as any); + dialogService.setConfirmResult({ confirmed: true }); + await new Promise(r => { + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + dispose: () => r() + } as Partial as any); + }); + }); + }); }); From 6af8133890f5e30ba90d8c907a8992f826c91115 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 07:20:17 -0800 Subject: [PATCH 170/330] Fix remote unit tests --- .../contrib/terminal/test/browser/terminalService.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index cc32143ac41..0a92dcb70ff 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -20,6 +20,8 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService import { ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { TestRemoteAgentService } from 'vs/workbench/services/remote/test/common/testServices'; class TestTerminalService extends TerminalService { convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { @@ -52,6 +54,8 @@ suite.only('Workbench - TerminalService', () => { instantiationService.stub(ITerminalGroupService, new TestTerminalGroupService()); instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); instantiationService.stub(ITerminalProfileService, new TestTerminalProfileService()); + instantiationService.stub(IRemoteAgentService, new TestRemoteAgentService()); + instantiationService.stub(IRemoteAgentService, 'getConnection', null); instantiationService.stub(IDialogService, dialogService); terminalService = instantiationService.createInstance(TestTerminalService); From e28da46f778e05684b52f648c34324176a817480 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 09:05:48 -0800 Subject: [PATCH 171/330] Remove only --- .../contrib/terminal/test/browser/terminalService.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index 0a92dcb70ff..e37efa6c897 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -29,7 +29,7 @@ class TestTerminalService extends TerminalService { } } -suite.only('Workbench - TerminalService', () => { +suite('Workbench - TerminalService', () => { let instantiationService: TestInstantiationService; let terminalService: TestTerminalService; let configurationService: TestConfigurationService; From b64378e0d9b04520eec26c21e980e89b983e7914 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 17 Nov 2021 09:41:35 -0800 Subject: [PATCH 172/330] Testing out panel alignment setting (#137009) * gridview support negative indices * support panel alignment --- src/vs/base/browser/ui/grid/gridview.ts | 56 ++--- src/vs/workbench/browser/layout.ts | 209 +++++++++++++++--- .../browser/workbench.contribution.ts | 7 + 3 files changed, 209 insertions(+), 63 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index b80ffaecb29..113822bdffd 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -10,7 +10,7 @@ import { equals as arrayEquals, tail2 as tail } from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event, Relay } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { clamp } from 'vs/base/common/numbers'; +import { rot } from 'vs/base/common/numbers'; import { isUndefined } from 'vs/base/common/types'; import 'vs/css!./gridview'; @@ -235,6 +235,14 @@ function fromAbsoluteBoundarySashes(sashes: IBoundarySashes, orientation: Orient } } +function validateIndex(index: number, numChildren: number): number { + if (Math.abs(index) > numChildren) { + throw new Error('Invalid index'); + } + + return rot(index, numChildren + 1); +} + class BranchNode implements ISplitView, IDisposable { readonly element: HTMLElement; @@ -474,9 +482,7 @@ class BranchNode implements ISplitView, IDisposable { } addChild(node: Node, size: number | Sizing, index: number, skipLayout?: boolean): void { - if (index < 0 || index > this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); this.splitview.addView(node, size, index, skipLayout); this._addChild(node, index); @@ -511,9 +517,7 @@ class BranchNode implements ISplitView, IDisposable { } removeChild(index: number, sizing?: Sizing): void { - if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); this.splitview.removeView(index, sizing); this._removeChild(index); @@ -543,16 +547,13 @@ class BranchNode implements ISplitView, IDisposable { } moveChild(from: number, to: number): void { + from = validateIndex(from, this.children.length); + to = validateIndex(to, this.children.length); + if (from === to) { return; } - if (from < 0 || from >= this.children.length) { - throw new Error('Invalid from index'); - } - - to = clamp(to, 0, this.children.length); - if (from < to) { to--; } @@ -566,16 +567,13 @@ class BranchNode implements ISplitView, IDisposable { } swapChildren(from: number, to: number): void { + from = validateIndex(from, this.children.length); + to = validateIndex(to, this.children.length); + if (from === to) { return; } - if (from < 0 || from >= this.children.length) { - throw new Error('Invalid from index'); - } - - to = clamp(to, 0, this.children.length); - this.splitview.swapViews(from, to); // swap boundary sashes @@ -589,9 +587,7 @@ class BranchNode implements ISplitView, IDisposable { } resizeChild(index: number, size: number): void { - if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); this.splitview.resizeView(index, size); } @@ -609,25 +605,19 @@ class BranchNode implements ISplitView, IDisposable { } getChildSize(index: number): number { - if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); return this.splitview.getViewSize(index); } isChildVisible(index: number): boolean { - if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); return this.splitview.isViewVisible(index); } setChildVisible(index: number, visible: boolean): void { - if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); if (this.splitview.isViewVisible(index) === visible) { return; @@ -637,9 +627,7 @@ class BranchNode implements ISplitView, IDisposable { } getChildCachedVisibleSize(index: number): number | undefined { - if (index < 0 || index >= this.children.length) { - throw new Error('Invalid index'); - } + index = validateIndex(index, this.children.length); return this.splitview.getViewCachedVisibleSize(index); } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 58ff7dc755b..9957a797e02 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -26,7 +26,7 @@ import { IEditor } from 'vs/editor/common/editorCommon'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { SerializableGrid, ISerializableView, ISerializedGrid, Orientation, ISerializedNode, ISerializedLeafNode, Direction, IViewSize } from 'vs/base/browser/ui/grid/grid'; +import { SerializableGrid, ISerializableView, ISerializedGrid, Orientation, ISerializedNode, ISerializedLeafNode, Direction, IViewSize, Sizing } from 'vs/base/browser/ui/grid/grid'; import { Part } from 'vs/workbench/browser/part'; import { IStatusbarService } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IFileService } from 'vs/platform/files/common/files'; @@ -51,6 +51,8 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import { AuxiliaryBarPart, AUXILIARYBAR_ENABLED } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart'; +type PanelAlignment = 'left' | 'center' | 'right' | 'justified'; + export enum Settings { ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', STATUSBAR_VISIBLE = 'workbench.statusBar.visible', @@ -58,6 +60,7 @@ export enum Settings { SIDEBAR_POSITION = 'workbench.sideBar.location', PANEL_POSITION = 'workbench.panel.defaultLocation', PANEL_OPENS_MAXIMIZED = 'workbench.panel.opensMaximized', + PANEL_ALIGNMENT = 'workbench.experimental.panel.alignment', ZEN_MODE_RESTORE = 'zenMode.restore', } @@ -217,7 +220,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi lastNonMaximizedWidth: 300, lastNonMaximizedHeight: 300, wasLastMaximized: false, - panelToRestore: undefined as string | undefined + panelToRestore: undefined as string | undefined, + alignment: 'center' as PanelAlignment }, auxiliaryBar: { @@ -396,6 +400,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Panel position this.updatePanelPosition(); + + // Panel alignment + const newPanelAlignmentValue = this.configurationService.getValue(Settings.PANEL_ALIGNMENT) ?? 'center'; + if (newPanelAlignmentValue !== this.state.panel.alignment) { + this.setPanelAlignment(newPanelAlignmentValue, skipLayout); + } + + if (!this.state.zenMode.active) { // Statusbar visibility @@ -451,17 +463,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.sideBar.width = this.workbenchGrid.getViewSize(this.sideBarPartView).width; } - if (position === Position.LEFT) { - this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, 0]); - this.workbenchGrid.moveViewTo(this.sideBarPartView, [2, 1]); - this.workbenchGrid.moveViewTo(this.auxiliaryBarPartView, [2, 10]); - } else { - this.workbenchGrid.moveViewTo(this.auxiliaryBarPartView, [2, 0]); - this.workbenchGrid.moveViewTo(this.sideBarPartView, [2, 10]); - this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, 10]); - } - - this.layout(); + // Move activity bar, side bar, and side panel + this.adjustPartPositions(position, this.state.panel.alignment); + // this.layout(); } private updateWindowBorder(skipLayout: boolean = false) { @@ -556,6 +560,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Panel position this.updatePanelPosition(); + this.state.panel.alignment = this.configurationService.getValue(Settings.PANEL_ALIGNMENT) ?? 'center'; + // Panel to restore if (!this.state.panel.hidden) { let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Panel)?.id); @@ -1567,6 +1573,59 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return viewContainerModel.activeViewDescriptors.length >= 1; } + private adjustPartPositions(sideBarPosition: Position, panelAlignment: PanelAlignment): void { + // Move activity bar, side bar, and side panel + const sideBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); + const auxiliaryBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); + const preMoveSideBarSize = this.state.sideBar.hidden ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) ?? this.sideBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.sideBarPartView).width; + const preMoveAuxiliaryBarSize = this.state.auxiliaryBar.hidden ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.auxiliaryBarPartView) ?? this.auxiliaryBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).width; + + if (sideBarPosition === Position.LEFT) { + this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, 0]); + this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarNextToEditor ? this.editorPartView : this.activityBarPartView, sideBarNextToEditor ? Direction.Left : Direction.Right); + if (auxiliaryBarNextToEditor) { + this.workbenchGrid.moveView(this.auxiliaryBarPartView, preMoveAuxiliaryBarSize, this.editorPartView, Direction.Right); + } else { + this.workbenchGrid.moveViewTo(this.auxiliaryBarPartView, [2, -1]); + } + } else { + this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, -1]); + this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarNextToEditor ? this.editorPartView : this.activityBarPartView, sideBarNextToEditor ? Direction.Right : Direction.Left); + if (auxiliaryBarNextToEditor) { + this.workbenchGrid.moveView(this.auxiliaryBarPartView, preMoveAuxiliaryBarSize, this.editorPartView, Direction.Left); + } else { + this.workbenchGrid.moveViewTo(this.auxiliaryBarPartView, [2, 0]); + } + } + + // Moving views in the grid can cause them to re-distribute sizing unnecessarily + // Resize visible parts to the width they were before the operation + if (this.isVisible(Parts.SIDEBAR_PART)) { + this.workbenchGrid.resizeView(this.sideBarPartView, { + height: this.workbenchGrid.getViewSize(this.sideBarPartView).height, + width: preMoveSideBarSize as number + }); + } + + if (this.isVisible(Parts.AUXILIARYBAR_PART)) { + this.workbenchGrid.resizeView(this.auxiliaryBarPartView, { + height: this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).height, + width: preMoveAuxiliaryBarSize as number + }); + } + } + + private setPanelAlignment(alignment: PanelAlignment, skipLayout?: boolean): void { + this.state.panel.alignment = alignment; + + // Panel alignment only applies to a panel in the bottom position + if (this.state.panel.position !== Position.BOTTOM) { + return; + } + + this.adjustPartPositions(this.state.sideBar.position, alignment); + } + private setPanelHidden(hidden: boolean, skipLayout?: boolean): void { const wasHidden = this.state.panel.hidden; this.state.panel.hidden = hidden; @@ -1909,15 +1968,107 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } - private arrangeEditorNodes(editorNode: ISerializedNode, panelNode: ISerializedNode, editorSectionWidth: number): ISerializedNode[] { - switch (this.state.panel.position) { - case Position.BOTTOM: - return [{ type: 'branch', data: [editorNode, panelNode], size: editorSectionWidth }]; - case Position.RIGHT: - return [editorNode, panelNode]; - case Position.LEFT: - return [panelNode, editorNode]; + private arrangeEditorNodes(nodes: { editor: ISerializedNode, sideBar?: ISerializedNode, auxiliaryBar?: ISerializedNode }, availableHeight: number, availableWidth: number): ISerializedNode { + if (!nodes.sideBar && !nodes.auxiliaryBar) { + nodes.editor.size = availableHeight; + return nodes.editor; } + + const result = [nodes.editor]; + nodes.editor.size = availableWidth; + if (nodes.sideBar) { + if (this.state.sideBar.position === Position.LEFT) { + result.splice(0, 0, nodes.sideBar); + } else { + result.push(nodes.sideBar); + } + + nodes.editor.size -= this.state.sideBar.hidden ? 0 : nodes.sideBar.size; + } + + if (nodes.auxiliaryBar) { + if (this.state.sideBar.position === Position.RIGHT) { + result.splice(0, 0, nodes.auxiliaryBar); + } else { + result.push(nodes.auxiliaryBar); + } + + nodes.editor.size -= this.state.auxiliaryBar.hidden ? 0 : nodes.auxiliaryBar.size; + } + + return { + type: 'branch', + data: result, + size: availableHeight + }; + } + + private arrangeMiddleSectionNodes(nodes: { editor: ISerializedNode, panel: ISerializedNode, activityBar: ISerializedNode, sideBar: ISerializedNode, auxiliaryBar: ISerializedNode }, availableWidth: number, availableHeight: number): ISerializedNode[] { + const activityBarSize = this.state.activityBar.hidden ? 0 : nodes.activityBar.size; + const sideBarSize = this.state.sideBar.hidden ? 0 : nodes.sideBar.size; + const auxiliaryBarSize = this.state.auxiliaryBar.hidden ? 0 : nodes.auxiliaryBar.size; + const panelSize = this.state.panel.hidden ? 0 : nodes.panel.size; + + const result = [] as ISerializedNode[]; + if (this.state.panel.position !== Position.BOTTOM) { + result.push(nodes.editor); + nodes.editor.size = availableWidth - activityBarSize - sideBarSize - panelSize - auxiliaryBarSize; + if (this.state.panel.position === Position.RIGHT) { + result.push(nodes.panel); + } else { + result.splice(0, 0, nodes.panel); + } + + if (this.state.sideBar.position === Position.LEFT) { + result.push(nodes.auxiliaryBar); + result.splice(0, 0, nodes.sideBar); + result.splice(0, 0, nodes.activityBar); + } else { + result.splice(0, 0, nodes.auxiliaryBar); + result.push(nodes.sideBar); + result.push(nodes.activityBar); + } + } else { + const panelAlignment = this.state.panel.alignment; + const sideBarPosition = this.state.sideBar.position; + const sideBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); + const auxiliaryBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); + + const editorSectionWidth = availableWidth - activityBarSize - (sideBarNextToEditor ? 0 : sideBarSize) - (auxiliaryBarNextToEditor ? 0 : auxiliaryBarSize); + result.push({ + type: 'branch', + data: [this.arrangeEditorNodes({ + editor: nodes.editor, + sideBar: sideBarNextToEditor ? nodes.sideBar : undefined, + auxiliaryBar: auxiliaryBarNextToEditor ? nodes.auxiliaryBar : undefined + }, availableHeight - panelSize, editorSectionWidth), nodes.panel], + size: editorSectionWidth + }); + + if (!sideBarNextToEditor) { + if (sideBarPosition === Position.LEFT) { + result.splice(0, 0, nodes.sideBar); + } else { + result.push(nodes.sideBar); + } + } + + if (!auxiliaryBarNextToEditor) { + if (sideBarPosition === Position.RIGHT) { + result.splice(0, 0, nodes.auxiliaryBar); + } else { + result.push(nodes.auxiliaryBar); + } + } + + if (sideBarPosition === Position.LEFT) { + result.splice(0, 0, nodes.activityBar); + } else { + result.push(nodes.activityBar); + } + } + + return result; } private createGridDescriptor(): ISerializedGrid { @@ -1935,7 +2086,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const statusBarHeight = this.statusBarPartView.minimumHeight; const activityBarWidth = this.activityBarPartView.minimumWidth; const middleSectionHeight = height - titleBarHeight - statusBarHeight; - const editorSectionWidth = width - (this.state.activityBar.hidden ? 0 : activityBarWidth) - (this.state.sideBar.hidden ? 0 : sideBarSize); const activityBarNode: ISerializedLeafNode = { type: 'leaf', @@ -1961,9 +2111,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const editorNode: ISerializedLeafNode = { type: 'leaf', data: { type: Parts.EDITOR_PART }, - size: this.state.panel.position === Position.BOTTOM ? - middleSectionHeight - (this.state.panel.hidden ? 0 : panelSize) : - editorSectionWidth - (this.state.panel.hidden ? 0 : panelSize), + size: 0, // Update based on sibling sizes visible: !this.state.editor.hidden }; @@ -1974,11 +2122,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi visible: !this.state.panel.hidden }; - const editorSectionNode = this.arrangeEditorNodes(editorNode, panelNode, editorSectionWidth); - const middleSection: ISerializedNode[] = this.state.sideBar.position === Position.LEFT - ? [activityBarNode, sideBarNode, ...editorSectionNode, auxiliaryBarNode] - : [auxiliaryBarNode, ...editorSectionNode, sideBarNode, activityBarNode]; + const middleSection: ISerializedNode[] = this.arrangeMiddleSectionNodes({ + activityBar: activityBarNode, + auxiliaryBar: auxiliaryBarNode, + editor: editorNode, + panel: panelNode, + sideBar: sideBarNode + }, width, middleSectionHeight); const result: ISerializedGrid = { root: { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 74a8e47247f..bf60658ac0c 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -367,6 +367,13 @@ const registry = Registry.as(ConfigurationExtensions.Con 'description': localize('auxiliaryBarEnabled', "Controls whether the side panel opposite the side bar is enabled."), 'included': product.quality !== 'stable' }, + 'workbench.experimental.panel.alignment': { + 'type': 'string', + 'enum': ['left', 'center', 'right', 'justified'], + 'default': 'center', + 'description': localize('panelAlignment', "Controls the alignment of the panel (terminal, debug console, output, problems) and whether or not it spans beneath the side bar and side panel."), + 'included': product.quality !== 'stable' + }, } }); From 3d796ced55af8e96421aff2b609383bf418364fa Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 17 Nov 2021 09:54:19 -0800 Subject: [PATCH 173/330] Fix terminal service unit test --- .../test/browser/terminalService.test.ts | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index e37efa6c897..9675e78f901 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -151,7 +151,7 @@ suite('Workbench - TerminalService', () => { suite('safeDisposeTerminal', () => { test('should not show prompt when confirmOnKill is never', async () => { - configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'never'); + setConfirmOnKill(configurationService, 'never'); await new Promise(r => { terminalService.safeDisposeTerminal({ target: TerminalLocation.Editor, @@ -168,7 +168,7 @@ suite('Workbench - TerminalService', () => { }); }); test('should not show prompt when any terminal editor is closed (handled by editor itself)', async () => { - configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'editor'); + setConfirmOnKill(configurationService, 'editor'); await new Promise(r => { terminalService.safeDisposeTerminal({ target: TerminalLocation.Editor, @@ -176,7 +176,7 @@ suite('Workbench - TerminalService', () => { dispose: () => r() } as Partial as any); }); - configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'always'); + setConfirmOnKill(configurationService, 'always'); await new Promise(r => { terminalService.safeDisposeTerminal({ target: TerminalLocation.Editor, @@ -186,7 +186,7 @@ suite('Workbench - TerminalService', () => { }); }); test('should not show prompt when confirmOnKill is editor and panel terminal is closed', async () => { - configurationService.setUserConfiguration('terminal.integrated.confirmOnKill', 'editor'); + setConfirmOnKill(configurationService, 'editor'); await new Promise(r => { terminalService.safeDisposeTerminal({ target: TerminalLocation.Panel, @@ -196,8 +196,7 @@ suite('Workbench - TerminalService', () => { }); }); test('should show prompt when confirmOnKill is panel and panel terminal is closed', async () => { - configurationService.setUserConfiguration('terminal', { integrated: { confirmOnKill: 'panel' } }); - configurationService.onDidChangeConfigurationEmitter.fire({ affectsConfiguration: () => true } as any); + setConfirmOnKill(configurationService, 'panel'); // No child process cases dialogService.setConfirmResult({ confirmed: false }); await new Promise(r => { @@ -232,8 +231,7 @@ suite('Workbench - TerminalService', () => { }); }); test('should show prompt when confirmOnKill is always and panel terminal is closed', async () => { - configurationService.setUserConfiguration('terminal', { integrated: { confirmOnKill: 'always' } }); - configurationService.onDidChangeConfigurationEmitter.fire({ affectsConfiguration: () => true } as any); + setConfirmOnKill(configurationService, 'always'); // No child process cases dialogService.setConfirmResult({ confirmed: false }); await new Promise(r => { @@ -269,3 +267,11 @@ suite('Workbench - TerminalService', () => { }); }); }); + +async function setConfirmOnKill(configurationService: TestConfigurationService, value: 'never' | 'always' | 'panel' | 'editor') { + await configurationService.setUserConfiguration('terminal', { integrated: { confirmOnKill: value } }); + configurationService.onDidChangeConfigurationEmitter.fire({ + affectsConfiguration: () => true, + affectedKeys: ['terminal.integrated.confirmOnKill'] + } as any); +} From dfbaecd969ab50111ea2516e766fb976de39198b Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 17 Nov 2021 10:06:11 -0800 Subject: [PATCH 174/330] have set value on the QuickPick/InputBox simply set the value on the input. Fixes #137279 --- .../src/singlefolder-tests/quickInput.test.ts | 146 ++++++++++++++++++ .../parts/quickinput/browser/quickInput.ts | 9 +- 2 files changed, 148 insertions(+), 7 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts index ba7ce21e32f..bf0da5076b1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts @@ -11,6 +11,7 @@ interface QuickPickExpected { events: string[]; activeItems: string[][]; selectionItems: string[][]; + values: string[]; acceptedItems: { active: string[][]; selection: string[][]; @@ -18,6 +19,15 @@ interface QuickPickExpected { }; } +interface InputBoxExpected { + events: string[]; + values: string[]; + accepted: { + values: string[]; + dispose: boolean[]; + }; +} + suite('vscode API - quick input', function () { teardown(async function () { @@ -35,6 +45,7 @@ suite('vscode API - quick input', function () { events: ['active', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], ['zwei']], selectionItems: [['zwei']], + values: [], acceptedItems: { active: [['zwei']], selection: [['zwei']], @@ -61,6 +72,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'hide'], activeItems: [['zwei']], selectionItems: [['zwei']], + values: [], acceptedItems: { active: [['zwei']], selection: [['zwei']], @@ -87,6 +99,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], ['zwei']], selectionItems: [['eins'], ['eins', 'zwei']], + values: [], acceptedItems: { active: [['zwei']], selection: [['eins', 'zwei']], @@ -117,6 +130,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'selection', 'accept', 'hide'], activeItems: [['eins']], selectionItems: [['zwei'], ['drei']], + values: [], acceptedItems: { active: [['eins'], ['eins']], selection: [['zwei'], ['drei']], @@ -142,6 +156,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'active', 'selection', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], [], ['drei']], selectionItems: [['eins'], [], ['drei']], + values: [], acceptedItems: { active: [['eins'], ['drei']], selection: [['eins'], ['drei']], @@ -163,6 +178,40 @@ suite('vscode API - quick input', function () { .catch(err => done(err)); }); + // NOTE: This test is currently accepting the wrong behavior of #135971 + // so that we can test the fix for #137279. + test('createQuickPick, onDidChangeValue gets triggered', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; + + const quickPick = createQuickPick({ + events: ['active', 'active', 'active', 'active', 'value', 'active', 'active', 'value', 'hide'], + activeItems: [['eins'], ['zwei'], [], ['zwei'], [], ['eins']], + selectionItems: [], + values: ['zwei', ''], + acceptedItems: { + active: [], + selection: [], + dispose: [] + }, + }, (err?: any) => done(err)); + quickPick.items = ['eins', 'zwei'].map(label => ({ label })); + quickPick.show(); + + (async () => { + quickPick.value = 'zwei'; + await timeout(async () => { + quickPick.value = ''; + await timeout(async () => { + quickPick.hide(); + }, 0); + }, 0); + })() + .catch(err => done(err)); + }); + test('createQuickPick, dispose in onDidHide', function (_done) { let done = (err?: any) => { done = () => { }; @@ -248,6 +297,34 @@ suite('vscode API - quick input', function () { quickPick.hide(); await waitForHide(quickPick); }); + + test('createInputBox, onDidChangeValue gets triggered', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; + + const quickPick = createInputBox({ + events: ['value', 'accept', 'hide'], + values: ['zwei'], + accepted: { + values: ['zwei'], + dispose: [true] + }, + }, (err?: any) => done(err)); + quickPick.show(); + + (async () => { + quickPick.value = 'zwei'; + await timeout(async () => { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + await timeout(async () => { + quickPick.hide(); + }, 0); + }, 0); + })() + .catch(err => done(err)); + }); }); function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, record = false) { @@ -316,9 +393,78 @@ function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, } }); + quickPick.onDidChangeValue(value => { + if (record) { + console.log('value'); + return; + } + + try { + eventIndex++; + assert.strictEqual('value', expected.events.shift(), `onDidChangeValue (event ${eventIndex})`); + const expectedValue = expected.values.shift(); + assert.deepStrictEqual(value, expectedValue, `onDidChangeValue event value (event ${eventIndex})`); + } catch (err) { + done(err); + } + }); + return quickPick; } +function createInputBox(expected: InputBoxExpected, done: (err?: any) => void, record = false) { + const inputBox = window.createInputBox(); + let eventIndex = -1; + inputBox.onDidAccept(() => { + if (record) { + console.log('accept'); + return; + } + try { + eventIndex++; + assert.strictEqual('accept', expected.events.shift(), `onDidAccept (event ${eventIndex})`); + const expectedValue = expected.accepted.values.shift(); + assert.deepStrictEqual(inputBox.value, expectedValue, `onDidAccept event value (event ${eventIndex})`); + if (expected.accepted.dispose.shift()) { + inputBox.dispose(); + } + } catch (err) { + done(err); + } + }); + inputBox.onDidHide(() => { + if (record) { + console.log('hide'); + done(); + return; + } + try { + assert.strictEqual('hide', expected.events.shift()); + done(); + } catch (err) { + done(err); + } + }); + + inputBox.onDidChangeValue(value => { + if (record) { + console.log('value'); + return; + } + + try { + eventIndex++; + assert.strictEqual('value', expected.events.shift(), `onDidChangeValue (event ${eventIndex})`); + const expectedValue = expected.values.shift(); + assert.deepStrictEqual(value, expectedValue, `onDidChangeValue event value (event ${eventIndex})`); + } catch (err) { + done(err); + } + }); + + return inputBox; +} + async function timeout(run: () => Promise | T, ms: number): Promise { return new Promise(resolve => setTimeout(() => resolve(run()), ms)); } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 4a9a2db8cd2..4e2fa1ab625 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -487,11 +487,7 @@ class QuickPick extends QuickInput implements IQuickPi } set value(value: string) { - if (this._value !== value) { - this._value = value || ''; - this.update(); - this.onDidChangeValueEmitter.fire(this._value); - } + this.ui.inputBox.value = value ?? ''; } filterValue = (value: string) => value; @@ -1070,8 +1066,7 @@ class InputBox extends QuickInput implements IInputBox { } set value(value: string) { - this._value = value || ''; - this.update(); + this.ui.inputBox.value = value ?? ''; } set valueSelection(valueSelection: Readonly<[number, number]>) { From d24c9eddf84f0ffd35061753566ea41a8fe8aa48 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 17 Nov 2021 10:17:31 -0800 Subject: [PATCH 175/330] Clean providerId --- src/vs/editor/contrib/suggest/suggestController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index ff9075b5a93..42e2561cef1 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -451,9 +451,11 @@ export class SuggestController implements IEditorContribution { fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; }; languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; }; }; - + // _debugDisplayName looks like `vscode.css-language-features(/-:)`, where the last bit is the trigger chars + // normalize it to just the extension ID and lowercase + const providerId = (acceptedSuggestion.item.provider._debugDisplayName ?? 'unknown').split('(', 1)[0].toLowerCase(); this._telemetryService.publicLog2('suggest.acceptedSuggestion', { - providerId: acceptedSuggestion.item.provider._debugDisplayName ?? 'unknown', + providerId, basenameHash: hash(basename(model.uri)).toString(16), languageId: model.getLanguageId(), fileExtension: extname(model.uri), From 8133e1fc5ebb895e9767dc1196740735ae01aafb Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 17 Nov 2021 11:40:05 -0800 Subject: [PATCH 176/330] Revert "have set value on the QuickPick/InputBox simply set the value on the input. Fixes #137279" This reverts commit dfbaecd969ab50111ea2516e766fb976de39198b. --- .../src/singlefolder-tests/quickInput.test.ts | 146 ------------------ .../parts/quickinput/browser/quickInput.ts | 9 +- 2 files changed, 7 insertions(+), 148 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts index bf0da5076b1..ba7ce21e32f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts @@ -11,7 +11,6 @@ interface QuickPickExpected { events: string[]; activeItems: string[][]; selectionItems: string[][]; - values: string[]; acceptedItems: { active: string[][]; selection: string[][]; @@ -19,15 +18,6 @@ interface QuickPickExpected { }; } -interface InputBoxExpected { - events: string[]; - values: string[]; - accepted: { - values: string[]; - dispose: boolean[]; - }; -} - suite('vscode API - quick input', function () { teardown(async function () { @@ -45,7 +35,6 @@ suite('vscode API - quick input', function () { events: ['active', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], ['zwei']], selectionItems: [['zwei']], - values: [], acceptedItems: { active: [['zwei']], selection: [['zwei']], @@ -72,7 +61,6 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'hide'], activeItems: [['zwei']], selectionItems: [['zwei']], - values: [], acceptedItems: { active: [['zwei']], selection: [['zwei']], @@ -99,7 +87,6 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], ['zwei']], selectionItems: [['eins'], ['eins', 'zwei']], - values: [], acceptedItems: { active: [['zwei']], selection: [['eins', 'zwei']], @@ -130,7 +117,6 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'selection', 'accept', 'hide'], activeItems: [['eins']], selectionItems: [['zwei'], ['drei']], - values: [], acceptedItems: { active: [['eins'], ['eins']], selection: [['zwei'], ['drei']], @@ -156,7 +142,6 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'active', 'selection', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], [], ['drei']], selectionItems: [['eins'], [], ['drei']], - values: [], acceptedItems: { active: [['eins'], ['drei']], selection: [['eins'], ['drei']], @@ -178,40 +163,6 @@ suite('vscode API - quick input', function () { .catch(err => done(err)); }); - // NOTE: This test is currently accepting the wrong behavior of #135971 - // so that we can test the fix for #137279. - test('createQuickPick, onDidChangeValue gets triggered', function (_done) { - let done = (err?: any) => { - done = () => { }; - _done(err); - }; - - const quickPick = createQuickPick({ - events: ['active', 'active', 'active', 'active', 'value', 'active', 'active', 'value', 'hide'], - activeItems: [['eins'], ['zwei'], [], ['zwei'], [], ['eins']], - selectionItems: [], - values: ['zwei', ''], - acceptedItems: { - active: [], - selection: [], - dispose: [] - }, - }, (err?: any) => done(err)); - quickPick.items = ['eins', 'zwei'].map(label => ({ label })); - quickPick.show(); - - (async () => { - quickPick.value = 'zwei'; - await timeout(async () => { - quickPick.value = ''; - await timeout(async () => { - quickPick.hide(); - }, 0); - }, 0); - })() - .catch(err => done(err)); - }); - test('createQuickPick, dispose in onDidHide', function (_done) { let done = (err?: any) => { done = () => { }; @@ -297,34 +248,6 @@ suite('vscode API - quick input', function () { quickPick.hide(); await waitForHide(quickPick); }); - - test('createInputBox, onDidChangeValue gets triggered', function (_done) { - let done = (err?: any) => { - done = () => { }; - _done(err); - }; - - const quickPick = createInputBox({ - events: ['value', 'accept', 'hide'], - values: ['zwei'], - accepted: { - values: ['zwei'], - dispose: [true] - }, - }, (err?: any) => done(err)); - quickPick.show(); - - (async () => { - quickPick.value = 'zwei'; - await timeout(async () => { - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - await timeout(async () => { - quickPick.hide(); - }, 0); - }, 0); - })() - .catch(err => done(err)); - }); }); function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, record = false) { @@ -393,78 +316,9 @@ function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, } }); - quickPick.onDidChangeValue(value => { - if (record) { - console.log('value'); - return; - } - - try { - eventIndex++; - assert.strictEqual('value', expected.events.shift(), `onDidChangeValue (event ${eventIndex})`); - const expectedValue = expected.values.shift(); - assert.deepStrictEqual(value, expectedValue, `onDidChangeValue event value (event ${eventIndex})`); - } catch (err) { - done(err); - } - }); - return quickPick; } -function createInputBox(expected: InputBoxExpected, done: (err?: any) => void, record = false) { - const inputBox = window.createInputBox(); - let eventIndex = -1; - inputBox.onDidAccept(() => { - if (record) { - console.log('accept'); - return; - } - try { - eventIndex++; - assert.strictEqual('accept', expected.events.shift(), `onDidAccept (event ${eventIndex})`); - const expectedValue = expected.accepted.values.shift(); - assert.deepStrictEqual(inputBox.value, expectedValue, `onDidAccept event value (event ${eventIndex})`); - if (expected.accepted.dispose.shift()) { - inputBox.dispose(); - } - } catch (err) { - done(err); - } - }); - inputBox.onDidHide(() => { - if (record) { - console.log('hide'); - done(); - return; - } - try { - assert.strictEqual('hide', expected.events.shift()); - done(); - } catch (err) { - done(err); - } - }); - - inputBox.onDidChangeValue(value => { - if (record) { - console.log('value'); - return; - } - - try { - eventIndex++; - assert.strictEqual('value', expected.events.shift(), `onDidChangeValue (event ${eventIndex})`); - const expectedValue = expected.values.shift(); - assert.deepStrictEqual(value, expectedValue, `onDidChangeValue event value (event ${eventIndex})`); - } catch (err) { - done(err); - } - }); - - return inputBox; -} - async function timeout(run: () => Promise | T, ms: number): Promise { return new Promise(resolve => setTimeout(() => resolve(run()), ms)); } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 4e2fa1ab625..4a9a2db8cd2 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -487,7 +487,11 @@ class QuickPick extends QuickInput implements IQuickPi } set value(value: string) { - this.ui.inputBox.value = value ?? ''; + if (this._value !== value) { + this._value = value || ''; + this.update(); + this.onDidChangeValueEmitter.fire(this._value); + } } filterValue = (value: string) => value; @@ -1066,7 +1070,8 @@ class InputBox extends QuickInput implements IInputBox { } set value(value: string) { - this.ui.inputBox.value = value ?? ''; + this._value = value || ''; + this.update(); } set valueSelection(valueSelection: Readonly<[number, number]>) { From e9ca473f62df65f58592a10227c147e933b17003 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 11:42:42 -0800 Subject: [PATCH 177/330] Change jupyter language name to make it more clear that it is for raw JSON, not a notebook --- extensions/ipynb/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index b31a322dff3..39c35edbb4f 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -32,7 +32,7 @@ { "id": "jupyter", "aliases": [ - "Jupyter" + "Jupyter (JSON)" ], "extensions": [ ".ipynb" From af87dc4a8226bf49e55b621c0599bb6e2f18d3b4 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 17 Nov 2021 12:35:35 -0800 Subject: [PATCH 178/330] Notebook List View Cell rendering. --- .../notebook/browser/notebook.layout.md | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index d1908fd19e9..28e65b0c836 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -4,9 +4,27 @@ The notebook editor is a virtualized list view rendered in two contexts (mainfra ## Notebook model resolution - ![arch](https://user-images.githubusercontent.com/876920/141845889-abe0384e-0093-4b08-831a-04424a4b8101.png) +## Viewport rendering + +The rendering of notebook list view is a "guess and validate" process. It will calcuate how many cells/rows it can render within the viewport, have them all rendered, and then ask for their real dimension, and based on the cell/row dimensions it will decide if it needs to render more cells (if there are still some room in the viewport) or remove a few. + +For short, the process is more or less + +* Render cell/row (DOM write) +* Read dimensions (DOM read) + +The catch here is while rendering a cell/row, if we happen to perform any DOM read operation between DOM write, it will trigger forced reflow and block the UI. To prevent this we would batch all DOM read operatiosn and postpone them untill the list view requests them. + +The worflow of rendering a code cell with a text output is like below and all operations are synchronous + +![render in the core](https://user-images.githubusercontent.com/876920/142256110-a1c5800f-be46-4bd2-bbff-077c1c73e1fd.png) + +When the notebook document contains markdown cells or rich outputs, the workflow is a bit more complex and become asynchornously partially due to the fact the markdown and rich outputs are rendered in a separate webview/iframe. While the list view renders the cell/row, it will send requests to the webview for output rendering, the rendering result (like dimensions of the output elements) won't come back in current frame. Once we receive the output rendering results from the webview (say next frame), we would ask the list view to adjust the position/dimension of the cell and ones below. + +![render outputs in the webview/iframe](https://user-images.githubusercontent.com/876920/142276957-f73a155e-70cb-4066-b5cc-5f451c1c91c8.png) + ## Cell rendering From 7600192459ba5e1b0bb64eebd11e9fb1e8f55db0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 17 Nov 2021 12:47:03 -0800 Subject: [PATCH 179/330] Pick up TS 4.5 as bundled TS version Fixes #136704 --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 22172c71c86..2c5746b4e73 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "^4.5.1-rc" + "typescript": "4.5" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 59cf0bcd5b8..a16a7bfc4b0 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -24,10 +24,10 @@ fast-plist@0.1.2: resolved "https://registry.yarnpkg.com/fast-plist/-/fast-plist-0.1.2.tgz#a45aff345196006d406ca6cdcd05f69051ef35b8" integrity sha1-pFr/NFGWAG1AbKbNzQX2kFHvNbg= -typescript@^4.5.1-rc: - version "4.5.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.1-rc.tgz#02155eaa0579d11babb2a55dcbd796948a994bb3" - integrity sha512-tQBWW1DCFqweyhSzsAUSFgssJ3uuRBh0zyOkRV4CaFF2rMBkGU0I+MqPMch0qhGCUGXjOW7FgMrbQLS6PE3Z6Q== +typescript@4.5: + version "4.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" + integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== vscode-grammar-updater@^1.0.3: version "1.0.3" From 59c53002de27a34712d72ec56ad29c8d9f92f1b7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 17 Nov 2021 12:47:23 -0800 Subject: [PATCH 180/330] Fix spelling --- .../src/typeScriptServiceClientHost.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 41d06ed0576..f67db7f3858 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -62,7 +62,7 @@ export default class TypeScriptServiceClientHost extends Disposable { constructor( descriptions: LanguageDescription[], context: vscode.ExtensionContext, - onCaseInsenitiveFileSystem: boolean, + onCaseInsensitiveFileSystem: boolean, services: { pluginManager: PluginManager, commandManager: CommandManager, @@ -82,7 +82,7 @@ export default class TypeScriptServiceClientHost extends Disposable { const allModeIds = this.getAllModeIds(descriptions, services.pluginManager); this.client = this._register(new TypeScriptServiceClient( context, - onCaseInsenitiveFileSystem, + onCaseInsensitiveFileSystem, services, allModeIds)); @@ -99,7 +99,7 @@ export default class TypeScriptServiceClientHost extends Disposable { this.typingsStatus = this._register(new TypingsStatus(this.client)); this._register(LargeProjectStatus.create(this.client)); - this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client, onCaseInsenitiveFileSystem)); + this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client, onCaseInsensitiveFileSystem)); for (const description of descriptions) { const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); From c1c103dee729b6f702c1c5272d25b24ea9664d48 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 17 Nov 2021 12:47:30 -0800 Subject: [PATCH 181/330] Have setting the value trigger the filter. Fixes #137279 --- .../src/singlefolder-tests/quickInput.test.ts | 146 ++++++++++++++++++ .../parts/quickinput/browser/quickInput.ts | 5 + .../test/browser/quickAccess.test.ts | 3 +- 3 files changed, 153 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts index ba7ce21e32f..bf0da5076b1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts @@ -11,6 +11,7 @@ interface QuickPickExpected { events: string[]; activeItems: string[][]; selectionItems: string[][]; + values: string[]; acceptedItems: { active: string[][]; selection: string[][]; @@ -18,6 +19,15 @@ interface QuickPickExpected { }; } +interface InputBoxExpected { + events: string[]; + values: string[]; + accepted: { + values: string[]; + dispose: boolean[]; + }; +} + suite('vscode API - quick input', function () { teardown(async function () { @@ -35,6 +45,7 @@ suite('vscode API - quick input', function () { events: ['active', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], ['zwei']], selectionItems: [['zwei']], + values: [], acceptedItems: { active: [['zwei']], selection: [['zwei']], @@ -61,6 +72,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'hide'], activeItems: [['zwei']], selectionItems: [['zwei']], + values: [], acceptedItems: { active: [['zwei']], selection: [['zwei']], @@ -87,6 +99,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], ['zwei']], selectionItems: [['eins'], ['eins', 'zwei']], + values: [], acceptedItems: { active: [['zwei']], selection: [['eins', 'zwei']], @@ -117,6 +130,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'selection', 'accept', 'hide'], activeItems: [['eins']], selectionItems: [['zwei'], ['drei']], + values: [], acceptedItems: { active: [['eins'], ['eins']], selection: [['zwei'], ['drei']], @@ -142,6 +156,7 @@ suite('vscode API - quick input', function () { events: ['active', 'selection', 'accept', 'active', 'selection', 'active', 'selection', 'accept', 'hide'], activeItems: [['eins'], [], ['drei']], selectionItems: [['eins'], [], ['drei']], + values: [], acceptedItems: { active: [['eins'], ['drei']], selection: [['eins'], ['drei']], @@ -163,6 +178,40 @@ suite('vscode API - quick input', function () { .catch(err => done(err)); }); + // NOTE: This test is currently accepting the wrong behavior of #135971 + // so that we can test the fix for #137279. + test('createQuickPick, onDidChangeValue gets triggered', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; + + const quickPick = createQuickPick({ + events: ['active', 'active', 'active', 'active', 'value', 'active', 'active', 'value', 'hide'], + activeItems: [['eins'], ['zwei'], [], ['zwei'], [], ['eins']], + selectionItems: [], + values: ['zwei', ''], + acceptedItems: { + active: [], + selection: [], + dispose: [] + }, + }, (err?: any) => done(err)); + quickPick.items = ['eins', 'zwei'].map(label => ({ label })); + quickPick.show(); + + (async () => { + quickPick.value = 'zwei'; + await timeout(async () => { + quickPick.value = ''; + await timeout(async () => { + quickPick.hide(); + }, 0); + }, 0); + })() + .catch(err => done(err)); + }); + test('createQuickPick, dispose in onDidHide', function (_done) { let done = (err?: any) => { done = () => { }; @@ -248,6 +297,34 @@ suite('vscode API - quick input', function () { quickPick.hide(); await waitForHide(quickPick); }); + + test('createInputBox, onDidChangeValue gets triggered', function (_done) { + let done = (err?: any) => { + done = () => { }; + _done(err); + }; + + const quickPick = createInputBox({ + events: ['value', 'accept', 'hide'], + values: ['zwei'], + accepted: { + values: ['zwei'], + dispose: [true] + }, + }, (err?: any) => done(err)); + quickPick.show(); + + (async () => { + quickPick.value = 'zwei'; + await timeout(async () => { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + await timeout(async () => { + quickPick.hide(); + }, 0); + }, 0); + })() + .catch(err => done(err)); + }); }); function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, record = false) { @@ -316,9 +393,78 @@ function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, } }); + quickPick.onDidChangeValue(value => { + if (record) { + console.log('value'); + return; + } + + try { + eventIndex++; + assert.strictEqual('value', expected.events.shift(), `onDidChangeValue (event ${eventIndex})`); + const expectedValue = expected.values.shift(); + assert.deepStrictEqual(value, expectedValue, `onDidChangeValue event value (event ${eventIndex})`); + } catch (err) { + done(err); + } + }); + return quickPick; } +function createInputBox(expected: InputBoxExpected, done: (err?: any) => void, record = false) { + const inputBox = window.createInputBox(); + let eventIndex = -1; + inputBox.onDidAccept(() => { + if (record) { + console.log('accept'); + return; + } + try { + eventIndex++; + assert.strictEqual('accept', expected.events.shift(), `onDidAccept (event ${eventIndex})`); + const expectedValue = expected.accepted.values.shift(); + assert.deepStrictEqual(inputBox.value, expectedValue, `onDidAccept event value (event ${eventIndex})`); + if (expected.accepted.dispose.shift()) { + inputBox.dispose(); + } + } catch (err) { + done(err); + } + }); + inputBox.onDidHide(() => { + if (record) { + console.log('hide'); + done(); + return; + } + try { + assert.strictEqual('hide', expected.events.shift()); + done(); + } catch (err) { + done(err); + } + }); + + inputBox.onDidChangeValue(value => { + if (record) { + console.log('value'); + return; + } + + try { + eventIndex++; + assert.strictEqual('value', expected.events.shift(), `onDidChangeValue (event ${eventIndex})`); + const expectedValue = expected.values.shift(); + assert.deepStrictEqual(value, expectedValue, `onDidChangeValue event value (event ${eventIndex})`); + } catch (err) { + done(err); + } + }); + + return inputBox; +} + async function timeout(run: () => Promise | T, ms: number): Promise { return new Promise(resolve => setTimeout(() => resolve(run()), ms)); } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 4a9a2db8cd2..867c93a2df5 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -490,6 +490,11 @@ class QuickPick extends QuickInput implements IQuickPi if (this._value !== value) { this._value = value || ''; this.update(); + // TODO: Remove this duplicate code and have the updating of the input box handle this. + const didFilter = this.ui.list.filter(this.filterValue(this.ui.inputBox.value)); + if (didFilter) { + this.trySelectFirst(); + } this.onDidChangeValueEmitter.fire(this._value); } } diff --git a/src/vs/workbench/test/browser/quickAccess.test.ts b/src/vs/workbench/test/browser/quickAccess.test.ts index 3130a990ac5..587a2fa9bac 100644 --- a/src/vs/workbench/test/browser/quickAccess.test.ts +++ b/src/vs/workbench/test/browser/quickAccess.test.ts @@ -66,7 +66,8 @@ suite('QuickAccess', () => { provide(picker: IQuickPick, token: CancellationToken): IDisposable { assert.ok(picker); provider2Called = true; - token.onCancellationRequested(() => provider2Canceled = true); + token.onCancellationRequested(() => + provider2Canceled = true); return toDisposable(() => provider2Disposed = true); } From bdc6162b1d40bfa5220490ddbc8369d2abc74481 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 17 Nov 2021 12:53:22 -0800 Subject: [PATCH 182/330] trigger didChange event when you update inputbox.value. ref c1c103dee729b6f702c1c5272d25b24ea9664d48 --- src/vs/base/parts/quickinput/browser/quickInput.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 867c93a2df5..9fbbf2ec5f6 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1061,7 +1061,6 @@ class QuickPick extends QuickInput implements IQuickPi } class InputBox extends QuickInput implements IInputBox { - private _value = ''; private _valueSelection: Readonly<[number, number]> | undefined; private valueSelectionUpdated = true; private _placeholder: string | undefined; @@ -1071,12 +1070,11 @@ class InputBox extends QuickInput implements IInputBox { private readonly onDidAcceptEmitter = this._register(new Emitter()); get value() { - return this._value; + return this.ui.inputBox.value; } set value(value: string) { - this._value = value || ''; - this.update(); + this.ui.inputBox.value = value ?? ''; } set valueSelection(valueSelection: Readonly<[number, number]>) { @@ -1123,10 +1121,6 @@ class InputBox extends QuickInput implements IInputBox { if (!this.visible) { this.visibleDisposables.add( this.ui.inputBox.onDidChange(value => { - if (value === this.value) { - return; - } - this._value = value; this.onDidValueChangeEmitter.fire(value); })); this.visibleDisposables.add(this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire())); @@ -1146,9 +1140,6 @@ class InputBox extends QuickInput implements IInputBox { }; this.ui.setVisibilities(visibilities); super.update(); - if (this.ui.inputBox.value !== this.value) { - this.ui.inputBox.value = this.value; - } if (this.valueSelectionUpdated) { this.valueSelectionUpdated = false; this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] }); From 00fa317c433d6c56f7853887ed42715bc7f29808 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 17 Nov 2021 14:00:01 -0800 Subject: [PATCH 183/330] Add terminal profile smoke tests (#137079) --- .../browser/parts/views/viewsService.ts | 7 + .../terminal/browser/terminalActions.ts | 69 ++++++++- .../terminal/browser/terminalGroupService.ts | 4 +- .../browser/terminalProfileQuickpick.ts | 7 +- .../terminal/browser/terminalQuickAccess.ts | 2 - .../terminal/browser/terminalService.ts | 2 +- .../contrib/terminal/common/terminal.ts | 2 + test/automation/src/code.ts | 6 +- test/automation/src/playwrightDriver.ts | 9 +- test/automation/src/quickaccess.ts | 4 +- test/automation/src/quickinput.ts | 6 +- test/automation/src/terminal.ts | 77 +++++++++- test/automation/src/workbench.ts | 2 +- .../areas/terminal/terminal-profiles.test.ts | 102 +++++++++++-- .../src/areas/terminal/terminal-tabs.test.ts | 139 ++++++++++++++++++ test/smoke/src/main.ts | 2 + 16 files changed, 402 insertions(+), 38 deletions(-) create mode 100644 test/smoke/src/areas/terminal/terminal-tabs.test.ts diff --git a/src/vs/workbench/browser/parts/views/viewsService.ts b/src/vs/workbench/browser/parts/views/viewsService.ts index e782029440e..4ecd0454eb0 100644 --- a/src/vs/workbench/browser/parts/views/viewsService.ts +++ b/src/vs/workbench/browser/parts/views/viewsService.ts @@ -271,6 +271,13 @@ export class ViewsService extends Disposable implements IViewsService { } else if (location === ViewContainerLocation.Panel) { this.paneCompositeService.hideActivePaneComposite(location); } + + // The blur event doesn't fire on WebKit when the focused element is hidden, + // so the context key needs to be forced here too otherwise a view may still + // think it's showing, breaking toggle commands. + if (this.focusedViewContextKey.get() === id) { + this.focusedViewContextKey.reset(); + } } else { view.setExpanded(false); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 01c66be16e9..f2b5da0aeb2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -46,6 +46,9 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { isAbsolute } from 'vs/base/common/path'; +import { ITerminalQuickPickItem } from 'vs/workbench/contrib/terminal/browser/terminalProfileQuickpick'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { getIconId, getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; // allow-any-unicode-next-line export const switchTerminalActionViewItemSeparator = '─────────'; @@ -1582,6 +1585,52 @@ export function registerTerminalActions() { } } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: TerminalCommandId.Join, + title: { value: localize('workbench.action.terminal.join', "Join Terminals"), original: 'Join Terminals' }, + category, + f1: true, + precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated)) + }); + } + async run(accessor: ServicesAccessor) { + const themeService = accessor.get(IThemeService); + const groupService = accessor.get(ITerminalGroupService); + const picks: ITerminalQuickPickItem[] = []; + if (!groupService.activeInstance || groupService.instances.length === 1) { + return; + } + const otherInstances = groupService.instances.filter(i => i.instanceId !== groupService.activeInstance?.instanceId); + for (const terminal of otherInstances) { + const group = groupService.getGroupForInstance(terminal); + if (group?.terminalInstances.length === 1) { + const iconId = getIconId(terminal); + const label = `$(${iconId}): ${terminal.title}`; + const iconClasses: string[] = []; + const colorClass = getColorClass(terminal); + if (colorClass) { + iconClasses.push(colorClass); + } + const uriClasses = getUriClasses(terminal, themeService.getColorTheme().type); + if (uriClasses) { + iconClasses.push(...uriClasses); + } + picks.push({ + terminal, + label, + iconClasses + }); + } + } + const result = await accessor.get(IQuickInputService).pick(picks, {}); + if (result) { + groupService.joinInstances([result.terminal, groupService.activeInstance!]); + } + } + } + ); registerAction2(class extends Action2 { constructor() { super({ @@ -1736,6 +1785,24 @@ export function registerTerminalActions() { } } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: TerminalCommandId.KillAll, + title: { value: localize('workbench.action.terminal.killAll', "Kill All Terminals"), original: 'Kill All Terminals' }, + f1: true, + category, + precondition: ContextKeyExpr.or(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.isOpen), + icon: Codicon.trash + }); + } + async run(accessor: ServicesAccessor) { + const terminalService = accessor.get(ITerminalService); + for (const instance of terminalService.instances) { + await terminalService.safeDisposeTerminal(instance); + } + } + }); registerAction2(class extends Action2 { constructor() { super({ @@ -1818,7 +1885,7 @@ export function registerTerminalActions() { constructor() { super({ id: TerminalCommandId.SelectDefaultProfile, - title: { value: localize('workbench.action.terminal.selectDefaultProfile', "Select Default Profile"), original: 'Select Default Profile' }, + title: { value: localize('workbench.action.terminal.selectDefaultShell', "Select Default Profile"), original: 'Select Default Profile' }, f1: true, category, precondition: TerminalContextKeys.processSupported diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index 4f56bbebdee..30da8845d02 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -20,7 +20,6 @@ import { getInstanceFromResource } from 'vs/workbench/contrib/terminal/browser/t import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; export class TerminalGroupService extends Disposable implements ITerminalGroupService, ITerminalFindHost { declare _serviceBrand: undefined; @@ -60,7 +59,6 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe @IContextKeyService private _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IViewsService private readonly _viewsService: IViewsService, - @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, @IConfigurationService private readonly _configurationService: IConfigurationService, ) { @@ -81,7 +79,7 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe if (location === ViewContainerLocation.Panel) { const panel = this._viewDescriptorService.getViewContainerByViewId(TERMINAL_VIEW_ID); if (panel && this._viewDescriptorService.getViewContainerModel(panel).activeViewDescriptors.length === 1) { - this._layoutService.setPartHidden(true, Parts.PANEL_PART); + this._viewsService.closeView(TERMINAL_VIEW_ID); TerminalContextKeys.tabsMouse.bindTo(this._contextKeyService).set(false); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts index e7dac04a1c1..1f2efc11842 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts @@ -12,7 +12,8 @@ import { configureTerminalProfileIcon } from 'vs/workbench/contrib/terminal/brow import * as nls from 'vs/nls'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IQuickPickTerminalObject } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IQuickPickTerminalObject, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; type DefaultProfileName = string; @@ -238,3 +239,7 @@ export interface IProfileQuickPickItem extends IQuickPickItem { profileName: string; keyMods?: IKeyMods | undefined; } + +export interface ITerminalQuickPickItem extends IPickerQuickAccessItem { + terminal: ITerminalInstance +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts index 4344e2f0a19..873f4e0ea91 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalQuickAccess.ts @@ -76,9 +76,7 @@ export class TerminalQuickAccessProvider extends PickerQuickAccessProvider this._commandService.executeCommand(TerminalCommandId.NewWithProfile) }); - return terminalPicks; - } private _createPick(terminal: ITerminalInstance, terminalIndex: number, filter: string, groupIndex?: number): IPickerQuickAccessItem | undefined { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 0895d2d7eed..b95ad260ca9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -889,7 +889,7 @@ export class TerminalService implements ITerminalService { // Launch the contributed profile if (contributedProfile) { const resolvedLocation = this.resolveLocation(options?.location); - const splitActiveTerminal = typeof options?.location === 'object' && 'splitActiveTerminal' in options.location ? options.location.splitActiveTerminal : false; + const splitActiveTerminal = typeof options?.location === 'object' && 'splitActiveTerminal' in options.location ? options.location.splitActiveTerminal : typeof options?.location === 'object' ? 'parentTerminal' in options.location : false; let location: TerminalLocation | { viewColumn: number, preserveState?: boolean } | { splitActiveTerminal: boolean } | undefined; if (splitActiveTerminal) { location = resolvedLocation === TerminalLocation.Editor ? { viewColumn: SIDE_GROUP } : { splitActiveTerminal: true }; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 35bdcce9826..c155c2edd45 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -430,6 +430,7 @@ export const enum TerminalCommandId { Kill = 'workbench.action.terminal.kill', KillEditor = 'workbench.action.terminal.killEditor', KillInstance = 'workbench.action.terminal.killInstance', + KillAll = 'workbench.action.terminal.killAll', QuickKill = 'workbench.action.terminal.quickKill', ConfigureTerminalSettings = 'workbench.action.terminal.openSettings', CopySelection = 'workbench.action.terminal.copySelection', @@ -450,6 +451,7 @@ export const enum TerminalCommandId { Unsplit = 'workbench.action.terminal.unsplit', UnsplitInstance = 'workbench.action.terminal.unsplitInstance', JoinInstance = 'workbench.action.terminal.joinInstance', + Join = 'workbench.action.terminal.join', Relaunch = 'workbench.action.terminal.relaunch', FocusPreviousPane = 'workbench.action.terminal.focusPreviousPane', ShowTabs = 'workbench.action.terminal.showTabs', diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 6f7a8a7e9ee..4357b5aef99 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -225,14 +225,14 @@ async function poll( if (trial > retryCount) { console.error('** Timeout!'); console.error(lastError); - + console.error(`Timeout: ${timeoutMessage} after ${(retryCount * retryInterval) / 1000} seconds.`); throw new Error(`Timeout: ${timeoutMessage} after ${(retryCount * retryInterval) / 1000} seconds.`); } let result; try { result = await fn(); - + console.log('DEBUG: poll result', result); if (acceptFn(result)) { return result; } else { @@ -250,7 +250,7 @@ async function poll( export class Code { private _activeWindowId: number | undefined = undefined; - private driver: IDriver; + driver: IDriver; constructor( private client: IDisposable, diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 4ea07cb4cc4..b469974df25 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -41,12 +41,13 @@ function buildDriver(browser: playwright.Browser, context: playwright.BrowserCon class PlaywrightDriver implements IDriver { _serviceBrand: undefined; - + page: playwright.Page; constructor( private readonly _browser: playwright.Browser, private readonly _context: playwright.BrowserContext, private readonly _page: playwright.Page ) { + this.page = _page; } async getWindowIds() { return [1]; } @@ -70,6 +71,12 @@ class PlaywrightDriver implements IDriver { if (i > 0) { await timeout(100); } + + if (keybinding.startsWith('Alt') || keybinding.startsWith('Control')) { + await this._page.keyboard.press(keybinding); + return; + } + const keys = chord.split('+'); const keysDown: string[] = []; for (let i = 0; i < keys.length; i++) { diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts index ff8131f5f81..86438768f37 100644 --- a/test/automation/src/quickaccess.ts +++ b/test/automation/src/quickaccess.ts @@ -79,14 +79,14 @@ export class QuickAccess { await this.editors.waitForEditorFocus(fileName); } - async runCommand(commandId: string): Promise { + async runCommand(commandId: string, keepOpen?: boolean): Promise { await this.openQuickAccess(`>${commandId}`); // wait for best choice to be focused await this.code.waitForTextContent(QuickInput.QUICK_INPUT_FOCUSED_ELEMENT); // wait and click on best choice - await this.quickInput.selectQuickInputElement(0); + await this.quickInput.selectQuickInputElement(0, keepOpen); } async openQuickOutline(): Promise { diff --git a/test/automation/src/quickinput.ts b/test/automation/src/quickinput.ts index 70b37ea9e2d..78170370fd9 100644 --- a/test/automation/src/quickinput.ts +++ b/test/automation/src/quickinput.ts @@ -39,12 +39,14 @@ export class QuickInput { await this.code.waitForElement(QuickInput.QUICK_INPUT, r => !!r && r.attributes.style.indexOf('display: none;') !== -1); } - async selectQuickInputElement(index: number): Promise { + async selectQuickInputElement(index: number, keepOpen?: boolean): Promise { await this.waitForQuickInputOpened(); for (let from = 0; from < index; from++) { await this.code.dispatchKeybinding('down'); } await this.code.dispatchKeybinding('enter'); - await this.waitForQuickInputClosed(); + if (!keepOpen) { + await this.waitForQuickInputClosed(); + } } } diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 16a6dae6197..42afbd78bf7 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -3,30 +3,84 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IElement, QuickInput } from '.'; import { Code } from './code'; import { QuickAccess } from './quickaccess'; const TERMINAL_VIEW_SELECTOR = `#terminal`; const XTERM_SELECTOR = `${TERMINAL_VIEW_SELECTOR} .terminal-wrapper`; -const XTERM_TEXTAREA = `${XTERM_SELECTOR} textarea.xterm-helper-textarea`; +const CONTRIBUTED_PROFILE_NAME = `JavaScript Debug Terminal`; +const TABS = '.tabs-list .terminal-tabs-entry'; +const XTERM_FOCUSED_SELECTOR = '.terminal.xterm.focus'; + +const enum TerminalCommandId { + Rename = 'workbench.action.terminal.rename', + ChangeColor = 'workbench.action.terminal.changeColor', + ChangeIcon = 'workbench.action.terminal.changeIcon', + Split = 'workbench.action.terminal.split', + KillAll = 'workbench.action.terminal.killAll', + Unsplit = 'workbench.action.terminal.unsplit', + Join = 'workbench.action.terminal.join', + Show = 'workbench.action.terminal.toggleTerminal', + CreateNew = 'workbench.action.terminal.new', + NewWithProfile = 'workbench.action.terminal.newWithProfile', + SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell' +} export class Terminal { - constructor(private code: Code, private quickaccess: QuickAccess) { } + constructor(private code: Code, private quickaccess: QuickAccess, private quickinput: QuickInput) { } - async showTerminal(): Promise { - await this.quickaccess.runCommand('workbench.action.terminal.toggleTerminal'); - await this.code.waitForActiveElement(XTERM_TEXTAREA); - await this.code.waitForTerminalBuffer(XTERM_SELECTOR, lines => lines.some(line => line.length > 0)); + // TODO: Strongly type using non-const enum TerminalCommandId + async runCommand(commandId: string, value?: string): Promise { + await this.quickaccess.runCommand(commandId, !!value || commandId === TerminalCommandId.Join); + if (commandId === TerminalCommandId.Show || commandId === TerminalCommandId.CreateNew) { + return await this._waitForTerminal(); + } + if (value) { + await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, value); + } + await this.code.dispatchKeybinding('enter'); + await this.quickinput.waitForQuickInputClosed(); } - async runCommand(commandText: string): Promise { + async runCommandInTerminal(commandText: string): Promise { await this.code.writeInTerminal(XTERM_SELECTOR, commandText); // hold your horses await new Promise(c => setTimeout(c, 500)); await this.code.dispatchKeybinding('enter'); } + // TODO: Return something more robust: + // export interface ITerminalInstance { + // name: string; + // icon: string; + // } + // export type TerminalGroup = ITerminalInstance[]; + // export type TerminalLayout = TerminalGroup[]; + async getTabLabels(expectedCount: number, splits?: boolean, accept?: (result: IElement[]) => boolean): Promise { + const result: string[] = []; + const tabs = await this.code.waitForElements(TABS, true, e => accept ? accept(e) : e.length === expectedCount && (!splits || e.some(e => e.textContent.startsWith('┌'))) && e.every(element => element.textContent.trim().length > 1)); + for (const t of tabs) { + result.push(t.textContent); + } + if (!result[0].startsWith('┌')) { + const first = result[1]; + const second = result[0]; + return [first, second]; + } + return result; + } + + async runProfileCommand(command: string, contributed?: boolean, altKey?: boolean): Promise { + await this.quickaccess.runCommand(command, true); + if (contributed) { + await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, CONTRIBUTED_PROFILE_NAME); + } + await this.code.dispatchKeybinding(altKey ? 'Alt+Enter' : 'enter'); + await this.quickinput.waitForQuickInputClosed(); + } + async waitForTerminalText(accept: (buffer: string[]) => boolean, message?: string): Promise { try { await this.code.waitForTerminalBuffer(XTERM_SELECTOR, accept); @@ -37,4 +91,13 @@ export class Terminal { throw err; } } + + async getPage(): Promise { + return (this.code.driver as any).page; + } + + private async _waitForTerminal(): Promise { + await this.code.waitForElement(XTERM_FOCUSED_SELECTOR); + await this.code.waitForTerminalBuffer(XTERM_SELECTOR, lines => lines.some(line => line.length > 0)); + } } diff --git a/test/automation/src/workbench.ts b/test/automation/src/workbench.ts index c2f5abe8096..b5c675fac7f 100644 --- a/test/automation/src/workbench.ts +++ b/test/automation/src/workbench.ts @@ -61,7 +61,7 @@ export class Workbench { this.problems = new Problems(code, this.quickaccess); this.settingsEditor = new SettingsEditor(code, userDataPath, this.editors, this.editor, this.quickaccess); this.keybindingsEditor = new KeybindingsEditor(code); - this.terminal = new Terminal(code, this.quickaccess); + this.terminal = new Terminal(code, this.quickaccess, this.quickinput); this.notebook = new Notebook(this.quickaccess, code); this.localization = new Localization(code); } diff --git a/test/smoke/src/areas/terminal/terminal-profiles.test.ts b/test/smoke/src/areas/terminal/terminal-profiles.test.ts index c4fe5e9d789..0f202d6e40c 100644 --- a/test/smoke/src/areas/terminal/terminal-profiles.test.ts +++ b/test/smoke/src/areas/terminal/terminal-profiles.test.ts @@ -5,31 +5,105 @@ import { ok } from 'assert'; import { ParsedArgs } from 'minimist'; -import { Application } from '../../../../automation'; +import { Code, Terminal } from '../../../../automation'; import { afterSuite, beforeSuite } from '../../utils'; -export function setup(opts: ParsedArgs) { - describe('Terminal Profiles', () => { - let app: Application; +const ContributedProfileName = `JavaScript Debug Terminal`; +export function setup(opts: ParsedArgs) { + + describe('Terminal Profiles', () => { + let code: Code; + let terminal: Terminal; + const enum TerminalCommandId { + Split = 'workbench.action.terminal.split', + KillAll = 'workbench.action.terminal.killAll', + Show = 'workbench.action.terminal.toggleTerminal', + CreateNew = 'workbench.action.terminal.new', + NewWithProfile = 'workbench.action.terminal.newWithProfile', + SelectDefaultProfile = 'workbench.action.terminal.selectDefaultShell' + } beforeSuite(opts); afterSuite(opts); before(function () { - app = this.app; + code = this.app.code; + terminal = this.app.workbench.terminal; }); - it('should launch the default profile', async function () { - await app.workbench.terminal.showTerminal(); + afterEach(async () => { + await terminal.runCommand(TerminalCommandId.KillAll); + }); - // Verify the terminal buffer has some content - await app.workbench.terminal.waitForTerminalText(buffer => { - return buffer.some(e => e.length > 0); - }, 'The terminal buffer should have some content'); + it('should launch the default profile', async () => { + await terminal.runCommand(TerminalCommandId.Show); + // TODO: Use getSingleTabLabel? Share logic with getTabLabel? + await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + }); - // Verify the terminal single tab shows up and has a title - const terminalTab = await app.code.waitForElement('.single-terminal-tab'); - ok(terminalTab.textContent.trim().length > 0); + it.skip('should set the default profile to a contributed one', async () => { + await terminal.runProfileCommand(TerminalCommandId.SelectDefaultProfile, true); + await terminal.runCommand(TerminalCommandId.CreateNew); + await code.waitForElement('.single-terminal-tab', e => e ? e.textContent.endsWith(ContributedProfileName) : false); + }); + + it.skip('should use the default contributed profile on panel open and for splitting', async () => { + await terminal.runProfileCommand(TerminalCommandId.SelectDefaultProfile, true); + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.Split); + const tabs = await terminal.getTabLabels(2); + console.log('DEBUG: tabs', tabs); + ok(tabs[0].startsWith('┌') && tabs[0].endsWith(ContributedProfileName)); + ok(tabs[1].startsWith('└') && tabs[1].endsWith(ContributedProfileName)); + }); + + it('should set the default profile', async () => { + await terminal.runProfileCommand(TerminalCommandId.SelectDefaultProfile, undefined); + await terminal.runCommand(TerminalCommandId.CreateNew); + await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + }); + + it('should use the default profile on panel open and for splitting', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + await terminal.runCommand(TerminalCommandId.Split); + const tabs = await terminal.getTabLabels(2, true); + ok(tabs[0].startsWith('┌') && !tabs[0].endsWith(ContributedProfileName)); + ok(tabs[1].startsWith('└') && !tabs[1].endsWith(ContributedProfileName)); + }); + + it('clicking the plus button should create a terminal and display the tabs view showing no split decorations', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await code.waitAndClick('li.action-item.monaco-dropdown-with-primary > div.action-container.menu-entry > a'); + const tabLabels = await terminal.getTabLabels(2); + ok(!tabLabels[0].startsWith('┌') && !tabLabels[1].startsWith('└')); + }); + + it('createWithProfile command should create a terminal with a profile', async () => { + await terminal.runProfileCommand(TerminalCommandId.NewWithProfile); + await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + }); + + it.skip('createWithProfile command should create a terminal with a contributed profile', async () => { + await terminal.runProfileCommand(TerminalCommandId.NewWithProfile, true); + await code.waitForElement('.single-terminal-tab', e => e ? e.textContent.endsWith(ContributedProfileName) : false); + }); + + it('createWithProfile command should create a split terminal with a profile', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runProfileCommand(TerminalCommandId.NewWithProfile, undefined, true); + const tabs = await terminal.getTabLabels(2, true); + ok(tabs[0].startsWith('┌') && !tabs[0].endsWith(ContributedProfileName)); + ok(tabs[1].startsWith('└') && !tabs[1].endsWith(ContributedProfileName)); + }); + + it.skip('createWithProfile command should create a split terminal with a contributed profile', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await code.waitForElement('.single-terminal-tab', e => e ? !e.textContent.endsWith(ContributedProfileName) : false); + await terminal.runProfileCommand(TerminalCommandId.NewWithProfile, true, true); + const tabs = await terminal.getTabLabels(2, true); + ok(tabs[0].startsWith('┌') && !tabs[0].endsWith(ContributedProfileName)); + ok(tabs[1].startsWith('└') && tabs[1].endsWith(ContributedProfileName)); }); }); } diff --git a/test/smoke/src/areas/terminal/terminal-tabs.test.ts b/test/smoke/src/areas/terminal/terminal-tabs.test.ts new file mode 100644 index 00000000000..1a5897eb0fd --- /dev/null +++ b/test/smoke/src/areas/terminal/terminal-tabs.test.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ok } from 'assert'; +import { ParsedArgs } from 'minimist'; +import { Code, Terminal } from '../../../../automation/out'; +import { afterSuite, beforeSuite } from '../../utils'; + +export function setup(opts: ParsedArgs) { + // TODO: Re-enable when stable + describe.skip('Terminal Tabs', () => { + let code: Code; + let terminal: Terminal; + + // TODO: Move into automation/terminal + const enum TerminalCommandId { + Rename = 'workbench.action.terminal.rename', + ChangeColor = 'workbench.action.terminal.changeColor', + ChangeIcon = 'workbench.action.terminal.changeIcon', + Split = 'workbench.action.terminal.split', + KillAll = 'workbench.action.terminal.killAll', + Unsplit = 'workbench.action.terminal.unsplit', + Join = 'workbench.action.terminal.join', + Show = 'workbench.action.terminal.toggleTerminal', + CreateNew = 'workbench.action.terminal.new' + } + + beforeSuite(opts); + afterSuite(opts); + + before(function () { + code = this.app.code; + terminal = this.app.workbench.terminal; + }); + + afterEach(async () => { + await terminal.runCommand(TerminalCommandId.KillAll); + }); + + it('clicking the plus button should create a terminal and display the tabs view showing no split decorations', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await code.waitAndClick('li.action-item.monaco-dropdown-with-primary > div.action-container.menu-entry > a'); + const tabLabels = await terminal.getTabLabels(2); + ok(!tabLabels[0].startsWith('┌') && !tabLabels[1].startsWith('└')); + }); + + it('should update color of the single tab', async () => { + await terminal.runCommand(TerminalCommandId.Show); + const color = 'Cyan'; + await terminal.runCommand(TerminalCommandId.ChangeColor, color); + const singleTab = await code.waitForElement('.single-terminal-tab'); + ok(singleTab.className.includes(`terminal-icon-terminal_ansi${color}`)); + }); + + it('should update color of the tab in the tabs list', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.Split); + const tabs = await terminal.getTabLabels(2); + ok(tabs[0].startsWith('┌')); + ok(tabs[1].startsWith('└')); + const color = 'Cyan'; + await terminal.runCommand(TerminalCommandId.ChangeColor, color); + await code.waitForElement(`.terminal-tabs-entry .terminal-icon-terminal_ansi${color}`); + }); + + it('should update icon of the single tab', async () => { + await terminal.runCommand(TerminalCommandId.Show); + const icon = 'symbol-method'; + await terminal.runCommand(TerminalCommandId.ChangeIcon, icon); + await code.waitForElement(`.single-terminal-tab .codicon-${icon}`); + }); + + it('should update icon of the tab in the tabs list', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.Split); + const tabs = await terminal.getTabLabels(2); + ok(tabs[0].startsWith('┌')); + ok(tabs[1].startsWith('└')); + const icon = 'symbol-method'; + await terminal.runCommand(TerminalCommandId.ChangeIcon, icon); + await code.waitForElement(`.terminal-tabs-entry .codicon-${icon}`); + }); + + it('should rename the single tab', async () => { + await terminal.runCommand(TerminalCommandId.Show); + const name = 'my terminal name'; + await terminal.runCommand(TerminalCommandId.Rename, name); + await code.waitForElement('.single-terminal-tab', e => e ? e?.textContent.includes(name) : false); + }); + + it('should rename the tab in the tabs list', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.Split); + const name = 'my terminal name'; + await terminal.runCommand(TerminalCommandId.Rename, name); + await terminal.getTabLabels(2, true, t => t.some(element => element.textContent.includes(name))); + }); + + it('should create a split terminal when single tab is alt clicked', async () => { + await terminal.runCommand(TerminalCommandId.Show); + const page = await terminal.getPage(); + page.keyboard.down('Alt'); + await code.waitAndClick('.single-terminal-tab'); + page.keyboard.up('Alt'); + await terminal.getTabLabels(2, true); + }); + + it('should do nothing when join tabs is run with only one terminal', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.Join); + await code.waitForElement('.single-terminal-tab'); + }); + + it('should join tabs when more than one terminal', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.CreateNew); + await terminal.runCommand(TerminalCommandId.Join); + await terminal.getTabLabels(2, true); + }); + + it('should do nothing when unsplit tabs called with no splits', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.CreateNew); + await terminal.getTabLabels(2, false); + await terminal.runCommand(TerminalCommandId.Unsplit); + await terminal.getTabLabels(2, false); + }); + + it('should unsplit tabs', async () => { + await terminal.runCommand(TerminalCommandId.Show); + await terminal.runCommand(TerminalCommandId.Split); + await terminal.getTabLabels(2, true); + await terminal.runCommand(TerminalCommandId.Unsplit); + await terminal.getTabLabels(2, false, t => t.every(label => !label.textContent.startsWith('┌') && !label.textContent.startsWith('└'))); + }); + }); +} diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 1cf54e68e77..7f95e942bec 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -28,6 +28,7 @@ import { setup as setupMultirootTests } from './areas/multiroot/multiroot.test'; import { setup as setupLocalizationTests } from './areas/workbench/localization.test'; import { setup as setupLaunchTests } from './areas/workbench/launch.test'; import { setup as setupTerminalProfileTests } from './areas/terminal/terminal-profiles.test'; +import { setup as setupTerminalTabsTests } from './areas/terminal/terminal-tabs.test'; const testDataPath = path.join(os.tmpdir(), 'vscsmoke'); if (fs.existsSync(testDataPath)) { @@ -360,4 +361,5 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { // TODO: Enable terminal tests for non-web if (opts.web) { setupTerminalProfileTests(opts); } + if (opts.web) { setupTerminalTabsTests(opts); } }); From 239a94cee8ad7d99f5232ec356323e742c685759 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 17 Nov 2021 14:01:31 -0800 Subject: [PATCH 184/330] remove debug log --- test/automation/src/code.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 4357b5aef99..8deb024b4ae 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -232,7 +232,6 @@ async function poll( let result; try { result = await fn(); - console.log('DEBUG: poll result', result); if (acceptFn(result)) { return result; } else { From 1becfd0feff09be5a7045994b7430770939083dd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 14:39:55 -0800 Subject: [PATCH 185/330] Refactor toolbars in cellRenderer #131808 --- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../browser/view/cellParts/cellContextKeys.ts | 4 +- .../browser/view/cellParts/cellToolbars.ts | 221 ++++++++++-------- .../browser/view/notebookRenderingCommon.ts | 15 +- .../browser/view/renderers/cellRenderer.ts | 82 +++++-- 5 files changed, 195 insertions(+), 129 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index ea2e6c4f3d2..a7bdb81f507 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1165,7 +1165,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD const focused = this._list.getFocusedElements()[0]; if (focused) { if (!this._cellContextKeyManager) { - this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.scopedContextKeyService, this, focused as CellViewModel)); + this._cellContextKeyManager = this._localStore.add(this.instantiationService.createInstance(CellContextKeyManager, this, focused as CellViewModel)); } this._cellContextKeyManager.updateForElement(focused as CellViewModel); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts index d66e823e72f..976e22e8bc4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts @@ -28,9 +28,9 @@ export class CellContextKeyManager extends Disposable { private readonly elementDisposables = this._register(new DisposableStore()); constructor( - private readonly contextKeyService: IContextKeyService, private readonly notebookEditor: INotebookEditorDelegate, - private element: ICellViewModel + private element: ICellViewModel, + @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index fc6a91278a8..70e5759a632 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -6,28 +6,26 @@ import * as DOM from 'vs/base/browser/dom'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction } from 'vs/base/common/actions'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { disposableTimeout } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { MarshalledId } from 'vs/base/common/marshalling'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { BaseCellRenderTemplate, ICellToolbars, isCodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; -export class CellToolbars extends Disposable implements ICellToolbars { - toolbar: ToolBar; - deleteToolbar: ToolBar; - betweenCellToolbar!: ToolBar; - titleMenu: IMenu; - cellDisposable: Disposable | null = null; +export class BetweenCellToolbar extends Disposable { + private _betweenCellToolbar!: ToolBar; constructor( readonly notebookEditor: INotebookEditorDelegate, @@ -41,34 +39,11 @@ export class CellToolbars extends Disposable implements ICellToolbars { ) { super(); - this.toolbar = this._register(this.createToolbar(this.titleToolbarContainer)); - this.deleteToolbar = this._register(this.createToolbar(titleToolbarContainer, 'cell-delete-toolbar')); - if (!this.notebookEditor.creationOptions.isReadOnly) { - this.deleteToolbar.setActions([this.instantiationService.createInstance(DeleteCellAction)]); - } - this.createBetweenCellToolbar(); - this.titleMenu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, contextKeyService)); - } - - createToolbar(container: HTMLElement, elementClass?: string): ToolBar { - const toolbar = new ToolBar(container, this.contextMenuService, { - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), - actionViewItemProvider: action => { - return createActionViewItem(this.instantiationService, action); - }, - renderDropdownAsChildElement: true - }); - - if (elementClass) { - toolbar.getElement().classList.add(elementClass); - } - - return toolbar; } createBetweenCellToolbar() { - this.betweenCellToolbar = this._register(new ToolBar(this.bottomCellToolbarContainer, this.contextMenuService, { + this._betweenCellToolbar = this._register(new ToolBar(this.bottomCellToolbarContainer, this.contextMenuService, { actionViewItemProvider: action => { if (action instanceof MenuItemAction) { if (this.notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { @@ -84,8 +59,8 @@ export class CellToolbars extends Disposable implements ICellToolbars { const menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellInsertToolbar, this.contextKeyService)); const updateActions = () => { - const actions = this.getCellToolbarActions(menu); - this.betweenCellToolbar.setActions(actions.primary, actions.secondary); + const actions = getCellToolbarActions(menu); + this._betweenCellToolbar.setActions(actions.primary, actions.secondary); }; this._register(menu.onDidChange(() => updateActions())); @@ -97,17 +72,7 @@ export class CellToolbars extends Disposable implements ICellToolbars { updateActions(); } - getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[]; } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; - } - - updateContext(element: CodeCellViewModel | MarkupCellViewModel, elementDisposables: DisposableStore) { + updateContext(element: CodeCellViewModel | MarkupCellViewModel) { const toolbarContext = { ui: true, cell: element, @@ -115,75 +80,143 @@ export class CellToolbars extends Disposable implements ICellToolbars { $mid: MarshalledId.NotebookCellActionContext }; - this.toolbar.context = toolbarContext; - this.deleteToolbar.context = toolbarContext; - this.betweenCellToolbar.context = toolbarContext; - - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; - - elementDisposables.add(element.onDidChangeLayout(() => { - const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; - })); + this._betweenCellToolbar.context = toolbarContext; } - setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { - const updateActions = () => { - const actions = this.getCellToolbarActions(this.titleMenu); + updateForLayout(element: CodeCellViewModel | MarkupCellViewModel) { + const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; + this.bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + } +} - const hadFocus = DOM.isAncestor(document.activeElement, this.toolbar.getElement()); - this.toolbar.setActions(actions.primary, actions.secondary); - if (hadFocus) { - this.notebookEditor.focus(); - } - const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); - if (actions.primary.length || actions.secondary.length) { - templateData.container.classList.add('cell-has-toolbar-actions'); - if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; - templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; - } - } else { - templateData.container.classList.remove('cell-has-toolbar-actions'); - if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; - templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; - } - } - }; +export interface ICssClassDelegate { + toggle: (className: string, force?: boolean) => void; +} +export class CellTitleToolbarPart extends Disposable { + private _toolbar: ToolBar; + private _deleteToolbar: ToolBar; + private _titleMenu: IMenu; + private _actionsDisposables = this._register(new DisposableStore()); + + private _hasActions = false; + private readonly _onDidUpdateActions: Emitter = this._register(new Emitter()); + readonly onDidUpdateActions: Event = this._onDidUpdateActions.event; + + get hasActions(): boolean { + return this._hasActions; + } + + constructor( + toolbarContainer: HTMLElement, + private readonly _rootClassDelegate: ICssClassDelegate, + toolbarId: MenuId, + private readonly _notebookEditor: INotebookEditorDelegate, + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService menuService: IMenuService, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + + this._toolbar = instantiationService.invokeFunction(accessor => createToolbar(accessor, toolbarContainer)); + this._titleMenu = this._register(menuService.createMenu(toolbarId, contextKeyService)); + + this._deleteToolbar = this._register(instantiationService.invokeFunction(accessor => createToolbar(accessor, toolbarContainer, 'cell-delete-toolbar'))); + if (!this._notebookEditor.creationOptions.isReadOnly) { + this._deleteToolbar.setActions([instantiationService.createInstance(DeleteCellAction)]); + } + + this.setupChangeListeners(); + } + + updateContext(toolbarContext: INotebookCellActionContext) { + this._toolbar.context = toolbarContext; + this._deleteToolbar.context = toolbarContext; + } + + private setupChangeListeners(): void { // #103926 let dropdownIsVisible = false; let deferredUpdate: (() => void) | undefined; - updateActions(); - disposables.add(this.titleMenu.onDidChange(() => { - if (this.notebookEditor.isDisposed) { - return; - } - + this.updateActions(); + this._register(this._titleMenu.onDidChange(() => { if (dropdownIsVisible) { - deferredUpdate = () => updateActions(); + deferredUpdate = () => this.updateActions(); return; } - updateActions(); + this.updateActions(); })); - templateData.container.classList.toggle('cell-toolbar-dropdown-active', false); - disposables.add(this.toolbar.onDidChangeDropdownVisibility(visible => { + this._rootClassDelegate.toggle('cell-toolbar-dropdown-active', false); + this._register(this._toolbar.onDidChangeDropdownVisibility(visible => { dropdownIsVisible = visible; - templateData.container.classList.toggle('cell-toolbar-dropdown-active', visible); + this._rootClassDelegate.toggle('cell-toolbar-dropdown-active', visible); if (deferredUpdate && !visible) { - setTimeout(() => { + this._register(disposableTimeout(() => { if (deferredUpdate) { deferredUpdate(); } - }, 0); + })); + deferredUpdate = undefined; } })); } + + private updateActions() { + this._actionsDisposables.clear(); + const actions = getCellToolbarActions(this._titleMenu); + this._actionsDisposables.add(actions.disposable); + + const hadFocus = DOM.isAncestor(document.activeElement, this._toolbar.getElement()); + this._toolbar.setActions(actions.primary, actions.secondary); + if (hadFocus) { + this._notebookEditor.focus(); + } + + if (actions.primary.length || actions.secondary.length) { + this._rootClassDelegate.toggle('cell-has-toolbar-actions', true); + this._hasActions = true; + this._onDidUpdateActions.fire(); + } else { + this._rootClassDelegate.toggle('cell-has-toolbar-actions', false); + this._hasActions = false; + this._onDidUpdateActions.fire(); + } + } +} + +function getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; disposable: IDisposable; } { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + const disposable = createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); + + return { + ...result, + disposable + }; +} + +function createToolbar(accessor: ServicesAccessor, container: HTMLElement, elementClass?: string): ToolBar { + const contextMenuService = accessor.get(IContextMenuService); + const keybindingService = accessor.get(IKeybindingService); + const instantiationService = accessor.get(IInstantiationService); + const toolbar = new ToolBar(container, contextMenuService, { + getKeyBinding: action => keybindingService.lookupKeybinding(action.id), + actionViewItemProvider: action => { + return createActionViewItem(instantiationService, action); + }, + renderDropdownAsChildElement: true + }); + + if (elementClass) { + toolbar.getElement().classList.add(elementClass); + } + + return toolbar; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 03d297d6a1e..8bb1b303162 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -22,6 +22,8 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import type { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { BetweenCellToolbar, CellTitleToolbarPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export interface INotebookCellList { isDisposed: boolean; @@ -89,23 +91,16 @@ export interface INotebookCellList { dispose(): void; } -export interface ICellToolbars { - toolbar: ToolBar; - deleteToolbar: ToolBar; - betweenCellToolbar: ToolBar; - updateContext(element: ICellViewModel, elementDisposables: DisposableStore): void; - setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void; -} - export interface BaseCellRenderTemplate { rootContainer: HTMLElement; editorPart: HTMLElement; cellInputCollapsedContainer: HTMLElement; - contextKeyService: IContextKeyService; + instantiationService: IInstantiationService; container: HTMLElement; cellContainer: HTMLElement; decorationContainer: HTMLElement; - cellToolbars: ICellToolbars; + betweenCellToolbar: BetweenCellToolbar; + titleToolbar: CellTitleToolbarPart; focusIndicatorLeft: FastDomNode; focusIndicatorRight: FastDomNode; readonly templateDisposables: DisposableStore; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 388d87c6257..b5778c314a3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -35,15 +35,15 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions'; -import { CellToolbars } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; +import { BetweenCellToolbar, CellTitleToolbarPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCell'; -import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell'; import { RunToolbar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar'; +import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell'; +import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -163,7 +163,7 @@ abstract class AbstractCellRenderer { templateData.container.classList.remove(DRAGGING_CLASS); } - templateData.elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); + templateData.elementDisposables.add(templateData.instantiationService.createInstance(CellContextKeyManager, this.notebookEditor, element)); } } @@ -210,14 +210,25 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen const innerContent = DOM.append(container, $('.cell.markdown')); const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); - const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); + + const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyService])); + const rootClassDelegate = { + toggle: (className: string, force?: boolean) => container.classList.toggle(className, force) + }; + const titleToolbar = templateDisposables.add(scopedInstaService.createInstance( + CellTitleToolbarPart, + titleToolbarContainer, + rootClassDelegate, + this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, + this.notebookEditor)); + const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')); const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); const templateData: MarkdownCellRenderTemplate = { rootContainer, cellInputCollapsedContainer, - contextKeyService, + instantiationService: scopedInstaService, container, decorationContainer, cellContainer: innerContent, @@ -229,7 +240,8 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen foldingIndicator, templateDisposables, elementDisposables: new DisposableStore(), - cellToolbars, + betweenCellToolbar, + titleToolbar, statusBar, toJSON: () => { return {}; } }; @@ -262,8 +274,10 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.updateForLayout(element, templateData); })); - // render toolbar first - templateData.cellToolbars.setupCellToolbarActions(templateData, elementDisposables); + templateData.betweenCellToolbar.updateContext(element); + + const markdownCell = templateData.instantiationService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); + elementDisposables.add(markdownCell); const toolbarContext = { ui: true, @@ -271,11 +285,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen notebookEditor: this.notebookEditor, $mid: MarshalledId.NotebookCellActionContext }; - templateData.cellToolbars.updateContext(element, templateData.elementDisposables); - - const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); - const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); - elementDisposables.add(markdownCell); templateData.statusBar.updateContext(toolbarContext); } @@ -287,6 +296,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen templateData.focusIndicatorRight.setHeight(indicatorPostion.verticalIndicatorHeight); templateData.container.classList.toggle('cell-statusbar-hidden', this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(element.internalMetadata) === 0); + templateData.betweenCellToolbar.updateForLayout(element); } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { @@ -496,14 +506,24 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const bottomCellToolbarContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); - const cellToolbars = templateDisposables.add(this.instantiationService.createInstance(CellToolbars, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellToolbarContainer)); + const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyService])); + const rootClassDelegate = { + toggle: (className: string, force?: boolean) => container.classList.toggle(className, force) + }; + const titleToolbar = templateDisposables.add(scopedInstaService.createInstance( + CellTitleToolbarPart, + titleToolbarContainer, + rootClassDelegate, + this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, + this.notebookEditor)); + const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellToolbarContainer)); const templateData: CodeCellRenderTemplate = { rootContainer, editorPart, cellInputCollapsedContainer, cellOutputCollapsedContainer, - contextKeyService, + instantiationService: scopedInstaService, container, decorationContainer, cellContainer, @@ -513,7 +533,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende focusIndicatorLeft: focusIndicator, focusIndicatorRight, focusIndicatorBottom, - cellToolbars, + titleToolbar, + betweenCellToolbar, focusSinkElement, runToolbar, executionOrderLabel, @@ -688,9 +709,23 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.dragHandle.setHeight(element.layoutInfo.totalHeight - bottomToolbarDimensions.bottomToolbarGap); templateData.container.classList.toggle('cell-statusbar-hidden', this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(element.internalMetadata) === 0); + + this.updateForTitleMenu(templateData); + templateData.betweenCellToolbar.updateForLayout(element); })); } + protected updateForTitleMenu(templateData: CodeCellRenderTemplate): void { + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); + if (templateData.titleToolbar.hasActions) { + templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; + templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px)`; + } else { + templateData.focusIndicatorLeft.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; + templateData.focusIndicatorRight.domNode.style.transform = `translateY(${layoutInfo.cellTopMargin}px)`; + } + } + renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { if (!this.notebookEditor.hasModel()) { throw new Error('The notebook editor is not attached with view model yet.'); @@ -710,8 +745,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.setupOutputCollapsedPart(templateData, cellOutputCollapsedContainer, element); const elementDisposables = templateData.elementDisposables; - const child = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); - elementDisposables.add(child.createInstance(CodeCell, this.notebookEditor, element, templateData)); + elementDisposables.add(templateData.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); const cellEditorOptions = new CellEditorOptions(this.notebookEditor, this.notebookEditor.notebookOptions, this.configurationService, element.language); @@ -760,8 +794,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForKernel(element, templateData); - templateData.cellToolbars.setupCellToolbarActions(templateData, elementDisposables); - + templateData.betweenCellToolbar.updateContext(element); const toolbarContext = { ui: true, cell: element, @@ -769,9 +802,14 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende notebookEditor: this.notebookEditor, $mid: MarshalledId.NotebookCellActionContext }; - templateData.cellToolbars.updateContext(element, templateData.elementDisposables); + templateData.titleToolbar.updateContext(toolbarContext); templateData.runToolbar.updateContext(toolbarContext); templateData.statusBar.updateContext(toolbarContext); + + templateData.elementDisposables.add(templateData.titleToolbar.onDidUpdateActions(() => { + // Don't call directly here - is initially called by updateForLayout in the next frame + this.updateForTitleMenu(templateData); + })); } disposeTemplate(templateData: CodeCellRenderTemplate): void { From 4dc57b1da6b152cd16aa32a4b1de7f999c18d36b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 14:42:18 -0800 Subject: [PATCH 186/330] Use injected contextKeyService in cell toolbar class --- .../contrib/notebook/browser/view/cellParts/cellToolbars.ts | 2 +- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 70e5759a632..dd61cd6b2b1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -29,11 +29,11 @@ export class BetweenCellToolbar extends Disposable { constructor( readonly notebookEditor: INotebookEditorDelegate, - readonly contextKeyService: IContextKeyService, readonly titleToolbarContainer: HTMLElement, readonly bottomCellToolbarContainer: HTMLElement, @IInstantiationService readonly instantiationService: IInstantiationService, @IContextMenuService readonly contextMenuService: IContextMenuService, + @IContextKeyService readonly contextKeyService: IContextKeyService, @IKeybindingService readonly keybindingService: IKeybindingService, @IMenuService readonly menuService: IMenuService ) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index b5778c314a3..978eaaafcbd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -221,7 +221,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen rootClassDelegate, this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, this.notebookEditor)); - const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellContainer)); + const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, titleToolbarContainer, bottomCellContainer)); const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')); const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); @@ -516,7 +516,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende rootClassDelegate, this.notebookEditor.creationOptions.menuIds.cellTitleToolbar, this.notebookEditor)); - const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, contextKeyService, titleToolbarContainer, bottomCellToolbarContainer)); + const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, titleToolbarContainer, bottomCellToolbarContainer)); const templateData: CodeCellRenderTemplate = { rootContainer, From 18743f736072461d06f52ba80c6031cc3a9ab8c7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 14:47:09 -0800 Subject: [PATCH 187/330] Simplify BetweenCellToolbar --- .../browser/view/cellParts/cellToolbars.ts | 47 +++++++------------ .../browser/view/renderers/cellRenderer.ts | 5 +- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index dd61cd6b2b1..f32814032ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -9,7 +9,6 @@ import { IAction } from 'vs/base/common/actions'; import { disposableTimeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { MarshalledId } from 'vs/base/common/marshalling'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -17,7 +16,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; @@ -28,28 +27,23 @@ export class BetweenCellToolbar extends Disposable { private _betweenCellToolbar!: ToolBar; constructor( - readonly notebookEditor: INotebookEditorDelegate, - readonly titleToolbarContainer: HTMLElement, - readonly bottomCellToolbarContainer: HTMLElement, - @IInstantiationService readonly instantiationService: IInstantiationService, - @IContextMenuService readonly contextMenuService: IContextMenuService, - @IContextKeyService readonly contextKeyService: IContextKeyService, - @IKeybindingService readonly keybindingService: IKeybindingService, - @IMenuService readonly menuService: IMenuService + private readonly _notebookEditor: INotebookEditorDelegate, + _titleToolbarContainer: HTMLElement, + private readonly _bottomCellToolbarContainer: HTMLElement, + @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService menuService: IMenuService ) { super(); - this.createBetweenCellToolbar(); - } - - createBetweenCellToolbar() { - this._betweenCellToolbar = this._register(new ToolBar(this.bottomCellToolbarContainer, this.contextMenuService, { + this._betweenCellToolbar = this._register(new ToolBar(this._bottomCellToolbarContainer, contextMenuService, { actionViewItemProvider: action => { if (action instanceof MenuItemAction) { - if (this.notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { - return this.instantiationService.createInstance(CodiconActionViewItem, action); + if (this._notebookEditor.notebookOptions.getLayoutConfiguration().insertToolbarAlignment === 'center') { + return instantiationService.createInstance(CodiconActionViewItem, action); } else { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + return instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); } } @@ -57,14 +51,14 @@ export class BetweenCellToolbar extends Disposable { } })); - const menu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellInsertToolbar, this.contextKeyService)); + const menu = this._register(menuService.createMenu(this._notebookEditor.creationOptions.menuIds.cellInsertToolbar, contextKeyService)); const updateActions = () => { const actions = getCellToolbarActions(menu); this._betweenCellToolbar.setActions(actions.primary, actions.secondary); }; this._register(menu.onDidChange(() => updateActions())); - this._register(this.notebookEditor.notebookOptions.onDidChangeOptions((e) => { + this._register(this._notebookEditor.notebookOptions.onDidChangeOptions((e) => { if (e.insertToolbarAlignment) { updateActions(); } @@ -72,20 +66,13 @@ export class BetweenCellToolbar extends Disposable { updateActions(); } - updateContext(element: CodeCellViewModel | MarkupCellViewModel) { - const toolbarContext = { - ui: true, - cell: element, - notebookEditor: this.notebookEditor, - $mid: MarshalledId.NotebookCellActionContext - }; - - this._betweenCellToolbar.context = toolbarContext; + updateContext(context: INotebookCellActionContext) { + this._betweenCellToolbar.context = context; } updateForLayout(element: CodeCellViewModel | MarkupCellViewModel) { const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; - this.bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; + this._bottomCellToolbarContainer.style.transform = `translateY(${bottomToolbarOffset}px)`; } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 978eaaafcbd..7f7068cdc65 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -274,8 +274,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.updateForLayout(element, templateData); })); - templateData.betweenCellToolbar.updateContext(element); - const markdownCell = templateData.instantiationService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); elementDisposables.add(markdownCell); @@ -285,6 +283,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen notebookEditor: this.notebookEditor, $mid: MarshalledId.NotebookCellActionContext }; + templateData.betweenCellToolbar.updateContext(toolbarContext); templateData.statusBar.updateContext(toolbarContext); } @@ -794,7 +793,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForKernel(element, templateData); - templateData.betweenCellToolbar.updateContext(element); const toolbarContext = { ui: true, cell: element, @@ -802,6 +800,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende notebookEditor: this.notebookEditor, $mid: MarshalledId.NotebookCellActionContext }; + templateData.betweenCellToolbar.updateContext(toolbarContext); templateData.titleToolbar.updateContext(toolbarContext); templateData.runToolbar.updateContext(toolbarContext); templateData.statusBar.updateContext(toolbarContext); From f280c5885f7790fd6f2377c9cca10845bda3bc50 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 14:56:38 -0800 Subject: [PATCH 188/330] Show focused border when output is focused --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index a7bdb81f507..3affea8857b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -760,6 +760,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD // boder should always show styleSheets.push(` + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container.cell-output-focus .cell-focus-indicator-left:before, .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left:before { border-color: var(--notebook-focused-cell-border-color) !important; } From 08890fbfe1809bbbb0d5ed85297fd9f8531a27ea Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 15:10:48 -0800 Subject: [PATCH 189/330] Fix #137399 --- .../contrib/notebook/browser/view/renderers/backLayerWebView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index fb24a88f2c0..59781739613 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -292,6 +292,7 @@ export class BackLayerWebView extends Disposable { #container > div.preview.dragging { background-color: var(--theme-background); + opacity: 0.5 !important; } .monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex img, From b2e3a42f9b17db70fe478761eb2dbfea016b6edb Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Wed, 17 Nov 2021 15:17:24 -0800 Subject: [PATCH 190/330] Split deprecation message first Fixes #131345 --- .../workbench/services/preferences/common/preferencesModels.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index c4f77c2470a..8579000624e 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -1009,7 +1009,8 @@ class SettingsContentBuilder { setting.descriptionRanges = []; const descriptionPreValue = indent + '// '; - for (let line of (setting.deprecationMessage ? [setting.deprecationMessage, ...setting.description] : setting.description)) { + const deprecationMessageLines = setting.deprecationMessage?.split(/\n/g) ?? []; + for (let line of [...deprecationMessageLines, ...setting.description]) { line = fixSettingLink(line); this._contentByLines.push(descriptionPreValue + line); From 8cb22ea15fc7deddad506f0ed8738183af686ece Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 17 Nov 2021 15:28:03 -0800 Subject: [PATCH 191/330] DIsplay `Select Kernel` in kernel picker (#137380) --- .../notebook/browser/contrib/editorStatusBar/editorStatusBar.ts | 2 +- .../notebook/browser/viewParts/notebookKernelActionViewItem.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index fdfbd20d35e..1c782a7dcb3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -347,7 +347,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { this._kernelInfoElement.clear(); let { selected, suggestions, all } = this._notebookKernelService.getMatchingKernel(notebook); - const suggested = suggestions.length === 1 ? suggestions[0] : undefined; + const suggested = (suggestions.length === 1 && all.length === 1) ? suggestions[0] : undefined; let isSuggested = false; if (all.length === 0) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 9fb10676a97..de591ff05a7 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -64,7 +64,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { this._action.enabled = true; - const selectedOrSuggested = info.selected ?? info.suggestions[0]; + const selectedOrSuggested = info.selected ?? (info.all.length === 1 && info.suggestions.length === 1 ? info.suggestions[0] : undefined); if (selectedOrSuggested) { // selected or suggested kernel this._action.label = selectedOrSuggested.label; From 333a215cb8262d76f6af06b235b91d68568cc6e6 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 15:39:01 -0800 Subject: [PATCH 192/330] Add CellProgressBar fix issue where progress bar doesn't appear when collapsing input while cell is running. Also small cleanup to cell dnd in renderer #131808 --- .../browser/view/cellParts/cellDnd.ts | 12 +++- .../browser/view/cellParts/cellProgressBar.ts | 56 +++++++++++++++++++ .../browser/view/notebookRenderingCommon.ts | 17 +++--- .../browser/view/renderers/cellRenderer.ts | 37 +++--------- 4 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd.ts index 13e42c99cfe..d69b228395a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd.ts @@ -15,8 +15,8 @@ import { cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/c const $ = DOM.$; -export const DRAGGING_CLASS = 'cell-dragging'; -export const GLOBAL_DRAG_CLASS = 'global-drag-active'; +const DRAGGING_CLASS = 'cell-dragging'; +const GLOBAL_DRAG_CLASS = 'global-drag-active'; type DragImageProvider = () => HTMLElement; @@ -97,6 +97,14 @@ export class CellDragAndDropController extends Disposable { }); } + renderElement(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + if (element.dragging) { + templateData.container.classList.add(DRAGGING_CLASS); + } else { + templateData.container.classList.remove(DRAGGING_CLASS); + } + } + private setInsertIndicatorVisibility(visible: boolean) { this.listInsertionIndicator.style.opacity = visible ? '1' : '0'; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts new file mode 100644 index 00000000000..00eac1b29e2 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; +import { NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export class CellProgressBar extends Disposable { + private readonly _progressBar: ProgressBar; + private readonly _collapsedProgressBar: ProgressBar; + + constructor( + editorContainer: HTMLElement, + collapsedInputContainer: HTMLElement) { + super(); + + this._progressBar = this._register(new ProgressBar(editorContainer)); + this._progressBar.hide(); + + this._collapsedProgressBar = this._register(new ProgressBar(collapsedInputContainer)); + this._collapsedProgressBar.hide(); + } + + updateForInternalMetadata(element: CodeCellViewModel, internalMetadata: NotebookCellInternalMetadata): void { + const progressBar = element.isInputCollapsed ? this._collapsedProgressBar : this._progressBar; + if (internalMetadata.runState === NotebookCellExecutionState.Executing && !internalMetadata.isPaused) { + showProgressBar(progressBar); + } else { + progressBar.hide(); + } + } + + updateForCellState(e: CellViewModelStateChangeEvent, element: CodeCellViewModel): void { + if (e.inputCollapsedChanged) { + if (element.isInputCollapsed) { + this._progressBar.hide(); + if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { + showProgressBar(this._collapsedProgressBar); + } + } else { + this._collapsedProgressBar.hide(); + if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { + showProgressBar(this._progressBar); + } + } + } + } +} + +function showProgressBar(progressBar: ProgressBar): void { + progressBar.infinite().show(500); +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 8bb1b303162..8432ea00093 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -3,27 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { Event } from 'vs/base/common/event'; -import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import type { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellProgressBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar'; +import { BetweenCellToolbar, CellTitleToolbarPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; +import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; -import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import type { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { BetweenCellToolbar, CellTitleToolbarPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export interface INotebookCellList { isDisposed: boolean; @@ -130,8 +130,7 @@ export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { outputShowMoreContainer: FastDomNode; focusSinkElement: HTMLElement; editor: ICodeEditor; - progressBar: ProgressBar; - collapsedProgressBar: ProgressBar; + progressBar: CellProgressBar; focusIndicatorRight: FastDomNode; focusIndicatorBottom: FastDomNode; dragHandle: FastDomNode; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 7f7068cdc65..4c6d8c7c270 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -7,7 +7,6 @@ import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { IAction } from 'vs/base/common/actions'; import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; @@ -36,8 +35,9 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; -import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd'; +import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions'; +import { CellProgressBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar'; import { BetweenCellToolbar, CellTitleToolbarPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCell'; @@ -47,7 +47,7 @@ import { BaseCellRenderTemplate, CodeCellRenderTemplate, MarkdownCellRenderTempl import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const $ = DOM.$; @@ -157,11 +157,7 @@ abstract class AbstractCellRenderer { generateCellTopDecorations(); - if (element.dragging) { - templateData.container.classList.add(DRAGGING_CLASS); - } else { - templateData.container.classList.remove(DRAGGING_CLASS); - } + this.dndController?.renderElement(element, templateData); templateData.elementDisposables.add(templateData.instantiationService.createInstance(CellContextKeyManager, this.notebookEditor, element)); } @@ -484,13 +480,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateDisposables.add(editor); - const progressBar = new ProgressBar(editorPart); - progressBar.hide(); - templateDisposables.add(progressBar); - - const collapsedProgressBar = new ProgressBar(cellInputCollapsedContainer); - collapsedProgressBar.hide(); - templateDisposables.add(collapsedProgressBar); + const progressBar = templateDisposables.add(new CellProgressBar(editorPart, cellInputCollapsedContainer)); const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); @@ -527,7 +517,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende decorationContainer, cellContainer, progressBar, - collapsedProgressBar, statusBar, focusIndicatorLeft: focusIndicator, focusIndicatorRight, @@ -664,13 +653,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const internalMetadata = element.internalMetadata; this.updateExecutionOrder(internalMetadata, templateData); - - const progressBar = element.isInputCollapsed ? templateData.collapsedProgressBar : templateData.progressBar; - if (internalMetadata.runState === NotebookCellExecutionState.Executing && !internalMetadata.isPaused) { - progressBar.infinite().show(500); - } else { - progressBar.hide(); - } + templateData.progressBar.updateForInternalMetadata(element, internalMetadata); } private updateForKernel(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { @@ -779,13 +762,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende cellEditorOptions.setLineNumbers(element.lineNumbers); } - if (e.inputCollapsedChanged) { - if (element.isInputCollapsed) { - templateData.progressBar.hide(); - } else { - templateData.collapsedProgressBar.hide(); - } - } + templateData.progressBar.updateForCellState(e, element); })); this.updateForOutputs(element, templateData); From 722cbe1910f9d6572fe0ce30c22220a16f1f3c72 Mon Sep 17 00:00:00 2001 From: Eric Cheng Date: Wed, 17 Nov 2021 20:34:57 -0500 Subject: [PATCH 193/330] Fixed minor typos and grammar mistakes in README and CONTRIBUTING (#134282) * changed minor typos, little grammar mistakes * fixed minor grammar mistakes in CONTRIBUTING --- CONTRIBUTING.md | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d547535187..3d6c01e8335 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ See the [Feedback Channels](https://github.com/microsoft/vscode/wiki/Feedback-Ch ## Reporting Issues -Have you identified a reproducible problem in VS Code? Have a feature request? We want to hear about it! Here's how you can make reporting your issue as effective as possible. +Have you identified a reproducible problem in VS Code? Have a feature request? We want to hear about it! Here's how you can report your issue as effectively as possible. ### Identify Where to Report @@ -43,7 +43,7 @@ If you cannot find an existing issue that describes your bug or feature, create File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue. -Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes. +Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar but have different causes. The more information you can provide, the more likely someone will be successful at reproducing the issue and finding a fix. diff --git a/README.md b/README.md index 1883fe4b76b..f6e8f39e2d5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Stu VS Code in action

-[Visual Studio Code](https://code.visualstudio.com) is a distribution of the `Code - OSS` repository with Microsoft specific customizations released under a traditional [Microsoft product license](https://code.visualstudio.com/License/). +[Visual Studio Code](https://code.visualstudio.com) is a distribution of the `Code - OSS` repository with Microsoft-specific customizations released under a traditional [Microsoft product license](https://code.visualstudio.com/License/). [Visual Studio Code](https://code.visualstudio.com) combines the simplicity of a code editor with what developers need for their core edit-build-debug cycle. It provides comprehensive code editing, navigation, and understanding support along with lightweight debugging, a rich extensibility model, and lightweight integration with existing tools. @@ -49,11 +49,11 @@ See our [wiki](https://github.com/microsoft/vscode/wiki/Feedback-Channels) for a ## Related Projects -Many of the core components and extensions to VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki). +Many of the core components and extensions to VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) repositories are separate from each other. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki). ## Bundled Extensions -VS Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many languages. Extensions that provide rich language support (code completion, Go to Definition) for a language have the suffix `language-features`. For example, the `json` extension provides coloring for `JSON` and the `json-language-features` provides rich language support for `JSON`. +VS Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many languages. Extensions that provide rich language support (code completion, Go to Definition) for a language have the suffix `language-features`. For example, the `json` extension provides coloring for `JSON` and the `json-language-features` extension provides rich language support for `JSON`. ## Development Container From bedf867b5b02c1c800fbaf4d6ce09cefbafa1592 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 17 Nov 2021 17:43:00 -0800 Subject: [PATCH 194/330] #131808 - Set up output collapsed part in cell template --- .../notebook/browser/view/renderers/cellRenderer.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 4c6d8c7c270..1257883c692 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -535,6 +535,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende toJSON: () => { return {}; } }; + this.setupOutputCollapsedPart(templateData); + this.dndController?.registerDragHandle(templateData, rootContainer, dragHandle.domNode, () => new CodeCellDragImageRenderer().getDragImage(templateData, templateData.editor, 'code')); templateDisposables.add(this.addCollapseClickCollapseHandler(templateData)); @@ -555,9 +557,10 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende return templateData; } - private setupOutputCollapsedPart(templateData: CodeCellRenderTemplate, cellOutputCollapseContainer: HTMLElement, element: CodeCellViewModel) { + private setupOutputCollapsedPart(templateData: CodeCellRenderTemplate) { + const cellOutputCollapseContainer = templateData.cellOutputCollapsedContainer; const placeholder = DOM.append(cellOutputCollapseContainer, $('span.expandOutputPlaceholder')) as HTMLElement; - placeholder.textContent = 'Outputs are collapsed'; + placeholder.textContent = localize('cellOutputsCollapsedMsg', "Outputs are collapsed"); const expandIcon = DOM.append(cellOutputCollapseContainer, $('span.expandOutputIcon')); expandIcon.classList.add(...CSSIcon.asClassNameArray(Codicon.more)); @@ -680,7 +683,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { - templateData.templateDisposables.add(DOM.scheduleAtNextAnimationFrame(() => { + templateData.elementDisposables.add(DOM.scheduleAtNextAnimationFrame(() => { const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); const bottomToolbarDimensions = this.notebookEditor.notebookOptions.computeBottomToolbarDimensions(this.notebookEditor.textModel?.viewType); templateData.focusIndicatorLeft.setHeight(element.layoutInfo.indicatorHeight); @@ -722,9 +725,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } templateData.outputContainer.domNode.innerText = ''; - const cellOutputCollapsedContainer = DOM.append(templateData.outputContainer.domNode, $('.output-collapse-container')); - templateData.cellOutputCollapsedContainer = cellOutputCollapsedContainer; - this.setupOutputCollapsedPart(templateData, cellOutputCollapsedContainer, element); + templateData.outputContainer.domNode.appendChild(templateData.cellOutputCollapsedContainer); const elementDisposables = templateData.elementDisposables; elementDisposables.add(templateData.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); From 05a71436c7239519cbc658aae78faf05cdc19276 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Nov 2021 08:25:02 +0100 Subject: [PATCH 195/330] tests - lift test to browser since it has no node.js deps --- .../api/mainThreadWorkspace.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/vs/workbench/test/{electron-browser => browser}/api/mainThreadWorkspace.test.ts (96%) diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadWorkspace.test.ts b/src/vs/workbench/test/browser/api/mainThreadWorkspace.test.ts similarity index 96% rename from src/vs/workbench/test/electron-browser/api/mainThreadWorkspace.test.ts rename to src/vs/workbench/test/browser/api/mainThreadWorkspace.test.ts index ef17845c3b9..39d82eb14b0 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadWorkspace.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadWorkspace.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ISearchService, IFileQuery } from 'vs/workbench/services/search/common/search'; import { MainThreadWorkspace } from 'vs/workbench/api/browser/mainThreadWorkspace'; @@ -22,7 +22,7 @@ suite('MainThreadWorkspace', () => { setup(() => { disposables = new DisposableStore(); - instantiationService = workbenchInstantiationService(disposables) as TestInstantiationService; + instantiationService = workbenchInstantiationService(undefined, disposables) as TestInstantiationService; configService = instantiationService.get(IConfigurationService) as TestConfigurationService; configService.setUserConfiguration('search', {}); From 999507ad645b0f9ac4fb6a551f306c6de08ea65a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Nov 2021 08:37:43 +0100 Subject: [PATCH 196/330] tests - merge keytar test into native modules test --- src/vs/base/test/node/keytar.test.ts | 31 ------------------- .../test/node/nativeModules.test.ts | 30 +++++++++++++----- 2 files changed, 23 insertions(+), 38 deletions(-) delete mode 100644 src/vs/base/test/node/keytar.test.ts diff --git a/src/vs/base/test/node/keytar.test.ts b/src/vs/base/test/node/keytar.test.ts deleted file mode 100644 index 4e187264b29..00000000000 --- a/src/vs/base/test/node/keytar.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { isLinux } from 'vs/base/common/platform'; - -suite('Keytar', () => { - - (isLinux ? test.skip : test)('loads and is functional', async () => { // TODO@RMacfarlane test seems to fail on Linux (Error: Unknown or unsupported transport 'disabled' for address 'disabled:') - const keytar = await import('keytar'); - const name = `VSCode Test ${Math.floor(Math.random() * 1e9)}`; - try { - await keytar.setPassword(name, 'foo', 'bar'); - assert.strictEqual(await keytar.findPassword(name), 'bar'); - assert.strictEqual((await keytar.findCredentials(name)).length, 1); - assert.strictEqual(await keytar.getPassword(name, 'foo'), 'bar'); - await keytar.deletePassword(name, 'foo'); - assert.strictEqual(await keytar.getPassword(name, 'foo'), null); - } catch (err) { - // try to clean up - try { - await keytar.deletePassword(name, 'foo'); - } finally { - // eslint-disable-next-line no-unsafe-finally - throw err; - } - } - }); -}); diff --git a/src/vs/platform/environment/test/node/nativeModules.test.ts b/src/vs/platform/environment/test/node/nativeModules.test.ts index 130d0561679..c59af13e372 100644 --- a/src/vs/platform/environment/test/node/nativeModules.test.ts +++ b/src/vs/platform/environment/test/node/nativeModules.test.ts @@ -37,20 +37,36 @@ suite('Native Modules (all platforms)', () => { assert.ok(typeof spdlog.createRotatingLogger === 'function', testErrorMessage('spdlog')); }); - test('nsfw', async () => { - const nsfWatcher = await import('vscode-nsfw'); - assert.ok(typeof nsfWatcher === 'function', testErrorMessage('nsfw')); - }); - - test('parcel', async () => { + test('@parcel/watcher', async () => { const parcelWatcher = await import('@parcel/watcher'); assert.ok(typeof parcelWatcher.subscribe === 'function', testErrorMessage('parcel')); }); - test('sqlite3', async () => { + test('@vscode/sqlite3', async () => { const sqlite3 = await import('@vscode/sqlite3'); assert.ok(typeof sqlite3.Database === 'function', testErrorMessage('@vscode/sqlite3')); }); + + test('keytar', async () => { + const keytar = await import('keytar'); + const name = `VSCode Test ${Math.floor(Math.random() * 1e9)}`; + try { + await keytar.setPassword(name, 'foo', 'bar'); + assert.strictEqual(await keytar.findPassword(name), 'bar'); + assert.strictEqual((await keytar.findCredentials(name)).length, 1); + assert.strictEqual(await keytar.getPassword(name, 'foo'), 'bar'); + await keytar.deletePassword(name, 'foo'); + assert.strictEqual(await keytar.getPassword(name, 'foo'), null); + } catch (err) { + // try to clean up + try { + await keytar.deletePassword(name, 'foo'); + } finally { + // eslint-disable-next-line no-unsafe-finally + throw err; + } + } + }); }); (!isWindows ? suite.skip : suite)('Native Modules (Windows)', () => { From 90a67d4013ea62e4751b463c02209e2fe8839416 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Nov 2021 09:06:29 +0100 Subject: [PATCH 197/330] fix tests --- .../test/node/nativeModules.test.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/environment/test/node/nativeModules.test.ts b/src/vs/platform/environment/test/node/nativeModules.test.ts index c59af13e372..f1379a6ff6f 100644 --- a/src/vs/platform/environment/test/node/nativeModules.test.ts +++ b/src/vs/platform/environment/test/node/nativeModules.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isWindows } from 'vs/base/common/platform'; +import { isLinux, isWindows } from 'vs/base/common/platform'; function testErrorMessage(module: string): string { return `Unable to load "${module}" dependency. It was probably not compiled for the right operating system architecture or had missing build tools.`; @@ -46,6 +46,9 @@ suite('Native Modules (all platforms)', () => { const sqlite3 = await import('@vscode/sqlite3'); assert.ok(typeof sqlite3.Database === 'function', testErrorMessage('@vscode/sqlite3')); }); +}); + +(isLinux ? suite.skip : suite)('Native Modules (Windows, macOS)', () => { test('keytar', async () => { const keytar = await import('keytar'); @@ -58,13 +61,11 @@ suite('Native Modules (all platforms)', () => { await keytar.deletePassword(name, 'foo'); assert.strictEqual(await keytar.getPassword(name, 'foo'), null); } catch (err) { - // try to clean up try { - await keytar.deletePassword(name, 'foo'); - } finally { - // eslint-disable-next-line no-unsafe-finally - throw err; - } + await keytar.deletePassword(name, 'foo'); // try to clean up + } catch { } + + throw err; } }); }); @@ -93,7 +94,9 @@ suite('Native Modules (all platforms)', () => { }); test('vscode-windows-ca-certs', async () => { - // @ts-ignore Windows only + // @ts-ignore we do not directly depend on this module anymore + // but indirectly from our dependency to `vscode-proxy-agent` + // we still want to ensure this module can work properly. const windowsCerts = await import('vscode-windows-ca-certs'); const store = new windowsCerts.Crypt32(); assert.ok(windowsCerts, testErrorMessage('vscode-windows-ca-certs')); From 6651f8ab436fa7e2061f59e53ff45411bb6b3101 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 18 Nov 2021 14:46:54 +0100 Subject: [PATCH 198/330] [json] Adapt language status indicator. Fixes #137288 --- .../client/src/jsonClient.ts | 10 +++ .../client/src/languageStatus.ts | 83 +++++++++++++++++++ .../client/tsconfig.json | 3 +- .../json-language-features/package.json | 6 +- .../server/package.json | 2 +- .../server/src/jsonServer.ts | 16 ++++ .../json-language-features/server/yarn.lock | 8 +- 7 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 extensions/json-language-features/client/src/languageStatus.ts diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 8fb6dda91e1..eeae0d3f03a 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -6,6 +6,8 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); +export type JSONLanguageStatus = { schemas: string[] }; + import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, @@ -19,6 +21,7 @@ import { import { hash } from './utils/hash'; import { RequestService, joinPath } from './requests'; +import { createLanguageStatusItem } from './languageStatus'; namespace VSCodeContentRequest { export const type: RequestType = new RequestType('vscode/content'); @@ -32,6 +35,11 @@ namespace ForceValidateRequest { export const type: RequestType = new RequestType('json/validate'); } +namespace LanguageStatusRequest { + export const type: RequestType = new RequestType('json/languageStatus'); +} + + export interface ISchemaAssociations { [pattern: string]: string[]; } @@ -314,6 +322,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua } }); + toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri))); + function updateFormatterRegistration() { const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter); if (!formatEnabled && rangeFormatting) { diff --git a/extensions/json-language-features/client/src/languageStatus.ts b/extensions/json-language-features/client/src/languageStatus.ts new file mode 100644 index 00000000000..f06a750424b --- /dev/null +++ b/extensions/json-language-features/client/src/languageStatus.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { window, languages, Uri, LanguageStatusSeverity, Disposable, commands, QuickPickItem } from 'vscode'; +import { JSONLanguageStatus } from './jsonClient'; + +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export function createLanguageStatusItem(documentSelector: string[], statusRequest: (uri: string) => Promise): Disposable { + const statusItem = languages.createLanguageStatusItem('json.projectStatus', documentSelector); + statusItem.name = localize('statusItem.name', "JSON Validation Status"); + statusItem.severity = LanguageStatusSeverity.Information; + + const showSchemasCommand = commands.registerCommand('json.showSchemasCommand', arg => { + const items = arg.schemas.sort().map((a: string) => ({ label: a })); + const quickPick = window.createQuickPick(); + quickPick.title = localize('schemaPicker.title', 'Associated JSON Schemas'); + quickPick.placeholder = localize('schemaPicker.placeholder', 'Select the schema to open'); + quickPick.items = items; + quickPick.show(); + quickPick.onDidAccept(() => { + const selectedSchema = quickPick.selectedItems[0].label; + commands.executeCommand('vscode.open', Uri.parse(selectedSchema)); + quickPick.dispose(); + }); + }); + + const activeEditorListener = window.onDidChangeActiveTextEditor(() => { + updateLanguageStatus(); + }); + + async function updateLanguageStatus() { + const document = window.activeTextEditor?.document; + if (document && documentSelector.indexOf(document.languageId) !== -1) { + try { + statusItem.text = '$(loading~spin)'; + statusItem.detail = localize('pending.detail', 'Loading JSON info'); + statusItem.command = undefined; + + const schemas = (await statusRequest(document.uri.toString())).schemas; + statusItem.detail = undefined; + if (schemas.length === 0) { + statusItem.text = localize('status.noSchema', 'Validated without JSON schema'); + } else if (schemas.length === 1) { + statusItem.text = localize('status.singleSchema', 'Validated with JSON schema'); + statusItem.command = { + command: 'vscode.open', + title: localize('status.openSchemaLink', 'Open Schema'), + tooltip: schemas[0], + arguments: [Uri.parse(schemas[0])] + }; + } else { + statusItem.text = localize('status.multipleSchema', 'Validated with multiple JSON schemas'); + statusItem.command = { + command: 'json.showSchemasCommand', + title: localize('status.openSchemasLink', 'Show Schemas'), + arguments: [{ schemas }] + }; + } + } catch (e) { + statusItem.text = localize('status.error', 'Unable to compute used schemas'); + statusItem.detail = undefined; + statusItem.command = undefined; + console.log(e); + } + } else { + statusItem.text = localize('status.notJSON', 'Not a JSON editor'); + statusItem.detail = undefined; + statusItem.command = undefined; + } + } + + updateLanguageStatus(); + + return Disposable.from(statusItem, activeEditorListener, showSchemasCommand); +} + + + diff --git a/extensions/json-language-features/client/tsconfig.json b/extensions/json-language-features/client/tsconfig.json index 573b24b4aa6..4254a37490e 100644 --- a/extensions/json-language-features/client/tsconfig.json +++ b/extensions/json-language-features/client/tsconfig.json @@ -5,6 +5,7 @@ }, "include": [ "src/**/*", - "../../../src/vscode-dts/vscode.d.ts" + "../../../src/vscode-dts/vscode.d.ts", + "../../../src/vscode-dts/vscode.proposed.languageStatus.d.ts", ] } diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 6a6fb61c9a6..6c5a15102cd 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -12,7 +12,8 @@ "icon": "icons/json.png", "activationEvents": [ "onLanguage:json", - "onLanguage:jsonc" + "onLanguage:jsonc", + "onCommand:json.showSchemas" ], "main": "./client/out/node/jsonClientMain", "browser": "./client/dist/browser/jsonClientMain", @@ -22,6 +23,9 @@ "supported": true } }, + "enabledApiProposals": [ + "languageStatus" + ], "scripts": { "compile": "npx gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", "watch": "npx gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 5c8bb31f68b..90ec7eb10d3 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,7 +14,7 @@ "dependencies": { "jsonc-parser": "^3.0.0", "request-light": "^0.5.4", - "vscode-json-languageservice": "^4.1.10", + "vscode-json-languageservice": "^4.2.0-next.1", "vscode-languageserver": "^7.0.0", "vscode-uri": "^3.0.2" }, diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index ffca9f40153..eb970784d9b 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -16,6 +16,8 @@ import { RequestService, basename, resolvePath } from './requests'; type ISchemaAssociations = Record; +type JSONLanguageStatus = { schemas: string[] }; + namespace SchemaAssociationNotification { export const type: NotificationType = new NotificationType('json/schemaAssociations'); } @@ -36,6 +38,10 @@ namespace ForceValidateRequest { export const type: RequestType = new RequestType('json/validate'); } +namespace LanguageStatusRequest { + export const type: RequestType = new RequestType('json/languageStatus'); +} + const workspaceContext = { resolveRelativePath: (relativePath: string, resource: string) => { @@ -277,6 +283,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); }); + connection.onRequest(LanguageStatusRequest.type, async uri => { + const document = documents.get(uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.getLanguageStatus(document, jsonDocument); + } else { + return { schemas: [] }; + } + }); + function updateConfiguration() { const languageSettings = { validate: true, diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 25e3ca55845..9b542a82f9c 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -22,10 +22,10 @@ request-light@^0.5.4: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.4.tgz#497a98c6d8ae49536417a5e2d7f383b934f3e38c" integrity sha512-t3566CMweOFlUk7Y1DJMu5OrtpoZEb6aSTsLQVT3wtrIEJ5NhcY9G/Oqxvjllzl4a15zXfFlcr9q40LbLVQJqw== -vscode-json-languageservice@^4.1.10: - version "4.1.10" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.1.10.tgz#5d5729fc4f3e02f41599e0104523a1877c25f0fb" - integrity sha512-IHliMEEYSY0tJjJt0ECb8ESx/nRXpoy9kN42WVQXgaqGyizFAf3jibSiezDQTrrY7f3kywXggCU+kkJEM+OLZQ== +vscode-json-languageservice@^4.2.0-next.1: + version "4.2.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.0-next.1.tgz#31a8c3be04c87d5aa593c11b98d84258b173a22f" + integrity sha512-aQvkkuZpeSPv86QLzyMdKTCgvXR+qSO39nSgj/XGaOcuHmTt7vMZB7ymYGGkQ4cAaQdHs/2G6a479LQybIGSbg== dependencies: jsonc-parser "^3.0.0" vscode-languageserver-textdocument "^1.0.1" From 24ba24e3298baf980cd6f9ea68b29353f7a45aa9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 18 Nov 2021 15:28:45 +0100 Subject: [PATCH 199/330] Fixes #137337: Respect theme colors when generating CSS for scrollbars --- src/vs/base/browser/ui/menu/menu.ts | 137 ++++++++++++------------- src/vs/platform/theme/common/styler.ts | 8 +- 2 files changed, 72 insertions(+), 73 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index dd3ec826f13..6469e3f3b45 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -59,6 +59,10 @@ export interface IMenuStyles { selectionBackgroundColor?: Color; selectionBorderColor?: Color; separatorColor?: Color; + scrollbarShadow?: Color; + scrollbarSliderBackground?: Color; + scrollbarSliderHoverBackground?: Color; + scrollbarSliderActiveBackground?: Color; } interface ISubMenuData { @@ -99,8 +103,6 @@ export class Menu extends ActionBar { this.menuDisposables = this._register(new DisposableStore()); - this.initializeStyleSheet(container); - this._register(Gesture.addTarget(menuElement)); addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { @@ -262,23 +264,25 @@ export class Menu extends ActionBar { }); } - private initializeStyleSheet(container: HTMLElement): void { - if (isInShadowDOM(container)) { - this.styleSheet = createStyleSheet(container); - this.styleSheet.textContent = MENU_WIDGET_CSS; - } else { - if (!Menu.globalStyleSheet) { - Menu.globalStyleSheet = createStyleSheet(); - Menu.globalStyleSheet.textContent = MENU_WIDGET_CSS; + private initializeOrUpdateStyleSheet(container: HTMLElement, style: IMenuStyles): void { + if (!this.styleSheet) { + if (isInShadowDOM(container)) { + this.styleSheet = createStyleSheet(container); + } else { + if (!Menu.globalStyleSheet) { + Menu.globalStyleSheet = createStyleSheet(); + } + this.styleSheet = Menu.globalStyleSheet; } - - this.styleSheet = Menu.globalStyleSheet; } + this.styleSheet.textContent = getMenuWidgetCSS(style); } style(style: IMenuStyles): void { const container = this.getContainer(); + this.initializeOrUpdateStyleSheet(container, style); + const fgColor = style.foregroundColor ? `${style.foregroundColor}` : ''; const bgColor = style.backgroundColor ? `${style.backgroundColor}` : ''; const border = style.borderColor ? `1px solid ${style.borderColor}` : ''; @@ -1016,7 +1020,8 @@ export function cleanMnemonic(label: string): string { return label.replace(regex, mnemonicInText ? '$2$3' : '').trim(); } -let MENU_WIDGET_CSS: string = /* css */` +function getMenuWidgetCSS(style: IMenuStyles): string { + let result = /* css */` .monaco-menu { font-size: 13px; @@ -1339,7 +1344,6 @@ ${formatRule(Codicon.menuSubmenu)} left: 3px; height: 3px; width: 100%; - box-shadow: #DDD 0 6px 6px -6px inset; } .monaco-scrollable-element > .shadow.left { display: block; @@ -1347,7 +1351,6 @@ ${formatRule(Codicon.menuSubmenu)} left: 0; height: 100%; width: 3px; - box-shadow: #DDD 6px 0 6px -6px inset; } .monaco-scrollable-element > .shadow.top-left-corner { display: block; @@ -1356,60 +1359,52 @@ ${formatRule(Codicon.menuSubmenu)} height: 3px; width: 3px; } -.monaco-scrollable-element > .shadow.top.left { - box-shadow: #DDD 6px 6px 6px -6px inset; -} - -/* ---------- Default Style ---------- */ - -:host-context(.vs) .monaco-scrollable-element > .scrollbar > .slider { - background: rgba(100, 100, 100, .4); -} -:host-context(.vs-dark) .monaco-scrollable-element > .scrollbar > .slider { - background: rgba(121, 121, 121, .4); -} -:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider { - background: rgba(111, 195, 223, .6); -} - -.monaco-scrollable-element > .scrollbar > .slider:hover { - background: rgba(100, 100, 100, .7); -} -:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider:hover { - background: rgba(111, 195, 223, .8); -} - -.monaco-scrollable-element > .scrollbar > .slider.active { - background: rgba(0, 0, 0, .6); -} -:host-context(.vs-dark) .monaco-scrollable-element > .scrollbar > .slider.active { - background: rgba(191, 191, 191, .4); -} -:host-context(.hc-black) .monaco-scrollable-element > .scrollbar > .slider.active { - background: rgba(111, 195, 223, 1); -} - -:host-context(.vs-dark) .monaco-scrollable-element .shadow.top { - box-shadow: none; -} - -:host-context(.vs-dark) .monaco-scrollable-element .shadow.left { - box-shadow: #000 6px 0 6px -6px inset; -} - -:host-context(.vs-dark) .monaco-scrollable-element .shadow.top.left { - box-shadow: #000 6px 6px 6px -6px inset; -} - -:host-context(.hc-black) .monaco-scrollable-element .shadow.top { - box-shadow: none; -} - -:host-context(.hc-black) .monaco-scrollable-element .shadow.left { - box-shadow: none; -} - -:host-context(.hc-black) .monaco-scrollable-element .shadow.top.left { - box-shadow: none; -} `; + + // Scrollbars + const scrollbarShadowColor = style.scrollbarShadow; + if (scrollbarShadowColor) { + result += ` + .monaco-scrollable-element > .shadow.top { + box-shadow: ${scrollbarShadowColor} 0 6px 6px -6px inset; + } + + .monaco-scrollable-element > .shadow.left { + box-shadow: ${scrollbarShadowColor} 6px 0 6px -6px inset; + } + + .monaco-scrollable-element > .shadow.top.left { + box-shadow: ${scrollbarShadowColor} 6px 6px 6px -6px inset; + } + `; + } + + const scrollbarSliderBackgroundColor = style.scrollbarSliderBackground; + if (scrollbarSliderBackgroundColor) { + result += ` + .monaco-scrollable-element > .scrollbar > .slider { + background: ${scrollbarSliderBackgroundColor}; + } + `; + } + + const scrollbarSliderHoverBackgroundColor = style.scrollbarSliderHoverBackground; + if (scrollbarSliderHoverBackgroundColor) { + result += ` + .monaco-scrollable-element > .scrollbar > .slider:hover { + background: ${scrollbarSliderHoverBackgroundColor}; + } + `; + } + + const scrollbarSliderActiveBackgroundColor = style.scrollbarSliderActiveBackground; + if (scrollbarSliderActiveBackgroundColor) { + result += ` + .monaco-scrollable-element > .scrollbar > .slider.active { + background: ${scrollbarSliderActiveBackgroundColor}; + } + `; + } + + return result; +} diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index b4dac78d2a6..b69d95e8ad9 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -6,7 +6,7 @@ import { Color } from 'vs/base/common/color'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IThemable, styleFn } from 'vs/base/common/styler'; -import { activeContrastBorder, badgeBackground, badgeForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, breadcrumbsFocusForeground, breadcrumbsForeground, buttonBackground, buttonBorder, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, ColorIdentifier, ColorTransform, ColorValue, contrastBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetForeground, focusBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, keybindingLabelForeground, listActiveSelectionBackground, listActiveSelectionForeground, listActiveSelectionIconForeground, listDropBackground, listFilterWidgetBackground, listFilterWidgetNoMatchesOutline, listFilterWidgetOutline, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, listInactiveFocusBackground, listInactiveFocusOutline, listInactiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionIconForeground, menuBackground, menuBorder, menuForeground, menuSelectionBackground, menuSelectionBorder, menuSelectionForeground, menuSeparatorBackground, pickerGroupForeground, problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground, progressBarBackground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, resolveColorValue, selectBackground, selectBorder, selectForeground, selectListBackground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, tableColumnsBorder, tableOddRowsBackgroundColor, textLinkForeground, treeIndentGuidesStroke, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, badgeBackground, badgeForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, breadcrumbsFocusForeground, breadcrumbsForeground, buttonBackground, buttonBorder, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, ColorIdentifier, ColorTransform, ColorValue, contrastBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetForeground, focusBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, keybindingLabelForeground, listActiveSelectionBackground, listActiveSelectionForeground, listActiveSelectionIconForeground, listDropBackground, listFilterWidgetBackground, listFilterWidgetNoMatchesOutline, listFilterWidgetOutline, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, listInactiveFocusBackground, listInactiveFocusOutline, listInactiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionIconForeground, menuBackground, menuBorder, menuForeground, menuSelectionBackground, menuSelectionBorder, menuSelectionForeground, menuSeparatorBackground, pickerGroupForeground, problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground, progressBarBackground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, resolveColorValue, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, selectBackground, selectBorder, selectForeground, selectListBackground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, tableColumnsBorder, tableOddRowsBackgroundColor, textLinkForeground, treeIndentGuidesStroke, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; @@ -314,7 +314,11 @@ export const defaultMenuStyles = { selectionForegroundColor: menuSelectionForeground, selectionBackgroundColor: menuSelectionBackground, selectionBorderColor: menuSelectionBorder, - separatorColor: menuSeparatorBackground + separatorColor: menuSeparatorBackground, + scrollbarShadow: scrollbarShadow, + scrollbarSliderBackground: scrollbarSliderBackground, + scrollbarSliderHoverBackground: scrollbarSliderHoverBackground, + scrollbarSliderActiveBackground: scrollbarSliderActiveBackground }; export function attachMenuStyler(widget: IThemable, themeService: IThemeService, style?: IMenuStyleOverrides): IDisposable { From acfb946ec167ef482c24c348e22e90d104eda9b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 18 Nov 2021 15:57:11 +0100 Subject: [PATCH 200/330] ci: :construction_worker: no need for yarninstaller task --- build/azure-pipelines/linux/product-build-alpine.yml | 10 +++------- build/azure-pipelines/linux/product-build-linux.yml | 4 ---- build/azure-pipelines/linux/snap-build-linux.yml | 4 ---- build/azure-pipelines/product-compile.yml | 6 +----- build/azure-pipelines/product-publish.yml | 8 ++------ build/azure-pipelines/product-release.yml | 6 +----- build/azure-pipelines/publish-types/publish-types.yml | 4 ---- build/azure-pipelines/sdl-scan.yml | 8 -------- build/azure-pipelines/web/product-build-web.yml | 8 ++------ build/azure-pipelines/win32/product-build-win32.yml | 4 ---- 10 files changed, 9 insertions(+), 53 deletions(-) diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index 2a74a37f5b0..0a2dd05f28b 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -3,16 +3,12 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'github-distro-mixin-password' + SecretsFilter: "github-distro-mixin-password" - task: DownloadPipelineArtifact@2 inputs: @@ -58,7 +54,7 @@ steps: - task: Cache@2 inputs: - key: 'nodeModules | $(Agent.OS) | .build/yarnlockhash' + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" path: .build/node_modules_cache cacheHitVar: NODE_MODULES_RESTORED displayName: Restore node_modules cache @@ -107,7 +103,7 @@ steps: displayName: Mix in quality - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - displayName: 'Register Docker QEMU' + displayName: "Register Docker QEMU" condition: eq(variables['VSCODE_ARCH'], 'arm64') - script: | diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index a1bcb6a8a90..c2d79d6067d 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -3,10 +3,6 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index a668fec06fb..35c0a03e383 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -3,10 +3,6 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: DownloadPipelineArtifact@0 displayName: "Download Pipeline Artifact" inputs: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index b8c3fd4140c..5458cae8a09 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -3,16 +3,12 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'github-distro-mixin-password,ticino-storage-key' + SecretsFilter: "github-distro-mixin-password,ticino-storage-key" - script: | set -e diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 6a4406f6a26..44c8835b360 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -3,16 +3,12 @@ steps: inputs: versionSpec: "12.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'builds-docdb-key-readwrite,github-distro-mixin-password,ticino-storage-key,vscode-storage-key,vscode-mooncake-storage-key' + SecretsFilter: "builds-docdb-key-readwrite,github-distro-mixin-password,ticino-storage-key,vscode-storage-key,vscode-mooncake-storage-key" - pwsh: | . build/azure-pipelines/win32/exec.ps1 @@ -21,7 +17,7 @@ steps: displayName: Install dependencies - download: current - patterns: '**/artifacts_processed_*.txt' + patterns: "**/artifacts_processed_*.txt" displayName: Download all artifacts_processed text files - pwsh: | diff --git a/build/azure-pipelines/product-release.yml b/build/azure-pipelines/product-release.yml index d62723be90e..823dca38acf 100644 --- a/build/azure-pipelines/product-release.yml +++ b/build/azure-pipelines/product-release.yml @@ -3,16 +3,12 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'builds-docdb-key-readwrite' + SecretsFilter: "builds-docdb-key-readwrite" - script: | set -e diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index f557e56279e..043bb5141ba 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -14,10 +14,6 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - bash: | TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) CHANNEL="C1C14HJ2F" diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index edccd0845b0..b1976140bd0 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -49,10 +49,6 @@ stages: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: @@ -144,10 +140,6 @@ stages: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 4977207b896..608f3a1701a 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -3,16 +3,12 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'github-distro-mixin-password,web-storage-account,web-storage-key,ticino-storage-key' + SecretsFilter: "github-distro-mixin-password,web-storage-account,web-storage-key,ticino-storage-key" - task: DownloadPipelineArtifact@2 inputs: @@ -49,7 +45,7 @@ steps: - task: Cache@2 inputs: - key: 'nodeModules | $(Agent.OS) | .build/yarnlockhash' + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" path: .build/node_modules_cache cacheHitVar: NODE_MODULES_RESTORED displayName: Restore node_modules cache diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ccd863c615b..3e7687a434c 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -3,10 +3,6 @@ steps: inputs: versionSpec: "14.x" - - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" - - task: UsePythonVersion@0 inputs: versionSpec: "2.x" From 4a1a8b07ff093e7dcb8067c185eec49502b56aa5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 18 Nov 2021 16:39:48 +0100 Subject: [PATCH 201/330] Cement launching the extension host from a worker on the main process --- .../sharedProcess/sharedProcessMain.ts | 9 - .../directMainProcessExtensionHostStarter.ts | 24 -- .../workerMainProcessExtensionHostStarter.ts | 3 +- .../extensions/node/extensionHostStarter.ts | 237 ------------------ .../node/extensionHostStarterWorker.ts | 232 ++++++++++++++++- .../localProcessExtensionHost.ts | 16 +- .../electron-sandbox/extensionHostStarter.ts | 10 +- 7 files changed, 228 insertions(+), 303 deletions(-) delete mode 100644 src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts delete mode 100644 src/vs/platform/extensions/node/extensionHostStarter.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 80d479c2284..e3077d66510 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -84,8 +84,6 @@ import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyn import { UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService'; import { ActiveWindowManager } from 'vs/platform/windows/node/windowTracker'; -import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; -import { ExtensionHostStarter } from 'vs/platform/extensions/node/extensionHostStarter'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/node/signService'; import { ISharedTunnelsService } from 'vs/platform/remote/common/tunnel'; @@ -337,9 +335,6 @@ class SharedProcessMain extends Disposable { // Terminal services.set(ILocalPtyService, this._register(ptyHostService)); - // Extension Host - services.set(IExtensionHostStarter, this._register(new ExtensionHostStarter(logService))); - // Signing services.set(ISignService, new SyncDescriptor(SignService)); @@ -398,10 +393,6 @@ class SharedProcessMain extends Disposable { const localPtyChannel = ProxyChannel.fromService(localPtyService); this.server.registerChannel(TerminalIpcChannels.LocalPty, localPtyChannel); - // Extension Host - const extensionHostStarterChannel = ProxyChannel.fromService(accessor.get(IExtensionHostStarter)); - this.server.registerChannel(ipcExtensionHostStarterChannelName, extensionHostStarterChannel); - // Tunnel const sharedProcessTunnelChannel = ProxyChannel.fromService(accessor.get(ISharedProcessTunnelService)); this.server.registerChannel(ipcSharedProcessTunnelChannelName, sharedProcessTunnelChannel); diff --git a/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts deleted file mode 100644 index c503cf3ed02..00000000000 --- a/src/vs/platform/extensions/electron-main/directMainProcessExtensionHostStarter.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionHostStarter, IPartialLogService } from 'vs/platform/extensions/node/extensionHostStarter'; -import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { ILogService } from 'vs/platform/log/common/log'; - -export class DirectMainProcessExtensionHostStarter extends ExtensionHostStarter { - - constructor( - @ILogService logService: IPartialLogService, - @ILifecycleMainService lifecycleMainService: ILifecycleMainService - ) { - super(logService); - - // On shutdown: gracefully await extension host shutdowns - lifecycleMainService.onWillShutdown((e) => { - e.join(this.waitForAllExit(6000)); - }); - } - -} diff --git a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts index c59eb65e452..dfc7164026f 100644 --- a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts @@ -11,9 +11,8 @@ import { FileAccess } from 'vs/base/common/network'; import { ILogService } from 'vs/platform/log/common/log'; import { Worker } from 'worker_threads'; import { IWorker, IWorkerCallback, IWorkerFactory, SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker'; -import { IExtensionHostStarterWorkerHost } from 'vs/platform/extensions/node/extensionHostStarterWorker'; +import type { ExtensionHostStarter, IExtensionHostStarterWorkerHost } from 'vs/platform/extensions/node/extensionHostStarterWorker'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { ExtensionHostStarter } from 'vs/platform/extensions/node/extensionHostStarter'; class NodeWorker implements IWorker { diff --git a/src/vs/platform/extensions/node/extensionHostStarter.ts b/src/vs/platform/extensions/node/extensionHostStarter.ts deleted file mode 100644 index c71a39fb46d..00000000000 --- a/src/vs/platform/extensions/node/extensionHostStarter.ts +++ /dev/null @@ -1,237 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { SerializedError, transformErrorForSerialization } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; -import { Emitter, Event } from 'vs/base/common/event'; -import { ChildProcess, fork } from 'child_process'; -import { FileAccess } from 'vs/base/common/network'; -import { StringDecoder } from 'string_decoder'; -import * as platform from 'vs/base/common/platform'; -import { ILogService } from 'vs/platform/log/common/log'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { mixin } from 'vs/base/common/objects'; -import { cwd } from 'vs/base/common/process'; -import { StopWatch } from 'vs/base/common/stopwatch'; -import { Promises, timeout } from 'vs/base/common/async'; - -export interface IPartialLogService { - readonly _serviceBrand: undefined; - info(message: string): void; -} - -class ExtensionHostProcess extends Disposable { - - readonly _onStdout = this._register(new Emitter()); - readonly onStdout = this._onStdout.event; - - readonly _onStderr = this._register(new Emitter()); - readonly onStderr = this._onStderr.event; - - readonly _onMessage = this._register(new Emitter()); - readonly onMessage = this._onMessage.event; - - readonly _onError = this._register(new Emitter<{ error: SerializedError; }>()); - readonly onError = this._onError.event; - - readonly _onExit = this._register(new Emitter<{ pid: number; code: number; signal: string }>()); - readonly onExit = this._onExit.event; - - private _process: ChildProcess | null = null; - private _hasExited: boolean = false; - - constructor( - public readonly id: string, - @ILogService private readonly _logService: IPartialLogService - ) { - super(); - } - - start(opts: IExtensionHostProcessOptions): { pid: number; } { - const sw = StopWatch.create(false); - this._process = fork( - FileAccess.asFileUri('bootstrap-fork', require).fsPath, - ['--type=extensionHost', '--skipWorkspaceStorageLock'], - mixin({ cwd: cwd() }, opts), - ); - const forkTime = sw.elapsed(); - const pid = this._process.pid; - - this._logService.info(`Starting extension host with pid ${pid} (fork() took ${forkTime} ms).`); - - const stdoutDecoder = new StringDecoder('utf-8'); - this._process.stdout?.on('data', (chunk) => { - const strChunk = typeof chunk === 'string' ? chunk : stdoutDecoder.write(chunk); - this._onStdout.fire(strChunk); - }); - - const stderrDecoder = new StringDecoder('utf-8'); - this._process.stderr?.on('data', (chunk) => { - const strChunk = typeof chunk === 'string' ? chunk : stderrDecoder.write(chunk); - this._onStderr.fire(strChunk); - }); - - this._process.on('message', msg => { - this._onMessage.fire(msg); - }); - - this._process.on('error', (err) => { - this._onError.fire({ error: transformErrorForSerialization(err) }); - }); - - this._process.on('exit', (code: number, signal: string) => { - this._hasExited = true; - this._onExit.fire({ pid, code, signal }); - }); - - return { pid }; - } - - enableInspectPort(): boolean { - if (!this._process) { - return false; - } - - this._logService.info(`Enabling inspect port on extension host with pid ${this._process.pid}.`); - - interface ProcessExt { - _debugProcess?(n: number): any; - } - - if (typeof (process)._debugProcess === 'function') { - // use (undocumented) _debugProcess feature of node - (process)._debugProcess!(this._process.pid); - return true; - } else if (!platform.isWindows) { - // use KILL USR1 on non-windows platforms (fallback) - this._process.kill('SIGUSR1'); - return true; - } else { - // not supported... - return false; - } - } - - kill(): void { - if (!this._process) { - return; - } - this._logService.info(`Killing extension host with pid ${this._process.pid}.`); - this._process.kill(); - } - - async waitForExit(maxWaitTimeMs: number): Promise { - if (!this._process) { - return; - } - const pid = this._process.pid; - this._logService.info(`Waiting for extension host with pid ${pid} to exit.`); - await Promise.race([Event.toPromise(this.onExit), timeout(maxWaitTimeMs)]); - - if (!this._hasExited) { - // looks like we timed out - this._logService.info(`Extension host with pid ${pid} did not exit within ${maxWaitTimeMs}ms.`); - this._process.kill(); - } - } -} - -export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { - _serviceBrand: undefined; - - private static _lastId: number = 0; - - protected readonly _extHosts: Map; - - constructor( - @ILogService private readonly _logService: IPartialLogService - ) { - this._extHosts = new Map(); - } - - dispose(): void { - // Intentionally not killing the extension host processes - } - - private _getExtHost(id: string): ExtensionHostProcess { - const extHostProcess = this._extHosts.get(id); - if (!extHostProcess) { - throw new Error(`Unknown extension host!`); - } - return extHostProcess; - } - - onDynamicStdout(id: string): Event { - return this._getExtHost(id).onStdout; - } - - onDynamicStderr(id: string): Event { - return this._getExtHost(id).onStderr; - } - - onDynamicMessage(id: string): Event { - return this._getExtHost(id).onMessage; - } - - onDynamicError(id: string): Event<{ error: SerializedError; }> { - return this._getExtHost(id).onError; - } - - onDynamicExit(id: string): Event<{ code: number; signal: string; }> { - return this._getExtHost(id).onExit; - } - - async createExtensionHost(): Promise<{ id: string; }> { - const id = String(++ExtensionHostStarter._lastId); - const extHost = new ExtensionHostProcess(id, this._logService); - this._extHosts.set(id, extHost); - extHost.onExit(({ pid, code, signal }) => { - this._logService.info(`Extension host with pid ${pid} exited with code: ${code}, signal: ${signal}.`); - setTimeout(() => { - extHost.dispose(); - this._extHosts.delete(id); - }); - }); - return { id }; - } - - async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number; }> { - return this._getExtHost(id).start(opts); - } - - async enableInspectPort(id: string): Promise { - const extHostProcess = this._extHosts.get(id); - if (!extHostProcess) { - return false; - } - return extHostProcess.enableInspectPort(); - } - - async kill(id: string): Promise { - const extHostProcess = this._extHosts.get(id); - if (!extHostProcess) { - // already gone! - return; - } - extHostProcess.kill(); - } - - async killAllNow(): Promise { - for (const [, extHost] of this._extHosts) { - extHost.kill(); - } - } - - async waitForAllExit(maxWaitTimeMs: number): Promise { - const exitPromises: Promise[] = []; - for (const [, extHost] of this._extHosts) { - exitPromises.push(extHost.waitForExit(maxWaitTimeMs)); - } - return Promises.settled(exitPromises).then(() => { }); - } -} - -registerSingleton(IExtensionHostStarter, ExtensionHostStarter, true); diff --git a/src/vs/platform/extensions/node/extensionHostStarterWorker.ts b/src/vs/platform/extensions/node/extensionHostStarterWorker.ts index 6bd329425ab..9ea3bd7857a 100644 --- a/src/vs/platform/extensions/node/extensionHostStarterWorker.ts +++ b/src/vs/platform/extensions/node/extensionHostStarterWorker.ts @@ -3,22 +3,238 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionHostStarter, IPartialLogService } from 'vs/platform/extensions/node/extensionHostStarter'; +import { SerializedError, transformErrorForSerialization } from 'vs/base/common/errors'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; +import { Emitter, Event } from 'vs/base/common/event'; +import { ChildProcess, fork } from 'child_process'; +import { FileAccess } from 'vs/base/common/network'; +import { StringDecoder } from 'string_decoder'; +import * as platform from 'vs/base/common/platform'; +import { mixin } from 'vs/base/common/objects'; +import { cwd } from 'vs/base/common/process'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { Promises, timeout } from 'vs/base/common/async'; export interface IExtensionHostStarterWorkerHost { logInfo(message: string): Promise; } +class ExtensionHostProcess extends Disposable { + + readonly _onStdout = this._register(new Emitter()); + readonly onStdout = this._onStdout.event; + + readonly _onStderr = this._register(new Emitter()); + readonly onStderr = this._onStderr.event; + + readonly _onMessage = this._register(new Emitter()); + readonly onMessage = this._onMessage.event; + + readonly _onError = this._register(new Emitter<{ error: SerializedError; }>()); + readonly onError = this._onError.event; + + readonly _onExit = this._register(new Emitter<{ pid: number; code: number; signal: string }>()); + readonly onExit = this._onExit.event; + + private _process: ChildProcess | null = null; + private _hasExited: boolean = false; + + constructor( + public readonly id: string, + private readonly _host: IExtensionHostStarterWorkerHost + ) { + super(); + } + + start(opts: IExtensionHostProcessOptions): { pid: number; } { + const sw = StopWatch.create(false); + this._process = fork( + FileAccess.asFileUri('bootstrap-fork', require).fsPath, + ['--type=extensionHost', '--skipWorkspaceStorageLock'], + mixin({ cwd: cwd() }, opts), + ); + const forkTime = sw.elapsed(); + const pid = this._process.pid; + + this._host.logInfo(`Starting extension host with pid ${pid} (fork() took ${forkTime} ms).`); + + const stdoutDecoder = new StringDecoder('utf-8'); + this._process.stdout?.on('data', (chunk) => { + const strChunk = typeof chunk === 'string' ? chunk : stdoutDecoder.write(chunk); + this._onStdout.fire(strChunk); + }); + + const stderrDecoder = new StringDecoder('utf-8'); + this._process.stderr?.on('data', (chunk) => { + const strChunk = typeof chunk === 'string' ? chunk : stderrDecoder.write(chunk); + this._onStderr.fire(strChunk); + }); + + this._process.on('message', msg => { + this._onMessage.fire(msg); + }); + + this._process.on('error', (err) => { + this._onError.fire({ error: transformErrorForSerialization(err) }); + }); + + this._process.on('exit', (code: number, signal: string) => { + this._hasExited = true; + this._onExit.fire({ pid, code, signal }); + }); + + return { pid }; + } + + enableInspectPort(): boolean { + if (!this._process) { + return false; + } + + this._host.logInfo(`Enabling inspect port on extension host with pid ${this._process.pid}.`); + + interface ProcessExt { + _debugProcess?(n: number): any; + } + + if (typeof (process)._debugProcess === 'function') { + // use (undocumented) _debugProcess feature of node + (process)._debugProcess!(this._process.pid); + return true; + } else if (!platform.isWindows) { + // use KILL USR1 on non-windows platforms (fallback) + this._process.kill('SIGUSR1'); + return true; + } else { + // not supported... + return false; + } + } + + kill(): void { + if (!this._process) { + return; + } + this._host.logInfo(`Killing extension host with pid ${this._process.pid}.`); + this._process.kill(); + } + + async waitForExit(maxWaitTimeMs: number): Promise { + if (!this._process) { + return; + } + const pid = this._process.pid; + this._host.logInfo(`Waiting for extension host with pid ${pid} to exit.`); + await Promise.race([Event.toPromise(this.onExit), timeout(maxWaitTimeMs)]); + + if (!this._hasExited) { + // looks like we timed out + this._host.logInfo(`Extension host with pid ${pid} did not exit within ${maxWaitTimeMs}ms.`); + this._process.kill(); + } + } +} + +export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { + _serviceBrand: undefined; + + private static _lastId: number = 0; + + protected readonly _extHosts: Map; + + constructor( + private readonly _host: IExtensionHostStarterWorkerHost + ) { + this._extHosts = new Map(); + } + + dispose(): void { + // Intentionally not killing the extension host processes + } + + private _getExtHost(id: string): ExtensionHostProcess { + const extHostProcess = this._extHosts.get(id); + if (!extHostProcess) { + throw new Error(`Unknown extension host!`); + } + return extHostProcess; + } + + onDynamicStdout(id: string): Event { + return this._getExtHost(id).onStdout; + } + + onDynamicStderr(id: string): Event { + return this._getExtHost(id).onStderr; + } + + onDynamicMessage(id: string): Event { + return this._getExtHost(id).onMessage; + } + + onDynamicError(id: string): Event<{ error: SerializedError; }> { + return this._getExtHost(id).onError; + } + + onDynamicExit(id: string): Event<{ code: number; signal: string; }> { + return this._getExtHost(id).onExit; + } + + async createExtensionHost(): Promise<{ id: string; }> { + const id = String(++ExtensionHostStarter._lastId); + const extHost = new ExtensionHostProcess(id, this._host); + this._extHosts.set(id, extHost); + extHost.onExit(({ pid, code, signal }) => { + this._host.logInfo(`Extension host with pid ${pid} exited with code: ${code}, signal: ${signal}.`); + setTimeout(() => { + extHost.dispose(); + this._extHosts.delete(id); + }); + }); + return { id }; + } + + async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number; }> { + return this._getExtHost(id).start(opts); + } + + async enableInspectPort(id: string): Promise { + const extHostProcess = this._extHosts.get(id); + if (!extHostProcess) { + return false; + } + return extHostProcess.enableInspectPort(); + } + + async kill(id: string): Promise { + const extHostProcess = this._extHosts.get(id); + if (!extHostProcess) { + // already gone! + return; + } + extHostProcess.kill(); + } + + async killAllNow(): Promise { + for (const [, extHost] of this._extHosts) { + extHost.kill(); + } + } + + async waitForAllExit(maxWaitTimeMs: number): Promise { + const exitPromises: Promise[] = []; + for (const [, extHost] of this._extHosts) { + exitPromises.push(extHost.waitForExit(maxWaitTimeMs)); + } + return Promises.settled(exitPromises).then(() => { }); + } +} + /** * The `create` function needs to be there by convention because * we are loaded via the `vs/base/common/worker/simpleWorker` utility. */ export function create(host: IExtensionHostStarterWorkerHost) { - const partialLogService: IPartialLogService = { - _serviceBrand: undefined, - info: (message: string): void => { - host.logInfo(message); - } - }; - return new ExtensionHostStarter(partialLogService); + return new ExtensionHostStarter(host); } diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 777123794b1..879333050a2 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -197,16 +197,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { this.terminate(); } - private async _createExtensionHost(): Promise<{ id: string; }> { - const sw = new StopWatch(false); - const result = await this._extensionHostStarter.createExtensionHost(); - if (sw.elapsed() > 20) { - // communicating to the shared process took more than 20ms - this._logService.info(`[LocalProcessExtensionHost]: IExtensionHostStarter.createExtensionHost() took ${sw.elapsed()} ms.`); - } - return result; - } - public start(): Promise | null { if (this._terminating) { // .terminate() was called @@ -217,7 +207,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { if (!this._messageProtocol) { this._messageProtocol = Promise.all([ - spyPromise(this._createExtensionHost(), () => timer.markDidCreateExtensionHost()), + spyPromise(this._extensionHostStarter.createExtensionHost(), () => timer.markDidCreateExtensionHost()), spyPromise(this._tryListenOnPipe(), () => timer.markDidListenOnPipe()), spyPromise(this._tryFindDebugPort(), () => timer.markDidFindDebugPort()), spyPromise(this._shellEnvironmentService.getShellEnv(), () => timer.markDidGetShellEnv()), @@ -473,12 +463,8 @@ export class LocalProcessExtensionHost implements IExtensionHost { }); // Now that the named pipe listener is installed, start the ext host process - const sw = new StopWatch(false); this._extensionHostProcess!.start(opts).then(() => { - sw.stop(); timer.markDidStartExtensionHost(); - - this._logService.info(`[LocalProcessExtensionHost]: IExtensionHostStarter.start() took ${sw.elapsed()} ms.`); }, (err) => { // Starting the ext host process resulted in an error reject(err); diff --git a/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts index 9bdffcd67fd..b2ea5b06d91 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/extensionHostStarter.ts @@ -3,13 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerMainProcessRemoteService, registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; +import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; -const location = 'main' as 'main' | 'shared'; - -if (location === 'main') { - registerMainProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true }); -} else { - registerSharedProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true }); -} +registerMainProcessRemoteService(IExtensionHostStarter, ipcExtensionHostStarterChannelName, { supportsDelayedInstantiation: true }); From 27bcdf57a7845cc95a1daf704ff74d9bad44dee5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 18 Nov 2021 16:56:28 +0100 Subject: [PATCH 202/330] Remove the `localProcessExtensionHostStartupTimes` telemetry event --- .../localProcessExtensionHost.ts | 121 +----------------- 1 file changed, 6 insertions(+), 115 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 879333050a2..7c141c4a42d 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -47,7 +47,6 @@ import { join } from 'vs/base/common/path'; import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService'; import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; import { SerializedError } from 'vs/base/common/errors'; -import { StopWatch } from 'vs/base/common/stopwatch'; import { removeDangerousEnvVariables } from 'vs/base/node/processes'; export interface ILocalProcessExtensionHostInitData { @@ -203,14 +202,12 @@ export class LocalProcessExtensionHost implements IExtensionHost { return null; } - const timer = new LocalProcessExtensionHostStartupTimer(); - if (!this._messageProtocol) { this._messageProtocol = Promise.all([ - spyPromise(this._extensionHostStarter.createExtensionHost(), () => timer.markDidCreateExtensionHost()), - spyPromise(this._tryListenOnPipe(), () => timer.markDidListenOnPipe()), - spyPromise(this._tryFindDebugPort(), () => timer.markDidFindDebugPort()), - spyPromise(this._shellEnvironmentService.getShellEnv(), () => timer.markDidGetShellEnv()), + this._extensionHostStarter.createExtensionHost(), + this._tryListenOnPipe(), + this._tryFindDebugPort(), + this._shellEnvironmentService.getShellEnv(), ]).then(([extensionHostCreationResult, pipeName, portNumber, processEnv]) => { this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter); @@ -369,12 +366,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { } // Initialize extension host process with hand shakes - return this._tryExtHostHandshake(opts, timer).then((protocol) => { - timer.markDidFinishHandhsake(); - - const localProcessExtensionHostStartupTimesEvent = timer.toEvent(); - this._telemetryService.publicLog2('localProcessExtensionHostStartupTimes', localProcessExtensionHostStartupTimesEvent); - + return this._tryExtHostHandshake(opts).then((protocol) => { clearTimeout(startupTimeoutHandle); return protocol; }); @@ -432,7 +424,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { return port || 0; } - private _tryExtHostHandshake(opts: IExtensionHostProcessOptions, timer: LocalProcessExtensionHostStartupTimer): Promise { + private _tryExtHostHandshake(opts: IExtensionHostProcessOptions): Promise { return new Promise((resolve, reject) => { @@ -447,7 +439,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { }, 60 * 1000); this._namedPipeServer!.on('connection', socket => { - timer.markDidReceiveConnection(); clearTimeout(handle); if (this._namedPipeServer) { @@ -464,7 +455,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { // Now that the named pipe listener is installed, start the ext host process this._extensionHostProcess!.start(opts).then(() => { - timer.markDidStartExtensionHost(); }, (err) => { // Starting the ext host process resulted in an error reject(err); @@ -492,7 +482,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { const disposable = protocol.onMessage(msg => { if (isMessageOfType(msg, MessageType.Ready)) { - timer.markDidReceiveReady(); // 1) Extension Host is ready to receive messages, initialize it uninstallTimeoutCheck(); @@ -508,7 +497,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { } if (isMessageOfType(msg, MessageType.Initialized)) { - timer.markDidReceiveInitialized(); // 2) Extension Host is initialized uninstallTimeoutCheck(); @@ -729,100 +717,3 @@ export class LocalProcessExtensionHost implements IExtensionHost { } } } - -async function spyPromise(p: Promise, whenDone: () => void): Promise { - const result = await p; - whenDone(); - return result; -} - -type LocalProcessExtensionHostStartupTimesClassification = { - didCreateExtensionHost: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didListenOnPipe: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didFindDebugPort: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didGetShellEnv: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didStartExtensionHost: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didReceiveConnection: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didReceiveReady: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didReceiveInitialized: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; - didFinishHandhsake: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; -}; -type LocalProcessExtensionHostStartupTimesEvent = { - didCreateExtensionHost: number; - didListenOnPipe: number; - didFindDebugPort: number; - didGetShellEnv: number; - didStartExtensionHost: number; - didReceiveConnection: number; - didReceiveReady: number; - didReceiveInitialized: number; - didFinishHandhsake: number; -}; - -class LocalProcessExtensionHostStartupTimer { - - private readonly _sw: StopWatch; - - constructor() { - this._sw = new StopWatch(false); - } - - public toEvent(): LocalProcessExtensionHostStartupTimesEvent { - return { - didCreateExtensionHost: this.didCreateExtensionHost, - didListenOnPipe: this.didListenOnPipe, - didFindDebugPort: this.didFindDebugPort, - didGetShellEnv: this.didGetShellEnv, - didStartExtensionHost: this.didStartExtensionHost, - didReceiveConnection: this.didReceiveConnection, - didReceiveReady: this.didReceiveReady, - didReceiveInitialized: this.didReceiveInitialized, - didFinishHandhsake: this.didFinishHandhsake, - }; - } - - private didCreateExtensionHost = 0; - public markDidCreateExtensionHost() { - this.didCreateExtensionHost = this._sw.elapsed(); - } - - private didListenOnPipe = 0; - public markDidListenOnPipe() { - this.didListenOnPipe = this._sw.elapsed(); - } - - private didFindDebugPort = 0; - public markDidFindDebugPort() { - this.didFindDebugPort = this._sw.elapsed(); - } - - private didGetShellEnv = 0; - public markDidGetShellEnv() { - this.didGetShellEnv = this._sw.elapsed(); - } - - private didStartExtensionHost = 0; - public markDidStartExtensionHost() { - this.didStartExtensionHost = this._sw.elapsed(); - } - - private didReceiveConnection = 0; - public markDidReceiveConnection() { - this.didReceiveConnection = this._sw.elapsed(); - } - - private didReceiveReady = 0; - public markDidReceiveReady() { - this.didReceiveReady = this._sw.elapsed(); - } - - private didReceiveInitialized = 0; - public markDidReceiveInitialized() { - this.didReceiveInitialized = this._sw.elapsed(); - } - - private didFinishHandhsake = 0; - public markDidFinishHandhsake() { - this.didFinishHandhsake = this._sw.elapsed(); - } -} From 1331a412d9704a61bf85e94a29d65dec8a6208b1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 08:09:55 -0800 Subject: [PATCH 203/330] Remove unused interface members from ITerminalInstanceService Part of #136062 --- .../contrib/terminal/browser/terminal.ts | 2 -- .../terminal/browser/terminalInstanceService.ts | 17 +---------------- .../test/browser/workbenchTestServices.ts | 3 --- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 7f2d006ee3f..18519d59078 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -39,8 +39,6 @@ export interface ITerminalInstanceService { onDidCreateInstance: Event; getXtermConstructor(): Promise; - getXtermSearchConstructor(): Promise; - getXtermUnicode11Constructor(): Promise; /** * Takes a path and returns the properly escaped path to send to the terminal. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index d1b825d0778..9b854e5b591 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -24,8 +24,6 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin import { Registry } from 'vs/platform/registry/common/platform'; let Terminal: typeof XTermTerminal; -let SearchAddon: typeof XTermSearchAddon; -let Unicode11Addon: typeof XTermUnicode11Addon; export class TerminalInstanceService extends Disposable implements ITerminalInstanceService { declare _serviceBrand: undefined; @@ -68,6 +66,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst return instance; } + // TODO: This is duplicated in TerminalService private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { // Profile was provided if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { @@ -102,20 +101,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst return Terminal; } - async getXtermSearchConstructor(): Promise { - if (!SearchAddon) { - SearchAddon = (await import('xterm-addon-search')).SearchAddon; - } - return SearchAddon; - } - - async getXtermUnicode11Constructor(): Promise { - if (!Unicode11Addon) { - Unicode11Addon = (await import('xterm-addon-unicode11')).Unicode11Addon; - } - return Unicode11Addon; - } - async preparePathForTerminalAsync(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, remoteAuthority: string | undefined): Promise { return new Promise(c => { if (!executable) { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 11287b9704a..d4e3e886605 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1727,9 +1727,6 @@ export class TestTerminalInstanceService implements ITerminalInstanceService { declare readonly _serviceBrand: undefined; async getXtermConstructor(): Promise { throw new Error('Method not implemented.'); } - async getXtermSearchConstructor(): Promise { throw new Error('Method not implemented.'); } - async getXtermUnicode11Constructor(): Promise { throw new Error('Method not implemented.'); } - async getXtermWebglConstructor(): Promise { throw new Error('Method not implemented.'); } preparePathForTerminalAsync(path: string, executable: string | undefined, title: string, shellType: TerminalShellType, remoteAuthority: string | undefined): Promise { throw new Error('Method not implemented.'); } createInstance(options: ICreateTerminalOptions, target?: TerminalLocation): ITerminalInstance { throw new Error('Method not implemented.'); } getBackend(remoteAuthority?: string): ITerminalBackend | undefined { throw new Error('Method not implemented.'); } From 9d7519e3eb7883d34c45b5584bccd04ff962b92e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 08:31:29 -0800 Subject: [PATCH 204/330] Move get xterm ctor out of instance service Part of #136062 --- .../contrib/terminal/browser/terminal.ts | 5 ---- .../terminal/browser/terminalInstance.ts | 29 +++++++++---------- .../browser/terminalInstanceService.ts | 12 -------- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 18519d59078..79fb2886c6b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -10,9 +10,6 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, TerminalIcon, TitleEventSource, TerminalShellType, IExtensionTerminalProfile, TerminalLocation, ProcessPropertyType, ProcessCapability, IProcessPropertyMap } from 'vs/platform/terminal/common/terminal'; import { ICommandTracker, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalBackend, ITerminalProcessExtHostProxy, IRegisterContributedProfileArgs } from 'vs/workbench/contrib/terminal/common/terminal'; -import type { Terminal as XTermTerminal } from 'xterm'; -import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IEditableData } from 'vs/workbench/common/views'; @@ -38,8 +35,6 @@ export interface ITerminalInstanceService { onDidCreateInstance: Event; - getXtermConstructor(): Promise; - /** * Takes a path and returns the properly escaped path to send to the terminal. * On Windows, this included trying to prepare the path for WSL if needed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 00673a5ad45..e88c937715c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -80,6 +80,19 @@ const enum Constants { } let xtermConstructor: Promise | undefined; +function getXtermConstructor(): Promise { + if (xtermConstructor) { + return xtermConstructor; + } + xtermConstructor = Promises.withAsyncBody(async (resolve) => { + const Terminal = (await import('xterm')).Terminal; + // Localize strings + Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); + Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); + resolve(Terminal); + }); + return xtermConstructor; +} interface ICanvasDimensions { width: number; @@ -537,25 +550,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { get persistentProcessId(): number | undefined { return this._processManager.persistentProcessId; } get shouldPersist(): boolean { return this._processManager.shouldPersist; } - private async _getXtermConstructor(): Promise { - if (xtermConstructor) { - return xtermConstructor; - } - xtermConstructor = Promises.withAsyncBody(async (resolve) => { - const Terminal = await this._terminalInstanceService.getXtermConstructor(); - // Localize strings - Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); - Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); - resolve(Terminal); - }); - return xtermConstructor; - } - /** * Create xterm.js instance and attach data listeners. */ protected async _createXterm(): Promise { - const Terminal = await this._getXtermConstructor(); + const Terminal = await getXtermConstructor(); if (this._isDisposed) { throw new Error('Terminal disposed of during xterm.js creation'); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 9b854e5b591..40cbafe061b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -4,9 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ITerminalInstance, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import type { Terminal as XTermTerminal } from 'xterm'; -import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig, ITerminalProfile, TerminalLocation, TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; @@ -23,8 +20,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { Registry } from 'vs/platform/registry/common/platform'; -let Terminal: typeof XTermTerminal; - export class TerminalInstanceService extends Disposable implements ITerminalInstanceService { declare _serviceBrand: undefined; private _terminalFocusContextKey: IContextKey; @@ -94,13 +89,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst return {}; } - async getXtermConstructor(): Promise { - if (!Terminal) { - Terminal = (await import('xterm')).Terminal; - } - return Terminal; - } - async preparePathForTerminalAsync(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, remoteAuthority: string | undefined): Promise { return new Promise(c => { if (!executable) { From 7d3408de99022b1cf4784a15491b2d046a858ceb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 08:45:09 -0800 Subject: [PATCH 205/330] Consolidate convertProfileToShellLaunchConfig into instance service Part of #136062 --- .../contrib/terminal/browser/terminal.ts | 8 ++ .../browser/terminalInstanceService.ts | 10 +- .../terminal/browser/terminalService.ts | 33 +---- .../browser/terminalInstanceService.test.ts | 122 ++++++++++++++++++ .../test/browser/terminalService.test.ts | 102 +-------------- .../test/browser/workbenchTestServices.ts | 2 +- 6 files changed, 143 insertions(+), 134 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 79fb2886c6b..5c07f86e402 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -35,6 +35,14 @@ export interface ITerminalInstanceService { onDidCreateInstance: Event; + /** + * Helper function to convert a shell launch config, a profile or undefined into its equivalent + * shell launch config. + * @param shellLaunchConfigOrProfile A shell launch config, a profile or undefined + * @param cwd A cwd to override. + */ + convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig; + /** * Takes a path and returns the properly escaped path to send to the terminal. * On Windows, this included trying to prepare the path for WSL if needed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 40cbafe061b..afe1818734b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -46,7 +46,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst createInstance(profile: ITerminalProfile, target?: TerminalLocation, resource?: URI): ITerminalInstance; createInstance(shellLaunchConfig: IShellLaunchConfig, target?: TerminalLocation, resource?: URI): ITerminalInstance; createInstance(config: IShellLaunchConfig | ITerminalProfile, target?: TerminalLocation, resource?: URI): ITerminalInstance { - const shellLaunchConfig = this._convertProfileToShellLaunchConfig(config); + const shellLaunchConfig = this.convertProfileToShellLaunchConfig(config); const instance = this._instantiationService.createInstance(TerminalInstance, this._terminalFocusContextKey, this._terminalHasFixedWidth, @@ -61,11 +61,13 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst return instance; } - // TODO: This is duplicated in TerminalService - private _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { + convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { // Profile was provided if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { const profile = shellLaunchConfigOrProfile; + if (!profile.path) { + return shellLaunchConfigOrProfile; + } return { executable: profile.path, args: profile.args, @@ -77,7 +79,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst }; } - // Shell launch config was provided + // A shell launch config was provided if (shellLaunchConfigOrProfile) { if (cwd) { shellLaunchConfigOrProfile.cwd = cwd; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index b95ad260ca9..8d73e0fff9d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -18,7 +18,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString } from 'vs/platform/terminal/common/terminal'; +import { ICreateContributedTerminalProfileOptions, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalLocation, TerminalLocationString } from 'vs/platform/terminal/common/terminal'; import { iconForeground } from 'vs/platform/theme/common/colorRegistry'; import { IconDefinition } from 'vs/platform/theme/common/iconRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; @@ -876,7 +876,7 @@ export class TerminalService implements ITerminalService { } const config = options?.config || this._terminalProfileService.availableProfiles?.find(p => p.profileName === this._terminalProfileService.getDefaultProfileName()); - const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : this._convertProfileToShellLaunchConfig(config || {}); + const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : this._terminalInstanceService.convertProfileToShellLaunchConfig(config || {}); // Get the contributed profile if it was provided let contributedProfile = config && 'extensionIdentifier' in config ? config : undefined; @@ -1043,35 +1043,6 @@ export class TerminalService implements ITerminalService { this._onDidChangeGroups.fire(); } - protected _convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { - if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) { - const profile = shellLaunchConfigOrProfile; - if (!profile.path) { - return shellLaunchConfigOrProfile; - } - return { - executable: profile.path, - args: profile.args, - env: profile.env, - icon: profile.icon, - color: profile.color, - name: profile.overrideName ? profile.profileName : undefined, - cwd - }; - } - - // A shell launch config was provided - if (shellLaunchConfigOrProfile) { - if (cwd) { - shellLaunchConfigOrProfile.cwd = cwd; - } - return shellLaunchConfigOrProfile; - } - - // Return empty shell launch config - return {}; - } - async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { this._configHelper.panelContainer = panelContainer; this._terminalGroupService.setContainer(terminalContainer); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts new file mode 100644 index 00000000000..038abf5d8a2 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { deepStrictEqual } from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; +import { ITerminalProfile } from 'vs/platform/terminal/common/terminal'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; + +suite('Workbench - TerminalInstanceService', () => { + let instantiationService: TestInstantiationService; + let terminalInstanceService: ITerminalInstanceService; + + setup(async () => { + instantiationService = new TestInstantiationService(); + // TODO: Should be able to create these services without this config set + instantiationService.stub(IConfigurationService, new TestConfigurationService({ + terminal: { + integrated: { + fontWeight: 'normal' + } + } + })); + instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService)); + + terminalInstanceService = instantiationService.createInstance(TerminalInstanceService); + }); + + suite('convertProfileToShellLaunchConfig', () => { + test('should return an empty shell launch config when undefined is provided', () => { + deepStrictEqual(terminalInstanceService.convertProfileToShellLaunchConfig(), {}); + deepStrictEqual(terminalInstanceService.convertProfileToShellLaunchConfig(undefined), {}); + }); + test('should return the same shell launch config when provided', () => { + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({}), + {} + ); + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ executable: '/foo' }), + { executable: '/foo' } + ); + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar', args: ['a', 'b'] }), + { executable: '/foo', cwd: '/bar', args: ['a', 'b'] } + ); + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ executable: '/foo' }, '/bar'), + { executable: '/foo', cwd: '/bar' } + ); + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar' }, '/baz'), + { executable: '/foo', cwd: '/baz' } + ); + }); + test('should convert a provided profile to a shell launch config', () => { + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ + profileName: 'abc', + path: '/foo', + isDefault: true + }), + { + args: undefined, + color: undefined, + cwd: undefined, + env: undefined, + executable: '/foo', + icon: undefined, + name: undefined + } + ); + const icon = URI.file('/icon'); + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ + profileName: 'abc', + path: '/foo', + isDefault: true, + args: ['a', 'b'], + color: 'color', + env: { test: 'TEST' }, + icon + } as ITerminalProfile, '/bar'), + { + args: ['a', 'b'], + color: 'color', + cwd: '/bar', + env: { test: 'TEST' }, + executable: '/foo', + icon, + name: undefined + } + ); + }); + test('should respect overrideName in profile', () => { + deepStrictEqual( + terminalInstanceService.convertProfileToShellLaunchConfig({ + profileName: 'abc', + path: '/foo', + isDefault: true, + overrideName: true + }), + { + args: undefined, + color: undefined, + cwd: undefined, + env: undefined, + executable: '/foo', + icon: undefined, + name: 'abc' + } + ); + }); + }); +}); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index 9675e78f901..ab9dbe73b5d 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { deepStrictEqual, fail } from 'assert'; -import { IShellLaunchConfig, ITerminalProfile, TerminalLocation } from 'vs/platform/terminal/common/terminal'; +import { fail } from 'assert'; +import { TerminalLocation } from 'vs/platform/terminal/common/terminal'; import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; -import { URI } from 'vs/base/common/uri'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; @@ -23,15 +22,9 @@ import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogSer import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TestRemoteAgentService } from 'vs/workbench/services/remote/test/common/testServices'; -class TestTerminalService extends TerminalService { - convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { - return this._convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile, cwd); - } -} - suite('Workbench - TerminalService', () => { let instantiationService: TestInstantiationService; - let terminalService: TestTerminalService; + let terminalService: TerminalService; let configurationService: TestConfigurationService; let dialogService: TestDialogService; @@ -58,97 +51,10 @@ suite('Workbench - TerminalService', () => { instantiationService.stub(IRemoteAgentService, 'getConnection', null); instantiationService.stub(IDialogService, dialogService); - terminalService = instantiationService.createInstance(TestTerminalService); + terminalService = instantiationService.createInstance(TerminalService); instantiationService.stub(ITerminalService, terminalService); }); - suite('convertProfileToShellLaunchConfig', () => { - test('should return an empty shell launch config when undefined is provided', () => { - deepStrictEqual(terminalService.convertProfileToShellLaunchConfig(), {}); - deepStrictEqual(terminalService.convertProfileToShellLaunchConfig(undefined), {}); - }); - test('should return the same shell launch config when provided', () => { - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({}), - {} - ); - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ executable: '/foo' }), - { executable: '/foo' } - ); - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar', args: ['a', 'b'] }), - { executable: '/foo', cwd: '/bar', args: ['a', 'b'] } - ); - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ executable: '/foo' }, '/bar'), - { executable: '/foo', cwd: '/bar' } - ); - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ executable: '/foo', cwd: '/bar' }, '/baz'), - { executable: '/foo', cwd: '/baz' } - ); - }); - test('should convert a provided profile to a shell launch config', () => { - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ - profileName: 'abc', - path: '/foo', - isDefault: true - }), - { - args: undefined, - color: undefined, - cwd: undefined, - env: undefined, - executable: '/foo', - icon: undefined, - name: undefined - } - ); - const icon = URI.file('/icon'); - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ - profileName: 'abc', - path: '/foo', - isDefault: true, - args: ['a', 'b'], - color: 'color', - env: { test: 'TEST' }, - icon - } as ITerminalProfile, '/bar'), - { - args: ['a', 'b'], - color: 'color', - cwd: '/bar', - env: { test: 'TEST' }, - executable: '/foo', - icon, - name: undefined - } - ); - }); - test('should respect overrideName in profile', () => { - deepStrictEqual( - terminalService.convertProfileToShellLaunchConfig({ - profileName: 'abc', - path: '/foo', - isDefault: true, - overrideName: true - }), - { - args: undefined, - color: undefined, - cwd: undefined, - env: undefined, - executable: '/foo', - icon: undefined, - name: 'abc' - } - ); - }); - }); - suite('safeDisposeTerminal', () => { test('should not show prompt when confirmOnKill is never', async () => { setConfirmOnKill(configurationService, 'never'); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index d4e3e886605..563a88879ab 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1726,7 +1726,7 @@ export class TestTerminalInstanceService implements ITerminalInstanceService { onDidCreateInstance = Event.None; declare readonly _serviceBrand: undefined; - async getXtermConstructor(): Promise { throw new Error('Method not implemented.'); } + convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig { throw new Error('Method not implemented.'); } preparePathForTerminalAsync(path: string, executable: string | undefined, title: string, shellType: TerminalShellType, remoteAuthority: string | undefined): Promise { throw new Error('Method not implemented.'); } createInstance(options: ICreateTerminalOptions, target?: TerminalLocation): ITerminalInstance { throw new Error('Method not implemented.'); } getBackend(remoteAuthority?: string): ITerminalBackend | undefined { throw new Error('Method not implemented.'); } From 3a55226ba35d947497c368de667cbd217cfb0381 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 08:55:55 -0800 Subject: [PATCH 206/330] Move preparePathForShell into terminalEnvironment --- .../terminal/common/terminalEnvironment.ts | 76 +++++++++++++++++++ .../contrib/terminal/browser/terminal.ts | 12 --- .../terminal/browser/terminalActions.ts | 3 +- .../terminal/browser/terminalInstance.ts | 4 +- .../browser/terminalInstanceService.ts | 67 +--------------- .../terminal/browser/terminalTabsList.ts | 3 +- 6 files changed, 84 insertions(+), 81 deletions(-) diff --git a/src/vs/platform/terminal/common/terminalEnvironment.ts b/src/vs/platform/terminal/common/terminalEnvironment.ts index 66b5ad31a3f..4ded8ee092d 100644 --- a/src/vs/platform/terminal/common/terminalEnvironment.ts +++ b/src/vs/platform/terminal/common/terminalEnvironment.ts @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { basename } from 'vs/base/common/path'; +import { isWindows } from 'vs/base/common/platform'; +import { TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; + export function escapeNonWindowsPath(path: string): string { let newPath = path; if (newPath.indexOf('\\') !== 0) { @@ -12,3 +16,75 @@ export function escapeNonWindowsPath(path: string): string { newPath = newPath.replace(bannedChars, ''); return `'${newPath}'`; } + +/** + * Takes a path and returns the properly escaped path to send to a given shell. On Windows, this + * included trying to prepare the path for WSL if needed. + * + * @param originalPath The path to be escaped and formatted. + * @param executable The executable off the shellLaunchConfig. + * @param title The terminal's title. + * @param shellType The type of shell the path is being sent to. + * @param getWslPath A callback to convert a path to its WSL version. + * @returns An escaped version of the path to be execuded in the terminal. + */ +export async function preparePathForShell(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, getWslPath: ((original: string) => Promise | undefined) | undefined): Promise { + return new Promise(c => { + if (!executable) { + c(originalPath); + return; + } + + const hasSpace = originalPath.indexOf(' ') !== -1; + const hasParens = originalPath.indexOf('(') !== -1 || originalPath.indexOf(')') !== -1; + + const pathBasename = basename(executable, '.exe'); + const isPowerShell = pathBasename === 'pwsh' || + title === 'pwsh' || + pathBasename === 'powershell' || + title === 'powershell'; + + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); + return; + } + + if (hasParens && isPowerShell) { + c(`& '${originalPath}'`); + return; + } + + // TODO: This should use the process manager's OS, not the local OS + if (isWindows) { + // 17063 is the build number where wsl path was introduced. + // Update Windows uriPath to be executed in WSL. + if (shellType !== undefined) { + if (shellType === WindowsShellType.GitBash) { + c(originalPath.replace(/\\/g, '/')); + } + else if (shellType === WindowsShellType.Wsl) { + c(getWslPath?.(originalPath) || originalPath); + } + + else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + } else { + const lowerExecutable = executable.toLowerCase(); + if (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1)) { + c(getWslPath?.(originalPath) || originalPath); + } else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + } + + return; + } + + c(escapeNonWindowsPath(originalPath)); + }); +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 5c07f86e402..0a05357fa83 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -43,18 +43,6 @@ export interface ITerminalInstanceService { */ convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig; - /** - * Takes a path and returns the properly escaped path to send to the terminal. - * On Windows, this included trying to prepare the path for WSL if needed. - * - * @param executable The executable off the shellLaunchConfig - * @param title The terminal's title - * @param path The path to be escaped and formatted. - * @param remoteAuthority The remote authority of the terminal's pty. - * @returns An escaped version of the path to be execuded in the terminal. - */ - preparePathForTerminalAsync(path: string, executable: string | undefined, title: string, shellType: TerminalShellType, remoteAuthority: string | undefined): Promise; - createInstance(launchConfig: IShellLaunchConfig, target?: TerminalLocation, resource?: URI): ITerminalInstance; /** diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index f2b5da0aeb2..ff4aaa0099a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -49,6 +49,7 @@ import { isAbsolute } from 'vs/base/common/path'; import { ITerminalQuickPickItem } from 'vs/workbench/contrib/terminal/browser/terminalProfileQuickpick'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { getIconId, getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; +import { preparePathForShell } from 'vs/platform/terminal/common/terminalEnvironment'; // allow-any-unicode-next-line export const switchTerminalActionViewItemSeparator = '─────────'; @@ -534,7 +535,7 @@ export function registerTerminalActions() { } // TODO: Convert this to ctrl+c, ctrl+v for pwsh? - const path = await terminalInstanceService.preparePathForTerminalAsync(uri.fsPath, instance.shellLaunchConfig.executable, instance.title, instance.shellType, instance.remoteAuthority); + const path = await preparePathForShell(uri.fsPath, instance.shellLaunchConfig.executable, instance.title, instance.shellType, (e) => terminalInstanceService.getBackend(instance!.remoteAuthority)?.getWslPath(e)); instance.sendText(path, true); return terminalGroupService.showPanel(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index e88c937715c..d0c7830e695 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -64,6 +64,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/xterm/lineDataEventAddon'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; +import { preparePathForShell } from 'vs/platform/terminal/common/terminalEnvironment'; const enum Constants { /** @@ -831,7 +832,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const dndController = this._instantiationService.createInstance(TerminalInstanceDragAndDropController, container); dndController.onDropTerminal(e => this._onRequestAddInstanceToGroup.fire(e)); dndController.onDropFile(async path => { - const preparedPath = await this._terminalInstanceService.preparePathForTerminalAsync(path, this.shellLaunchConfig.executable, this.title, this.shellType, this._processManager.remoteAuthority); + // TODO: Create ITerminalInstance.sendPathToTerminal + const preparedPath = await preparePathForShell(path, this.shellLaunchConfig.executable, this.title, this.shellType, (e) => this._terminalInstanceService.getBackend(this.remoteAuthority)?.getWslPath(e)); this.sendText(preparedPath, false); this.focus(); }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index afe1818734b..c9da9557b27 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -6,11 +6,8 @@ import { ITerminalInstance, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IShellLaunchConfig, ITerminalProfile, TerminalLocation, TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalProfile, TerminalLocation } from 'vs/platform/terminal/common/terminal'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; -import { basename } from 'vs/base/common/path'; -import { isWindows } from 'vs/base/common/platform'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; @@ -91,68 +88,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst return {}; } - async preparePathForTerminalAsync(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, remoteAuthority: string | undefined): Promise { - return new Promise(c => { - if (!executable) { - c(originalPath); - return; - } - - const hasSpace = originalPath.indexOf(' ') !== -1; - const hasParens = originalPath.indexOf('(') !== -1 || originalPath.indexOf(')') !== -1; - - const pathBasename = basename(executable, '.exe'); - const isPowerShell = pathBasename === 'pwsh' || - title === 'pwsh' || - pathBasename === 'powershell' || - title === 'powershell'; - - if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace(/'/g, '\'\'')}'`); - return; - } - - if (hasParens && isPowerShell) { - c(`& '${originalPath}'`); - return; - } - - if (isWindows) { - // 17063 is the build number where wsl path was introduced. - // Update Windows uriPath to be executed in WSL. - if (shellType !== undefined) { - if (shellType === WindowsShellType.GitBash) { - c(originalPath.replace(/\\/g, '/')); - } - else if (shellType === WindowsShellType.Wsl) { - const offProcService = this.getBackend(remoteAuthority); - c(offProcService?.getWslPath(originalPath) || originalPath); - } - - else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - } else { - const lowerExecutable = executable.toLowerCase(); - if (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1)) { - const offProcService = this.getBackend(remoteAuthority); - c(offProcService?.getWslPath(originalPath) || originalPath); - } else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - } - - return; - } - - c(escapeNonWindowsPath(originalPath)); - }); - } - getBackend(remoteAuthority?: string): ITerminalBackend | undefined { return Registry.as(TerminalExtensions.Backend).getTerminalBackend(remoteAuthority); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 28c37528472..30069546947 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -46,6 +46,7 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { IProcessDetails } from 'vs/platform/terminal/common/terminalProcess'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { getTerminalResourcesFromDragEvent, parseTerminalUri } from 'vs/workbench/contrib/terminal/browser/terminalUri'; +import { preparePathForShell } from 'vs/platform/terminal/common/terminalEnvironment'; const $ = DOM.$; @@ -710,7 +711,7 @@ class TerminalTabsDragAndDrop implements IListDragAndDrop { this._terminalService.setActiveInstance(instance); - const preparedPath = await this._terminalInstanceService.preparePathForTerminalAsync(path, instance.shellLaunchConfig.executable, instance.title, instance.shellType, instance.remoteAuthority); + const preparedPath = await preparePathForShell(path, instance.shellLaunchConfig.executable, instance.title, instance.shellType, (e) => this._terminalInstanceService.getBackend(instance.remoteAuthority)?.getWslPath(e)); instance.sendText(preparedPath, false); instance.focus(); } From e9d7fac2c7ecd4b9a2926e165a9d61a513c84083 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Nov 2021 18:36:39 +0100 Subject: [PATCH 207/330] fix https://github.com/microsoft/vscode/issues/137415 --- .../api/browser/mainThreadLanguageFeatures.ts | 2 +- .../browser/api/extHostApiCommands.test.ts | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 3229f48b2aa..3f14d2df3ef 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -678,7 +678,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha prepareCallHierarchy: async (document, position, token) => { const items = await this._proxy.$prepareCallHierarchy(handle, document.uri, position, token); - if (!items) { + if (!items || items.length === 0) { return undefined; } return { diff --git a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts index 334d6dbf96c..c4b4ea584ef 100644 --- a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts @@ -1366,6 +1366,28 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.strictEqual(outgoing[0].to.name, 'OUTGOING'); }); + test('prepareCallHierarchy throws TypeError if clangd returns empty result #137415', async function () { + + disposables.push(extHost.registerCallHierarchyProvider(nullExtensionDescription, defaultSelector, new class implements vscode.CallHierarchyProvider { + prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult { + return []; + } + provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): vscode.ProviderResult { + return []; + } + provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): vscode.ProviderResult { + return []; + } + })); + + await rpcProtocol.sync(); + + const root = await commands.executeCommand('vscode.prepareCallHierarchy', model.uri, new types.Position(0, 0)); + + assert.ok(Array.isArray(root)); + assert.strictEqual(root.length, 0); + }); + // --- type hierarchy test('TypeHierarchy, back and forth', async function () { From f9ccf968f6e3b86ec8e4a98a1b9275ce2a4cd741 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 18 Nov 2021 09:44:34 -0800 Subject: [PATCH 208/330] specify output id in show more... --- .../notebook/browser/notebook.contribution.ts | 74 +++++++++++-------- .../browser/view/cellParts/cellOutput.ts | 3 +- .../view/output/transforms/richTransform.ts | 4 +- .../view/output/transforms/textHelper.ts | 17 +++-- .../contrib/notebook/common/notebookCommon.ts | 23 ++++-- 5 files changed, 74 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 399f893bed9..795e10d1c02 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd import { isCompositeNotebookEditorInput, NotebookEditorInput, NotebookEditorInputOptions } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl'; -import { CellKind, CellUri, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookWorkingCopyTypeIdentifier, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookWorkingCopyTypeIdentifier, NotebookSetting, ICellOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; @@ -372,13 +372,32 @@ class CellInfoContentProvider { return result; } + private parseStreamOutput(resource: URI, op?: ICellOutput) { + if (!op) { + return; + } + + const streamOutputData = getStreamOutputData(op.outputs); + if (streamOutputData) { + const result = this._modelService.createModel( + streamOutputData, + this._modeService.create('plaintext'), + resource + ); + + return result; + } + + return; + } + async provideOutputTextContent(resource: URI): Promise { const existing = this._modelService.getModel(resource); if (existing) { return existing; } - const data = CellUri.parseCellUri(resource, Schemas.vscodeNotebookCellOutput); + const data = CellUri.parseCellOutputUri(resource); if (!data) { return null; } @@ -389,36 +408,33 @@ class CellInfoContentProvider { const mode = this._modeService.create('json'); for (const cell of ref.object.notebook.cells) { - if (cell.handle === data.handle) { - if (cell.outputs.length === 1) { - // single output - const streamOutputData = getStreamOutputData(cell.outputs[0].outputs); - if (streamOutputData) { - result = this._modelService.createModel( - streamOutputData, - this._modeService.create('plaintext'), - resource - ); - break; - } - } + if (cell.handle !== data.handle) { + continue; + } - const content = JSON.stringify(cell.outputs.map(output => ({ - metadata: output.metadata, - outputItems: output.outputs.map(opit => ({ - mimeType: opit.mime, - data: opit.data.toString() - })) - }))); - const edits = format(content, undefined, {}); - const outputSource = applyEdits(content, edits); - result = this._modelService.createModel( - outputSource, - mode, - resource - ); + const op = cell.outputs.find(op => op.outputId === data.outputId); + const streamOutputData = this.parseStreamOutput(resource, op); + if (streamOutputData) { + result = streamOutputData; break; } + + const content = JSON.stringify(cell.outputs.map(output => ({ + metadata: output.metadata, + outputItems: output.outputs.map(opit => ({ + mimeType: opit.mime, + data: opit.data.toString() + })) + }))); + + const edits = format(content, undefined, {}); + const outputSource = applyEdits(content, edits); + result = this._modelService.createModel( + outputSource, + mode, + resource + ); + break; } if (result) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index 91bfd890984..cecfd8a20f8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -11,7 +11,6 @@ import { Action, IAction } from 'vs/base/common/actions'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { MarshalledId } from 'vs/base/common/marshalling'; -import { Schemas } from 'vs/base/common/network'; import * as nls from 'vs/nls'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -862,7 +861,7 @@ export class CellOutputContainer extends Disposable { actionHandler: { callback: (content) => { if (content === 'command:workbench.action.openLargeOutput') { - this.openerService.open(CellUri.generateCellUri(this.notebookEditor.textModel!.uri, this.viewCell.handle, Schemas.vscodeNotebookCellOutput)); + this.openerService.open(CellUri.generateCellOutputUri(this.notebookEditor.textModel!.uri, this.viewCell.handle)); } return; diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 57f448967a6..73e7e871db0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -75,7 +75,7 @@ class StreamRendererContrib extends Disposable implements IOutputRendererContrib const text = getStringValue(item); const contentNode = DOM.$('span.output-stream'); const lineLimit = this.configurationService.getValue(NotebookSetting.textOutputLineLimit) ?? 30; - truncatedArrayOfString(notebookUri, output.cellViewModel, Math.max(lineLimit, 6), contentNode, [text], disposables, linkDetector, this.openerService, this.themeService); + truncatedArrayOfString(notebookUri, output.cellViewModel, output.model.outputId, Math.max(lineLimit, 6), contentNode, [text], disposables, linkDetector, this.openerService, this.themeService); container.appendChild(contentNode); return { type: RenderOutputType.Mainframe, disposable: disposables }; @@ -179,7 +179,7 @@ class PlainTextRendererContrib extends Disposable implements IOutputRendererCont const str = getStringValue(item); const contentNode = DOM.$('.output-plaintext'); const lineLimit = this.configurationService.getValue(NotebookSetting.textOutputLineLimit) ?? 30; - truncatedArrayOfString(notebookUri, output.cellViewModel, Math.max(lineLimit, 6), contentNode, [str], disposables, linkDetector, this.openerService, this.themeService); + truncatedArrayOfString(notebookUri, output.cellViewModel, output.model.outputId, Math.max(lineLimit, 6), contentNode, [str], disposables, linkDetector, this.openerService, this.themeService); container.appendChild(contentNode); return { type: RenderOutputType.Mainframe, supportAppend: true, disposable: disposables }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts index a30a41e387b..54cbf6b13ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper.ts @@ -7,7 +7,6 @@ import * as DOM from 'vs/base/browser/dom'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { DefaultEndOfLine, EndOfLinePreference, ITextBuffer } from 'vs/editor/common/model'; @@ -21,9 +20,9 @@ import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const SIZE_LIMIT = 65535; -function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellViewModel, disposables: DisposableStore, openerService: IOpenerService): HTMLElement { +function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellViewModel, outputId: string, disposables: DisposableStore, openerService: IOpenerService): HTMLElement { const md: IMarkdownString = { - value: '[show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput)', + value: `[show more (open the raw output data in a text editor) ...](command:workbench.action.openLargeOutput?${outputId})`, isTrusted: true, supportThemeIcons: true }; @@ -31,8 +30,10 @@ function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellVi const rendered = disposables.add(renderMarkdown(md, { actionHandler: { callback: (content) => { - if (content === 'command:workbench.action.openLargeOutput') { - openerService.open(CellUri.generateCellUri(notebookUri, cellViewModel.handle, Schemas.vscodeNotebookCellOutput)); + const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(content); + if (ret && ret.length === 2) { + const outputId = ret[1]; + openerService.open(CellUri.generateCellOutputUri(notebookUri, cellViewModel.handle, outputId)); } return; @@ -45,7 +46,7 @@ function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellVi return rendered.element; } -export function truncatedArrayOfString(notebookUri: URI, cellViewModel: IGenericCellViewModel, linesLimit: number, container: HTMLElement, outputs: string[], disposables: DisposableStore, linkDetector: LinkDetector, openerService: IOpenerService, themeService: IThemeService) { +export function truncatedArrayOfString(notebookUri: URI, cellViewModel: IGenericCellViewModel, outputId: string, linesLimit: number, container: HTMLElement, outputs: string[], disposables: DisposableStore, linkDetector: LinkDetector, openerService: IOpenerService, themeService: IThemeService) { const fullLen = outputs.reduce((p, c) => { return p + c.length; }, 0); @@ -63,7 +64,7 @@ export function truncatedArrayOfString(notebookUri: URI, cellViewModel: IGeneric const truncatedText = buffer.getValueInRange(new Range(1, 1, sizeBufferLimitPosition.lineNumber, sizeBufferLimitPosition.column), EndOfLinePreference.TextDefined); container.appendChild(handleANSIOutput(truncatedText, linkDetector, themeService, undefined)); // view more ... - container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, disposables, openerService)); + container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, outputId, disposables, openerService)); return; } } @@ -87,7 +88,7 @@ export function truncatedArrayOfString(notebookUri: URI, cellViewModel: IGeneric pre.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(1, 1, linesLimit - 5, buffer.getLineLastNonWhitespaceColumn(linesLimit - 5)), EndOfLinePreference.TextDefined), linkDetector, themeService, undefined)); // view more ... - container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, disposables, openerService)); + container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, outputId, disposables, openerService)); const lineCount = buffer.getLineCount(); const pre2 = DOM.$('div'); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index c495315f175..abd6d0525aa 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -521,19 +521,30 @@ export namespace CellUri { }; } - export function parseCellMetadataUri(metadata: URI) { - if (metadata.scheme !== Schemas.vscodeNotebookCellMetadata) { - return undefined; + export function generateCellOutputUri(notebook: URI, handle: number, outputId?: string) { + return notebook.with({ + scheme: Schemas.vscodeNotebookCellOutput, + fragment: `ch${handle.toString().padStart(7, '0')},${outputId ?? ''},${notebook.scheme !== Schemas.file ? notebook.scheme : ''}` + }) + } + + export function parseCellOutputUri(uri: URI): { notebook: URI, handle: number; outputId?: string } | undefined { + if (uri.scheme !== Schemas.vscodeNotebookCellOutput) { + return; } - const match = _regex.exec(metadata.fragment); + + const match = /^ch(\d{7,})\,([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?\,(.*)$/i.exec(uri.fragment); if (!match) { return undefined; } const handle = Number(match[1]); + const outputId = (match[2] && match[2] !== '') ? match[2] : undefined; + const scheme = match[3]; return { handle, - notebook: metadata.with({ - scheme: metadata.fragment.substr(match[0].length) || Schemas.file, + outputId, + notebook: uri.with({ + scheme: scheme || Schemas.file, fragment: null }) }; From a9bddd66bc148b2589af5efccee3b94d92daf39d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 18 Nov 2021 18:53:44 +0100 Subject: [PATCH 209/330] Improve caching of previous find matches in the `SelectionHighlighterState` --- .../editor/contrib/multicursor/multicursor.ts | 76 ++++++++----------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 497370e7a66..9eebf15826b 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -817,37 +817,33 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction { } class SelectionHighlighterState { - public readonly searchText: string; - public readonly matchCase: boolean; - public readonly wordSeparators: string | null; - public readonly modelVersionId: number; - public selections: SelectionHighlighterState[]; + private readonly _modelVersionId: number = this._model.getVersionId(); + private _cachedFindMatches: Range[] | null = null; - constructor(searchText: string, matchCase: boolean, wordSeparators: string | null, modelVersionId: number, selections: any[]) { - this.searchText = searchText; - this.matchCase = matchCase; - this.wordSeparators = wordSeparators; - this.modelVersionId = modelVersionId; - this.selections = selections; + constructor( + private readonly _model: ITextModel, + private readonly _searchText: string, + private readonly _matchCase: boolean, + private readonly _wordSeparators: string | null, + prevState: SelectionHighlighterState | null + ) { + if (prevState + && this._model === prevState._model + && this._searchText === prevState._searchText + && this._matchCase === prevState._matchCase + && this._wordSeparators === prevState._wordSeparators + && this._modelVersionId === prevState._modelVersionId + ) { + this._cachedFindMatches = prevState._cachedFindMatches; + } } - /** - * Everything equals except for `lastWordUnderCursor` - */ - public static softEquals(a: SelectionHighlighterState | null, b: SelectionHighlighterState | null): boolean { - if (!a && !b) { - return true; + public findMatches(): Range[] { + if (this._cachedFindMatches === null) { + this._cachedFindMatches = this._model.findMatches(this._searchText, true, false, this._matchCase, this._wordSeparators, false).map(m => m.range); + this._cachedFindMatches.sort(Range.compareRangesUsingStarts); } - if (!a || !b) { - return false; - } - return ( - a.selections === b.selections - && a.searchText === b.searchText - && a.matchCase === b.matchCase - && a.wordSeparators === b.wordSeparators - && a.modelVersionId === b.modelVersionId - ); + return this._cachedFindMatches; } } @@ -907,10 +903,10 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut } private _update(): void { - this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor)); + this._setState(SelectionHighlighter._createState(this.state, this._isEnabled, this.editor)); } - private static _createState(isEnabled: boolean, editor: ICodeEditor): SelectionHighlighterState | null { + private static _createState(oldState: SelectionHighlighterState | null, isEnabled: boolean, editor: ICodeEditor): SelectionHighlighterState | null { if (!isEnabled) { return null; } @@ -983,15 +979,11 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut } } - return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null, editor.getModel().getVersionId(), editor.getSelections()); + return new SelectionHighlighterState(editor.getModel(), r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null, oldState); } - private _setState(state: SelectionHighlighterState | null): void { - if (SelectionHighlighterState.softEquals(this.state, state)) { - this.state = state; - return; - } - this.state = state; + private _setState(newState: SelectionHighlighterState | null): void { + this.state = newState; if (!this.state) { this.decorations = this.editor.deltaDecorations(this.decorations, []); @@ -1004,20 +996,17 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut const model = this.editor.getModel(); if (model.isTooLargeForTokenization()) { - // the file is too large, so searching word under cursor in the whole document takes is blocking the UI. + // the file is too large, so searching word under cursor in the whole document would be blocking the UI. return; } - const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model) && this.editor.getOption(EditorOption.occurrencesHighlight); + const allMatches = this.state.findMatches(); - let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range); - allMatches.sort(Range.compareRangesUsingStarts); - - let selections = this.editor.getSelections(); + const selections = this.editor.getSelections(); selections.sort(Range.compareRangesUsingStarts); // do not overlap with selection (issue #64 and #512) - let matches: Range[] = []; + const matches: Range[] = []; for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len;) { const match = allMatches[i]; @@ -1044,6 +1033,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut } } + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model) && this.editor.getOption(EditorOption.occurrencesHighlight); const decorations = matches.map(r => { return { range: r, From 6df426e6702962959978bbf670af6ba4d5e57d80 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 09:54:08 -0800 Subject: [PATCH 210/330] Create ITerminalInstance.sendPath --- .../contrib/terminal/browser/terminal.ts | 18 +++++++++++++++--- .../terminal/browser/terminalActions.ts | 5 +---- .../terminal/browser/terminalInstance.ts | 9 ++++++--- .../terminal/browser/terminalTabsList.ts | 7 ++----- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 0a05357fa83..7df59d048a6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -647,12 +647,24 @@ export interface ITerminalInstance { * process (shell) of the terminal instance. * * @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`. + * @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): Promise; + /** + * Sends a path to the terminal instance, preparing it as needed based on the detected shell + * running within the terminal. The text is written to the stdin of the underlying pty process + * (shell) of the terminal instance. + * + * @param originalPath The path to send. + * @param addNewLine Whether to add a new line to the path 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`. + */ + sendPath(originalPath: string, addNewLine: boolean): Promise; + /** Scroll the terminal buffer down 1 line. */ scrollDownLine(): void; /** Scroll the terminal buffer down 1 page. */ scrollDownPage(): void; /** Scroll the terminal buffer to the bottom. */ scrollToBottom(): void; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index ff4aaa0099a..daa8b1b19e2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -49,7 +49,6 @@ import { isAbsolute } from 'vs/base/common/path'; import { ITerminalQuickPickItem } from 'vs/workbench/contrib/terminal/browser/terminalProfileQuickpick'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { getIconId, getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; -import { preparePathForShell } from 'vs/platform/terminal/common/terminalEnvironment'; // allow-any-unicode-next-line export const switchTerminalActionViewItemSeparator = '─────────'; @@ -512,7 +511,6 @@ export function registerTerminalActions() { async run(accessor: ServicesAccessor) { const terminalService = accessor.get(ITerminalService); const terminalGroupService = accessor.get(ITerminalGroupService); - const terminalInstanceService = accessor.get(ITerminalInstanceService); const codeEditorService = accessor.get(ICodeEditorService); const notificationService = accessor.get(INotificationService); const workbenchEnvironmentService = accessor.get(IWorkbenchEnvironmentService); @@ -535,8 +533,7 @@ export function registerTerminalActions() { } // TODO: Convert this to ctrl+c, ctrl+v for pwsh? - const path = await preparePathForShell(uri.fsPath, instance.shellLaunchConfig.executable, instance.title, instance.shellType, (e) => terminalInstanceService.getBackend(instance!.remoteAuthority)?.getWslPath(e)); - instance.sendText(path, true); + await instance.sendPath(uri.fsPath, true); return terminalGroupService.showPanel(); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index d0c7830e695..0a9d6d36786 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -832,10 +832,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const dndController = this._instantiationService.createInstance(TerminalInstanceDragAndDropController, container); dndController.onDropTerminal(e => this._onRequestAddInstanceToGroup.fire(e)); dndController.onDropFile(async path => { - // TODO: Create ITerminalInstance.sendPathToTerminal - const preparedPath = await preparePathForShell(path, this.shellLaunchConfig.executable, this.title, this.shellType, (e) => this._terminalInstanceService.getBackend(this.remoteAuthority)?.getWslPath(e)); - this.sendText(preparedPath, false); this.focus(); + await this.sendPath(path, false); }); this._dndObserver = new DragAndDropObserver(container, dndController); } @@ -972,6 +970,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._onDidInputData.fire(this); } + async sendPath(originalPath: string, addNewLine: boolean): Promise { + const preparedPath = await preparePathForShell(originalPath, this.shellLaunchConfig.executable, this.title, this.shellType, (e) => this._terminalInstanceService.getBackend(this.remoteAuthority)?.getWslPath(e)); + return this.sendText(preparedPath, addNewLine); + } + setVisible(visible: boolean): void { this._isVisible = visible; if (this._wrapperElement) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 30069546947..812b001a543 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -9,7 +9,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalGroupService, ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -46,7 +46,6 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { IProcessDetails } from 'vs/platform/terminal/common/terminalProcess'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { getTerminalResourcesFromDragEvent, parseTerminalUri } from 'vs/workbench/contrib/terminal/browser/terminalUri'; -import { preparePathForShell } from 'vs/platform/terminal/common/terminalEnvironment'; const $ = DOM.$; @@ -549,7 +548,6 @@ class TerminalTabsDragAndDrop implements IListDragAndDrop { constructor( @ITerminalService private readonly _terminalService: ITerminalService, @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, ) { this._primaryBackend = this._terminalService.getPrimaryBackend(); } @@ -711,8 +709,7 @@ class TerminalTabsDragAndDrop implements IListDragAndDrop { this._terminalService.setActiveInstance(instance); - const preparedPath = await preparePathForShell(path, instance.shellLaunchConfig.executable, instance.title, instance.shellType, (e) => this._terminalInstanceService.getBackend(instance.remoteAuthority)?.getWslPath(e)); - instance.sendText(preparedPath, false); instance.focus(); + await instance.sendPath(path, false); } } From 17c8f52cdf7b3de7d13f03edf872e5dac974335a Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 18 Nov 2021 09:54:44 -0800 Subject: [PATCH 211/330] missing; --- src/vs/workbench/contrib/notebook/common/notebookCommon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index abd6d0525aa..4f792558b17 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -525,7 +525,7 @@ export namespace CellUri { return notebook.with({ scheme: Schemas.vscodeNotebookCellOutput, fragment: `ch${handle.toString().padStart(7, '0')},${outputId ?? ''},${notebook.scheme !== Schemas.file ? notebook.scheme : ''}` - }) + }); } export function parseCellOutputUri(uri: URI): { notebook: URI, handle: number; outputId?: string } | undefined { From 0a5f68d2cce837b69919597bd26ecf1a31888d96 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 09:57:46 -0800 Subject: [PATCH 212/330] Move preparePathForShell into TerminalInstance --- .../terminal/common/terminalEnvironment.ts | 76 ------------------- .../terminal/browser/terminalInstance.ts | 76 ++++++++++++++++++- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/src/vs/platform/terminal/common/terminalEnvironment.ts b/src/vs/platform/terminal/common/terminalEnvironment.ts index 4ded8ee092d..66b5ad31a3f 100644 --- a/src/vs/platform/terminal/common/terminalEnvironment.ts +++ b/src/vs/platform/terminal/common/terminalEnvironment.ts @@ -3,10 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { basename } from 'vs/base/common/path'; -import { isWindows } from 'vs/base/common/platform'; -import { TerminalShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; - export function escapeNonWindowsPath(path: string): string { let newPath = path; if (newPath.indexOf('\\') !== 0) { @@ -16,75 +12,3 @@ export function escapeNonWindowsPath(path: string): string { newPath = newPath.replace(bannedChars, ''); return `'${newPath}'`; } - -/** - * Takes a path and returns the properly escaped path to send to a given shell. On Windows, this - * included trying to prepare the path for WSL if needed. - * - * @param originalPath The path to be escaped and formatted. - * @param executable The executable off the shellLaunchConfig. - * @param title The terminal's title. - * @param shellType The type of shell the path is being sent to. - * @param getWslPath A callback to convert a path to its WSL version. - * @returns An escaped version of the path to be execuded in the terminal. - */ -export async function preparePathForShell(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, getWslPath: ((original: string) => Promise | undefined) | undefined): Promise { - return new Promise(c => { - if (!executable) { - c(originalPath); - return; - } - - const hasSpace = originalPath.indexOf(' ') !== -1; - const hasParens = originalPath.indexOf('(') !== -1 || originalPath.indexOf(')') !== -1; - - const pathBasename = basename(executable, '.exe'); - const isPowerShell = pathBasename === 'pwsh' || - title === 'pwsh' || - pathBasename === 'powershell' || - title === 'powershell'; - - if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace(/'/g, '\'\'')}'`); - return; - } - - if (hasParens && isPowerShell) { - c(`& '${originalPath}'`); - return; - } - - // TODO: This should use the process manager's OS, not the local OS - if (isWindows) { - // 17063 is the build number where wsl path was introduced. - // Update Windows uriPath to be executed in WSL. - if (shellType !== undefined) { - if (shellType === WindowsShellType.GitBash) { - c(originalPath.replace(/\\/g, '/')); - } - else if (shellType === WindowsShellType.Wsl) { - c(getWslPath?.(originalPath) || originalPath); - } - - else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - } else { - const lowerExecutable = executable.toLowerCase(); - if (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1)) { - c(getWslPath?.(originalPath) || originalPath); - } else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - } - - return; - } - - c(escapeNonWindowsPath(originalPath)); - }); -} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 0a9d6d36786..902e7204dd5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -38,7 +38,7 @@ import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTy import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId, TitleEventSource, TerminalIcon, TerminalLocation, ProcessPropertyType, ProcessCapability, IProcessPropertyMap } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId, TitleEventSource, TerminalIcon, TerminalLocation, ProcessPropertyType, ProcessCapability, IProcessPropertyMap, WindowsShellType } from 'vs/platform/terminal/common/terminal'; import { IProductService } from 'vs/platform/product/common/productService'; import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { AutoOpenBarrier, Promises } from 'vs/base/common/async'; @@ -64,7 +64,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/xterm/lineDataEventAddon'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; -import { preparePathForShell } from 'vs/platform/terminal/common/terminalEnvironment'; +import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; const enum Constants { /** @@ -2173,3 +2173,75 @@ export function parseExitResult( return { code, message }; } + +/** + * Takes a path and returns the properly escaped path to send to a given shell. On Windows, this + * included trying to prepare the path for WSL if needed. + * + * @param originalPath The path to be escaped and formatted. + * @param executable The executable off the shellLaunchConfig. + * @param title The terminal's title. + * @param shellType The type of shell the path is being sent to. + * @param getWslPath A callback to convert a path to its WSL version. + * @returns An escaped version of the path to be execuded in the terminal. + */ +async function preparePathForShell(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, getWslPath: ((original: string) => Promise | undefined) | undefined): Promise { + return new Promise(c => { + if (!executable) { + c(originalPath); + return; + } + + const hasSpace = originalPath.indexOf(' ') !== -1; + const hasParens = originalPath.indexOf('(') !== -1 || originalPath.indexOf(')') !== -1; + + const pathBasename = path.basename(executable, '.exe'); + const isPowerShell = pathBasename === 'pwsh' || + title === 'pwsh' || + pathBasename === 'powershell' || + title === 'powershell'; + + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); + return; + } + + if (hasParens && isPowerShell) { + c(`& '${originalPath}'`); + return; + } + + // TODO: This should use the process manager's OS, not the local OS + if (isWindows) { + // 17063 is the build number where wsl path was introduced. + // Update Windows uriPath to be executed in WSL. + if (shellType !== undefined) { + if (shellType === WindowsShellType.GitBash) { + c(originalPath.replace(/\\/g, '/')); + } + else if (shellType === WindowsShellType.Wsl) { + c(getWslPath?.(originalPath) || originalPath); + } + + else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + } else { + const lowerExecutable = executable.toLowerCase(); + if (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1)) { + c(getWslPath?.(originalPath) || originalPath); + } else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + } + + return; + } + + c(escapeNonWindowsPath(originalPath)); + }); +} From d2d8b74e68e1450df507387d45972558d47685be Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:16:25 -0800 Subject: [PATCH 213/330] Update/add docs for ITerminalInstanceService --- .../contrib/terminal/browser/terminal.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 7df59d048a6..689d7033219 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -24,15 +24,17 @@ export const ITerminalGroupService = createDecorator('ter export const ITerminalInstanceService = createDecorator('terminalInstanceService'); /** - * A service used by TerminalInstance (and components owned by it) that allows it to break its - * dependency on electron-browser and node layers, while at the same time avoiding a cyclic - * dependency on ITerminalService. + * A service used to create instances or fetch backends, this services allows services that + * ITerminalService depends on to also create instances. * * **This service is intended to only be used within the terminal contrib.** */ export interface ITerminalInstanceService { readonly _serviceBrand: undefined; + /** + * An event that's fired when a terminal instance is created. + */ onDidCreateInstance: Event; /** @@ -43,11 +45,20 @@ export interface ITerminalInstanceService { */ convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig; + /** + * Create a new terminal instance. + * @param launchConfig The shell launch config. + * @param target The target of the terminal, when this is undefined the default target will be + * used. + * @param resource The URI for the terminal. Note that this is the unique identifier for the + * terminal, not the cwd. + */ createInstance(launchConfig: IShellLaunchConfig, target?: TerminalLocation, resource?: URI): ITerminalInstance; /** * Gets the registered backend for a remote authority (undefined = local). This is a convenience * method to avoid using the more verbose fetching from the registry. + * @param remoteAuthority The remote authority of the backend. */ getBackend(remoteAuthority?: string): ITerminalBackend | undefined; } From db65778f51f72df193a7744becd4c8eb3ec25c1f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 18 Nov 2021 10:31:40 -0800 Subject: [PATCH 214/330] Break circular dependency between term instance/instance service Fixes #136062 --- .../contrib/terminal/browser/terminalInstance.ts | 15 +++++++-------- .../terminal/browser/terminalProcessManager.ts | 2 ++ .../workbench/contrib/terminal/common/terminal.ts | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 902e7204dd5..e0a5863e1da 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -23,11 +23,11 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, ProcessState, TERMINAL_VIEW_ID, INavigationMode, DEFAULT_COMMANDS_TO_SKIP_SHELL, TERMINAL_CREATION_COMMANDS, ITerminalProfileResolverService, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager, ProcessState, TERMINAL_VIEW_ID, INavigationMode, DEFAULT_COMMANDS_TO_SKIP_SHELL, TERMINAL_CREATION_COMMANDS, ITerminalProfileResolverService, TerminalCommandId, ITerminalBackend } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalInstanceService, ITerminalInstance, ITerminalExternalLinkProvider, IRequestAddInstanceToGroupEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance, ITerminalExternalLinkProvider, IRequestAddInstanceToGroupEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; import type { Terminal as XTermTerminal, ITerminalAddon } from 'xterm'; import { NavigationModeAddon } from 'vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon'; @@ -296,7 +296,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private readonly _configHelper: TerminalConfigHelper, private _shellLaunchConfig: IShellLaunchConfig, resource: URI | undefined, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService, @IPathService private readonly _pathService: IPathService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -971,7 +970,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } async sendPath(originalPath: string, addNewLine: boolean): Promise { - const preparedPath = await preparePathForShell(originalPath, this.shellLaunchConfig.executable, this.title, this.shellType, (e) => this._terminalInstanceService.getBackend(this.remoteAuthority)?.getWslPath(e)); + const preparedPath = await preparePathForShell(originalPath, this.shellLaunchConfig.executable, this.title, this.shellType, this._processManager.backend); return this.sendText(preparedPath, addNewLine); } @@ -2182,10 +2181,10 @@ export function parseExitResult( * @param executable The executable off the shellLaunchConfig. * @param title The terminal's title. * @param shellType The type of shell the path is being sent to. - * @param getWslPath A callback to convert a path to its WSL version. + * @param backend The backend for the terminal. * @returns An escaped version of the path to be execuded in the terminal. */ -async function preparePathForShell(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, getWslPath: ((original: string) => Promise | undefined) | undefined): Promise { +async function preparePathForShell(originalPath: string, executable: string | undefined, title: string, shellType: TerminalShellType, backend: ITerminalBackend | undefined): Promise { return new Promise(c => { if (!executable) { c(originalPath); @@ -2220,7 +2219,7 @@ async function preparePathForShell(originalPath: string, executable: string | un c(originalPath.replace(/\\/g, '/')); } else if (shellType === WindowsShellType.Wsl) { - c(getWslPath?.(originalPath) || originalPath); + c(backend?.getWslPath(originalPath) || originalPath); } else if (hasSpace) { @@ -2231,7 +2230,7 @@ async function preparePathForShell(originalPath: string, executable: string | un } else { const lowerExecutable = executable.toLowerCase(); if (lowerExecutable.indexOf('wsl') !== -1 || (lowerExecutable.indexOf('bash.exe') !== -1 && lowerExecutable.toLowerCase().indexOf('git') === -1)) { - c(getWslPath?.(originalPath) || originalPath); + c(backend?.getWslPath(originalPath) || originalPath); } else if (hasSpace) { c('"' + originalPath + '"'); } else { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index ed69c1fdb3a..680dd89b9cb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -59,6 +59,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce userHome: string | undefined; isDisconnected: boolean = false; environmentVariableInfo: IEnvironmentVariableInfo | undefined; + backend: ITerminalBackend | undefined; private _isDisposed: boolean = false; private _process: ITerminalChildProcess | null = null; @@ -198,6 +199,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (!backend) { throw new Error(`No terminal backend registered for remote authority '${this.remoteAuthority}'`); } + this.backend = backend; // Create variable resolver const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index c155c2edd45..e4a4bbf4791 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -341,6 +341,7 @@ export interface ITerminalProcessManager extends IDisposable { readonly isDisconnected: boolean; readonly hasWrittenData: boolean; readonly hasChildProcesses: boolean; + readonly backend: ITerminalBackend | undefined; readonly onPtyDisconnect: Event; readonly onPtyReconnect: Event; From 4fb4bc9dfb8b6797afd4fc9d90ea901c5d93cc19 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Nov 2021 19:55:37 +0100 Subject: [PATCH 215/330] Update to a newer `sudo-prompt` library (#137467) * :up: @vscode/sudo-prompt * use @vscode/sudo-prompt --- package.json | 2 +- .../native/electron-main/nativeHostMainService.ts | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index df347738880..fadda8eb01a 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@microsoft/applicationinsights-web": "^2.6.4", "@parcel/watcher": "2.0.2", "@vscode/sqlite3": "4.0.12", + "@vscode/sudo-prompt": "^9.3.0", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", "graceful-fs": "4.2.8", @@ -75,7 +76,6 @@ "native-watchdog": "1.3.0", "node-pty": "0.11.0-beta11", "spdlog": "^0.13.0", - "sudo-prompt": "9.2.1", "tas-client-umd": "0.1.4", "v8-inspect-profiler": "^0.0.22", "vscode-nsfw": "2.1.8", diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 42522056520..bd4dc019b68 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -509,7 +509,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async writeElevated(windowId: number | undefined, source: URI, target: URI, options?: { unlock?: boolean }): Promise { - const sudoPrompt = await import('sudo-prompt'); + const sudoPrompt = await import('@vscode/sudo-prompt'); return new Promise((resolve, reject) => { const sudoCommand: string[] = [`"${this.cliPath}"`]; diff --git a/yarn.lock b/yarn.lock index f525f917925..3e237c05554 100644 --- a/yarn.lock +++ b/yarn.lock @@ -847,6 +847,11 @@ dependencies: nan "2.14.2" +"@vscode/sudo-prompt@^9.3.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@vscode/sudo-prompt/-/sudo-prompt-9.3.0.tgz#5c63b195986d1d6a142856146e51c6b46a3319b2" + integrity sha512-/c/A4/fEBhKXiEW2+Cv8qid6uWA6Igg9iaHrlMoNyoHynziYLYFCaF8lGcmvQ20wEi5l7xyx4YxG2h5nVKazeg== + "@vscode/vscode-languagedetection@1.0.21": version "1.0.21" resolved "https://registry.yarnpkg.com/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz#89b48f293f6aa3341bb888c1118d16ff13b032d3" @@ -9530,11 +9535,6 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -sudo-prompt@9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" - integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== - sumchecker@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" From 2eeac2f8004b132fb7a1c9de946aa42fcd6409d0 Mon Sep 17 00:00:00 2001 From: mamphis Date: Thu, 18 Nov 2021 21:10:09 +0100 Subject: [PATCH 216/330] glyphMargin: Wrapped codicons inside parent div This patch resolves #137436. When multiple classes (e.g. codicon-debug-breakpoint,codicon-debug-hint) are added they would disable the ::after content for consecutive classes for the HTML Element. This patch adds a parent element to the overlay and adds all classes as seperated divs, so they can be drawn on top of each other. --- .../editor/browser/viewParts/glyphMargin/glyphMargin.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 761b0ce1afb..1d88adbba7d 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -166,7 +166,7 @@ export class GlyphMarginOverlay extends DedupOverlay { const lineHeight = this._lineHeight.toString(); const left = this._glyphMarginLeft.toString(); const width = this._glyphMarginWidth.toString(); - const common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">'; + const common = (content: string = '') => `" style="left:${left}px;width:${width}px;height:${lineHeight}px;">${content}`; const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { @@ -176,11 +176,8 @@ export class GlyphMarginOverlay extends DedupOverlay { if (classNames.length === 0) { output[lineIndex] = ''; } else { - output[lineIndex] = ( - '
``; + const common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">
'; const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { @@ -176,8 +176,11 @@ export class GlyphMarginOverlay extends DedupOverlay { if (classNames.length === 0) { output[lineIndex] = ''; } else { - // Map codicons inside parent div - output[lineIndex] = `
`; + output[lineIndex] = ( + '
>> MetadataConsts.LANGUAGEID_OFFSET` */ - tokenizeLine2(lineText: string, prevState: StackElement | null): ITokenizeLineResult2; + tokenizeLine2(lineText: string, prevState: StackElement | null, timeLimit?: number): ITokenizeLineResult2; } export interface ITokenizeLineResult { readonly tokens: IToken[]; @@ -52,6 +52,10 @@ export interface ITokenizeLineResult { * The `prevState` to be passed on to the next line tokenization. */ readonly ruleStack: StackElement; + /** + * Did tokenization stop early due to reaching the time limit. + */ + readonly stoppedEarly: boolean; } /** * Helpers to manage the "collapsed" metadata of an entire StackElement stack. @@ -97,6 +101,10 @@ export interface ITokenizeLineResult2 { * The `prevState` to be passed on to the next line tokenization. */ readonly ruleStack: StackElement; + /** + * Did tokenization stop early due to reaching the time limit. + */ + readonly stoppedEarly: boolean; } export interface IToken { startIndex: number; diff --git a/yarn.lock b/yarn.lock index c62f11cfbfa..09e3bab2c2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10476,10 +10476,10 @@ vscode-telemetry-extractor@^1.9.5: ts-morph "^12.2.0" vscode-ripgrep "^1.12.1" -vscode-textmate@5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.4.1.tgz#09d566724fc76b60b3ad9791eebf1f0b50f29e5a" - integrity sha512-4CvPHmfuZQaXrcCpathdh6jo7myuR+MU8BvscgQADuponpbqfmu2rwTOtCXhGwwEgStvJF8V4s9FwMKRVLNmKQ== +vscode-textmate@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.5.0.tgz#d83776562c07d1e3181c2c7f1b3d5f20afcab483" + integrity sha512-jToQkPGMNKn0eyKyitYeINJF0NoD240aYyKPIWJv5W2jfPt++jIRg0OSergubtGhbw6SoefkvBYEpX7TsfoSUQ== vscode-windows-ca-certs@^0.3.0: version "0.3.0" From 96cc3b8fc07a64b061739f19d2853809c834e785 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 19 Nov 2021 06:01:46 -0800 Subject: [PATCH 256/330] Disable should respect dimension overrides again Part of #137155 --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index fd700eace65..2bb71edea11 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -492,7 +492,8 @@ import { assertNoRpc } from '../utils'; // const terminal = window.createTerminal({ name: 'foo', pty }); // }); - test('should respect dimension overrides', async () => { + // TODO: Fix test, flaky in CI (local and remote) https://github.com/microsoft/vscode/issues/137155 + test.skip('should respect dimension overrides', async () => { const writeEmitter = new EventEmitter(); const overrideDimensionsEmitter = new EventEmitter(); const pty: Pseudoterminal = { From 2ec3bfc440d648f37d1614f2e7036d469f695d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 19 Nov 2021 15:07:35 +0100 Subject: [PATCH 257/330] builds: disable caching --- .../darwin/product-build-darwin.yml | 44 ++++++++-------- .../linux/product-build-alpine.yml | 44 ++++++++-------- .../linux/product-build-linux.yml | 44 ++++++++-------- build/azure-pipelines/product-compile.yml | 46 ++++++++--------- .../azure-pipelines/web/product-build-web.yml | 44 ++++++++-------- .../win32/product-build-win32.yml | 50 +++++++++---------- 6 files changed, 136 insertions(+), 136 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index ce62104f949..94409739ee7 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -48,23 +48,23 @@ steps: git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") displayName: Merge distro - - script: | - mkdir -p .build - node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash - displayName: Prepare yarn cache flags + # - script: | + # mkdir -p .build + # node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash + # displayName: Prepare yarn cache flags - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + # - task: Cache@2 + # inputs: + # key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + # path: .build/node_modules_cache + # cacheHitVar: NODE_MODULES_RESTORED + # displayName: Restore node_modules cache - - script: | - set -e - tar -xzf .build/node_modules_cache/cache.tgz - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache + # - script: | + # set -e + # tar -xzf .build/node_modules_cache/cache.tgz + # condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Extract node_modules cache - script: | set -e @@ -101,13 +101,13 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | - set -e - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -p .build/node_modules_cache - tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive + # - script: | + # set -e + # node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + # mkdir -p .build/node_modules_cache + # tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Create node_modules archive # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) - script: | diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index 0a2dd05f28b..966111fbac5 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -47,23 +47,23 @@ steps: git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") displayName: Merge distro - - script: | - mkdir -p .build - node build/azure-pipelines/common/computeNodeModulesCacheKey.js "alpine" $ENABLE_TERRAPIN > .build/yarnlockhash - displayName: Prepare yarn cache flags + # - script: | + # mkdir -p .build + # node build/azure-pipelines/common/computeNodeModulesCacheKey.js "alpine" $ENABLE_TERRAPIN > .build/yarnlockhash + # displayName: Prepare yarn cache flags - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + # - task: Cache@2 + # inputs: + # key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + # path: .build/node_modules_cache + # cacheHitVar: NODE_MODULES_RESTORED + # displayName: Restore node_modules cache - - script: | - set -e - tar -xzf .build/node_modules_cache/cache.tgz - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache + # - script: | + # set -e + # tar -xzf .build/node_modules_cache/cache.tgz + # condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Extract node_modules cache - script: | set -e @@ -89,13 +89,13 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | - set -e - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -p .build/node_modules_cache - tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive + # - script: | + # set -e + # node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + # mkdir -p .build/node_modules_cache + # tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Create node_modules archive - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 45145cc9007..3b80dd0b521 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -38,23 +38,23 @@ steps: git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") displayName: Merge distro - - script: | - mkdir -p .build - node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash - displayName: Prepare yarn cache flags + # - script: | + # mkdir -p .build + # node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash + # displayName: Prepare yarn cache flags - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + # - task: Cache@2 + # inputs: + # key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + # path: .build/node_modules_cache + # cacheHitVar: NODE_MODULES_RESTORED + # displayName: Restore node_modules cache - - script: | - set -e - tar -xzf .build/node_modules_cache/cache.tgz - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache + # - script: | + # set -e + # tar -xzf .build/node_modules_cache/cache.tgz + # condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Extract node_modules cache - script: | set -e @@ -110,13 +110,13 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | - set -e - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -p .build/node_modules_cache - tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive + # - script: | + # set -e + # node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + # mkdir -p .build/node_modules_cache + # tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Create node_modules archive - script: | set -e diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 5458cae8a09..709d8e39f49 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -27,24 +27,24 @@ steps: git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") displayName: Merge distro - - script: | - mkdir -p .build - node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash - displayName: Prepare yarn cache flags + # - script: | + # mkdir -p .build + # node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash + # displayName: Prepare yarn cache flags - # using `genericNodeModules` instead of `nodeModules` here to avoid sharing the cache with builds running inside containers - - task: Cache@2 - inputs: - key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + # # using `genericNodeModules` instead of `nodeModules` here to avoid sharing the cache with builds running inside containers + # - task: Cache@2 + # inputs: + # key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash" + # path: .build/node_modules_cache + # cacheHitVar: NODE_MODULES_RESTORED + # displayName: Restore node_modules cache - - script: | - set -e - tar -xzf .build/node_modules_cache/cache.tgz - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache + # - script: | + # set -e + # tar -xzf .build/node_modules_cache/cache.tgz + # condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Extract node_modules cache - script: | set -e @@ -77,13 +77,13 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | - set -e - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -p .build/node_modules_cache - tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive + # - script: | + # set -e + # node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + # mkdir -p .build/node_modules_cache + # tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Create node_modules archive # Mixin must run before optimize, because the CSS loader will inline small SVGs - script: | diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 608f3a1701a..c12cdf063bf 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -38,23 +38,23 @@ steps: git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") displayName: Merge distro - - script: | - mkdir -p .build - node build/azure-pipelines/common/computeNodeModulesCacheKey.js "web" $ENABLE_TERRAPIN > .build/yarnlockhash - displayName: Prepare yarn cache flags + # - script: | + # mkdir -p .build + # node build/azure-pipelines/common/computeNodeModulesCacheKey.js "web" $ENABLE_TERRAPIN > .build/yarnlockhash + # displayName: Prepare yarn cache flags - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + # - task: Cache@2 + # inputs: + # key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + # path: .build/node_modules_cache + # cacheHitVar: NODE_MODULES_RESTORED + # displayName: Restore node_modules cache - - script: | - set -e - tar -xzf .build/node_modules_cache/cache.tgz - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache + # - script: | + # set -e + # tar -xzf .build/node_modules_cache/cache.tgz + # condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Extract node_modules cache - script: | set -e @@ -80,13 +80,13 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | - set -e - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -p .build/node_modules_cache - tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive + # - script: | + # set -e + # node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + # mkdir -p .build/node_modules_cache + # tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Create node_modules archive - script: | set -e diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 6d63fc24e59..ffc32e9f83b 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -42,25 +42,25 @@ steps: exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") } displayName: Merge distro - - powershell: | - "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch - "$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin - node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash - displayName: Prepare yarn cache flags + # - powershell: | + # "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch + # "$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin + # node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash + # displayName: Prepare yarn cache flags - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/arch, .build/terrapin, .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + # - task: Cache@2 + # inputs: + # key: "nodeModules | $(Agent.OS) | .build/arch, .build/terrapin, .build/yarnlockhash" + # path: .build/node_modules_cache + # cacheHitVar: NODE_MODULES_RESTORED + # displayName: Restore node_modules cache - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { 7z.exe x .build/node_modules_cache/cache.7z -aos } - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache + # - powershell: | + # . build/azure-pipelines/win32/exec.ps1 + # $ErrorActionPreference = "Stop" + # exec { 7z.exe x .build/node_modules_cache/cache.7z -aos } + # condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Extract node_modules cache - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -84,14 +84,14 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt } - exec { mkdir -Force .build/node_modules_cache } - exec { 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt } - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive + # - powershell: | + # . build/azure-pipelines/win32/exec.ps1 + # $ErrorActionPreference = "Stop" + # exec { node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt } + # exec { mkdir -Force .build/node_modules_cache } + # exec { 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt } + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # displayName: Create node_modules archive - powershell: | . build/azure-pipelines/win32/exec.ps1 From e53aa2a0dc6b4058193e32a87d332dea2a584ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 19 Nov 2021 16:53:55 +0100 Subject: [PATCH 258/330] fix snap --- build/azure-pipelines/linux/snap-build-linux.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 045fea15241..33a80b74391 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -23,7 +23,8 @@ steps: # Yarn curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list - apt-get update && apt-get install -y yarn + sudo apt-get update + sudo apt-get install -y yarn # Define variables REPO="$(pwd)" From a71275a72f716ee60d9a6b632d6a685f34355b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 19 Nov 2021 16:55:07 +0100 Subject: [PATCH 259/330] increase smoke test limits --- build/azure-pipelines/darwin/product-build-darwin.yml | 6 +++--- build/azure-pipelines/linux/product-build-linux.yml | 6 +++--- build/azure-pipelines/win32/product-build-win32.yml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 94409739ee7..928bd234e0c 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -224,7 +224,7 @@ steps: APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) APP_NAME="`ls $APP_ROOT | head -n 1`" yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests - timeoutInMinutes: 5 + timeoutInMinutes: 10 displayName: Run smoke tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -234,7 +234,7 @@ steps: APP_NAME="`ls $APP_ROOT | head -n 1`" VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote - timeoutInMinutes: 5 + timeoutInMinutes: 10 displayName: Run smoke tests (Remote) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -242,7 +242,7 @@ steps: set -e VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ yarn smoketest-no-compile --web --headless - timeoutInMinutes: 5 + timeoutInMinutes: 10 displayName: Run smoke tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 3b80dd0b521..dc232cc0219 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -208,7 +208,7 @@ steps: set -e APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) yarn smoketest-no-compile --build "$APP_PATH" --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests - timeoutInMinutes: 5 + timeoutInMinutes: 10 displayName: Run smoke tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -217,7 +217,7 @@ steps: APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote - timeoutInMinutes: 5 + timeoutInMinutes: 10 displayName: Run smoke tests (Remote) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) @@ -225,7 +225,7 @@ steps: set -e VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --web --headless --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" - timeoutInMinutes: 5 + timeoutInMinutes: 10 displayName: Run smoke tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ffc32e9f83b..f74a431c522 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -204,7 +204,7 @@ steps: $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" exec { yarn smoketest-no-compile --build "$AppRoot" --screenshots $(Build.SourcesDirectory)\.build\logs\smoke-tests } displayName: Run smoke tests (Electron) - timeoutInMinutes: 5 + timeoutInMinutes: 10 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | @@ -214,7 +214,7 @@ steps: $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" exec { yarn smoketest-no-compile --build "$AppRoot" --remote --screenshots $(Build.SourcesDirectory)\.build\logs\smoke-tests-remote } displayName: Run smoke tests (Remote) - timeoutInMinutes: 5 + timeoutInMinutes: 10 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | @@ -223,7 +223,7 @@ steps: $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" exec { yarn smoketest-no-compile --web --browser firefox --headless } displayName: Run smoke tests (Browser) - timeoutInMinutes: 5 + timeoutInMinutes: 10 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - task: PublishPipelineArtifact@0 From 1b5c8e1e0836c3493d11f6272f2c7eb190f98830 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 19 Nov 2021 11:01:53 -0500 Subject: [PATCH 260/330] Bump distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1550d9e186..d6fdb211e57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.63.0", - "distro": "a9033b9207d753215ad13618e125ddb770066bd1", + "distro": "f5a2c7f01629f6d3c0dc65e3fbb3818033206bf2", "author": { "name": "Microsoft Corporation" }, From a320e4c68b896797f6ef8a14eeb669a562211e9a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 19 Nov 2021 18:29:09 +0100 Subject: [PATCH 261/330] call resolveWorkspaceSymbol when the provider has it, no more incomplete locations, https://github.com/microsoft/vscode/issues/69558 --- src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts b/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts index 478712d763e..159ed9e7e72 100644 --- a/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts @@ -228,7 +228,7 @@ export class SymbolsQuickAccessProvider extends PickerQuickAccessProvider Date: Fri, 19 Nov 2021 19:54:22 +0100 Subject: [PATCH 262/330] :up: sudo-prompt --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d6fdb211e57..fdb1efeca5a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@microsoft/applicationinsights-web": "^2.6.4", "@parcel/watcher": "2.0.2", "@vscode/sqlite3": "4.0.12", - "@vscode/sudo-prompt": "^9.3.0", + "@vscode/sudo-prompt": "9.3.1", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", "graceful-fs": "4.2.8", diff --git a/yarn.lock b/yarn.lock index 09e3bab2c2d..b1568055bff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -847,10 +847,10 @@ dependencies: nan "2.14.2" -"@vscode/sudo-prompt@^9.3.0": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@vscode/sudo-prompt/-/sudo-prompt-9.3.0.tgz#5c63b195986d1d6a142856146e51c6b46a3319b2" - integrity sha512-/c/A4/fEBhKXiEW2+Cv8qid6uWA6Igg9iaHrlMoNyoHynziYLYFCaF8lGcmvQ20wEi5l7xyx4YxG2h5nVKazeg== +"@vscode/sudo-prompt@9.3.1": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@vscode/sudo-prompt/-/sudo-prompt-9.3.1.tgz#c562334bc6647733649fd42afc96c0eea8de3b65" + integrity sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA== "@vscode/vscode-languagedetection@1.0.21": version "1.0.21" From 08aeb7f34d3807f1585c7e62280fb6c0b1e88265 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 19 Nov 2021 10:55:29 -0800 Subject: [PATCH 263/330] Update input options to include hover feedback (Fixes #137441) --- src/vs/base/browser/ui/checkbox/checkbox.css | 11 +++++++---- src/vs/base/browser/ui/checkbox/checkbox.ts | 4 ++-- src/vs/platform/theme/common/colorRegistry.ts | 3 ++- .../preferences/browser/media/keybindingsEditor.css | 9 +++++++-- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/checkbox/checkbox.css b/src/vs/base/browser/ui/checkbox/checkbox.css index c754adc1518..706c9585e40 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.css +++ b/src/vs/base/browser/ui/checkbox/checkbox.css @@ -8,9 +8,9 @@ float: left; cursor: pointer; overflow: hidden; - opacity: 0.7; width: 20px; height: 20px; + border-radius: 3px; border: 1px solid transparent; padding: 1px; box-sizing: border-box; @@ -19,9 +19,12 @@ -ms-user-select: none; } -.monaco-custom-checkbox:hover, -.monaco-custom-checkbox.checked { - opacity: 1; +.monaco-custom-checkbox:hover { + background-color: var(--vscode-inputOption-hoverBackground); +} + +.hc-black .monaco-custom-checkbox:hover { + border: 1px dashed var(--vscode-focusBorder); } .hc-black .monaco-custom-checkbox { diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index b66ce0497db..27b4c5875fa 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -191,9 +191,9 @@ export class Checkbox extends Widget { protected applyStyles(): void { if (this.domNode) { - this.domNode.style.borderColor = this._checked && this._opts.inputActiveOptionBorder ? this._opts.inputActiveOptionBorder.toString() : 'transparent'; + this.domNode.style.borderColor = this._checked && this._opts.inputActiveOptionBorder ? this._opts.inputActiveOptionBorder.toString() : ''; this.domNode.style.color = this._checked && this._opts.inputActiveOptionForeground ? this._opts.inputActiveOptionForeground.toString() : 'inherit'; - this.domNode.style.backgroundColor = this._checked && this._opts.inputActiveOptionBackground ? this._opts.inputActiveOptionBackground.toString() : 'transparent'; + this.domNode.style.backgroundColor = this._checked && this._opts.inputActiveOptionBackground ? this._opts.inputActiveOptionBackground.toString() : ''; } } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 1c0695f665f..d0ecdc716d4 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -225,7 +225,8 @@ export const inputBackground = registerColor('input.background', { dark: '#3C3C3 export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground.")); export const inputBorder = registerColor('input.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('inputBoxBorder', "Input box border.")); export const inputActiveOptionBorder = registerColor('inputOption.activeBorder', { dark: '#007ACC00', light: '#007ACC00', hc: contrastBorder }, nls.localize('inputBoxActiveOptionBorder', "Border color of activated options in input fields.")); -export const inputActiveOptionBackground = registerColor('inputOption.activeBackground', { dark: transparent(focusBorder, 0.4), light: transparent(focusBorder, 0.2), hc: Color.transparent }, nls.localize('inputOption.activeBackground', "Background color of activated options in input fields.")); +export const inputActiveOptionHoverBackground = registerColor('inputOption.hoverBackground', { dark: '#5a5d5e80', light: '#b8b8b850', hc: null }, nls.localize('inputOption.hoverBackground', "Background color of activated options in input fields.")); +export const inputActiveOptionBackground = registerColor('inputOption.activeBackground', { dark: transparent(focusBorder, 0.4), light: transparent(focusBorder, 0.2), hc: Color.transparent }, nls.localize('inputOption.activeBackground', "Background hover color of options in input fields.")); export const inputActiveOptionForeground = registerColor('inputOption.activeForeground', { dark: Color.white, light: Color.black, hc: null }, nls.localize('inputOption.activeForeground', "Foreground color of activated options in input fields.")); export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { light: transparent(foreground, 0.5), dark: transparent(foreground, 0.5), hc: transparent(foreground, 0.7) }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text.")); diff --git a/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css b/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css index b2553a8dd68..f6d027c3373 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css +++ b/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css @@ -21,13 +21,13 @@ position: absolute; top: 0; right: 10px; - margin-top: 5px; + margin-top: 4px; display: flex; } .keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container > .recording-badge { margin-right: 8px; - padding: 3px; + padding: 4px; } .keybindings-editor > .keybindings-header.small > .search-container > .keybindings-search-actions-container > .recording-badge, @@ -43,12 +43,17 @@ .keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container .monaco-action-bar .action-item { margin-right: 4px; } +.keybindings-editor .monaco-action-bar .action-item .monaco-custom-checkbox { + margin: 0; + padding: 2px; +} .keybindings-editor .monaco-action-bar .action-item > .codicon { display: flex; align-items: center; justify-content: center; color: inherit; + box-sizing: content-box; } .keybindings-editor > .keybindings-header .open-keybindings-container { From bd93a73c14ac23b4194519d83aef8257bebae2b5 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 19 Nov 2021 11:30:07 -0800 Subject: [PATCH 264/330] prepare layout. --- .../notebook/browser/view/cellParts/cellEditorOptions.ts | 2 +- .../browser/view/cellParts/cellFocusIndicator.ts | 2 +- .../notebook/browser/view/cellParts/cellOutput.ts | 2 +- .../contrib/notebook/browser/view/cellParts/cellPart.ts | 9 ++++++--- .../notebook/browser/view/cellParts/cellToolbars.ts | 2 +- .../notebook/browser/view/cellParts/cellWidgets.ts | 2 +- .../contrib/notebook/browser/view/cellParts/codeCell.ts | 4 ++-- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts index c89954efb52..bebd40f700f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts @@ -86,7 +86,7 @@ export class CellEditorOptions extends CellPart { this._value = this._computeEditorOptions(); } - prepareRender(): void { + prepareLayout(): void { // nothing to read } updateLayoutNow(element: ICellViewModel): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts index 2b47bf53039..5ccbd0fd7bc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts @@ -21,7 +21,7 @@ export class CellFocusIndicator extends CellPart { super(); } - prepareRender(): void { + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index ef404e39e93..b4538e6af71 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -593,7 +593,7 @@ export class CellOutputContainer extends CellPart { }); } - prepareRender() { + prepareLayout() { this._outputEntries.forEach(entry => { const index = this.viewCell.outputsViewModels.indexOf(entry.model); if (index >= 0) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts index 61319b8bbb1..9fe2c4f7264 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts @@ -12,14 +12,17 @@ export abstract class CellPart extends Disposable { } /** - * Read DOM + * Perform DOM read operations to prepare for the list/cell layout update. */ - abstract prepareRender(): void; + abstract prepareLayout(): void; /** - * Update DOM based on layout info change of cell + * Update DOM per cell layout info change */ abstract updateLayoutNow(element: ICellViewModel): void; + /** + * Update per cell state change + */ abstract updateState(element: ICellViewModel, e: CellViewModelStateChangeEvent): void; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 3c26ff165a8..9e04e2b73ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -69,7 +69,7 @@ export class BetweenCellToolbar extends CellPart { this._betweenCellToolbar.context = context; } - prepareRender(): void { + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts index 26b22de0038..3a7ea729458 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts @@ -93,7 +93,7 @@ export class CellEditorStatusBar extends CellPart { })); } - prepareRender(): void { + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index 197c98790f7..38b1add96b5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -106,8 +106,8 @@ export class CodeCell extends Disposable { } this._register(this.viewCell.onLayoutInfoRead(() => { - this._outputContainerRenderer.prepareRender(); - this.cellParts.forEach(cellPart => cellPart.prepareRender()); + this._outputContainerRenderer.prepareLayout(); + this.cellParts.forEach(cellPart => cellPart.prepareLayout()); })); this.updateForCollapseState(); From 30aa38554bc14e3e0b302f8a2a6d6a18a1c91e7e Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 19 Nov 2021 11:39:49 -0800 Subject: [PATCH 265/330] fixes #137523 --- .../workbench/contrib/workspace/browser/workspaceTrustEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index 0786d610505..405141da521 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -62,7 +62,7 @@ const checkListIcon = registerIcon('workspace-trust-editor-check', Codicon.check const xListIcon = registerIcon('workspace-trust-editor-cross', Codicon.x, localize('xListIcon', 'Icon for the cross in the workspace trust editor.')); const folderPickerIcon = registerIcon('workspace-trust-editor-folder-picker', Codicon.folder, localize('folderPickerIcon', 'Icon for the pick folder icon in the workspace trust editor.')); const editIcon = registerIcon('workspace-trust-editor-edit-folder', Codicon.edit, localize('editIcon', 'Icon for the edit folder icon in the workspace trust editor.')); -const removeIcon = registerIcon('workspace-trust-editor-remove-folder', Codicon.edit, localize('removeIcon', 'Icon for the remove folder icon in the workspace trust editor.')); +const removeIcon = registerIcon('workspace-trust-editor-remove-folder', Codicon.close, localize('removeIcon', 'Icon for the remove folder icon in the workspace trust editor.')); interface ITrustedUriItem { parentOfWorkspaceItem: boolean; From b33ab96c3da806753423b4e42e6d3540e07652a3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 19 Nov 2021 11:49:35 -0800 Subject: [PATCH 266/330] Move progress bar to cell part. --- .../browser/view/cellParts/cellProgressBar.ts | 33 +++++++++++++------ .../browser/view/cellParts/codeCell.ts | 4 --- .../browser/view/renderers/cellRenderer.ts | 1 + 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts index 46394309daa..547478ef98d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { ICellViewModel, CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -export class CellProgressBar extends Disposable { +export class CellProgressBar extends CellPart { private readonly _progressBar: ProgressBar; private readonly _collapsedProgressBar: ProgressBar; @@ -24,16 +25,19 @@ export class CellProgressBar extends Disposable { this._collapsedProgressBar.hide(); } - updateForInternalMetadata(element: CodeCellViewModel, internalMetadata: NotebookCellInternalMetadata): void { - const progressBar = element.isInputCollapsed ? this._collapsedProgressBar : this._progressBar; - if (internalMetadata.runState === NotebookCellExecutionState.Executing && !internalMetadata.isPaused) { - showProgressBar(progressBar); - } else { - progressBar.hide(); - } + prepareLayout(): void { + // nothing to read } - updateForCellState(element: CodeCellViewModel): void { + updateLayoutNow(element: ICellViewModel): void { + // nothing to update + } + + updateState(element: ICellViewModel, e: CellViewModelStateChangeEvent): void { + if (!e.inputCollapsedChanged) { + return; + } + if (element.isInputCollapsed) { this._progressBar.hide(); if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { @@ -46,6 +50,15 @@ export class CellProgressBar extends Disposable { } } } + + updateForInternalMetadata(element: CodeCellViewModel, internalMetadata: NotebookCellInternalMetadata): void { + const progressBar = element.isInputCollapsed ? this._collapsedProgressBar : this._progressBar; + if (internalMetadata.runState === NotebookCellExecutionState.Executing && !internalMetadata.isPaused) { + showProgressBar(progressBar); + } else { + progressBar.hide(); + } + } } function showProgressBar(progressBar: ProgressBar): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index 38b1add96b5..ef8c0ae4836 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -65,10 +65,6 @@ export class CodeCell extends Disposable { this.updateForOutputHover(); } - if (e.inputCollapsedChanged) { - this.templateData.progressBar.updateForCellState(this.viewCell); - } - if (e.outputIsFocusedChanged) { this.updateForOutputFocus(); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index aa770285fd8..68a244e4436 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -665,6 +665,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.focusIndicator, templateData.betweenCellToolbar, templateData.statusBar, + templateData.progressBar, cellEditorOptions ])); this.renderedEditors.set(element, templateData.editor); From 6b52ad88d6ca2de112359c62f6073bebafaa1d21 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 19 Nov 2021 12:21:56 -0800 Subject: [PATCH 267/330] Fix #136774 --- .../contrib/welcome/gettingStarted/browser/gettingStarted.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css index 85b0163d359..4f49339c6ae 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -478,7 +478,7 @@ margin: 0 auto; padding: 0 0 0 32px; display: grid; - grid-template-columns: 1fr 4fr 1fr 8fr; + grid-template-columns: 1fr 5fr 1fr 8fr; grid-template-rows: calc(25% - 100px) auto auto 1fr auto; grid-template-areas: ". back . media ." From c28aeab31095d7b6670ba01526aab29bba48a857 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 19 Nov 2021 14:14:19 +0100 Subject: [PATCH 268/330] add verbose to remoteCli --- src/vs/server/remoteCli.ts | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/vs/server/remoteCli.ts b/src/vs/server/remoteCli.ts index 28dfc625974..01f272b7139 100644 --- a/src/vs/server/remoteCli.ts +++ b/src/vs/server/remoteCli.ts @@ -70,6 +70,7 @@ const isSupportedForPipe = (optionId: keyof RemoteParsedArgs) => { case 'force': case 'show-versions': case 'category': + case 'verbose': return true; default: return false; @@ -116,6 +117,8 @@ export function main(desc: ProductDescription, args: string[]): void { const parsedArgs = parseArgs(args, options, errorReporter); const mapFileUri = remoteAuthority ? mapFileToRemoteUri : (uri: string) => uri; + const verbose = !!parsedArgs['verbose']; + if (parsedArgs.help) { console.log(buildHelpMessage(desc.productName, desc.executableName, desc.version, options, true)); return; @@ -126,7 +129,7 @@ export function main(desc: ProductDescription, args: string[]): void { } if (cliPipe) { if (parsedArgs['openExternal']) { - openInBrowser(parsedArgs['_']); + openInBrowser(parsedArgs['_'], verbose); return; } } @@ -155,7 +158,7 @@ export function main(desc: ProductDescription, args: string[]): void { let stdinFilePath = cliStdInFilePath; if (!stdinFilePath) { stdinFilePath = getStdinFilePath(); - readFromStdin(stdinFilePath, !!parsedArgs.verbose); // throws error if file can not be written + readFromStdin(stdinFilePath, verbose); // throws error if file can not be written } // Make sure to open tmp file @@ -226,7 +229,7 @@ export function main(desc: ProductDescription, args: string[]): void { const ext = extname(cliCommand); if (ext === '.bat' || ext === '.cmd') { const processCwd = cliCommandCwd || cwd(); - if (parsedArgs['verbose']) { + if (verbose) { console.log(`Invoking: cmd.exe /C ${cliCommand} ${newCommandline.join(' ')} in ${processCwd}`); } _cp.spawn('cmd.exe', ['/C', cliCommand, ...newCommandline], { @@ -238,8 +241,8 @@ export function main(desc: ProductDescription, args: string[]): void { const env = { ...process.env, ELECTRON_RUN_AS_NODE: '1' }; newCommandline.unshift('--ms-enable-electron-run-as-node'); newCommandline.unshift('resources/app/out/cli.js'); - if (parsedArgs['verbose']) { - console.log(`Invoking: ${cliCommand} ${newCommandline.join(' ')} in ${cliCwd}`); + if (verbose) { + console.log(`Invoking: cd "${cliCwd}" && ELECTRON_RUN_AS_NODE=1 "${cliCommand}" "${newCommandline.join('" "')}"`); } _cp.spawn(cliCommand, newCommandline, { cwd: cliCwd, env, stdio: ['inherit'] }); } @@ -251,7 +254,7 @@ export function main(desc: ProductDescription, args: string[]): void { if (parsedArgs.status) { sendToPipe({ type: 'status' - }).then((res: string) => { + }, verbose).then((res: string) => { console.log(res); }); return; @@ -264,7 +267,7 @@ export function main(desc: ProductDescription, args: string[]): void { install: asExtensionIdOrVSIX(parsedArgs['install-extension']), uninstall: asExtensionIdOrVSIX(parsedArgs['uninstall-extension']), force: parsedArgs['force'] - }).then((res: string) => { + }, verbose).then((res: string) => { console.log(res); }); return; @@ -281,7 +284,7 @@ export function main(desc: ProductDescription, args: string[]): void { console.log('At least one file must be provided to wait for.'); return; } - waitMarkerFilePath = createWaitMarkerFile(parsedArgs.verbose); + waitMarkerFilePath = createWaitMarkerFile(verbose); } sendToPipe({ @@ -294,7 +297,7 @@ export function main(desc: ProductDescription, args: string[]): void { forceReuseWindow: parsedArgs['reuse-window'], forceNewWindow: parsedArgs['new-window'], waitMarkerFilePath - }); + }, verbose); if (waitMarkerFilePath) { waitForFileDeleted(waitMarkerFilePath); @@ -308,7 +311,7 @@ async function waitForFileDeleted(path: string) { } } -function openInBrowser(args: string[]) { +function openInBrowser(args: string[], verbose: boolean) { let uris: string[] = []; for (let location of args) { try { @@ -325,11 +328,14 @@ function openInBrowser(args: string[]) { sendToPipe({ type: 'openExternal', uris - }); + }, verbose); } } -function sendToPipe(args: PipeCommand): Promise { +function sendToPipe(args: PipeCommand, verbose: boolean): Promise { + if (verbose) { + console.log(JSON.stringify(args, null, ' ')); + } return new Promise(resolve => { const message = JSON.stringify(args); if (!cliPipe) { From cc30f1f967d70fb48f9debdb224dd5ad5d1ee49f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 19 Nov 2021 15:48:09 +0100 Subject: [PATCH 269/330] windowsMainServer: reuse doOpenEmpty & include arguments in traces --- .../electron-main/windowsMainService.ts | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 8720d6f7f16..17b6c9540eb 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -570,16 +570,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; const filesToOpenInWindow = isEqualAuthority(filesToOpen?.remoteAuthority, remoteAuthority) ? filesToOpen : undefined; - addUsedWindow(this.openInBrowserWindow({ - userEnv: openConfig.userEnv, - cli: openConfig.cli, - initialStartup: openConfig.initialStartup, - filesToOpen: filesToOpenInWindow, - remoteAuthority, - forceNewWindow: true, - forceNewTabbedWindow: openConfig.forceNewTabbedWindow, - emptyWindowBackupInfo - }), !!filesToOpenInWindow); + addUsedWindow(this.doOpenEmpty(openConfig, true, remoteAuthority, filesToOpenInWindow, emptyWindowBackupInfo), !!filesToOpenInWindow); openFolderInNewWindow = true; // any other folders to open must open in new window then }); @@ -605,7 +596,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen?: IFilesToOpen): ICodeWindow { - this.logService.trace('windowsManager#doOpenFilesInExistingWindow'); + this.logService.trace('windowsManager#doOpenFilesInExistingWindow', { filesToOpen }); window.focus(); // make sure window has focus @@ -621,7 +612,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } private doAddFoldersToExistingWindow(window: ICodeWindow, foldersToAdd: URI[]): ICodeWindow { - this.logService.trace('windowsManager#doAddFoldersToExistingWindow'); + this.logService.trace('windowsManager#doAddFoldersToExistingWindow', { foldersToAdd }); window.focus(); // make sure window has focus @@ -631,8 +622,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return window; } - private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { - if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { + private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, filesToOpen: IFilesToOpen | undefined, emptyWindowBackupInfo?: IEmptyWindowBackupInfo): ICodeWindow { + this.logService.trace('windowsManager#doOpenEmpty', { restore: !!emptyWindowBackupInfo, remoteAuthority, filesToOpen, forceNewWindow }); + + let windowToUse: ICodeWindow | undefined; + if (!forceNewWindow && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/97172 } @@ -644,11 +638,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, filesToOpen, - windowToUse + windowToUse, + emptyWindowBackupInfo }); } private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IWorkspacePathToOpen | ISingleFolderWorkspacePathToOpen, forceNewWindow: boolean, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow { + this.logService.trace('windowsManager#doOpenFolderOrWorkspace', { folderOrWorkspace, filesToOpen }); + if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') { windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/49587 } From d3aabc5449fa9aa3e98c0f07d0790905d3647845 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 19 Nov 2021 17:14:20 +0100 Subject: [PATCH 270/330] remoteCli: support opening new windows, including other remotes and locals. Fixes #137529 --- src/vs/server/remoteCli.ts | 40 +++++++++---------- .../api/browser/mainThreadCLICommands.ts | 5 ++- src/vs/workbench/api/node/extHostCLIServer.ts | 14 +++---- .../electron-sandbox/nativeHostService.ts | 5 +++ 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/vs/server/remoteCli.ts b/src/vs/server/remoteCli.ts index 01f272b7139..0210fcaa0b6 100644 --- a/src/vs/server/remoteCli.ts +++ b/src/vs/server/remoteCli.ts @@ -71,6 +71,7 @@ const isSupportedForPipe = (optionId: keyof RemoteParsedArgs) => { case 'show-versions': case 'category': case 'verbose': + case 'remote': return true; default: return false; @@ -80,7 +81,7 @@ const isSupportedForPipe = (optionId: keyof RemoteParsedArgs) => { const cliPipe = process.env['VSCODE_IPC_HOOK_CLI'] as string; const cliCommand = process.env['VSCODE_CLIENT_COMMAND'] as string; const cliCommandCwd = process.env['VSCODE_CLIENT_COMMAND_CWD'] as string; -const remoteAuthority = process.env['VSCODE_CLI_AUTHORITY'] as string; +const cliRemoteAuthority = process.env['VSCODE_CLI_AUTHORITY'] as string; const cliStdInFilePath = process.env['VSCODE_STDIN_FILE_PATH'] as string; @@ -115,7 +116,7 @@ export function main(desc: ProductDescription, args: string[]): void { }; const parsedArgs = parseArgs(args, options, errorReporter); - const mapFileUri = remoteAuthority ? mapFileToRemoteUri : (uri: string) => uri; + const mapFileUri = cliRemoteAuthority ? mapFileToRemoteUri : (uri: string) => uri; const verbose = !!parsedArgs['verbose']; @@ -134,20 +135,26 @@ export function main(desc: ProductDescription, args: string[]): void { } } + let remote: string | null | undefined = parsedArgs.remote; + if (remote === 'local' || remote === 'false' || remote === '') { + remote = null; + } - let folderURIs = (parsedArgs['folder-uri'] || []).map(mapFileUri); + const folderURIs = (parsedArgs['folder-uri'] || []).map(mapFileUri); parsedArgs['folder-uri'] = folderURIs; - let fileURIs = (parsedArgs['file-uri'] || []).map(mapFileUri); + const fileURIs = (parsedArgs['file-uri'] || []).map(mapFileUri); parsedArgs['file-uri'] = fileURIs; - let inputPaths = parsedArgs['_']; + const inputPaths = parsedArgs['_']; let hasReadStdinArg = false; for (let input of inputPaths) { if (input === '-') { hasReadStdinArg = true; } else { - translatePath(input, mapFileUri, folderURIs, fileURIs); + if (remote !== undefined) { + translatePath(input, mapFileUri, folderURIs, fileURIs); + } } } @@ -189,10 +196,6 @@ export function main(desc: ProductDescription, args: string[]): void { return; } - if (remoteAuthority) { - parsedArgs['remote'] = remoteAuthority; - } - if (cliCommand) { if (parsedArgs['install-extension'] !== undefined || parsedArgs['uninstall-extension'] !== undefined || parsedArgs['list-extensions']) { const cmdLine: string[] = []; @@ -225,6 +228,9 @@ export function main(desc: ProductDescription, args: string[]): void { newCommandline.push(`--${key}=${val.toString()}`); } } + if (remote !== null) { + newCommandline.push(`--remote=${remote || cliRemoteAuthority}`); + } const ext = extname(cliCommand); if (ext === '.bat' || ext === '.cmd') { @@ -247,10 +253,6 @@ export function main(desc: ProductDescription, args: string[]): void { _cp.spawn(cliCommand, newCommandline, { cwd: cliCwd, env, stdio: ['inherit'] }); } } else { - if (args.length === 0) { - console.log(buildHelpMessage(desc.productName, desc.executableName, desc.version, options, true)); - return; - } if (parsedArgs.status) { sendToPipe({ type: 'status' @@ -273,11 +275,6 @@ export function main(desc: ProductDescription, args: string[]): void { return; } - if (!fileURIs.length && !folderURIs.length) { - console.log('At least one file or folder must be provided.'); - return; - } - let waitMarkerFilePath: string | undefined = undefined; if (parsedArgs['wait']) { if (!fileURIs.length) { @@ -296,7 +293,8 @@ export function main(desc: ProductDescription, args: string[]): void { gotoLineMode: parsedArgs.goto, forceReuseWindow: parsedArgs['reuse-window'], forceNewWindow: parsedArgs['new-window'], - waitMarkerFilePath + waitMarkerFilePath, + remoteAuthority: remote }, verbose); if (waitMarkerFilePath) { @@ -411,7 +409,7 @@ function translatePath(input: string, mapFileUri: (input: string) => string, fol } function mapFileToRemoteUri(uri: string): string { - return uri.replace(/^file:\/\//, 'vscode-remote://' + remoteAuthority); + return uri.replace(/^file:\/\//, 'vscode-remote://' + cliRemoteAuthority); } let [, , productName, version, commit, executableName, ...remainingArgs] = process.argv; diff --git a/src/vs/workbench/api/browser/mainThreadCLICommands.ts b/src/vs/workbench/api/browser/mainThreadCLICommands.ts index 40d8f0a48d2..1fbb67106bc 100644 --- a/src/vs/workbench/api/browser/mainThreadCLICommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCLICommands.ts @@ -31,8 +31,11 @@ CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: return openerService.open(isString(uri) ? uri : URI.revive(uri), { openExternal: true, allowTunneling: true }); }); -CommandsRegistry.registerCommand('_remoteCLI.windowOpen', function (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options?: IOpenWindowOptions) { +CommandsRegistry.registerCommand('_remoteCLI.windowOpen', function (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options: IOpenWindowOptions) { const commandService = accessor.get(ICommandService); + if (!toOpen.length) { + return commandService.executeCommand('_files.newWindow', options); + } return commandService.executeCommand('_files.windowOpen', toOpen, options); }); diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index 6177f03c80f..2a03770b37e 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -22,6 +22,7 @@ export interface OpenCommandPipeArgs { gotoLineMode?: boolean; forceReuseWindow?: boolean; waitMarkerFilePath?: string; + remoteAuthority?: string | null; } export interface OpenExternalCommandPipeArgs { @@ -111,7 +112,7 @@ export class CLIServerBase { } private open(data: OpenCommandPipeArgs, res: http.ServerResponse) { - let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow, gotoLineMode, waitMarkerFilePath } = data; + const { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow, gotoLineMode, waitMarkerFilePath, remoteAuthority } = data; const urisToOpen: IWindowOpenable[] = []; if (Array.isArray(folderURIs)) { for (const s of folderURIs) { @@ -135,12 +136,11 @@ export class CLIServerBase { } } } - if (urisToOpen.length) { - const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined; - const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode; - const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI }; - this._commands.executeCommand('_remoteCLI.windowOpen', urisToOpen, windowOpenArgs); - } + const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined; + const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode; + const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI, remoteAuthority }; + this._commands.executeCommand('_remoteCLI.windowOpen', urisToOpen, windowOpenArgs); + res.writeHead(200); res.end(); } diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index a9e065ae5ee..cc5a96cedfc 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -89,6 +89,11 @@ export class NativeHostService extends Disposable implements IHostService { } private doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { + const remoteAuthority = this.environmentService.remoteAuthority; + if (!!remoteAuthority && options?.remoteAuthority === undefined) { + // set the remoteAuthority of the window the request came from + options = options ? { ...options, remoteAuthority } : { remoteAuthority }; + } return this.nativeHostService.openWindow(options); } From f13f5832fdf6e4177aff257d32b3c57c443d32c7 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 19 Nov 2021 22:06:46 +0100 Subject: [PATCH 271/330] [json] when downloading JSON Schema files, do Conditional GETs. Fixes #101050 --- .../client/src/browser/jsonClientMain.ts | 8 +- .../client/src/jsonClient.ts | 14 +- .../client/src/node/jsonClientMain.ts | 126 +++++++++++++++--- .../client/src/node/schemaCache.ts | 108 +++++++++++++++ .../client/src/requests.ts | 4 - 5 files changed, 229 insertions(+), 31 deletions(-) create mode 100644 extensions/json-language-features/client/src/node/schemaCache.ts diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts index 6389dafb5ba..9eb390b545c 100644 --- a/extensions/json-language-features/client/src/browser/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -5,9 +5,8 @@ import { ExtensionContext, Uri } from 'vscode'; import { LanguageClientOptions } from 'vscode-languageclient'; -import { startClient, LanguageClientConstructor } from '../jsonClient'; +import { startClient, LanguageClientConstructor, SchemaRequestService } from '../jsonClient'; import { LanguageClient } from 'vscode-languageclient/browser'; -import { RequestService } from '../requests'; declare const Worker: { new(stringUrl: string): any; @@ -24,7 +23,7 @@ export function activate(context: ExtensionContext) { return new LanguageClient(id, name, clientOptions, worker); }; - const http: RequestService = { + const schemaRequests: SchemaRequestService = { getContent(uri: string) { return fetch(uri, { mode: 'cors' }) .then(function (response: any) { @@ -32,7 +31,8 @@ export function activate(context: ExtensionContext) { }); } }; - startClient(context, newLanguageClient, { http }); + + startClient(context, newLanguageClient, { schemaRequests }); } catch (e) { console.log(e); diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index eeae0d3f03a..2f0a845f1fe 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -20,7 +20,7 @@ import { } from 'vscode-languageclient'; import { hash } from './utils/hash'; -import { RequestService, joinPath } from './requests'; +import { joinPath } from './requests'; import { createLanguageStatusItem } from './languageStatus'; namespace VSCodeContentRequest { @@ -96,10 +96,16 @@ export interface TelemetryReporter { export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; export interface Runtime { - http: RequestService; + schemaRequests: SchemaRequestService; telemetry?: TelemetryReporter } +export interface SchemaRequestService { + getContent(uri: string): Promise; +} + +export const languageServerDescription = localize('jsonserver.name', 'JSON Language Server'); + export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { const toDispose = context.subscriptions; @@ -198,7 +204,7 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua }; // Create the language client and start the client. - const client = newLanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), clientOptions); + const client = newLanguageClient('json', languageServerDescription, clientOptions); client.registerProposedFeatures(); const disposable = client.start(); @@ -228,7 +234,7 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua */ runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); } - return runtime.http.getContent(uriPath).catch(e => { + return runtime.schemaRequests.getContent(uriPath).catch(e => { return Promise.reject(new ResponseError(4, e.toString())); }); } else { diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts index b8f96f51753..186e7c8f7bf 100644 --- a/extensions/json-language-features/client/src/node/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -3,24 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext } from 'vscode'; -import { startClient, LanguageClientConstructor } from '../jsonClient'; +import { ExtensionContext, OutputChannel, window, workspace } from 'vscode'; +import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient'; import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; -import * as fs from 'fs'; -import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { xhr, XHRResponse, getErrorStatusDescription, Headers } from 'request-light'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { RequestService } from '../requests'; +import { JSONSchemaCache } from './schemaCache'; let telemetry: TelemetryReporter | undefined; // this method is called when vs code is activated -export function activate(context: ExtensionContext) { - - const clientPackageJSON = getPackageInfo(context); +export async function activate(context: ExtensionContext) { + const clientPackageJSON = await getPackageInfo(context); telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey); + const outputChannel = window.createOutputChannel(languageServerDescription); + const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`; const serverModule = context.asAbsolutePath(serverMain); @@ -35,10 +37,15 @@ export function activate(context: ExtensionContext) { }; const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { + clientOptions.outputChannel = outputChannel; return new LanguageClient(id, name, serverOptions, clientOptions); }; + const log = getLog(outputChannel); + context.subscriptions.push(log); - startClient(context, newLanguageClient, { http: getHTTPRequestService(), telemetry }); + const schemaRequests = await getSchemaRequestService(context, log); + + startClient(context, newLanguageClient, { schemaRequests, telemetry }); } export function deactivate(): Promise { @@ -52,23 +59,88 @@ interface IPackageInfo { main: string; } -function getPackageInfo(context: ExtensionContext): IPackageInfo { +async function getPackageInfo(context: ExtensionContext): Promise { const location = context.asAbsolutePath('./package.json'); try { - return JSON.parse(fs.readFileSync(location).toString()); + return JSON.parse((await fs.readFile(location)).toString()); } catch (e) { console.log(`Problems reading ${location}: ${e}`); return { name: '', version: '', aiKey: '', main: '' }; } } -function getHTTPRequestService(): RequestService { +interface Log { + trace(message: string): void; + dispose(): void; +} + +const traceSetting = 'json.trace.server'; +function getLog(outputChannel: OutputChannel): Log { + let trace = workspace.getConfiguration().get(traceSetting) === 'verbose'; + const configListener = workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(traceSetting)) { + trace = workspace.getConfiguration().get(traceSetting) === 'verbose'; + } + }); return { - getContent(uri: string, _encoding?: string): Promise { - const headers = { 'Accept-Encoding': 'gzip, deflate' }; - return xhr({ url: uri, followRedirects: 5, headers }).then(response => { - return response.responseText; - }, (error: XHRResponse) => { + trace(message: string) { + if (trace) { + outputChannel.appendLine(message); + } + }, + dispose: () => configListener.dispose() + }; +} + +const retryTimeoutInDays = 2; // 2 days +const retryTimeoutInMs = retryTimeoutInDays * 24 * 60 * 60 * 1000; + +async function getSchemaRequestService(context: ExtensionContext, log: Log): Promise { + let cache: JSONSchemaCache | undefined = undefined; + const globalStorage = context.globalStorageUri; + if (globalStorage.scheme === 'file') { + const schemaCacheLocation = path.join(globalStorage.fsPath, 'json-schema-cache'); + await fs.mkdir(schemaCacheLocation, { recursive: true }); + + cache = new JSONSchemaCache(schemaCacheLocation, context.globalState); + log.trace(`[json schema cache] initial state: ${JSON.stringify(cache.getCacheInfo(), null, ' ')}`); + } + + const isXHRResponse = (error: any): error is XHRResponse => typeof error?.status === 'number'; + + const request = async (uri: string, etag?: string): Promise => { + const headers: Headers = { 'Accept-Encoding': 'gzip, deflate' }; + if (etag) { + headers['If-None-Match'] = etag; + } + try { + log.trace(`[json schema cache] Requesting schema ${uri} etag ${etag}...`); + + const response = await xhr({ url: uri, followRedirects: 5, headers }); + if (cache) { + const etag = response.headers['etag']; + if (typeof etag === 'string') { + log.trace(`[json schema cache] Storing schema ${uri} etag ${etag} in cache`); + await cache.putSchema(uri, etag, response.responseText); + } else { + log.trace(`[json schema cache] Response: schema ${uri} no etag`); + } + } + return response.responseText; + } catch (error: unknown) { + if (isXHRResponse(error)) { + if (error.status === 304 && etag && cache) { + + log.trace(`[json schema cache] Response: schema ${uri} unchanged etag ${etag}`); + + const content = await cache.getSchema(uri, etag); + if (content) { + log.trace(`[json schema cache] Get schema ${uri} etag ${etag} from cache`); + return content; + } + return request(uri); + } + let status = getErrorStatusDescription(error.status); if (status && error.responseText) { status = `${status}\n${error.responseText.substring(0, 200)}`; @@ -76,8 +148,24 @@ function getHTTPRequestService(): RequestService { if (!status) { status = error.toString(); } - return Promise.reject(status); - }); + log.trace(`[json schema cache] Respond schema ${uri} error ${status}`); + + throw status; + } + throw error; + } + }; + + return { + getContent: async (uri: string) => { + if (cache && /^https?:\/\/json\.schemastore\.org\//.test(uri)) { + const content = await cache.getSchemaIfAccessedSince(uri, retryTimeoutInMs); + if (content) { + log.trace(`[json schema cache] Schema ${uri} from cache without request (last accessed less than ${retryTimeoutInDays} days ago)`); + return content; + } + } + return request(uri, cache?.getETag(uri)); } }; } diff --git a/extensions/json-language-features/client/src/node/schemaCache.ts b/extensions/json-language-features/client/src/node/schemaCache.ts new file mode 100644 index 00000000000..4661c5c7a12 --- /dev/null +++ b/extensions/json-language-features/client/src/node/schemaCache.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { createHash } from 'crypto'; +import { Memento } from 'vscode'; + +interface CacheEntry { + etag: string; + fileName: string; + accessTime: number; +} + +interface CacheInfo { + [schemaUri: string]: CacheEntry; +} + +const MEMENTO_KEY = 'json-schema-cache'; + +export class JSONSchemaCache { + private readonly cacheInfo: CacheInfo; + + constructor(private readonly schemaCacheLocation: string, private readonly globalState: Memento) { + this.cacheInfo = globalState.get(MEMENTO_KEY, {}); + } + + getETag(schemaUri: string): string | undefined { + return this.cacheInfo[schemaUri]?.etag; + } + + async putSchema(schemaUri: string, etag: string, schemaContent: string): Promise { + try { + const fileName = getCacheFileName(schemaUri); + await fs.writeFile(path.join(this.schemaCacheLocation, fileName), schemaContent); + const entry: CacheEntry = { etag, fileName, accessTime: new Date().getTime() }; + this.cacheInfo[schemaUri] = entry; + } catch (e) { + delete this.cacheInfo[schemaUri]; + } finally { + await this.updateMemento(); + } + } + + async getSchemaIfAccessedSince(schemaUri: string, expirationDuration: number): Promise { + const cacheEntry = this.cacheInfo[schemaUri]; + if (cacheEntry && cacheEntry.accessTime + expirationDuration >= new Date().getTime()) { + return this.loadSchemaFile(schemaUri, cacheEntry); + } + return undefined; + } + + async getSchema(schemaUri: string, etag: string): Promise { + const cacheEntry = this.cacheInfo[schemaUri]; + if (cacheEntry) { + if (cacheEntry.etag === etag) { + return this.loadSchemaFile(schemaUri, cacheEntry); + } else { + this.deleteSchemaFile(schemaUri, cacheEntry); + } + } + return undefined; + } + + private async loadSchemaFile(schemaUri: string, cacheEntry: CacheEntry): Promise { + const cacheLocation = path.join(this.schemaCacheLocation, cacheEntry.fileName); + try { + const content = (await fs.readFile(cacheLocation)).toString(); + cacheEntry.accessTime = new Date().getTime(); + return content; + } catch (e) { + delete this.cacheInfo[schemaUri]; + return undefined; + } finally { + await this.updateMemento(); + } + } + + private async deleteSchemaFile(schemaUri: string, cacheEntry: CacheEntry): Promise { + const cacheLocation = path.join(this.schemaCacheLocation, cacheEntry.fileName); + delete this.cacheInfo[schemaUri]; + await this.updateMemento(); + try { + await fs.rm(cacheLocation); + } catch (e) { + // ignore + } + } + + + // for debugging + public getCacheInfo() { + return this.cacheInfo; + } + + private async updateMemento() { + try { + await this.globalState.update(MEMENTO_KEY, this.cacheInfo); + } catch (e) { + // ignore + } + } +} +function getCacheFileName(uri: string): string { + return `${createHash('MD5').update(uri).digest('hex')}.schema.json`; +} diff --git a/extensions/json-language-features/client/src/requests.ts b/extensions/json-language-features/client/src/requests.ts index ed06ded574f..dd12ffb7ba5 100644 --- a/extensions/json-language-features/client/src/requests.ts +++ b/extensions/json-language-features/client/src/requests.ts @@ -5,10 +5,6 @@ import { Uri } from 'vscode'; -export interface RequestService { - getContent(uri: string, encoding?: string): Promise; -} - export function getScheme(uri: string) { return uri.substr(0, uri.indexOf(':')); } From 9a97289dc76399e3e213a8d82362ac7c97302db1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 19 Nov 2021 13:07:54 -0800 Subject: [PATCH 272/330] redundant layout change of cell status bar. --- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 68a244e4436..149e15231ee 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -683,7 +683,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(element.onDidChangeState((e) => { if (e.metadataChanged || e.internalMetadataChanged) { this.updateForInternalMetadata(element, templateData); - this.updateForLayout(element, templateData); } })); From 6123d781cbd8dc3b0b13462546a79344bd79ea2d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 19 Nov 2021 22:26:58 +0100 Subject: [PATCH 273/330] [json] use uri helpers --- .../client/src/jsonClient.ts | 5 +- .../client/src/requests.ts | 64 -------------- .../server/src/jsonServer.ts | 12 ++- .../server/src/node/jsonServerMain.ts | 3 +- .../server/src/requests.ts | 87 ------------------- 5 files changed, 11 insertions(+), 160 deletions(-) delete mode 100644 extensions/json-language-features/client/src/requests.ts delete mode 100644 extensions/json-language-features/server/src/requests.ts diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 2f0a845f1fe..11c8c615299 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -20,7 +20,6 @@ import { } from 'vscode-languageclient'; import { hash } from './utils/hash'; -import { joinPath } from './requests'; import { createLanguageStatusItem } from './languageStatus'; namespace VSCodeContentRequest { @@ -392,7 +391,7 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] if (Array.isArray(fileMatch) && typeof url === 'string') { let uri: string = url; if (uri[0] === '.' && uri[1] === '/') { - uri = joinPath(extension.extensionUri, uri).toString(); + uri = Uri.joinPath(extension.extensionUri, uri).toString(); } fileMatch = fileMatch.map(fm => { if (fm[0] === '%') { @@ -513,7 +512,7 @@ function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri): string | unde url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`; } } else if (folderUri && (url[0] === '.' || url[0] === '/')) { - url = joinPath(folderUri, url).toString(); + url = Uri.joinPath(folderUri, url).toString(); } return url; } diff --git a/extensions/json-language-features/client/src/requests.ts b/extensions/json-language-features/client/src/requests.ts deleted file mode 100644 index dd12ffb7ba5..00000000000 --- a/extensions/json-language-features/client/src/requests.ts +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Uri } from 'vscode'; - -export function getScheme(uri: string) { - return uri.substr(0, uri.indexOf(':')); -} - -export function dirname(uri: string) { - const lastIndexOfSlash = uri.lastIndexOf('/'); - return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; -} - -export function basename(uri: string) { - const lastIndexOfSlash = uri.lastIndexOf('/'); - return uri.substr(lastIndexOfSlash + 1); -} - -const Slash = '/'.charCodeAt(0); -const Dot = '.'.charCodeAt(0); - -export function isAbsolutePath(path: string) { - return path.charCodeAt(0) === Slash; -} - -export function resolvePath(uri: Uri, path: string): Uri { - if (isAbsolutePath(path)) { - return uri.with({ path: normalizePath(path.split('/')) }); - } - return joinPath(uri, path); -} - -export function normalizePath(parts: string[]): string { - const newParts: string[] = []; - for (const part of parts) { - if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { - // ignore - } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { - newParts.pop(); - } else { - newParts.push(part); - } - } - if (parts.length > 1 && parts[parts.length - 1].length === 0) { - newParts.push(''); - } - let res = newParts.join('/'); - if (parts[0].length === 0) { - res = '/' + res; - } - return res; -} - - -export function joinPath(uri: Uri, ...paths: string[]): Uri { - const parts = uri.path.split('/'); - for (let path of paths) { - parts.push(...path.split('/')); - } - return uri.with({ path: normalizePath(parts) }); -} diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index eb970784d9b..142594e0c3c 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -12,7 +12,7 @@ import { import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Diagnostic, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; -import { RequestService, basename, resolvePath } from './requests'; +import { Utils, URI } from 'vscode-uri'; type ISchemaAssociations = Record; @@ -45,11 +45,15 @@ namespace LanguageStatusRequest { const workspaceContext = { resolveRelativePath: (relativePath: string, resource: string) => { - const base = resource.substr(0, resource.lastIndexOf('/') + 1); - return resolvePath(base, relativePath); + const base = resource.substring(0, resource.lastIndexOf('/') + 1); + return Utils.resolvePath(URI.parse(base), relativePath).toString(); } }; +export interface RequestService { + getContent(uri: string): Promise; +} + export interface RuntimeEnvironment { file?: RequestService; http?: RequestService @@ -185,7 +189,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const showLimitedNotification = (uri: string, resultLimit: number) => { const warning = pendingWarnings[uri]; - connection.sendNotification(ResultLimitReachedNotification.type, `${basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); + connection.sendNotification(ResultLimitReachedNotification.type, `${Utils.basename(URI.parse(uri))}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); warning.timeout = undefined; }; diff --git a/extensions/json-language-features/server/src/node/jsonServerMain.ts b/extensions/json-language-features/server/src/node/jsonServerMain.ts index 17e0fcd9940..4cb387095aa 100644 --- a/extensions/json-language-features/server/src/node/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/node/jsonServerMain.ts @@ -5,8 +5,7 @@ import { createConnection, Connection, Disposable } from 'vscode-languageserver/node'; import { formatError } from '../utils/runner'; -import { RuntimeEnvironment, startServer } from '../jsonServer'; -import { RequestService } from '../requests'; +import { RequestService, RuntimeEnvironment, startServer } from '../jsonServer'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; import { URI as Uri } from 'vscode-uri'; diff --git a/extensions/json-language-features/server/src/requests.ts b/extensions/json-language-features/server/src/requests.ts deleted file mode 100644 index a87cf92483f..00000000000 --- a/extensions/json-language-features/server/src/requests.ts +++ /dev/null @@ -1,87 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { URI } from 'vscode-uri'; - -export interface RequestService { - getContent(uri: string, encoding?: string): Promise; -} - -export function getScheme(uri: string) { - return uri.substr(0, uri.indexOf(':')); -} - -export function dirname(uri: string) { - const lastIndexOfSlash = uri.lastIndexOf('/'); - return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; -} - -export function basename(uri: string) { - const lastIndexOfSlash = uri.lastIndexOf('/'); - return uri.substr(lastIndexOfSlash + 1); -} - - -const Slash = '/'.charCodeAt(0); -const Dot = '.'.charCodeAt(0); - -export function extname(uri: string) { - for (let i = uri.length - 1; i >= 0; i--) { - const ch = uri.charCodeAt(i); - if (ch === Dot) { - if (i > 0 && uri.charCodeAt(i - 1) !== Slash) { - return uri.substr(i); - } else { - break; - } - } else if (ch === Slash) { - break; - } - } - return ''; -} - -export function isAbsolutePath(path: string) { - return path.charCodeAt(0) === Slash; -} - -export function resolvePath(uriString: string, path: string): string { - if (isAbsolutePath(path)) { - const uri = URI.parse(uriString); - const parts = path.split('/'); - return uri.with({ path: normalizePath(parts) }).toString(); - } - return joinPath(uriString, path); -} - -export function normalizePath(parts: string[]): string { - const newParts: string[] = []; - for (const part of parts) { - if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { - // ignore - } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { - newParts.pop(); - } else { - newParts.push(part); - } - } - if (parts.length > 1 && parts[parts.length - 1].length === 0) { - newParts.push(''); - } - let res = newParts.join('/'); - if (parts[0].length === 0) { - res = '/' + res; - } - return res; -} - -export function joinPath(uriString: string, ...paths: string[]): string { - const uri = URI.parse(uriString); - const parts = uri.path.split('/'); - for (let path of paths) { - parts.push(...path.split('/')); - } - return uri.with({ path: normalizePath(parts) }).toString(); -} From 9ed1645650cc03cf5f4ff46a7558a62ebafc82f4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 19 Nov 2021 09:45:09 -0800 Subject: [PATCH 274/330] Fix terminal grace time constants in remote This was causing detach to kill procs immediately Part of #136064 --- src/vs/server/remoteExtensionHostAgentServer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/server/remoteExtensionHostAgentServer.ts b/src/vs/server/remoteExtensionHostAgentServer.ts index a54bbbe1d82..21726c2c206 100644 --- a/src/vs/server/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/remoteExtensionHostAgentServer.ts @@ -322,8 +322,8 @@ export class RemoteExtensionHostAgentServer extends Disposable { const ptyService = instantiationService.createInstance( PtyHostService, { - GraceTime: ProtocolConstants.ReconnectionGraceTime, - ShortGraceTime: ProtocolConstants.ReconnectionShortGraceTime, + graceTime: ProtocolConstants.ReconnectionGraceTime, + shortGraceTime: ProtocolConstants.ReconnectionShortGraceTime, scrollback: configurationService.getValue(TerminalSettingId.PersistentSessionScrollback) ?? 100 } ); From ec4b7e10abcccd84ea87734deea9aef559629ec1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 19 Nov 2021 09:45:53 -0800 Subject: [PATCH 275/330] Fix fetching remote backends for attach session command Part of #136064 --- .../workbench/contrib/terminal/browser/terminalActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index daa8b1b19e2..c292ceeb876 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -970,11 +970,13 @@ export function registerTerminalActions() { const labelService = accessor.get(ILabelService); const remoteAgentService = accessor.get(IRemoteAgentService); const notificationService = accessor.get(INotificationService); - const backend = accessor.get(ITerminalInstanceService).getBackend(); const terminalGroupService = accessor.get(ITerminalGroupService); + const remoteAuthority = remoteAgentService.getConnection()?.remoteAuthority ?? undefined; + const backend = accessor.get(ITerminalInstanceService).getBackend(remoteAuthority); + if (!backend) { - throw new Error(`No backend registered for remote authority '${remoteAgentService.getConnection()?.remoteAuthority ?? undefined}'`); + throw new Error(`No backend registered for remote authority '${remoteAuthority}'`); } const terms = await backend.listProcesses(); From 2f7ab5c0e03480d6474959a47ecdfbb07b832b0e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 19 Nov 2021 13:58:01 -0800 Subject: [PATCH 276/330] Disable should update color of the tab Flake in CI --- test/smoke/src/areas/terminal/terminal-editors.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-editors.test.ts b/test/smoke/src/areas/terminal/terminal-editors.test.ts index 747c2cd761c..a57d6d50d9c 100644 --- a/test/smoke/src/areas/terminal/terminal-editors.test.ts +++ b/test/smoke/src/areas/terminal/terminal-editors.test.ts @@ -22,7 +22,8 @@ export function setup(opts: ParsedArgs) { await terminal.runCommand(TerminalCommandId.KillAll); }); - it('should update color of the tab', async () => { + // TODO: This was flaky in CI + it.skip('should update color of the tab', async () => { await terminal.runCommand(TerminalCommandId.CreateNewEditor); const color = 'Cyan'; await terminal.runCommandWithValue(TerminalCommandIdWithValue.ChangeColor, color); From 3c7bc14d8c379d0e91348bc6be04e17a33ffda45 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 19 Nov 2021 14:03:48 -0800 Subject: [PATCH 277/330] Fix terminal find z index Fixes #137017 --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index e2f0e2be9b9..5710c5ab11a 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -70,6 +70,10 @@ z-index: 31; } +.monaco-workbench .simple-find-part-wrapper { + z-index: 32 !important; +} + .xterm .xterm-screen { cursor: text; } From 75c202e435e5d6a949b0d6bdd76b55d84fcd933a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Nov 2021 16:04:29 -0800 Subject: [PATCH 278/330] Don't send over npmLocation on web --- extensions/typescript-language-features/src/tsServer/spawner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 6dcf81d929d..efec8685ae9 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -256,7 +256,7 @@ export class TypeScriptServerSpawner { } } - if (configuration.npmLocation) { + if (configuration.npmLocation && !isWeb()) { args.push('--npmLocation', `"${configuration.npmLocation}"`); } From fa1266520cb8e6da10e5e9c8df415e4841079154 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Nov 2021 16:06:08 -0800 Subject: [PATCH 279/330] Removing gating for ts args These should be ignored on older TS versions --- .../src/tsServer/spawner.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index efec8685ae9..9a1e767bdbc 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -260,17 +260,11 @@ export class TypeScriptServerSpawner { args.push('--npmLocation', `"${configuration.npmLocation}"`); } - if (apiVersion.gte(API.v260)) { - args.push('--locale', TypeScriptServerSpawner.getTsLocale(configuration)); - } + args.push('--locale', TypeScriptServerSpawner.getTsLocale(configuration)); - if (apiVersion.gte(API.v291)) { - args.push('--noGetErrOnBackgroundUpdate'); - } + args.push('--noGetErrOnBackgroundUpdate'); - if (apiVersion.gte(API.v345)) { - args.push('--validateDefaultNpmLocation'); - } + args.push('--validateDefaultNpmLocation'); return { args, tsServerLogFile, tsServerTraceDirectory }; } From 9939a828518081421301ecbe11e693f3dff05d96 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Nov 2021 16:17:25 -0800 Subject: [PATCH 280/330] Add separator for TS version quick pick --- extensions/typescript-language-features/package.json | 1 + .../src/tsServer/versionManager.ts | 7 ++++++- extensions/typescript-language-features/tsconfig.json | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 3eaf31c0c5d..3a232706a87 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -10,6 +10,7 @@ "enabledApiProposals": [ "inlayHints", "languageStatus", + "quickPickSeparators", "resolvers", "workspaceTrust" ], diff --git a/extensions/typescript-language-features/src/tsServer/versionManager.ts b/extensions/typescript-language-features/src/tsServer/versionManager.ts index efd567148dd..7b8a3640660 100644 --- a/extensions/typescript-language-features/src/tsServer/versionManager.ts +++ b/extensions/typescript-language-features/src/tsServer/versionManager.ts @@ -82,6 +82,11 @@ export class TypeScriptVersionManager extends Disposable { const selected = await vscode.window.showQuickPick([ this.getBundledPickItem(), ...this.getLocalPickItems(), + { + kind: vscode.QuickPickItemKind.Separator, + label: '', + run: () => { /* noop */ }, + }, LearnMorePickItem, ], { placeHolder: localize( @@ -180,7 +185,7 @@ export class TypeScriptVersionManager extends Disposable { } const LearnMorePickItem: QuickPickItem = { - label: localize('learnMore', 'Learn more about managing TypeScript versions'), + label: localize('learnMore', "Learn more about managing TypeScript versions"), description: '', run: () => { vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 07d4066f89b..757f9ec5984 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -12,6 +12,7 @@ "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.inlayHints.d.ts", "../../src/vscode-dts/vscode.proposed.languageStatus.d.ts", + "../../src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts", "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", ] From e784e88a872d8d08e25b36da40150d05b582058f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Nov 2021 16:32:23 -0800 Subject: [PATCH 281/330] Update setting name to be more extensible in the future Discussed in https://github.com/microsoft/TypeScript/issues/46590#issuecomment-966756378 --- extensions/typescript-language-features/package.json | 8 ++++---- extensions/typescript-language-features/package.nls.json | 2 +- .../src/languageFeatures/fileConfigurationManager.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 3a232706a87..6651308b424 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -1116,16 +1116,16 @@ "markdownDescription": "%typescript.workspaceSymbols.scope%", "scope": "window" }, - "javascript.suggest.includeCompletionsWithClassMemberSnippets": { + "javascript.suggest.classMemberSnippets.enabled": { "type": "boolean", "default": true, - "description": "%configuration.suggest.includeCompletionsWithClassMemberSnippets%", + "description": "%configuration.suggest.classMemberSnippets.enabled%", "scope": "resource" }, - "typescript.suggest.includeCompletionsWithClassMemberSnippets": { + "typescript.suggest.classMemberSnippets.enabled": { "type": "boolean", "default": true, - "description": "%configuration.suggest.includeCompletionsWithClassMemberSnippets%", + "description": "%configuration.suggest.classMemberSnippets.enabled%", "scope": "resource" } } diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 0014fe26992..4f76591ad37 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -184,5 +184,5 @@ "codeActions.refactor.rewrite.property.generateAccessors.description": "Generate 'get' and 'set' accessors", "codeActions.source.organizeImports.title": "Organize imports", "typescript.findAllFileReferences": "Find File References", - "configuration.suggest.includeCompletionsWithClassMemberSnippets": "Enable/disable snippet completions for class members. Requires using TypeScript 4.5+ in the workspace" + "configuration.suggest.classMemberSnippets.enabled": "Enable/disable snippet completions for class members. Requires using TypeScript 4.5+ in the workspace" } diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 369136f378b..c79e44c63eb 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -186,7 +186,7 @@ export default class FileConfigurationManager extends Disposable { generateReturnInDocTemplate: config.get('suggest.jsdoc.generateReturns', true), includeCompletionsForImportStatements: config.get('suggest.includeCompletionsForImportStatements', true), includeCompletionsWithSnippetText: config.get('suggest.includeCompletionsWithSnippetText', true), - includeCompletionsWithClassMemberSnippets: config.get('suggest.includeCompletionsWithClassMemberSnippets', true), + includeCompletionsWithClassMemberSnippets: config.get('suggest.classMemberSnippets.enabled', true), allowIncompleteCompletions: true, displayPartsForJSDoc: true, ...getInlayHintsPreferences(config), From bbc10afedac1f9ea35d510211a5ec59296573cd5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 19 Nov 2021 16:00:50 -0800 Subject: [PATCH 282/330] Improving ux for search in webviews on web --- src/vs/workbench/contrib/webview/browser/pre/main.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 62f557f75ec..16cc137a10b 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -994,13 +994,18 @@ onDomReady(() => { if (!target) { return; } + + // Reset selection so we start search after current find result + const selection = target.contentWindow.getSelection(); + selection.collapse(selection.anchorNode); + const didFind = (/** @type {any} */ (target.contentWindow)).find( data.value, - false, + /* caseSensitive*/ false, /* backwards*/ data.previous, /* wrapAround*/ true, - false, - /*aSearchInFrames*/ true, + /* wholeWord */ false, + /* searchInFrames*/ false, false); hostMessaging.postMessage('did-find', didFind); }); From 4c83c132c3b95238f94d811ac8861f7a3f099ff6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 19 Nov 2021 16:11:51 -0800 Subject: [PATCH 283/330] Reduce timeout For #135075 --- .../contrib/parameterHints/test/parameterHintsModel.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index f935d89342f..40b8e94a437 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -104,7 +104,7 @@ suite('ParameterHintsModel', () => { assert.strictEqual(context.activeSignatureHelp, undefined); // Retrigger - setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 50); + setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 0); } else { assert.strictEqual(invokeCount, 2); assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); From 03f1fc5cfd8454df55aeec4a4202d1992eed97fa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 19 Nov 2021 16:13:20 -0800 Subject: [PATCH 284/330] Spelling --- .../editor/contrib/codeAction/test/codeActionModel.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index 2d925dcb22a..6d5dacc5a78 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -51,7 +51,7 @@ suite('CodeActionModel', () => { markerService.dispose(); }); - test('Orcale -> marker added', done => { + test('Oracle -> marker added', done => { const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); disposables.add(reg); @@ -81,7 +81,7 @@ suite('CodeActionModel', () => { }); - test('Orcale -> position changed', () => { + test('Oracle -> position changed', () => { const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); disposables.add(reg); @@ -155,7 +155,7 @@ suite('CodeActionModel', () => { }); }); - test('Orcale -> should only auto trigger once for cursor and marker update right after each other', done => { + test('Oracle -> should only auto trigger once for cursor and marker update right after each other', done => { const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); disposables.add(reg); From edfee5c9457257a46a7166965675251545c085fa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 19 Nov 2021 16:14:31 -0800 Subject: [PATCH 285/330] Reduce delay in test For #135075 --- src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index 6d5dacc5a78..c066026042e 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -30,7 +30,7 @@ const testProvider = { suite('CodeActionModel', () => { const languageId = 'foo-lang'; - let uri = URI.parse('untitled:path'); + const uri = URI.parse('untitled:path'); let model: TextModel; let markerService: MarkerService; let editor: ICodeEditor; @@ -173,7 +173,7 @@ suite('CodeActionModel', () => { model.dispose(); assert.strictEqual(triggerCount, 1); done(); - }, 50); + }, 0); }, 5 /*delay*/)); markerService.changeOne('fake', uri, [{ From aa810777e30a7031a4b5e352803e78e7a8153e0e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 19 Nov 2021 16:23:05 -0800 Subject: [PATCH 286/330] Speed up tests with take timers For #135075 --- .../codeAction/test/codeActionModel.test.ts | 220 ++++++++++-------- 1 file changed, 120 insertions(+), 100 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index c066026042e..2fb77836c15 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; @@ -27,6 +28,7 @@ const testProvider = { }; } }; + suite('CodeActionModel', () => { const languageId = 'foo-lang'; @@ -51,51 +53,15 @@ suite('CodeActionModel', () => { markerService.dispose(); }); - test('Oracle -> marker added', done => { - const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); - disposables.add(reg); + test('Oracle -> marker added', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { + done = resolve; + }); + await runWithFakedTimers({ useFakeTimers: true }, () => { + const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); + disposables.add(reg); - const contextKeys = new MockContextKeyService(); - const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { - assertType(e.type === CodeActionsState.Type.Triggered); - - assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); - assert.ok(e.actions); - - e.actions.then(fixes => { - model.dispose(); - assert.strictEqual(fixes.validActions.length, 1); - done(); - }, done); - })); - - // start here - markerService.changeOne('fake', uri, [{ - startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, - message: 'error', - severity: 1, - code: '', - source: '' - }]); - - }); - - test('Oracle -> position changed', () => { - const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); - disposables.add(reg); - - markerService.changeOne('fake', uri, [{ - startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, - message: 'error', - severity: 1, - code: '', - source: '' - }]); - - editor.setPosition({ lineNumber: 2, column: 1 }); - - return new Promise((resolve, reject) => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { @@ -103,18 +69,62 @@ suite('CodeActionModel', () => { assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); assert.ok(e.actions); + e.actions.then(fixes => { model.dispose(); assert.strictEqual(fixes.validActions.length, 1); - resolve(undefined); - }, reject); + done(); + }, done); })); + // start here - editor.setPosition({ lineNumber: 1, column: 1 }); + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + return donePromise; }); }); - test('Lightbulb is in the wrong place, #29933', async function () { + test('Oracle -> position changed', async () => { + await runWithFakedTimers({ useFakeTimers: true }, () => { + const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); + disposables.add(reg); + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + editor.setPosition({ lineNumber: 2, column: 1 }); + + return new Promise((resolve, reject) => { + const contextKeys = new MockContextKeyService(); + const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); + assert.ok(e.actions); + e.actions.then(fixes => { + model.dispose(); + assert.strictEqual(fixes.validActions.length, 1); + resolve(undefined); + }, reject); + })); + // start here + editor.setPosition({ lineNumber: 1, column: 1 }); + }); + }); + }); + + test('Lightbulb is in the wrong place, #29933', async () => { const reg = modes.CodeActionProviderRegistry.register(languageId, { provideCodeActions(_doc, _range): modes.CodeActionList { return { actions: [], dispose() { /* noop*/ } }; @@ -122,68 +132,78 @@ suite('CodeActionModel', () => { }); disposables.add(reg); - editor.getModel()!.setValue('// @ts-check\n2\ncon\n'); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + editor.getModel()!.setValue('// @ts-check\n2\ncon\n'); - markerService.changeOne('fake', uri, [{ - startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 4, - message: 'error', - severity: 1, - code: '', - source: '' - }]); + markerService.changeOne('fake', uri, [{ + startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 4, + message: 'error', + severity: 1, + code: '', + source: '' + }]); - // case 1 - drag selection over multiple lines -> range of enclosed marker, position or marker - await new Promise(resolve => { + // case 1 - drag selection over multiple lines -> range of enclosed marker, position or marker + await new Promise(resolve => { + const contextKeys = new MockContextKeyService(); + const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); + const selection = e.rangeOrSelection; + assert.strictEqual(selection.selectionStartLineNumber, 1); + assert.strictEqual(selection.selectionStartColumn, 1); + assert.strictEqual(selection.endLineNumber, 4); + assert.strictEqual(selection.endColumn, 1); + assert.strictEqual(e.position.lineNumber, 3); + assert.strictEqual(e.position.column, 1); + model.dispose(); + resolve(undefined); + }, 5)); + + editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }); + }); + }); + }); + + test('Oracle -> should only auto trigger once for cursor and marker update right after each other', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { + done = resolve; + }); + await runWithFakedTimers({ useFakeTimers: true }, () => { + const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); + disposables.add(reg); + + let triggerCount = 0; const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { assertType(e.type === CodeActionsState.Type.Triggered); assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); - const selection = e.rangeOrSelection; - assert.strictEqual(selection.selectionStartLineNumber, 1); - assert.strictEqual(selection.selectionStartColumn, 1); - assert.strictEqual(selection.endLineNumber, 4); - assert.strictEqual(selection.endColumn, 1); - assert.strictEqual(e.position.lineNumber, 3); - assert.strictEqual(e.position.column, 1); - model.dispose(); - resolve(undefined); - }, 5)); + ++triggerCount; + + // give time for second trigger before completing test + setTimeout(() => { + model.dispose(); + assert.strictEqual(triggerCount, 1); + done(); + }, 0); + }, 5 /*delay*/)); + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }); + + return donePromise; }); }); - - test('Oracle -> should only auto trigger once for cursor and marker update right after each other', done => { - const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); - disposables.add(reg); - - let triggerCount = 0; - const contextKeys = new MockContextKeyService(); - const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { - assertType(e.type === CodeActionsState.Type.Triggered); - - assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); - ++triggerCount; - - // give time for second trigger before completing test - setTimeout(() => { - model.dispose(); - assert.strictEqual(triggerCount, 1); - done(); - }, 0); - }, 5 /*delay*/)); - - markerService.changeOne('fake', uri, [{ - startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, - message: 'error', - severity: 1, - code: '', - source: '' - }]); - - editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }); - }); }); From 6fc33324aff616a2be2b5b2658ca4100927538b0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 19 Nov 2021 16:39:19 -0800 Subject: [PATCH 287/330] Use `runWithFakedTimers` in more tests For #135075 --- .../codeAction/test/codeActionModel.test.ts | 5 +- .../test/parameterHintsModel.test.ts | 148 ++++++++++++------ 2 files changed, 104 insertions(+), 49 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index 2fb77836c15..b4dd20023c7 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -169,9 +169,8 @@ suite('CodeActionModel', () => { test('Oracle -> should only auto trigger once for cursor and marker update right after each other', async () => { let done: () => void; - const donePromise = new Promise(resolve => { - done = resolve; - }); + const donePromise = new Promise(resolve => { done = resolve; }); + await runWithFakedTimers({ useFakeTimers: true }, () => { const reg = modes.CodeActionProviderRegistry.register(languageId, testProvider); disposables.add(reg); diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 40b8e94a437..99a1b20d6f1 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { Position } from 'vs/editor/common/core/position'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -62,7 +63,10 @@ suite('ParameterHintsModel', () => { return editor; } - test('Provider should get trigger character on type', (done) => { + test('Provider should get trigger character on type', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const triggerChar = '('; const editor = createMockEditor(''); @@ -80,10 +84,16 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + await donePromise; + }); }); - test('Provider should be retriggered if already active', (done) => { + test('Provider should be retriggered if already active', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const triggerChar = '('; const editor = createMockEditor(''); @@ -122,10 +132,16 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + await donePromise; + }); }); - test('Provider should not be retriggered if previous help is canceled first', (done) => { + test('Provider should not be retriggered if previous help is canceled first', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const triggerChar = '('; const editor = createMockEditor(''); @@ -165,10 +181,16 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + await runWithFakedTimers({ useFakeTimers: true }, () => { + editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + return donePromise; + }); }); - test('Provider should get last trigger character when triggered multiple times and only be invoked once', (done) => { + test('Provider should get last trigger character when triggered multiple times and only be invoked once', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const editor = createMockEditor(''); disposables.add(new ParameterHintsModel(editor, 5)); @@ -199,16 +221,24 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: 'a' }); - editor.trigger('keyboard', Handler.Type, { text: 'b' }); - editor.trigger('keyboard', Handler.Type, { text: 'c' }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + editor.trigger('keyboard', Handler.Type, { text: 'a' }); + editor.trigger('keyboard', Handler.Type, { text: 'b' }); + editor.trigger('keyboard', Handler.Type, { text: 'c' }); + + await donePromise; + }); }); - test('Provider should be retriggered if already active', (done) => { + test('Provider should be retriggered if already active', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const editor = createMockEditor(''); disposables.add(new ParameterHintsModel(editor, 5)); let invokeCount = 0; + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = ['a', 'b']; signatureHelpRetriggerCharacters = []; @@ -239,10 +269,14 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: 'a' }); + await runWithFakedTimers({ useFakeTimers: true }, () => { + editor.trigger('keyboard', Handler.Type, { text: 'a' }); + return donePromise; + }); }); - test('Should cancel existing request when new request comes in', () => { + test('Should cancel existing request when new request comes in', async () => { + const editor = createMockEditor('abc def'); const hintsModel = new ParameterHintsModel(editor); @@ -287,22 +321,28 @@ suite('ParameterHintsModel', () => { disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, longRunningProvider)); - hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0); - assert.strictEqual(-1, didRequestCancellationOf); + await runWithFakedTimers({ useFakeTimers: true }, async () => { - return new Promise((resolve, reject) => - hintsModel.onChangedHints(newParamterHints => { - try { - assert.strictEqual(0, didRequestCancellationOf); - assert.strictEqual('1', newParamterHints!.signatures[0].label); - resolve(); - } catch (e) { - reject(e); - } - })); + hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0); + assert.strictEqual(-1, didRequestCancellationOf); + + return new Promise((resolve, reject) => + hintsModel.onChangedHints(newParamterHints => { + try { + assert.strictEqual(0, didRequestCancellationOf); + assert.strictEqual('1', newParamterHints!.signatures[0].label); + resolve(); + } catch (e) { + reject(e); + } + })); + }); }); - test('Provider should be retriggered by retrigger character', (done) => { + test('Provider should be retriggered by retrigger character', async () => { + let done: () => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const triggerChar = 'a'; const retriggerChar = 'b'; @@ -340,11 +380,15 @@ suite('ParameterHintsModel', () => { } })); - // This should not trigger anything - editor.trigger('keyboard', Handler.Type, { text: retriggerChar }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + // This should not trigger anything + editor.trigger('keyboard', Handler.Type, { text: retriggerChar }); - // But a trigger character should - editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + // But a trigger character should + editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + + return donePromise; + }); }); test('should use first result from multiple providers', async () => { @@ -413,17 +457,19 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: triggerChar }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + editor.trigger('keyboard', Handler.Type, { text: triggerChar }); - const firstHint = (await getNextHint(model))!.value; - assert.strictEqual(firstHint.signatures[0].label, firstProviderId); - assert.strictEqual(firstHint.activeSignature, 0); - assert.strictEqual(firstHint.signatures[0].parameters[0].label, paramterLabel); + const firstHint = (await getNextHint(model))!.value; + assert.strictEqual(firstHint.signatures[0].label, firstProviderId); + assert.strictEqual(firstHint.activeSignature, 0); + assert.strictEqual(firstHint.signatures[0].parameters[0].label, paramterLabel); - const secondHint = (await getNextHint(model))!.value; - assert.strictEqual(secondHint.signatures[0].label, secondProviderId); - assert.strictEqual(secondHint.activeSignature, 1); - assert.strictEqual(secondHint.signatures[0].parameters[0].label, paramterLabel); + const secondHint = (await getNextHint(model))!.value; + assert.strictEqual(secondHint.signatures[0].label, secondProviderId); + assert.strictEqual(secondHint.activeSignature, 1); + assert.strictEqual(secondHint.signatures[0].parameters[0].label, paramterLabel); + }); }); test('Quick typing should use the first trigger character', async () => { @@ -457,13 +503,18 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: triggerCharacter }); - editor.trigger('keyboard', Handler.Type, { text: 'x' }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { + editor.trigger('keyboard', Handler.Type, { text: triggerCharacter }); + editor.trigger('keyboard', Handler.Type, { text: 'x' }); - await getNextHint(model); + await getNextHint(model); + }); }); - test('Retrigger while a pending resolve is still going on should preserve last active signature #96702', (done) => { + test('Retrigger while a pending resolve is still going on should preserve last active signature #96702', async () => { + let done: (r?: any) => void; + const donePromise = new Promise(resolve => { done = resolve; }); + const editor = createMockEditor(''); const model = new ParameterHintsModel(editor, 50); disposables.add(model); @@ -505,10 +556,15 @@ suite('ParameterHintsModel', () => { } })); - editor.trigger('keyboard', Handler.Type, { text: triggerCharacter }); + await runWithFakedTimers({ useFakeTimers: true }, async () => { - getNextHint(model) - .then(() => getNextHint(model)); + editor.trigger('keyboard', Handler.Type, { text: triggerCharacter }); + + await getNextHint(model); + await getNextHint(model); + + await donePromise; + }); }); }); From a36f0976ba0b884b0095bc71bda31f0f47c2abcd Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Fri, 19 Nov 2021 17:29:32 -0800 Subject: [PATCH 288/330] Fix jupyter issue 8316 - Jupyter renderer extension not used for IW --- .../contrib/notebook/browser/notebookServiceImpl.ts | 3 ++- src/vs/workbench/contrib/notebook/common/notebookCommon.ts | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 6f309a54847..ff39f09416d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -320,6 +320,7 @@ export class NotebookOutputRendererInfoStore { const preferred = notebookProviderInfo && this.preferredMimetype.getValue()[notebookProviderInfo.id]?.[mimeType]; const notebookExtId = notebookProviderInfo?.extension?.value; + const notebookId = notebookProviderInfo?.id; const renderers: { ordered: IOrderedMimeType, score: number }[] = Array.from(this.contributedRenderers.values()) .map(renderer => { const ownScore = kernelProvides === undefined @@ -333,7 +334,7 @@ export class NotebookOutputRendererInfoStore { const rendererExtId = renderer.extensionId.value; const reuseScore = preferred === renderer.id ? ReuseOrder.PreviouslySelected - : rendererExtId === notebookExtId || RENDERER_EQUIVALENT_EXTENSIONS.get(rendererExtId)?.has(notebookExtId!) + : rendererExtId === notebookExtId || RENDERER_EQUIVALENT_EXTENSIONS.get(rendererExtId)?.has(notebookId!) ? ReuseOrder.SameExtensionAsNotebook : renderer.isBuiltin ? ReuseOrder.BuiltIn : ReuseOrder.OtherRenderer; return { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 4f792558b17..63dbfdff771 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -57,14 +57,14 @@ export const ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER: readonly string[] = [ ]; /** - * A mapping of extension IDs who contain renderers, to extensions who they + * A mapping of extension IDs who contain renderers, to notebook ids who they * should be treated as the same in the renderer selection logic. This is used * to prefer the 1st party Jupyter renderers even though they're in a separate * extension, for instance. See #136247. */ export const RENDERER_EQUIVALENT_EXTENSIONS: ReadonlyMap> = new Map([ - ['ms-toolsai.jupyter', new Set(['vscode.ipynb'])], - ['ms-toolsai.jupyter-renderers', new Set(['vscode.ipynb'])], + ['ms-toolsai.jupyter', new Set(['jupyter-notebook', 'interactive'])], + ['ms-toolsai.jupyter-renderers', new Set(['jupyter-notebook', 'interactive'])], ]); export const BUILTIN_RENDERER_ID = '_builtin'; From f57aaeb56c63c9fe5910a4883bf7337b4f1261e7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 19 Nov 2021 15:26:19 -0800 Subject: [PATCH 289/330] progress bar listener. --- .../browser/view/cellParts/cellProgressBar.ts | 27 ++++++++++--------- .../browser/view/renderers/cellRenderer.ts | 3 ++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts index 547478ef98d..f9e3c1e1f98 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts @@ -6,7 +6,6 @@ import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ICellViewModel, CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; -import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class CellProgressBar extends CellPart { @@ -34,24 +33,26 @@ export class CellProgressBar extends CellPart { } updateState(element: ICellViewModel, e: CellViewModelStateChangeEvent): void { - if (!e.inputCollapsedChanged) { - return; + if (e.metadataChanged || e.internalMetadataChanged) { + this.updateForInternalMetadata(element, element.internalMetadata); } - if (element.isInputCollapsed) { - this._progressBar.hide(); - if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { - showProgressBar(this._collapsedProgressBar); - } - } else { - this._collapsedProgressBar.hide(); - if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { - showProgressBar(this._progressBar); + if (e.inputCollapsedChanged) { + if (element.isInputCollapsed) { + this._progressBar.hide(); + if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { + showProgressBar(this._collapsedProgressBar); + } + } else { + this._collapsedProgressBar.hide(); + if (element.internalMetadata.runState === NotebookCellExecutionState.Executing) { + showProgressBar(this._progressBar); + } } } } - updateForInternalMetadata(element: CodeCellViewModel, internalMetadata: NotebookCellInternalMetadata): void { + updateForInternalMetadata(element: ICellViewModel, internalMetadata: NotebookCellInternalMetadata): void { const progressBar = element.isInputCollapsed ? this._collapsedProgressBar : this._progressBar; if (internalMetadata.runState === NotebookCellExecutionState.Executing && !internalMetadata.isPaused) { showProgressBar(progressBar); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 149e15231ee..fb458506ef1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -598,7 +598,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const internalMetadata = element.internalMetadata; this.updateExecutionOrder(internalMetadata, templateData); - templateData.progressBar.updateForInternalMetadata(element, internalMetadata); } private updateForKernel(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { @@ -680,6 +679,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende })); this.updateForInternalMetadata(element, templateData); + templateData.progressBar.updateForInternalMetadata(element, element.internalMetadata); + elementDisposables.add(element.onDidChangeState((e) => { if (e.metadataChanged || e.internalMetadataChanged) { this.updateForInternalMetadata(element, templateData); From 36adb23bca5715c600bc4777a14d2f2c13081998 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 19 Nov 2021 18:16:53 -0800 Subject: [PATCH 290/330] CellPart#renderCell. --- .../view/cellParts/cellEditorOptions.ts | 5 +++ .../view/cellParts/cellFocusIndicator.ts | 4 ++ .../browser/view/cellParts/cellOutput.ts | 4 ++ .../browser/view/cellParts/cellPart.ts | 6 +++ .../browser/view/cellParts/cellProgressBar.ts | 4 ++ .../browser/view/cellParts/cellToolbars.ts | 38 +++++++++++++++++-- .../browser/view/cellParts/cellWidgets.ts | 16 +++++++- .../browser/view/cellParts/codeCell.ts | 4 ++ .../view/cellParts/codeCellRunToolbar.ts | 36 ++++++++++++++---- .../browser/view/notebookRenderingCommon.ts | 10 +---- .../browser/view/renderers/cellRenderer.ts | 22 +++-------- 11 files changed, 113 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts index bebd40f700f..9117c436853 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts @@ -86,6 +86,11 @@ export class CellEditorOptions extends CellPart { this._value = this._computeEditorOptions(); } + + renderCell(element: ICellViewModel): void { + // no op + } + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts index 5ccbd0fd7bc..4c54c4ab158 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts @@ -21,6 +21,10 @@ export class CellFocusIndicator extends CellPart { super(); } + renderCell(element: ICellViewModel): void { + // no op + } + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index b4538e6af71..2590f1251b1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -583,6 +583,10 @@ export class CellOutputContainer extends CellPart { })); } + renderCell(element: ICellViewModel): void { + // no op + } + updateLayoutNow(viewCell: CodeCellViewModel) { this._outputEntries.forEach(entry => { const index = this.viewCell.outputsViewModels.indexOf(entry.model); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts index 9fe2c4f7264..6a2ca9bc9b8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellPart.ts @@ -5,12 +5,18 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { CellViewModelStateChangeEvent, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; export abstract class CellPart extends Disposable { constructor() { super(); } + /** + * Update the DOM for the cell `element` + */ + abstract renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void; + /** * Perform DOM read operations to prepare for the list/cell layout update. */ diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts index f9e3c1e1f98..fb061f811d2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar.ts @@ -24,6 +24,10 @@ export class CellProgressBar extends CellPart { this._collapsedProgressBar.hide(); } + renderCell(element: ICellViewModel): void { + this.updateForInternalMetadata(element, element.internalMetadata); + } + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 9e04e2b73ba..55228eb4a14 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -8,7 +8,8 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction } from 'vs/base/common/actions'; import { disposableTimeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshalling'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -21,6 +22,7 @@ import { DeleteCellAction } from 'vs/workbench/contrib/notebook/browser/controll import { CellViewModelStateChangeEvent, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; +import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; export class BetweenCellToolbar extends CellPart { private _betweenCellToolbar!: ToolBar; @@ -69,6 +71,16 @@ export class BetweenCellToolbar extends CellPart { this._betweenCellToolbar.context = context; } + renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + this._betweenCellToolbar.context = { + ui: true, + cell: element, + cellTemplate: templateData, + notebookEditor: this._notebookEditor, + $mid: MarshalledId.NotebookCellActionContext + }; + } + prepareLayout(): void { // nothing to read } @@ -89,7 +101,7 @@ export interface ICssClassDelegate { toggle: (className: string, force?: boolean) => void; } -export class CellTitleToolbarPart extends Disposable { +export class CellTitleToolbarPart extends CellPart { private _toolbar: ToolBar; private _deleteToolbar: ToolBar; private _titleMenu: IMenu; @@ -125,7 +137,27 @@ export class CellTitleToolbarPart extends Disposable { this.setupChangeListeners(); } - updateContext(toolbarContext: INotebookCellActionContext) { + renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + this.updateContext({ + ui: true, + cell: element, + cellTemplate: templateData, + notebookEditor: this._notebookEditor, + $mid: MarshalledId.NotebookCellActionContext + }); + } + + prepareLayout(): void { + // nothing to read + } + updateLayoutNow(element: ICellViewModel): void { + // no op + } + updateState(element: ICellViewModel, e: CellViewModelStateChangeEvent): void { + // no op + } + + private updateContext(toolbarContext: INotebookCellActionContext) { this._toolbar.context = toolbarContext; this._deleteToolbar.context = toolbarContext; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts index 3a7ea729458..a048e62a486 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets.ts @@ -12,6 +12,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { stripIcons } from 'vs/base/common/iconLabels'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshalling'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { IDimension, isThemeColor } from 'vs/editor/common/editorCommon'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -20,8 +21,9 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService'; import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { CellViewModelStateChangeEvent, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellViewModelStateChangeEvent, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; +import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CellStatusbarAlignment, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const $ = DOM.$; @@ -53,6 +55,7 @@ export class CellEditorStatusBar extends CellPart { readonly onDidClick: Event = this._onDidClick.event; constructor( + private readonly _notebookEditor: INotebookEditorDelegate, container: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IThemeService private readonly _themeService: IThemeService, @@ -93,6 +96,17 @@ export class CellEditorStatusBar extends CellPart { })); } + + renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + this.updateContext({ + ui: true, + cell: element, + cellTemplate: templateData, + notebookEditor: this._notebookEditor, + $mid: MarshalledId.NotebookCellActionContext + }); + } + prepareLayout(): void { // nothing to read } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index ef8c0ae4836..0dca46ec29e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -87,6 +87,10 @@ export class CodeCell extends Disposable { } })); + this.cellParts.forEach(cellPart => { + cellPart.renderCell(this.viewCell, this.templateData); + }); + this.updateEditorOptions(); this.updateEditorForFocusModeChange(); this.updateForOutputHover(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index 37f35387a72..53b22067c70 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -5,7 +5,8 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { Action, IAction } from 'vs/base/common/actions'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshalling'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; @@ -17,10 +18,11 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { IRunToolbar } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { CellViewModelStateChangeEvent, ICellViewModel, INotebookEditorDelegate, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; +import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; -export class RunToolbar extends Disposable implements IRunToolbar { +export class RunToolbar extends CellPart { toolbar!: ToolBar; constructor( @@ -47,6 +49,28 @@ export class RunToolbar extends Disposable implements IRunToolbar { this._register(this.notebookEditor.notebookOptions.onDidChangeOptions(updateActions)); } + renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + this.toolbar.context = { + ui: true, + cell: element, + cellTemplate: templateData, + notebookEditor: this.notebookEditor, + $mid: MarshalledId.NotebookCellActionContext + }; + } + + prepareLayout(): void { + // no op + } + + updateLayoutNow(element: ICellViewModel): void { + // no op + } + + updateState(element: ICellViewModel, e: CellViewModelStateChangeEvent): void { + // no op + } + getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[]; } { const primary: IAction[] = []; const secondary: IAction[] = []; @@ -98,10 +122,6 @@ export class RunToolbar extends Disposable implements IRunToolbar { renderDropdownAsChildElement: true })); } - - updateContext(context: INotebookCellActionContext) { - this.toolbar.context = context; - } } export function getCodeCellExecutionContextKeyService(contextKeyService: IContextKeyService): IContextKeyService { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 1c1e26d2601..9231bcfc62c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -7,7 +7,6 @@ import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; @@ -16,12 +15,12 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import type { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellFocusIndicator } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator'; import { CellProgressBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar'; import { BetweenCellToolbar, CellTitleToolbarPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; +import { RunToolbar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -116,13 +115,8 @@ export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate { currentEditor?: ICodeEditor; } -export interface IRunToolbar { - toolbar: ToolBar; - updateContext(context: INotebookCellActionContext): void; -} - export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { - runToolbar: IRunToolbar; + runToolbar: RunToolbar; executionOrderLabel: HTMLElement; outputContainer: FastDomNode; cellOutputCollapsedContainer: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index fb458506ef1..b76734f9eef 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -30,7 +30,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { INotebookCellActionContext, INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; import { CellDecorations } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations'; @@ -178,7 +178,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.notebookEditor)); const betweenCellToolbar = templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, titleToolbarContainer, bottomCellContainer)); const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); - const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); + const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, this.notebookEditor, editorPart)); const templateData: MarkdownCellRenderTemplate = { rootContainer, @@ -434,7 +434,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const progressBar = templateDisposables.add(new CellProgressBar(editorPart, cellInputCollapsedContainer)); - const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); + const statusBar = templateDisposables.add(this.instantiationService.createInstance(CellEditorStatusBar, this.notebookEditor, editorPart)); const outputContainer = new FastDomNode(DOM.append(container, $('.output'))); const cellOutputCollapsedContainer = DOM.append(outputContainer.domNode, $('.output-collapse-container')); @@ -665,8 +665,11 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateData.betweenCellToolbar, templateData.statusBar, templateData.progressBar, + templateData.titleToolbar, + templateData.runToolbar, cellEditorOptions ])); + this.renderedEditors.set(element, templateData.editor); elementDisposables.add(cellEditorOptions.onDidChange(() => templateData.editor.updateOptions(cellEditorOptions.getUpdatedValue(element.internalMetadata)))); @@ -679,7 +682,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende })); this.updateForInternalMetadata(element, templateData); - templateData.progressBar.updateForInternalMetadata(element, element.internalMetadata); elementDisposables.add(element.onDidChangeState((e) => { if (e.metadataChanged || e.internalMetadataChanged) { @@ -689,18 +691,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateForKernel(element, templateData); - const toolbarContext = { - ui: true, - cell: element, - cellTemplate: templateData, - notebookEditor: this.notebookEditor, - $mid: MarshalledId.NotebookCellActionContext - }; - templateData.betweenCellToolbar.updateContext(toolbarContext); - templateData.titleToolbar.updateContext(toolbarContext); - templateData.runToolbar.updateContext(toolbarContext); - templateData.statusBar.updateContext(toolbarContext); - templateData.elementDisposables.add(templateData.titleToolbar.onDidUpdateActions(() => { // Don't call directly here - is initially called by updateForLayout in the next frame this.updateForTitleMenu(templateData); From 165611d073e49841c281f246260381f8ac1b2f54 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 19 Nov 2021 20:31:58 -0800 Subject: [PATCH 291/330] CellParts for markdown. --- .../browser/view/cellParts/markdownCell.ts | 12 ++++++++++++ .../browser/view/renderers/cellRenderer.ts | 17 ++++------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts index 2879bfce310..f1bf7ef082b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts @@ -27,6 +27,7 @@ import { MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browse import { IModeService } from 'vs/editor/common/services/modeService'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; export class StatefulMarkdownCell extends Disposable { @@ -47,6 +48,7 @@ export class StatefulMarkdownCell extends Disposable { private readonly notebookEditor: IActiveNotebookEditorDelegate, private readonly viewCell: MarkupCellViewModel, private readonly templateData: MarkdownCellRenderTemplate, + private readonly cellParts: CellPart[], private readonly renderedEditors: Map, @IContextKeyService private readonly contextKeyService: IContextKeyService, @INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService, @@ -67,6 +69,10 @@ export class StatefulMarkdownCell extends Disposable { this.registerListeners(); // update for init state + this.cellParts.forEach(cellPart => { + cellPart.renderCell(this.viewCell, this.templateData); + }); + this.updateForHover(); this.updateForFocusModeChange(); this.foldingState = viewCell.foldingState; @@ -98,6 +104,12 @@ export class StatefulMarkdownCell extends Disposable { } private registerListeners() { + this._register(this.viewCell.onDidChangeState(e => { + this.cellParts.forEach(cellPart => { + cellPart.updateState(this.viewCell, e); + }); + })); + this._register(this.viewCell.model.onDidChangeMetadata(() => { this.viewUpdate(); })); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index b76734f9eef..4330548950d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -10,7 +10,6 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { combinedDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { MarshalledId } from 'vs/base/common/marshalling'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -30,7 +29,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { INotebookCellToolbarActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CodeCellLayoutInfo, EXPAND_CELL_OUTPUT_COMMAND_ID, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; import { CellDecorations } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations'; @@ -227,17 +225,10 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.updateForLayout(element, templateData); })); - const markdownCell = templateData.instantiationService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.renderedEditors); - elementDisposables.add(markdownCell); - - const toolbarContext = { - ui: true, - cell: element, - notebookEditor: this.notebookEditor, - $mid: MarshalledId.NotebookCellActionContext - }; - templateData.betweenCellToolbar.updateContext(toolbarContext); - templateData.statusBar.updateContext(toolbarContext); + elementDisposables.add(templateData.instantiationService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, [ + templateData.betweenCellToolbar, + templateData.statusBar + ], this.renderedEditors)); } private updateForLayout(element: MarkupCellViewModel, templateData: MarkdownCellRenderTemplate): void { From 27a61bd9852cb8b808af99f0acedd3b5d3b9afd5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 13:04:52 +0100 Subject: [PATCH 292/330] Small tweaks --- src/vs/server/remoteExtensionHostAgentServer.ts | 4 ++-- src/vs/server/serverEnvironmentService.ts | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/vs/server/remoteExtensionHostAgentServer.ts b/src/vs/server/remoteExtensionHostAgentServer.ts index b41ccdeb138..60f925fe191 100644 --- a/src/vs/server/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/remoteExtensionHostAgentServer.ts @@ -914,12 +914,12 @@ export class RemoteExtensionHostAgentServer extends Disposable { function parseConnectionToken(args: ServerParsedArgs): { connectionToken: string; connectionTokenIsMandatory: boolean; } { if (args['connectionToken']) { - console.warn(`The argument connectionToken is deprecated, please use connection-token instead`); + console.warn(`The argument '--connectionToken' is deprecated, please use '--connection-token' instead`); } if (args['connection-secret']) { if (args['connection-token']) { - console.warn(`Please do not use the argument connection-token at the same time as connection-secret.`); + console.warn(`Please do not use the argument '--connection-token' at the same time as '--connection-secret'.`); process.exit(1); } let rawConnectionToken = fs.readFileSync(args['connection-secret']).toString(); diff --git a/src/vs/server/serverEnvironmentService.ts b/src/vs/server/serverEnvironmentService.ts index c3992fba47f..a014d82f701 100644 --- a/src/vs/server/serverEnvironmentService.ts +++ b/src/vs/server/serverEnvironmentService.ts @@ -12,7 +12,8 @@ import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/envi export const serverOptions: OptionDescriptions = { 'port': { type: 'string' }, 'pick-port': { type: 'string' }, - 'connection-token': { type: 'string' }, + 'connectionToken': { type: 'string' }, // deprecated in favor of `--connection-token` + 'connection-token': { type: 'string', description: nls.localize('connection-token', "A secret that must be included by the web client with all requests.") }, 'connection-secret': { type: 'string', description: nls.localize('connection-secret', "Path to file that contains the connection token. This will require that all incoming connections know the secret.") }, 'host': { type: 'string' }, 'socket-path': { type: 'string' }, @@ -59,12 +60,22 @@ export const serverOptions: OptionDescriptions = { export interface ServerParsedArgs { port?: string; - 'connection-token'?: string; + 'pick-port'?: string; /** * @deprecated use `connection-token` instead */ connectionToken?: string; - 'pick-port'?: string; + /** + * A secret token that must be provided by the web client with all requests. + * Use only `[0-9A-Za-z\-]`. + * + * By default, a UUID will be generated every time the server starts up. + * + * If the server is running on a multi-user system, then consider + * using `--connection-secret` which has the advantage that the token cannot + * be seen by other users using `ps` or similar commands. + */ + 'connection-token'?: string; /** * A path to a filename which will be read on startup. * Consider placing this file in a folder readable only by the same user (a `chmod 0700` directory). From 6b1a058a882e3bad5e9b53992f794ddc78af1549 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 15:00:26 +0100 Subject: [PATCH 293/330] Improve running on `https` --- src/vs/platform/remote/browser/browserSocketFactory.ts | 3 ++- .../platform/remote/browser/remoteAuthorityResolverService.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts index 5197724fb1d..d9a5d9b26de 100644 --- a/src/vs/platform/remote/browser/browserSocketFactory.ts +++ b/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -240,7 +240,8 @@ export class BrowserSocketFactory implements ISocketFactory { } connect(host: string, port: number, query: string, callback: IConnectCallback): void { - const socket = this._webSocketFactory.create(`ws://${/:/.test(host) ? `[${host}]` : host}:${port}/?${query}&skipWebSocketFrames=false`); + const webSocketSchema = (/^https:/.test(window.location.href) ? 'wss' : 'ws'); + const socket = this._webSocketFactory.create(`${webSocketSchema}://${/:/.test(host) ? `[${host}]` : host}:${port}/?${query}&skipWebSocketFrames=false`); const errorListener = socket.onError((err) => callback(err, undefined)); socket.onOpen(() => { errorListener.dispose(); diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index 76bb55627fe..a50828715f2 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -63,7 +63,8 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot const pieces = authority.split(':'); return { authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10), connectionToken } }; } - return { authority: { authority, host: authority, port: 80, connectionToken } }; + const port = (/^https:/.test(window.location.href) ? 443 : 80); + return { authority: { authority, host: authority, port: port, connectionToken } }; } _clearResolvedAuthority(authority: string): void { From 1d8839096bf7341f7d0ac6568177445cb6236c0f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 15:55:37 +0100 Subject: [PATCH 294/330] Remove type hints --- src/vs/editor/browser/view/domLineBreaksComputer.ts | 3 --- src/vs/editor/browser/viewParts/lines/viewLine.ts | 4 +--- src/vs/editor/common/core/token.ts | 2 +- src/vs/editor/common/view/overviewZoneManager.ts | 6 +++--- .../editor/common/viewModel/monospaceLineBreaksComputer.ts | 5 +---- src/vs/editor/common/viewModel/prefixSumComputer.ts | 2 +- 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 68b9aab8b09..fbc84e86d86 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -25,9 +25,6 @@ export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory } public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { - tabSize = tabSize | 0; //@perf - wrappingColumn = +wrappingColumn; //@perf - let requests: string[] = []; let injectedTexts: (LineInjectedText[] | null)[] = []; return { diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index fcc405103f6..9148c986b73 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -331,13 +331,11 @@ export class ViewLine implements IVisibleLine { if (!this._renderedViewLine) { return null; } - startColumn = startColumn | 0; // @perf - endColumn = endColumn | 0; // @perf startColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, startColumn)); endColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, endColumn)); - const stopRenderingLineAfter = this._renderedViewLine.input.stopRenderingLineAfter | 0; // @perf + const stopRenderingLineAfter = this._renderedViewLine.input.stopRenderingLineAfter; let outsideRenderedLine = false; if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1 && endColumn > stopRenderingLineAfter + 1) { diff --git a/src/vs/editor/common/core/token.ts b/src/vs/editor/common/core/token.ts index 9d7f3560197..083d7c29d15 100644 --- a/src/vs/editor/common/core/token.ts +++ b/src/vs/editor/common/core/token.ts @@ -13,7 +13,7 @@ export class Token { public readonly language: string; constructor(offset: number, type: string, language: string) { - this.offset = offset | 0;// @perf + this.offset = offset; this.type = type; this.language = language; } diff --git a/src/vs/editor/common/view/overviewZoneManager.ts b/src/vs/editor/common/view/overviewZoneManager.ts index 2397cb12722..5952be0122d 100644 --- a/src/vs/editor/common/view/overviewZoneManager.ts +++ b/src/vs/editor/common/view/overviewZoneManager.ts @@ -175,9 +175,9 @@ export class OverviewZoneManager { public resolveColorZones(): ColorZone[] { const colorZonesInvalid = this._colorZonesInvalid; - const lineHeight = Math.floor(this._lineHeight); // @perf - const totalHeight = Math.floor(this.getCanvasHeight()); // @perf - const outerHeight = Math.floor(this._outerHeight); // @perf + const lineHeight = Math.floor(this._lineHeight); + const totalHeight = Math.floor(this.getCanvasHeight()); + const outerHeight = Math.floor(this._outerHeight); const heightRatio = totalHeight / outerHeight; const halfMinimumHeight = Math.floor(Constants.MINIMUM_HEIGHT * this._pixelRatio / 2); diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 4c1cd5c4ad9..3479b1739da 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -27,9 +27,6 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa } public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { - tabSize = tabSize | 0; //@perf - wrappingColumn = +wrappingColumn; //@perf - const requests: string[] = []; const injectedTexts: (LineInjectedText[] | null)[] = []; const previousBreakingData: (ModelLineProjectionData | null)[] = []; @@ -40,7 +37,7 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa previousBreakingData.push(previousLineBreakData); }, finalize: () => { - const columnsForFullWidthChar = fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth; //@perf + const columnsForFullWidthChar = fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth; let result: (ModelLineProjectionData | null)[] = []; for (let i = 0, len = requests.length; i < len; i++) { const injectedText = injectedTexts[i]; diff --git a/src/vs/editor/common/viewModel/prefixSumComputer.ts b/src/vs/editor/common/viewModel/prefixSumComputer.ts index 47ac2a2c65b..7c6eba5854e 100644 --- a/src/vs/editor/common/viewModel/prefixSumComputer.ts +++ b/src/vs/editor/common/viewModel/prefixSumComputer.ts @@ -151,7 +151,7 @@ export class PrefixSumComputer { } public getIndexOf(sum: number): PrefixSumIndexOfResult { - sum = Math.floor(sum); //@perf + sum = Math.floor(sum); // Compute all sums (to get a fully valid prefixSum) this.getTotalSum(); From 6f2ad1994cb7bf8b4288fc52e2d6ea72aecd9b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Sat, 20 Nov 2021 18:01:39 +0100 Subject: [PATCH 295/330] More RBAC usage (#137579) * ci: :construction_worker: remove secret references * inline web storage account * remove unused reference * inline storage accounts * formatting * formatting * drop ticino-storage-key, web-storage-key * remove leftovers * fix build * fix build * catch errors on all upload* scripts * bump gulp-azure-storage --- .../darwin/product-build-darwin.yml | 21 +- build/azure-pipelines/distro-build.yml | 2 +- build/azure-pipelines/exploration-build.yml | 2 +- .../linux/product-build-linux.yml | 2 +- build/azure-pipelines/product-build.yml | 4 - build/azure-pipelines/product-compile.yml | 18 +- build/azure-pipelines/product-publish.yml | 2 +- build/azure-pipelines/sdl-scan.yml | 348 +++++++++--------- build/azure-pipelines/upload-cdn.js | 45 ++- build/azure-pipelines/upload-cdn.ts | 47 ++- build/azure-pipelines/upload-configuration.js | 112 ++++++ build/azure-pipelines/upload-configuration.ts | 132 +++++++ build/azure-pipelines/upload-nlsmetadata.js | 147 ++++---- build/azure-pipelines/upload-nlsmetadata.ts | 165 +++++---- build/azure-pipelines/upload-sourcemaps.js | 33 +- build/azure-pipelines/upload-sourcemaps.ts | 38 +- .../azure-pipelines/web/product-build-web.yml | 31 +- .../win32/product-build-win32.yml | 2 +- build/gulpfile.vscode.js | 108 +----- package.json | 2 +- yarn.lock | 306 ++++++++------- 21 files changed, 928 insertions(+), 639 deletions(-) create mode 100644 build/azure-pipelines/upload-configuration.js create mode 100644 build/azure-pipelines/upload-configuration.ts diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 928bd234e0c..f309fc1dab8 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -8,7 +8,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key,ticino-storage-key' + SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" - task: DownloadPipelineArtifact@2 inputs: @@ -301,10 +301,25 @@ steps: displayName: Publish web server archive condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), ne(variables['VSCODE_PUBLISH'], 'false')) + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - script: | - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + set -e + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ VSCODE_ARCH="$(VSCODE_ARCH)" \ - yarn gulp upload-vscode-configuration + node build/azure-pipelines/upload-configuration displayName: Upload configuration (for Bing settings search) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), ne(variables['VSCODE_PUBLISH'], 'false')) continueOnError: true diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 6031405fe37..53b62b47a4e 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -18,7 +18,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'github-distro-mixin-password' + SecretsFilter: "github-distro-mixin-password" - script: | set -e diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index ca78aca17f4..5b6599d8dab 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -14,7 +14,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: 'github-distro-mixin-password' + SecretsFilter: "github-distro-mixin-password" - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index dc232cc0219..641be042f73 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -8,7 +8,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,builds-docdb-key-readwrite,vscode-storage-key,ESRP-PKI,esrp-aad-username,esrp-aad-password" + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - task: DownloadPipelineArtifact@2 inputs: diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index e87939db37e..c0b619cab09 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -113,10 +113,6 @@ variables: value: https://az764295.vo.msecnd.net - name: AZURE_DOCUMENTDB_ENDPOINT value: https://vscode.documents.azure.com:443/ - - name: AZURE_STORAGE_ACCOUNT - value: ticino - - name: AZURE_STORAGE_ACCOUNT_2 - value: vscode - name: MOONCAKE_CDN_URL value: https://vscode.cdn.azure.cn - name: VSCODE_MIXIN_REPO diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 709d8e39f49..88af1af2918 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -8,7 +8,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,ticino-storage-key" + SecretsFilter: "github-distro-mixin-password" - script: | set -e @@ -103,9 +103,23 @@ steps: displayName: Compile test suites condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - script: | set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ node build/azure-pipelines/upload-sourcemaps displayName: Upload sourcemaps condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 44516763081..c43180ea0a3 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -8,7 +8,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: "builds-docdb-key-readwrite,github-distro-mixin-password,ticino-storage-key,vscode-storage-key,vscode-mooncake-storage-key" + SecretsFilter: "github-distro-mixin-password" - pwsh: | . build/azure-pipelines/win32/exec.ps1 diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index b1976140bd0..d1cd72b3d95 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -32,201 +32,201 @@ variables: value: x64 stages: -- stage: Windows - condition: eq(variables.SCAN_WINDOWS, 'true') - pool: - vmImage: VS2017-Win2016 - jobs: - - job: WindowsJob - timeoutInMinutes: 0 - steps: - - task: CredScan@3 - continueOnError: true - inputs: - scanFolder: '$(Build.SourcesDirectory)' - outputFormat: 'pre' - - task: NodeTool@0 - inputs: - versionSpec: "14.x" + - stage: Windows + condition: eq(variables.SCAN_WINDOWS, 'true') + pool: + vmImage: VS2017-Win2016 + jobs: + - job: WindowsJob + timeoutInMinutes: 0 + steps: + - task: CredScan@3 + continueOnError: true + inputs: + scanFolder: "$(Build.SourcesDirectory)" + outputFormat: "pre" + - task: NodeTool@0 + inputs: + versionSpec: "14.x" - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password" + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password" - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - exec { git config user.email "vscode@microsoft.com" } - exec { git config user.name "VSCode" } - displayName: Prepare tooling + exec { git config user.email "vscode@microsoft.com" } + exec { git config user.name "VSCode" } + displayName: Prepare tooling - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") } - displayName: Merge distro + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") } + displayName: Merge distro - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { npx https://aka.ms/enablesecurefeed standAlone } - timeoutInMinutes: 5 - condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) - displayName: Switch to Terrapin packages + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { npx https://aka.ms/enablesecurefeed standAlone } + timeoutInMinutes: 5 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) + displayName: Switch to Terrapin packages - - task: Semmle@1 - inputs: - sourceCodeDirectory: '$(Build.SourcesDirectory)' - language: 'cpp' - buildCommandsString: 'yarn --frozen-lockfile' - querySuite: 'Required' - timeout: '1800' - ram: '16384' - addProjectDirToScanningExclusionList: true - env: - npm_config_arch: "$(NPM_ARCH)" - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: CodeQL + - task: Semmle@1 + inputs: + sourceCodeDirectory: "$(Build.SourcesDirectory)" + language: "cpp" + buildCommandsString: "yarn --frozen-lockfile" + querySuite: "Required" + timeout: "1800" + ram: "16384" + addProjectDirToScanningExclusionList: true + env: + npm_config_arch: "$(NPM_ARCH)" + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: CodeQL - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - . build/azure-pipelines/win32/retry.ps1 - $ErrorActionPreference = "Stop" - retry { exec { yarn --frozen-lockfile } } - env: - npm_config_arch: "$(NPM_ARCH)" - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - CHILD_CONCURRENCY: 1 - displayName: Install dependencies + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + . build/azure-pipelines/win32/retry.ps1 + $ErrorActionPreference = "Stop" + retry { exec { yarn --frozen-lockfile } } + env: + npm_config_arch: "$(NPM_ARCH)" + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + CHILD_CONCURRENCY: 1 + displayName: Install dependencies - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn gulp "vscode-symbols-win32-$(VSCODE_ARCH)" } - displayName: Download Symbols + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp "vscode-symbols-win32-$(VSCODE_ARCH)" } + displayName: Download Symbols - - task: BinSkim@4 - inputs: - InputType: 'Basic' - Function: 'analyze' - TargetPattern: 'guardianGlob' - AnalyzeTargetGlob: '$(agent.builddirectory)\scanbin\**.dll;$(agent.builddirectory)\scanbin\**.exe;$(agent.builddirectory)\scanbin\**.node' - AnalyzeLocalSymbolDirectories: '$(agent.builddirectory)\scanbin\VSCode-win32-$(VSCODE_ARCH)\pdb' + - task: BinSkim@4 + inputs: + InputType: "Basic" + Function: "analyze" + TargetPattern: "guardianGlob" + AnalyzeTargetGlob: '$(agent.builddirectory)\scanbin\**.dll;$(agent.builddirectory)\scanbin\**.exe;$(agent.builddirectory)\scanbin\**.node' + AnalyzeLocalSymbolDirectories: '$(agent.builddirectory)\scanbin\VSCode-win32-$(VSCODE_ARCH)\pdb' - - task: TSAUpload@2 - inputs: - GdnPublishTsaOnboard: true - GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)\build\azure-pipelines\.gdntsa' + - task: TSAUpload@2 + inputs: + GdnPublishTsaOnboard: true + GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)\build\azure-pipelines\.gdntsa' -- stage: Linux - dependsOn: [] - condition: eq(variables.SCAN_LINUX, 'true') - pool: - vmImage: "Ubuntu-18.04" - jobs: - - job: LinuxJob - steps: - - task: CredScan@2 - inputs: - toolMajorVersion: 'V2' - - task: NodeTool@0 - inputs: - versionSpec: "14.x" + - stage: Linux + dependsOn: [] + condition: eq(variables.SCAN_LINUX, 'true') + pool: + vmImage: "Ubuntu-18.04" + jobs: + - job: LinuxJob + steps: + - task: CredScan@2 + inputs: + toolMajorVersion: "V2" + - task: NodeTool@0 + inputs: + versionSpec: "14.x" - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password" + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password" - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - - script: | - set -e - npx https://aka.ms/enablesecurefeed standAlone - timeoutInMinutes: 5 - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) - displayName: Switch to Terrapin packages + - script: | + set -e + npx https://aka.ms/enablesecurefeed standAlone + timeoutInMinutes: 5 + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) + displayName: Switch to Terrapin packages - - script: | - set -e - yarn --cwd build - yarn --cwd build compile - displayName: Compile build tools + - script: | + set -e + yarn --cwd build + yarn --cwd build compile + displayName: Compile build tools - - script: | - set -e - export npm_config_arch=$(NPM_ARCH) + - script: | + set -e + export npm_config_arch=$(NPM_ARCH) - if [ -z "$CC" ] || [ -z "$CXX" ]; then - # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/91.0.4472.164/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux - # Download libcxx headers and objects from upstream electron releases - DEBUG=libcxx-fetcher \ - VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \ - VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \ - VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \ - VSCODE_ARCH="$(NPM_ARCH)" \ - node build/linux/libcxx-fetcher.js - # Set compiler toolchain - export CC=$PWD/.build/CR_Clang/bin/clang - export CXX=$PWD/.build/CR_Clang/bin/clang++ - export CXXFLAGS="-nostdinc++ -D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit" - export LDFLAGS="-stdlib=libc++ -fuse-ld=lld -flto=thin -fsplit-lto-unit -L$PWD/.build/libcxx-objects -lc++abi" - fi + if [ -z "$CC" ] || [ -z "$CXX" ]; then + # Download clang based on chromium revision used by vscode + curl -s https://raw.githubusercontent.com/chromium/chromium/91.0.4472.164/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + # Download libcxx headers and objects from upstream electron releases + DEBUG=libcxx-fetcher \ + VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \ + VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \ + VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \ + VSCODE_ARCH="$(NPM_ARCH)" \ + node build/linux/libcxx-fetcher.js + # Set compiler toolchain + export CC=$PWD/.build/CR_Clang/bin/clang + export CXX=$PWD/.build/CR_Clang/bin/clang++ + export CXXFLAGS="-nostdinc++ -D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit" + export LDFLAGS="-stdlib=libc++ -fuse-ld=lld -flto=thin -fsplit-lto-unit -L$PWD/.build/libcxx-objects -lc++abi" + fi - if [ "$VSCODE_ARCH" == "x64" ]; then - export VSCODE_REMOTE_CC=$(which gcc-4.8) - export VSCODE_REMOTE_CXX=$(which g++-4.8) - fi + if [ "$VSCODE_ARCH" == "x64" ]; then + export VSCODE_REMOTE_CC=$(which gcc-4.8) + export VSCODE_REMOTE_CXX=$(which g++-4.8) + fi - for i in {1..3}; do # try 3 times, for Terrapin - yarn --frozen-lockfile && break - if [ $i -eq 3 ]; then - echo "Yarn failed too many times" >&2 - exit 1 - fi - echo "Yarn failed $i, trying again..." - done - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Install dependencies + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies - - script: | - set -e - yarn gulp vscode-symbols-linux-$(VSCODE_ARCH) - displayName: Build + - script: | + set -e + yarn gulp vscode-symbols-linux-$(VSCODE_ARCH) + displayName: Build - - task: BinSkim@3 - inputs: - toolVersion: Latest - InputType: CommandLine - arguments: analyze $(agent.builddirectory)\scanbin\exe\*.* --recurse --local-symbol-directories $(agent.builddirectory)\scanbin\VSCode-linux-$(VSCODE_ARCH)\pdb + - task: BinSkim@3 + inputs: + toolVersion: Latest + InputType: CommandLine + arguments: analyze $(agent.builddirectory)\scanbin\exe\*.* --recurse --local-symbol-directories $(agent.builddirectory)\scanbin\VSCode-linux-$(VSCODE_ARCH)\pdb - - task: TSAUpload@2 - inputs: - GdnPublishTsaConfigFile: '$(Build.SourceDirectory)\build\azure-pipelines\.gdntsa' + - task: TSAUpload@2 + inputs: + GdnPublishTsaConfigFile: '$(Build.SourceDirectory)\build\azure-pipelines\.gdntsa' diff --git a/build/azure-pipelines/upload-cdn.js b/build/azure-pipelines/upload-cdn.js index 16a072905a0..fe3817c9183 100644 --- a/build/azure-pipelines/upload-cdn.js +++ b/build/azure-pipelines/upload-cdn.js @@ -10,26 +10,35 @@ const vfs = require("vinyl-fs"); const util = require("../lib/util"); const filter = require("gulp-filter"); const gzip = require("gulp-gzip"); +const identity_1 = require("@azure/identity"); const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); +const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); function main() { - return vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) - .pipe(filter(f => !f.isDirectory())) - .pipe(gzip({ append: false })) - .pipe(es.through(function (data) { - console.log('Uploading CDN file:', data.relative); // debug - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: process.env.VSCODE_QUALITY, - prefix: commit + '/', - contentSettings: { - contentEncoding: 'gzip', - cacheControl: 'max-age=31536000, public' - } - })); + return new Promise((c, e) => { + vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) + .pipe(filter(f => !f.isDirectory())) + .pipe(gzip({ append: false })) + .pipe(es.through(function (data) { + console.log('Uploading CDN file:', data.relative); // debug + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: process.env.VSCODE_QUALITY, + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' + } + })) + .on('end', () => c()) + .on('error', (err) => e(err)); + }); } -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/azure-pipelines/upload-cdn.ts b/build/azure-pipelines/upload-cdn.ts index 71589033867..c35582017d7 100644 --- a/build/azure-pipelines/upload-cdn.ts +++ b/build/azure-pipelines/upload-cdn.ts @@ -12,29 +12,38 @@ import * as vfs from 'vinyl-fs'; import * as util from '../lib/util'; import * as filter from 'gulp-filter'; import * as gzip from 'gulp-gzip'; +import { ClientSecretCredential } from '@azure/identity'; const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); +const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); -function main() { - return vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) - .pipe(filter(f => !f.isDirectory())) - .pipe(gzip({ append: false })) - .pipe(es.through(function (data: Vinyl) { - console.log('Uploading CDN file:', data.relative); // debug - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: process.env.VSCODE_QUALITY, - prefix: commit + '/', - contentSettings: { - contentEncoding: 'gzip', - cacheControl: 'max-age=31536000, public' - } - })); +function main(): Promise { + return new Promise((c, e) => { + vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) + .pipe(filter(f => !f.isDirectory())) + .pipe(gzip({ append: false })) + .pipe(es.through(function (data: Vinyl) { + console.log('Uploading CDN file:', data.relative); // debug + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: process.env.VSCODE_QUALITY, + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' + } + })) + .on('end', () => c()) + .on('error', (err: any) => e(err)); + }); } -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/azure-pipelines/upload-configuration.js b/build/azure-pipelines/upload-configuration.js new file mode 100644 index 00000000000..689d99fdae0 --- /dev/null +++ b/build/azure-pipelines/upload-configuration.js @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getSettingsSearchBuildId = exports.shouldSetupSettingsSearch = void 0; +const path = require("path"); +const os = require("os"); +const cp = require("child_process"); +const vfs = require("vinyl-fs"); +const util = require("../lib/util"); +const identity_1 = require("@azure/identity"); +const azure = require('gulp-azure-storage'); +const packageJson = require("../../package.json"); +const root = path.dirname(path.dirname(__dirname)); +const commit = util.getVersion(root); +function generateVSCodeConfigurationTask() { + return new Promise((resolve, reject) => { + const buildDir = process.env['AGENT_BUILDDIRECTORY']; + if (!buildDir) { + return reject(new Error('$AGENT_BUILDDIRECTORY not set')); + } + if (!shouldSetupSettingsSearch()) { + console.log(`Only runs on main and release branches, not ${process.env.BUILD_SOURCEBRANCH}`); + return resolve(undefined); + } + if (process.env.VSCODE_QUALITY !== 'insider' && process.env.VSCODE_QUALITY !== 'stable') { + console.log(`Only runs on insider and stable qualities, not ${process.env.VSCODE_QUALITY}`); + return resolve(undefined); + } + const result = path.join(os.tmpdir(), 'configuration.json'); + const userDataDir = path.join(os.tmpdir(), 'tmpuserdata'); + const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); + const arch = process.env['VSCODE_ARCH']; + const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); + const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; + const appPath = path.join(appRoot, appName, 'Contents', 'Resources', 'app', 'bin', 'code'); + const codeProc = cp.exec(`${appPath} --export-default-configuration='${result}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, (err, stdout, stderr) => { + clearTimeout(timer); + if (err) { + console.log(`err: ${err} ${err.message} ${err.toString()}`); + reject(err); + } + if (stdout) { + console.log(`stdout: ${stdout}`); + } + if (stderr) { + console.log(`stderr: ${stderr}`); + } + resolve(result); + }); + const timer = setTimeout(() => { + codeProc.kill(); + reject(new Error('export-default-configuration process timed out')); + }, 12 * 1000); + codeProc.on('error', err => { + clearTimeout(timer); + reject(err); + }); + }); +} +function shouldSetupSettingsSearch() { + const branch = process.env.BUILD_SOURCEBRANCH; + return !!(branch && (/\/main$/.test(branch) || branch.indexOf('/release/') >= 0)); +} +exports.shouldSetupSettingsSearch = shouldSetupSettingsSearch; +function getSettingsSearchBuildId(packageJson) { + try { + const branch = process.env.BUILD_SOURCEBRANCH; + const branchId = branch.indexOf('/release/') >= 0 ? 0 : + /\/main$/.test(branch) ? 1 : + 2; // Some unexpected branch + const out = cp.execSync(`git rev-list HEAD --count`); + const count = parseInt(out.toString()); + // + // 1.25.1, 1,234,567 commits, main = 1250112345671 + return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; + } + catch (e) { + throw new Error('Could not determine build number: ' + e.toString()); + } +} +exports.getSettingsSearchBuildId = getSettingsSearchBuildId; +async function main() { + const configPath = await generateVSCodeConfigurationTask(); + if (!configPath) { + return; + } + const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); + if (!settingsSearchBuildId) { + throw new Error('Failed to compute build number'); + } + const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); + return new Promise((c, e) => { + vfs.src(configPath) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: 'configuration', + prefix: `${settingsSearchBuildId}/${commit}/` + })) + .on('end', () => c()) + .on('error', (err) => e(err)); + }); +} +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/azure-pipelines/upload-configuration.ts b/build/azure-pipelines/upload-configuration.ts new file mode 100644 index 00000000000..3acc337e749 --- /dev/null +++ b/build/azure-pipelines/upload-configuration.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as path from 'path'; +import * as os from 'os'; +import * as cp from 'child_process'; +import * as vfs from 'vinyl-fs'; +import * as util from '../lib/util'; +import { ClientSecretCredential } from '@azure/identity'; +const azure = require('gulp-azure-storage'); +import * as packageJson from '../../package.json'; + +const root = path.dirname(path.dirname(__dirname)); +const commit = util.getVersion(root); + +function generateVSCodeConfigurationTask(): Promise { + return new Promise((resolve, reject) => { + const buildDir = process.env['AGENT_BUILDDIRECTORY']; + if (!buildDir) { + return reject(new Error('$AGENT_BUILDDIRECTORY not set')); + } + + if (!shouldSetupSettingsSearch()) { + console.log(`Only runs on main and release branches, not ${process.env.BUILD_SOURCEBRANCH}`); + return resolve(undefined); + } + + if (process.env.VSCODE_QUALITY !== 'insider' && process.env.VSCODE_QUALITY !== 'stable') { + console.log(`Only runs on insider and stable qualities, not ${process.env.VSCODE_QUALITY}`); + return resolve(undefined); + } + + const result = path.join(os.tmpdir(), 'configuration.json'); + const userDataDir = path.join(os.tmpdir(), 'tmpuserdata'); + const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); + const arch = process.env['VSCODE_ARCH']; + const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); + const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; + const appPath = path.join(appRoot, appName, 'Contents', 'Resources', 'app', 'bin', 'code'); + const codeProc = cp.exec( + `${appPath} --export-default-configuration='${result}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, + (err, stdout, stderr) => { + clearTimeout(timer); + if (err) { + console.log(`err: ${err} ${err.message} ${err.toString()}`); + reject(err); + } + + if (stdout) { + console.log(`stdout: ${stdout}`); + } + + if (stderr) { + console.log(`stderr: ${stderr}`); + } + + resolve(result); + } + ); + const timer = setTimeout(() => { + codeProc.kill(); + reject(new Error('export-default-configuration process timed out')); + }, 12 * 1000); + + codeProc.on('error', err => { + clearTimeout(timer); + reject(err); + }); + }); +} + +export function shouldSetupSettingsSearch(): boolean { + const branch = process.env.BUILD_SOURCEBRANCH; + return !!(branch && (/\/main$/.test(branch) || branch.indexOf('/release/') >= 0)); +} + +export function getSettingsSearchBuildId(packageJson: { version: string }) { + try { + const branch = process.env.BUILD_SOURCEBRANCH!; + const branchId = branch.indexOf('/release/') >= 0 ? 0 : + /\/main$/.test(branch) ? 1 : + 2; // Some unexpected branch + + const out = cp.execSync(`git rev-list HEAD --count`); + const count = parseInt(out.toString()); + + // + // 1.25.1, 1,234,567 commits, main = 1250112345671 + return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; + } catch (e) { + throw new Error('Could not determine build number: ' + e.toString()); + } +} + +async function main(): Promise { + const configPath = await generateVSCodeConfigurationTask(); + + if (!configPath) { + return; + } + + const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); + + if (!settingsSearchBuildId) { + throw new Error('Failed to compute build number'); + } + + const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); + + return new Promise((c, e) => { + vfs.src(configPath) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: 'configuration', + prefix: `${settingsSearchBuildId}/${commit}/` + })) + .on('end', () => c()) + .on('error', (err: any) => e(err)); + }); +} + +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/azure-pipelines/upload-nlsmetadata.js b/build/azure-pipelines/upload-nlsmetadata.js index 27c9438187f..a09d569f7f6 100644 --- a/build/azure-pipelines/upload-nlsmetadata.js +++ b/build/azure-pipelines/upload-nlsmetadata.js @@ -10,79 +10,88 @@ const vfs = require("vinyl-fs"); const util = require("../lib/util"); const merge = require("gulp-merge-json"); const gzip = require("gulp-gzip"); +const identity_1 = require("@azure/identity"); const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); +const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); function main() { - return es.merge(vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })) - .pipe(merge({ - fileName: 'combined.nls.metadata.json', - jsonSpace: '', - edit: (parsedJson, file) => { - let key; - if (file.base === 'out-vscode-web-min') { - return { vscode: parsedJson }; - } - // Handle extensions and follow the same structure as the Core nls file. - switch (file.basename) { - case 'package.nls.json': - // put package.nls.json content in Core NlsMetadata format - // language packs use the key "package" to specify that - // translations are for the package.json file - parsedJson = { - messages: { - package: Object.values(parsedJson) - }, - keys: { - package: Object.keys(parsedJson) - }, - bundles: { - main: ['package'] + return new Promise((c, e) => { + es.merge(vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })) + .pipe(merge({ + fileName: 'combined.nls.metadata.json', + jsonSpace: '', + edit: (parsedJson, file) => { + let key; + if (file.base === 'out-vscode-web-min') { + return { vscode: parsedJson }; + } + // Handle extensions and follow the same structure as the Core nls file. + switch (file.basename) { + case 'package.nls.json': + // put package.nls.json content in Core NlsMetadata format + // language packs use the key "package" to specify that + // translations are for the package.json file + parsedJson = { + messages: { + package: Object.values(parsedJson) + }, + keys: { + package: Object.keys(parsedJson) + }, + bundles: { + main: ['package'] + } + }; + break; + case 'nls.metadata.header.json': + parsedJson = { header: parsedJson }; + break; + case 'nls.metadata.json': + // put nls.metadata.json content in Core NlsMetadata format + const modules = Object.keys(parsedJson); + const json = { + keys: {}, + messages: {}, + bundles: { + main: [] + } + }; + for (const module of modules) { + json.messages[module] = parsedJson[module].messages; + json.keys[module] = parsedJson[module].keys; + json.bundles.main.push(module); } - }; - break; - case 'nls.metadata.header.json': - parsedJson = { header: parsedJson }; - break; - case 'nls.metadata.json': - // put nls.metadata.json content in Core NlsMetadata format - const modules = Object.keys(parsedJson); - const json = { - keys: {}, - messages: {}, - bundles: { - main: [] - } - }; - for (const module of modules) { - json.messages[module] = parsedJson[module].messages; - json.keys[module] = parsedJson[module].keys; - json.bundles.main.push(module); - } - parsedJson = json; - break; + parsedJson = json; + break; + } + key = 'vscode.' + file.relative.split('/')[0]; + return { [key]: parsedJson }; + }, + })) + .pipe(gzip({ append: false })) + .pipe(vfs.dest('./nlsMetadata')) + .pipe(es.through(function (data) { + console.log(`Uploading ${data.path}`); + // trigger artifact upload + console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`); + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: 'nlsmetadata', + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' } - key = 'vscode.' + file.relative.split('/')[0]; - return { [key]: parsedJson }; - }, - })) - .pipe(gzip({ append: false })) - .pipe(vfs.dest('./nlsMetadata')) - .pipe(es.through(function (data) { - console.log(`Uploading ${data.path}`); - // trigger artifact upload - console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`); - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'nlsmetadata', - prefix: commit + '/', - contentSettings: { - contentEncoding: 'gzip', - cacheControl: 'max-age=31536000, public' - } - })); + })) + .on('end', () => c()) + .on('error', (err) => e(err)); + }); } -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/azure-pipelines/upload-nlsmetadata.ts b/build/azure-pipelines/upload-nlsmetadata.ts index 72a6701dddd..b3a19b218e3 100644 --- a/build/azure-pipelines/upload-nlsmetadata.ts +++ b/build/azure-pipelines/upload-nlsmetadata.ts @@ -12,10 +12,12 @@ import * as vfs from 'vinyl-fs'; import * as util from '../lib/util'; import * as merge from 'gulp-merge-json'; import * as gzip from 'gulp-gzip'; +import { ClientSecretCredential } from '@azure/identity'; const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); +const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); interface NlsMetadata { keys: { [module: string]: string }, @@ -23,85 +25,94 @@ interface NlsMetadata { bundles: { [bundle: string]: string[] }, } -function main() { - return es.merge( - vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), - vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), - vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), - vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })) - .pipe(merge({ - fileName: 'combined.nls.metadata.json', - jsonSpace: '', - edit: (parsedJson, file) => { - let key; - if (file.base === 'out-vscode-web-min') { - return { vscode: parsedJson }; - } +function main(): Promise { + return new Promise((c, e) => { - // Handle extensions and follow the same structure as the Core nls file. - switch (file.basename) { - case 'package.nls.json': - // put package.nls.json content in Core NlsMetadata format - // language packs use the key "package" to specify that - // translations are for the package.json file - parsedJson = { - messages: { - package: Object.values(parsedJson) - }, - keys: { - package: Object.keys(parsedJson) - }, - bundles: { - main: ['package'] + es.merge( + vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), + vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), + vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), + vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })) + .pipe(merge({ + fileName: 'combined.nls.metadata.json', + jsonSpace: '', + edit: (parsedJson, file) => { + let key; + if (file.base === 'out-vscode-web-min') { + return { vscode: parsedJson }; + } + + // Handle extensions and follow the same structure as the Core nls file. + switch (file.basename) { + case 'package.nls.json': + // put package.nls.json content in Core NlsMetadata format + // language packs use the key "package" to specify that + // translations are for the package.json file + parsedJson = { + messages: { + package: Object.values(parsedJson) + }, + keys: { + package: Object.keys(parsedJson) + }, + bundles: { + main: ['package'] + } + }; + break; + + case 'nls.metadata.header.json': + parsedJson = { header: parsedJson }; + break; + + case 'nls.metadata.json': + // put nls.metadata.json content in Core NlsMetadata format + const modules = Object.keys(parsedJson); + + const json: NlsMetadata = { + keys: {}, + messages: {}, + bundles: { + main: [] + } + }; + for (const module of modules) { + json.messages[module] = parsedJson[module].messages; + json.keys[module] = parsedJson[module].keys; + json.bundles.main.push(module); } - }; - break; - - case 'nls.metadata.header.json': - parsedJson = { header: parsedJson }; - break; - - case 'nls.metadata.json': - // put nls.metadata.json content in Core NlsMetadata format - const modules = Object.keys(parsedJson); - - const json: NlsMetadata = { - keys: {}, - messages: {}, - bundles: { - main: [] - } - }; - for (const module of modules) { - json.messages[module] = parsedJson[module].messages; - json.keys[module] = parsedJson[module].keys; - json.bundles.main.push(module); - } - parsedJson = json; - break; + parsedJson = json; + break; + } + key = 'vscode.' + file.relative.split('/')[0]; + return { [key]: parsedJson }; + }, + })) + .pipe(gzip({ append: false })) + .pipe(vfs.dest('./nlsMetadata')) + .pipe(es.through(function (data: Vinyl) { + console.log(`Uploading ${data.path}`); + // trigger artifact upload + console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`); + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: 'nlsmetadata', + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' } - key = 'vscode.' + file.relative.split('/')[0]; - return { [key]: parsedJson }; - }, - })) - .pipe(gzip({ append: false })) - .pipe(vfs.dest('./nlsMetadata')) - .pipe(es.through(function (data: Vinyl) { - console.log(`Uploading ${data.path}`); - // trigger artifact upload - console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`); - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'nlsmetadata', - prefix: commit + '/', - contentSettings: { - contentEncoding: 'gzip', - cacheControl: 'max-age=31536000, public' - } - })); + })) + .on('end', () => c()) + .on('error', (err: any) => e(err)); + }); } -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); + diff --git a/build/azure-pipelines/upload-sourcemaps.js b/build/azure-pipelines/upload-sourcemaps.js index b2a886f6c7c..4edcd2ccd70 100644 --- a/build/azure-pipelines/upload-sourcemaps.js +++ b/build/azure-pipelines/upload-sourcemaps.js @@ -10,9 +10,11 @@ const vfs = require("vinyl-fs"); const util = require("../lib/util"); // @ts-ignore const deps = require("../lib/dependencies"); +const identity_1 = require("@azure/identity"); const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); +const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); // optionally allow to pass in explicit base/maps to upload const [, , base, maps] = process.argv; function src(base, maps = `${base}/**/*.map`) { @@ -40,16 +42,23 @@ function main() { else { sources.push(src(base, maps)); } - return es.merge(...sources) - .pipe(es.through(function (data) { - console.log('Uploading Sourcemap', data.relative); // debug - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'sourcemaps', - prefix: commit + '/' - })); + return new Promise((c, e) => { + es.merge(...sources) + .pipe(es.through(function (data) { + console.log('Uploading Sourcemap', data.relative); // debug + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: 'sourcemaps', + prefix: commit + '/' + })) + .on('end', () => c()) + .on('error', (err) => e(err)); + }); } -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/azure-pipelines/upload-sourcemaps.ts b/build/azure-pipelines/upload-sourcemaps.ts index 769e224e6f7..f065ff2cf38 100644 --- a/build/azure-pipelines/upload-sourcemaps.ts +++ b/build/azure-pipelines/upload-sourcemaps.ts @@ -12,10 +12,12 @@ import * as vfs from 'vinyl-fs'; import * as util from '../lib/util'; // @ts-ignore import * as deps from '../lib/dependencies'; +import { ClientSecretCredential } from '@azure/identity'; const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); +const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); // optionally allow to pass in explicit base/maps to upload const [, , base, maps] = process.argv; @@ -28,8 +30,8 @@ function src(base: string, maps = `${base}/**/*.map`) { })); } -function main() { - const sources = []; +function main(): Promise { + const sources: any[] = []; // vscode client maps (default) if (!base) { @@ -51,17 +53,25 @@ function main() { sources.push(src(base, maps)); } - return es.merge(...sources) - .pipe(es.through(function (data: Vinyl) { - console.log('Uploading Sourcemap', data.relative); // debug - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'sourcemaps', - prefix: commit + '/' - })); + return new Promise((c, e) => { + es.merge(...sources) + .pipe(es.through(function (data: Vinyl) { + console.log('Uploading Sourcemap', data.relative); // debug + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: 'sourcemaps', + prefix: commit + '/' + })) + .on('end', () => c()) + .on('error', (err: any) => e(err)); + }); } -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); + diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index c12cdf063bf..2a467124141 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -8,7 +8,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,web-storage-account,web-storage-key,ticino-storage-key" + SecretsFilter: "github-distro-mixin-password" - task: DownloadPipelineArtifact@2 inputs: @@ -99,11 +99,24 @@ steps: yarn gulp vscode-web-min-ci displayName: Build + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - script: | set -e - AZURE_STORAGE_ACCOUNT="$(web-storage-account)" \ - AZURE_STORAGE_ACCESS_KEY="$(web-storage-key)" \ - node build/azure-pipelines/upload-cdn.js + AZURE_STORAGE_ACCOUNT="vscodeweb" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + node build/azure-pipelines/upload-cdn displayName: Upload to CDN # upload only the workbench.web.api.js source maps because @@ -111,13 +124,19 @@ steps: # general task to upload source maps has already been run - script: | set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.api.js.map displayName: Upload sourcemaps (Web) - script: | set -e - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ node build/azure-pipelines/upload-nlsmetadata displayName: Upload NLS Metadata condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index f74a431c522..6ff2da9724a 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -13,7 +13,7 @@ steps: inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,vscode-storage-key,builds-docdb-key-readwrite,ESRP-PKI,esrp-aad-username,esrp-aad-password" + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - task: DownloadPipelineArtifact@2 inputs: diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index e292abeda2c..250eb01bcdd 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -32,6 +32,7 @@ const createAsar = require('./lib/asar').createAsar; const minimist = require('minimist'); const { compileBuildTask } = require('./gulpfile.compile'); const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); +const { getSettingsSearchBuildId, shouldSetupSettingsSearch } = require('./azure-pipelines/upload-configuration'); // Build const vscodeEntryPoints = _.flatten([ @@ -475,110 +476,3 @@ gulp.task('vscode-translations-import', function () { .pipe(vfs.dest(`./build/win32/i18n`)); })); }); - -// This task is only run for the MacOS build -const generateVSCodeConfigurationTask = task.define('generate-vscode-configuration', () => { - return new Promise((resolve, reject) => { - const buildDir = process.env['AGENT_BUILDDIRECTORY']; - if (!buildDir) { - return reject(new Error('$AGENT_BUILDDIRECTORY not set')); - } - - if (process.env.VSCODE_QUALITY !== 'insider' && process.env.VSCODE_QUALITY !== 'stable') { - return resolve(); - } - - const userDataDir = path.join(os.tmpdir(), 'tmpuserdata'); - const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); - const arch = process.env['VSCODE_ARCH']; - const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); - const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; - const appPath = path.join(appRoot, appName, 'Contents', 'Resources', 'app', 'bin', 'code'); - const codeProc = cp.exec( - `${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, - (err, stdout, stderr) => { - clearTimeout(timer); - if (err) { - console.log(`err: ${err} ${err.message} ${err.toString()}`); - reject(err); - } - - if (stdout) { - console.log(`stdout: ${stdout}`); - } - - if (stderr) { - console.log(`stderr: ${stderr}`); - } - - resolve(); - } - ); - const timer = setTimeout(() => { - codeProc.kill(); - reject(new Error('export-default-configuration process timed out')); - }, 12 * 1000); - - codeProc.on('error', err => { - clearTimeout(timer); - reject(err); - }); - }); -}); - -const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json'); -gulp.task(task.define( - 'upload-vscode-configuration', - task.series( - generateVSCodeConfigurationTask, - () => { - const azure = require('gulp-azure-storage'); - - if (!shouldSetupSettingsSearch()) { - const branch = process.env.BUILD_SOURCEBRANCH; - console.log(`Only runs on main and release branches, not ${branch}`); - return; - } - - if (!fs.existsSync(allConfigDetailsPath)) { - throw new Error(`configuration file at ${allConfigDetailsPath} does not exist`); - } - - const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); - if (!settingsSearchBuildId) { - throw new Error('Failed to compute build number'); - } - - return gulp.src(allConfigDetailsPath) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'configuration', - prefix: `${settingsSearchBuildId}/${commit}/` - })); - } - ) -)); - -function shouldSetupSettingsSearch() { - const branch = process.env.BUILD_SOURCEBRANCH; - return branch && (/\/main$/.test(branch) || branch.indexOf('/release/') >= 0); -} - -function getSettingsSearchBuildId(packageJson) { - try { - const branch = process.env.BUILD_SOURCEBRANCH; - const branchId = branch.indexOf('/release/') >= 0 ? 0 : - /\/main$/.test(branch) ? 1 : - 2; // Some unexpected branch - - const out = cp.execSync(`git rev-list HEAD --count`); - const count = parseInt(out.toString()); - - // - // 1.25.1, 1,234,567 commits, main = 1250112345671 - return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; - } catch (e) { - throw new Error('Could not determine build number: ' + e.toString()); - } -} diff --git a/package.json b/package.json index fdb1efeca5a..736e2a4dead 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "glob": "^5.0.13", "gulp": "^4.0.0", "gulp-atom-electron": "1.32.0", - "gulp-azure-storage": "^0.11.1", + "gulp-azure-storage": "^0.12.1", "gulp-bom": "^3.0.0", "gulp-buffer": "0.0.2", "gulp-concat": "^2.6.1", diff --git a/yarn.lock b/yarn.lock index b1568055bff..d1842821be2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,94 @@ resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30" integrity sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA= +"@azure/abort-controller@^1.0.0": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.4.tgz#fd3c4d46c8ed67aace42498c8e2270960250eafd" + integrity sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw== + dependencies: + tslib "^2.0.0" + +"@azure/core-asynciterator-polyfill@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" + integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== + +"@azure/core-auth@^1.3.0": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.3.2.tgz#6a2c248576c26df365f6c7881ca04b7f6d08e3d0" + integrity sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + +"@azure/core-http@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-2.2.2.tgz#573798f087d808d39aa71fd7c52b8d7b89f440da" + integrity sha512-V1DdoO9V/sFimKpdWoNBgsE+QUjQgpXYnxrTdUp5RyhsTJjvEVn/HKmTQXIHuLUUo6IyIWj+B+Dg4VaXse9dIA== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-asynciterator-polyfill" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-tracing" "1.0.0-preview.13" + "@azure/logger" "^1.0.0" + "@types/node-fetch" "^2.5.0" + "@types/tunnel" "^0.0.3" + form-data "^4.0.0" + node-fetch "^2.6.0" + process "^0.11.10" + tough-cookie "^4.0.0" + tslib "^2.2.0" + tunnel "^0.0.6" + uuid "^8.3.0" + xml2js "^0.4.19" + +"@azure/core-lro@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.2.1.tgz#5527b41037c658d3aefc19d68633e51e53d6e6a3" + integrity sha512-HE6PBl+mlKa0eBsLwusHqAqjLc5n9ByxeDo3Hz4kF3B1hqHvRkBr4oMgoT6tX7Hc3q97KfDctDUon7EhvoeHPA== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-tracing" "1.0.0-preview.13" + "@azure/logger" "^1.0.0" + tslib "^2.2.0" + +"@azure/core-paging@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.2.0.tgz#3754da429e8687bdc3613c750e79a564582e802b" + integrity sha512-ZX1bCjm/MjKPCN6kQD/9GJErYSoKA8YWp6YWoo5EIzcTWlSBLXu3gNaBTUl8usGl+UShiKo7b4Gdy1NSTIlpZg== + dependencies: + "@azure/core-asynciterator-polyfill" "^1.0.0" + tslib "^2.2.0" + +"@azure/core-tracing@1.0.0-preview.13": + version "1.0.0-preview.13" + resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz#55883d40ae2042f6f1e12b17dd0c0d34c536d644" + integrity sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ== + dependencies: + "@opentelemetry/api" "^1.0.1" + tslib "^2.2.0" + +"@azure/logger@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" + integrity sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g== + dependencies: + tslib "^2.2.0" + +"@azure/storage-blob@^12.8.0": + version "12.8.0" + resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.8.0.tgz#97b7ecc6c7b17bcbaf0281c79c16af6f512d6130" + integrity sha512-c8+Wz19xauW0bGkTCoqZH4dYfbtBniPiGiRQOn1ca6G5jsjr4azwaTk9gwjVY8r3vY2Taf95eivLzipfIfiS4A== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-http" "^2.0.0" + "@azure/core-lro" "^2.2.0" + "@azure/core-paging" "^1.1.1" + "@azure/core-tracing" "1.0.0-preview.13" + "@azure/logger" "^1.0.0" + events "^3.0.0" + tslib "^2.2.0" + "@babel/code-frame@^7.0.0": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -417,6 +505,11 @@ dependencies: "@octokit/openapi-types" "^10.2.2" +"@opentelemetry/api@^1.0.1": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.0.3.tgz#13a12ae9e05c2a782f7b5e84c3cbfda4225eaf80" + integrity sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ== + "@parcel/watcher@2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.2.tgz#46bef14584497147bad5247cfb41f80b24d24dfb" @@ -629,6 +722,14 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.0.tgz#3eb56d13a1de1d347ecb1957c6860c911704bc44" integrity sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ== +"@types/node-fetch@^2.5.0": + version "2.5.12" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" + integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "4.2.22" resolved "https://registry.yarnpkg.com/@types/node/-/node-4.2.22.tgz#cf488a0f6b4a9c245d09927f4f757ca278b9c8ce" @@ -688,6 +789,13 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.6.tgz#569b8a08121d3203398290d602d84d73c8dcf5da" integrity sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw== +"@types/tunnel@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" + integrity sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA== + dependencies: + "@types/node" "*" + "@types/uglify-js@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.3.tgz#801a5ca1dc642861f47c46d14b700ed2d610840b" @@ -1657,23 +1765,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -azure-storage@^2.10.2: - version "2.10.2" - resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.2.tgz#3bcabdbf10e72fd0990db81116e49023c4a675b6" - integrity sha512-pOyGPya9+NDpAfm5YcFfklo57HfjDbYLXxs4lomPwvRxmb0Di/A+a+RkUmEFzaQ8S13CqxK40bRRB0sjj2ZQxA== - dependencies: - browserify-mime "~1.2.9" - extend "^3.0.2" - json-edm-parser "0.1.2" - md5.js "1.3.4" - readable-stream "~2.0.0" - request "^2.86.0" - underscore "~1.8.3" - uuid "^3.0.0" - validator "~9.4.1" - xml2js "0.2.8" - xmlbuilder "^9.0.7" - bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" @@ -1883,11 +1974,6 @@ browserify-des@^1.0.0: inherits "^2.0.1" safe-buffer "^5.1.2" -browserify-mime@~1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f" - integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8= - browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" @@ -2478,7 +2564,7 @@ colorette@^1.2.1, colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -4259,6 +4345,24 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -4697,12 +4801,12 @@ gulp-atom-electron@1.32.0: vinyl "^2.2.0" vinyl-fs "^3.0.3" -gulp-azure-storage@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/gulp-azure-storage/-/gulp-azure-storage-0.11.1.tgz#0e5f5d0f789da11206f1e5a9311a6cf7107877d7" - integrity sha512-csOwItwZV1P9GLsORVQy+CFwjYDdHNrBol89JlHdlhGx0fTgJBc1COTRZbjGRyRjgdUuVguo3YLl4ToJ10/SIQ== +gulp-azure-storage@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/gulp-azure-storage/-/gulp-azure-storage-0.12.1.tgz#be2be1268af7dea6fdf56045b3eb3f090335de4a" + integrity sha512-n/hx8bbGsqrcizruqDTX6zy2FUdkTDGAz04IdopNxNTZivZmizf8u9WLYJreUE6/qCnSJnyjS1HP82+mLk7rjg== dependencies: - azure-storage "^2.10.2" + "@azure/storage-blob" "^12.8.0" delayed-stream "0.0.6" event-stream "3.3.4" mime "^1.3.4" @@ -4961,7 +5065,7 @@ har-schema@^2.0.0: resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0, har-validator@~5.1.3: +har-validator@~5.1.3: version "5.1.5" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== @@ -5950,13 +6054,6 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-edm-parser@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/json-edm-parser/-/json-edm-parser-0.1.2.tgz#1e60b0fef1bc0af67bc0d146dfdde5486cd615b4" - integrity sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ= - dependencies: - jsonparse "~1.2.0" - json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -6010,11 +6107,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" - integrity sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70= - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -6397,14 +6489,6 @@ matcher@^3.0.0: dependencies: escape-string-regexp "^4.0.0" -md5.js@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" - integrity sha1-6b296UogpawYsENA/Fdk1bCdkB0= - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -6943,6 +7027,13 @@ node-addon-api@^4.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== +node-fetch@^2.6.0: + version "2.6.6" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" + integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== + dependencies: + whatwg-url "^5.0.0" + node-fetch@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.2.tgz#986996818b73785e47b1965cc34eb093a1d464d0" @@ -8111,11 +8202,6 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= - process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -8165,7 +8251,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24, psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -8220,7 +8306,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -8383,18 +8469,6 @@ readable-stream@~1.0.17: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -8534,32 +8608,6 @@ replacestream@^4.0.0: tunnel-agent "^0.6.0" uuid "^3.3.2" -request@^2.86.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - requestretry@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-4.0.0.tgz#4e9e7280a7d8561bf33e9925264cf026e2be3e89" @@ -8784,11 +8832,6 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@0.5.x: - version "0.5.8" - resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" - integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= - sax@>=0.6.0, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -9885,13 +9928,14 @@ to-through@^2.0.0: dependencies: through2 "^2.0.3" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: - psl "^1.1.24" - punycode "^1.4.1" + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" tough-cookie@~2.5.0: version "2.5.0" @@ -9901,6 +9945,11 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -9949,6 +9998,11 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.0, tslib@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -10063,11 +10117,6 @@ underscore@^1.12.1: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== -underscore@~1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" - integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= - undertaker-registry@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" @@ -10136,7 +10185,7 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0: +universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== @@ -10232,16 +10281,16 @@ util@^0.12.4: safe-buffer "^5.1.2" which-typed-array "^1.1.2" -uuid@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" - integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g== - uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache@^2.0.3: version "2.2.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" @@ -10274,11 +10323,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validator@~9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" - integrity sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA== - value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" @@ -10519,6 +10563,11 @@ watchpack@^2.2.0: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + webpack-cli@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.2.tgz#a718db600de6d3906a4357e059ae584a89f4c1a5" @@ -10635,6 +10684,14 @@ webpack@^5.42.0: watchpack "^2.2.0" webpack-sources "^2.3.0" +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + when@^3.7.7: version "3.7.8" resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" @@ -10809,13 +10866,6 @@ xml-name-validator@^1.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-1.0.0.tgz#dcf82ee092322951ef8cc1ba596c9cbfd14a83f1" integrity sha1-3Pgu4JIyKVHvjMG6WWycv9FKg/E= -xml2js@0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.8.tgz#9b81690931631ff09d1957549faf54f4f980b3c2" - integrity sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I= - dependencies: - sax "0.5.x" - xml2js@^0.4.17: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" From 981ab5e8d3d2b7683a03d57f25052f34cb25b7b2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 18:41:25 +0100 Subject: [PATCH 296/330] Update to Unicode 14.0.0 --- src/vs/base/common/strings.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index ac3308e8a3e..cdb818b15ae 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -548,9 +548,9 @@ export function getCharContainingOffset(str: string, offset: number): [number, n } /** - * Generated using https://github.com/alexdima/unicode-utils/blob/master/generate-rtl-test.js + * Generated using https://github.com/alexdima/unicode-utils/blob/main/rtl-test.js */ -const CONTAINS_RTL = /(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u08BD\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE33\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDCFF]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD50-\uDFFF]|\uD83B[\uDC00-\uDEBB])/; +const CONTAINS_RTL = /(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA\u07FE-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u088E\u08A0-\u08C9\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDC7\uFDF0-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE35\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDD23\uDE80-\uDEA9\uDEAD-\uDF45\uDF51-\uDF81\uDF86-\uDFF6]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD4B-\uDFFF]|\uD83B[\uDC00-\uDEBB])/; /** * Returns true if `str` contains any Unicode character that is classified as "R" or "AL". @@ -560,9 +560,9 @@ export function containsRTL(str: string): boolean { } /** - * Generated using https://github.com/alexdima/unicode-utils/blob/master/generate-emoji-test.js + * Generated using https://github.com/alexdima/unicode-utils/blob/main/emoji-test.js */ -const CONTAINS_EMOJI = /(?:[\u231A\u231B\u23F0\u23F3\u2600-\u27BF\u2B50\u2B55]|\uD83C[\uDDE6-\uDDFF\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F\uDE80-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD00-\uDDFF\uDE70-\uDED6])/; +const CONTAINS_EMOJI = /(?:[\u231A\u231B\u23F0\u23F3\u2600-\u27BF\u2B50\u2B55]|\uD83C[\uDDE6-\uDDFF\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F\uDE80-\uDEFC\uDFE0-\uDFF0]|\uD83E[\uDD00-\uDDFF\uDE70-\uDEF6])/; export function containsEmoji(str: string): boolean { return CONTAINS_EMOJI.test(str); @@ -641,15 +641,15 @@ export function isFullWidthCharacter(charCode: number): boolean { /** * A fast function (therefore imprecise) to check if code points are emojis. - * Generated using https://github.com/alexdima/unicode-utils/blob/master/generate-emoji-test.js + * Generated using https://github.com/alexdima/unicode-utils/blob/main/emoji-test.js */ export function isEmojiImprecise(x: number): boolean { return ( (x >= 0x1F1E6 && x <= 0x1F1FF) || (x === 8986) || (x === 8987) || (x === 9200) || (x === 9203) || (x >= 9728 && x <= 10175) || (x === 11088) || (x === 11093) || (x >= 127744 && x <= 128591) || (x >= 128640 && x <= 128764) - || (x >= 128992 && x <= 129003) || (x >= 129280 && x <= 129535) - || (x >= 129648 && x <= 129750) + || (x >= 128992 && x <= 129008) || (x >= 129280 && x <= 129535) + || (x >= 129648 && x <= 129782) ); } @@ -947,8 +947,8 @@ class GraphemeBreakTree { } function getGraphemeBreakRawData(): number[] { - // generated using https://github.com/alexdima/unicode-utils/blob/master/generate-grapheme-break.js - return JSON.parse('[0,0,0,51592,51592,11,44424,44424,11,72251,72254,5,7150,7150,7,48008,48008,11,55176,55176,11,128420,128420,14,3276,3277,5,9979,9980,14,46216,46216,11,49800,49800,11,53384,53384,11,70726,70726,5,122915,122916,5,129320,129327,14,2558,2558,5,5906,5908,5,9762,9763,14,43360,43388,8,45320,45320,11,47112,47112,11,48904,48904,11,50696,50696,11,52488,52488,11,54280,54280,11,70082,70083,1,71350,71350,7,73111,73111,5,127892,127893,14,128726,128727,14,129473,129474,14,2027,2035,5,2901,2902,5,3784,3789,5,6754,6754,5,8418,8420,5,9877,9877,14,11088,11088,14,44008,44008,5,44872,44872,11,45768,45768,11,46664,46664,11,47560,47560,11,48456,48456,11,49352,49352,11,50248,50248,11,51144,51144,11,52040,52040,11,52936,52936,11,53832,53832,11,54728,54728,11,69811,69814,5,70459,70460,5,71096,71099,7,71998,71998,5,72874,72880,5,119149,119149,7,127374,127374,14,128335,128335,14,128482,128482,14,128765,128767,14,129399,129400,14,129680,129685,14,1476,1477,5,2377,2380,7,2759,2760,5,3137,3140,7,3458,3459,7,4153,4154,5,6432,6434,5,6978,6978,5,7675,7679,5,9723,9726,14,9823,9823,14,9919,9923,14,10035,10036,14,42736,42737,5,43596,43596,5,44200,44200,11,44648,44648,11,45096,45096,11,45544,45544,11,45992,45992,11,46440,46440,11,46888,46888,11,47336,47336,11,47784,47784,11,48232,48232,11,48680,48680,11,49128,49128,11,49576,49576,11,50024,50024,11,50472,50472,11,50920,50920,11,51368,51368,11,51816,51816,11,52264,52264,11,52712,52712,11,53160,53160,11,53608,53608,11,54056,54056,11,54504,54504,11,54952,54952,11,68108,68111,5,69933,69940,5,70197,70197,7,70498,70499,7,70845,70845,5,71229,71229,5,71727,71735,5,72154,72155,5,72344,72345,5,73023,73029,5,94095,94098,5,121403,121452,5,126981,127182,14,127538,127546,14,127990,127990,14,128391,128391,14,128445,128449,14,128500,128505,14,128752,128752,14,129160,129167,14,129356,129356,14,129432,129442,14,129648,129651,14,129751,131069,14,173,173,4,1757,1757,1,2274,2274,1,2494,2494,5,2641,2641,5,2876,2876,5,3014,3016,7,3262,3262,7,3393,3396,5,3570,3571,7,3968,3972,5,4228,4228,7,6086,6086,5,6679,6680,5,6912,6915,5,7080,7081,5,7380,7392,5,8252,8252,14,9096,9096,14,9748,9749,14,9784,9786,14,9833,9850,14,9890,9894,14,9938,9938,14,9999,9999,14,10085,10087,14,12349,12349,14,43136,43137,7,43454,43456,7,43755,43755,7,44088,44088,11,44312,44312,11,44536,44536,11,44760,44760,11,44984,44984,11,45208,45208,11,45432,45432,11,45656,45656,11,45880,45880,11,46104,46104,11,46328,46328,11,46552,46552,11,46776,46776,11,47000,47000,11,47224,47224,11,47448,47448,11,47672,47672,11,47896,47896,11,48120,48120,11,48344,48344,11,48568,48568,11,48792,48792,11,49016,49016,11,49240,49240,11,49464,49464,11,49688,49688,11,49912,49912,11,50136,50136,11,50360,50360,11,50584,50584,11,50808,50808,11,51032,51032,11,51256,51256,11,51480,51480,11,51704,51704,11,51928,51928,11,52152,52152,11,52376,52376,11,52600,52600,11,52824,52824,11,53048,53048,11,53272,53272,11,53496,53496,11,53720,53720,11,53944,53944,11,54168,54168,11,54392,54392,11,54616,54616,11,54840,54840,11,55064,55064,11,65438,65439,5,69633,69633,5,69837,69837,1,70018,70018,7,70188,70190,7,70368,70370,7,70465,70468,7,70712,70719,5,70835,70840,5,70850,70851,5,71132,71133,5,71340,71340,7,71458,71461,5,71985,71989,7,72002,72002,7,72193,72202,5,72281,72283,5,72766,72766,7,72885,72886,5,73104,73105,5,92912,92916,5,113824,113827,4,119173,119179,5,121505,121519,5,125136,125142,5,127279,127279,14,127489,127490,14,127570,127743,14,127900,127901,14,128254,128254,14,128369,128370,14,128400,128400,14,128425,128432,14,128468,128475,14,128489,128494,14,128715,128720,14,128745,128745,14,128759,128760,14,129004,129023,14,129296,129304,14,129340,129342,14,129388,129392,14,129404,129407,14,129454,129455,14,129485,129487,14,129659,129663,14,129719,129727,14,917536,917631,5,13,13,2,1160,1161,5,1564,1564,4,1807,1807,1,2085,2087,5,2363,2363,7,2402,2403,5,2507,2508,7,2622,2624,7,2691,2691,7,2786,2787,5,2881,2884,5,3006,3006,5,3072,3072,5,3170,3171,5,3267,3268,7,3330,3331,7,3406,3406,1,3538,3540,5,3655,3662,5,3897,3897,5,4038,4038,5,4184,4185,5,4352,4447,8,6068,6069,5,6155,6157,5,6448,6449,7,6742,6742,5,6783,6783,5,6966,6970,5,7042,7042,7,7143,7143,7,7212,7219,5,7412,7412,5,8206,8207,4,8294,8303,4,8596,8601,14,9410,9410,14,9742,9742,14,9757,9757,14,9770,9770,14,9794,9794,14,9828,9828,14,9855,9855,14,9882,9882,14,9900,9903,14,9929,9933,14,9963,9967,14,9987,9988,14,10006,10006,14,10062,10062,14,10175,10175,14,11744,11775,5,42607,42607,5,43043,43044,7,43263,43263,5,43444,43445,7,43569,43570,5,43698,43700,5,43766,43766,5,44032,44032,11,44144,44144,11,44256,44256,11,44368,44368,11,44480,44480,11,44592,44592,11,44704,44704,11,44816,44816,11,44928,44928,11,45040,45040,11,45152,45152,11,45264,45264,11,45376,45376,11,45488,45488,11,45600,45600,11,45712,45712,11,45824,45824,11,45936,45936,11,46048,46048,11,46160,46160,11,46272,46272,11,46384,46384,11,46496,46496,11,46608,46608,11,46720,46720,11,46832,46832,11,46944,46944,11,47056,47056,11,47168,47168,11,47280,47280,11,47392,47392,11,47504,47504,11,47616,47616,11,47728,47728,11,47840,47840,11,47952,47952,11,48064,48064,11,48176,48176,11,48288,48288,11,48400,48400,11,48512,48512,11,48624,48624,11,48736,48736,11,48848,48848,11,48960,48960,11,49072,49072,11,49184,49184,11,49296,49296,11,49408,49408,11,49520,49520,11,49632,49632,11,49744,49744,11,49856,49856,11,49968,49968,11,50080,50080,11,50192,50192,11,50304,50304,11,50416,50416,11,50528,50528,11,50640,50640,11,50752,50752,11,50864,50864,11,50976,50976,11,51088,51088,11,51200,51200,11,51312,51312,11,51424,51424,11,51536,51536,11,51648,51648,11,51760,51760,11,51872,51872,11,51984,51984,11,52096,52096,11,52208,52208,11,52320,52320,11,52432,52432,11,52544,52544,11,52656,52656,11,52768,52768,11,52880,52880,11,52992,52992,11,53104,53104,11,53216,53216,11,53328,53328,11,53440,53440,11,53552,53552,11,53664,53664,11,53776,53776,11,53888,53888,11,54000,54000,11,54112,54112,11,54224,54224,11,54336,54336,11,54448,54448,11,54560,54560,11,54672,54672,11,54784,54784,11,54896,54896,11,55008,55008,11,55120,55120,11,64286,64286,5,66272,66272,5,68900,68903,5,69762,69762,7,69817,69818,5,69927,69931,5,70003,70003,5,70070,70078,5,70094,70094,7,70194,70195,7,70206,70206,5,70400,70401,5,70463,70463,7,70475,70477,7,70512,70516,5,70722,70724,5,70832,70832,5,70842,70842,5,70847,70848,5,71088,71089,7,71102,71102,7,71219,71226,5,71231,71232,5,71342,71343,7,71453,71455,5,71463,71467,5,71737,71738,5,71995,71996,5,72000,72000,7,72145,72147,7,72160,72160,5,72249,72249,7,72273,72278,5,72330,72342,5,72752,72758,5,72850,72871,5,72882,72883,5,73018,73018,5,73031,73031,5,73109,73109,5,73461,73462,7,94031,94031,5,94192,94193,7,119142,119142,7,119155,119162,4,119362,119364,5,121476,121476,5,122888,122904,5,123184,123190,5,126976,126979,14,127184,127231,14,127344,127345,14,127405,127461,14,127514,127514,14,127561,127567,14,127778,127779,14,127896,127896,14,127985,127986,14,127995,127999,5,128326,128328,14,128360,128366,14,128378,128378,14,128394,128397,14,128405,128406,14,128422,128423,14,128435,128443,14,128453,128464,14,128479,128480,14,128484,128487,14,128496,128498,14,128640,128709,14,128723,128724,14,128736,128741,14,128747,128748,14,128755,128755,14,128762,128762,14,128981,128991,14,129096,129103,14,129292,129292,14,129311,129311,14,129329,129330,14,129344,129349,14,129360,129374,14,129394,129394,14,129402,129402,14,129413,129425,14,129445,129450,14,129466,129471,14,129483,129483,14,129511,129535,14,129653,129655,14,129667,129670,14,129705,129711,14,129731,129743,14,917505,917505,4,917760,917999,5,10,10,3,127,159,4,768,879,5,1471,1471,5,1536,1541,1,1648,1648,5,1767,1768,5,1840,1866,5,2070,2073,5,2137,2139,5,2307,2307,7,2366,2368,7,2382,2383,7,2434,2435,7,2497,2500,5,2519,2519,5,2563,2563,7,2631,2632,5,2677,2677,5,2750,2752,7,2763,2764,7,2817,2817,5,2879,2879,5,2891,2892,7,2914,2915,5,3008,3008,5,3021,3021,5,3076,3076,5,3146,3149,5,3202,3203,7,3264,3265,7,3271,3272,7,3298,3299,5,3390,3390,5,3402,3404,7,3426,3427,5,3535,3535,5,3544,3550,7,3635,3635,7,3763,3763,7,3893,3893,5,3953,3966,5,3981,3991,5,4145,4145,7,4157,4158,5,4209,4212,5,4237,4237,5,4520,4607,10,5970,5971,5,6071,6077,5,6089,6099,5,6277,6278,5,6439,6440,5,6451,6456,7,6683,6683,5,6744,6750,5,6765,6770,7,6846,6846,5,6964,6964,5,6972,6972,5,7019,7027,5,7074,7077,5,7083,7085,5,7146,7148,7,7154,7155,7,7222,7223,5,7394,7400,5,7416,7417,5,8204,8204,5,8233,8233,4,8288,8292,4,8413,8416,5,8482,8482,14,8986,8987,14,9193,9203,14,9654,9654,14,9733,9733,14,9745,9745,14,9752,9752,14,9760,9760,14,9766,9766,14,9774,9775,14,9792,9792,14,9800,9811,14,9825,9826,14,9831,9831,14,9852,9853,14,9872,9873,14,9880,9880,14,9885,9887,14,9896,9897,14,9906,9916,14,9926,9927,14,9936,9936,14,9941,9960,14,9974,9974,14,9982,9985,14,9992,9997,14,10002,10002,14,10017,10017,14,10055,10055,14,10071,10071,14,10145,10145,14,11013,11015,14,11503,11505,5,12334,12335,5,12951,12951,14,42612,42621,5,43014,43014,5,43047,43047,7,43204,43205,5,43335,43345,5,43395,43395,7,43450,43451,7,43561,43566,5,43573,43574,5,43644,43644,5,43710,43711,5,43758,43759,7,44005,44005,5,44012,44012,7,44060,44060,11,44116,44116,11,44172,44172,11,44228,44228,11,44284,44284,11,44340,44340,11,44396,44396,11,44452,44452,11,44508,44508,11,44564,44564,11,44620,44620,11,44676,44676,11,44732,44732,11,44788,44788,11,44844,44844,11,44900,44900,11,44956,44956,11,45012,45012,11,45068,45068,11,45124,45124,11,45180,45180,11,45236,45236,11,45292,45292,11,45348,45348,11,45404,45404,11,45460,45460,11,45516,45516,11,45572,45572,11,45628,45628,11,45684,45684,11,45740,45740,11,45796,45796,11,45852,45852,11,45908,45908,11,45964,45964,11,46020,46020,11,46076,46076,11,46132,46132,11,46188,46188,11,46244,46244,11,46300,46300,11,46356,46356,11,46412,46412,11,46468,46468,11,46524,46524,11,46580,46580,11,46636,46636,11,46692,46692,11,46748,46748,11,46804,46804,11,46860,46860,11,46916,46916,11,46972,46972,11,47028,47028,11,47084,47084,11,47140,47140,11,47196,47196,11,47252,47252,11,47308,47308,11,47364,47364,11,47420,47420,11,47476,47476,11,47532,47532,11,47588,47588,11,47644,47644,11,47700,47700,11,47756,47756,11,47812,47812,11,47868,47868,11,47924,47924,11,47980,47980,11,48036,48036,11,48092,48092,11,48148,48148,11,48204,48204,11,48260,48260,11,48316,48316,11,48372,48372,11,48428,48428,11,48484,48484,11,48540,48540,11,48596,48596,11,48652,48652,11,48708,48708,11,48764,48764,11,48820,48820,11,48876,48876,11,48932,48932,11,48988,48988,11,49044,49044,11,49100,49100,11,49156,49156,11,49212,49212,11,49268,49268,11,49324,49324,11,49380,49380,11,49436,49436,11,49492,49492,11,49548,49548,11,49604,49604,11,49660,49660,11,49716,49716,11,49772,49772,11,49828,49828,11,49884,49884,11,49940,49940,11,49996,49996,11,50052,50052,11,50108,50108,11,50164,50164,11,50220,50220,11,50276,50276,11,50332,50332,11,50388,50388,11,50444,50444,11,50500,50500,11,50556,50556,11,50612,50612,11,50668,50668,11,50724,50724,11,50780,50780,11,50836,50836,11,50892,50892,11,50948,50948,11,51004,51004,11,51060,51060,11,51116,51116,11,51172,51172,11,51228,51228,11,51284,51284,11,51340,51340,11,51396,51396,11,51452,51452,11,51508,51508,11,51564,51564,11,51620,51620,11,51676,51676,11,51732,51732,11,51788,51788,11,51844,51844,11,51900,51900,11,51956,51956,11,52012,52012,11,52068,52068,11,52124,52124,11,52180,52180,11,52236,52236,11,52292,52292,11,52348,52348,11,52404,52404,11,52460,52460,11,52516,52516,11,52572,52572,11,52628,52628,11,52684,52684,11,52740,52740,11,52796,52796,11,52852,52852,11,52908,52908,11,52964,52964,11,53020,53020,11,53076,53076,11,53132,53132,11,53188,53188,11,53244,53244,11,53300,53300,11,53356,53356,11,53412,53412,11,53468,53468,11,53524,53524,11,53580,53580,11,53636,53636,11,53692,53692,11,53748,53748,11,53804,53804,11,53860,53860,11,53916,53916,11,53972,53972,11,54028,54028,11,54084,54084,11,54140,54140,11,54196,54196,11,54252,54252,11,54308,54308,11,54364,54364,11,54420,54420,11,54476,54476,11,54532,54532,11,54588,54588,11,54644,54644,11,54700,54700,11,54756,54756,11,54812,54812,11,54868,54868,11,54924,54924,11,54980,54980,11,55036,55036,11,55092,55092,11,55148,55148,11,55216,55238,9,65056,65071,5,65529,65531,4,68097,68099,5,68159,68159,5,69446,69456,5,69688,69702,5,69808,69810,7,69815,69816,7,69821,69821,1,69888,69890,5,69932,69932,7,69957,69958,7,70016,70017,5,70067,70069,7,70079,70080,7,70089,70092,5,70095,70095,5,70191,70193,5,70196,70196,5,70198,70199,5,70367,70367,5,70371,70378,5,70402,70403,7,70462,70462,5,70464,70464,5,70471,70472,7,70487,70487,5,70502,70508,5,70709,70711,7,70720,70721,7,70725,70725,7,70750,70750,5,70833,70834,7,70841,70841,7,70843,70844,7,70846,70846,7,70849,70849,7,71087,71087,5,71090,71093,5,71100,71101,5,71103,71104,5,71216,71218,7,71227,71228,7,71230,71230,7,71339,71339,5,71341,71341,5,71344,71349,5,71351,71351,5,71456,71457,7,71462,71462,7,71724,71726,7,71736,71736,7,71984,71984,5,71991,71992,7,71997,71997,7,71999,71999,1,72001,72001,1,72003,72003,5,72148,72151,5,72156,72159,7,72164,72164,7,72243,72248,5,72250,72250,1,72263,72263,5,72279,72280,7,72324,72329,1,72343,72343,7,72751,72751,7,72760,72765,5,72767,72767,5,72873,72873,7,72881,72881,7,72884,72884,7,73009,73014,5,73020,73021,5,73030,73030,1,73098,73102,7,73107,73108,7,73110,73110,7,73459,73460,5,78896,78904,4,92976,92982,5,94033,94087,7,94180,94180,5,113821,113822,5,119141,119141,5,119143,119145,5,119150,119154,5,119163,119170,5,119210,119213,5,121344,121398,5,121461,121461,5,121499,121503,5,122880,122886,5,122907,122913,5,122918,122922,5,123628,123631,5,125252,125258,5,126980,126980,14,127183,127183,14,127245,127247,14,127340,127343,14,127358,127359,14,127377,127386,14,127462,127487,6,127491,127503,14,127535,127535,14,127548,127551,14,127568,127569,14,127744,127777,14,127780,127891,14,127894,127895,14,127897,127899,14,127902,127984,14,127987,127989,14,127991,127994,14,128000,128253,14,128255,128317,14,128329,128334,14,128336,128359,14,128367,128368,14,128371,128377,14,128379,128390,14,128392,128393,14,128398,128399,14,128401,128404,14,128407,128419,14,128421,128421,14,128424,128424,14,128433,128434,14,128444,128444,14,128450,128452,14,128465,128467,14,128476,128478,14,128481,128481,14,128483,128483,14,128488,128488,14,128495,128495,14,128499,128499,14,128506,128591,14,128710,128714,14,128721,128722,14,128725,128725,14,128728,128735,14,128742,128744,14,128746,128746,14,128749,128751,14,128753,128754,14,128756,128758,14,128761,128761,14,128763,128764,14,128884,128895,14,128992,129003,14,129036,129039,14,129114,129119,14,129198,129279,14,129293,129295,14,129305,129310,14,129312,129319,14,129328,129328,14,129331,129338,14,129343,129343,14,129351,129355,14,129357,129359,14,129375,129387,14,129393,129393,14,129395,129398,14,129401,129401,14,129403,129403,14,129408,129412,14,129426,129431,14,129443,129444,14,129451,129453,14,129456,129465,14,129472,129472,14,129475,129482,14,129484,129484,14,129488,129510,14,129536,129647,14,129652,129652,14,129656,129658,14,129664,129666,14,129671,129679,14,129686,129704,14,129712,129718,14,129728,129730,14,129744,129750,14,917504,917504,4,917506,917535,4,917632,917759,4,918000,921599,4,0,9,4,11,12,4,14,31,4,169,169,14,174,174,14,1155,1159,5,1425,1469,5,1473,1474,5,1479,1479,5,1552,1562,5,1611,1631,5,1750,1756,5,1759,1764,5,1770,1773,5,1809,1809,5,1958,1968,5,2045,2045,5,2075,2083,5,2089,2093,5,2259,2273,5,2275,2306,5,2362,2362,5,2364,2364,5,2369,2376,5,2381,2381,5,2385,2391,5,2433,2433,5,2492,2492,5,2495,2496,7,2503,2504,7,2509,2509,5,2530,2531,5,2561,2562,5,2620,2620,5,2625,2626,5,2635,2637,5,2672,2673,5,2689,2690,5,2748,2748,5,2753,2757,5,2761,2761,7,2765,2765,5,2810,2815,5,2818,2819,7,2878,2878,5,2880,2880,7,2887,2888,7,2893,2893,5,2903,2903,5,2946,2946,5,3007,3007,7,3009,3010,7,3018,3020,7,3031,3031,5,3073,3075,7,3134,3136,5,3142,3144,5,3157,3158,5,3201,3201,5,3260,3260,5,3263,3263,5,3266,3266,5,3270,3270,5,3274,3275,7,3285,3286,5,3328,3329,5,3387,3388,5,3391,3392,7,3398,3400,7,3405,3405,5,3415,3415,5,3457,3457,5,3530,3530,5,3536,3537,7,3542,3542,5,3551,3551,5,3633,3633,5,3636,3642,5,3761,3761,5,3764,3772,5,3864,3865,5,3895,3895,5,3902,3903,7,3967,3967,7,3974,3975,5,3993,4028,5,4141,4144,5,4146,4151,5,4155,4156,7,4182,4183,7,4190,4192,5,4226,4226,5,4229,4230,5,4253,4253,5,4448,4519,9,4957,4959,5,5938,5940,5,6002,6003,5,6070,6070,7,6078,6085,7,6087,6088,7,6109,6109,5,6158,6158,4,6313,6313,5,6435,6438,7,6441,6443,7,6450,6450,5,6457,6459,5,6681,6682,7,6741,6741,7,6743,6743,7,6752,6752,5,6757,6764,5,6771,6780,5,6832,6845,5,6847,6848,5,6916,6916,7,6965,6965,5,6971,6971,7,6973,6977,7,6979,6980,7,7040,7041,5,7073,7073,7,7078,7079,7,7082,7082,7,7142,7142,5,7144,7145,5,7149,7149,5,7151,7153,5,7204,7211,7,7220,7221,7,7376,7378,5,7393,7393,7,7405,7405,5,7415,7415,7,7616,7673,5,8203,8203,4,8205,8205,13,8232,8232,4,8234,8238,4,8265,8265,14,8293,8293,4,8400,8412,5,8417,8417,5,8421,8432,5,8505,8505,14,8617,8618,14,9000,9000,14,9167,9167,14,9208,9210,14,9642,9643,14,9664,9664,14,9728,9732,14,9735,9741,14,9743,9744,14,9746,9746,14,9750,9751,14,9753,9756,14,9758,9759,14,9761,9761,14,9764,9765,14,9767,9769,14,9771,9773,14,9776,9783,14,9787,9791,14,9793,9793,14,9795,9799,14,9812,9822,14,9824,9824,14,9827,9827,14,9829,9830,14,9832,9832,14,9851,9851,14,9854,9854,14,9856,9861,14,9874,9876,14,9878,9879,14,9881,9881,14,9883,9884,14,9888,9889,14,9895,9895,14,9898,9899,14,9904,9905,14,9917,9918,14,9924,9925,14,9928,9928,14,9934,9935,14,9937,9937,14,9939,9940,14,9961,9962,14,9968,9973,14,9975,9978,14,9981,9981,14,9986,9986,14,9989,9989,14,9998,9998,14,10000,10001,14,10004,10004,14,10013,10013,14,10024,10024,14,10052,10052,14,10060,10060,14,10067,10069,14,10083,10084,14,10133,10135,14,10160,10160,14,10548,10549,14,11035,11036,14,11093,11093,14,11647,11647,5,12330,12333,5,12336,12336,14,12441,12442,5,12953,12953,14,42608,42610,5,42654,42655,5,43010,43010,5,43019,43019,5,43045,43046,5,43052,43052,5,43188,43203,7,43232,43249,5,43302,43309,5,43346,43347,7,43392,43394,5,43443,43443,5,43446,43449,5,43452,43453,5,43493,43493,5,43567,43568,7,43571,43572,7,43587,43587,5,43597,43597,7,43696,43696,5,43703,43704,5,43713,43713,5,43756,43757,5,43765,43765,7,44003,44004,7,44006,44007,7,44009,44010,7,44013,44013,5,44033,44059,12,44061,44087,12,44089,44115,12,44117,44143,12,44145,44171,12,44173,44199,12,44201,44227,12,44229,44255,12,44257,44283,12,44285,44311,12,44313,44339,12,44341,44367,12,44369,44395,12,44397,44423,12,44425,44451,12,44453,44479,12,44481,44507,12,44509,44535,12,44537,44563,12,44565,44591,12,44593,44619,12,44621,44647,12,44649,44675,12,44677,44703,12,44705,44731,12,44733,44759,12,44761,44787,12,44789,44815,12,44817,44843,12,44845,44871,12,44873,44899,12,44901,44927,12,44929,44955,12,44957,44983,12,44985,45011,12,45013,45039,12,45041,45067,12,45069,45095,12,45097,45123,12,45125,45151,12,45153,45179,12,45181,45207,12,45209,45235,12,45237,45263,12,45265,45291,12,45293,45319,12,45321,45347,12,45349,45375,12,45377,45403,12,45405,45431,12,45433,45459,12,45461,45487,12,45489,45515,12,45517,45543,12,45545,45571,12,45573,45599,12,45601,45627,12,45629,45655,12,45657,45683,12,45685,45711,12,45713,45739,12,45741,45767,12,45769,45795,12,45797,45823,12,45825,45851,12,45853,45879,12,45881,45907,12,45909,45935,12,45937,45963,12,45965,45991,12,45993,46019,12,46021,46047,12,46049,46075,12,46077,46103,12,46105,46131,12,46133,46159,12,46161,46187,12,46189,46215,12,46217,46243,12,46245,46271,12,46273,46299,12,46301,46327,12,46329,46355,12,46357,46383,12,46385,46411,12,46413,46439,12,46441,46467,12,46469,46495,12,46497,46523,12,46525,46551,12,46553,46579,12,46581,46607,12,46609,46635,12,46637,46663,12,46665,46691,12,46693,46719,12,46721,46747,12,46749,46775,12,46777,46803,12,46805,46831,12,46833,46859,12,46861,46887,12,46889,46915,12,46917,46943,12,46945,46971,12,46973,46999,12,47001,47027,12,47029,47055,12,47057,47083,12,47085,47111,12,47113,47139,12,47141,47167,12,47169,47195,12,47197,47223,12,47225,47251,12,47253,47279,12,47281,47307,12,47309,47335,12,47337,47363,12,47365,47391,12,47393,47419,12,47421,47447,12,47449,47475,12,47477,47503,12,47505,47531,12,47533,47559,12,47561,47587,12,47589,47615,12,47617,47643,12,47645,47671,12,47673,47699,12,47701,47727,12,47729,47755,12,47757,47783,12,47785,47811,12,47813,47839,12,47841,47867,12,47869,47895,12,47897,47923,12,47925,47951,12,47953,47979,12,47981,48007,12,48009,48035,12,48037,48063,12,48065,48091,12,48093,48119,12,48121,48147,12,48149,48175,12,48177,48203,12,48205,48231,12,48233,48259,12,48261,48287,12,48289,48315,12,48317,48343,12,48345,48371,12,48373,48399,12,48401,48427,12,48429,48455,12,48457,48483,12,48485,48511,12,48513,48539,12,48541,48567,12,48569,48595,12,48597,48623,12,48625,48651,12,48653,48679,12,48681,48707,12,48709,48735,12,48737,48763,12,48765,48791,12,48793,48819,12,48821,48847,12,48849,48875,12,48877,48903,12,48905,48931,12,48933,48959,12,48961,48987,12,48989,49015,12,49017,49043,12,49045,49071,12,49073,49099,12,49101,49127,12,49129,49155,12,49157,49183,12,49185,49211,12,49213,49239,12,49241,49267,12,49269,49295,12,49297,49323,12,49325,49351,12,49353,49379,12,49381,49407,12,49409,49435,12,49437,49463,12,49465,49491,12,49493,49519,12,49521,49547,12,49549,49575,12,49577,49603,12,49605,49631,12,49633,49659,12,49661,49687,12,49689,49715,12,49717,49743,12,49745,49771,12,49773,49799,12,49801,49827,12,49829,49855,12,49857,49883,12,49885,49911,12,49913,49939,12,49941,49967,12,49969,49995,12,49997,50023,12,50025,50051,12,50053,50079,12,50081,50107,12,50109,50135,12,50137,50163,12,50165,50191,12,50193,50219,12,50221,50247,12,50249,50275,12,50277,50303,12,50305,50331,12,50333,50359,12,50361,50387,12,50389,50415,12,50417,50443,12,50445,50471,12,50473,50499,12,50501,50527,12,50529,50555,12,50557,50583,12,50585,50611,12,50613,50639,12,50641,50667,12,50669,50695,12,50697,50723,12,50725,50751,12,50753,50779,12,50781,50807,12,50809,50835,12,50837,50863,12,50865,50891,12,50893,50919,12,50921,50947,12,50949,50975,12,50977,51003,12,51005,51031,12,51033,51059,12,51061,51087,12,51089,51115,12,51117,51143,12,51145,51171,12,51173,51199,12,51201,51227,12,51229,51255,12,51257,51283,12,51285,51311,12,51313,51339,12,51341,51367,12,51369,51395,12,51397,51423,12,51425,51451,12,51453,51479,12,51481,51507,12,51509,51535,12,51537,51563,12,51565,51591,12,51593,51619,12,51621,51647,12,51649,51675,12,51677,51703,12,51705,51731,12,51733,51759,12,51761,51787,12,51789,51815,12,51817,51843,12,51845,51871,12,51873,51899,12,51901,51927,12,51929,51955,12,51957,51983,12,51985,52011,12,52013,52039,12,52041,52067,12,52069,52095,12,52097,52123,12,52125,52151,12,52153,52179,12,52181,52207,12,52209,52235,12,52237,52263,12,52265,52291,12,52293,52319,12,52321,52347,12,52349,52375,12,52377,52403,12,52405,52431,12,52433,52459,12,52461,52487,12,52489,52515,12,52517,52543,12,52545,52571,12,52573,52599,12,52601,52627,12,52629,52655,12,52657,52683,12,52685,52711,12,52713,52739,12,52741,52767,12,52769,52795,12,52797,52823,12,52825,52851,12,52853,52879,12,52881,52907,12,52909,52935,12,52937,52963,12,52965,52991,12,52993,53019,12,53021,53047,12,53049,53075,12,53077,53103,12,53105,53131,12,53133,53159,12,53161,53187,12,53189,53215,12,53217,53243,12,53245,53271,12,53273,53299,12,53301,53327,12,53329,53355,12,53357,53383,12,53385,53411,12,53413,53439,12,53441,53467,12,53469,53495,12,53497,53523,12,53525,53551,12,53553,53579,12,53581,53607,12,53609,53635,12,53637,53663,12,53665,53691,12,53693,53719,12,53721,53747,12,53749,53775,12,53777,53803,12,53805,53831,12,53833,53859,12,53861,53887,12,53889,53915,12,53917,53943,12,53945,53971,12,53973,53999,12,54001,54027,12,54029,54055,12,54057,54083,12,54085,54111,12,54113,54139,12,54141,54167,12,54169,54195,12,54197,54223,12,54225,54251,12,54253,54279,12,54281,54307,12,54309,54335,12,54337,54363,12,54365,54391,12,54393,54419,12,54421,54447,12,54449,54475,12,54477,54503,12,54505,54531,12,54533,54559,12,54561,54587,12,54589,54615,12,54617,54643,12,54645,54671,12,54673,54699,12,54701,54727,12,54729,54755,12,54757,54783,12,54785,54811,12,54813,54839,12,54841,54867,12,54869,54895,12,54897,54923,12,54925,54951,12,54953,54979,12,54981,55007,12,55009,55035,12,55037,55063,12,55065,55091,12,55093,55119,12,55121,55147,12,55149,55175,12,55177,55203,12,55243,55291,10,65024,65039,5,65279,65279,4,65520,65528,4,66045,66045,5,66422,66426,5,68101,68102,5,68152,68154,5,68325,68326,5,69291,69292,5,69632,69632,7,69634,69634,7,69759,69761,5]'); + // generated using https://github.com/alexdima/unicode-utils/blob/main/grapheme-break.js + return JSON.parse('[0,0,0,51229,51255,12,44061,44087,12,127462,127487,6,7083,7085,5,47645,47671,12,54813,54839,12,128678,128678,14,3270,3270,5,9919,9923,14,45853,45879,12,49437,49463,12,53021,53047,12,71216,71218,7,128398,128399,14,129360,129374,14,2519,2519,5,4448,4519,9,9742,9742,14,12336,12336,14,44957,44983,12,46749,46775,12,48541,48567,12,50333,50359,12,52125,52151,12,53917,53943,12,69888,69890,5,73018,73018,5,127990,127990,14,128558,128559,14,128759,128760,14,129653,129655,14,2027,2035,5,2891,2892,7,3761,3761,5,6683,6683,5,8293,8293,4,9825,9826,14,9999,9999,14,43452,43453,5,44509,44535,12,45405,45431,12,46301,46327,12,47197,47223,12,48093,48119,12,48989,49015,12,49885,49911,12,50781,50807,12,51677,51703,12,52573,52599,12,53469,53495,12,54365,54391,12,65279,65279,4,70471,70472,7,72145,72147,7,119173,119179,5,127799,127818,14,128240,128244,14,128512,128512,14,128652,128652,14,128721,128722,14,129292,129292,14,129445,129450,14,129734,129743,14,1476,1477,5,2366,2368,7,2750,2752,7,3076,3076,5,3415,3415,5,4141,4144,5,6109,6109,5,6964,6964,5,7394,7400,5,9197,9198,14,9770,9770,14,9877,9877,14,9968,9969,14,10084,10084,14,43052,43052,5,43713,43713,5,44285,44311,12,44733,44759,12,45181,45207,12,45629,45655,12,46077,46103,12,46525,46551,12,46973,46999,12,47421,47447,12,47869,47895,12,48317,48343,12,48765,48791,12,49213,49239,12,49661,49687,12,50109,50135,12,50557,50583,12,51005,51031,12,51453,51479,12,51901,51927,12,52349,52375,12,52797,52823,12,53245,53271,12,53693,53719,12,54141,54167,12,54589,54615,12,55037,55063,12,69506,69509,5,70191,70193,5,70841,70841,7,71463,71467,5,72330,72342,5,94031,94031,5,123628,123631,5,127763,127765,14,127941,127941,14,128043,128062,14,128302,128317,14,128465,128467,14,128539,128539,14,128640,128640,14,128662,128662,14,128703,128703,14,128745,128745,14,129004,129007,14,129329,129330,14,129402,129402,14,129483,129483,14,129686,129704,14,130048,131069,14,173,173,4,1757,1757,1,2200,2207,5,2434,2435,7,2631,2632,5,2817,2817,5,3008,3008,5,3201,3201,5,3387,3388,5,3542,3542,5,3902,3903,7,4190,4192,5,6002,6003,5,6439,6440,5,6765,6770,7,7019,7027,5,7154,7155,7,8205,8205,13,8505,8505,14,9654,9654,14,9757,9757,14,9792,9792,14,9852,9853,14,9890,9894,14,9937,9937,14,9981,9981,14,10035,10036,14,11035,11036,14,42654,42655,5,43346,43347,7,43587,43587,5,44006,44007,7,44173,44199,12,44397,44423,12,44621,44647,12,44845,44871,12,45069,45095,12,45293,45319,12,45517,45543,12,45741,45767,12,45965,45991,12,46189,46215,12,46413,46439,12,46637,46663,12,46861,46887,12,47085,47111,12,47309,47335,12,47533,47559,12,47757,47783,12,47981,48007,12,48205,48231,12,48429,48455,12,48653,48679,12,48877,48903,12,49101,49127,12,49325,49351,12,49549,49575,12,49773,49799,12,49997,50023,12,50221,50247,12,50445,50471,12,50669,50695,12,50893,50919,12,51117,51143,12,51341,51367,12,51565,51591,12,51789,51815,12,52013,52039,12,52237,52263,12,52461,52487,12,52685,52711,12,52909,52935,12,53133,53159,12,53357,53383,12,53581,53607,12,53805,53831,12,54029,54055,12,54253,54279,12,54477,54503,12,54701,54727,12,54925,54951,12,55149,55175,12,68101,68102,5,69762,69762,7,70067,70069,7,70371,70378,5,70720,70721,7,71087,71087,5,71341,71341,5,71995,71996,5,72249,72249,7,72850,72871,5,73109,73109,5,118576,118598,5,121505,121519,5,127245,127247,14,127568,127569,14,127777,127777,14,127872,127891,14,127956,127967,14,128015,128016,14,128110,128172,14,128259,128259,14,128367,128368,14,128424,128424,14,128488,128488,14,128530,128532,14,128550,128551,14,128566,128566,14,128647,128647,14,128656,128656,14,128667,128673,14,128691,128693,14,128715,128715,14,128728,128732,14,128752,128752,14,128765,128767,14,129096,129103,14,129311,129311,14,129344,129349,14,129394,129394,14,129413,129425,14,129466,129471,14,129511,129535,14,129664,129666,14,129719,129722,14,129760,129767,14,917536,917631,5,13,13,2,1160,1161,5,1564,1564,4,1807,1807,1,2085,2087,5,2307,2307,7,2382,2383,7,2497,2500,5,2563,2563,7,2677,2677,5,2763,2764,7,2879,2879,5,2914,2915,5,3021,3021,5,3142,3144,5,3263,3263,5,3285,3286,5,3398,3400,7,3530,3530,5,3633,3633,5,3864,3865,5,3974,3975,5,4155,4156,7,4229,4230,5,5909,5909,7,6078,6085,7,6277,6278,5,6451,6456,7,6744,6750,5,6846,6846,5,6972,6972,5,7074,7077,5,7146,7148,7,7222,7223,5,7416,7417,5,8234,8238,4,8417,8417,5,9000,9000,14,9203,9203,14,9730,9731,14,9748,9749,14,9762,9763,14,9776,9783,14,9800,9811,14,9831,9831,14,9872,9873,14,9882,9882,14,9900,9903,14,9929,9933,14,9941,9960,14,9974,9974,14,9989,9989,14,10006,10006,14,10062,10062,14,10160,10160,14,11647,11647,5,12953,12953,14,43019,43019,5,43232,43249,5,43443,43443,5,43567,43568,7,43696,43696,5,43765,43765,7,44013,44013,5,44117,44143,12,44229,44255,12,44341,44367,12,44453,44479,12,44565,44591,12,44677,44703,12,44789,44815,12,44901,44927,12,45013,45039,12,45125,45151,12,45237,45263,12,45349,45375,12,45461,45487,12,45573,45599,12,45685,45711,12,45797,45823,12,45909,45935,12,46021,46047,12,46133,46159,12,46245,46271,12,46357,46383,12,46469,46495,12,46581,46607,12,46693,46719,12,46805,46831,12,46917,46943,12,47029,47055,12,47141,47167,12,47253,47279,12,47365,47391,12,47477,47503,12,47589,47615,12,47701,47727,12,47813,47839,12,47925,47951,12,48037,48063,12,48149,48175,12,48261,48287,12,48373,48399,12,48485,48511,12,48597,48623,12,48709,48735,12,48821,48847,12,48933,48959,12,49045,49071,12,49157,49183,12,49269,49295,12,49381,49407,12,49493,49519,12,49605,49631,12,49717,49743,12,49829,49855,12,49941,49967,12,50053,50079,12,50165,50191,12,50277,50303,12,50389,50415,12,50501,50527,12,50613,50639,12,50725,50751,12,50837,50863,12,50949,50975,12,51061,51087,12,51173,51199,12,51285,51311,12,51397,51423,12,51509,51535,12,51621,51647,12,51733,51759,12,51845,51871,12,51957,51983,12,52069,52095,12,52181,52207,12,52293,52319,12,52405,52431,12,52517,52543,12,52629,52655,12,52741,52767,12,52853,52879,12,52965,52991,12,53077,53103,12,53189,53215,12,53301,53327,12,53413,53439,12,53525,53551,12,53637,53663,12,53749,53775,12,53861,53887,12,53973,53999,12,54085,54111,12,54197,54223,12,54309,54335,12,54421,54447,12,54533,54559,12,54645,54671,12,54757,54783,12,54869,54895,12,54981,55007,12,55093,55119,12,55243,55291,10,66045,66045,5,68325,68326,5,69688,69702,5,69817,69818,5,69957,69958,7,70089,70092,5,70198,70199,5,70462,70462,5,70502,70508,5,70750,70750,5,70846,70846,7,71100,71101,5,71230,71230,7,71351,71351,5,71737,71738,5,72000,72000,7,72160,72160,5,72273,72278,5,72752,72758,5,72882,72883,5,73031,73031,5,73461,73462,7,94192,94193,7,119149,119149,7,121403,121452,5,122915,122916,5,126980,126980,14,127358,127359,14,127535,127535,14,127759,127759,14,127771,127771,14,127792,127793,14,127825,127867,14,127897,127899,14,127945,127945,14,127985,127986,14,128000,128007,14,128021,128021,14,128066,128100,14,128184,128235,14,128249,128252,14,128266,128276,14,128335,128335,14,128379,128390,14,128407,128419,14,128444,128444,14,128481,128481,14,128499,128499,14,128526,128526,14,128536,128536,14,128543,128543,14,128556,128556,14,128564,128564,14,128577,128580,14,128643,128645,14,128649,128649,14,128654,128654,14,128660,128660,14,128664,128664,14,128675,128675,14,128686,128689,14,128695,128696,14,128705,128709,14,128717,128719,14,128725,128725,14,128736,128741,14,128747,128748,14,128755,128755,14,128762,128762,14,128981,128991,14,129009,129023,14,129160,129167,14,129296,129304,14,129320,129327,14,129340,129342,14,129356,129356,14,129388,129392,14,129399,129400,14,129404,129407,14,129432,129442,14,129454,129455,14,129473,129474,14,129485,129487,14,129648,129651,14,129659,129660,14,129671,129679,14,129709,129711,14,129728,129730,14,129751,129753,14,129776,129782,14,917505,917505,4,917760,917999,5,10,10,3,127,159,4,768,879,5,1471,1471,5,1536,1541,1,1648,1648,5,1767,1768,5,1840,1866,5,2070,2073,5,2137,2139,5,2274,2274,1,2363,2363,7,2377,2380,7,2402,2403,5,2494,2494,5,2507,2508,7,2558,2558,5,2622,2624,7,2641,2641,5,2691,2691,7,2759,2760,5,2786,2787,5,2876,2876,5,2881,2884,5,2901,2902,5,3006,3006,5,3014,3016,7,3072,3072,5,3134,3136,5,3157,3158,5,3260,3260,5,3266,3266,5,3274,3275,7,3328,3329,5,3391,3392,7,3405,3405,5,3457,3457,5,3536,3537,7,3551,3551,5,3636,3642,5,3764,3772,5,3895,3895,5,3967,3967,7,3993,4028,5,4146,4151,5,4182,4183,7,4226,4226,5,4253,4253,5,4957,4959,5,5940,5940,7,6070,6070,7,6087,6088,7,6158,6158,4,6432,6434,5,6448,6449,7,6679,6680,5,6742,6742,5,6754,6754,5,6783,6783,5,6912,6915,5,6966,6970,5,6978,6978,5,7042,7042,7,7080,7081,5,7143,7143,7,7150,7150,7,7212,7219,5,7380,7392,5,7412,7412,5,8203,8203,4,8232,8232,4,8265,8265,14,8400,8412,5,8421,8432,5,8617,8618,14,9167,9167,14,9200,9200,14,9410,9410,14,9723,9726,14,9733,9733,14,9745,9745,14,9752,9752,14,9760,9760,14,9766,9766,14,9774,9774,14,9786,9786,14,9794,9794,14,9823,9823,14,9828,9828,14,9833,9850,14,9855,9855,14,9875,9875,14,9880,9880,14,9885,9887,14,9896,9897,14,9906,9916,14,9926,9927,14,9935,9935,14,9939,9939,14,9962,9962,14,9972,9972,14,9978,9978,14,9986,9986,14,9997,9997,14,10002,10002,14,10017,10017,14,10055,10055,14,10071,10071,14,10133,10135,14,10548,10549,14,11093,11093,14,12330,12333,5,12441,12442,5,42608,42610,5,43010,43010,5,43045,43046,5,43188,43203,7,43302,43309,5,43392,43394,5,43446,43449,5,43493,43493,5,43571,43572,7,43597,43597,7,43703,43704,5,43756,43757,5,44003,44004,7,44009,44010,7,44033,44059,12,44089,44115,12,44145,44171,12,44201,44227,12,44257,44283,12,44313,44339,12,44369,44395,12,44425,44451,12,44481,44507,12,44537,44563,12,44593,44619,12,44649,44675,12,44705,44731,12,44761,44787,12,44817,44843,12,44873,44899,12,44929,44955,12,44985,45011,12,45041,45067,12,45097,45123,12,45153,45179,12,45209,45235,12,45265,45291,12,45321,45347,12,45377,45403,12,45433,45459,12,45489,45515,12,45545,45571,12,45601,45627,12,45657,45683,12,45713,45739,12,45769,45795,12,45825,45851,12,45881,45907,12,45937,45963,12,45993,46019,12,46049,46075,12,46105,46131,12,46161,46187,12,46217,46243,12,46273,46299,12,46329,46355,12,46385,46411,12,46441,46467,12,46497,46523,12,46553,46579,12,46609,46635,12,46665,46691,12,46721,46747,12,46777,46803,12,46833,46859,12,46889,46915,12,46945,46971,12,47001,47027,12,47057,47083,12,47113,47139,12,47169,47195,12,47225,47251,12,47281,47307,12,47337,47363,12,47393,47419,12,47449,47475,12,47505,47531,12,47561,47587,12,47617,47643,12,47673,47699,12,47729,47755,12,47785,47811,12,47841,47867,12,47897,47923,12,47953,47979,12,48009,48035,12,48065,48091,12,48121,48147,12,48177,48203,12,48233,48259,12,48289,48315,12,48345,48371,12,48401,48427,12,48457,48483,12,48513,48539,12,48569,48595,12,48625,48651,12,48681,48707,12,48737,48763,12,48793,48819,12,48849,48875,12,48905,48931,12,48961,48987,12,49017,49043,12,49073,49099,12,49129,49155,12,49185,49211,12,49241,49267,12,49297,49323,12,49353,49379,12,49409,49435,12,49465,49491,12,49521,49547,12,49577,49603,12,49633,49659,12,49689,49715,12,49745,49771,12,49801,49827,12,49857,49883,12,49913,49939,12,49969,49995,12,50025,50051,12,50081,50107,12,50137,50163,12,50193,50219,12,50249,50275,12,50305,50331,12,50361,50387,12,50417,50443,12,50473,50499,12,50529,50555,12,50585,50611,12,50641,50667,12,50697,50723,12,50753,50779,12,50809,50835,12,50865,50891,12,50921,50947,12,50977,51003,12,51033,51059,12,51089,51115,12,51145,51171,12,51201,51227,12,51257,51283,12,51313,51339,12,51369,51395,12,51425,51451,12,51481,51507,12,51537,51563,12,51593,51619,12,51649,51675,12,51705,51731,12,51761,51787,12,51817,51843,12,51873,51899,12,51929,51955,12,51985,52011,12,52041,52067,12,52097,52123,12,52153,52179,12,52209,52235,12,52265,52291,12,52321,52347,12,52377,52403,12,52433,52459,12,52489,52515,12,52545,52571,12,52601,52627,12,52657,52683,12,52713,52739,12,52769,52795,12,52825,52851,12,52881,52907,12,52937,52963,12,52993,53019,12,53049,53075,12,53105,53131,12,53161,53187,12,53217,53243,12,53273,53299,12,53329,53355,12,53385,53411,12,53441,53467,12,53497,53523,12,53553,53579,12,53609,53635,12,53665,53691,12,53721,53747,12,53777,53803,12,53833,53859,12,53889,53915,12,53945,53971,12,54001,54027,12,54057,54083,12,54113,54139,12,54169,54195,12,54225,54251,12,54281,54307,12,54337,54363,12,54393,54419,12,54449,54475,12,54505,54531,12,54561,54587,12,54617,54643,12,54673,54699,12,54729,54755,12,54785,54811,12,54841,54867,12,54897,54923,12,54953,54979,12,55009,55035,12,55065,55091,12,55121,55147,12,55177,55203,12,65024,65039,5,65520,65528,4,66422,66426,5,68152,68154,5,69291,69292,5,69633,69633,5,69747,69748,5,69811,69814,5,69826,69826,5,69932,69932,7,70016,70017,5,70079,70080,7,70095,70095,5,70196,70196,5,70367,70367,5,70402,70403,7,70464,70464,5,70487,70487,5,70709,70711,7,70725,70725,7,70833,70834,7,70843,70844,7,70849,70849,7,71090,71093,5,71103,71104,5,71227,71228,7,71339,71339,5,71344,71349,5,71458,71461,5,71727,71735,5,71985,71989,7,71998,71998,5,72002,72002,7,72154,72155,5,72193,72202,5,72251,72254,5,72281,72283,5,72344,72345,5,72766,72766,7,72874,72880,5,72885,72886,5,73023,73029,5,73104,73105,5,73111,73111,5,92912,92916,5,94095,94098,5,113824,113827,4,119142,119142,7,119155,119162,4,119362,119364,5,121476,121476,5,122888,122904,5,123184,123190,5,125252,125258,5,127183,127183,14,127340,127343,14,127377,127386,14,127491,127503,14,127548,127551,14,127744,127756,14,127761,127761,14,127769,127769,14,127773,127774,14,127780,127788,14,127796,127797,14,127820,127823,14,127869,127869,14,127894,127895,14,127902,127903,14,127943,127943,14,127947,127950,14,127972,127972,14,127988,127988,14,127992,127994,14,128009,128011,14,128019,128019,14,128023,128041,14,128064,128064,14,128102,128107,14,128174,128181,14,128238,128238,14,128246,128247,14,128254,128254,14,128264,128264,14,128278,128299,14,128329,128330,14,128348,128359,14,128371,128377,14,128392,128393,14,128401,128404,14,128421,128421,14,128433,128434,14,128450,128452,14,128476,128478,14,128483,128483,14,128495,128495,14,128506,128506,14,128519,128520,14,128528,128528,14,128534,128534,14,128538,128538,14,128540,128542,14,128544,128549,14,128552,128555,14,128557,128557,14,128560,128563,14,128565,128565,14,128567,128576,14,128581,128591,14,128641,128642,14,128646,128646,14,128648,128648,14,128650,128651,14,128653,128653,14,128655,128655,14,128657,128659,14,128661,128661,14,128663,128663,14,128665,128666,14,128674,128674,14,128676,128677,14,128679,128685,14,128690,128690,14,128694,128694,14,128697,128702,14,128704,128704,14,128710,128714,14,128716,128716,14,128720,128720,14,128723,128724,14,128726,128727,14,128733,128735,14,128742,128744,14,128746,128746,14,128749,128751,14,128753,128754,14,128756,128758,14,128761,128761,14,128763,128764,14,128884,128895,14,128992,129003,14,129008,129008,14,129036,129039,14,129114,129119,14,129198,129279,14,129293,129295,14,129305,129310,14,129312,129319,14,129328,129328,14,129331,129338,14,129343,129343,14,129351,129355,14,129357,129359,14,129375,129387,14,129393,129393,14,129395,129398,14,129401,129401,14,129403,129403,14,129408,129412,14,129426,129431,14,129443,129444,14,129451,129453,14,129456,129465,14,129472,129472,14,129475,129482,14,129484,129484,14,129488,129510,14,129536,129647,14,129652,129652,14,129656,129658,14,129661,129663,14,129667,129670,14,129680,129685,14,129705,129708,14,129712,129718,14,129723,129727,14,129731,129733,14,129744,129750,14,129754,129759,14,129768,129775,14,129783,129791,14,917504,917504,4,917506,917535,4,917632,917759,4,918000,921599,4,0,9,4,11,12,4,14,31,4,169,169,14,174,174,14,1155,1159,5,1425,1469,5,1473,1474,5,1479,1479,5,1552,1562,5,1611,1631,5,1750,1756,5,1759,1764,5,1770,1773,5,1809,1809,5,1958,1968,5,2045,2045,5,2075,2083,5,2089,2093,5,2192,2193,1,2250,2273,5,2275,2306,5,2362,2362,5,2364,2364,5,2369,2376,5,2381,2381,5,2385,2391,5,2433,2433,5,2492,2492,5,2495,2496,7,2503,2504,7,2509,2509,5,2530,2531,5,2561,2562,5,2620,2620,5,2625,2626,5,2635,2637,5,2672,2673,5,2689,2690,5,2748,2748,5,2753,2757,5,2761,2761,7,2765,2765,5,2810,2815,5,2818,2819,7,2878,2878,5,2880,2880,7,2887,2888,7,2893,2893,5,2903,2903,5,2946,2946,5,3007,3007,7,3009,3010,7,3018,3020,7,3031,3031,5,3073,3075,7,3132,3132,5,3137,3140,7,3146,3149,5,3170,3171,5,3202,3203,7,3262,3262,7,3264,3265,7,3267,3268,7,3271,3272,7,3276,3277,5,3298,3299,5,3330,3331,7,3390,3390,5,3393,3396,5,3402,3404,7,3406,3406,1,3426,3427,5,3458,3459,7,3535,3535,5,3538,3540,5,3544,3550,7,3570,3571,7,3635,3635,7,3655,3662,5,3763,3763,7,3784,3789,5,3893,3893,5,3897,3897,5,3953,3966,5,3968,3972,5,3981,3991,5,4038,4038,5,4145,4145,7,4153,4154,5,4157,4158,5,4184,4185,5,4209,4212,5,4228,4228,7,4237,4237,5,4352,4447,8,4520,4607,10,5906,5908,5,5938,5939,5,5970,5971,5,6068,6069,5,6071,6077,5,6086,6086,5,6089,6099,5,6155,6157,5,6159,6159,5,6313,6313,5,6435,6438,7,6441,6443,7,6450,6450,5,6457,6459,5,6681,6682,7,6741,6741,7,6743,6743,7,6752,6752,5,6757,6764,5,6771,6780,5,6832,6845,5,6847,6862,5,6916,6916,7,6965,6965,5,6971,6971,7,6973,6977,7,6979,6980,7,7040,7041,5,7073,7073,7,7078,7079,7,7082,7082,7,7142,7142,5,7144,7145,5,7149,7149,5,7151,7153,5,7204,7211,7,7220,7221,7,7376,7378,5,7393,7393,7,7405,7405,5,7415,7415,7,7616,7679,5,8204,8204,5,8206,8207,4,8233,8233,4,8252,8252,14,8288,8292,4,8294,8303,4,8413,8416,5,8418,8420,5,8482,8482,14,8596,8601,14,8986,8987,14,9096,9096,14,9193,9196,14,9199,9199,14,9201,9202,14,9208,9210,14,9642,9643,14,9664,9664,14,9728,9729,14,9732,9732,14,9735,9741,14,9743,9744,14,9746,9746,14,9750,9751,14,9753,9756,14,9758,9759,14,9761,9761,14,9764,9765,14,9767,9769,14,9771,9773,14,9775,9775,14,9784,9785,14,9787,9791,14,9793,9793,14,9795,9799,14,9812,9822,14,9824,9824,14,9827,9827,14,9829,9830,14,9832,9832,14,9851,9851,14,9854,9854,14,9856,9861,14,9874,9874,14,9876,9876,14,9878,9879,14,9881,9881,14,9883,9884,14,9888,9889,14,9895,9895,14,9898,9899,14,9904,9905,14,9917,9918,14,9924,9925,14,9928,9928,14,9934,9934,14,9936,9936,14,9938,9938,14,9940,9940,14,9961,9961,14,9963,9967,14,9970,9971,14,9973,9973,14,9975,9977,14,9979,9980,14,9982,9985,14,9987,9988,14,9992,9996,14,9998,9998,14,10000,10001,14,10004,10004,14,10013,10013,14,10024,10024,14,10052,10052,14,10060,10060,14,10067,10069,14,10083,10083,14,10085,10087,14,10145,10145,14,10175,10175,14,11013,11015,14,11088,11088,14,11503,11505,5,11744,11775,5,12334,12335,5,12349,12349,14,12951,12951,14,42607,42607,5,42612,42621,5,42736,42737,5,43014,43014,5,43043,43044,7,43047,43047,7,43136,43137,7,43204,43205,5,43263,43263,5,43335,43345,5,43360,43388,8,43395,43395,7,43444,43445,7,43450,43451,7,43454,43456,7,43561,43566,5,43569,43570,5,43573,43574,5,43596,43596,5,43644,43644,5,43698,43700,5,43710,43711,5,43755,43755,7,43758,43759,7,43766,43766,5,44005,44005,5,44008,44008,5,44012,44012,7,44032,44032,11,44060,44060,11,44088,44088,11,44116,44116,11,44144,44144,11,44172,44172,11,44200,44200,11,44228,44228,11,44256,44256,11,44284,44284,11,44312,44312,11,44340,44340,11,44368,44368,11,44396,44396,11,44424,44424,11,44452,44452,11,44480,44480,11,44508,44508,11,44536,44536,11,44564,44564,11,44592,44592,11,44620,44620,11,44648,44648,11,44676,44676,11,44704,44704,11,44732,44732,11,44760,44760,11,44788,44788,11,44816,44816,11,44844,44844,11,44872,44872,11,44900,44900,11,44928,44928,11,44956,44956,11,44984,44984,11,45012,45012,11,45040,45040,11,45068,45068,11,45096,45096,11,45124,45124,11,45152,45152,11,45180,45180,11,45208,45208,11,45236,45236,11,45264,45264,11,45292,45292,11,45320,45320,11,45348,45348,11,45376,45376,11,45404,45404,11,45432,45432,11,45460,45460,11,45488,45488,11,45516,45516,11,45544,45544,11,45572,45572,11,45600,45600,11,45628,45628,11,45656,45656,11,45684,45684,11,45712,45712,11,45740,45740,11,45768,45768,11,45796,45796,11,45824,45824,11,45852,45852,11,45880,45880,11,45908,45908,11,45936,45936,11,45964,45964,11,45992,45992,11,46020,46020,11,46048,46048,11,46076,46076,11,46104,46104,11,46132,46132,11,46160,46160,11,46188,46188,11,46216,46216,11,46244,46244,11,46272,46272,11,46300,46300,11,46328,46328,11,46356,46356,11,46384,46384,11,46412,46412,11,46440,46440,11,46468,46468,11,46496,46496,11,46524,46524,11,46552,46552,11,46580,46580,11,46608,46608,11,46636,46636,11,46664,46664,11,46692,46692,11,46720,46720,11,46748,46748,11,46776,46776,11,46804,46804,11,46832,46832,11,46860,46860,11,46888,46888,11,46916,46916,11,46944,46944,11,46972,46972,11,47000,47000,11,47028,47028,11,47056,47056,11,47084,47084,11,47112,47112,11,47140,47140,11,47168,47168,11,47196,47196,11,47224,47224,11,47252,47252,11,47280,47280,11,47308,47308,11,47336,47336,11,47364,47364,11,47392,47392,11,47420,47420,11,47448,47448,11,47476,47476,11,47504,47504,11,47532,47532,11,47560,47560,11,47588,47588,11,47616,47616,11,47644,47644,11,47672,47672,11,47700,47700,11,47728,47728,11,47756,47756,11,47784,47784,11,47812,47812,11,47840,47840,11,47868,47868,11,47896,47896,11,47924,47924,11,47952,47952,11,47980,47980,11,48008,48008,11,48036,48036,11,48064,48064,11,48092,48092,11,48120,48120,11,48148,48148,11,48176,48176,11,48204,48204,11,48232,48232,11,48260,48260,11,48288,48288,11,48316,48316,11,48344,48344,11,48372,48372,11,48400,48400,11,48428,48428,11,48456,48456,11,48484,48484,11,48512,48512,11,48540,48540,11,48568,48568,11,48596,48596,11,48624,48624,11,48652,48652,11,48680,48680,11,48708,48708,11,48736,48736,11,48764,48764,11,48792,48792,11,48820,48820,11,48848,48848,11,48876,48876,11,48904,48904,11,48932,48932,11,48960,48960,11,48988,48988,11,49016,49016,11,49044,49044,11,49072,49072,11,49100,49100,11,49128,49128,11,49156,49156,11,49184,49184,11,49212,49212,11,49240,49240,11,49268,49268,11,49296,49296,11,49324,49324,11,49352,49352,11,49380,49380,11,49408,49408,11,49436,49436,11,49464,49464,11,49492,49492,11,49520,49520,11,49548,49548,11,49576,49576,11,49604,49604,11,49632,49632,11,49660,49660,11,49688,49688,11,49716,49716,11,49744,49744,11,49772,49772,11,49800,49800,11,49828,49828,11,49856,49856,11,49884,49884,11,49912,49912,11,49940,49940,11,49968,49968,11,49996,49996,11,50024,50024,11,50052,50052,11,50080,50080,11,50108,50108,11,50136,50136,11,50164,50164,11,50192,50192,11,50220,50220,11,50248,50248,11,50276,50276,11,50304,50304,11,50332,50332,11,50360,50360,11,50388,50388,11,50416,50416,11,50444,50444,11,50472,50472,11,50500,50500,11,50528,50528,11,50556,50556,11,50584,50584,11,50612,50612,11,50640,50640,11,50668,50668,11,50696,50696,11,50724,50724,11,50752,50752,11,50780,50780,11,50808,50808,11,50836,50836,11,50864,50864,11,50892,50892,11,50920,50920,11,50948,50948,11,50976,50976,11,51004,51004,11,51032,51032,11,51060,51060,11,51088,51088,11,51116,51116,11,51144,51144,11,51172,51172,11,51200,51200,11,51228,51228,11,51256,51256,11,51284,51284,11,51312,51312,11,51340,51340,11,51368,51368,11,51396,51396,11,51424,51424,11,51452,51452,11,51480,51480,11,51508,51508,11,51536,51536,11,51564,51564,11,51592,51592,11,51620,51620,11,51648,51648,11,51676,51676,11,51704,51704,11,51732,51732,11,51760,51760,11,51788,51788,11,51816,51816,11,51844,51844,11,51872,51872,11,51900,51900,11,51928,51928,11,51956,51956,11,51984,51984,11,52012,52012,11,52040,52040,11,52068,52068,11,52096,52096,11,52124,52124,11,52152,52152,11,52180,52180,11,52208,52208,11,52236,52236,11,52264,52264,11,52292,52292,11,52320,52320,11,52348,52348,11,52376,52376,11,52404,52404,11,52432,52432,11,52460,52460,11,52488,52488,11,52516,52516,11,52544,52544,11,52572,52572,11,52600,52600,11,52628,52628,11,52656,52656,11,52684,52684,11,52712,52712,11,52740,52740,11,52768,52768,11,52796,52796,11,52824,52824,11,52852,52852,11,52880,52880,11,52908,52908,11,52936,52936,11,52964,52964,11,52992,52992,11,53020,53020,11,53048,53048,11,53076,53076,11,53104,53104,11,53132,53132,11,53160,53160,11,53188,53188,11,53216,53216,11,53244,53244,11,53272,53272,11,53300,53300,11,53328,53328,11,53356,53356,11,53384,53384,11,53412,53412,11,53440,53440,11,53468,53468,11,53496,53496,11,53524,53524,11,53552,53552,11,53580,53580,11,53608,53608,11,53636,53636,11,53664,53664,11,53692,53692,11,53720,53720,11,53748,53748,11,53776,53776,11,53804,53804,11,53832,53832,11,53860,53860,11,53888,53888,11,53916,53916,11,53944,53944,11,53972,53972,11,54000,54000,11,54028,54028,11,54056,54056,11,54084,54084,11,54112,54112,11,54140,54140,11,54168,54168,11,54196,54196,11,54224,54224,11,54252,54252,11,54280,54280,11,54308,54308,11,54336,54336,11,54364,54364,11,54392,54392,11,54420,54420,11,54448,54448,11,54476,54476,11,54504,54504,11,54532,54532,11,54560,54560,11,54588,54588,11,54616,54616,11,54644,54644,11,54672,54672,11,54700,54700,11,54728,54728,11,54756,54756,11,54784,54784,11,54812,54812,11,54840,54840,11,54868,54868,11,54896,54896,11,54924,54924,11,54952,54952,11,54980,54980,11,55008,55008,11,55036,55036,11,55064,55064,11,55092,55092,11,55120,55120,11,55148,55148,11,55176,55176,11,55216,55238,9,64286,64286,5,65056,65071,5,65438,65439,5,65529,65531,4,66272,66272,5,68097,68099,5,68108,68111,5,68159,68159,5,68900,68903,5,69446,69456,5,69632,69632,7,69634,69634,7,69744,69744,5,69759,69761,5,69808,69810,7,69815,69816,7,69821,69821,1,69837,69837,1,69927,69931,5,69933,69940,5,70003,70003,5,70018,70018,7,70070,70078,5,70082,70083,1,70094,70094,7,70188,70190,7,70194,70195,7,70197,70197,7,70206,70206,5,70368,70370,7,70400,70401,5,70459,70460,5,70463,70463,7,70465,70468,7,70475,70477,7,70498,70499,7,70512,70516,5,70712,70719,5,70722,70724,5,70726,70726,5,70832,70832,5,70835,70840,5,70842,70842,5,70845,70845,5,70847,70848,5,70850,70851,5,71088,71089,7,71096,71099,7,71102,71102,7,71132,71133,5,71219,71226,5,71229,71229,5,71231,71232,5,71340,71340,7,71342,71343,7,71350,71350,7,71453,71455,5,71462,71462,7,71724,71726,7,71736,71736,7,71984,71984,5,71991,71992,7,71997,71997,7,71999,71999,1,72001,72001,1,72003,72003,5,72148,72151,5,72156,72159,7,72164,72164,7,72243,72248,5,72250,72250,1,72263,72263,5,72279,72280,7,72324,72329,1,72343,72343,7,72751,72751,7,72760,72765,5,72767,72767,5,72873,72873,7,72881,72881,7,72884,72884,7,73009,73014,5,73020,73021,5,73030,73030,1,73098,73102,7,73107,73108,7,73110,73110,7,73459,73460,5,78896,78904,4,92976,92982,5,94033,94087,7,94180,94180,5,113821,113822,5,118528,118573,5,119141,119141,5,119143,119145,5,119150,119154,5,119163,119170,5,119210,119213,5,121344,121398,5,121461,121461,5,121499,121503,5,122880,122886,5,122907,122913,5,122918,122922,5,123566,123566,5,125136,125142,5,126976,126979,14,126981,127182,14,127184,127231,14,127279,127279,14,127344,127345,14,127374,127374,14,127405,127461,14,127489,127490,14,127514,127514,14,127538,127546,14,127561,127567,14,127570,127743,14,127757,127758,14,127760,127760,14,127762,127762,14,127766,127768,14,127770,127770,14,127772,127772,14,127775,127776,14,127778,127779,14,127789,127791,14,127794,127795,14,127798,127798,14,127819,127819,14,127824,127824,14,127868,127868,14,127870,127871,14,127892,127893,14,127896,127896,14,127900,127901,14,127904,127940,14,127942,127942,14,127944,127944,14,127946,127946,14,127951,127955,14,127968,127971,14,127973,127984,14,127987,127987,14,127989,127989,14,127991,127991,14,127995,127999,5,128008,128008,14,128012,128014,14,128017,128018,14,128020,128020,14,128022,128022,14,128042,128042,14,128063,128063,14,128065,128065,14,128101,128101,14,128108,128109,14,128173,128173,14,128182,128183,14,128236,128237,14,128239,128239,14,128245,128245,14,128248,128248,14,128253,128253,14,128255,128258,14,128260,128263,14,128265,128265,14,128277,128277,14,128300,128301,14,128326,128328,14,128331,128334,14,128336,128347,14,128360,128366,14,128369,128370,14,128378,128378,14,128391,128391,14,128394,128397,14,128400,128400,14,128405,128406,14,128420,128420,14,128422,128423,14,128425,128432,14,128435,128443,14,128445,128449,14,128453,128464,14,128468,128475,14,128479,128480,14,128482,128482,14,128484,128487,14,128489,128494,14,128496,128498,14,128500,128505,14,128507,128511,14,128513,128518,14,128521,128525,14,128527,128527,14,128529,128529,14,128533,128533,14,128535,128535,14,128537,128537,14]'); } //#endregion From cae6e662ce0c65cb812a93f9ccad42521c74e443 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 21:01:29 +0100 Subject: [PATCH 297/330] Reduce usage of `allow-any-unicode-next-line` --- extensions/git/src/timelineProvider.ts | 9 +++----- .../src/utils/previewer.ts | 6 ++--- src/vs/base/common/keybindingLabels.ts | 3 +-- .../browser/controller/textAreaState.ts | 3 +-- .../browser/workbench.contribution.ts | 3 +-- .../debug/browser/debugActionViewItems.ts | 3 +-- .../extensions/browser/extensionEditor.ts | 6 ++--- .../contrib/output/browser/outputView.ts | 3 +-- .../preferences/browser/settingsEditor2.ts | 3 +-- .../preferences/browser/settingsTreeModels.ts | 3 +-- .../terminal/browser/terminalActions.ts | 6 ++--- .../browser/explorerProjections/display.ts | 3 +-- .../common/macLinuxKeyboardMapper.ts | 22 ++++++++----------- .../services/search/common/getFileResults.ts | 3 +-- 14 files changed, 27 insertions(+), 49 deletions(-) diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index e43a2a0b84f..f6e543257e2 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -166,8 +166,7 @@ export class GitTimelineProvider implements TimelineProvider { if (showAuthor) { item.description = c.authorName; } - // allow-any-unicode-next-line - item.detail = `${c.authorName} (${c.authorEmail}) — ${c.hash.substr(0, 8)}\n${dateFormatter.format(date)}\n\n${message}`; + item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.format(date)}\n\n${message}`; const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -192,8 +191,7 @@ export class GitTimelineProvider implements TimelineProvider { // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new ThemeIcon('git-commit'); item.description = ''; - // allow-any-unicode-next-line - item.detail = localize('git.timeline.detail', '{0} — {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format(date), Resource.getStatusText(index.type)); + item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format(date), Resource.getStatusText(index.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -215,8 +213,7 @@ export class GitTimelineProvider implements TimelineProvider { // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new ThemeIcon('git-commit'); item.description = ''; - // allow-any-unicode-next-line - item.detail = localize('git.timeline.detail', '{0} — {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format(date), Resource.getStatusText(working.type)); + item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format(date), Resource.getStatusText(working.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { diff --git a/extensions/typescript-language-features/src/utils/previewer.ts b/extensions/typescript-language-features/src/utils/previewer.ts index a443efba037..c6d1c942050 100644 --- a/extensions/typescript-language-features/src/utils/previewer.ts +++ b/extensions/typescript-language-features/src/utils/previewer.ts @@ -90,8 +90,7 @@ function getTagDocumentation( if (!doc) { return label; } - // allow-any-unicode-next-line - return label + (doc.match(/\r\n|\n/g) ? ' \n' + processInlineTags(doc) : ` — ${processInlineTags(doc)}`); + return label + (doc.match(/\r\n|\n/g) ? ' \n' + processInlineTags(doc) : ` \u2014 ${processInlineTags(doc)}`); } } @@ -101,8 +100,7 @@ function getTagDocumentation( if (!text) { return label; } - // allow-any-unicode-next-line - return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` — ${text}`); + return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` \u2014 ${text}`); } export function plainWithLinks( diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index 0c2f1a93468..62e905a4bb4 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -54,8 +54,7 @@ export class ModifierLabelProvider { */ export const UILabelProvider = new ModifierLabelProvider( { - // allow-any-unicode-next-line - ctrlKey: '⌃', + ctrlKey: '\u2303', shiftKey: '⇧', altKey: '⌥', metaKey: '⌘', diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts index 4c240bfd378..cf8846dbc02 100644 --- a/src/vs/editor/browser/controller/textAreaState.ts +++ b/src/vs/editor/browser/controller/textAreaState.ts @@ -172,8 +172,7 @@ export class TextAreaState { if (potentialEmojiInput !== null && potentialEmojiInput.length > 0) { // now we check that this is indeed an emoji // emojis can grow quite long, so a length check is of no help - // allow-any-unicode-next-line - // e.g. 1F3F4 E0067 E0062 E0065 E006E E0067 E007F ; fully-qualified # 🏴󠁧󠁢󠁥󠁮󠁧󠁿 England + // e.g. 1F3F4 E0067 E0062 E0065 E006E E0067 E007F -- flag of England // Oftentimes, emojis use Variation Selector-16 (U+FE0F), so that is a good hint // http://emojipedia.org/variation-selector-16/ diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index bf60658ac0c..7dbe26ca935 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -421,8 +421,7 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'window.titleSeparator': { 'type': 'string', - // allow-any-unicode-next-line - 'default': isMacintosh ? ' — ' : ' - ', + 'default': isMacintosh ? ' \u2014 ' : ' - ', 'markdownDescription': localize("window.titleSeparator", "Separator used by `window.title`.") }, 'window.menuBarVisibility': { diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index 310e7549105..b68d7d7036c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -27,8 +27,7 @@ const $ = dom.$; export class StartDebugActionViewItem extends BaseActionViewItem { - // allow-any-unicode-next-line - private static readonly SEPARATOR = '─────────'; + private static readonly SEPARATOR = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; private container!: HTMLElement; private start!: HTMLElement; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index e3712271e12..b2d08fc2d68 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -1550,10 +1550,8 @@ export class ExtensionEditor extends EditorPane { $('td', undefined, l.id), $('td', undefined, l.name), $('td', undefined, ...join(l.extensions.map(ext => $('code', undefined, ext)), ' ')), - // allow-any-unicode-next-line - $('td', undefined, document.createTextNode(l.hasGrammar ? '✔︎' : '—')), - // allow-any-unicode-next-line - $('td', undefined, document.createTextNode(l.hasSnippets ? '✔︎' : '—')) + $('td', undefined, document.createTextNode(l.hasGrammar ? '✔︎' : '\u2014')), + $('td', undefined, document.createTextNode(l.hasSnippets ? '✔︎' : '\u2014')) )) ) ); diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 9db7a355a31..3ebd3590841 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -271,8 +271,7 @@ export class OutputEditor extends AbstractTextResourceEditor { class SwitchOutputActionViewItem extends SelectActionViewItem { - // allow-any-unicode-next-line - private static readonly SEPARATOR = '─────────'; + private static readonly SEPARATOR = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; private outputChannels: IOutputChannelDescriptor[] = []; private logChannels: IOutputChannelDescriptor[] = []; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index f12bf278726..21ab9a92d6e 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -1181,8 +1181,7 @@ export class SettingsEditor2 extends EditorPane { const query = this.searchWidget.getValue().trim(); this.delayedFilterLogging.cancel(); - // allow-any-unicode-next-line - await this.triggerSearch(query.replace(/›/g, ' ')); + await this.triggerSearch(query.replace(/\u203A/g, ' ')); if (query && this.searchResultModel) { this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResultModel!.getUniqueResults())); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index f70a5afeff2..ea6967e593d 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -521,8 +521,7 @@ export function settingKeyToDisplayFormat(key: string, groupId = ''): { category function wordifyKey(key: string): string { key = key - // allow-any-unicode-next-line - .replace(/\.([a-z0-9])/g, (_, p1) => ` › ${p1.toUpperCase()}`) // Replace dot with spaced '>' + .replace(/\.([a-z0-9])/g, (_, p1) => ` \u203A ${p1.toUpperCase()}`) // Replace dot with spaced '>' .replace(/([a-z0-9])([A-Z])/g, '$1 $2') // Camel case to spacing, fooBar => foo Bar .replace(/^[a-z]/g, match => match.toUpperCase()) // Upper casing all first letters, foo => Foo .replace(/\b\w+\b/g, match => { // Upper casing known acronyms diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index c292ceeb876..32699b858aa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -50,8 +50,7 @@ import { ITerminalQuickPickItem } from 'vs/workbench/contrib/terminal/browser/te import { IThemeService } from 'vs/platform/theme/common/themeService'; import { getIconId, getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; -// allow-any-unicode-next-line -export const switchTerminalActionViewItemSeparator = '─────────'; +export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; export const switchTerminalShowTabsTitle = localize('showTerminalTabs', "Show Tabs"); async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { @@ -988,8 +987,7 @@ export function registerTerminalActions() { const cwdLabel = labelService.getUriLabel(URI.file(term.cwd)); return { label: term.title, - // allow-any-unicode-next-line - detail: term.workspaceName ? `${term.workspaceName} ⸱ ${cwdLabel}` : cwdLabel, + detail: term.workspaceName ? `${term.workspaceName} \u2E31 ${cwdLabel}` : cwdLabel, description: term.pid ? String(term.pid) : '', term }; diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/display.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/display.ts index c26642329d4..548ee8b1da8 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/display.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/display.ts @@ -3,5 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// allow-any-unicode-next-line -export const flatTestItemDelimiter = ' › '; +export const flatTestItemDelimiter = ' \u203A '; diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts index 988566b13e7..06df5b9e20a 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -1022,19 +1022,15 @@ export class MacLinuxKeyboardMapper implements IKeyboardMapper { private static _redirectCharCode(charCode: number): number { switch (charCode) { // allow-any-unicode-next-line - case CharCode.U_IDEOGRAPHIC_FULL_STOP: return CharCode.Period; // CJK 。 => . - // allow-any-unicode-next-line - case CharCode.U_LEFT_CORNER_BRACKET: return CharCode.OpenSquareBracket; // CJK 「 => [ - // allow-any-unicode-next-line - case CharCode.U_RIGHT_CORNER_BRACKET: return CharCode.CloseSquareBracket; // CJK 」 => ] - // allow-any-unicode-next-line - case CharCode.U_LEFT_BLACK_LENTICULAR_BRACKET: return CharCode.OpenSquareBracket; // CJK 【 => [ - // allow-any-unicode-next-line - case CharCode.U_RIGHT_BLACK_LENTICULAR_BRACKET: return CharCode.CloseSquareBracket; // CJK 】 => ] - // allow-any-unicode-next-line - case CharCode.U_FULLWIDTH_SEMICOLON: return CharCode.Semicolon; // CJK ; => ; - // allow-any-unicode-next-line - case CharCode.U_FULLWIDTH_COMMA: return CharCode.Comma; // CJK , => , + // CJK: 。 「 」 【 】 ; , + // map: . [ ] [ ] ; , + case CharCode.U_IDEOGRAPHIC_FULL_STOP: return CharCode.Period; + case CharCode.U_LEFT_CORNER_BRACKET: return CharCode.OpenSquareBracket; + case CharCode.U_RIGHT_CORNER_BRACKET: return CharCode.CloseSquareBracket; + case CharCode.U_LEFT_BLACK_LENTICULAR_BRACKET: return CharCode.OpenSquareBracket; + case CharCode.U_RIGHT_BLACK_LENTICULAR_BRACKET: return CharCode.CloseSquareBracket; + case CharCode.U_FULLWIDTH_SEMICOLON: return CharCode.Semicolon; + case CharCode.U_FULLWIDTH_COMMA: return CharCode.Comma; } return charCode; } diff --git a/src/vs/workbench/services/search/common/getFileResults.ts b/src/vs/workbench/services/search/common/getFileResults.ts index 91fe2623dfd..f2e81f913dd 100644 --- a/src/vs/workbench/services/search/common/getFileResults.ts +++ b/src/vs/workbench/services/search/common/getFileResults.ts @@ -25,8 +25,7 @@ export const getFileResults = ( text = new TextDecoder('utf-16be').decode(bytes); } else { text = new TextDecoder('utf8').decode(bytes); - // allow-any-unicode-next-line - if (text.slice(0, 1000).includes('�') && bytes.includes(0)) { + if (text.slice(0, 1000).includes('\uFFFD') && bytes.includes(0)) { return []; } } From 94d07f18eaa025f58253f2f22e87c01ef8ec00cb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 22:03:07 +0100 Subject: [PATCH 298/330] Add comments and rename variables --- .../common/controller/cursorTypeOperations.ts | 94 +++++++++++-------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 09dbf3671cf..72df1f3335d 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -502,53 +502,63 @@ export class TypeOperations { return !isBeforeStartingBrace && isBeforeClosingBrace; } + /** + * Determine if typing `ch` at all `positions` in the `model` results in an + * auto closing open sequence being typed. + * + * Auto closing open sequences can consist of multiple characters, which + * can lead to ambiguities. In such a case, the longest auto-closing open + * sequence is returned. + */ private static _findAutoClosingPairOpen(config: CursorConfiguration, model: ITextModel, positions: Position[], ch: string): StandardAutoClosingPairConditional | null { - const autoClosingPairCandidates = config.autoClosingPairs.autoClosingPairsOpenByEnd.get(ch); - if (!autoClosingPairCandidates) { + const candidates = config.autoClosingPairs.autoClosingPairsOpenByEnd.get(ch); + if (!candidates) { return null; } // Determine which auto-closing pair it is - let autoClosingPair: StandardAutoClosingPairConditional | null = null; - for (const autoClosingPairCandidate of autoClosingPairCandidates) { - if (autoClosingPair === null || autoClosingPairCandidate.open.length > autoClosingPair.open.length) { + let result: StandardAutoClosingPairConditional | null = null; + for (const candidate of candidates) { + if (result === null || candidate.open.length > result.open.length) { let candidateIsMatch = true; for (const position of positions) { - const relevantText = model.getValueInRange(new Range(position.lineNumber, position.column - autoClosingPairCandidate.open.length + 1, position.lineNumber, position.column)); - if (relevantText + ch !== autoClosingPairCandidate.open) { + const relevantText = model.getValueInRange(new Range(position.lineNumber, position.column - candidate.open.length + 1, position.lineNumber, position.column)); + if (relevantText + ch !== candidate.open) { candidateIsMatch = false; break; } } if (candidateIsMatch) { - autoClosingPair = autoClosingPairCandidate; + result = candidate; } } } - return autoClosingPair; + return result; } - private static _findSubAutoClosingPairClose(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional): string { - if (autoClosingPair.open.length <= 1) { - return ''; + /** + * Find another auto-closing pair that is contained by the one passed in. + * + * e.g. when having [(,)] and [(*,*)] as auto-closing pairs + * this method will find [(,)] as a containment pair for [(*,*)] + */ + private static _findContainedAutoClosingPair(config: CursorConfiguration, pair: StandardAutoClosingPairConditional): StandardAutoClosingPairConditional | null { + if (pair.open.length <= 1) { + return null; } - const lastChar = autoClosingPair.close.charAt(autoClosingPair.close.length - 1); + const lastChar = pair.close.charAt(pair.close.length - 1); // get candidates with the same last character as close - const subPairCandidates = config.autoClosingPairs.autoClosingPairsCloseByEnd.get(lastChar) || []; - let subPairMatch: StandardAutoClosingPairConditional | null = null; - for (const x of subPairCandidates) { - if (x.open !== autoClosingPair.open && autoClosingPair.open.includes(x.open) && autoClosingPair.close.endsWith(x.close)) { - if (!subPairMatch || x.open.length > subPairMatch.open.length) { - subPairMatch = x; + const candidates = config.autoClosingPairs.autoClosingPairsCloseByEnd.get(lastChar) || []; + let result: StandardAutoClosingPairConditional | null = null; + for (const candidate of candidates) { + if (candidate.open !== pair.open && pair.open.includes(candidate.open) && pair.close.endsWith(candidate.close)) { + if (!result || candidate.open.length > result.open.length) { + result = candidate; } } } - if (subPairMatch) { - return subPairMatch.close; - } else { - return ''; - } + return result; } private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): string | null { @@ -558,18 +568,24 @@ export class TypeOperations { return null; } - const autoClosingPair = this._findAutoClosingPairOpen(config, model, selections.map(s => s.getPosition()), ch); - if (!autoClosingPair) { + // Find the longest auto-closing open pair in case of multiple ending in `ch` + // e.g. when having [f","] and [","], it picks [f","] if the character before is f + const pair = this._findAutoClosingPairOpen(config, model, selections.map(s => s.getPosition()), ch); + if (!pair) { return null; } - const subAutoClosingPairClose = this._findSubAutoClosingPairClose(config, autoClosingPair); - let isSubAutoClosingPairPresent = true; + // Sometimes, it is possible to have two auto-closing pairs that have a containment relationship + // e.g. when having [(,)] and [(*,*)] + // - when typing (, the resulting state is (|) + // - when typing *, the desired resulting state is (*|*), not (*|*)) + const containedPair = this._findContainedAutoClosingPair(config, pair); + const containedPairClose = containedPair ? containedPair.close : ''; + let isContainedPairPresent = true; const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; - for (let i = 0, len = selections.length; i < len; i++) { - const selection = selections[i]; + for (const selection of selections) { if (!selection.isEmpty()) { return null; } @@ -578,13 +594,13 @@ export class TypeOperations { const lineText = model.getLineContent(position.lineNumber); const lineAfter = lineText.substring(position.column - 1); - if (!lineAfter.startsWith(subAutoClosingPairClose)) { - isSubAutoClosingPairPresent = false; + if (!lineAfter.startsWith(containedPairClose)) { + isContainedPairPresent = false; } // Only consider auto closing the pair if an allowed character follows or if another autoclosed pair closing brace follows - if (lineText.length > position.column - 1) { - const characterAfter = lineText.charAt(position.column - 1); + if (lineAfter.length > 0) { + const characterAfter = lineAfter.charAt(0); const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, lineAfter); if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { @@ -598,7 +614,7 @@ export class TypeOperations { } // Do not auto-close ' or " after a word character - if (autoClosingPair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') { + if (pair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') { const wordSeparators = getMapForWordSeparators(config.wordSeparators); if (insertOpenCharacter && position.column > 1 && wordSeparators.get(lineText.charCodeAt(position.column - 2)) === WordCharacterClass.Regular) { return null; @@ -613,7 +629,7 @@ export class TypeOperations { let shouldAutoClosePair = false; try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(autoClosingPair, lineTokens, insertOpenCharacter ? position.column : position.column - 1); + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(pair, lineTokens, insertOpenCharacter ? position.column : position.column - 1); } catch (e) { onUnexpectedError(e); } @@ -623,10 +639,10 @@ export class TypeOperations { } } - if (isSubAutoClosingPairPresent) { - return autoClosingPair.close.substring(0, autoClosingPair.close.length - subAutoClosingPairClose.length); + if (isContainedPairPresent) { + return pair.close.substring(0, pair.close.length - containedPairClose.length); } else { - return autoClosingPair.close; + return pair.close; } } From 97aa083b9391ba49e1bad6e45f41262e93435e26 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 22:24:05 +0100 Subject: [PATCH 299/330] Simplify logic dealing with the case when the character is already typed (the composition end case) --- .../common/controller/cursorTypeOperations.ts | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 72df1f3335d..979b39de5fb 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -561,16 +561,39 @@ export class TypeOperations { return result; } - private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): string | null { + private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean): string | null { const chIsQuote = isQuote(ch); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (autoCloseConfig === 'never') { return null; } + for (const selection of selections) { + if (!selection.isEmpty()) { + return null; + } + } + + // This method is called both when typing (regularly) and when composition ends + // This means that we need to work with a text buffer where sometimes `ch` is not + // there (it is being typed right now) or with a text buffer where `ch` has already been typed + // + // In order to avoid adding checks for `chIsAlreadyTyped` in all places, we will work + // with two conceptual positions, the position before `ch` and the position after `ch` + // + const positions: { lineNumber: number; beforeColumn: number; afterColumn: number; }[] = selections.map((s) => { + const position = s.getPosition(); + if (chIsAlreadyTyped) { + return { lineNumber: position.lineNumber, beforeColumn: position.column - ch.length, afterColumn: position.column }; + } else { + return { lineNumber: position.lineNumber, beforeColumn: position.column, afterColumn: position.column }; + } + }); + + // Find the longest auto-closing open pair in case of multiple ending in `ch` // e.g. when having [f","] and [","], it picks [f","] if the character before is f - const pair = this._findAutoClosingPairOpen(config, model, selections.map(s => s.getPosition()), ch); + const pair = this._findAutoClosingPairOpen(config, model, positions.map(p => new Position(p.lineNumber, p.beforeColumn)), ch); if (!pair) { return null; } @@ -585,14 +608,11 @@ export class TypeOperations { const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; - for (const selection of selections) { - if (!selection.isEmpty()) { - return null; - } - - const position = selection.getPosition(); - const lineText = model.getLineContent(position.lineNumber); - const lineAfter = lineText.substring(position.column - 1); + for (const position of positions) { + const { lineNumber, beforeColumn, afterColumn } = position; + const lineText = model.getLineContent(lineNumber); + const lineBefore = lineText.substring(0, beforeColumn - 1); + const lineAfter = lineText.substring(afterColumn - 1); if (!lineAfter.startsWith(containedPairClose)) { isContainedPairPresent = false; @@ -608,7 +628,7 @@ export class TypeOperations { } } - if (!model.isCheapToTokenize(position.lineNumber)) { + if (!model.isCheapToTokenize(lineNumber)) { // Do not force tokenization return null; } @@ -616,20 +636,20 @@ export class TypeOperations { // Do not auto-close ' or " after a word character if (pair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') { const wordSeparators = getMapForWordSeparators(config.wordSeparators); - if (insertOpenCharacter && position.column > 1 && wordSeparators.get(lineText.charCodeAt(position.column - 2)) === WordCharacterClass.Regular) { - return null; - } - if (!insertOpenCharacter && position.column > 2 && wordSeparators.get(lineText.charCodeAt(position.column - 3)) === WordCharacterClass.Regular) { - return null; + if (lineBefore.length > 0) { + const characterBefore = lineBefore.charCodeAt(lineBefore.length - 1); + if (wordSeparators.get(characterBefore) === WordCharacterClass.Regular) { + return null; + } } } - model.forceTokenization(position.lineNumber); - const lineTokens = model.getLineTokens(position.lineNumber); + model.forceTokenization(lineNumber); + const lineTokens = model.getLineTokens(lineNumber); let shouldAutoClosePair = false; try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(pair, lineTokens, insertOpenCharacter ? position.column : position.column - 1); + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(pair, lineTokens, beforeColumn); } catch (e) { onUnexpectedError(e); } @@ -646,11 +666,11 @@ export class TypeOperations { } } - private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPairClose: string): EditOperationResult { + private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean, autoClosingPairClose: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; - commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPairClose); + commands[i] = new TypeWithAutoClosingCommand(selection, ch, !chIsAlreadyTyped, autoClosingPairClose); } return new EditOperationResult(EditOperationType.TypingOther, commands, { shouldPushStackElementBefore: true, @@ -825,9 +845,9 @@ export class TypeOperations { }); } - const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false); + const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true); if (autoClosingPairClose !== null) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose); + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose); } return null; @@ -869,9 +889,9 @@ export class TypeOperations { } if (!isDoingComposition) { - const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true); + const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false); if (autoClosingPairClose) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose); + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose); } } From 1b7a0df59c0afbe1eb6482e90de23efca47d542b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 20 Nov 2021 22:57:04 +0100 Subject: [PATCH 300/330] Minor refactorings --- .../common/controller/cursorTypeOperations.ts | 28 ++--- .../common/modes/languageConfiguration.ts | 13 ++- .../modes/languageConfigurationRegistry.ts | 7 +- .../common/modes/supports/characterPair.ts | 12 --- .../modes/supports/characterPair.test.ts | 101 ++++++++---------- 5 files changed, 71 insertions(+), 90 deletions(-) diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 979b39de5fb..c5637a1307d 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -20,6 +20,7 @@ import { EnterAction, IndentAction, StandardAutoClosingPairConditional } from 'v import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; +import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; export class TypeOperations { @@ -563,7 +564,9 @@ export class TypeOperations { private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean): string | null { const chIsQuote = isQuote(ch); - const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; + const autoCloseConfig = (chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets); + const shouldAutoCloseBefore = (chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket); + if (autoCloseConfig === 'never') { return null; } @@ -606,8 +609,6 @@ export class TypeOperations { const containedPairClose = containedPair ? containedPair.close : ''; let isContainedPairPresent = true; - const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; - for (const position of positions) { const { lineNumber, beforeColumn, afterColumn } = position; const lineText = model.getLineContent(lineNumber); @@ -628,11 +629,6 @@ export class TypeOperations { } } - if (!model.isCheapToTokenize(lineNumber)) { - // Do not force tokenization - return null; - } - // Do not auto-close ' or " after a word character if (pair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') { const wordSeparators = getMapForWordSeparators(config.wordSeparators); @@ -644,17 +640,15 @@ export class TypeOperations { } } - model.forceTokenization(lineNumber); - const lineTokens = model.getLineTokens(lineNumber); - - let shouldAutoClosePair = false; - try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(pair, lineTokens, beforeColumn); - } catch (e) { - onUnexpectedError(e); + if (!model.isCheapToTokenize(lineNumber)) { + // Do not force tokenization + return null; } - if (!shouldAutoClosePair) { + model.forceTokenization(lineNumber); + const lineTokens = model.getLineTokens(lineNumber); + const scopedLineTokens = createScopedLineTokens(lineTokens, beforeColumn - 1); + if (!pair.shouldAutoClose(scopedLineTokens, beforeColumn - scopedLineTokens.firstCharOffset)) { return null; } } diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index c911ae318d0..5b2ba7dbdcd 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { StandardTokenType } from 'vs/editor/common/modes'; +import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; /** * Describes how comments for a language work. @@ -268,7 +269,6 @@ export interface CompleteEnterAction { * @internal */ export class StandardAutoClosingPairConditional { - _standardAutoClosingPairConditionalBrand: void = undefined; readonly open: string; readonly close: string; @@ -302,6 +302,17 @@ export class StandardAutoClosingPairConditional { public isOK(standardToken: StandardTokenType): boolean { return (this._standardTokenMask & standardToken) === 0; } + + public shouldAutoClose(context: ScopedLineTokens, column: number): boolean { + // Always complete on empty line + if (context.getTokenCount() === 0) { + return true; + } + + const tokenIndex = context.findTokenIndexAtOffset(column - 2); + const standardTokenType = context.getStandardTokenType(tokenIndex); + return this.isOK(standardTokenType); + } } /** diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index b1b73e3d713..704e51f7866 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -10,7 +10,7 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; -import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction, AutoClosingPairs, CharacterPair, ExplicitLanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; +import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, CompleteEnterAction, AutoClosingPairs, CharacterPair, ExplicitLanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; @@ -280,11 +280,6 @@ export class LanguageConfigurationRegistryImpl { return characterPairSupport.getSurroundingPairs(); } - public shouldAutoClosePair(autoClosingPair: StandardAutoClosingPairConditional, context: LineTokens, column: number): boolean { - const scopedLineTokens = createScopedLineTokens(context, column - 1); - return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, scopedLineTokens, column - scopedLineTokens.firstCharOffset); - } - // end characterPair public getWordDefinition(languageId: string): RegExp { diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts index e1f8d13dcd8..61ff9873862 100644 --- a/src/vs/editor/common/modes/supports/characterPair.ts +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IAutoClosingPair, StandardAutoClosingPairConditional, LanguageConfiguration, CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; -import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; export class CharacterPairSupport { @@ -58,17 +57,6 @@ export class CharacterPairSupport { return this._autoCloseBefore; } - public static shouldAutoClosePair(autoClosingPair: StandardAutoClosingPairConditional, context: ScopedLineTokens, column: number): boolean { - // Always complete on empty line - if (context.getTokenCount() === 0) { - return true; - } - - const tokenIndex = context.findTokenIndexAtOffset(column - 2); - const standardTokenType = context.getStandardTokenType(tokenIndex); - return autoClosingPair.isOK(standardTokenType); - } - public getSurroundingPairs(): IAutoClosingPair[] { return this._surroundingPairs; } diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index ce1c31ee92d..5d799f478de 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -53,78 +53,71 @@ suite('CharacterPairSupport', () => { assert.deepStrictEqual(characaterPairSupport.getSurroundingPairs(), []); }); - function findAutoClosingPair(characterPairSupport: CharacterPairSupport, character: string): StandardAutoClosingPairConditional | undefined { - return characterPairSupport.getAutoClosingPairs().find(autoClosingPair => autoClosingPair.open === character); - } - - function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean { - const autoClosingPair = findAutoClosingPair(characterPairSupport, character); - if (!autoClosingPair) { - return false; - } - return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, createFakeScopedLineTokens(line), column); + function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], column: number): boolean { + const autoClosingPair = characterPairSupport.getAutoClosingPairs()[0]; + return autoClosingPair.shouldAutoClose(createFakeScopedLineTokens(line), column); } test('shouldAutoClosePair in empty line', () => { - let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); - assert.strictEqual(testShouldAutoClose(sup, [], 'a', 1), false); - assert.strictEqual(testShouldAutoClose(sup, [], '{', 1), true); + const sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + const tokenText: TokenText[] = []; + assert.strictEqual(testShouldAutoClose(sup, tokenText, 1), true); }); test('shouldAutoClosePair in not interesting line 1', () => { - let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.Other }], '{', 3), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.Other }], 'a', 3), false); + const sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + const tokenText: TokenText[] = [ + { text: 'do', type: StandardTokenType.Other } + ]; + assert.strictEqual(testShouldAutoClose(sup, tokenText, 3), true); }); test('shouldAutoClosePair in not interesting line 2', () => { - let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}' }] }); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.String }], '{', 3), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'do', type: StandardTokenType.String }], 'a', 3), false); + const sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}' }] }); + const tokenText: TokenText[] = [ + { text: 'do', type: StandardTokenType.String } + ]; + assert.strictEqual(testShouldAutoClose(sup, tokenText, 3), true); }); test('shouldAutoClosePair in interesting line 1', () => { - let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 1), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 1), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 2), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 2), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 3), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 3), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], '{', 4), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: '"a"', type: StandardTokenType.String }], 'a', 4), false); + const sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + const tokenText: TokenText[] = [ + { text: '"a"', type: StandardTokenType.String } + ]; + assert.strictEqual(testShouldAutoClose(sup, tokenText, 1), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 2), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 3), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 4), false); }); test('shouldAutoClosePair in interesting line 2', () => { - let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 1), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 1), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 2), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 2), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 3), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 3), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 4), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 4), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 5), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 5), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 6), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 6), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], '{', 7), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: 'x=', type: StandardTokenType.Other }, { text: '"a"', type: StandardTokenType.String }, { text: ';', type: StandardTokenType.Other }], 'a', 7), false); + const sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + const tokenText: TokenText[] = [ + { text: 'x=', type: StandardTokenType.Other }, + { text: '"a"', type: StandardTokenType.String }, + { text: ';', type: StandardTokenType.Other } + ]; + assert.strictEqual(testShouldAutoClose(sup, tokenText, 1), true); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 2), true); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 3), true); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 4), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 5), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 6), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 7), true); }); test('shouldAutoClosePair in interesting line 3', () => { - let sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 1), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 1), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 2), true); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 2), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 3), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 3), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 4), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 4), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], '{', 5), false); - assert.strictEqual(testShouldAutoClose(sup, [{ text: ' ', type: StandardTokenType.Other }, { text: '//a', type: StandardTokenType.Comment }], 'a', 5), false); + const sup = new CharacterPairSupport({ autoClosingPairs: [{ open: '{', close: '}', notIn: ['string', 'comment'] }] }); + const tokenText: TokenText[] = [ + { text: ' ', type: StandardTokenType.Other }, + { text: '//a', type: StandardTokenType.Comment } + ]; + assert.strictEqual(testShouldAutoClose(sup, tokenText, 1), true); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 2), true); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 3), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 4), false); + assert.strictEqual(testShouldAutoClose(sup, tokenText, 5), false); }); }); From 6add87d4512711440c3a3e302d629d36f774a98d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 21 Nov 2021 00:46:12 +0100 Subject: [PATCH 301/330] Fixes #132912: Insert a neutral character to check what its token type would be when deciding to auto-close a pair --- .../common/controller/cursorTypeOperations.ts | 16 ++ src/vs/editor/common/model.ts | 9 +- src/vs/editor/common/model/textModel.ts | 7 +- src/vs/editor/common/model/textModelTokens.ts | 33 +++- .../common/modes/languageConfiguration.ts | 32 ++++ .../test/browser/controller/cursor.test.ts | 154 +++++++++++++++++- 6 files changed, 243 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index c5637a1307d..4f8a560a404 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -651,6 +651,22 @@ export class TypeOperations { if (!pair.shouldAutoClose(scopedLineTokens, beforeColumn - scopedLineTokens.firstCharOffset)) { return null; } + + // Typing for example a quote could either start a new string, in which case auto-closing is desirable + // or it could end a previously started string, in which case auto-closing is not desirable + // + // In certain cases, it is really not possible to look at the previous token to determine + // what would happen. That's why we do something really unusual, we pretend to type a different + // character and ask the tokenizer what the outcome of doing that is: after typing a neutral + // character, are we in a string (i.e. the quote would most likely end a string) or not? + // + const neutralCharacter = pair.findNeutralCharacter(); + if (neutralCharacter) { + const tokenType = model.getTokenTypeIfInsertingCharacter(lineNumber, beforeColumn, neutralCharacter); + if (!pair.isOK(tokenType)) { + return null; + } + } } if (isContainedPairPresent) { diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 7266715e1f3..ebb63ed6586 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -13,7 +13,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelInjectedTextChangedEvent, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; -import { FormattingOptions } from 'vs/editor/common/modes'; +import { FormattingOptions, StandardTokenType } from 'vs/editor/common/modes'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { MultilineTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore'; import { TextChange } from 'vs/editor/common/model/textChange'; @@ -944,6 +944,13 @@ export interface ITextModel { */ getLanguageIdAtPosition(lineNumber: number, column: number): string; + /** + * Returns the standard token type for a character if the character were to be inserted at + * the given position. If the result cannot be accurate, it returns null. + * @internal + */ + getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType; + /** * Get the word under or besides `position`. * @param position The position to look for a word. diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 828352da600..bb687179f70 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -24,7 +24,7 @@ import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguag import { SearchData, SearchParams, TextModelSearch } from 'vs/editor/common/model/textModelSearch'; import { TextModelTokenization } from 'vs/editor/common/model/textModelTokens'; import { getWordAtText } from 'vs/editor/common/model/wordHelper'; -import { FormattingOptions } from 'vs/editor/common/modes'; +import { FormattingOptions, StandardTokenType } from 'vs/editor/common/modes'; import { ILanguageConfigurationService, ResolvedLanguageConfiguration } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_MODE_ID } from 'vs/editor/common/modes/nullMode'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; @@ -2126,6 +2126,11 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati return lineTokens.getLanguageId(lineTokens.findTokenIndexAtOffset(position.column - 1)); } + public getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType { + const position = this.validatePosition(new Position(lineNumber, column)); + return this._tokenization.getTokenTypeIfInsertingCharacter(position, character); + } + private getLanguageConfiguration(languageId: string): ResolvedLanguageConfiguration { return this._languageConfigurationService.getLanguageConfiguration(languageId); } diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 04aa75a2096..5199d3e7003 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -9,7 +9,7 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/modes'; +import { ILanguageIdCodec, IState, ITokenizationSupport, StandardTokenType, TokenizationRegistry } from 'vs/editor/common/modes'; import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; import { TextModel } from 'vs/editor/common/model/textModel'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -309,6 +309,37 @@ export class TextModelTokenization extends Disposable { this._textModel.setTokens(builder.tokens, !this._hasLinesToTokenize()); } + public getTokenTypeIfInsertingCharacter(position: Position, character: string): StandardTokenType { + if (!this._tokenizationSupport) { + return StandardTokenType.Other; + } + + this.forceTokenization(position.lineNumber); + const lineStartState = this._tokenizationStateStore.getBeginState(position.lineNumber - 1); + if (!lineStartState) { + return StandardTokenType.Other; + } + + const languageId = this._textModel.getLanguageId(); + const lineContent = this._textModel.getLineContent(position.lineNumber); + + // Create the text as if `character` was inserted + const text = ( + lineContent.substring(0, position.column - 1) + + character + + lineContent.substring(position.column - 1) + ); + + const r = safeTokenize(this._languageIdCodec, languageId, this._tokenizationSupport, text, true, lineStartState); + const lineTokens = new LineTokens(r.tokens, text, this._languageIdCodec); + if (lineTokens.getCount() === 0) { + return StandardTokenType.Other; + } + + const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1); + return lineTokens.getStandardTokenType(tokenIndex); + } + public isCheapToTokenize(lineNumber: number): boolean { if (!this._tokenizationSupport) { return true; diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 5b2ba7dbdcd..3c8a5c22b45 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CharCode } from 'vs/base/common/charCode'; import { StandardTokenType } from 'vs/editor/common/modes'; import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; @@ -273,6 +274,8 @@ export class StandardAutoClosingPairConditional { readonly open: string; readonly close: string; private readonly _standardTokenMask: number; + private _neutralCharacter: string | null = null; + private _neutralCharacterSearched: boolean = false; constructor(source: IAutoClosingPairConditional) { this.open = source.open; @@ -313,6 +316,35 @@ export class StandardAutoClosingPairConditional { const standardTokenType = context.getStandardTokenType(tokenIndex); return this.isOK(standardTokenType); } + + private _findNeutralCharacterInRange(fromCharCode: number, toCharCode: number): string | null { + for (let charCode = fromCharCode; charCode <= toCharCode; charCode++) { + const character = String.fromCharCode(charCode); + if (!this.open.includes(character) && !this.close.includes(character)) { + return character; + } + } + return null; + } + + /** + * Find a character in the range [0-9a-zA-Z] that does not appear in the open or close + */ + public findNeutralCharacter(): string | null { + if (!this._neutralCharacterSearched) { + this._neutralCharacterSearched = true; + if (!this._neutralCharacter) { + this._neutralCharacter = this._findNeutralCharacterInRange(CharCode.Digit0, CharCode.Digit9); + } + if (!this._neutralCharacter) { + this._neutralCharacter = this._findNeutralCharacterInRange(CharCode.a, CharCode.z); + } + if (!this._neutralCharacter) { + this._neutralCharacter = this._findNeutralCharacterInRange(CharCode.A, CharCode.Z); + } + } + return this._neutralCharacter; + } } /** diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index aa6af2cf6bb..8a1dc5e697c 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -14,16 +14,18 @@ import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/modes'; +import { IState, ITokenizationSupport, MetadataConsts, StandardTokenType, TokenizationRegistry } from 'vs/editor/common/modes'; import { IndentAction, IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; -import { withTestCodeEditor, TestCodeEditorCreationOptions, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/test/common/editorTestUtils'; +import { withTestCodeEditor, TestCodeEditorCreationOptions, ITestCodeEditor, createCodeEditorServices } from 'vs/editor/test/browser/testCodeEditor'; +import { IRelaxedTextModelCreationOptions, createTextModel, createTextModel2 } from 'vs/editor/test/common/editorTestUtils'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; // --------- utils @@ -4810,7 +4812,7 @@ suite('autoClosingPairs', () => { private static readonly _id = 'autoClosingMode'; - constructor() { + constructor(modeService: IModeService | null = null) { super(AutoClosingMode._id); this._register(LanguageConfigurationRegistry.register(this.languageId, { autoClosingPairs: [ @@ -4827,6 +4829,129 @@ suite('autoClosingPairs', () => { docComment: { open: '/**', close: ' */' } } })); + class BaseState implements IState { + constructor( + public readonly parent: State | null = null + ) { } + clone(): IState { return this; } + equals(other: IState): boolean { + if (!(other instanceof BaseState)) { + return false; + } + if (!this.parent && !other.parent) { + return true; + } + if (!this.parent || !other.parent) { + return false; + } + return this.parent.equals(other.parent); + } + } + class StringState implements IState { + constructor( + public readonly char: string, + public readonly parentState: State + ) { } + clone(): IState { return this; } + equals(other: IState): boolean { return other instanceof StringState && this.char === other.char && this.parentState.equals(other.parentState); } + } + class BlockCommentState implements IState { + constructor( + public readonly parentState: State + ) { } + clone(): IState { return this; } + equals(other: IState): boolean { return other instanceof StringState && this.parentState.equals(other.parentState); } + } + type State = BaseState | StringState | BlockCommentState; + + if (modeService) { + const encodedLanguageId = modeService.languageIdCodec.encodeLanguageId(this.languageId); + this._register(TokenizationRegistry.register(this.languageId, { + getInitialState: () => new BaseState(), + tokenize: undefined!, + tokenize2: function (line: string, hasEOL: boolean, _state: IState, offsetDelta: number): TokenizationResult2 { + let state = _state; + const tokens: { length: number; type: StandardTokenType; }[] = []; + const generateToken = (length: number, type: StandardTokenType, newState?: State) => { + if (tokens.length > 0 && tokens[tokens.length - 1].type === type) { + // grow last tokens + tokens[tokens.length - 1].length += length; + } else { + tokens.push({ length, type }); + } + line = line.substring(length); + if (newState) { + state = newState; + } + }; + while (line.length > 0) { + advance(); + } + let result = new Uint32Array(tokens.length * 2); + let startIndex = 0; + for (let i = 0; i < tokens.length; i++) { + result[2 * i] = startIndex; + result[2 * i + 1] = ( + (encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET) + | (tokens[i].type << MetadataConsts.TOKEN_TYPE_OFFSET) + ); + startIndex += tokens[i].length; + } + return new TokenizationResult2(result, state); + + function advance(): void { + if (state instanceof BaseState) { + const m1 = line.match(/^[^'"`{}/]+/g); + if (m1) { + return generateToken(m1[0].length, StandardTokenType.Other); + } + if (/^['"`]/.test(line)) { + return generateToken(1, StandardTokenType.String, new StringState(line.charAt(0), state)); + } + if (/^{/.test(line)) { + return generateToken(1, StandardTokenType.Other, new BaseState(state)); + } + if (/^}/.test(line)) { + return generateToken(1, StandardTokenType.Other, state.parent || new BaseState()); + } + if (/^\/\//.test(line)) { + return generateToken(line.length, StandardTokenType.Comment, state); + } + if (/^\/\*/.test(line)) { + return generateToken(2, StandardTokenType.Comment, new BlockCommentState(state)); + } + return generateToken(1, StandardTokenType.Other, state); + } else if (state instanceof StringState) { + const m1 = line.match(/^[^\\'"`\$]+/g); + if (m1) { + return generateToken(m1[0].length, StandardTokenType.String); + } + if (/^\\/.test(line)) { + return generateToken(2, StandardTokenType.String); + } + if (line.charAt(0) === state.char) { + return generateToken(1, StandardTokenType.String, state.parentState); + } + if (/^\$\{/.test(line)) { + return generateToken(2, StandardTokenType.Other, new BaseState(state)); + } + return generateToken(1, StandardTokenType.Other, state); + } else if (state instanceof BlockCommentState) { + const m1 = line.match(/^[^*]+/g); + if (m1) { + return generateToken(m1[0].length, StandardTokenType.String); + } + if (/^\*\//.test(line)) { + return generateToken(2, StandardTokenType.Comment, state.parentState); + } + return generateToken(1, StandardTokenType.Other, state); + } else { + throw new Error(`unknown state`); + } + } + } + })); + } } public setAutocloseEnabledSet(chars: string) { @@ -4869,7 +4994,7 @@ suite('autoClosingPairs', () => { return result; } - function assertType(editor: ITestCodeEditor, model: TextModel, viewModel: ViewModel, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void { + function assertType(editor: ITestCodeEditor, model: ITextModel, viewModel: ViewModel, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void { let lineContent = model.getLineContent(lineNumber); let expected = lineContent.substr(0, column - 1) + expectedInsert + lineContent.substr(column - 1); moveTo(editor, viewModel, lineNumber, column); @@ -4890,6 +5015,25 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('issue #132912: quotes should not auto-close if they are closing a string', () => { + const disposables = new DisposableStore(); + const instantiationService = createCodeEditorServices(disposables); + const modeService = instantiationService.invokeFunction((accessor) => accessor.get(IModeService)); + const mode = disposables.add(new AutoClosingMode(modeService)); + withTestCodeEditor( + null, + { + model: disposables.add(createTextModel2(instantiationService, 'const t2 = `something ${t1}', undefined, mode.languageId)) + }, + (editor, viewModel) => { + const model = viewModel.model; + model.forceTokenization(1); + assertType(editor, model, viewModel, 1, 28, '`', '`', `does not auto close \` @ (1, 28)`); + } + ); + disposables.dispose(); + }); + test('open parens: default', () => { let mode = new AutoClosingMode(); usingCursor({ From fe1958cdcb715525ab5303f9b9c75c746d8fac21 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Sat, 20 Nov 2021 18:08:30 -0800 Subject: [PATCH 302/330] reset terminal name when empty title is provided (#137589) --- .../contrib/terminal/browser/terminal.ts | 5 +++-- .../terminal/browser/terminalActions.ts | 8 +++---- .../terminal/browser/terminalInstance.ts | 22 +++++++++---------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 689d7033219..0a8fe39a7da 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -768,9 +768,10 @@ export interface ITerminalInstance { /** * Sets the terminal name to the provided title or triggers a quick pick - * to take user input. + * to take user input. If no title is provided, will reset based to the value indicated + * user's configration. */ - rename(title?: string): Promise; + rename(title?: string | 'triggerQuickpick'): Promise; /** * Triggers a quick pick to change the icon of this terminal. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 32699b858aa..0ee189630af 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -836,7 +836,7 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor, resource: unknown) { - doWithInstance(accessor, resource)?.rename(); + doWithInstance(accessor, resource)?.rename('triggerQuickpick'); } }); @@ -851,7 +851,7 @@ export function registerTerminalActions() { }); } async run(accessor: ServicesAccessor) { - return accessor.get(ITerminalGroupService).activeInstance?.rename(); + return accessor.get(ITerminalGroupService).activeInstance?.rename('triggerQuickpick'); } }); registerAction2(class extends Action2 { @@ -2132,8 +2132,8 @@ function focusNext(accessor: ServicesAccessor): void { export function validateTerminalName(name: string): { content: string, severity: Severity } | null { if (!name || name.trim().length === 0) { return { - content: localize('emptyTerminalNameError', "A name must be provided."), - severity: Severity.Error + content: localize('emptyTerminalNameInfo', "Providing no name will reset it to the default value"), + severity: Severity.Info }; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index e0a5863e1da..097503c3791 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1466,10 +1466,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } refreshTabLabels(title: string | undefined, eventSource: TitleEventSource): void { + const reset = !title; title = this._updateTitleProperties(title, eventSource); const titleChanged = title !== this._title; this._title = title; - this._labelComputer?.refreshLabel(); + this._labelComputer?.refreshLabel(reset); this._setAriaLabel(this.xterm?.raw, this._instanceId, this._title); if (this._titleReadyComplete) { @@ -1762,16 +1763,14 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return this._linkManager.registerExternalLinkProvider(this, provider); } - async rename(title?: string) { - if (!title) { + async rename(title?: string | 'triggerQuickpick') { + if (title === 'triggerQuickpick') { title = await this._quickInputService.input({ value: this.title, prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"), }); } - if (title) { - this.refreshTabLabels(title, TitleEventSource.Api); - } + this.refreshTabLabels(title, TitleEventSource.Api); } async changeIcon() { @@ -2039,17 +2038,18 @@ export class TerminalLabelComputer extends Disposable { super(); } - refreshLabel(): void { - this._title = this.computeLabel(this._configHelper.config.tabs.title, TerminalLabelType.Title); + refreshLabel(reset?: boolean): void { + this._title = this.computeLabel(this._configHelper.config.tabs.title, TerminalLabelType.Title, reset); this._description = this.computeLabel(this._configHelper.config.tabs.description, TerminalLabelType.Description); - if (this._title !== this._instance.title || this._description !== this._instance.description) { + if (this._title !== this._instance.title || this._description !== this._instance.description || reset) { this._onDidChangeLabel.fire({ title: this._title, description: this._description }); } } computeLabel( labelTemplate: string, - labelType: TerminalLabelType + labelType: TerminalLabelType, + reset?: boolean ) { const templateProperties: ITerminalLabelTemplateProperties = { cwd: this._instance.cwd || this._instance.initialCwd || '', @@ -2068,7 +2068,7 @@ export class TerminalLabelComputer extends Disposable { if (!labelTemplate) { return labelType === TerminalLabelType.Title ? (this._instance.processName || '') : ''; } - if (this._instance.staticTitle && labelType === TerminalLabelType.Title) { + if (!reset && this._instance.staticTitle && labelType === TerminalLabelType.Title) { return this._instance.staticTitle.replace(/[\n\r\t]/g, '') || templateProperties.process?.replace(/[\n\r\t]/g, '') || ''; } const detection = this._instance.capabilities.includes(ProcessCapability.CwdDetection); From 8d1b714a230dfeedb22f7d16bf55e4cc0c257ebf Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Sat, 20 Nov 2021 18:08:49 -0800 Subject: [PATCH 303/330] disable local echo when local by default (#137588) --- src/vs/platform/terminal/common/terminal.ts | 1 + .../terminal/browser/terminalInstance.ts | 26 ++++++++++++++++--- .../browser/terminalProcessManager.ts | 1 - .../contrib/terminal/common/terminal.ts | 1 + .../terminal/common/terminalConfiguration.ts | 11 ++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 71c62368b09..026521405f7 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -91,6 +91,7 @@ export const enum TerminalSettingId { UnicodeVersion = 'terminal.integrated.unicodeVersion', ExperimentalLinkProvider = 'terminal.integrated.experimentalLinkProvider', LocalEchoLatencyThreshold = 'terminal.integrated.localEchoLatencyThreshold', + LocalEchoEnabled = 'terminal.integrated.localEchoEnabled', LocalEchoExcludePrograms = 'terminal.integrated.localEchoExcludePrograms', LocalEchoStyle = 'terminal.integrated.localEchoStyle', EnablePersistentSessions = 'terminal.integrated.enablePersistentSessions', diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 097503c3791..712e0057c7a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -621,15 +621,35 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._onLinksReady.fire(this); }); - // TODO: This should be an optional addon - this._xtermTypeAheadAddon = this._register(this._instantiationService.createInstance(TypeAheadAddon, this._processManager, this._configHelper)); - xterm.raw.loadAddon(this._xtermTypeAheadAddon); + this._loadTypeAheadAddon(xterm); + + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(TerminalSettingId.LocalEchoEnabled)) { + this._loadTypeAheadAddon(xterm); + } + }); + this._pathService.userHome().then(userHome => { this._userHome = userHome.fsPath; }); return xterm; } + private _loadTypeAheadAddon(xterm: XtermTerminal): void { + const enabled = this._configHelper.config.localEchoEnabled; + const isRemote = !!this.remoteAuthority; + if (enabled === 'off' || enabled === 'auto' && !isRemote) { + return this._xtermTypeAheadAddon?.dispose(); + } + if (this._xtermTypeAheadAddon) { + return; + } + if (enabled === 'on' || (enabled === 'auto' && isRemote)) { + this._xtermTypeAheadAddon = this._register(this._instantiationService.createInstance(TypeAheadAddon, this._processManager, this._configHelper)); + xterm.raw.loadAddon(this._xtermTypeAheadAddon); + } + } + detachFromElement(): void { this._wrapperElement?.remove(); this._container = undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 680dd89b9cb..9063d049942 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -194,7 +194,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce } else { this.remoteAuthority = this._workbenchEnvironmentService.remoteAuthority; } - const backend = this._terminalInstanceService.getBackend(this.remoteAuthority); if (!backend) { throw new Error(`No terminal backend registered for remote authority '${this.remoteAuthority}'`); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index e4a4bbf4791..f916d9111c0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -248,6 +248,7 @@ export interface ITerminalConfiguration { experimentalLinkProvider: boolean; localEchoLatencyThreshold: number; localEchoExcludePrograms: ReadonlyArray; + localEchoEnabled: 'auto' | 'on' | 'off'; localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string; enablePersistentSessions: boolean; tabs: { diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 84d3e578a02..f01fbc9cf8a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -447,6 +447,17 @@ const terminalConfiguration: IConfigurationNode = { minimum: -1, default: 30, }, + [TerminalSettingId.LocalEchoEnabled]: { + description: localize('terminal.integrated.localEchoEnabled', "When local echo should be enabled. This will override `terminal.integrated.localEchoLatencyThreshold`"), + type: 'string', + enum: ['on', 'off', 'auto'], + enumDescriptions: [ + localize('terminal.integrated.localEchoEnabled.on', "Always enabled"), + localize('terminal.integrated.localEchoEnabled.off', "Always disabled"), + localize('terminal.integrated.localEchoEnabled.auto', "Enabled only for remote workspaces") + ], + default: 'auto' + }, [TerminalSettingId.LocalEchoExcludePrograms]: { description: localize('terminal.integrated.localEchoExcludePrograms', "Experimental: local echo will be disabled when any of these program names are found in the terminal title."), type: 'array', From ce15e23a06afd39d3a3ddc06ff1e49fa91250b40 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Sat, 20 Nov 2021 18:25:44 -0800 Subject: [PATCH 304/330] fix #128289 --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 048a1fe59c8..d2f42f47998 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -137,6 +137,7 @@ export class TerminalViewPane extends ViewPane { if (this._terminalsInitialized) { if (!hadTerminals) { this._terminalService.createTerminal({ location: TerminalLocation.Panel }); + this._terminalGroupService.showPanel(true); } } else { this._terminalsInitialized = true; @@ -149,7 +150,6 @@ export class TerminalViewPane extends ViewPane { if (hadTerminals) { this._terminalGroupService.activeGroup?.setVisible(visible); } - this._terminalGroupService.showPanel(true); } else { this._terminalGroupService.activeGroup?.setVisible(false); } From 6358042bbcd72bca2c3c2a667b74fd3a8a7172a2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Nov 2021 09:28:29 +0100 Subject: [PATCH 305/330] process explorer - improve detection logic of window kind (#137427) --- src/vs/base/node/ps.ts | 23 +++++++++++++------ .../processExplorer/media/processExplorer.css | 2 +- .../processExplorer/processExplorerMain.ts | 2 +- .../issue/electron-main/issueMainService.ts | 8 +++---- .../electron-main/sharedProcess.ts | 5 ++-- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 786da64c247..0a4d82d0ae4 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -48,18 +48,15 @@ export function listProcesses(rootPid: number): Promise { function findName(cmd: string): string { - const SHARED_PROCESS_HINT = /--disable-blink-features=Auxclick/; - const WINDOWS_WATCHER_HINT = /\\watcher\\win32\\CodeHelper\.exe/; + const SHARED_PROCESS_HINT = /--vscode-window-kind=shared-process/; + const ISSUE_REPORTER_HINT = /--vscode-window-kind=issue-reporter/; + const PROCESS_EXPLORER_HINT = /--vscode-window-kind=process-explorer/; + const UTILITY_NETWORK_HINT = /--utility-sub-type=network/; const WINDOWS_CRASH_REPORTER = /--crashes-directory/; const WINDOWS_PTY = /\\pipe\\winpty-control/; const WINDOWS_CONSOLE_HOST = /conhost\.exe/; const TYPE = /--type=([a-zA-Z-]+)/; - // find windows file watcher - if (WINDOWS_WATCHER_HINT.exec(cmd)) { - return 'watcherService '; - } - // find windows crash reporter if (WINDOWS_CRASH_REPORTER.exec(cmd)) { return 'electron-crash-reporter'; @@ -83,7 +80,19 @@ export function listProcesses(rootPid: number): Promise { return 'shared-process'; } + if (ISSUE_REPORTER_HINT.exec(cmd)) { + return 'issue-reporter'; + } + + if (PROCESS_EXPLORER_HINT.exec(cmd)) { + return 'process-explorer'; + } + return `window`; + } else if (matches[1] === 'utility') { + if (UTILITY_NETWORK_HINT.exec(cmd)) { + return 'utility-network-service'; + } } return matches[1]; } diff --git a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css index 0129b721f77..3baf48ce1af 100644 --- a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css +++ b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css @@ -38,7 +38,7 @@ body { } .cpu { - width: 45px; + width: 60px; } .pid { diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index f447d8e8645..74c171ceb34 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -115,7 +115,7 @@ class ProcessHeaderTreeRenderer implements ITreeRenderer, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void { templateData.name.textContent = localize('name', "Process Name"); - templateData.CPU.textContent = localize('cpu', "CPU %"); + templateData.CPU.textContent = localize('cpu', "CPU (%)"); templateData.PID.textContent = localize('pid', "PID"); templateData.memory.textContent = localize('memory', "Memory (MB)"); diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 2fb91fc0a3b..1166a4ec7f3 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -212,7 +212,7 @@ export class IssueMainService implements ICommonIssueService { title: localize('issueReporter', "Issue Reporter"), zoomLevel: data.zoomLevel, alwaysOnTop: false - }); + }, 'issue-reporter'); // Store into config object URL issueReporterWindowConfigUrl.update({ @@ -267,7 +267,7 @@ export class IssueMainService implements ICommonIssueService { title: localize('processExplorer', "Process Explorer"), zoomLevel: data.zoomLevel, alwaysOnTop: true - }); + }, 'process-explorer'); // Store into config object URL processExplorerWindowConfigUrl.update({ @@ -301,7 +301,7 @@ export class IssueMainService implements ICommonIssueService { this.processExplorerWindow?.focus(); } - private createBrowserWindow(position: IWindowState, ipcObjectUrl: IIPCObjectUrl, options: IBrowserWindowOptions): BrowserWindow { + private createBrowserWindow(position: IWindowState, ipcObjectUrl: IIPCObjectUrl, options: IBrowserWindowOptions, windowKind: string): BrowserWindow { const window = new BrowserWindow({ fullscreen: false, skipTaskbar: true, @@ -316,7 +316,7 @@ export class IssueMainService implements ICommonIssueService { backgroundColor: options.backgroundColor || IssueMainService.DEFAULT_BACKGROUND_COLOR, webPreferences: { preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, - additionalArguments: [`--vscode-window-config=${ipcObjectUrl.resource.toString()}`], + additionalArguments: [`--vscode-window-config=${ipcObjectUrl.resource.toString()}`, `--vscode-window-kind=${windowKind}`], v8CacheOptions: this.environmentMainService.useCodeCache ? 'bypassHeatCheck' : 'none', enableWebSQL: false, spellcheck: false, diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 36cd750dc0c..1f75cf92d18 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -218,7 +218,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { backgroundColor: this.themeMainService.getBackgroundColor(), webPreferences: { preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, - additionalArguments: [`--vscode-window-config=${configObjectUrl.resource.toString()}`], + additionalArguments: [`--vscode-window-config=${configObjectUrl.resource.toString()}`, '--vscode-window-kind=shared-process'], v8CacheOptions: this.environmentMainService.useCodeCache ? 'bypassHeatCheck' : 'none', nodeIntegration: true, nodeIntegrationInWorker: true, @@ -227,8 +227,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { spellcheck: false, nativeWindowOpen: true, images: false, - webgl: false, - disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer + webgl: false } }); From 6825c886700ac11d07f7646d8d8119c9cdd9d288 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Nov 2021 09:31:09 +0100 Subject: [PATCH 306/330] debt - adopt more `DeferredPromise` --- .../quickAccess/gotoSymbolQuickAccess.ts | 10 +++++----- .../watcher/parcel/parcelWatcherService.ts | 20 +++++++++---------- .../quickinput/browser/quickAccess.ts | 10 +++++----- .../sharedProcessWorkerService.ts | 8 ++++---- src/vs/workbench/browser/layout.ts | 14 ++++++------- .../browser/parts/editor/editorGroupView.ts | 8 ++++---- .../browser/parts/editor/editorPart.ts | 14 ++++++------- src/vs/workbench/common/dialogs.ts | 10 +++++++--- .../progress/browser/progressService.ts | 13 ++++++------ src/vs/workbench/workbench.web.api.ts | 16 +++++++-------- 10 files changed, 62 insertions(+), 61 deletions(-) diff --git a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts index 0c82db5f0d8..ddc4b3e90af 100644 --- a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DeferredPromise } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { IMatch } from 'vs/base/common/filters'; @@ -101,22 +102,21 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit return true; } - let symbolProviderRegistryPromiseResolve: (res: boolean) => void; - const symbolProviderRegistryPromise = new Promise(resolve => symbolProviderRegistryPromiseResolve = resolve); + const symbolProviderRegistryPromise = new DeferredPromise(); // Resolve promise when registry knows model const symbolProviderListener = disposables.add(DocumentSymbolProviderRegistry.onDidChange(() => { if (DocumentSymbolProviderRegistry.has(model)) { symbolProviderListener.dispose(); - symbolProviderRegistryPromiseResolve(true); + symbolProviderRegistryPromise.complete(true); } })); // Resolve promise when we get disposed too - disposables.add(toDisposable(() => symbolProviderRegistryPromiseResolve(false))); + disposables.add(toDisposable(() => symbolProviderRegistryPromise.complete(false))); - return symbolProviderRegistryPromise; + return symbolProviderRegistryPromise.p; } private doProvideWithEditorSymbols(context: IQuickAccessTextEditorContext, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable { diff --git a/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts b/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts index 63a859ec83d..26963631571 100644 --- a/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts +++ b/src/vs/platform/files/node/watcher/parcel/parcelWatcherService.ts @@ -6,7 +6,7 @@ import * as parcelWatcher from '@parcel/watcher'; import { existsSync, unlinkSync } from 'fs'; import { tmpdir } from 'os'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; @@ -244,15 +244,14 @@ export class ParcelWatcherService extends Disposable implements IWatcherService private startPolling(request: IWatchRequest, pollingInterval: number, restarts = 0): void { const cts = new CancellationTokenSource(); - let parcelWatcherPromiseResolve: () => void; - const instance = new Promise(resolve => parcelWatcherPromiseResolve = resolve); + const instance = new DeferredPromise(); const snapshotFile = join(tmpdir(), `vscode-watcher-snapshot-${generateUuid()}`); // Remember as watcher instance const watcher: IWatcher = { request, - ready: instance, + ready: instance.p, restarts, token: cts.token, stop: async () => { @@ -299,7 +298,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService // Signal we are ready now when the first snapshot was written if (counter === 1) { - parcelWatcherPromiseResolve(); + instance.complete(); } if (cts.token.isCancellationRequested) { @@ -315,19 +314,18 @@ export class ParcelWatcherService extends Disposable implements IWatcherService private startWatching(request: IWatchRequest, restarts = 0): void { const cts = new CancellationTokenSource(); - let parcelWatcherPromiseResolve: (watcher: parcelWatcher.AsyncSubscription | undefined) => void; - const instance = new Promise(resolve => parcelWatcherPromiseResolve = resolve); + const instance = new DeferredPromise(); // Remember as watcher instance const watcher: IWatcher = { request, - ready: instance, + ready: instance.p, restarts, token: cts.token, stop: async () => { cts.dispose(true); - const watcherInstance = await instance; + const watcherInstance = await instance.p; await watcherInstance?.unsubscribe(); } }; @@ -361,11 +359,11 @@ export class ParcelWatcherService extends Disposable implements IWatcherService }).then(parcelWatcher => { this.debug(`Started watching: '${realPath}' with backend '${ParcelWatcherService.PARCEL_WATCHER_BACKEND}' and native excludes '${ignore?.join(', ')}'`); - parcelWatcherPromiseResolve(parcelWatcher); + instance.complete(parcelWatcher); }).catch(error => { this.onUnexpectedError(error, watcher); - parcelWatcherPromiseResolve(undefined); + instance.complete(undefined); }); } diff --git a/src/vs/platform/quickinput/browser/quickAccess.ts b/src/vs/platform/quickinput/browser/quickAccess.ts index a0d19728eac..cf29d5ae602 100644 --- a/src/vs/platform/quickinput/browser/quickAccess.ts +++ b/src/vs/platform/quickinput/browser/quickAccess.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DeferredPromise } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { once } from 'vs/base/common/functional'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -111,10 +112,9 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon // Pick mode: setup a promise that can be resolved // with the selected items and prevent execution - let pickPromise: Promise | undefined = undefined; - let pickResolve: Function | undefined = undefined; + let pickPromise: DeferredPromise | undefined = undefined; if (pick) { - pickPromise = new Promise(resolve => pickResolve = resolve); + pickPromise = new DeferredPromise(); disposables.add(once(picker.onWillAccept)(e => { e.veto(); picker.hide(); @@ -143,7 +143,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon disposables.dispose(); // Resolve pick promise with selected items - pickResolve?.(picker.selectedItems); + pickPromise?.complete(picker.selectedItems.slice(0)); }); // Finally, show the picker. This is important because a provider @@ -153,7 +153,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon // Pick mode: return with promise if (pick) { - return pickPromise; + return pickPromise?.p; } } diff --git a/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService.ts b/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService.ts index 1137f76e556..39d09c554ca 100644 --- a/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService.ts +++ b/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService.ts @@ -5,6 +5,7 @@ import { CrashReporterStartOptions, ipcRenderer } from 'electron'; import { join } from 'path'; +import { DeferredPromise } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -176,8 +177,7 @@ class SharedProcessWebWorker extends Disposable { } private doInit(): Promise { - let readyResolve: (result: Worker) => void; - const readyPromise = new Promise(resolve => readyResolve = resolve); + const readyPromise = new DeferredPromise(); const worker = new Worker('../../../base/worker/workerMain.js', { name: `Shared Process Worker (${this.type})` @@ -198,7 +198,7 @@ class SharedProcessWebWorker extends Disposable { // Lifecycle: Ready case SharedProcessWorkerMessages.Ready: - readyResolve(worker); + readyPromise.complete(worker); break; // Lifecycle: Ack @@ -250,7 +250,7 @@ class SharedProcessWebWorker extends Disposable { // First message triggers the load of the worker worker.postMessage('vs/platform/sharedProcess/electron-browser/sharedProcessWorkerMain'); - return readyPromise; + return readyPromise.p; } private async send(message: ISharedProcessToWorkerMessage, token: CancellationToken, port?: MessagePort): Promise { diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 9957a797e02..91e523f7e66 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -43,7 +43,7 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { mark } from 'vs/base/common/performance'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { Promises } from 'vs/base/common/async'; +import { DeferredPromise, Promises } from 'vs/base/common/async'; import { IBannerService } from 'vs/workbench/services/banner/browser/bannerService'; import { getVirtualWorkspaceScheme } from 'vs/platform/remote/common/remoteHosts'; import { Schemas } from 'vs/base/common/network'; @@ -707,11 +707,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return undefined; } - private whenReadyResolve: (() => void) | undefined; - protected readonly whenReady = new Promise(resolve => (this.whenReadyResolve = resolve)); + private readonly whenReadyPromise = new DeferredPromise(); + protected readonly whenReady = this.whenReadyPromise.p; - private whenRestoredResolve: (() => void) | undefined; - readonly whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve)); + private readonly whenRestoredPromise = new DeferredPromise(); + readonly whenRestored = this.whenRestoredPromise.p; private restored = false; isRestored(): boolean { @@ -911,11 +911,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Await for promises that we recorded to update // our ready and restored states properly. Promises.settled(layoutReadyPromises).finally(() => { - this.whenReadyResolve?.(); + this.whenReadyPromise.complete(); Promises.settled(layoutRestoredPromises).finally(() => { this.restored = true; - this.whenRestoredResolve?.(); + this.whenRestoredPromise.complete(); }); }); } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 411f50891eb..6b54f1c7577 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -30,7 +30,7 @@ import { combinedDisposable, dispose, MutableDisposable, toDisposable } from 'vs import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { Promises, RunOnceWorker } from 'vs/base/common/async'; +import { DeferredPromise, Promises, RunOnceWorker } from 'vs/base/common/async'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; import { IEditorGroupsAccessor, IEditorGroupView, fillActiveEditorViewState, EditorServiceImpl, IEditorGroupTitleHeight, IInternalEditorOpenOptions, IInternalMoveCopyOptions, IInternalEditorCloseOptions, IInternalEditorTitleControlOptions } from 'vs/workbench/browser/parts/editor/editor'; @@ -128,8 +128,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private readonly containerToolBarMenuDisposable = this._register(new MutableDisposable()); - private whenRestoredResolve: (() => void) | undefined; - readonly whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve)); + private readonly whenRestoredPromise = new DeferredPromise(); + readonly whenRestored = this.whenRestoredPromise.p; private isRestored = false; constructor( @@ -232,7 +232,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Signal restored once editors have restored restoreEditorsPromise.finally(() => { this.isRestored = true; - this.whenRestoredResolve?.(); + this.whenRestoredPromise.complete(); }); // Register Listeners diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 06f3a883d2b..efcada70fec 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -30,7 +30,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { assertIsDefined } from 'vs/base/common/types'; import { IBoundarySashes } from 'vs/base/browser/ui/grid/gridview'; import { CompositeDragAndDropObserver } from 'vs/workbench/browser/dnd'; -import { Promises } from 'vs/base/common/async'; +import { DeferredPromise, Promises } from 'vs/base/common/async'; import { findGroup } from 'vs/workbench/services/editor/common/editorGroupFinder'; import { SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -222,11 +222,11 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private _isReady = false; get isReady(): boolean { return this._isReady; } - private whenReadyResolve: (() => void) | undefined; - readonly whenReady = new Promise(resolve => (this.whenReadyResolve = resolve)); + private readonly whenReadyPromise = new DeferredPromise(); + readonly whenReady = this.whenReadyPromise.p; - private whenRestoredResolve: (() => void) | undefined; - readonly whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve)); + private readonly whenRestoredPromise = new DeferredPromise(); + readonly whenRestored = this.whenRestoredPromise.p; get hasRestorableState(): boolean { return !!this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY]; @@ -864,12 +864,12 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.setupDragAndDropSupport(parent, this.container); // Signal ready - this.whenReadyResolve?.(); + this.whenReadyPromise.complete(); this._isReady = true; // Signal restored Promises.settled(this.groups.map(group => group.whenRestored)).finally(() => { - this.whenRestoredResolve?.(); + this.whenRestoredPromise.complete(); }); return this.container; diff --git a/src/vs/workbench/common/dialogs.ts b/src/vs/workbench/common/dialogs.ts index 5e0561583da..e6fd73a42b7 100644 --- a/src/vs/workbench/common/dialogs.ts +++ b/src/vs/workbench/common/dialogs.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DeferredPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IDialog, IDialogResult } from 'vs/platform/dialogs/common/dialogs'; @@ -34,11 +35,14 @@ export class DialogsModel extends Disposable implements IDialogsModel { readonly onDidShowDialog = this._onDidShowDialog.event; show(dialog: IDialog): IDialogHandle { - let resolver: (value?: IDialogResult) => void; + const promise = new DeferredPromise(); const item: IDialogViewItem = { args: dialog, - close: (result) => { this.dialogs.splice(0, 1); resolver(result); } + close: result => { + this.dialogs.splice(0, 1); + promise.complete(result); + } }; this.dialogs.push(item); @@ -46,7 +50,7 @@ export class DialogsModel extends Disposable implements IDialogsModel { return { item, - result: new Promise(resolve => { resolver = resolve; }) + result: promise.p }; } } diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index ce7401d6d33..0957d9529c6 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { IDisposable, dispose, DisposableStore, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions, IProgressRunner, IProgressIndicator, IProgressWindowOptions, IProgressDialogOptions } from 'vs/platform/progress/common/progress'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar'; -import { RunOnceScheduler, timeout } from 'vs/base/common/async'; +import { DeferredPromise, RunOnceScheduler, timeout } from 'vs/base/common/async'; import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; import { INotificationService, Severity, INotificationHandle } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; @@ -225,8 +225,7 @@ export class ProgressService extends Disposable implements IProgressService { // Create a promise that we can resolve as needed // when the outside calls dispose on us - let promiseResolve: () => void; - const promise = new Promise(resolve => promiseResolve = resolve); + const promise = new DeferredPromise(); this.withWindowProgress({ location: ProgressLocation.Window, @@ -249,16 +248,16 @@ export class ProgressService extends Disposable implements IProgressService { // Continue to report progress as it happens const onDidReportListener = progressStateModel.onDidReport(step => reportProgress(step)); - promise.finally(() => onDidReportListener.dispose()); + promise.p.finally(() => onDidReportListener.dispose()); // When the progress model gets disposed, we are done as well - Event.once(progressStateModel.onWillDispose)(() => promiseResolve()); + Event.once(progressStateModel.onWillDispose)(() => promise.complete()); - return promise; + return promise.p; }); // Dispose means completing our promise - return toDisposable(() => promiseResolve()); + return toDisposable(() => promise.complete()); }; const createNotification = (message: string, silent: boolean, increment?: number): INotificationHandle => { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index a7516917032..051070b0d9a 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -19,6 +19,7 @@ import { mark } from 'vs/base/common/performance'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/common/credentials'; import { TunnelProviderFeatures } from 'vs/platform/remote/common/tunnel'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { DeferredPromise } from 'vs/base/common/async'; interface IResourceUriProvider { (uri: URI): URI; @@ -624,8 +625,7 @@ interface IWorkbench { * @param options for setting up the workbench */ let created = false; -let workbenchPromiseResolve: Function; -const workbenchPromise = new Promise(resolve => workbenchPromiseResolve = resolve); +const workbenchPromise = new DeferredPromise(); function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): IDisposable { // Mark start of workbench @@ -661,14 +661,14 @@ function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions) let instantiatedWorkbench: IWorkbench | undefined = undefined; main(domElement, options).then(workbench => { instantiatedWorkbench = workbench; - workbenchPromiseResolve(workbench); + workbenchPromise.complete(workbench); }); return toDisposable(() => { if (instantiatedWorkbench) { instantiatedWorkbench.shutdown(); } else { - workbenchPromise.then(instantiatedWorkbench => instantiatedWorkbench.shutdown()); + workbenchPromise.p.then(instantiatedWorkbench => instantiatedWorkbench.shutdown()); } }); } @@ -686,7 +686,7 @@ namespace commands { * @return A promise that resolves to the returned value of the given command. */ export async function executeCommand(command: string, ...args: any[]): Promise { - const workbench = await workbenchPromise; + const workbench = await workbenchPromise.p; return workbench.commands.executeCommand(command, ...args); } @@ -706,7 +706,7 @@ namespace env { * @returns A promise that resolves to tuples of source and marks. */ export async function retrievePerformanceMarks(): Promise<[string, readonly IPerformanceMark[]][]> { - const workbench = await workbenchPromise; + const workbench = await workbenchPromise.p; return workbench.env.retrievePerformanceMarks(); } @@ -716,7 +716,7 @@ namespace env { * experience via protocol handler. */ export async function getUriScheme(): Promise { - const workbench = await workbenchPromise; + const workbench = await workbenchPromise.p; return workbench.env.uriScheme; } @@ -726,7 +726,7 @@ namespace env { * workbench. */ export async function openUri(target: URI): Promise { - const workbench = await workbenchPromise; + const workbench = await workbenchPromise.p; return workbench.env.openUri(target); } From 2e636ded90fd03c8fbcd0553f550325c01cbcc60 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 21 Nov 2021 14:04:28 +0100 Subject: [PATCH 307/330] editors - `clearInput` before `setInput` (#34697) --- src/vs/workbench/browser/parts/editor/editor.ts | 3 ++- .../browser/parts/editor/editorGroupView.ts | 2 +- .../workbench/browser/parts/editor/editorPane.ts | 3 ++- .../browser/parts/editor/editorPanes.ts | 16 +++++++++++++--- .../browser/parts/editor/editorWithViewState.ts | 10 ---------- .../workbench/browser/workbench.contribution.ts | 5 +++++ src/vs/workbench/common/editor.ts | 3 ++- .../contrib/notebook/browser/notebookEditor.ts | 2 -- .../walkThrough/browser/walkThroughPart.ts | 4 ---- 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 22791e7c00f..1d41297143c 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -39,7 +39,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { closeEmptyGroups: true, labelFormat: 'default', splitSizing: 'distribute', - splitOnDragAndDrop: true + splitOnDragAndDrop: true, + experimentalDisableClearInputOnSetInput: false //TODO@bpasero remove this setting in December }; export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 6b54f1c7577..7a2aa70b8ac 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -211,7 +211,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.element.appendChild(this.editorContainer); // Editor pane - this.editorPane = this._register(this.scopedInstantiationService.createInstance(EditorPanes, this.editorContainer, this)); + this.editorPane = this._register(this.scopedInstantiationService.createInstance(EditorPanes, this.editorContainer, this.accessor, this)); this._onDidChange.input = this.editorPane.onDidChangeSizeConstraints; // Track Focus diff --git a/src/vs/workbench/browser/parts/editor/editorPane.ts b/src/vs/workbench/browser/parts/editor/editorPane.ts index 23e36f6c84e..f87a7651c19 100644 --- a/src/vs/workbench/browser/parts/editor/editorPane.ts +++ b/src/vs/workbench/browser/parts/editor/editorPane.ts @@ -122,7 +122,8 @@ export abstract class EditorPane extends Composite implements IEditorPane { * resources associated with the input should be freed. * * This method can be called based on different contexts, e.g. when opening - * a different editor control or when closing all editors in a group. + * a different input or different editor control or when closing all editors + * in a group. * * To monitor the lifecycle of editor inputs, you should not rely on this * method, rather refer to the listeners on `IEditorGroup` via `IEditorGroupService`. diff --git a/src/vs/workbench/browser/parts/editor/editorPanes.ts b/src/vs/workbench/browser/parts/editor/editorPanes.ts index 86562c72954..d12911518bb 100644 --- a/src/vs/workbench/browser/parts/editor/editorPanes.ts +++ b/src/vs/workbench/browser/parts/editor/editorPanes.ts @@ -13,7 +13,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress'; -import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS, IEditorGroupsAccessor } from 'vs/workbench/browser/parts/editor/editor'; import { Emitter } from 'vs/base/common/event'; import { assertIsDefined } from 'vs/base/common/types'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; @@ -84,6 +84,7 @@ export class EditorPanes extends Disposable { constructor( private parent: HTMLElement, + private accessor: IEditorGroupsAccessor, private groupView: IEditorGroupView, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -268,9 +269,18 @@ export class EditorPanes extends Disposable { // started will cancel the previous one. const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200); - // Set the input to the editor pane let cancelled = false; try { + + // Clear the current input before setting new input + // This ensures that a slow loading input will not + // be visible for the duration of the new input to + // load (https://github.com/microsoft/vscode/issues/34697) + if (this.accessor.partOptions.experimentalDisableClearInputOnSetInput !== true) { + editorPane.clearInput(); + } + + // Set the input to the editor pane await editorPane.setInput(editor, options, context, operation.token); if (!operation.isCurrent()) { @@ -309,7 +319,7 @@ export class EditorPanes extends Disposable { } closeEditor(editor: EditorInput): void { - if (this._activeEditorPane && this._activeEditorPane.input && editor.matches(this._activeEditorPane.input)) { + if (this._activeEditorPane?.input && editor.matches(this._activeEditorPane.input)) { this.doHideActiveEditorPane(); } } diff --git a/src/vs/workbench/browser/parts/editor/editorWithViewState.ts b/src/vs/workbench/browser/parts/editor/editorWithViewState.ts index 2f1abb31153..07a13f8955e 100644 --- a/src/vs/workbench/browser/parts/editor/editorWithViewState.ts +++ b/src/vs/workbench/browser/parts/editor/editorWithViewState.ts @@ -17,8 +17,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IExtUri } from 'vs/base/common/resources'; import { IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { CancellationToken } from 'vs/base/common/cancellation'; /** * Base class of editors that want to store and restore view state. @@ -65,14 +63,6 @@ export abstract class AbstractEditorWithViewState extends Edit } } - override async setInput(input: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { - - // Preserve current input view state before opening new - this.updateEditorViewState(this.input); - - await super.setInput(input, options, context, token); - } - override clearInput(): void { // Preserve current input view state before clearing diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 7dbe26ca935..cfc14325479 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -241,6 +241,11 @@ const registry = Registry.as(ConfigurationExtensions.Con 'default': false, 'description': localize('perEditorGroup', "Controls if the limit of maximum opened editors should apply per editor group or across all editor groups.") }, + 'workbench.editor.experimentalDisableClearInputOnSetInput': { + 'type': 'boolean', + 'default': false, + 'description': localize('experimentalDisableClearInputOnSetInput', "Experimental setting: do not change unless instructed.") + }, 'workbench.commandPalette.history': { 'type': 'number', 'description': localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."), diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 682522d5688..b63bbd6a36d 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -864,7 +864,8 @@ interface IEditorPartConfiguration { decorations?: { badges?: boolean; colors?: boolean; - } + }, + experimentalDisableClearInputOnSetInput?: boolean; } export interface IEditorPartOptions extends IEditorPartConfiguration { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 31e939f76cb..67683ca4f6e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -175,8 +175,6 @@ export class NotebookEditor extends EditorPane { this.inputListener.value = input.onDidChangeCapabilities(() => this.onDidChangeInputCapabilities(input)); - this._saveEditorViewState(this.input); - this._widgetDisposableStore.clear(); // there currently is a widget which we still own so diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index b1c5d45c47a..f0b72fb52c0 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -268,10 +268,6 @@ export class WalkThroughPart extends EditorPane { } override setInput(input: WalkThroughInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { - if (this.input instanceof WalkThroughInput) { - this.saveTextEditorViewState(this.input); - } - const store = new DisposableStore(); this.contentDisposables.push(store); From 6729fbbc861ece871549026a0a64f3aff2302445 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Sun, 21 Nov 2021 08:53:14 -0800 Subject: [PATCH 308/330] only focus when showing panel if active instance is not an extHost terminal --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index d2f42f47998..596a05c71e9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -137,7 +137,6 @@ export class TerminalViewPane extends ViewPane { if (this._terminalsInitialized) { if (!hadTerminals) { this._terminalService.createTerminal({ location: TerminalLocation.Panel }); - this._terminalGroupService.showPanel(true); } } else { this._terminalsInitialized = true; @@ -146,6 +145,11 @@ export class TerminalViewPane extends ViewPane { } else { this._onDidChangeViewWelcomeState.fire(); } + if (!this._terminalService.activeInstance?.shellLaunchConfig.extHostTerminalId) { + // showPanel is already called with !preserveFocus + // when extension host terminals are created + this._terminalGroupService.showPanel(true); + } if (hadTerminals) { this._terminalGroupService.activeGroup?.setVisible(visible); From 4e303fcc52269ab578affa6cb1eb455eacab445f Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Sun, 21 Nov 2021 10:04:21 -0800 Subject: [PATCH 309/330] fix #137590 --- test/automation/src/playwrightDriver.ts | 2 +- test/automation/src/terminal.ts | 9 ++++++++- test/smoke/src/areas/terminal/terminal-tabs.test.ts | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index b469974df25..9fd2c83b8e7 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -72,7 +72,7 @@ class PlaywrightDriver implements IDriver { await timeout(100); } - if (keybinding.startsWith('Alt') || keybinding.startsWith('Control')) { + if (keybinding.startsWith('Alt') || keybinding.startsWith('Control') || keybinding.startsWith('Backspace')) { await this._page.keyboard.press(keybinding); return; } diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 03ec6d53937..0108ec4d759 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -64,10 +64,13 @@ export class Terminal { } async runCommandWithValue(commandId: TerminalCommandIdWithValue, value?: string, altKey?: boolean): Promise { - const shouldKeepOpen = !!value || commandId === TerminalCommandIdWithValue.SelectDefaultProfile || commandId === TerminalCommandIdWithValue.NewWithProfile; + const shouldKeepOpen = !!value || commandId === TerminalCommandIdWithValue.SelectDefaultProfile || commandId === TerminalCommandIdWithValue.NewWithProfile || commandId === TerminalCommandIdWithValue.Rename; await this.quickaccess.runCommand(commandId, shouldKeepOpen); if (value) { await this.code.waitForSetValue(QuickInput.QUICK_INPUT_INPUT, value); + } else if (commandId === TerminalCommandIdWithValue.Rename) { + // Reset + await this.code.dispatchKeybinding('Backspace'); } await this.code.dispatchKeybinding(altKey ? 'Alt+Enter' : 'enter'); await this.quickinput.waitForQuickInputClosed(); @@ -108,6 +111,10 @@ export class Terminal { } } + async getSingleTabName(): Promise { + return await (await this.code.waitForElement(Selector.SingleTab, singleTab => !!singleTab && singleTab?.textContent.length > 1)).textContent; + } + private async assertTabExpected(selector?: string, listIndex?: number, nameRegex?: RegExp, icon?: string, color?: string): Promise { if (listIndex) { if (nameRegex) { diff --git a/test/smoke/src/areas/terminal/terminal-tabs.test.ts b/test/smoke/src/areas/terminal/terminal-tabs.test.ts index 161f077eeef..287956135a0 100644 --- a/test/smoke/src/areas/terminal/terminal-tabs.test.ts +++ b/test/smoke/src/areas/terminal/terminal-tabs.test.ts @@ -67,6 +67,15 @@ export function setup(opts: ParsedArgs) { await terminal.assertSingleTab({ name }); }); + it('should reset the tab name to the default value when no name is provided', async () => { + await terminal.runCommand(TerminalCommandId.Show); + const defaultName = await terminal.getSingleTabName(); + const name = 'my terminal name'; + await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, name); + await terminal.runCommandWithValue(TerminalCommandIdWithValue.Rename, undefined); + await terminal.assertSingleTab({ name: defaultName }); + }); + it('should rename the tab in the tabs list', async () => { await terminal.runCommand(TerminalCommandId.Show); await terminal.runCommand(TerminalCommandId.Split); From 3bd85562bb3ffd5a01e99ae4c8ca9d3bbf5d1990 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 21 Nov 2021 21:28:41 -0800 Subject: [PATCH 310/330] support dynamic label rendering. --- .../notebook/browser/notebook.contribution.ts | 5 +- .../viewParts/notebookEditorToolbar.ts | 266 +++++++++++++----- 2 files changed, 201 insertions(+), 70 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 795e10d1c02..5585fd0cb4c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -770,8 +770,9 @@ configurationRegistry.registerConfiguration({ }, [NotebookSetting.globalToolbarShowLabel]: { description: nls.localize('notebook.globalToolbarShowLabel', "Control whether the actions on the notebook toolbar should render label or not."), - type: 'boolean', - default: true, + type: 'string', + enum: ['always', 'never', 'dynamic'], + default: 'always', tags: ['notebookLayout'] }, [NotebookSetting.textOutputLineLimit]: { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 2569ebf1b93..7cc4f3303c1 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -29,9 +29,20 @@ import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/co import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; interface IActionModel { - action: IAction; size: number; visible: boolean; + action: IAction; + size: number; + visible: boolean; + renderLabel: boolean; } +enum RenderLabel { + Always = 0, + Never = 1, + Dynamic = 2 +} + +type RenderLabelWithFallback = true | false | 'always' | 'never' | 'dynamic'; + const TOGGLE_MORE_ACTION_WIDTH = 21; const ACTION_PADDING = 8; @@ -46,7 +57,7 @@ export class NotebookEditorToolbar extends Disposable { private _secondaryActions: IAction[]; private _notebookRightToolbar!: ToolBar; private _useGlobalToolbar: boolean = false; - private _renderLabel: boolean = true; + private _renderLabel: RenderLabel = RenderLabel.Always; private readonly _onDidChangeState = this._register(new Emitter()); onDidChangeState: Event = this._onDidChangeState.event; @@ -114,7 +125,7 @@ export class NotebookEditorToolbar extends Disposable { this._register(this._notebookGlobalActionsMenu); this._useGlobalToolbar = this.notebookOptions.getLayoutConfiguration().globalToolbar; - this._renderLabel = this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel); + this._renderLabel = this._convertConfiguration(this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel)); const context = { ui: true, @@ -127,8 +138,13 @@ export class NotebookEditorToolbar extends Disposable { return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); } - if (this._renderLabel) { - return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action) : undefined; + if (this._renderLabel !== RenderLabel.Never) { + const a = this._primaryActions.find(a => a.action.id === action.id); + if (a && a.renderLabel) { + return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action) : undefined; + } else { + return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined; + } } else { return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined; } @@ -185,7 +201,7 @@ export class NotebookEditorToolbar extends Disposable { this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(NotebookSetting.globalToolbarShowLabel)) { - this._renderLabel = this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel); + this._renderLabel = this._convertConfiguration(this.configurationService.getValue(NotebookSetting.globalToolbarShowLabel)); const oldElement = this._notebookLeftToolbar.getElement(); oldElement.parentElement?.removeChild(oldElement); this._notebookLeftToolbar.dispose(); @@ -214,6 +230,21 @@ export class NotebookEditorToolbar extends Disposable { } } + private _convertConfiguration(value: RenderLabelWithFallback): RenderLabel { + switch (value) { + case true: + return RenderLabel.Always; + case false: + return RenderLabel.Never; + case 'always': + return RenderLabel.Always; + case 'never': + return RenderLabel.Never; + case 'dynamic': + return RenderLabel.Dynamic; + } + } + private _showNotebookActionsinEditorToolbar() { // when there is no view model, just ignore. if (!this.notebookEditor.hasModel()) { @@ -223,48 +254,52 @@ export class NotebookEditorToolbar extends Disposable { if (!this._useGlobalToolbar) { this.domNode.style.display = 'none'; } else { - const groups = this._notebookGlobalActionsMenu.getActions({ shouldForwardArgs: true, renderShortTitle: true }); - this.domNode.style.display = 'flex'; - const primaryLeftGroups = groups.filter(group => /^navigation/.test(group[0])); - let primaryActions: IAction[] = []; - primaryLeftGroups.sort((a, b) => { - if (a[0] === 'navigation') { - return 1; - } - - if (b[0] === 'navigation') { - return -1; - } - - return 0; - }).forEach((group, index) => { - primaryActions.push(...group[1]); - if (index < primaryLeftGroups.length - 1) { - primaryActions.push(new Separator()); - } - }); - const primaryRightGroup = groups.find(group => /^status/.test(group[0])); - const primaryRightActions = primaryRightGroup ? primaryRightGroup[1] : []; - const secondaryActions = groups.filter(group => !/^navigation/.test(group[0]) && !/^status/.test(group[0])).reduce((prev: (MenuItemAction | SubmenuItemAction)[], curr) => { prev.push(...curr[1]); return prev; }, []); - - this._notebookLeftToolbar.setActions([], []); - - this._notebookLeftToolbar.setActions(primaryActions, secondaryActions); - this._notebookRightToolbar.setActions(primaryRightActions, []); - this._secondaryActions = secondaryActions; - // flush to make sure it can be updated later - this._primaryActions = []; - - if (this._dimension && this._dimension.width >= 0 && this._dimension.height >= 0) { - this._cacheItemSizes(this._notebookLeftToolbar); - } - - this._computeSizes(); + this._setNotebookActions(); } this._onDidChangeState.fire(); } + private _setNotebookActions() { + const groups = this._notebookGlobalActionsMenu.getActions({ shouldForwardArgs: true, renderShortTitle: true }); + this.domNode.style.display = 'flex'; + const primaryLeftGroups = groups.filter(group => /^navigation/.test(group[0])); + let primaryActions: IAction[] = []; + primaryLeftGroups.sort((a, b) => { + if (a[0] === 'navigation') { + return 1; + } + + if (b[0] === 'navigation') { + return -1; + } + + return 0; + }).forEach((group, index) => { + primaryActions.push(...group[1]); + if (index < primaryLeftGroups.length - 1) { + primaryActions.push(new Separator()); + } + }); + const primaryRightGroup = groups.find(group => /^status/.test(group[0])); + const primaryRightActions = primaryRightGroup ? primaryRightGroup[1] : []; + const secondaryActions = groups.filter(group => !/^navigation/.test(group[0]) && !/^status/.test(group[0])).reduce((prev: (MenuItemAction | SubmenuItemAction)[], curr) => { prev.push(...curr[1]); return prev; }, []); + + this._notebookLeftToolbar.setActions([], []); + + this._notebookLeftToolbar.setActions(primaryActions, secondaryActions); + this._notebookRightToolbar.setActions(primaryRightActions, []); + this._secondaryActions = secondaryActions; + // flush to make sure it can be updated later + this._primaryActions = []; + + if (this._dimension && this._dimension.width >= 0 && this._dimension.height >= 0) { + this._cacheItemSizes(this._notebookLeftToolbar); + } + + this._computeSizes(); + } + private _cacheItemSizes(toolbar: ToolBar) { let actions: IActionModel[] = []; @@ -273,7 +308,8 @@ export class NotebookEditorToolbar extends Disposable { actions.push({ action: action, size: toolbar.getItemWidth(i), - visible: true + visible: true, + renderLabel: true }); } @@ -305,39 +341,133 @@ export class NotebookEditorToolbar extends Disposable { const kernelWidth = (rightToolbar.getItemsLength() ? rightToolbar.getItemWidth(0) : 0) + ACTION_PADDING; if (this._canBeVisible(this._dimension.width - kernelWidth - ACTION_PADDING /** left margin */)) { - this._primaryActions.forEach(action => action.visible = true); + this._primaryActions.forEach(action => { + action.visible = true; + action.renderLabel = true; + }); toolbar.setActions(this._primaryActions.filter(action => action.action.id !== ToggleMenuAction.ID).map(model => model.action), this._secondaryActions); return; } const leftToolbarContainerMaxWidth = this._dimension.width - kernelWidth - (TOGGLE_MORE_ACTION_WIDTH + ACTION_PADDING) /** ... */ - ACTION_PADDING /** toolbar left margin */; - const lastItemInLeft = this._primaryActions[this._primaryActions.length - 1]; - const hasToggleMoreAction = lastItemInLeft.action.id === ToggleMenuAction.ID; - - let size = 0; - let actions: IActionModel[] = []; - - for (let i = 0; i < this._primaryActions.length - (hasToggleMoreAction ? 1 : 0); i++) { - const actionModel = this._primaryActions[i]; - - const itemSize = actionModel.size; - if (size + itemSize <= leftToolbarContainerMaxWidth) { - size += ACTION_PADDING + itemSize; - actions.push(actionModel); - } else { - break; - } + if (this._renderLabel === RenderLabel.Dynamic) { + this._calculateDynamicLabel(leftToolbarContainerMaxWidth); + } else { + this._calcuateFixedLabel(leftToolbarContainerMaxWidth); } - - actions.forEach(action => action.visible = true); - this._primaryActions.slice(actions.length).forEach(action => action.visible = false); - - toolbar.setActions( - actions.filter(action => (action.visible && action.action.id !== ToggleMenuAction.ID)).map(action => action.action), - [...this._primaryActions.slice(actions.length).filter(action => !action.visible && action.action.id !== ToggleMenuAction.ID).map(action => action.action), ...this._secondaryActions]); } } + private _calcuateFixedLabel(leftToolbarContainerMaxWidth: number) { + const lastItemInLeft = this._primaryActions[this._primaryActions.length - 1]; + const hasToggleMoreAction = lastItemInLeft.action.id === ToggleMenuAction.ID; + + let size = 0; + let actions: IActionModel[] = []; + + for (let i = 0; i < this._primaryActions.length - (hasToggleMoreAction ? 1 : 0); i++) { + const actionModel = this._primaryActions[i]; + + const itemSize = actionModel.size; + if (size + itemSize <= leftToolbarContainerMaxWidth) { + size += ACTION_PADDING + itemSize; + actions.push(actionModel); + } else { + break; + } + } + + actions.forEach(action => action.visible = true); + this._primaryActions.slice(actions.length).forEach(action => action.visible = false); + + this._notebookLeftToolbar.setActions( + actions.filter(action => (action.visible && action.action.id !== ToggleMenuAction.ID)).map(action => action.action), + [...this._primaryActions.slice(actions.length).filter(action => !action.visible && action.action.id !== ToggleMenuAction.ID).map(action => action.action), ...this._secondaryActions]); + } + + private _calculateDynamicLabel(leftToolbarContainerMaxWidth: number) { + const lastItemInLeft = this._primaryActions[this._primaryActions.length - 1]; + const hasToggleMoreAction = lastItemInLeft.action.id === ToggleMenuAction.ID; + const actions = this._primaryActions.slice(0, this._primaryActions.length - (hasToggleMoreAction ? 1 : 0)); + + if (actions.length === 0) { + this._notebookLeftToolbar.setActions(this._primaryActions.filter(action => (action.visible && action.action.id !== ToggleMenuAction.ID)).map(action => action.action), this._secondaryActions); + return; + } + + let totalWidthWithLabels = actions.map(action => action.size).reduce((a, b) => a + b, 0) + (actions.length - 1) * ACTION_PADDING; + if (totalWidthWithLabels <= leftToolbarContainerMaxWidth) { + this._primaryActions.forEach(action => { + action.visible = true; + action.renderLabel = true; + }); + this._notebookLeftToolbar.setActions(this._primaryActions.filter(action => (action.visible && action.action.id !== ToggleMenuAction.ID)).map(action => action.action), this._secondaryActions); + return; + } + + // too narrow, we need to hide some labels + + if ((actions.length * 21 + (actions.length - 1) * ACTION_PADDING) > leftToolbarContainerMaxWidth) { + this._calcuateWithAlllabelsHidden(actions, leftToolbarContainerMaxWidth); + return; + } + + const sums = []; + let sum = 0; + let lastActionWithLabel = -1; + for (let i = 0; i < actions.length; i++) { + sum += actions[i].size; + sums.push(sum); + + if (actions[i].action instanceof Separator) { + // find group separator + const remainingItems = actions.slice(i + 1); + const newTotalSum = sum + (remainingItems.length === 0 ? 0 : (remainingItems.length * 21 + (remainingItems.length - 1) * ACTION_PADDING)); + if (newTotalSum <= leftToolbarContainerMaxWidth) { + lastActionWithLabel = i; + } + } else { + continue; + } + } + + if (lastActionWithLabel < 0) { + this._calcuateWithAlllabelsHidden(actions, leftToolbarContainerMaxWidth); + return; + } + + const visibleActions = actions.slice(0, lastActionWithLabel + 1); + visibleActions.forEach(action => { action.visible = true; action.renderLabel = true; }); + this._primaryActions.slice(visibleActions.length).forEach(action => { action.visible = true; action.renderLabel = false; }); + this._notebookLeftToolbar.setActions(this._primaryActions.filter(action => (action.visible && action.action.id !== ToggleMenuAction.ID)).map(action => action.action), this._secondaryActions); + } + + private _calcuateWithAlllabelsHidden(actions: IActionModel[], leftToolbarContainerMaxWidth: number) { + // all actions hidden labels + this._primaryActions.forEach(action => { action.renderLabel = false; }); + let size = 0; + let renderActions: IActionModel[] = []; + + for (let i = 0; i < actions.length; i++) { + const actionModel = actions[i]; + + const itemSize = 21; + if (size + itemSize <= leftToolbarContainerMaxWidth) { + size += ACTION_PADDING + itemSize; + renderActions.push(actionModel); + } else { + break; + } + } + + actions.forEach(action => action.visible = true); + this._primaryActions.slice(actions.length).forEach(action => action.visible = false); + + this._notebookLeftToolbar.setActions( + actions.filter(action => (action.visible && action.action.id !== ToggleMenuAction.ID)).map(action => action.action), + [...this._primaryActions.slice(actions.length).filter(action => !action.visible && action.action.id !== ToggleMenuAction.ID).map(action => action.action), ...this._secondaryActions]); + } + layout(dimension: DOM.Dimension) { this._dimension = dimension; From 05012ac17714c828c4daa136f58c2bc5b675f195 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 21 Nov 2021 21:39:44 -0800 Subject: [PATCH 311/330] update nb doc. --- src/vs/workbench/contrib/notebook/browser/notebook.layout.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md index 28e65b0c836..715ff1317b1 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/notebook.layout.md @@ -19,7 +19,7 @@ The catch here is while rendering a cell/row, if we happen to perform any DOM re The worflow of rendering a code cell with a text output is like below and all operations are synchronous -![render in the core](https://user-images.githubusercontent.com/876920/142256110-a1c5800f-be46-4bd2-bbff-077c1c73e1fd.png) +![render in the core](https://user-images.githubusercontent.com/876920/142806570-a477d315-40f3-4e0c-8079-f2867d5f3e88.png) When the notebook document contains markdown cells or rich outputs, the workflow is a bit more complex and become asynchornously partially due to the fact the markdown and rich outputs are rendered in a separate webview/iframe. While the list view renders the cell/row, it will send requests to the webview for output rendering, the rendering result (like dimensions of the output elements) won't come back in current frame. Once we receive the output rendering results from the webview (say next frame), we would ask the list view to adjust the position/dimension of the cell and ones below. From 3c0f5b2aaedc8733fac7b3cb60ba2f996e20a6eb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 22 Nov 2021 08:18:59 +0100 Subject: [PATCH 312/330] Fixes #134234: Remove `__uniqueWebWorkerExtensionHostOrigin` option --- .../browser/webWorkerExtensionHost.ts | 39 ++++++++----------- src/vs/workbench/workbench.web.api.ts | 7 ---- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 1a8d82ad570..a3ac30b427e 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -92,30 +92,23 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost } const forceHTTPS = (location.protocol === 'https:'); + const webEndpointUrlTemplate = this._productService.webEndpointUrlTemplate; + const commit = this._productService.commit; + const quality = this._productService.quality; + if (webEndpointUrlTemplate && commit && quality) { + const baseUrl = ( + webEndpointUrlTemplate + .replace('{{uuid}}', generateUuid()) + .replace('{{commit}}', commit) + .replace('{{quality}}', quality) + ); + const base = ( + forceHTTPS + ? `${baseUrl}/out/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html` + : `${baseUrl}/out/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html` + ); - let uniqueWebWorkerExtensionHostOrigin = true; - if (this._environmentService.options && typeof this._environmentService.options.__uniqueWebWorkerExtensionHostOrigin !== 'undefined') { - uniqueWebWorkerExtensionHostOrigin = this._environmentService.options.__uniqueWebWorkerExtensionHostOrigin; - } - if (uniqueWebWorkerExtensionHostOrigin) { - const webEndpointUrlTemplate = this._productService.webEndpointUrlTemplate; - const commit = this._productService.commit; - const quality = this._productService.quality; - if (webEndpointUrlTemplate && commit && quality) { - const baseUrl = ( - webEndpointUrlTemplate - .replace('{{uuid}}', generateUuid()) - .replace('{{commit}}', commit) - .replace('{{quality}}', quality) - ); - const base = ( - forceHTTPS - ? `${baseUrl}/out/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html` - : `${baseUrl}/out/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html` - ); - - return base + suffix; - } + return base + suffix; } if (this._productService.webEndpointUrl) { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 051070b0d9a..d619ce20089 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -364,13 +364,6 @@ interface IWorkbenchConstructionOptions { */ readonly webWorkerExtensionHostIframeSrc?: string; - /** - * [TEMPORARY]: This will be removed soon. - * Use an unique origin for the web worker extension host. - * Defaults to true. - */ - readonly __uniqueWebWorkerExtensionHostOrigin?: boolean; - /** * A factory for web sockets. */ From 20fc98529f282cb3be34273773b4507c554c8653 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 22 Nov 2021 09:09:24 +0100 Subject: [PATCH 313/330] Adopt `queueMicrotask` (#133173) --- src/vs/base/parts/ipc/common/ipc.net.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index f72112c1522..4a708e614fa 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -6,8 +6,6 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import * as platform from 'vs/base/common/platform'; -import * as process from 'vs/base/common/process'; import { IIPCLogger, IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc'; export const enum SocketCloseEventType { @@ -370,7 +368,7 @@ class ProtocolWriter { private _writeSoon(header: VSBuffer, data: VSBuffer): void { if (this._bufferAdd(header, data)) { - platform.setImmediate(() => { + queueMicrotask(() => { this._writeNow(); }); } @@ -484,7 +482,7 @@ export class BufferedEmitter { // it is important to deliver these messages after this call, but before // other messages have a chance to be received (to guarantee in order delivery) // that's why we're using here nextTick and not other types of timeouts - process.nextTick(() => this._deliverMessages()); + queueMicrotask(() => this._deliverMessages()); }, onLastListenerRemove: () => { this._hasListeners = false; From 6b07ef8a343a68357a64e55b31d23239fc25d7e9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 22 Nov 2021 10:31:27 +0100 Subject: [PATCH 314/330] Partially revert 20fc98529f282cb3be34273773b4507c554c8653 due to failing integration tests --- src/vs/base/parts/ipc/common/ipc.net.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 4a708e614fa..6229c073c7a 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -6,6 +6,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; import { IIPCLogger, IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc'; export const enum SocketCloseEventType { @@ -368,7 +369,7 @@ class ProtocolWriter { private _writeSoon(header: VSBuffer, data: VSBuffer): void { if (this._bufferAdd(header, data)) { - queueMicrotask(() => { + platform.setImmediate(() => { this._writeNow(); }); } From aa21039c3cd69bd45db0a62df1fb0cf5edc28452 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 22 Nov 2021 11:51:08 +0100 Subject: [PATCH 315/330] :up: iconv-lite --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- extensions/git/package.json | 2 +- extensions/git/yarn.lock | 8 ++++---- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/build/package.json b/build/package.json index 0d8ca2f1c40..7b1131e894c 100644 --- a/build/package.json +++ b/build/package.json @@ -54,7 +54,7 @@ "fs-extra": "^9.1.0", "got": "11.8.1", "gulp-merge-json": "^2.1.1", - "iconv-lite-umd": "0.6.8", + "iconv-lite-umd": "0.6.10", "jsonc-parser": "^2.3.0", "mime": "^1.4.1", "mkdirp": "^1.0.4", diff --git a/build/yarn.lock b/build/yarn.lock index b5825310739..bf575cab8d4 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -1627,10 +1627,10 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -iconv-lite-umd@0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" - integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== +iconv-lite-umd@0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" + integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== inflight@^1.0.4: version "1.0.6" diff --git a/extensions/git/package.json b/extensions/git/package.json index ec5d16e0dbd..c508830b8d8 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2365,7 +2365,7 @@ "dependencies": { "byline": "^5.0.0", "file-type": "^7.2.0", - "iconv-lite-umd": "0.6.8", + "iconv-lite-umd": "0.6.10", "jschardet": "3.0.0", "vscode-extension-telemetry": "0.4.3", "vscode-nls": "^4.0.0", diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 36a12fb119e..ff535a1ee3a 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -46,10 +46,10 @@ file-type@^7.2.0: resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74" integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q= -iconv-lite-umd@0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" - integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== +iconv-lite-umd@0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" + integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== isexe@^2.0.0: version "2.0.0" diff --git a/package.json b/package.json index 736e2a4dead..3fec7491aaa 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "graceful-fs": "4.2.8", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite-umd": "0.6.8", + "iconv-lite-umd": "0.6.10", "jschardet": "3.0.0", "keytar": "7.2.0", "minimist": "^1.2.5", diff --git a/remote/package.json b/remote/package.json index 09901109015..27a382951c2 100644 --- a/remote/package.json +++ b/remote/package.json @@ -11,7 +11,7 @@ "graceful-fs": "4.2.8", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite-umd": "0.6.8", + "iconv-lite-umd": "0.6.10", "jschardet": "3.0.0", "minimist": "^1.2.5", "native-watchdog": "1.3.0", diff --git a/remote/web/package.json b/remote/web/package.json index 6093712246c..e44ba1550a4 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -5,7 +5,7 @@ "dependencies": { "@microsoft/applicationinsights-web": "^2.6.4", "@vscode/vscode-languagedetection": "1.0.21", - "iconv-lite-umd": "0.6.8", + "iconv-lite-umd": "0.6.10", "jschardet": "3.0.0", "tas-client-umd": "0.1.4", "vscode-oniguruma": "1.6.1", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 40d4b7c658b..4144af4392b 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -88,10 +88,10 @@ resolved "https://registry.yarnpkg.com/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz#89b48f293f6aa3341bb888c1118d16ff13b032d3" integrity sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g== -iconv-lite-umd@0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" - integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== +iconv-lite-umd@0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" + integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== jschardet@3.0.0: version "3.0.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index a96bdf2daf4..5b1c1bd4b32 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -305,10 +305,10 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -iconv-lite-umd@0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" - integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== +iconv-lite-umd@0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" + integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== inherits@~2.0.1: version "2.0.4" diff --git a/yarn.lock b/yarn.lock index d1842821be2..9ff8176cc69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5298,10 +5298,10 @@ husky@^0.13.1: is-ci "^1.0.9" normalize-path "^1.0.0" -iconv-lite-umd@0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" - integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== +iconv-lite-umd@0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" + integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== iconv-lite@^0.4.19: version "0.4.19" From 7be48d527a1a7fdf5fbaf4fc7af52195e6fa0bac Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 22 Nov 2021 12:17:22 +0100 Subject: [PATCH 316/330] :up: web playground --- resources/web/code-web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 0625a848805..e2e23f9edec 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -37,7 +37,7 @@ const ALLOWED_CORS_ORIGINS = [ 'http://127.0.0.1:8080', ]; -const WEB_PLAYGROUND_VERSION = '0.0.12'; +const WEB_PLAYGROUND_VERSION = '0.0.13'; const args = minimist(process.argv, { boolean: [ From 2fa5d4f76e9e9b9f2d68dc0d77d910e2c506a6c8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 22 Nov 2021 14:51:21 +0100 Subject: [PATCH 317/330] Adopt `runWhenIdle` (#133173) --- src/vs/base/common/async.ts | 47 +++++++++++++++++-- src/vs/editor/common/model/textModelTokens.ts | 31 +++++++----- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 0972e6f8bf1..3257ab9d802 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -968,6 +968,45 @@ export interface IdleDeadline { readonly didTimeout: boolean; timeRemaining(): number; } + +/** + * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-. + * + * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay + * that browsers set when the nesting level is > 5. + */ +const scheduleAsyncWork = (() => { + if (typeof globalThis.postMessage === 'function' && !globalThis.importScripts) { + interface IQueueElement { + id: number; + callback: () => void; + } + let pending: IQueueElement[] = []; + globalThis.addEventListener('message', (e: MessageEvent) => { + if (e.data && e.data.vscodeScheduleAsyncWork) { + for (let i = 0, len = pending.length; i < len; i++) { + const candidate = pending[i]; + if (candidate.id === e.data.vscodeScheduleAsyncWork) { + pending.splice(i, 1); + candidate.callback(); + return; + } + } + } + }); + let lastId = 0; + return (callback: () => void) => { + const myId = ++lastId; + pending.push({ + id: myId, + callback: callback + }); + globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*'); + }; + } + return (callback: () => void) => setTimeout(callback); +})(); + /** * Execute the callback the next time the browser is idle */ @@ -979,8 +1018,11 @@ declare function cancelIdleCallback(handle: number): void; (function () { if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { runWhenIdle = (runner) => { - const handle = setTimeout(() => { - const end = Date.now() + 15; // one frame at 64fps + scheduleAsyncWork(() => { + if (disposed) { + return; + } + const end = Date.now() + 3; // yield often runner(Object.freeze({ didTimeout: true, timeRemaining() { @@ -995,7 +1037,6 @@ declare function cancelIdleCallback(handle: number): void; return; } disposed = true; - clearTimeout(handle); } }; }; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 5199d3e7003..16ccb6e35fa 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -15,7 +15,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { Disposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { MultilineTokensBuilder, countEOL } from 'vs/editor/common/model/tokensStore'; -import * as platform from 'vs/base/common/platform'; +import { runWhenIdle } from 'vs/base/common/async'; const enum Constants { CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048 @@ -255,19 +255,26 @@ export class TextModelTokenization extends Disposable { this._beginBackgroundTokenization(); } + private _isScheduled = false; private _beginBackgroundTokenization(): void { - if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize()) { - platform.setImmediate(() => { - if (this._isDisposed) { - // disposed in the meantime - return; - } - this._revalidateTokensNow(); - }); + if (this._isScheduled || !this._textModel.isAttachedToEditor() || !this._hasLinesToTokenize()) { + return; } + + this._isScheduled = true; + runWhenIdle((deadline) => { + this._isScheduled = false; + + if (this._isDisposed) { + // disposed in the meantime + return; + } + + this._revalidateTokensNow(deadline); + }); } - private _revalidateTokensNow(): void { + private _revalidateTokensNow(deadline: IdleDeadline): void { const textModelLastLineNumber = this._textModel.getLineCount(); const MAX_ALLOWED_TIME = 1; @@ -275,7 +282,7 @@ export class TextModelTokenization extends Disposable { const sw = StopWatch.create(false); let tokenizedLineNumber = -1; - while (this._hasLinesToTokenize()) { + do { if (sw.elapsed() > MAX_ALLOWED_TIME) { // Stop if MAX_ALLOWED_TIME is reached break; @@ -286,7 +293,7 @@ export class TextModelTokenization extends Disposable { if (tokenizedLineNumber >= textModelLastLineNumber) { break; } - } + } while (this._hasLinesToTokenize() && deadline.timeRemaining() > 0); this._beginBackgroundTokenization(); this._textModel.setTokens(builder.tokens, !this._hasLinesToTokenize()); From 9bd899fabad214ad2385cc2a252a5deb581049e3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 22 Nov 2021 14:59:46 +0100 Subject: [PATCH 318/330] Check the active element also when 'focusin' or 'focusout' bubble. This change was initially done only in the editor for microsoft/monaco-editor#2339, but after a discussion we have agreed to bring it to all users of `FocusTracker`. Co-authored-by: Johannes Rieken --- src/vs/base/browser/dom.ts | 2 ++ src/vs/editor/browser/widget/codeEditorWidget.ts | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 3a6cc1c87ad..cff9352a148 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -978,6 +978,8 @@ class FocusTracker extends Disposable implements IFocusTracker { this._register(addDisposableListener(element, EventType.FOCUS, onFocus, true)); this._register(addDisposableListener(element, EventType.BLUR, onBlur, true)); + this._register(addDisposableListener(element, EventType.FOCUS_IN, () => this._refreshStateHandler())); + this._register(addDisposableListener(element, EventType.FOCUS_OUT, () => this._refreshStateHandler())); } refreshState() { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index be2140eea29..acb8fb98c0c 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -2007,12 +2007,6 @@ class CodeEditorWidgetFocusTracker extends Disposable { this._hasFocus = false; this._onChange.fire(undefined); })); - this._register(dom.addDisposableListener(domElement, 'focusin', () => { - this._domFocusTracker.refreshState(); - })); - this._register(dom.addDisposableListener(domElement, 'focusout', () => { - this._domFocusTracker.refreshState(); - })); } public hasFocus(): boolean { From dee69dc8078b0ddf2fc4d5538d446c937d8d5cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 22 Nov 2021 15:05:39 +0100 Subject: [PATCH 319/330] build: :arrow_up: gulp-atom-electron --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3fec7491aaa..0cd6378bac8 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "file-loader": "^4.2.0", "glob": "^5.0.13", "gulp": "^4.0.0", - "gulp-atom-electron": "1.32.0", + "gulp-atom-electron": "^1.32.1", "gulp-azure-storage": "^0.12.1", "gulp-bom": "^3.0.0", "gulp-buffer": "0.0.2", diff --git a/yarn.lock b/yarn.lock index 9ff8176cc69..0733d18dbee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4779,10 +4779,10 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -gulp-atom-electron@1.32.0: - version "1.32.0" - resolved "https://registry.yarnpkg.com/gulp-atom-electron/-/gulp-atom-electron-1.32.0.tgz#5ddfdfea3fda3dc2a81686a358a82e2387030dc3" - integrity sha512-epw+yaEIEywwEiTW2qbNLRvERapcqFCKaEVwxK1uyFCULiNi7RDnLYIyAO8etLRdk3mkTFvlRNzLWwu4B75QFw== +gulp-atom-electron@^1.32.1: + version "1.32.1" + resolved "https://registry.yarnpkg.com/gulp-atom-electron/-/gulp-atom-electron-1.32.1.tgz#5a7ae8b79338d0f9c55f2714048111c5fff17ad7" + integrity sha512-hsuj0y50QCFR+SrDsmOOPE7gEPRx5PpVbi4lEl5OySNEuGmUAUdTboN1LTM2UwvUQtxdDOIEUZ6WhuFDnzUWYw== dependencies: "@electron/get" "^1.12.4" "@octokit/rest" "^18.0.14" From 9d56323618ad08081c421be620f1325884fd21ae Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 22 Nov 2021 15:09:24 +0100 Subject: [PATCH 320/330] Extract the workaround for `setTimeout` artificially being delayed by 4ms to a separate function. --- src/vs/base/common/async.ts | 41 +------------------ src/vs/base/common/platform.ts | 27 ++++++++---- src/vs/editor/common/model/textModelTokens.ts | 2 +- 3 files changed, 23 insertions(+), 47 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 3257ab9d802..f2f264f747c 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -9,6 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { extUri as defaultExtUri, IExtUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { setTimeout0 } from 'vs/base/common/platform'; export function isThenable(obj: unknown): obj is Promise { return !!obj && typeof (obj as unknown as Promise).then === 'function'; @@ -969,44 +970,6 @@ export interface IdleDeadline { timeRemaining(): number; } -/** - * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-. - * - * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay - * that browsers set when the nesting level is > 5. - */ -const scheduleAsyncWork = (() => { - if (typeof globalThis.postMessage === 'function' && !globalThis.importScripts) { - interface IQueueElement { - id: number; - callback: () => void; - } - let pending: IQueueElement[] = []; - globalThis.addEventListener('message', (e: MessageEvent) => { - if (e.data && e.data.vscodeScheduleAsyncWork) { - for (let i = 0, len = pending.length; i < len; i++) { - const candidate = pending[i]; - if (candidate.id === e.data.vscodeScheduleAsyncWork) { - pending.splice(i, 1); - candidate.callback(); - return; - } - } - } - }); - let lastId = 0; - return (callback: () => void) => { - const myId = ++lastId; - pending.push({ - id: myId, - callback: callback - }); - globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*'); - }; - } - return (callback: () => void) => setTimeout(callback); -})(); - /** * Execute the callback the next time the browser is idle */ @@ -1018,7 +981,7 @@ declare function cancelIdleCallback(handle: number): void; (function () { if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { runWhenIdle = (runner) => { - scheduleAsyncWork(() => { + setTimeout0(() => { if (disposed) { return; } diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 1a571e41337..973ac98d615 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -190,10 +190,13 @@ interface ISetImmediate { (callback: (...args: unknown[]) => void): void; } -export const setImmediate: ISetImmediate = (function defineSetImmediate() { - if (globals.setImmediate) { - return globals.setImmediate.bind(globals); - } +/** + * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-. + * + * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay + * that browsers set when the nesting level is > 5. + */ +export const setTimeout0 = (() => { if (typeof globals.postMessage === 'function' && !globals.importScripts) { interface IQueueElement { id: number; @@ -201,10 +204,10 @@ export const setImmediate: ISetImmediate = (function defineSetImmediate() { } let pending: IQueueElement[] = []; globals.addEventListener('message', (e: MessageEvent) => { - if (e.data && e.data.vscodeSetImmediateId) { + if (e.data && e.data.vscodeScheduleAsyncWork) { for (let i = 0, len = pending.length; i < len; i++) { const candidate = pending[i]; - if (candidate.id === e.data.vscodeSetImmediateId) { + if (candidate.id === e.data.vscodeScheduleAsyncWork) { pending.splice(i, 1); candidate.callback(); return; @@ -219,9 +222,19 @@ export const setImmediate: ISetImmediate = (function defineSetImmediate() { id: myId, callback: callback }); - globals.postMessage({ vscodeSetImmediateId: myId }, '*'); + globals.postMessage({ vscodeScheduleAsyncWork: myId }, '*'); }; } + return (callback: () => void) => setTimeout(callback); +})(); + +export const setImmediate: ISetImmediate = (function defineSetImmediate() { + if (globals.setImmediate) { + return globals.setImmediate.bind(globals); + } + if (typeof globals.postMessage === 'function' && !globals.importScripts) { + return setTimeout0; + } if (typeof nodeProcess?.nextTick === 'function') { return nodeProcess.nextTick.bind(nodeProcess); } diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 16ccb6e35fa..cabbec32642 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -15,7 +15,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { Disposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { MultilineTokensBuilder, countEOL } from 'vs/editor/common/model/tokensStore'; -import { runWhenIdle } from 'vs/base/common/async'; +import { runWhenIdle, IdleDeadline } from 'vs/base/common/async'; const enum Constants { CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048 From 74e7864370241df106ff7a330a8aaf2a3b6735a5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Nov 2021 06:11:34 -0800 Subject: [PATCH 321/330] Fix ctrl+delete to correctly send alt+d (ESC + d) Fixes #136994 --- .../contrib/terminal/browser/terminal.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index e5653c210f0..10055403665 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -173,8 +173,8 @@ if (isWindows) { primary: KeyMod.CtrlCmd | KeyCode.Backspace, }); } -// Delete word right: alt+d -registerSendSequenceKeybinding('\u000d', { +// Delete word right: alt+d [27, 100] +registerSendSequenceKeybinding('\u001bd', { primary: KeyMod.CtrlCmd | KeyCode.Delete, mac: { primary: KeyMod.Alt | KeyCode.Delete } }); From 0dce4690e87a066f202731d01eb9526d9df566a5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Nov 2021 06:25:22 -0800 Subject: [PATCH 322/330] Don't force a tab refresh when doing a terminal tabs layout Fixes #133635 --- src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index adae67849dc..c7ed0495e46 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -318,7 +318,6 @@ export class TerminalTabbedView extends Disposable { if (this._shouldShowTabs()) { this._splitView.resizeView(this._tabTreeIndex, this._getLastListWidth()); } - this._rerenderTabs(); } private _updateTheme(theme?: IColorTheme): void { From 145e3802769319046f6fb35db76c5ca85b8ae1a1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Nov 2021 06:42:36 -0800 Subject: [PATCH 323/330] Fix copy in process explorer Fixes #137597 --- .../electron-sandbox/processExplorer/processExplorerMain.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 74c171ceb34..5f2040266cf 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -181,12 +181,14 @@ class ProcessRenderer implements ITreeRenderer { - const row = document.getElementById(pid.toString()); + const row = document.getElementById(`pid-${pid}`); if (row) { this.nativeHostService.writeClipboardText(row.innerText); } From 01d105b4a661a791920e18aa650960a839460cac Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:13:22 +0100 Subject: [PATCH 324/330] Add SCM contribution to manage context key for active resource changes (#137297) --- extensions/git/package.json | 3 +- .../workbench/contrib/scm/browser/activity.ts | 67 ++++++++++++++++++- .../contrib/scm/browser/scm.contribution.ts | 5 +- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c508830b8d8..a26962bab09 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -81,7 +81,8 @@ "command": "git.openChange", "title": "%command.openChange%", "category": "Git", - "icon": "$(compare-changes)" + "icon": "$(compare-changes)", + "enablement": "scmActiveResourceHasChanges" }, { "command": "git.openAllChanges", diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index eaf9a8766d0..ce1fb353da4 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -10,13 +10,14 @@ import { Event } from 'vs/base/common/event'; import { VIEW_PANE_ID, ISCMService, ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorResourceAccessor } from 'vs/workbench/common/editor'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { stripIcons } from 'vs/base/common/iconLabels'; +import { Schemas } from 'vs/base/common/network'; function getCount(repository: ISCMRepository): number { if (typeof repository.provider.count === 'number') { @@ -193,3 +194,67 @@ export class SCMStatusController implements IWorkbenchContribution { this.repositoryDisposables.clear(); } } + +export class SCMActiveResourceContextKeyController implements IWorkbenchContribution { + + private contextKey: IContextKey; + private disposables = new DisposableStore(); + private repositoryDisposables = new Set(); + + constructor( + @IContextKeyService readonly contextKeyService: IContextKeyService, + @IEditorService private readonly editorService: IEditorService, + @ISCMService private readonly scmService: ISCMService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + ) { + this.contextKey = contextKeyService.createKey('scmActiveResourceHasChanges', false); + + this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + + for (const repository of this.scmService.repositories) { + this.onDidAddRepository(repository); + } + + editorService.onDidActiveEditorChange(this.updateContextKey, this, this.disposables); + } + + private onDidAddRepository(repository: ISCMRepository): void { + const onDidChange = Event.any(repository.provider.onDidChange, repository.provider.onDidChangeResources); + const changeDisposable = onDidChange(() => this.updateContextKey()); + + const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository); + const removeDisposable = onDidRemove(() => { + disposable.dispose(); + this.repositoryDisposables.delete(disposable); + this.updateContextKey(); + }); + + const disposable = combinedDisposable(changeDisposable, removeDisposable); + this.repositoryDisposables.add(disposable); + } + + private updateContextKey(): void { + const activeResource = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor); + + if (activeResource && activeResource.scheme === Schemas.file) { + for (const repository of this.scmService.repositories) { + for (const resourceGroup of repository.provider.groups.elements) { + if (resourceGroup.elements.find(scmResource => { + return this.uriIdentityService.extUri.isEqual(activeResource, scmResource.sourceUri); + })) { + this.contextKey.set(true); + return; + } + } + } + } + + this.contextKey.set(false); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + dispose(this.repositoryDisposables.values()); + this.repositoryDisposables.clear(); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 8728eff160a..a2a5d265b66 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -10,7 +10,7 @@ import { DirtyDiffWorkbenchController } from './dirtydiffDecorator'; import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { SCMStatusController } from './activity'; +import { SCMActiveResourceContextKeyController, SCMStatusController } from './activity'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -97,6 +97,9 @@ viewsRegistry.registerViews([{ containerIcon: sourceControlViewIcon }], viewContainer); +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored); + Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(SCMStatusController, LifecyclePhase.Restored); From d159ea189ba3368d8114969a9e2dd7921188daef Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 22 Nov 2021 14:54:45 +0100 Subject: [PATCH 325/330] improve json language indicator --- .../client/src/languageStatus.ts | 80 ++++++++++++++++--- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/extensions/json-language-features/client/src/languageStatus.ts b/extensions/json-language-features/client/src/languageStatus.ts index c3366399b13..63541be773d 100644 --- a/extensions/json-language-features/client/src/languageStatus.ts +++ b/extensions/json-language-features/client/src/languageStatus.ts @@ -3,28 +3,85 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, languages, Uri, LanguageStatusSeverity, Disposable, commands, QuickPickItem } from 'vscode'; +import { window, languages, Uri, LanguageStatusSeverity, Disposable, commands, QuickPickItem, extensions, workspace } from 'vscode'; import { JSONLanguageStatus } from './jsonClient'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); +type ShowSchemasInput = { + schemas: string[]; + uri: string; +}; + +interface ShowSchemasItem extends QuickPickItem { + uri: Uri; +} + +function equalsIgnoreCase(a: string, b: string): boolean { + return a.length === b.length && a.toLowerCase().localeCompare(b.toLowerCase()) === 0; +} + +function isEqualAuthority(a1: string | undefined, a2: string | undefined) { + return a1 === a2 || (a1 !== undefined && a2 !== undefined && equalsIgnoreCase(a1, a2)); +} + +function findExtension(uri: Uri) { + for (const ext of extensions.all) { + const parent = ext.extensionUri; + if (uri.scheme === parent.scheme && isEqualAuthority(uri.authority, parent.authority) && uri.path.startsWith(parent.path + '/')) { + return ext; + } + } + return undefined; +} + +function findWorkspaceFolder(uri: Uri) { + if (workspace.workspaceFolders) { + for (const wf of workspace.workspaceFolders) { + const parent = wf.uri; + if (uri.scheme === parent.scheme && isEqualAuthority(uri.authority, parent.authority) && uri.path.startsWith(parent.path + '/')) { + return wf; + } + } + } + return undefined; +} + +function renderShowSchemasItem(schema: string): ShowSchemasItem { + const uri = Uri.parse(schema); + const extension = findExtension(uri); + if (extension) { + return { label: extension.id, description: uri.path.substring(extension.extensionUri.path.length + 1), uri }; + } + const wf = findWorkspaceFolder(uri); + if (wf) { + return { label: uri.path.substring(wf.uri.path.length + 1), description: 'Workspace', uri }; + } + if (uri.scheme === 'file') { + return { label: uri.fsPath, uri }; + } else if (uri.scheme === 'vscode') { + return { label: schema, description: 'internally generated', uri }; + } + return { label: schema, uri }; +} + + export function createLanguageStatusItem(documentSelector: string[], statusRequest: (uri: string) => Promise): Disposable { const statusItem = languages.createLanguageStatusItem('json.projectStatus', documentSelector); statusItem.name = localize('statusItem.name', "JSON Validation Status"); statusItem.severity = LanguageStatusSeverity.Information; - const showSchemasCommand = commands.registerCommand('_json.showAssociatedSchemaList', arg => { - const items = arg.schemas.sort().map((a: string) => ({ label: a })); - const quickPick = window.createQuickPick(); - quickPick.title = localize('schemaPicker.title', 'Associated JSON Schemas'); + const showSchemasCommand = commands.registerCommand('_json.showAssociatedSchemaList', (arg: ShowSchemasInput) => { + const items: ShowSchemasItem[] = arg.schemas.sort().map(renderShowSchemasItem); + const quickPick = window.createQuickPick(); + quickPick.title = localize('schemaPicker.title', 'JSON Schemas used for {0}', arg.uri.toString()); quickPick.placeholder = localize('schemaPicker.placeholder', 'Select the schema to open'); quickPick.items = items; quickPick.show(); quickPick.onDidAccept(() => { - const selectedSchema = quickPick.selectedItems[0].label; - commands.executeCommand('vscode.open', Uri.parse(selectedSchema)); + commands.executeCommand('vscode.open', quickPick.selectedItems[0].uri); quickPick.dispose(); }); }); @@ -46,19 +103,20 @@ export function createLanguageStatusItem(documentSelector: string[], statusReque if (schemas.length === 0) { statusItem.text = localize('status.noSchema', 'Validated without JSON schema'); } else if (schemas.length === 1) { + const item = renderShowSchemasItem(schemas[0]); statusItem.text = localize('status.singleSchema', 'Validated with JSON schema'); statusItem.command = { command: 'vscode.open', title: localize('status.openSchemaLink', 'Open Schema'), - tooltip: schemas[0], - arguments: [Uri.parse(schemas[0])] + tooltip: item.description ? `${item.label} - ${item.description}` : item.label, + arguments: [item.uri] }; } else { statusItem.text = localize('status.multipleSchema', 'Validated with multiple JSON schemas'); statusItem.command = { command: '_json.showAssociatedSchemaList', title: localize('status.openSchemasLink', 'Show Schemas'), - arguments: [{ schemas }] + arguments: [{ schemas, uri: document.uri.toString() } as ShowSchemasInput] }; } } catch (e) { @@ -79,5 +137,3 @@ export function createLanguageStatusItem(documentSelector: string[], statusReque return Disposable.from(statusItem, activeEditorListener, showSchemasCommand); } - - From 479cc2c593391678552ed48743edbaee2375f598 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 22 Nov 2021 17:22:38 +0100 Subject: [PATCH 326/330] add --help and --version to server CLI. For #137658 --- src/vs/platform/environment/node/argv.ts | 10 ++++++---- src/vs/server/main.js | 9 ++++----- src/vs/server/remoteCli.ts | 2 +- src/vs/server/remoteExtensionHostAgent.ts | 2 +- src/vs/server/remoteExtensionHostAgentCli.ts | 16 +++++++++++++++- src/vs/server/serverEnvironmentService.ts | 7 +++++++ 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 821cbf257f6..763442884a9 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -290,14 +290,16 @@ function wrapText(text: string, columns: number): string[] { return lines; } -export function buildHelpMessage(productName: string, executableName: string, version: string, options: OptionDescriptions, isPipeSupported = true): string { +export function buildHelpMessage(productName: string, executableName: string, version: string, options: OptionDescriptions, capabilities?: { noPipe?: boolean, noInputFiles: boolean }): string { const columns = (process.stdout).isTTY && (process.stdout).columns || 80; - let help = [`${productName} ${version}`]; + const inputFiles = capabilities?.noInputFiles !== true ? `[${localize('paths', 'paths')}...]` : ''; + + const help = [`${productName} ${version}`]; help.push(''); - help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`); + help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}]${inputFiles}`); help.push(''); - if (isPipeSupported) { + if (capabilities?.noPipe !== true) { if (isWindows) { help.push(localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", executableName)); } else { diff --git a/src/vs/server/main.js b/src/vs/server/main.js index 37c7dab5a78..d223052aa7a 100644 --- a/src/vs/server/main.js +++ b/src/vs/server/main.js @@ -24,14 +24,13 @@ async function start() { // Do a quick parse to determine if a server or the cli needs to be started const parsedArgs = minimist(process.argv.slice(2), { - boolean: ['start-server', 'list-extensions', 'print-ip-address'], + boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version'], string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port', 'pick-port'] }); - const shouldSpawnCli = ( - !parsedArgs['start-server'] && - (!!parsedArgs['list-extensions'] || !!parsedArgs['install-extension'] || !!parsedArgs['install-builtin-extension'] || !!parsedArgs['uninstall-extension'] || !!parsedArgs['locate-extension']) - ); + const extensionCliArgs = ['list-extensions', 'install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension']; + + const shouldSpawnCli = parsedArgs.help || parsedArgs.version || !parsedArgs['start-server'] && extensionCliArgs.some(a => !!parsedArgs[a]); if (shouldSpawnCli) { loadCode().then((mod) => { diff --git a/src/vs/server/remoteCli.ts b/src/vs/server/remoteCli.ts index 0210fcaa0b6..5dd86c63814 100644 --- a/src/vs/server/remoteCli.ts +++ b/src/vs/server/remoteCli.ts @@ -121,7 +121,7 @@ export function main(desc: ProductDescription, args: string[]): void { const verbose = !!parsedArgs['verbose']; if (parsedArgs.help) { - console.log(buildHelpMessage(desc.productName, desc.executableName, desc.version, options, true)); + console.log(buildHelpMessage(desc.productName, desc.executableName, desc.version, options)); return; } if (parsedArgs.version) { diff --git a/src/vs/server/remoteExtensionHostAgent.ts b/src/vs/server/remoteExtensionHostAgent.ts index ab226cd31f4..1c45e1e21ed 100644 --- a/src/vs/server/remoteExtensionHostAgent.ts +++ b/src/vs/server/remoteExtensionHostAgent.ts @@ -53,7 +53,7 @@ args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'ext * invoked by vs/server/main.js */ export function spawnCli() { - runCli(args, REMOTE_DATA_FOLDER); + runCli(args, REMOTE_DATA_FOLDER, serverOptions); } /** diff --git a/src/vs/server/remoteExtensionHostAgentCli.ts b/src/vs/server/remoteExtensionHostAgentCli.ts index f6b66dffed6..d375069f5f0 100644 --- a/src/vs/server/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/remoteExtensionHostAgentCli.ts @@ -38,6 +38,8 @@ import { DownloadService } from 'vs/platform/download/common/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { buildHelpMessage, buildVersionMessage, OptionDescriptions } from 'vs/platform/environment/node/argv'; +import { isWindows } from 'vs/base/common/platform'; class CliMain extends Disposable { @@ -135,7 +137,19 @@ function eventuallyExit(code: number): void { setTimeout(() => process.exit(code), 0); } -export async function run(args: ServerParsedArgs, REMOTE_DATA_FOLDER: string): Promise { +export async function run(args: ServerParsedArgs, REMOTE_DATA_FOLDER: string, optionDescriptions: OptionDescriptions): Promise { + if (args.help) { + const executable = `server${isWindows ? '.bat' : '.sh'}`; + console.log(buildHelpMessage(product.nameLong, executable, product.version, optionDescriptions, { noInputFiles: true, noPipe: true })); + return; + } + // Version Info + if (args.version) { + console.log(buildVersionMessage(product.version, product.commit)); + return; + } + + const cliMain = new CliMain(args, REMOTE_DATA_FOLDER); try { await cliMain.run(); diff --git a/src/vs/server/serverEnvironmentService.ts b/src/vs/server/serverEnvironmentService.ts index a014d82f701..e993dbdcde3 100644 --- a/src/vs/server/serverEnvironmentService.ts +++ b/src/vs/server/serverEnvironmentService.ts @@ -55,6 +55,9 @@ export const serverOptions: OptionDescriptions = { 'log': { type: 'string' }, 'logsPath': { type: 'string' }, + 'help': OPTIONS['help'], + 'version': OPTIONS['version'], + _: OPTIONS['_'] }; @@ -129,6 +132,10 @@ export interface ServerParsedArgs { 'log'?: string; 'logsPath'?: string; + // server cli + help: boolean; + version: boolean; + _: string[]; } From 2fc6cc9a64b5de6867bdb2c0f2494ba289ee9faa Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Wed, 17 Nov 2021 23:35:22 +0900 Subject: [PATCH 327/330] chore: fix perf hook integration with node environment --- src/vs/base/common/performance.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index d15367be8b2..15eab308bf2 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -40,7 +40,9 @@ */ function _define() { - if (typeof performance === 'object' && typeof performance.mark === 'function') { + // Identify browser environment when following property is not present + // https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#performancenodetiming + if (typeof performance === 'object' && typeof performance.mark === 'function' && !performance.nodeTiming) { // in a browser context, reuse performance-util if (typeof performance.timeOrigin !== 'number' && !performance.timing) { From c084b6ce2e3dc06c60bbbdd9cf35c7764c87d29f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Nov 2021 19:28:14 +0100 Subject: [PATCH 328/330] add contribViewsRemote-proposal to api-tests extension --- extensions/vscode-api-tests/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index a4b8c32909a..45de8bf3b0a 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -6,6 +6,7 @@ "license": "MIT", "enabledApiProposals": [ "authSession", + "contribViewsRemote", "customEditorMove", "diffCommand", "documentFiltersExclusive", From 00e466b4b93b9eb890c7ce1def7021e2ea89f38b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Nov 2021 19:31:17 +0100 Subject: [PATCH 329/330] fix https://github.com/microsoft/vscode/issues/137365 --- src/vs/workbench/services/extensions/common/extensions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 5eccd39dc01..66fc91a775c 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -143,7 +143,7 @@ export function isProposedApiEnabled(extension: IExtensionDescription, proposal: export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void { if (!isProposedApiEnabled(extension, proposal)) { - throw new Error(`Extension '${extension.identifier.value}' CANNOT use API proposal: ${proposal}.\nAccording to its package.json#enabledApiProposals-property it wants: ${extension.enabledApiProposals?.join(', ') ?? ''}.\n You MUST start in extension development mode or use the following command line switch: --enable-proposed-api ${extension.identifier.value}`); + throw new Error(`Extension '${extension.identifier.value}' CANNOT use API proposal: ${proposal}.\nIts package.json#enabledApiProposals-property declares: ${extension.enabledApiProposals?.join(', ') ?? '[]'} but NOT ${proposal}.\n The missing proposal MUST be added and you must start in extension development mode or use the following command line switch: --enable-proposed-api ${extension.identifier.value}`); } } From 5128d2c32c217dcc33da2527a6936acd083f0603 Mon Sep 17 00:00:00 2001 From: John Murray Date: Mon, 22 Nov 2021 18:38:39 +0000 Subject: [PATCH 330/330] Avoid conflicting scopes and commandIds in quiet logins from Accounts (fix #137601) (#137613) * Avoid conflicting scopes and commandIds in quiet logins from Accounts (fix #137601) * revert from scopesJSON to scopesList but use space as separator * define SCOPESLIST_SEPARATOR and use it consistently * simplify diff --- .../browser/authenticationService.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 69a722795bf..0888f24f9d3 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -163,13 +163,16 @@ export function readAllowedExtensions(storageService: IStorageService, providerI return trustedExtensions; } -export interface SessionRequest { +// OAuth2 spec prohibits space in a scope, so use that to join them. +const SCOPESLIST_SEPARATOR = ' '; + +interface SessionRequest { disposables: IDisposable[]; requestingExtensionIds: string[]; } -export interface SessionRequestInfo { - [scopes: string]: SessionRequest; +interface SessionRequestInfo { + [scopesList: string]: SessionRequest; } CommandsRegistry.registerCommand('workbench.getCodeExchangeProxyEndpoints', function (accessor, _) { @@ -347,7 +350,7 @@ export class AuthenticationService extends Disposable implements IAuthentication } Object.keys(existingRequestsForProvider).forEach(requestedScopes => { - if (addedSessions.some(session => session.scopes.slice().join('') === requestedScopes)) { + if (addedSessions.some(session => session.scopes.slice().join(SCOPESLIST_SEPARATOR) === requestedScopes)) { const sessionRequest = existingRequestsForProvider[requestedScopes]; sessionRequest?.disposables.forEach(item => item.dispose()); @@ -613,7 +616,7 @@ export class AuthenticationService extends Disposable implements IAuthentication if (provider) { const providerRequests = this._signInRequestItems.get(providerId); - const scopesList = scopes.join(''); + const scopesList = scopes.join(SCOPESLIST_SEPARATOR); const extensionHasExistingRequest = providerRequests && providerRequests[scopesList] && providerRequests[scopesList].requestingExtensionIds.includes(extensionId); @@ -622,10 +625,12 @@ export class AuthenticationService extends Disposable implements IAuthentication return; } + // Construct a commandId that won't clash with others generated here, nor likely with an extension's command + const commandId = `${providerId}:${extensionId}:signIn${Object.keys(providerRequests || []).length}`; const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, { group: '2_signInRequests', command: { - id: `${extensionId}signIn`, + id: commandId, title: nls.localize({ key: 'signInRequest', comment: [`The placeholder {0} will be replaced with an authentication provider's label. {1} will be replaced with an extension name. (1) is to indicate that this menu item contributes to a badge count.`] @@ -637,7 +642,7 @@ export class AuthenticationService extends Disposable implements IAuthentication }); const signInCommand = CommandsRegistry.registerCommand({ - id: `${extensionId}signIn`, + id: commandId, handler: async (accessor) => { const authenticationService = accessor.get(IAuthenticationService); const storageService = accessor.get(IStorageService);