From 3563e2b7403537f104daaa08f57909442ff4c255 Mon Sep 17 00:00:00 2001 From: Nick Rayburn Date: Fri, 1 Jan 2021 21:20:12 -0600 Subject: [PATCH 01/34] rename run to exec for consistency --- extensions/git/src/git.ts | 145 +++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index aec131a03db..bbe10a1a363 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -824,8 +824,7 @@ export class Repository { return this.repositoryRoot; } - // TODO@Joao: rename to exec - async run(args: string[], options: SpawnOptions = {}): Promise> { + async exec(args: string[], options: SpawnOptions = {}): Promise> { return await this.git.exec(this.repositoryRoot, args, options); } @@ -850,7 +849,7 @@ export class Repository { args.push(value); } - const result = await this.run(args, options); + const result = await this.exec(args, options); return result.stdout.trim(); } @@ -863,7 +862,7 @@ export class Repository { args.push('-l'); - const result = await this.run(args); + const result = await this.exec(args); const lines = result.stdout.trim().split(/\r|\r\n|\n/); return lines.map(entry => { @@ -879,7 +878,7 @@ export class Repository { args.push(options.path); } - const result = await this.run(args); + const result = await this.exec(args); if (result.exitCode) { // An empty repo return []; @@ -910,7 +909,7 @@ export class Repository { args.push('--', uri.fsPath); - const result = await this.run(args); + const result = await this.exec(args); if (result.exitCode) { // No file history, e.g. a new file or untracked return []; @@ -965,7 +964,7 @@ export class Repository { } const { mode, object } = elements[0]; - const catFile = await this.run(['cat-file', '-s', object]); + const catFile = await this.exec(['cat-file', '-s', object]); const size = parseInt(catFile.stdout); return { mode, object, size }; @@ -982,12 +981,12 @@ export class Repository { } async lstree(treeish: string, path: string): Promise { - const { stdout } = await this.run(['ls-tree', '-l', treeish, '--', sanitizePath(path)]); + const { stdout } = await this.exec(['ls-tree', '-l', treeish, '--', sanitizePath(path)]); return parseLsTree(stdout); } async lsfiles(path: string): Promise { - const { stdout } = await this.run(['ls-files', '--stage', '--', sanitizePath(path)]); + const { stdout } = await this.exec(['ls-files', '--stage', '--', sanitizePath(path)]); return parseLsFiles(stdout); } @@ -1052,7 +1051,7 @@ export class Repository { } try { - await this.run(args); + await this.exec(args); } catch (err) { if (/patch does not apply/.test(err.stderr)) { err.gitErrorCode = GitErrorCodes.PatchDoesNotApply; @@ -1069,7 +1068,7 @@ export class Repository { args.push('--cached'); } - const result = await this.run(args); + const result = await this.exec(args); return result.stdout; } @@ -1082,7 +1081,7 @@ export class Repository { } const args = ['diff', '--', sanitizePath(path)]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout; } @@ -1095,7 +1094,7 @@ export class Repository { } const args = ['diff', ref, '--', sanitizePath(path)]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout; } @@ -1108,7 +1107,7 @@ export class Repository { } const args = ['diff', '--cached', '--', sanitizePath(path)]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout; } @@ -1121,13 +1120,13 @@ export class Repository { } const args = ['diff', '--cached', ref, '--', sanitizePath(path)]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout; } async diffBlobs(object1: string, object2: string): Promise { const args = ['diff', object1, object2]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout; } @@ -1141,7 +1140,7 @@ export class Repository { } const args = ['diff', range, '--', sanitizePath(path)]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout.trim(); } @@ -1156,7 +1155,7 @@ export class Repository { args.push(ref); } - const gitResult = await this.run(args); + const gitResult = await this.exec(args); if (gitResult.exitCode) { return []; } @@ -1229,14 +1228,14 @@ export class Repository { async getMergeBase(ref1: string, ref2: string): Promise { const args = ['merge-base', ref1, ref2]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout.trim(); } async hashObject(data: string): Promise { const args = ['hash-object', '-w', '--stdin']; - const result = await this.run(args, { input: data }); + const result = await this.exec(args, { input: data }); return result.stdout.trim(); } @@ -1252,10 +1251,10 @@ export class Repository { if (paths && paths.length) { for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { - await this.run([...args, '--', ...chunk]); + await this.exec([...args, '--', ...chunk]); } } else { - await this.run([...args, '--', '.']); + await this.exec([...args, '--', '.']); } } @@ -1268,7 +1267,7 @@ export class Repository { args.push(...paths.map(sanitizePath)); - await this.run(args); + await this.exec(args); } async stage(path: string, data: string): Promise { @@ -1301,7 +1300,7 @@ export class Repository { add = '--add'; } - await this.run(['update-index', add, '--cacheinfo', mode, hash, path]); + await this.exec(['update-index', add, '--cacheinfo', mode, hash, path]); } async checkout(treeish: string, paths: string[], opts: { track?: boolean, detached?: boolean } = Object.create(null)): Promise { @@ -1322,10 +1321,10 @@ export class Repository { try { if (paths && paths.length > 0) { for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { - await this.run([...args, '--', ...chunk]); + await this.exec([...args, '--', ...chunk]); } } else { - await this.run(args); + await this.exec(args); } } catch (err) { if (/Please,? commit your changes or stash them/.test(err.stderr || '')) { @@ -1374,21 +1373,21 @@ export class Repository { args.splice(0, 0, '-c', 'user.useConfigOnly=true'); try { - await this.run(args, !opts.amend || message ? { input: message || '' } : {}); + await this.exec(args, !opts.amend || message ? { input: message || '' } : {}); } catch (commitErr) { await this.handleCommitError(commitErr); } } async rebaseAbort(): Promise { - await this.run(['rebase', '--abort']); + await this.exec(['rebase', '--abort']); } async rebaseContinue(): Promise { const args = ['rebase', '--continue']; try { - await this.run(args); + await this.exec(args); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1401,14 +1400,14 @@ export class Repository { } try { - await this.run(['config', '--get-all', 'user.name']); + await this.exec(['config', '--get-all', 'user.name']); } catch (err) { err.gitErrorCode = GitErrorCodes.NoUserNameConfigured; throw err; } try { - await this.run(['config', '--get-all', 'user.email']); + await this.exec(['config', '--get-all', 'user.email']); } catch (err) { err.gitErrorCode = GitErrorCodes.NoUserEmailConfigured; throw err; @@ -1424,39 +1423,39 @@ export class Repository { args.push(ref); } - await this.run(args); + await this.exec(args); } async deleteBranch(name: string, force?: boolean): Promise { const args = ['branch', force ? '-D' : '-d', name]; - await this.run(args); + await this.exec(args); } async renameBranch(name: string): Promise { const args = ['branch', '-m', name]; - await this.run(args); + await this.exec(args); } async move(from: string, to: string): Promise { const args = ['mv', from, to]; - await this.run(args); + await this.exec(args); } async setBranchUpstream(name: string, upstream: string): Promise { const args = ['branch', '--set-upstream-to', upstream, name]; - await this.run(args); + await this.exec(args); } async deleteRef(ref: string): Promise { const args = ['update-ref', '-d', ref]; - await this.run(args); + await this.exec(args); } async merge(ref: string): Promise { const args = ['merge', ref]; try { - await this.run(args); + await this.exec(args); } catch (err) { if (/^CONFLICT /m.test(err.stdout || '')) { err.gitErrorCode = GitErrorCodes.Conflict; @@ -1475,12 +1474,12 @@ export class Repository { args = [...args, name]; } - await this.run(args); + await this.exec(args); } async deleteTag(name: string): Promise { let args = ['tag', '-d', name]; - await this.run(args); + await this.exec(args); } async clean(paths: string[]): Promise { @@ -1493,7 +1492,7 @@ export class Repository { for (const paths of groups) { for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { - promises.push(limiter.queue(() => this.run([...args, '--', ...chunk]))); + promises.push(limiter.queue(() => this.exec([...args, '--', ...chunk]))); } } @@ -1501,10 +1500,10 @@ export class Repository { } async undo(): Promise { - await this.run(['clean', '-fd']); + await this.exec(['clean', '-fd']); try { - await this.run(['checkout', '--', '.']); + await this.exec(['checkout', '--', '.']); } catch (err) { if (/did not match any file\(s\) known to git\./.test(err.stderr || '')) { return; @@ -1516,11 +1515,11 @@ export class Repository { async reset(treeish: string, hard: boolean = false): Promise { const args = ['reset', hard ? '--hard' : '--soft', treeish]; - await this.run(args); + await this.exec(args); } async revert(treeish: string, paths: string[]): Promise { - const result = await this.run(['branch']); + const result = await this.exec(['branch']); let args: string[]; // In case there are no branches, we must use rm --cached @@ -1533,10 +1532,10 @@ export class Repository { try { if (paths && paths.length > 0) { for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { - await this.run([...args, '--', ...chunk]); + await this.exec([...args, '--', ...chunk]); } } else { - await this.run([...args, '--', '.']); + await this.exec([...args, '--', '.']); } } catch (err) { // In case there are merge conflicts to be resolved, git reset will output @@ -1551,17 +1550,17 @@ export class Repository { async addRemote(name: string, url: string): Promise { const args = ['remote', 'add', name, url]; - await this.run(args); + await this.exec(args); } async removeRemote(name: string): Promise { const args = ['remote', 'remove', name]; - await this.run(args); + await this.exec(args); } async renameRemote(name: string, newName: string): Promise { const args = ['remote', 'rename', name, newName]; - await this.run(args); + await this.exec(args); } async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean, readonly cancellationToken?: CancellationToken } = {}): Promise { @@ -1593,7 +1592,7 @@ export class Repository { } try { - await this.run(args, spawnOptions); + await this.exec(args, spawnOptions); } catch (err) { if (/No remote repository specified\./.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.NoRemoteRepositorySpecified; @@ -1626,7 +1625,7 @@ export class Repository { } try { - await this.run(args, options); + await this.exec(args, options); } catch (err) { if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) { err.gitErrorCode = GitErrorCodes.Conflict; @@ -1653,7 +1652,7 @@ export class Repository { args.push(branch); try { - await this.run(args, options); + await this.exec(args, options); } catch (err) { if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) { err.gitErrorCode = GitErrorCodes.Conflict; @@ -1697,7 +1696,7 @@ export class Repository { args.splice(0, 0, '-c', `http.userAgent=${this.git.userAgent}`); try { - await this.run(args); + await this.exec(args); } catch (err) { if (/^error: failed to push some refs to\b/m.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.PushRejected; @@ -1715,13 +1714,13 @@ export class Repository { async cherryPick(commitHash: string): Promise { const args = ['cherry-pick', commitHash]; - await this.run(args); + await this.exec(args); } async blame(path: string): Promise { try { const args = ['blame', sanitizePath(path)]; - const result = await this.run(args); + const result = await this.exec(args); return result.stdout.trim(); } catch (err) { if (/^fatal: no such path/.test(err.stderr || '')) { @@ -1744,7 +1743,7 @@ export class Repository { args.push('-m', message); } - await this.run(args); + await this.exec(args); } catch (err) { if (/No local changes to save/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.NoLocalChanges; @@ -1770,7 +1769,7 @@ export class Repository { args.push(`stash@{${index}}`); } - await this.run(args); + await this.exec(args); } catch (err) { if (/No stash found/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.NoStashFound; @@ -1792,7 +1791,7 @@ export class Repository { } try { - await this.run(args); + await this.exec(args); } catch (err) { if (/No stash found/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.NoStashFound; @@ -1857,7 +1856,7 @@ export class Repository { async getHEAD(): Promise { try { - const result = await this.run(['symbolic-ref', '--short', 'HEAD']); + const result = await this.exec(['symbolic-ref', '--short', 'HEAD']); if (!result.stdout) { throw new Error('Not in a branch'); @@ -1865,7 +1864,7 @@ export class Repository { return { name: result.stdout.trim(), commit: undefined, type: RefType.Head }; } catch (err) { - const result = await this.run(['rev-parse', 'HEAD']); + const result = await this.exec(['rev-parse', 'HEAD']); if (!result.stdout) { throw new Error('Error parsing HEAD'); @@ -1876,7 +1875,7 @@ export class Repository { } async findTrackingBranches(upstreamBranch: string): Promise { - const result = await this.run(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']); + const result = await this.exec(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']); return result.stdout.trim().split('\n') .map(line => line.trim().split('\0')) .filter(([_, upstream]) => upstream === upstreamBranch) @@ -1904,7 +1903,7 @@ export class Repository { args.push('--contains', opts.contains); } - const result = await this.run(args); + const result = await this.exec(args); const fn = (line: string): Ref | null => { let match: RegExpExecArray | null; @@ -1927,7 +1926,7 @@ export class Repository { } async getStashes(): Promise { - const result = await this.run(['stash', 'list']); + const result = await this.exec(['stash', 'list']); const regex = /^stash@{(\d+)}:(.+)$/; const rawStashes = result.stdout.trim().split('\n') .filter(b => !!b) @@ -1939,7 +1938,7 @@ export class Repository { } async getRemotes(): Promise { - const result = await this.run(['remote', '--verbose']); + const result = await this.exec(['remote', '--verbose']); const lines = result.stdout.trim().split('\n').filter(l => !!l); const remotes: MutableRemote[] = []; @@ -1975,13 +1974,13 @@ export class Repository { return this.getHEAD(); } - let result = await this.run(['rev-parse', name]); + let result = await this.exec(['rev-parse', name]); if (!result.stdout && /^@/.test(name)) { - const symbolicFullNameResult = await this.run(['rev-parse', '--symbolic-full-name', name]); + const symbolicFullNameResult = await this.exec(['rev-parse', '--symbolic-full-name', name]); name = symbolicFullNameResult.stdout.trim(); - result = await this.run(['rev-parse', name]); + result = await this.exec(['rev-parse', name]); } if (!result.stdout) { @@ -1991,7 +1990,7 @@ export class Repository { const commit = result.stdout.trim(); try { - const res2 = await this.run(['rev-parse', '--symbolic-full-name', name + '@{u}']); + const res2 = await this.exec(['rev-parse', '--symbolic-full-name', name + '@{u}']); const fullUpstream = res2.stdout.trim(); const match = /^refs\/remotes\/([^/]+)\/(.+)$/.exec(fullUpstream); @@ -2000,7 +1999,7 @@ export class Repository { } const upstream = { remote: match[1], name: match[2] }; - const res3 = await this.run(['rev-list', '--left-right', name + '...' + fullUpstream]); + const res3 = await this.exec(['rev-list', '--left-right', name + '...' + fullUpstream]); let ahead = 0, behind = 0; let i = 0; @@ -2055,7 +2054,7 @@ export class Repository { async getCommitTemplate(): Promise { try { - const result = await this.run(['config', '--get', 'commit.template']); + const result = await this.exec(['config', '--get', 'commit.template']); if (!result.stdout) { return ''; @@ -2078,7 +2077,7 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.run(['show', '-s', `--format=${COMMIT_FORMAT}`, '-z', ref]); + const result = await this.exec(['show', '-s', `--format=${COMMIT_FORMAT}`, '-z', ref]); const commits = parseGitCommits(result.stdout); if (commits.length === 0) { return Promise.reject('bad commit format'); @@ -2090,7 +2089,7 @@ export class Repository { const args = ['submodule', 'update']; for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { - await this.run([...args, '--', ...chunk]); + await this.exec([...args, '--', ...chunk]); } } From 9be03fe29b299e15ca04473b83682c6afefd728b Mon Sep 17 00:00:00 2001 From: susiwen8 Date: Tue, 12 Jan 2021 13:44:52 +0800 Subject: [PATCH 02/34] Fix: add alwaysConsumeMouseWheel for list --- src/vs/base/browser/ui/list/listView.ts | 5 ++++- src/vs/base/browser/ui/list/listWidget.ts | 1 + src/vs/editor/contrib/suggest/suggestWidget.ts | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index b26c34c9517..13a7a6d028f 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -64,6 +64,7 @@ export interface IListViewOptions extends IListViewOptionsUpdate { readonly mouseSupport?: boolean; readonly accessibilityProvider?: IListViewAccessibilityProvider; readonly transformOptimization?: boolean; + readonly alwaysConsumeMouseWheel?: boolean; } const DefaultOptions = { @@ -80,7 +81,8 @@ const DefaultOptions = { drop() { } }, horizontalScrolling: false, - transformOptimization: true + transformOptimization: true, + alwaysConsumeMouseWheel: true, }; export class ElementsDragAndDropData implements IDragAndDropData { @@ -327,6 +329,7 @@ export class ListView implements ISpliceable, IDisposable { this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => scheduleAtNextAnimationFrame(cb)); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { + alwaysConsumeMouseWheel: getOrDefault(options, o => o.alwaysConsumeMouseWheel, DefaultOptions.alwaysConsumeMouseWheel), horizontal: ScrollbarVisibility.Auto, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e530b43d36f..714f46ec89b 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -854,6 +854,7 @@ export interface IListOptions { readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; readonly smoothScrolling?: boolean; + readonly alwaysConsumeMouseWheel?: boolean; } export interface IListStyles { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index af0c4f6e415..e5c45498e9a 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -102,7 +102,7 @@ export class SuggestWidget implements IDisposable { private _focusedItem?: CompletionItem; private _ignoreFocusEvents: boolean = false; private _completionModel?: CompletionModel; - private _cappedHeight?: { wanted: number, capped: number }; + private _cappedHeight?: { wanted: number; capped: number; }; private _explainMode: boolean = false; readonly element: ResizableHTMLElement; @@ -216,6 +216,7 @@ export class SuggestWidget implements IDisposable { getHeight: (_element: CompletionItem): number => this.getLayoutInfo().itemHeight, getTemplateId: (_element: CompletionItem): string => 'suggestion' }, [renderer], { + alwaysConsumeMouseWheel: true, useShadows: false, mouseSupport: false, accessibilityProvider: { From 1ecf01b9c71e76160c96f8b2dd89752b11f785ca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 29 Jan 2021 12:12:06 +0100 Subject: [PATCH 03/34] mark rpc protocol and proxies with symbols and try to find them from an API test (with success...) --- .../src/singlefolder-tests/rpc.test.ts | 53 +++++++++++++++++++ .../services/extensions/common/rpcProtocol.ts | 14 +++-- 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts new file mode 100644 index 00000000000..f75da5a79a9 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.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. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('vscode', function () { + + test('rpc protocol, proxies not reachable', function () { + + const symProxy = Symbol.for('rpcProxy'); + const symProtocol = Symbol.for('rpcProtocol'); + + const proxyPaths: string[] = []; + const rpcPaths: string[] = []; + + function walk(obj: any, path: string, seen: Set) { + if (!obj) { + return; + } + if (typeof obj !== 'object' && typeof obj !== 'function') { + return; + } + if (seen.has(obj)) { + return; + } + seen.add(obj); + + if (obj[symProtocol]) { + rpcPaths.push(`PROTOCOL via ${path}`); + } + if (obj[symProxy]) { + proxyPaths.push(`PROXY '${obj[symProxy]}' via ${path}`); + } + + for (const key in obj) { + walk(obj[key], `${path}.${String(key)}`, seen); + } + } + + try { + walk(vscode, 'vscode', new Set()); + } catch (err) { + assert.fail(err); + } + assert.strictEqual(rpcPaths.length, 0); + + // assert.strictEqual(proxyPaths.length, 0); // proxies are accessible... + }); + +}); diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index 199ea6e15ef..a9fef74ecec 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -60,8 +60,13 @@ export interface IRPCProtocolLogger { const noop = () => { }; +const _RPCProtocolSymbol = Symbol.for('rpcProtocol'); +const _RPCProxySymbol = Symbol.for('rpcProxy'); + export class RPCProtocol extends Disposable implements IRPCProtocol { + [_RPCProtocolSymbol] = true; + private static readonly UNRESPONSIVE_TIME = 3 * 1000; // 3s private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); @@ -182,14 +187,14 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { } public getProxy(identifier: ProxyIdentifier): T { - const rpcId = identifier.nid; + const { nid: rpcId, sid } = identifier; if (!this._proxies[rpcId]) { - this._proxies[rpcId] = this._createProxy(rpcId); + this._proxies[rpcId] = this._createProxy(rpcId, sid); } return this._proxies[rpcId]; } - private _createProxy(rpcId: number): T { + private _createProxy(rpcId: number, debugName: string): T { let handler = { get: (target: any, name: PropertyKey) => { if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) { @@ -197,6 +202,9 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { return this._remoteCall(rpcId, name, myArgs); }; } + if (name === _RPCProxySymbol) { + return debugName; + } return target[name]; } }; From 4cfd5f850806fd293d5ca6e988c612a5514f93b3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 29 Jan 2021 12:30:02 +0100 Subject: [PATCH 04/34] clipboard changes --- .../src/singlefolder-tests/rpc.test.ts | 4 ++-- .../workbench/api/common/extHost.api.impl.ts | 4 +--- .../workbench/api/common/extHostClipboard.ts | 24 +++++++++---------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index f75da5a79a9..7e0d622120f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -45,9 +45,9 @@ suite('vscode', function () { } catch (err) { assert.fail(err); } - assert.strictEqual(rpcPaths.length, 0); + assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); - // assert.strictEqual(proxyPaths.length, 0); // proxies are accessible... + // assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // proxies are accessible... }); }); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 2da02381a46..a575b13d7c7 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -288,9 +288,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; }, get uriScheme() { return initData.environment.appUriScheme; }, - get clipboard(): vscode.Clipboard { - return extHostClipboard; - }, + get clipboard(): vscode.Clipboard { return extHostClipboard.value; }, get shell() { return extHostTerminalService.getDefaultShell(false, configProvider); }, diff --git a/src/vs/workbench/api/common/extHostClipboard.ts b/src/vs/workbench/api/common/extHostClipboard.ts index 2983f22bd5b..f8665dacbed 100644 --- a/src/vs/workbench/api/common/extHostClipboard.ts +++ b/src/vs/workbench/api/common/extHostClipboard.ts @@ -3,22 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import type * as vscode from 'vscode'; -export class ExtHostClipboard implements vscode.Clipboard { +export class ExtHostClipboard { - private readonly _proxy: MainThreadClipboardShape; + readonly value: vscode.Clipboard; constructor(mainContext: IMainContext) { - this._proxy = mainContext.getProxy(MainContext.MainThreadClipboard); - } - - readText(): Promise { - return this._proxy.$readText(); - } - - writeText(value: string): Promise { - return this._proxy.$writeText(value); + const proxy = mainContext.getProxy(MainContext.MainThreadClipboard); + this.value = Object.freeze({ + readText() { + return proxy.$readText(); + }, + writeText(value: string) { + return proxy.$writeText(value); + } + }); } } From 3114b1c4c52c9efb624c2ba2c9cb1a0367650860 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 29 Jan 2021 13:03:42 +0100 Subject: [PATCH 05/34] hide rpc proxies --- .../src/singlefolder-tests/rpc.test.ts | 3 +- .../workbench/api/common/extHost.api.impl.ts | 20 ++--- .../api/common/extHostDebugService.ts | 22 +++--- .../api/common/extHostFileSystemConsumer.ts | 74 ++++++++++--------- .../api/common/extHostStoragePaths.ts | 6 +- 5 files changed, 63 insertions(+), 62 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 7e0d622120f..b7c4c7c2abe 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -46,8 +46,7 @@ suite('vscode', function () { assert.fail(err); } assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); - - // assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // proxies are accessible... + assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); }); }); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index a575b13d7c7..a32d5b80013 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -829,7 +829,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostFileSystem.registerFileSystemProvider(extension.identifier, scheme, provider, options); }, get fs() { - return extHostConsumerFileSystem; + return extHostConsumerFileSystem.value; }, registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => { checkProposedApiEnabled(extension); @@ -1290,9 +1290,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I class Extension implements vscode.Extension { - private _extensionService: IExtHostExtensionService; - private _originExtensionId: ExtensionIdentifier; - private _identifier: ExtensionIdentifier; + #extensionService: IExtHostExtensionService; + #originExtensionId: ExtensionIdentifier; + #identifier: ExtensionIdentifier; readonly id: string; readonly extensionUri: URI; @@ -1301,9 +1301,9 @@ class Extension implements vscode.Extension { readonly extensionKind: vscode.ExtensionKind; constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) { - this._extensionService = extensionService; - this._originExtensionId = originExtensionId; - this._identifier = description.identifier; + this.#extensionService = extensionService; + this.#originExtensionId = originExtensionId; + this.#identifier = description.identifier; this.id = description.identifier.value; this.extensionUri = description.extensionLocation; this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); @@ -1312,17 +1312,17 @@ class Extension implements vscode.Extension { } get isActive(): boolean { - return this._extensionService.isActivated(this._identifier); + return this.#extensionService.isActivated(this.#identifier); } get exports(): T { if (this.packageJSON.api === 'none') { return undefined!; // Strict nulloverride - Public api } - return this._extensionService.getExtensionExports(this._identifier); + return this.#extensionService.getExtensionExports(this.#identifier); } activate(): Thenable { - return this._extensionService.activateByIdWithErrors(this._identifier, { startup: false, extensionId: this._originExtensionId, activationEvent: 'api' }).then(() => this.exports); + return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports); } } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index d639431a13e..c1bbd4806c8 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -89,7 +89,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E get onDidReceiveDebugSessionCustomEvent(): Event { return this._onDidReceiveDebugSessionCustomEvent.event; } private _activeDebugConsole: ExtHostDebugConsole; - get activeDebugConsole(): ExtHostDebugConsole { return this._activeDebugConsole; } + get activeDebugConsole(): vscode.DebugConsole { return this._activeDebugConsole.value; } private _breakpoints: Map; private _breakpointEventsActive: boolean; @@ -911,20 +911,20 @@ export class ExtHostDebugSession implements vscode.DebugSession { } } -export class ExtHostDebugConsole implements vscode.DebugConsole { +export class ExtHostDebugConsole { - private _debugServiceProxy: MainThreadDebugServiceShape; + readonly value: vscode.DebugConsole; constructor(proxy: MainThreadDebugServiceShape) { - this._debugServiceProxy = proxy; - } - append(value: string): void { - this._debugServiceProxy.$appendDebugConsole(value); - } - - appendLine(value: string): void { - this.append(value + '\n'); + this.value = Object.freeze({ + append(value: string): void { + proxy.$appendDebugConsole(value); + }, + appendLine(value: string): void { + this.append(value + '\n'); + } + }); } } diff --git a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts index 5bbd185c775..46e331f4c83 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 { MainThreadFileSystemShape, MainContext } from './extHost.protocol'; +import { MainContext } 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'; @@ -12,49 +12,51 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; -export class ExtHostConsumerFileSystem implements vscode.FileSystem { +export class ExtHostConsumerFileSystem { readonly _serviceBrand: undefined; - private readonly _proxy: MainThreadFileSystemShape; + readonly value: vscode.FileSystem; constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, - @IExtHostFileSystemInfo private readonly _fileSystemInfo: IExtHostFileSystemInfo, + @IExtHostFileSystemInfo fileSystemInfo: IExtHostFileSystemInfo, ) { - this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem); - } + const proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem); - stat(uri: vscode.Uri): Promise { - return this._proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError); - } - readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { - return this._proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError); - } - createDirectory(uri: vscode.Uri): Promise { - return this._proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError); - } - async readFile(uri: vscode.Uri): Promise { - return this._proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError); - } - writeFile(uri: vscode.Uri, content: Uint8Array): Promise { - return this._proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError); - } - delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise { - return this._proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); - } - rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise { - return this._proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); - } - copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise { - return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); - } - isWritableFileSystem(scheme: string): boolean | undefined { - const capabilities = this._fileSystemInfo.getCapabilities(scheme); - if (typeof capabilities === 'number') { - return !(capabilities & files.FileSystemProviderCapabilities.Readonly); - } - return undefined; + this.value = Object.freeze({ + stat(uri: vscode.Uri): Promise { + return proxy.$stat(uri).catch(ExtHostConsumerFileSystem._handleError); + }, + readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { + return proxy.$readdir(uri).catch(ExtHostConsumerFileSystem._handleError); + }, + createDirectory(uri: vscode.Uri): Promise { + return proxy.$mkdir(uri).catch(ExtHostConsumerFileSystem._handleError); + }, + async readFile(uri: vscode.Uri): Promise { + return proxy.$readFile(uri).then(buff => buff.buffer).catch(ExtHostConsumerFileSystem._handleError); + }, + writeFile(uri: vscode.Uri, content: Uint8Array): Promise { + return proxy.$writeFile(uri, VSBuffer.wrap(content)).catch(ExtHostConsumerFileSystem._handleError); + }, + delete(uri: vscode.Uri, options?: { recursive?: boolean; useTrash?: boolean; }): Promise { + return proxy.$delete(uri, { ...{ recursive: false, useTrash: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + }, + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options?: { overwrite?: boolean; }): Promise { + return proxy.$rename(oldUri, newUri, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + }, + copy(source: vscode.Uri, destination: vscode.Uri, options?: { overwrite?: boolean; }): Promise { + return proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError); + }, + isWritableFileSystem(scheme: string): boolean | undefined { + const capabilities = fileSystemInfo.getCapabilities(scheme); + if (typeof capabilities === 'number') { + return !(capabilities & files.FileSystemProviderCapabilities.Readonly); + } + return undefined; + } + }); } private static _handleError(err: any): never { diff --git a/src/vs/workbench/api/common/extHostStoragePaths.ts b/src/vs/workbench/api/common/extHostStoragePaths.ts index 34dc47ea635..45dc6abf5ed 100644 --- a/src/vs/workbench/api/common/extHostStoragePaths.ts +++ b/src/vs/workbench/api/common/extHostStoragePaths.ts @@ -48,7 +48,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths { const storageUri = URI.joinPath(this._environment.workspaceStorageHome, storageName); try { - await this._extHostFileSystem.stat(storageUri); + await this._extHostFileSystem.value.stat(storageUri); this._logService.trace('[ExtHostStorage] storage dir already exists', storageUri); return storageUri; } catch { @@ -57,8 +57,8 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths { try { this._logService.trace('[ExtHostStorage] creating dir and metadata-file', storageUri); - await this._extHostFileSystem.createDirectory(storageUri); - await this._extHostFileSystem.writeFile( + await this._extHostFileSystem.value.createDirectory(storageUri); + await this._extHostFileSystem.value.writeFile( URI.joinPath(storageUri, 'meta.json'), new TextEncoder().encode(JSON.stringify({ id: this._workspace.id, From 930b5686ffa042fdbc37fb9a7c3e800d786bca99 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 29 Jan 2021 13:08:41 +0100 Subject: [PATCH 06/34] add todo --- .../vscode-api-tests/src/singlefolder-tests/rpc.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index b7c4c7c2abe..9a688165d86 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -47,6 +47,10 @@ suite('vscode', function () { } assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); + + // todo@jrieken + // this should be run after/before each test because some objects are short lived, + // like notebook-editor, documents etc. }); }); From 46cf57b2646c33fee17806fb92a8438980c07b4c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 29 Jan 2021 16:33:25 +0100 Subject: [PATCH 07/34] a bunch of failing tests --- .../src/singlefolder-tests/commands.test.ts | 3 ++ .../singlefolder-tests/configuration.test.ts | 3 ++ .../src/singlefolder-tests/debug.test.ts | 4 +- .../src/singlefolder-tests/editor.test.ts | 7 ++- .../src/singlefolder-tests/env.test.ts | 3 ++ .../src/singlefolder-tests/languages.test.ts | 4 +- .../src/singlefolder-tests/quickInput.test.ts | 7 ++- .../src/singlefolder-tests/rpc.test.ts | 49 ++----------------- .../src/singlefolder-tests/terminal.test.ts | 2 + .../src/singlefolder-tests/types.test.ts | 3 ++ .../src/singlefolder-tests/webview.test.ts | 4 +- .../src/singlefolder-tests/window.test.ts | 31 ++++++------ .../workspace.event.test.ts | 7 ++- .../singlefolder-tests/workspace.fs.test.ts | 3 ++ .../workspace.tasks.test.ts | 2 + .../src/singlefolder-tests/workspace.test.ts | 7 ++- extensions/vscode-api-tests/src/utils.ts | 41 ++++++++++++++++ 17 files changed, 106 insertions(+), 74 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts index 1d62a12c170..f9f971bdbda 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts @@ -7,9 +7,12 @@ import 'mocha'; import * as assert from 'assert'; import { join } from 'path'; import { commands, workspace, window, Uri, Range, Position, ViewColumn } from 'vscode'; +import { assertNoRpc } from '../utils'; suite('vscode API - commands', () => { + teardown(assertNoRpc); + test('getCommands', function (done) { let p1 = commands.getCommands().then(commands => { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts index 0b6f13fb01f..0a9ddf7aa2d 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/configuration.test.ts @@ -6,9 +6,12 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; +import { assertNoRpc } from '../utils'; suite('vscode API - configuration', () => { + teardown(assertNoRpc); + test('configurations, language defaults', function () { const defaultLanguageSettings = vscode.workspace.getConfiguration().get('[abcLang]'); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts index ec976ff5334..145c2ee3bce 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts @@ -5,11 +5,13 @@ import * as assert from 'assert'; import { debug, workspace, Disposable, commands, window } from 'vscode'; -import { disposeAll } from '../utils'; +import { assertNoRpc, disposeAll } from '../utils'; import { basename } from 'path'; suite('vscode API - debug', function () { + teardown(assertNoRpc); + test('breakpoints', async function () { assert.equal(debug.breakpoints.length, 0); let onDidChangeBreakpointsCounter = 0; diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts index b5a0a79166b..ef16bbe1c61 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts @@ -5,11 +5,14 @@ import * as assert from 'assert'; import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle, SnippetString, Selection, Uri, env } from 'vscode'; -import { createRandomFile, deleteFile, closeAllEditors } from '../utils'; +import { createRandomFile, deleteFile, closeAllEditors, assertNoRpc } from '../utils'; suite('vscode API - editors', () => { - teardown(closeAllEditors); + teardown(async function () { + assertNoRpc(); + await closeAllEditors(); + }); function withRandomFileEditor(initialContents: string, run: (editor: TextEditor, doc: TextDocument) => Thenable): Thenable { return createRandomFile(initialContents).then(file => { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts index 7bc2f75973f..e3f0106fe10 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts @@ -5,9 +5,12 @@ import * as assert from 'assert'; import { env, extensions, ExtensionKind, UIKind, Uri } from 'vscode'; +import { assertNoRpc } from '../utils'; suite('vscode API - env', () => { + teardown(assertNoRpc); + test('env is set', function () { assert.equal(typeof env.language, 'string'); assert.equal(typeof env.appRoot, 'string'); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts index a59841f6689..0553a4444db 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts @@ -6,10 +6,12 @@ import * as assert from 'assert'; import { join } from 'path'; import * as vscode from 'vscode'; -import { createRandomFile, testFs } from '../utils'; +import { assertNoRpc, createRandomFile, testFs } from '../utils'; suite('vscode API - languages', () => { + teardown(assertNoRpc); + const isWindows = process.platform === 'win32'; function positionToString(p: vscode.Position) { 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 55fbe0655a7..701c947426b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { window, commands } from 'vscode'; -import { closeAllEditors } from '../utils'; +import { assertNoRpc, closeAllEditors } from '../utils'; interface QuickPickExpected { events: string[]; @@ -20,7 +20,10 @@ interface QuickPickExpected { suite('vscode API - quick input', function () { - teardown(closeAllEditors); + teardown(async function () { + assertNoRpc(); + await closeAllEditors(); + }); test('createQuickPick, select second', function (_done) { let done = (err?: any) => { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 9a688165d86..596a7f9746f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -3,54 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import * as vscode from 'vscode'; +import { assertNoRpc } from '../utils'; suite('vscode', function () { - test('rpc protocol, proxies not reachable', function () { - - const symProxy = Symbol.for('rpcProxy'); - const symProtocol = Symbol.for('rpcProtocol'); - - const proxyPaths: string[] = []; - const rpcPaths: string[] = []; - - function walk(obj: any, path: string, seen: Set) { - if (!obj) { - return; - } - if (typeof obj !== 'object' && typeof obj !== 'function') { - return; - } - if (seen.has(obj)) { - return; - } - seen.add(obj); - - if (obj[symProtocol]) { - rpcPaths.push(`PROTOCOL via ${path}`); - } - if (obj[symProxy]) { - proxyPaths.push(`PROXY '${obj[symProxy]}' via ${path}`); - } - - for (const key in obj) { - walk(obj[key], `${path}.${String(key)}`, seen); - } - } - - try { - walk(vscode, 'vscode', new Set()); - } catch (err) { - assert.fail(err); - } - assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); - assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); - - // todo@jrieken - // this should be run after/before each test because some objects are short lived, - // like notebook-editor, documents etc. + test('no rpc', function () { + assertNoRpc(); }); - }); 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 01232a99c8c..9cede7605b4 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -5,6 +5,7 @@ import { window, Pseudoterminal, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget, Disposable, UIKind, env, EnvironmentVariableMutatorType, EnvironmentVariableMutator, extensions, ExtensionContext, TerminalOptions, ExtensionTerminalOptions } from 'vscode'; import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; +import { assertNoRpc } from '../utils'; // Disable terminal tests: // - Web https://github.com/microsoft/vscode/issues/92826 @@ -30,6 +31,7 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; let disposables: Disposable[] = []; teardown(() => { + assertNoRpc(); disposables.forEach(d => d.dispose()); disposables.length = 0; }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts index 53265b35e99..7143b40fa0c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts @@ -6,9 +6,12 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; +import { assertNoRpc } from '../utils'; suite('vscode API - types', () => { + teardown(assertNoRpc); + test('static properties, es5 compat class', function () { assert.ok(vscode.ThemeIcon.File instanceof vscode.ThemeIcon); assert.ok(vscode.ThemeIcon.Folder instanceof vscode.ThemeIcon); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index 83357278c5d..14947da78e2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as os from 'os'; import * as vscode from 'vscode'; -import { closeAllEditors, delay, disposeAll } from '../utils'; +import { assertNoRpc, closeAllEditors, delay, disposeAll } from '../utils'; const webviewId = 'myWebview'; @@ -26,8 +26,8 @@ suite.skip('vscode API - webview', () => { } teardown(async () => { + assertNoRpc(); await closeAllEditors(); - disposeAll(disposables); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 89f3dbb7259..65fd89d3b96 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -6,12 +6,15 @@ import * as assert from 'assert'; import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, QuickPickItem, TextEditor } from 'vscode'; import { join } from 'path'; -import { closeAllEditors, pathEquals, createRandomFile } from '../utils'; +import { closeAllEditors, pathEquals, createRandomFile, assertNoRpc } from '../utils'; suite('vscode API - window', () => { - teardown(closeAllEditors); + teardown(async function () { + assertNoRpc(); + await closeAllEditors(); + }); test('editor, active text editor', async () => { const doc = await workspace.openTextDocument(join(workspace.rootPath || '', './far.js')); @@ -429,8 +432,8 @@ suite('vscode API - window', () => { }); test('showQuickPick, select first two', async function () { - const label = 'showQuickPick, select first two'; - let i = 0; + // const label = 'showQuickPick, select first two'; + // let i = 0; const resolves: ((value: string) => void)[] = []; let done: () => void; const unexpected = new Promise((resolve, reject) => { @@ -442,26 +445,26 @@ suite('vscode API - window', () => { canPickMany: true }); const first = new Promise(resolve => resolves.push(resolve)); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); await new Promise(resolve => setTimeout(resolve, 100)); // Allow UI to update. - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickOpenSelectNext'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); assert.equal(await first, 'eins'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickPickManyToggle'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); const second = new Promise(resolve => resolves.push(resolve)); await commands.executeCommand('workbench.action.quickOpenSelectNext'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); assert.equal(await second, 'zwei'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.quickPickManyToggle'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); assert.deepStrictEqual(await picks, ['eins', 'zwei']); - console.log(`${label}: ${++i}`); + // console.log(`${label}: ${++i}`); done!(); return unexpected; }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts index e192c63c0ab..e3f0c13dd2d 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.event.test.ts @@ -5,16 +5,15 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, withLogDisabled } from '../utils'; +import { assertNoRpc, createRandomFile, disposeAll, withLogDisabled } from '../utils'; suite('vscode API - workspace events', () => { const disposables: vscode.Disposable[] = []; teardown(() => { - for (const dispo of disposables) { - dispo.dispose(); - } + assertNoRpc(); + disposeAll(disposables); disposables.length = 0; }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts index 2eb21a4c1f9..193ab54be61 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { posix } from 'path'; +import { assertNoRpc } from '../utils'; suite('vscode API - workspace-fs', () => { @@ -15,6 +16,8 @@ suite('vscode API - workspace-fs', () => { root = vscode.workspace.workspaceFolders![0]!.uri; }); + teardown(assertNoRpc); + test('fs.stat', async function () { const stat = await vscode.workspace.fs.stat(root); assert.equal(stat.type, vscode.FileType.Directory); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts index c831e6d4306..d6761c73444 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event } from 'vscode'; +import { assertNoRpc } from '../utils'; // Disable tasks tests: // - Web https://github.com/microsoft/vscode/issues/90528 @@ -14,6 +15,7 @@ import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomEx let disposables: Disposable[] = []; teardown(() => { + assertNoRpc(); disposables.forEach(d => d.dispose()); disposables.length = 0; }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index b3241e6d384..ace2479b343 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -5,14 +5,17 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled, revertAllDirty } from '../utils'; +import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName, disposeAll, testFs, delay, withLogDisabled, revertAllDirty, assertNoRpc } from '../utils'; import { join, posix, basename } from 'path'; import * as fs from 'fs'; import { TestFS } from '../memfs'; suite('vscode API - workspace', () => { - teardown(closeAllEditors); + teardown(async function () { + assertNoRpc(); + await closeAllEditors(); + }); test('MarkdownString', function () { let md = new vscode.MarkdownString(); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 392d0be8461..f35acafde7d 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -72,3 +72,44 @@ export function withLogDisabled(runnable: () => Promise): () => Promise) { + if (!obj) { + return; + } + if (typeof obj !== 'object' && typeof obj !== 'function') { + return; + } + if (seen.has(obj)) { + return; + } + seen.add(obj); + + if (obj[symProtocol]) { + rpcPaths.push(`PROTOCOL via ${path}`); + } + if (obj[symProxy]) { + proxyPaths.push(`PROXY '${obj[symProxy]}' via ${path}`); + } + + for (const key in obj) { + walk(obj[key], `${path}.${String(key)}`, seen); + } + } + + try { + walk(vscode, 'vscode', new Set()); + } catch (err) { + assert.fail(err); + } + assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); + assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // happens... +} From 82c629eb3a4360f4ba5a47668e7e973839b5f6e8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Feb 2021 11:23:37 +0100 Subject: [PATCH 08/34] hide tasks in TaskExecution --- src/vs/workbench/api/common/extHostTask.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 9b0753a5709..35e824bad1d 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -341,7 +341,10 @@ export namespace TaskFilterDTO { class TaskExecutionImpl implements vscode.TaskExecution { - constructor(private readonly _tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) { + readonly #tasks: ExtHostTaskBase; + + constructor(tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) { + this.#tasks = tasks; } public get task(): vscode.Task { @@ -349,7 +352,7 @@ class TaskExecutionImpl implements vscode.TaskExecution { } public terminate(): void { - this._tasks.terminateTask(this); + this.#tasks.terminateTask(this); } public fireDidStartProcess(value: tasks.TaskProcessStartedDTO): void { From a60beb9d7a8ade758f62dd12fa3e4461955554eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Feb 2021 11:24:30 +0100 Subject: [PATCH 09/34] don't leak proxies in editor land, also remove indentSize property which isn't API --- .../workbench/api/browser/mainThreadEditor.ts | 9 - .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 - .../workbench/api/common/extHostCodeInsets.ts | 6 +- .../api/common/extHostDocumentsAndEditors.ts | 23 +- .../workbench/api/common/extHostTextEditor.ts | 432 ++++++++---------- .../api/common/extHostTextEditors.ts | 23 +- .../browser/api/extHostTextEditor.test.ts | 207 ++------- 8 files changed, 258 insertions(+), 446 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index f805b7f9cbe..37d4d9326d4 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -79,7 +79,6 @@ export class MainThreadTextEditorProperties { return { insertSpaces: modelOptions.insertSpaces, tabSize: modelOptions.tabSize, - indentSize: modelOptions.indentSize, cursorStyle: cursorStyle, lineNumbers: lineNumbers }; @@ -146,7 +145,6 @@ export class MainThreadTextEditorProperties { } return ( a.tabSize === b.tabSize - && a.indentSize === b.indentSize && a.insertSpaces === b.insertSpaces && a.cursorStyle === b.cursorStyle && a.lineNumbers === b.lineNumbers @@ -377,13 +375,6 @@ export class MainThreadTextEditor { if (typeof newConfiguration.tabSize !== 'undefined') { newOpts.tabSize = newConfiguration.tabSize; } - if (typeof newConfiguration.indentSize !== 'undefined') { - if (newConfiguration.indentSize === 'tabSize') { - newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize; - } else { - newOpts.indentSize = newConfiguration.indentSize; - } - } this._model.updateOptions(newOpts); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index a32d5b80013..a21a1a708b0 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -262,7 +262,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { checkProposedApiEnabled(extension); return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { - const activeTextEditor = extHostEditors.getActiveTextEditor(); + const activeTextEditor = extHostDocumentsAndEditors.activeEditor(true); if (!activeTextEditor) { extHostLogService.warn('Cannot execute ' + id + ' because there is no active text editor.'); return undefined; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index be7a14f8859..1be08c2c007 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -237,7 +237,6 @@ export interface MainThreadDocumentsShape extends IDisposable { export interface ITextEditorConfigurationUpdate { tabSize?: number | 'auto'; - indentSize?: number | 'tabSize'; insertSpaces?: boolean | 'auto'; cursorStyle?: TextEditorCursorStyle; lineNumbers?: RenderLineNumbersType; @@ -245,7 +244,6 @@ export interface ITextEditorConfigurationUpdate { export interface IResolvedTextEditorConfiguration { tabSize: number; - indentSize: number; insertSpaces: boolean; cursorStyle: TextEditorCursorStyle; lineNumbers: RenderLineNumbersType; diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index 138e4950ae2..7db034cd81f 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -44,8 +44,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { createWebviewEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions | undefined, extension: IExtensionDescription): vscode.WebviewEditorInset { let apiEditor: ExtHostTextEditor | undefined; - for (const candidate of this._editors.getVisibleTextEditors()) { - if (candidate === editor) { + for (const candidate of this._editors.getVisibleTextEditors(true)) { + if (candidate.value === editor) { apiEditor = candidate; break; } @@ -121,7 +121,7 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { } }; - this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation); + this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.value.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation); this._insets.set(handle, { editor, inset, onDidReceiveMessage }); return inset; diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index e378b49a198..bd80952ee53 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -18,6 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Iterable } from 'vs/base/common/iterator'; +import { Lazy } from 'vs/base/common/lazy'; class Reference { private _count = 0; @@ -49,13 +50,13 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha private readonly _onDidAddDocuments = new Emitter(); private readonly _onDidRemoveDocuments = new Emitter(); - private readonly _onDidChangeVisibleTextEditors = new Emitter(); - private readonly _onDidChangeActiveTextEditor = new Emitter(); + private readonly _onDidChangeVisibleTextEditors = new Emitter(); + private readonly _onDidChangeActiveTextEditor = new Emitter(); readonly onDidAddDocuments: Event = this._onDidAddDocuments.event; readonly onDidRemoveDocuments: Event = this._onDidRemoveDocuments.event; - readonly onDidChangeVisibleTextEditors: Event = this._onDidChangeVisibleTextEditors.event; - readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; + readonly onDidChangeVisibleTextEditors: Event = this._onDidChangeVisibleTextEditors.event; + readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; constructor( @IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService, @@ -135,7 +136,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha data.id, this._extHostRpc.getProxy(MainContext.MainThreadTextEditors), this._logService, - documentData, + new Lazy(() => documentData.document), data.selections.map(typeConverters.Selection.to), data.options, data.visibleRanges.map(range => typeConverters.Range.to(range)), @@ -162,7 +163,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } if (delta.removedEditors || delta.addedEditors) { - this._onDidChangeVisibleTextEditors.fire(this.allEditors()); + this._onDidChangeVisibleTextEditors.fire(this.allEditors().map(editor => editor.value)); } if (delta.newActiveEditor !== undefined) { this._onDidChangeActiveTextEditor.fire(this.activeEditor()); @@ -181,11 +182,17 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha return this._editors.get(id); } - activeEditor(): ExtHostTextEditor | undefined { + activeEditor(): vscode.TextEditor | undefined; + activeEditor(internal: true): ExtHostTextEditor | undefined; + activeEditor(internal?: true): vscode.TextEditor | ExtHostTextEditor | undefined { if (!this._activeEditorId) { return undefined; + } + const editor = this._editors.get(this._activeEditorId); + if (internal) { + return editor; } else { - return this._editors.get(this._activeEditorId); + return editor?.value; } } diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index 635a5c6c253..6e2a3e4e324 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -10,11 +10,11 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; +import { Lazy } from 'vs/base/common/lazy'; export class TextEditorDecorationType implements vscode.TextEditorDecorationType { @@ -134,36 +134,63 @@ export class TextEditorEdit { } } -export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { +export class ExtHostTextEditorOptions { private _proxy: MainThreadTextEditorsShape; private _id: string; private _logService: ILogService; private _tabSize!: number; - private _indentSize!: number; private _insertSpaces!: boolean; private _cursorStyle!: TextEditorCursorStyle; private _lineNumbers!: TextEditorLineNumbersStyle; + readonly value: vscode.TextEditorOptions; + constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration, logService: ILogService) { this._proxy = proxy; this._id = id; this._accept(source); this._logService = logService; + + const that = this; + + this.value = { + get tabSize(): number | string { + return that._tabSize; + }, + set tabSize(value: number | string) { + that._setTabSize(value); + }, + get insertSpaces(): boolean | string { + return that._insertSpaces; + }, + set insertSpaces(value: boolean | string) { + that._setInsertSpaces(value); + }, + get cursorStyle(): TextEditorCursorStyle { + return that._cursorStyle; + }, + set cursorStyle(value: TextEditorCursorStyle) { + that._setCursorStyle(value); + }, + get lineNumbers(): TextEditorLineNumbersStyle { + return that._lineNumbers; + }, + set lineNumbers(value: TextEditorLineNumbersStyle) { + that._setLineNumbers(value); + } + }; } public _accept(source: IResolvedTextEditorConfiguration): void { this._tabSize = source.tabSize; - this._indentSize = source.indentSize; this._insertSpaces = source.insertSpaces; this._cursorStyle = source.cursorStyle; this._lineNumbers = TypeConverters.TextEditorLineNumbersStyle.to(source.lineNumbers); } - public get tabSize(): number | string { - return this._tabSize; - } + // --- internal: tabSize private _validateTabSize(value: number | string): number | 'auto' | null { if (value === 'auto') { @@ -183,7 +210,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return null; } - public set tabSize(value: number | string) { + private _setTabSize(value: number | string) { const tabSize = this._validateTabSize(value); if (tabSize === null) { // ignore invalid call @@ -202,50 +229,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } - public get indentSize(): number | string { - return this._indentSize; - } - - private _validateIndentSize(value: number | string): number | 'tabSize' | null { - if (value === 'tabSize') { - return 'tabSize'; - } - if (typeof value === 'number') { - const r = Math.floor(value); - return (r > 0 ? r : null); - } - if (typeof value === 'string') { - const r = parseInt(value, 10); - if (isNaN(r)) { - return null; - } - return (r > 0 ? r : null); - } - return null; - } - - public set indentSize(value: number | string) { - const indentSize = this._validateIndentSize(value); - if (indentSize === null) { - // ignore invalid call - return; - } - if (typeof indentSize === 'number') { - if (this._indentSize === indentSize) { - // nothing to do - return; - } - // reflect the new indentSize value immediately - this._indentSize = indentSize; - } - this._warnOnError(this._proxy.$trySetOptions(this._id, { - indentSize: indentSize - })); - } - - public get insertSpaces(): boolean | string { - return this._insertSpaces; - } + // --- internal: insert spaces private _validateInsertSpaces(value: boolean | string): boolean | 'auto' { if (value === 'auto') { @@ -254,7 +238,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return (value === 'false' ? false : Boolean(value)); } - public set insertSpaces(value: boolean | string) { + private _setInsertSpaces(value: boolean | string) { const insertSpaces = this._validateInsertSpaces(value); if (typeof insertSpaces === 'boolean') { if (this._insertSpaces === insertSpaces) { @@ -269,11 +253,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } - public get cursorStyle(): TextEditorCursorStyle { - return this._cursorStyle; - } + // --- internal: cursor style - public set cursorStyle(value: TextEditorCursorStyle) { + private _setCursorStyle(value: TextEditorCursorStyle) { if (this._cursorStyle === value) { // nothing to do return; @@ -284,11 +266,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } - public get lineNumbers(): TextEditorLineNumbersStyle { - return this._lineNumbers; - } + // --- internal: line number - public set lineNumbers(value: TextEditorLineNumbersStyle) { + private _setLineNumbers(value: TextEditorLineNumbersStyle) { if (this._lineNumbers === value) { // nothing to do return; @@ -368,31 +348,170 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } } -export class ExtHostTextEditor implements vscode.TextEditor { - - private readonly _documentData: ExtHostDocumentData; +export class ExtHostTextEditor { private _selections: Selection[]; private _options: ExtHostTextEditorOptions; private _visibleRanges: Range[]; private _viewColumn: vscode.ViewColumn | undefined; private _disposed: boolean = false; - private _hasDecorationsForKey: { [key: string]: boolean; }; + private _hasDecorationsForKey = new Set(); + + readonly value: vscode.TextEditor; constructor( readonly id: string, private readonly _proxy: MainThreadTextEditorsShape, private readonly _logService: ILogService, - document: ExtHostDocumentData, + document: Lazy, selections: Selection[], options: IResolvedTextEditorConfiguration, visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined ) { - this._documentData = document; this._selections = selections; this._options = new ExtHostTextEditorOptions(this._proxy, this.id, options, _logService); this._visibleRanges = visibleRanges; this._viewColumn = viewColumn; - this._hasDecorationsForKey = Object.create(null); + + const that = this; + + this.value = Object.freeze({ + get document(): vscode.TextDocument { + return document.getValue(); + }, + set document(_value) { + throw readonly('document'); + }, + // --- selection + get selection(): Selection { + return that._selections && that._selections[0]; + }, + set selection(value: Selection) { + if (!(value instanceof Selection)) { + throw illegalArgument('selection'); + } + that._selections = [value]; + that._trySetSelection(); + }, + get selections(): Selection[] { + return that._selections; + }, + set selections(value: Selection[]) { + if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) { + throw illegalArgument('selections'); + } + that._selections = value; + that._trySetSelection(); + }, + // --- visible ranges + get visibleRanges(): Range[] { + return that._visibleRanges; + }, + set visibleRanges(_value: Range[]) { + throw readonly('visibleRanges'); + }, + // --- options + get options(): vscode.TextEditorOptions { + return that._options.value; + }, + set options(value: vscode.TextEditorOptions) { + if (!that._disposed) { + that._options.assign(value); + } + }, + // --- view column + get viewColumn(): vscode.ViewColumn | undefined { + return that._viewColumn; + }, + set viewColumn(_value) { + throw readonly('viewColumn'); + }, + // --- edit + edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { + if (that._disposed) { + return Promise.reject(new Error('TextEditor#edit not possible on closed editors')); + } + const edit = new TextEditorEdit(document.getValue(), options); + callback(edit); + return that._applyEdit(edit); + }, + // --- snippet edit + insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { + if (that._disposed) { + return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors')); + } + let ranges: IRange[]; + + if (!where || (Array.isArray(where) && where.length === 0)) { + ranges = that._selections.map(range => TypeConverters.Range.from(range)); + + } else if (where instanceof Position) { + const { lineNumber, column } = TypeConverters.Position.from(where); + ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }]; + + } else if (where instanceof Range) { + ranges = [TypeConverters.Range.from(where)]; + } else { + ranges = []; + for (const posOrRange of where) { + if (posOrRange instanceof Range) { + ranges.push(TypeConverters.Range.from(posOrRange)); + } else { + const { lineNumber, column } = TypeConverters.Position.from(posOrRange); + ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }); + } + } + } + return _proxy.$tryInsertSnippet(id, snippet.value, ranges, options); + }, + setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void { + const willBeEmpty = (ranges.length === 0); + if (willBeEmpty && !that._hasDecorationsForKey.has(decorationType.key)) { + // avoid no-op call to the renderer + return; + } + if (willBeEmpty) { + that._hasDecorationsForKey.delete(decorationType.key); + } else { + that._hasDecorationsForKey.add(decorationType.key); + } + that._runOnProxy(() => { + if (TypeConverters.isDecorationOptionsArr(ranges)) { + return _proxy.$trySetDecorations( + id, + decorationType.key, + TypeConverters.fromRangeOrRangeWithMessage(ranges) + ); + } else { + const _ranges: number[] = new Array(4 * ranges.length); + for (let i = 0, len = ranges.length; i < len; i++) { + const range = ranges[i]; + _ranges[4 * i] = range.start.line + 1; + _ranges[4 * i + 1] = range.start.character + 1; + _ranges[4 * i + 2] = range.end.line + 1; + _ranges[4 * i + 3] = range.end.character + 1; + } + return _proxy.$trySetDecorationsFast( + id, + decorationType.key, + _ranges + ); + } + }); + }, + revealRange(range: Range, revealType: vscode.TextEditorRevealType): void { + that._runOnProxy(() => _proxy.$tryRevealRange( + id, + TypeConverters.Range.from(range), + (revealType || TextEditorRevealType.Default) + )); + }, + show(column: vscode.ViewColumn) { + _proxy.$tryShowEditor(id, TypeConverters.ViewColumn.from(column)); + }, + hide() { + _proxy.$tryHideEditor(id); + } + }); } dispose() { @@ -400,164 +519,32 @@ export class ExtHostTextEditor implements vscode.TextEditor { this._disposed = true; } - show(column: vscode.ViewColumn) { - this._proxy.$tryShowEditor(this.id, TypeConverters.ViewColumn.from(column)); - } - - hide() { - this._proxy.$tryHideEditor(this.id); - } - - // ---- the document - - get document(): vscode.TextDocument { - return this._documentData.document; - } - - set document(value) { - throw readonly('document'); - } - - // ---- options - - get options(): vscode.TextEditorOptions { - return this._options; - } - - set options(value: vscode.TextEditorOptions) { - if (!this._disposed) { - this._options.assign(value); - } - } + // --- incoming: extension host MUST accept what the renderer says _acceptOptions(options: IResolvedTextEditorConfiguration): void { ok(!this._disposed); this._options._accept(options); } - // ---- visible ranges - - get visibleRanges(): Range[] { - return this._visibleRanges; - } - - set visibleRanges(value: Range[]) { - throw readonly('visibleRanges'); - } - _acceptVisibleRanges(value: Range[]): void { ok(!this._disposed); this._visibleRanges = value; } - // ---- view column - - get viewColumn(): vscode.ViewColumn | undefined { - return this._viewColumn; - } - - set viewColumn(value) { - throw readonly('viewColumn'); - } - _acceptViewColumn(value: vscode.ViewColumn) { ok(!this._disposed); this._viewColumn = value; } - // ---- selections - - get selection(): Selection { - return this._selections && this._selections[0]; - } - - set selection(value: Selection) { - if (!(value instanceof Selection)) { - throw illegalArgument('selection'); - } - this._selections = [value]; - this._trySetSelection(); - } - - get selections(): Selection[] { - return this._selections; - } - - set selections(value: Selection[]) { - if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) { - throw illegalArgument('selections'); - } - this._selections = value; - this._trySetSelection(); - } - - setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void { - const willBeEmpty = (ranges.length === 0); - if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) { - // avoid no-op call to the renderer - return; - } - if (willBeEmpty) { - delete this._hasDecorationsForKey[decorationType.key]; - } else { - this._hasDecorationsForKey[decorationType.key] = true; - } - this._runOnProxy( - () => { - if (TypeConverters.isDecorationOptionsArr(ranges)) { - return this._proxy.$trySetDecorations( - this.id, - decorationType.key, - TypeConverters.fromRangeOrRangeWithMessage(ranges) - ); - } else { - const _ranges: number[] = new Array(4 * ranges.length); - for (let i = 0, len = ranges.length; i < len; i++) { - const range = ranges[i]; - _ranges[4 * i] = range.start.line + 1; - _ranges[4 * i + 1] = range.start.character + 1; - _ranges[4 * i + 2] = range.end.line + 1; - _ranges[4 * i + 3] = range.end.character + 1; - } - return this._proxy.$trySetDecorationsFast( - this.id, - decorationType.key, - _ranges - ); - } - } - ); - } - - revealRange(range: Range, revealType: vscode.TextEditorRevealType): void { - this._runOnProxy( - () => this._proxy.$tryRevealRange( - this.id, - TypeConverters.Range.from(range), - (revealType || TextEditorRevealType.Default) - ) - ); - } - - private _trySetSelection(): Promise { - const selection = this._selections.map(TypeConverters.Selection.from); - return this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection)); - } - _acceptSelections(selections: Selection[]): void { ok(!this._disposed); this._selections = selections; } - // ---- editing - - edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { - if (this._disposed) { - return Promise.reject(new Error('TextEditor#edit not possible on closed editors')); - } - const edit = new TextEditorEdit(this._documentData.document, options); - callback(edit); - return this._applyEdit(edit); + private async _trySetSelection(): Promise { + const selection = this._selections.map(TypeConverters.Selection.from); + await this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection)); + return this.value; } private _applyEdit(editBuilder: TextEditorEdit): Promise { @@ -613,44 +600,12 @@ export class ExtHostTextEditor implements vscode.TextEditor { undoStopAfter: editData.undoStopAfter }); } - - insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { - if (this._disposed) { - return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors')); - } - let ranges: IRange[]; - - if (!where || (Array.isArray(where) && where.length === 0)) { - ranges = this._selections.map(range => TypeConverters.Range.from(range)); - - } else if (where instanceof Position) { - const { lineNumber, column } = TypeConverters.Position.from(where); - ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }]; - - } else if (where instanceof Range) { - ranges = [TypeConverters.Range.from(where)]; - } else { - ranges = []; - for (const posOrRange of where) { - if (posOrRange instanceof Range) { - ranges.push(TypeConverters.Range.from(posOrRange)); - } else { - const { lineNumber, column } = TypeConverters.Position.from(posOrRange); - ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }); - } - } - } - - return this._proxy.$tryInsertSnippet(this.id, snippet.value, ranges, options); - } - - // ---- util - private _runOnProxy(callback: () => Promise): Promise { if (this._disposed) { this._logService.warn('TextEditor is closed/disposed'); return Promise.resolve(undefined); } + return callback().then(() => this, err => { if (!(err instanceof Error && err.name === 'DISPOSED')) { this._logService.warn(err); @@ -659,4 +614,3 @@ export class ExtHostTextEditor implements vscode.TextEditor { }); } } - diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index 3e95b2f382e..b01d113fe2b 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -41,12 +41,17 @@ export class ExtHostEditors implements ExtHostEditorsShape { this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e)); } - getActiveTextEditor(): ExtHostTextEditor | undefined { + getActiveTextEditor(): vscode.TextEditor | undefined { return this._extHostDocumentsAndEditors.activeEditor(); } - getVisibleTextEditors(): vscode.TextEditor[] { - return this._extHostDocumentsAndEditors.allEditors(); + getVisibleTextEditors(): vscode.TextEditor[]; + getVisibleTextEditors(internal: true): ExtHostTextEditor[]; + getVisibleTextEditors(internal?: true): ExtHostTextEditor[] | vscode.TextEditor[] { + const editors = this._extHostDocumentsAndEditors.allEditors(); + return internal + ? editors + : editors.map(editor => editor.value); } showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise; @@ -75,7 +80,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { const editorId = await this._proxy.$tryShowTextDocument(document.uri, options); const editor = editorId && this._extHostDocumentsAndEditors.getEditor(editorId); if (editor) { - return editor; + return editor.value; } // we have no editor... having an id means that we had an editor // on the main side and that it isn't the current editor anymore... @@ -114,7 +119,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { // (2) fire change events if (data.options) { this._onDidChangeTextEditorOptions.fire({ - textEditor: textEditor, + textEditor: textEditor.value, options: { ...data.options, lineNumbers: TypeConverters.TextEditorLineNumbersStyle.to(data.options.lineNumbers) } }); } @@ -122,7 +127,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { const kind = TextEditorSelectionChangeKind.fromValue(data.selections.source); const selections = data.selections.selections.map(TypeConverters.Selection.to); this._onDidChangeTextEditorSelection.fire({ - textEditor, + textEditor: textEditor.value, selections, kind }); @@ -130,7 +135,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { if (data.visibleRanges) { const visibleRanges = arrays.coalesce(data.visibleRanges.map(TypeConverters.Range.to)); this._onDidChangeTextEditorVisibleRanges.fire({ - textEditor, + textEditor: textEditor.value, visibleRanges }); } @@ -143,9 +148,9 @@ export class ExtHostEditors implements ExtHostEditorsShape { throw new Error('Unknown text editor'); } const viewColumn = TypeConverters.ViewColumn.to(data[id]); - if (textEditor.viewColumn !== viewColumn) { + if (textEditor.value.viewColumn !== viewColumn) { textEditor._acceptViewColumn(viewColumn); - this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn }); + this._onDidChangeTextEditorViewColumn.fire({ textEditor: textEditor.value, viewColumn }); } } } diff --git a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts index 6e59c522f2a..ca9a7f7999d 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts @@ -11,6 +11,7 @@ import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { NullLogService } from 'vs/platform/log/common/log'; +import { Lazy } from 'vs/base/common/lazy'; suite('ExtHostTextEditor', () => { @@ -20,21 +21,21 @@ suite('ExtHostTextEditor', () => { ], '\n', 1, 'text', false); setup(() => { - editor = new ExtHostTextEditor('fake', null!, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); + editor = new ExtHostTextEditor('fake', null!, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); }); test('disposed editor', () => { - assert.ok(editor.document); + assert.ok(editor.value.document); editor._acceptViewColumn(3); - assert.strictEqual(3, editor.viewColumn); + assert.strictEqual(3, editor.value.viewColumn); editor.dispose(); assert.throws(() => editor._acceptViewColumn(2)); - assert.strictEqual(3, editor.viewColumn); + assert.strictEqual(3, editor.value.viewColumn); - assert.ok(editor.document); + assert.ok(editor.value.document); assert.throws(() => editor._acceptOptions(null!)); assert.throws(() => editor._acceptSelections([])); }); @@ -47,15 +48,15 @@ suite('ExtHostTextEditor', () => { applyCount += 1; return Promise.resolve(true); } - }, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); + }, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); - await editor.edit(edit => { }); + await editor.value.edit(edit => { }); assert.strictEqual(applyCount, 0); - await editor.edit(edit => { edit.setEndOfLine(1); }); + await editor.value.edit(edit => { edit.setEndOfLine(1); }); assert.strictEqual(applyCount, 1); - await editor.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); }); + await editor.value.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); }); assert.strictEqual(applyCount, 2); }); }); @@ -89,7 +90,6 @@ suite('ExtHostTextEditorOptions', () => { }; opts = new ExtHostTextEditorOptions(mockProxy, '1', { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -103,20 +103,18 @@ suite('ExtHostTextEditorOptions', () => { function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void { let actual = { - tabSize: opts.tabSize, - indentSize: opts.indentSize, - insertSpaces: opts.insertSpaces, - cursorStyle: opts.cursorStyle, - lineNumbers: opts.lineNumbers + tabSize: opts.value.tabSize, + insertSpaces: opts.value.insertSpaces, + cursorStyle: opts.value.cursorStyle, + lineNumbers: opts.value.lineNumbers }; assert.deepStrictEqual(actual, expected); } test('can set tabSize to the same value', () => { - opts.tabSize = 4; + opts.value.tabSize = 4; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -125,10 +123,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change tabSize to positive integer', () => { - opts.tabSize = 1; + opts.value.tabSize = 1; assertState(opts, { tabSize: 1, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -137,10 +134,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change tabSize to positive float', () => { - opts.tabSize = 2.3; + opts.value.tabSize = 2.3; assertState(opts, { tabSize: 2, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -149,10 +145,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change tabSize to a string number', () => { - opts.tabSize = '2'; + opts.value.tabSize = '2'; assertState(opts, { tabSize: 2, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -161,10 +156,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('tabSize can request indentation detection', () => { - opts.tabSize = 'auto'; + opts.value.tabSize = 'auto'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -173,10 +167,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 1', () => { - opts.tabSize = null!; + opts.value.tabSize = null!; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -185,10 +178,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 2', () => { - opts.tabSize = -5; + opts.value.tabSize = -5; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -197,10 +189,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 3', () => { - opts.tabSize = 'hello'; + opts.value.tabSize = 'hello'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -209,130 +200,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 4', () => { - opts.tabSize = '-17'; + opts.value.tabSize = '-17'; assertState(opts, { tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('can set indentSize to the same value', () => { - opts.indentSize = 4; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('can change indentSize to positive integer', () => { - opts.indentSize = 1; - assertState(opts, { - tabSize: 4, - indentSize: 1, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 1 }]); - }); - - test('can change indentSize to positive float', () => { - opts.indentSize = 2.3; - assertState(opts, { - tabSize: 4, - indentSize: 2, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 2 }]); - }); - - test('can change indentSize to a string number', () => { - opts.indentSize = '2'; - assertState(opts, { - tabSize: 4, - indentSize: 2, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 2 }]); - }); - - test('indentSize can request to use tabSize', () => { - opts.indentSize = 'tabSize'; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 'tabSize' }]); - }); - - test('indentSize cannot request indentation detection', () => { - opts.indentSize = 'auto'; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 1', () => { - opts.indentSize = null!; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 2', () => { - opts.indentSize = -5; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 3', () => { - opts.indentSize = 'hello'; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 4', () => { - opts.indentSize = '-17'; - assertState(opts, { - tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -341,10 +211,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to the same value', () => { - opts.insertSpaces = false; + opts.value.insertSpaces = false; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -353,10 +222,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to boolean', () => { - opts.insertSpaces = true; + opts.value.insertSpaces = true; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -365,10 +233,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to false string', () => { - opts.insertSpaces = 'false'; + opts.value.insertSpaces = 'false'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -377,10 +244,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to truey', () => { - opts.insertSpaces = 'hello'; + opts.value.insertSpaces = 'hello'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -389,10 +255,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('insertSpaces can request indentation detection', () => { - opts.insertSpaces = 'auto'; + opts.value.insertSpaces = 'auto'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -401,10 +266,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set cursorStyle to same value', () => { - opts.cursorStyle = TextEditorCursorStyle.Line; + opts.value.cursorStyle = TextEditorCursorStyle.Line; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -413,10 +277,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change cursorStyle', () => { - opts.cursorStyle = TextEditorCursorStyle.Block; + opts.value.cursorStyle = TextEditorCursorStyle.Block; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.On @@ -425,10 +288,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set lineNumbers to same value', () => { - opts.lineNumbers = TextEditorLineNumbersStyle.On; + opts.value.lineNumbers = TextEditorLineNumbersStyle.On; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -437,10 +299,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change lineNumbers', () => { - opts.lineNumbers = TextEditorLineNumbersStyle.Off; + opts.value.lineNumbers = TextEditorLineNumbersStyle.Off; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.Off @@ -457,7 +318,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -472,7 +332,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -487,7 +346,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 3, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -502,7 +360,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.Relative From 3d2ca29012c9dfc4e6671cbda949b458713d7827 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Feb 2021 11:55:06 +0100 Subject: [PATCH 10/34] hide terminal proxy --- .../api/common/extHostTerminalService.ts | 123 +++++++++--------- .../api/node/extHostTerminalService.ts | 4 +- 2 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index f684dbb85b4..babcce145eb 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -47,7 +47,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID export const IExtHostTerminalService = createDecorator('IExtHostTerminalService'); -export class ExtHostTerminal implements vscode.Terminal { +export class ExtHostTerminal { private _disposed: boolean = false; private _pidPromise: Promise; private _cols: number | undefined; @@ -57,6 +57,8 @@ export class ExtHostTerminal implements vscode.Terminal { public isOpen: boolean = false; + readonly value: vscode.Terminal; + constructor( private _proxy: MainThreadTerminalServiceShape, public _id: TerminalIdentifier, @@ -65,6 +67,49 @@ export class ExtHostTerminal implements vscode.Terminal { ) { this._creationOptions = Object.freeze(this._creationOptions); this._pidPromise = new Promise(c => this._pidPromiseComplete = c); + + const that = this; + this.value = { + get name(): string { + return that._name || ''; + }, + get processId(): Promise { + return that._pidPromise; + }, + get creationOptions(): Readonly { + return that._creationOptions; + }, + get exitStatus(): vscode.TerminalExitStatus | undefined { + return that._exitStatus; + }, + sendText(text: string, addNewLine: boolean = true): void { + that._checkDisposed(); + that._proxy.$sendText(that._id, text, addNewLine); + }, + show(preserveFocus: boolean): void { + that._checkDisposed(); + that._proxy.$show(that._id, preserveFocus); + }, + hide(): void { + that._checkDisposed(); + that._proxy.$hide(that._id); + }, + dispose(): void { + if (!that._disposed) { + that._disposed = true; + that._proxy.$dispose(that._id); + } + }, + get dimensions(): vscode.TerminalDimensions | undefined { + if (that._cols === undefined || that._rows === undefined) { + return undefined; + } + return { + columns: that._cols, + rows: that._rows + }; + } + }; } public async create( @@ -95,41 +140,16 @@ export class ExtHostTerminal implements vscode.Terminal { return this._id; } - public dispose(): void { - if (!this._disposed) { - this._disposed = true; - this._proxy.$dispose(this._id); - } - } - private _checkDisposed() { if (this._disposed) { throw new Error('Terminal has already been disposed'); } } - public get name(): string { - return this._name || ''; - } - public set name(name: string) { this._name = name; } - public get exitStatus(): vscode.TerminalExitStatus | undefined { - return this._exitStatus; - } - - public get dimensions(): vscode.TerminalDimensions | undefined { - if (this._cols === undefined || this._rows === undefined) { - return undefined; - } - return { - columns: this._cols, - rows: this._rows - }; - } - public setExitCode(code: number | undefined) { this._exitStatus = Object.freeze({ code }); } @@ -147,29 +167,6 @@ export class ExtHostTerminal implements vscode.Terminal { return true; } - public get processId(): Promise { - return this._pidPromise; - } - - public get creationOptions(): Readonly { - return this._creationOptions; - } - - public sendText(text: string, addNewLine: boolean = true): void { - this._checkDisposed(); - this._proxy.$sendText(this._id, text, addNewLine); - } - - public show(preserveFocus: boolean): void { - this._checkDisposed(); - this._proxy.$show(this._id, preserveFocus); - } - - public hide(): void { - this._checkDisposed(); - this._proxy.$hide(this._id); - } - public _setProcessId(processId: number | undefined): void { // The event may fire 2 times when the panel is restored if (this._pidPromiseComplete) { @@ -284,8 +281,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I private readonly _terminalLinkCache: Map> = new Map(); private readonly _terminalLinkCancellationSource: Map = new Map(); - public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } - public get terminals(): ExtHostTerminal[] { return this._terminals; } + public get activeTerminal(): vscode.Terminal | undefined { return this._activeTerminal?.value; } + public get terminals(): vscode.Terminal[] { return this._terminals.map(term => term.value); } protected readonly _onDidCloseTerminal: Emitter = new Emitter(); public get onDidCloseTerminal(): Event { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; } @@ -336,7 +333,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I this._terminalProcessDisposables[id] = disposable; }); this._terminals.push(terminal); - return terminal; + return terminal.value; } public attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void { @@ -362,7 +359,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I if (terminal) { this._activeTerminal = terminal; if (original !== this._activeTerminal) { - this._onDidChangeActiveTerminal.fire(this._activeTerminal); + this._onDidChangeActiveTerminal.fire(this._activeTerminal.value); } } } @@ -370,7 +367,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public async $acceptTerminalProcessData(id: number, data: string): Promise { const terminal = this._getTerminalById(id); if (terminal) { - this._onDidWriteTerminalData.fire({ terminal, data }); + this._onDidWriteTerminalData.fire({ terminal: terminal.value, data }); } } @@ -379,8 +376,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I if (terminal) { if (terminal.setDimensions(cols, rows)) { this._onDidChangeTerminalDimensions.fire({ - terminal: terminal, - dimensions: terminal.dimensions as vscode.TerminalDimensions + terminal: terminal.value, + dimensions: terminal.value.dimensions as vscode.TerminalDimensions }); } } @@ -400,11 +397,11 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I } public async $acceptTerminalClosed(id: number, exitCode: number | undefined): Promise { - const index = this._getTerminalObjectIndexById(this.terminals, id); + const index = this._getTerminalObjectIndexById(this._terminals, id); if (index !== null) { const terminal = this._terminals.splice(index, 1)[0]; terminal.setExitCode(exitCode); - this._onDidCloseTerminal.fire(terminal); + this._onDidCloseTerminal.fire(terminal.value); } } @@ -414,9 +411,9 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I const index = this._getTerminalObjectIndexById(this._terminals, extHostTerminalId); if (index !== null) { // The terminal has already been created (via createTerminal*), only fire the event - this.terminals[index]._id = id; + this._terminals[index]._id = id; this._onDidOpenTerminal.fire(this.terminals[index]); - this.terminals[index].isOpen = true; + this._terminals[index].isOpen = true; return; } } @@ -431,7 +428,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I }; const terminal = new ExtHostTerminal(this._proxy, id, creationOptions, name); this._terminals.push(terminal); - this._onDidOpenTerminal.fire(terminal); + this._onDidOpenTerminal.fire(terminal.value); terminal.isOpen = true; } @@ -455,7 +452,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I await new Promise(r => { // Ensure open is called after onDidOpenTerminal const listener = this.onDidOpenTerminal(async e => { - if (e === terminal) { + if (e === terminal.value) { listener.dispose(); r(); } @@ -564,7 +561,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I this._terminalLinkCancellationSource.set(terminalId, cancellationSource); const result: ITerminalLinkDto[] = []; - const context: vscode.TerminalLinkContext = { terminal, line }; + const context: vscode.TerminalLinkContext = { terminal: terminal.value, line }; const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = []; for (const provider of this._linkProviders) { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index adad271b38f..53616f44ba3 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -60,7 +60,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { const terminal = new ExtHostTerminal(this._proxy, generateUuid(), { name, shellPath, shellArgs }, name); this._terminals.push(terminal); terminal.create(shellPath, shellArgs); - return terminal; + return terminal.value; } public createTerminalFromOptions(options: vscode.TerminalOptions, isFeatureTerminal?: boolean): vscode.Terminal { @@ -75,7 +75,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { withNullAsUndefined(options.strictEnv), withNullAsUndefined(options.hideFromUser), withNullAsUndefined(isFeatureTerminal)); - return terminal; + return terminal.value; } public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string { From a0c75f60530da98fbccf796a7c78c23a7ca168d3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Feb 2021 14:52:37 +0100 Subject: [PATCH 11/34] add tests for createXYZ functions and skip most of them... --- .../src/singlefolder-tests/rpc.test.ts | 49 ++++++++++++++++++- extensions/vscode-api-tests/src/utils.ts | 6 ++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 596a7f9746f..68707e42df2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -3,11 +3,58 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assertNoRpc } from '../utils'; +import { assertNoRpcFromEntry, assertNoRpc, disposeAll } from '../utils'; +import * as vscode from 'vscode'; suite('vscode', function () { + const dispo: vscode.Disposable[] = []; + + teardown(() => { + assertNoRpc(); + disposeAll(dispo); + }); + test('no rpc', function () { assertNoRpc(); }); + + test('no rpc, createTextEditorDecorationType(...)', function () { + this.skip(); + const item = vscode.window.createTextEditorDecorationType({}); + dispo.push(item); + assertNoRpcFromEntry([item, 'TextEditorDecorationType']); + }); + + test('no rpc, createOutputChannel(...)', function () { + const item = vscode.window.createOutputChannel('hello'); + dispo.push(item); + assertNoRpcFromEntry([item, 'OutputChannel']); + }); + + test('no rpc, createDiagnosticCollection(...)', function () { + const item = vscode.languages.createDiagnosticCollection(); + dispo.push(item); + assertNoRpcFromEntry([item, 'DiagnosticCollection']); + }); + + test('no rpc, createStatusBarItem(...)', function () { + this.skip(); + const item = vscode.window.createStatusBarItem(); + dispo.push(item); + assertNoRpcFromEntry([item, 'StatusBarItem']); + }); + + test('no rpc, createSourceControl(...)', function () { + this.skip(); + const item = vscode.scm.createSourceControl('foo', 'Hello'); + dispo.push(item); + assertNoRpcFromEntry([item, 'SourceControl']); + }); + test('no rpc, createCommentController(...)', function () { + this.skip(); + const item = vscode.comments.createCommentController('foo', 'Hello'); + dispo.push(item); + assertNoRpcFromEntry([item, 'CommentController']); + }); }); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index f35acafde7d..f7923bbc9f7 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -74,6 +74,10 @@ export function withLogDisabled(runnable: () => Promise): () => Promise Date: Mon, 1 Feb 2021 17:06:51 +0100 Subject: [PATCH 12/34] hide proxy from TextEditorDecorationType --- .../src/singlefolder-tests/rpc.test.ts | 1 - .../workbench/api/common/extHostTextEditor.ts | 19 ++++++++++--------- .../api/common/extHostTextEditors.ts | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 68707e42df2..7d60f3e8771 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -20,7 +20,6 @@ suite('vscode', function () { }); test('no rpc, createTextEditorDecorationType(...)', function () { - this.skip(); const item = vscode.window.createTextEditorDecorationType({}); dispo.push(item); assertNoRpcFromEntry([item, 'TextEditorDecorationType']); diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index 6e2a3e4e324..f3dff81c544 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -16,22 +16,23 @@ import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { Lazy } from 'vs/base/common/lazy'; -export class TextEditorDecorationType implements vscode.TextEditorDecorationType { +export class TextEditorDecorationType { private static readonly _Keys = new IdGenerator('TextEditorDecorationType'); - private _proxy: MainThreadTextEditorsShape; - public key: string; + readonly value: vscode.TextEditorDecorationType; constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) { - this.key = TextEditorDecorationType._Keys.nextId(); - this._proxy = proxy; - this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options)); + const key = TextEditorDecorationType._Keys.nextId(); + proxy.$registerTextEditorDecorationType(key, TypeConverters.DecorationRenderOptions.from(options)); + this.value = Object.freeze({ + key, + dispose() { + proxy.$removeTextEditorDecorationType(key); + } + }); } - public dispose(): void { - this._proxy.$removeTextEditorDecorationType(this.key); - } } export interface ITextEditOperation { diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index b01d113fe2b..dc8bd7bd910 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -92,7 +92,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { } createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType { - return new TextEditorDecorationType(this._proxy, options); + return new TextEditorDecorationType(this._proxy, options).value; } // --- called from main thread From a99dd66f0f70f547e728ce468a41dee7d90841bf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Feb 2021 17:41:29 +0100 Subject: [PATCH 13/34] test more results of create-functions --- .../src/singlefolder-tests/rpc.test.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 7d60f3e8771..0d203040d3a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -37,6 +37,20 @@ suite('vscode', function () { assertNoRpcFromEntry([item, 'DiagnosticCollection']); }); + test('no rpc, createQuickPick(...)', function () { + this.skip(); + const item = vscode.window.createQuickPick(); + dispo.push(item); + assertNoRpcFromEntry([item, 'QuickPick']); + }); + + test('no rpc, createInputBox(...)', function () { + this.skip(); + const item = vscode.window.createInputBox(); + dispo.push(item); + assertNoRpcFromEntry([item, 'InputBox']); + }); + test('no rpc, createStatusBarItem(...)', function () { this.skip(); const item = vscode.window.createStatusBarItem(); @@ -50,10 +64,31 @@ suite('vscode', function () { dispo.push(item); assertNoRpcFromEntry([item, 'SourceControl']); }); + test('no rpc, createCommentController(...)', function () { this.skip(); const item = vscode.comments.createCommentController('foo', 'Hello'); dispo.push(item); assertNoRpcFromEntry([item, 'CommentController']); }); + + test('no rpc, createWebviewPanel(...)', function () { + const item = vscode.window.createWebviewPanel('webview', 'Hello', vscode.ViewColumn.Active); + dispo.push(item); + assertNoRpcFromEntry([item, 'WebviewPanel']); + }); + + test('no rpc, createTreeView(...)', function () { + const treeDataProvider = new class implements vscode.TreeDataProvider { + getTreeItem(element: string): vscode.TreeItem | Thenable { + return new vscode.TreeItem(element); + } + getChildren(_element?: string): vscode.ProviderResult { + return ['foo', 'bar']; + } + }; + const item = vscode.window.createTreeView('test.treeId', { treeDataProvider }); + dispo.push(item); + assertNoRpcFromEntry([item, 'TreeView']); + }); }); From ef1f94d969bde185aa0fcc2bd801f33deb3d32eb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 3 Feb 2021 08:30:47 +0100 Subject: [PATCH 14/34] "Select for Compare" is available for WebView tabs (fix #113703) --- .../contrib/files/browser/fileActions.contribution.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index c9b2dcd2a7f..e19fa398b85 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -227,6 +227,8 @@ appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_ // Menu registration - open editors +const isFileOrUntitledResourceContextKey = ContextKeyExpr.or(ResourceContextKey.IsFileSystemResource, ResourceContextKey.Scheme.isEqualTo(Schemas.untitled)); + const openToSideCommand = { id: OPEN_TO_SIDE_COMMAND_ID, title: nls.localize('openToSide', "Open to the Side") @@ -235,7 +237,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 10, command: openToSideCommand, - when: ContextKeyExpr.or(ResourceContextKey.IsFileSystemResource, ResourceContextKey.Scheme.isEqualTo(Schemas.untitled)) + when: isFileOrUntitledResourceContextKey }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -326,7 +328,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 20, command: compareResourceCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, ResourceSelectedForCompareContext, isFileOrUntitledResourceContextKey, WorkbenchListDoubleSelection.toNegated()) }); const selectForCompareCommand = { @@ -337,7 +339,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: selectForCompareCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, isFileOrUntitledResourceContextKey, WorkbenchListDoubleSelection.toNegated()) }); const compareSelectedCommand = { @@ -348,7 +350,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: compareSelectedCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection, isFileOrUntitledResourceContextKey) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { From 7690293ae208f91f4e3612e309ac130a85244486 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Feb 2021 08:47:31 +0100 Subject: [PATCH 15/34] clean up and add some doc --- .../browser/parts/views/viewsService.ts | 388 +++++++++--------- src/vs/workbench/common/views.ts | 25 +- .../debug/browser/debug.contribution.ts | 7 +- .../browser/extensions.contribution.ts | 2 +- .../contrib/files/browser/explorerViewlet.ts | 5 +- .../markers/browser/markers.contribution.ts | 5 +- .../output/browser/output.contribution.ts | 5 +- .../contrib/scm/browser/scm.contribution.ts | 5 +- .../search/browser/search.contribution.ts | 5 +- .../terminal/browser/terminal.contribution.ts | 5 +- .../views/common/viewContainerModel.ts | 2 +- 11 files changed, 235 insertions(+), 219 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewsService.ts b/src/vs/workbench/browser/parts/views/viewsService.ts index cc1a211f6db..a76607f2e8e 100644 --- a/src/vs/workbench/browser/parts/views/viewsService.ts +++ b/src/vs/workbench/browser/parts/views/viewsService.ts @@ -11,7 +11,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ContextKeyDefinedExpr, ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; import { isString } from 'vs/base/common/types'; -import { MenuId, registerAction2, Action2, MenuRegistry, ICommandActionTitle } from 'vs/platform/actions/common/actions'; +import { MenuId, registerAction2, Action2, MenuRegistry, ICommandActionTitle, ILocalizedString } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -140,68 +140,7 @@ export class ViewsService extends Disposable implements IViewsService { this.onViewDescriptorsAdded(added, viewContainer); this.onViewDescriptorsRemoved(removed); })); - - // Register Action to Open View Container - if (viewContainer.commandActionDescriptor !== false) { - let { id, title, mnemonicTitle, keybindings, order } = viewContainer.commandActionDescriptor ?? { id: viewContainer.id }; - title = title ?? viewContainer.title; - const that = this; - this._register(registerAction2(class OpenViewContainerAction extends Action2 { - constructor() { - super({ - id, - get title(): ICommandActionTitle { - const viewContainerLocation = that.viewDescriptorService.getViewContainerLocation(viewContainer); - if (viewContainerLocation === ViewContainerLocation.Sidebar) { - return { value: localize('show view', "Show {0}", title), original: `Show ${title}` }; - } else { - return { value: localize('toggle view', "Toggle {0}", title), original: `Toggle ${title}` }; - } - }, - category: CATEGORIES.View.value, - precondition: ContextKeyExpr.has(getEnabledViewContainerContextKey(viewContainer.id)), - keybinding: keybindings ? { ...keybindings, weight: KeybindingWeight.WorkbenchContrib } : undefined, - f1: true - }); - } - public async run(serviceAccessor: ServicesAccessor): Promise { - const editorGroupService = serviceAccessor.get(IEditorGroupsService); - const viewDescriptorService = serviceAccessor.get(IViewDescriptorService); - const layoutService = serviceAccessor.get(IWorkbenchLayoutService); - const viewsService = serviceAccessor.get(IViewsService); - const viewContainerLocation = viewDescriptorService.getViewContainerLocation(viewContainer); - switch (viewContainerLocation) { - case ViewContainerLocation.Sidebar: - if (!viewsService.isViewContainerVisible(viewContainer.id) || !layoutService.hasFocus(Parts.SIDEBAR_PART)) { - await viewsService.openViewContainer(viewContainer.id, true); - } else { - editorGroupService.activeGroup.focus(); - } - break; - case ViewContainerLocation.Panel: - if (!viewsService.isViewContainerVisible(viewContainer.id) || !layoutService.hasFocus(Parts.PANEL_PART)) { - await viewsService.openViewContainer(viewContainer.id, true); - } else { - viewsService.closeViewContainer(viewContainer.id); - } - break; - } - } - })); - - if (mnemonicTitle) { - const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer); - this._register(MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - command: { - id, - title: mnemonicTitle, - }, - group: defaultLocation === ViewContainerLocation.Sidebar ? '3_views' : '4_panels', - when: ContextKeyExpr.has(getEnabledViewContainerContextKey(viewContainer.id)), - order: order ?? Number.MAX_VALUE - })); - } - } + this._register(this.registerOpenViewContainerAction(viewContainer)); } private onDidChangeContainerLocation(viewContainer: ViewContainer, from: ViewContainerLocation, to: ViewContainerLocation): void { @@ -218,130 +157,9 @@ export class ViewsService extends Disposable implements IViewsService { const composite = this.getComposite(container.id, location); for (const viewDescriptor of views) { const disposables = new DisposableStore(); - - // Register Action to Open View - if (viewDescriptor.commandActionDescriptor) { - const title = viewDescriptor.commandActionDescriptor.title ?? viewDescriptor.name; - const commandId = viewDescriptor.commandActionDescriptor.id; - const that = this; - disposables.add(registerAction2(class OpenViewAction extends Action2 { - constructor() { - super({ - id: commandId, - get title(): ICommandActionTitle { - const viewContainerLocation = that.viewDescriptorService.getViewLocationById(viewDescriptor.id); - if (viewContainerLocation === ViewContainerLocation.Sidebar) { - return { value: localize('show view', "Show {0}", title), original: `Show ${title}` }; - } else { - return { value: localize('toggle view', "Toggle {0}", title), original: `Toggle ${title}` }; - } - }, - category: CATEGORIES.View.value, - precondition: ContextKeyDefinedExpr.create(`${viewDescriptor.id}.active`), - keybinding: viewDescriptor.commandActionDescriptor!.keybindings ? { ...viewDescriptor.commandActionDescriptor!.keybindings, weight: KeybindingWeight.WorkbenchContrib } : undefined, - f1: true - }); - } - public async run(serviceAccessor: ServicesAccessor): Promise { - const editorGroupService = serviceAccessor.get(IEditorGroupsService); - const viewDescriptorService = serviceAccessor.get(IViewDescriptorService); - const layoutService = serviceAccessor.get(IWorkbenchLayoutService); - const viewsService = serviceAccessor.get(IViewsService); - const contextKeyService = serviceAccessor.get(IContextKeyService); - - const focusedViewId = FocusedViewContext.getValue(contextKeyService); - if (focusedViewId === viewDescriptor.id) { - if (viewDescriptorService.getViewLocationById(viewDescriptor.id) === ViewContainerLocation.Sidebar) { - editorGroupService.activeGroup.focus(); - } else { - layoutService.setPanelHidden(true); - } - } else { - viewsService.openView(viewDescriptor.id, true); - } - } - })); - - if (viewDescriptor.commandActionDescriptor.mnemonicTitle) { - const defaultViewContainer = this.viewDescriptorService.getDefaultContainerById(viewDescriptor.id); - if (defaultViewContainer) { - const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(defaultViewContainer); - disposables.add(MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - command: { - id: commandId, - title: viewDescriptor.commandActionDescriptor.mnemonicTitle, - }, - group: defaultLocation === ViewContainerLocation.Sidebar ? '3_views' : '4_panels', - when: ContextKeyDefinedExpr.create(`${viewDescriptor.id}.active`), - order: viewDescriptor.commandActionDescriptor.order ?? Number.MAX_VALUE - })); - } - } - } - - disposables.add(registerAction2(class FocusViewAction extends Action2 { - constructor() { - super({ - id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, - title: { original: `Focus on ${viewDescriptor.name} View`, value: localize({ key: 'focus view', comment: ['{0} indicates the name of the view to be focused.'] }, "Focus on {0} View", viewDescriptor.name) }, - category: composite ? composite.name : CATEGORIES.View, - menu: [{ - id: MenuId.CommandPalette, - when: viewDescriptor.when, - }], - keybinding: { - when: ContextKeyExpr.has(`${viewDescriptor.id}.active`), - weight: KeybindingWeight.WorkbenchContrib, - primary: viewDescriptor.focusCommand?.keybindings?.primary, - secondary: viewDescriptor.focusCommand?.keybindings?.secondary, - linux: viewDescriptor.focusCommand?.keybindings?.linux, - mac: viewDescriptor.focusCommand?.keybindings?.mac, - win: viewDescriptor.focusCommand?.keybindings?.win - } - }); - } - run(accessor: ServicesAccessor): void { - accessor.get(IViewsService).openView(viewDescriptor.id, true); - } - })); - - disposables.add(registerAction2(class ResetViewLocationAction extends Action2 { - constructor() { - super({ - id: `${viewDescriptor.id}.resetViewLocation`, - title: { - original: 'Reset Location', - value: localize('resetViewLocation', "Reset Location") - }, - menu: [{ - id: MenuId.ViewTitleContext, - when: ContextKeyExpr.or( - ContextKeyExpr.and( - ContextKeyExpr.equals('view', viewDescriptor.id), - ContextKeyExpr.equals(`${viewDescriptor.id}.defaultViewLocation`, false) - ) - ), - group: '1_hide', - order: 2 - }], - }); - } - run(accessor: ServicesAccessor): void { - const viewDescriptorService = accessor.get(IViewDescriptorService); - const defaultContainer = viewDescriptorService.getDefaultContainerById(viewDescriptor.id)!; - const containerModel = viewDescriptorService.getViewContainerModel(defaultContainer)!; - - // The default container is hidden so we should try to reset its location first - if (defaultContainer.hideIfEmpty && containerModel.visibleViewDescriptors.length === 0) { - const defaultLocation = viewDescriptorService.getDefaultViewContainerLocation(defaultContainer)!; - viewDescriptorService.moveViewContainerToLocation(defaultContainer, defaultLocation); - } - - viewDescriptorService.moveViewsToContainer([viewDescriptor], viewDescriptorService.getDefaultContainerById(viewDescriptor.id)!); - accessor.get(IViewsService).openView(viewDescriptor.id, true); - } - })); - + disposables.add(this.registerOpenViewAction(viewDescriptor)); + disposables.add(this.registerFocusViewAction(viewDescriptor, composite?.name ?? CATEGORIES.View)); + disposables.add(this.registerResetViewLocationAction(viewDescriptor)); this.viewDisposable.set(viewDescriptor, disposables); } } @@ -543,6 +361,202 @@ export class ViewsService extends Disposable implements IViewsService { return this.viewDescriptorService.getViewContainerLocation(viewContainer) === ViewContainerLocation.Sidebar ? this.viewletService.getProgressIndicator(viewContainer.id) : this.panelService.getProgressIndicator(viewContainer.id); } + private registerOpenViewContainerAction(viewContainer: ViewContainer): IDisposable { + const disposables = new DisposableStore(); + if (viewContainer.openCommandActionDescriptor) { + let { id, title, mnemonicTitle, keybindings, order } = viewContainer.openCommandActionDescriptor ?? { id: viewContainer.id }; + title = title ?? viewContainer.title; + const that = this; + disposables.add(registerAction2(class OpenViewContainerAction extends Action2 { + constructor() { + super({ + id, + get title(): ICommandActionTitle { + const viewContainerLocation = that.viewDescriptorService.getViewContainerLocation(viewContainer); + if (viewContainerLocation === ViewContainerLocation.Sidebar) { + return { value: localize('show view', "Show {0}", title), original: `Show ${title}` }; + } else { + return { value: localize('toggle view', "Toggle {0}", title), original: `Toggle ${title}` }; + } + }, + category: CATEGORIES.View.value, + precondition: ContextKeyExpr.has(getEnabledViewContainerContextKey(viewContainer.id)), + keybinding: keybindings ? { ...keybindings, weight: KeybindingWeight.WorkbenchContrib } : undefined, + f1: true + }); + } + public async run(serviceAccessor: ServicesAccessor): Promise { + const editorGroupService = serviceAccessor.get(IEditorGroupsService); + const viewDescriptorService = serviceAccessor.get(IViewDescriptorService); + const layoutService = serviceAccessor.get(IWorkbenchLayoutService); + const viewsService = serviceAccessor.get(IViewsService); + const viewContainerLocation = viewDescriptorService.getViewContainerLocation(viewContainer); + switch (viewContainerLocation) { + case ViewContainerLocation.Sidebar: + if (!viewsService.isViewContainerVisible(viewContainer.id) || !layoutService.hasFocus(Parts.SIDEBAR_PART)) { + await viewsService.openViewContainer(viewContainer.id, true); + } else { + editorGroupService.activeGroup.focus(); + } + break; + case ViewContainerLocation.Panel: + if (!viewsService.isViewContainerVisible(viewContainer.id) || !layoutService.hasFocus(Parts.PANEL_PART)) { + await viewsService.openViewContainer(viewContainer.id, true); + } else { + viewsService.closeViewContainer(viewContainer.id); + } + break; + } + } + })); + + if (mnemonicTitle) { + const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer); + disposables.add(MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + command: { + id, + title: mnemonicTitle, + }, + group: defaultLocation === ViewContainerLocation.Sidebar ? '3_views' : '4_panels', + when: ContextKeyExpr.has(getEnabledViewContainerContextKey(viewContainer.id)), + order: order ?? Number.MAX_VALUE + })); + } + } + + return disposables; + } + + private registerOpenViewAction(viewDescriptor: IViewDescriptor): IDisposable { + const disposables = new DisposableStore(); + if (viewDescriptor.openCommandActionDescriptor) { + const title = viewDescriptor.openCommandActionDescriptor.title ?? viewDescriptor.name; + const commandId = viewDescriptor.openCommandActionDescriptor.id; + const that = this; + disposables.add(registerAction2(class OpenViewAction extends Action2 { + constructor() { + super({ + id: commandId, + get title(): ICommandActionTitle { + const viewContainerLocation = that.viewDescriptorService.getViewLocationById(viewDescriptor.id); + if (viewContainerLocation === ViewContainerLocation.Sidebar) { + return { value: localize('show view', "Show {0}", title), original: `Show ${title}` }; + } else { + return { value: localize('toggle view', "Toggle {0}", title), original: `Toggle ${title}` }; + } + }, + category: CATEGORIES.View.value, + precondition: ContextKeyDefinedExpr.create(`${viewDescriptor.id}.active`), + keybinding: viewDescriptor.openCommandActionDescriptor!.keybindings ? { ...viewDescriptor.openCommandActionDescriptor!.keybindings, weight: KeybindingWeight.WorkbenchContrib } : undefined, + f1: true + }); + } + public async run(serviceAccessor: ServicesAccessor): Promise { + const editorGroupService = serviceAccessor.get(IEditorGroupsService); + const viewDescriptorService = serviceAccessor.get(IViewDescriptorService); + const layoutService = serviceAccessor.get(IWorkbenchLayoutService); + const viewsService = serviceAccessor.get(IViewsService); + const contextKeyService = serviceAccessor.get(IContextKeyService); + + const focusedViewId = FocusedViewContext.getValue(contextKeyService); + if (focusedViewId === viewDescriptor.id) { + if (viewDescriptorService.getViewLocationById(viewDescriptor.id) === ViewContainerLocation.Sidebar) { + editorGroupService.activeGroup.focus(); + } else { + layoutService.setPanelHidden(true); + } + } else { + viewsService.openView(viewDescriptor.id, true); + } + } + })); + + if (viewDescriptor.openCommandActionDescriptor.mnemonicTitle) { + const defaultViewContainer = this.viewDescriptorService.getDefaultContainerById(viewDescriptor.id); + if (defaultViewContainer) { + const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(defaultViewContainer); + disposables.add(MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + command: { + id: commandId, + title: viewDescriptor.openCommandActionDescriptor.mnemonicTitle, + }, + group: defaultLocation === ViewContainerLocation.Sidebar ? '3_views' : '4_panels', + when: ContextKeyDefinedExpr.create(`${viewDescriptor.id}.active`), + order: viewDescriptor.openCommandActionDescriptor.order ?? Number.MAX_VALUE + })); + } + } + } + return disposables; + } + + private registerFocusViewAction(viewDescriptor: IViewDescriptor, category?: string | ILocalizedString): IDisposable { + return registerAction2(class FocusViewAction extends Action2 { + constructor() { + super({ + id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, + title: { original: `Focus on ${viewDescriptor.name} View`, value: localize({ key: 'focus view', comment: ['{0} indicates the name of the view to be focused.'] }, "Focus on {0} View", viewDescriptor.name) }, + category, + menu: [{ + id: MenuId.CommandPalette, + when: viewDescriptor.when, + }], + keybinding: { + when: ContextKeyExpr.has(`${viewDescriptor.id}.active`), + weight: KeybindingWeight.WorkbenchContrib, + primary: viewDescriptor.focusCommand?.keybindings?.primary, + secondary: viewDescriptor.focusCommand?.keybindings?.secondary, + linux: viewDescriptor.focusCommand?.keybindings?.linux, + mac: viewDescriptor.focusCommand?.keybindings?.mac, + win: viewDescriptor.focusCommand?.keybindings?.win + } + }); + } + run(accessor: ServicesAccessor): void { + accessor.get(IViewsService).openView(viewDescriptor.id, true); + } + }); + } + + private registerResetViewLocationAction(viewDescriptor: IViewDescriptor): IDisposable { + return registerAction2(class ResetViewLocationAction extends Action2 { + constructor() { + super({ + id: `${viewDescriptor.id}.resetViewLocation`, + title: { + original: 'Reset Location', + value: localize('resetViewLocation', "Reset Location") + }, + menu: [{ + id: MenuId.ViewTitleContext, + when: ContextKeyExpr.or( + ContextKeyExpr.and( + ContextKeyExpr.equals('view', viewDescriptor.id), + ContextKeyExpr.equals(`${viewDescriptor.id}.defaultViewLocation`, false) + ) + ), + group: '1_hide', + order: 2 + }], + }); + } + run(accessor: ServicesAccessor): void { + const viewDescriptorService = accessor.get(IViewDescriptorService); + const defaultContainer = viewDescriptorService.getDefaultContainerById(viewDescriptor.id)!; + const containerModel = viewDescriptorService.getViewContainerModel(defaultContainer)!; + + // The default container is hidden so we should try to reset its location first + if (defaultContainer.hideIfEmpty && containerModel.visibleViewDescriptors.length === 0) { + const defaultLocation = viewDescriptorService.getDefaultViewContainerLocation(defaultContainer)!; + viewDescriptorService.moveViewContainerToLocation(defaultContainer, defaultLocation); + } + + viewDescriptorService.moveViewsToContainer([viewDescriptor], viewDescriptorService.getDefaultContainerById(viewDescriptor.id)!); + accessor.get(IViewsService).openView(viewDescriptor.id, true); + } + }); + } + private registerViewletOrPanel(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void { switch (viewContainerLocation) { case ViewContainerLocation.Panel: diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 1a492c81fde..90a0191738a 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -48,7 +48,7 @@ export function ViewContainerLocationToString(viewContainerLocation: ViewContain } } -type CommandActionDescriptor = { +type OpenCommandActionDescriptor = { readonly id: string; readonly title?: string; readonly mnemonicTitle?: string; @@ -89,9 +89,12 @@ export interface IViewContainerDescriptor { readonly ctorDescriptor: SyncDescriptor; /** - * Command action descriptor to be registered + * Descriptor for open view container command + * If not provided, view container info (id, title) is used. + * + * Note: To prevent registering open command, use `donotRegisterOpenCommand` flag while registering the view container */ - readonly commandActionDescriptor?: CommandActionDescriptor | false; + readonly openCommandActionDescriptor?: OpenCommandActionDescriptor; /** * Storage id to use to store the view container state. @@ -143,7 +146,7 @@ export interface IViewContainersRegistry { * * @returns the registered ViewContainer. */ - registerViewContainer(viewContainerDescriptor: IViewContainerDescriptor, location: ViewContainerLocation, isDefault?: boolean): ViewContainer; + registerViewContainer(viewContainerDescriptor: IViewContainerDescriptor, location: ViewContainerLocation, options?: { isDefault?: boolean, donotRegisterOpenCommand?: boolean }): ViewContainer; /** * Deregisters the given view container @@ -180,6 +183,11 @@ interface ViewOrderDelegate { export interface ViewContainer extends IViewContainerDescriptor { } +interface RelaxedViewContainer extends ViewContainer { + + openCommandActionDescriptor?: OpenCommandActionDescriptor; +} + class ViewContainersRegistryImpl extends Disposable implements IViewContainersRegistry { private readonly _onDidRegister = this._register(new Emitter<{ viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation }>()); @@ -195,16 +203,17 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe return flatten([...this.viewContainers.values()]); } - registerViewContainer(viewContainerDescriptor: IViewContainerDescriptor, viewContainerLocation: ViewContainerLocation, isDefault?: boolean): ViewContainer { + registerViewContainer(viewContainerDescriptor: IViewContainerDescriptor, viewContainerLocation: ViewContainerLocation, options?: { isDefault?: boolean, donotRegisterOpenCommand?: boolean }): ViewContainer { const existing = this.get(viewContainerDescriptor.id); if (existing) { return existing; } - const viewContainer: ViewContainer = viewContainerDescriptor; + const viewContainer: RelaxedViewContainer = viewContainerDescriptor; + viewContainer.openCommandActionDescriptor = options?.donotRegisterOpenCommand ? undefined : (viewContainer.openCommandActionDescriptor ?? { id: viewContainer.id }); const viewContainers = getOrSet(this.viewContainers, viewContainerLocation, []); viewContainers.push(viewContainer); - if (isDefault) { + if (options?.isDefault) { this.defaultViewContainers.push(viewContainer); } this._onDidRegister.fire({ viewContainer, viewContainerLocation }); @@ -283,7 +292,7 @@ export interface IViewDescriptor { readonly remoteAuthority?: string | string[]; - readonly commandActionDescriptor?: CommandActionDescriptor + readonly openCommandActionDescriptor?: OpenCommandActionDescriptor } export interface IViewDescriptorRef { diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index f6dd6daccbf..d8c5fd56fb3 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -334,8 +334,7 @@ function registerDebugPanel(): void { ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: DEBUG_PANEL_ID, hideIfEmpty: true, - commandActionDescriptor: false, - }, ViewContainerLocation.Panel); + }, ViewContainerLocation.Panel, { donotRegisterOpenCommand: true }); Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ id: REPL_VIEW_ID, @@ -345,7 +344,7 @@ function registerDebugPanel(): void { canMoveView: true, when: CONTEXT_DEBUGGERS_AVAILABLE, ctorDescriptor: new SyncDescriptor(Repl), - commandActionDescriptor: { + openCommandActionDescriptor: { id: 'workbench.debug.action.toggleRepl', mnemonicTitle: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console"), keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Y }, @@ -359,7 +358,7 @@ function registerDebugView(): void { const viewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, title: nls.localize('run and debug', "Run and Debug"), - commandActionDescriptor: { + openCommandActionDescriptor: { id: VIEWLET_ID, mnemonicTitle: nls.localize({ key: 'miViewRun', comment: ['&& denotes a mnemonic'] }, "&&Run"), keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D }, diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 165caad9b9d..bd2454e646d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -102,7 +102,7 @@ Registry.as(ViewContainerExtensions.ViewContainersRegis { id: VIEWLET_ID, title: localize('extensions', "Extensions"), - commandActionDescriptor: { + openCommandActionDescriptor: { id: VIEWLET_ID, mnemonicTitle: localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions"), keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_X }, diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index d6dcc9292e9..e56bc902579 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -149,7 +149,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor ctorDescriptor: new SyncDescriptor(ExplorerView), order: 1, canToggleVisibility: false, - commandActionDescriptor: { + openCommandActionDescriptor: { id: VIEW_CONTAINER.id, title: localize('explore', "Explorer"), mnemonicTitle: localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer"), @@ -283,8 +283,7 @@ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewC icon: explorerViewIcon, alwaysUseContainerInfo: true, order: 0, - commandActionDescriptor: false, -}, ViewContainerLocation.Sidebar, true); +}, ViewContainerLocation.Sidebar, { donotRegisterOpenCommand: true, isDefault: true }); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index bd9534dffb1..e0d049f9a56 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -115,8 +115,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewC order: 0, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: Constants.MARKERS_VIEW_STORAGE_ID, - commandActionDescriptor: false, -}, ViewContainerLocation.Panel); +}, ViewContainerLocation.Panel, { donotRegisterOpenCommand: true }); Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: Constants.MARKERS_VIEW_ID, @@ -125,7 +124,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews canToggleVisibility: false, canMoveView: true, ctorDescriptor: new SyncDescriptor(MarkersView), - commandActionDescriptor: { + openCommandActionDescriptor: { id: 'workbench.actions.view.problems', mnemonicTitle: localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems"), keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M }, diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 5a42221b2ec..0795ae19c99 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -60,8 +60,7 @@ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewC ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [OUTPUT_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), storageId: OUTPUT_VIEW_ID, hideIfEmpty: true, - commandActionDescriptor: false, -}, ViewContainerLocation.Panel); +}, ViewContainerLocation.Panel, { donotRegisterOpenCommand: true }); Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: OUTPUT_VIEW_ID, @@ -70,7 +69,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews canMoveView: true, canToggleVisibility: false, ctorDescriptor: new SyncDescriptor(OutputViewPane), - commandActionDescriptor: { + openCommandActionDescriptor: { id: 'workbench.action.output.toggleOutput', mnemonicTitle: nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output"), keybindings: { diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 25f3bd37cd8..a6025446821 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -50,8 +50,7 @@ const viewContainer = Registry.as(ViewContainerExtensio alwaysUseContainerInfo: true, order: 2, hideIfEmpty: true, - commandActionDescriptor: false, -}, ViewContainerLocation.Sidebar); +}, ViewContainerLocation.Sidebar, { donotRegisterOpenCommand: true }); const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -70,7 +69,7 @@ viewsRegistry.registerViews([{ weight: 80, order: -999, containerIcon: sourceControlViewIcon, - commandActionDescriptor: { + openCommandActionDescriptor: { id: viewContainer.id, mnemonicTitle: localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM"), keybindings: { diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 41164bb0c0d..ec8c2afce05 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -623,8 +623,7 @@ const viewContainer = Registry.as(ViewExtensions.ViewCo hideIfEmpty: true, icon: searchViewIcon, order: 1, - commandActionDescriptor: false, -}, ViewContainerLocation.Sidebar); +}, ViewContainerLocation.Sidebar, { donotRegisterOpenCommand: true }); const viewDescriptor: IViewDescriptor = { id: VIEW_ID, @@ -633,7 +632,7 @@ const viewDescriptor: IViewDescriptor = { ctorDescriptor: new SyncDescriptor(SearchView), canToggleVisibility: false, canMoveView: true, - commandActionDescriptor: { + openCommandActionDescriptor: { id: viewContainer.id, mnemonicTitle: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search"), keybindings: { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 955296c8224..97e41c87da7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -69,8 +69,7 @@ const VIEW_CONTAINER = Registry.as(ViewContainerExtensi storageId: TERMINAL_VIEW_ID, hideIfEmpty: true, order: 3, - commandActionDescriptor: false, -}, ViewContainerLocation.Panel); +}, ViewContainerLocation.Panel, { donotRegisterOpenCommand: true }); Registry.as(panel.Extensions.Panels).setDefaultPanelId(TERMINAL_VIEW_ID); Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ id: TERMINAL_VIEW_ID, @@ -79,7 +78,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews canToggleVisibility: false, canMoveView: true, ctorDescriptor: new SyncDescriptor(TerminalViewPane), - commandActionDescriptor: { + openCommandActionDescriptor: { id: TERMINAL_COMMAND_ID.TOGGLE, mnemonicTitle: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal"), keybindings: { diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index 9a3fcf1554d..51a406117d8 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -359,7 +359,7 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode iconChanged = true; } - const keybindingId = this.viewContainer.commandActionDescriptor === false ? this.activeViewDescriptors.find(v => v.commandActionDescriptor)?.commandActionDescriptor?.id : (this.viewContainer.commandActionDescriptor?.id ?? this.viewContainer.id); + const keybindingId = this.viewContainer.openCommandActionDescriptor?.id ?? this.activeViewDescriptors.find(v => v.openCommandActionDescriptor)?.openCommandActionDescriptor?.id; let keybindingIdChanged: boolean = false; if (this._keybindingId !== keybindingId) { this._keybindingId = keybindingId; From e99ec0c6e5483aa593745933a190903cbea060b6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Feb 2021 09:00:52 +0100 Subject: [PATCH 16/34] do not register open command for generated view containers --- .../workbench/services/views/browser/viewDescriptorService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index a742ef1a517..e00e3f50978 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -463,11 +463,11 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor const container = this.viewContainersRegistry.registerViewContainer({ id, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [id, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), - title: 'Custom Views', // we don't want to see this, so no need to localize + title: id, // we don't want to see this so using id icon: location === ViewContainerLocation.Sidebar ? defaultViewIcon : undefined, storageId: getViewContainerStorageId(id), hideIfEmpty: true - }, location); + }, location, { donotRegisterOpenCommand: true }); const cachedInfo = this.cachedViewContainerInfo.get(container.id); if (cachedInfo !== location) { From aa840d53d2804e0b6c12348714514c40bf716a37 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 3 Feb 2021 09:02:08 +0100 Subject: [PATCH 17/34] Move action microsoft/vscode-github-triage-actions --- .github/actions/build-chat/.gitignore | 2 - .github/actions/build-chat/action.yml | 10 - .github/actions/build-chat/package.json | 23 - .github/actions/build-chat/src/main.ts | 217 ------- .github/actions/build-chat/tsconfig.json | 18 - .github/actions/build-chat/yarn.lock | 728 ----------------------- .github/workflows/build-chat.yml | 39 +- 7 files changed, 19 insertions(+), 1018 deletions(-) delete mode 100644 .github/actions/build-chat/.gitignore delete mode 100644 .github/actions/build-chat/action.yml delete mode 100644 .github/actions/build-chat/package.json delete mode 100644 .github/actions/build-chat/src/main.ts delete mode 100644 .github/actions/build-chat/tsconfig.json delete mode 100644 .github/actions/build-chat/yarn.lock diff --git a/.github/actions/build-chat/.gitignore b/.github/actions/build-chat/.gitignore deleted file mode 100644 index db4c6d9b679..00000000000 --- a/.github/actions/build-chat/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules \ No newline at end of file diff --git a/.github/actions/build-chat/action.yml b/.github/actions/build-chat/action.yml deleted file mode 100644 index 278475c9c01..00000000000 --- a/.github/actions/build-chat/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: 'Build Chat' -description: 'Notify in chat about build results.' -author: 'Christof Marti' -inputs: - workflow_run_url: - description: 'Workflow run URL of the completed build.' - required: true -runs: - using: 'node12' - main: 'dist/main.js' diff --git a/.github/actions/build-chat/package.json b/.github/actions/build-chat/package.json deleted file mode 100644 index 156717a823e..00000000000 --- a/.github/actions/build-chat/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "build-chat", - "version": "0.0.0", - "author": "Microsoft Corporation", - "license": "MIT", - "description": "A GitHub action to create a Windows Package Manager manifest file.", - "main": "dist/main.js", - "scripts": { - "build": "tsc" - }, - "dependencies": { - "@actions/core": "^1.2.6", - "@octokit/rest": "^18.0.12", - "@slack/web-api": "^6.0.0", - "azure-storage": "^2.10.3", - "stream-buffers": "^3.0.2" - }, - "devDependencies": { - "@types/node": "^14.14.22", - "@types/stream-buffers": "^3.0.3", - "typescript": "^4.1.3" - } -} diff --git a/.github/actions/build-chat/src/main.ts b/.github/actions/build-chat/src/main.ts deleted file mode 100644 index 71e035850f7..00000000000 --- a/.github/actions/build-chat/src/main.ts +++ /dev/null @@ -1,217 +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 core from '@actions/core'; -import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; -import { WebClient } from '@slack/web-api'; -import * as storage from 'azure-storage'; -import { WritableStreamBuffer } from 'stream-buffers'; - -(async () => { - const actionUrl = core.getInput('workflow_run_url'); - const url = actionUrl || 'https://api.github.com/repos/microsoft/vscode/actions/runs/503514090'; - console.log(url); - const parts = url.split('/'); - const owner = parts[parts.length - 5]; - const repo = parts[parts.length - 4]; - const runId = parseInt(parts[parts.length - 1], 10); - if (actionUrl) { - await handleNotification(owner, repo, runId); - } else { - const results = await buildComplete(owner, repo, runId); - for (const message of [...results.logMessages, ...results.messages]) { - console.log(message); - } - } -})() - .then(null, console.error); - -const testChannels = ['bot-log', 'bot-test-log']; - -async function handleNotification(owner: string, repo: string, runId: number) { - - const results = await buildComplete(owner, repo, runId); - if (results.logMessages.length || results.messages.length) { - - const web = new WebClient(process.env.SLACK_TOKEN); - const memberships = await listAllMemberships(web); - const memberTestChannels = memberships.filter(m => testChannels.indexOf(m.name) !== -1); - - for (const message of results.logMessages) { - for (const testChannel of memberTestChannels) { - await web.chat.postMessage({ - text: message, - link_names: true, - channel: testChannel.id, - }); - } - } - for (const message of results.messages) { - for (const channel of memberships) { - await web.chat.postMessage({ - text: message, - link_names: true, - channel: channel.id, - }); - } - } - } -} - -async function buildComplete(owner: string, repo: string, runId: number) { - console.log(`buildComplete: https://github.com/${owner}/${repo}/actions/runs/${runId}`); - const auth = `token ${process.env.GITHUB_TOKEN}`; - const octokit = new Octokit({ auth }); - const buildResult = (await octokit.actions.getWorkflowRun({ - owner, - repo, - run_id: runId, - })).data; - if (buildResult.head_branch !== 'master' && !buildResult.head_branch?.startsWith('release/')) { - console.error('Private branch. Terminating.') - return { logMessages: [], messages: [] }; - } - - // const buildQuery = `${buildsApiUrl}?$top=10&maxTime=${buildResult.finishTime}&definitions=${buildResult.definition.id}&branchName=${buildResult.sourceBranch}&resultFilter=${results.join(',')}&api-version=5.0-preview.4`; - - const buildResults = (await octokit.actions.listWorkflowRuns({ - owner, - repo, - workflow_id: buildResult.workflow_id, - branch: buildResult.head_branch || undefined, - per_page: 5, // More returns 502s. - })).data.workflow_runs - .filter(run => run.status === 'completed'); - - const currentBuildIndex = buildResults.findIndex(build => build.id === buildResult.id); - if (currentBuildIndex === -1) { - console.error('Build not on first page. Terminating.') - console.error(buildResults.map(({ id, status, conclusion }) => ({ id, status, conclusion }))); - return { logMessages: [], messages: [] }; - } - const slicedResults = buildResults.slice(currentBuildIndex, currentBuildIndex + 2); - const builds = slicedResults - .map((build, i, array) => ({ - data: build, - previousSourceVersion: i < array.length - 1 ? array[i + 1].head_sha : undefined, - authors: [], - buildHtmlUrl: build.html_url, - changesHtmlUrl: '', - })); - const logMessages = builds.slice(0, 1) - .map(build => `Id: ${build.data.id} | Branch: ${build.data.head_branch} | Conclusion: ${build.data.conclusion} | Created: ${build.data.created_at} | Updated: ${build.data.updated_at}`); - const transitionedBuilds = builds.filter((build, i, array) => i < array.length - 1 && transitioned(build, array[i + 1])); - await Promise.all(transitionedBuilds - .map(async build => { - if (build.previousSourceVersion) { - const cmp = await compareCommits(octokit, owner, repo, build.previousSourceVersion, build.data.head_sha); - const commits = cmp.data.commits; - const authors = new Set([ - ...commits.map((c: any) => c.author.login), - ...commits.map((c: any) => c.committer.login), - ]); - authors.delete('web-flow'); // GitHub Web UI committer - build.authors = [...authors]; - build.changesHtmlUrl = `https://github.com/${owner}/${repo}/compare/${build.previousSourceVersion.substr(0, 7)}...${build.data.head_sha.substr(0, 7)}`; // Shorter than: cmp.data.html_url - } - })); - const vscode = repo === 'vscode'; - const name = vscode ? `VS Code ${buildResult.name} Build` : buildResult.name; - // TBD: `Requester: ${vstsToSlackUser(build.requester, build.degraded)}${pingBenForSmokeTests && releaseBuild && build.result === 'partiallySucceeded' ? ' | Ping: @bpasero' : ''}` - const accounts = await readAccounts(); - const githubAccountMap = githubToAccounts(accounts); - const messages = transitionedBuilds.map(build => `${name} -Result: ${build.data.conclusion} | Branch: ${build.data.head_branch} | Authors: ${githubToSlackUsers(githubAccountMap, build.authors, build.degraded).sort().join(', ') || `None (rebuild)`} -Build: ${build.buildHtmlUrl} -Changes: ${build.changesHtmlUrl}`); - return { logMessages, messages }; -} - -const conclusions = ['success', 'failure'] - -function transitioned(newer: Build, older: Build) { - const newerResult = newer.data.conclusion || 'success'; - const olderResult = older.data.conclusion || 'success'; - if (newerResult === olderResult) { - return false; - } - if (conclusions.indexOf(newerResult) > conclusions.indexOf(olderResult)) { - newer.degraded = true; - } - return true; -} - -async function compareCommits(octokit: Octokit, owner: string, repo: string, base: string, head: string) { - return octokit.repos.compareCommits({ owner, repo, base, head }); -} - -function githubToSlackUsers(githubToAccounts: Record, githubUsers: string[], at?: boolean) { - return githubUsers.map(g => githubToAccounts[g] ? `${at ? '@' : ''}${githubToAccounts[g].slack}` : g); -} - -interface Accounts { - github: string; - slack: string; - vsts: string; -} - -function githubToAccounts(accounts: Accounts[]) { - return accounts.reduce((m, e) => { - m[e.github] = e; - return m; - }, >{}); -} - -async function readAccounts() { - const connectionString = process.env.BUILD_CHAT_STORAGE_CONNECTION_STRING; - if (!connectionString) { - console.error('Connection string missing.'); - return []; - } - const buf = await readFile(connectionString, 'config', '/', 'accounts.json'); - return JSON.parse(buf.toString()) as Accounts[]; -} - -async function readFile(connectionString: string, share: string, directory: string, filename: string) { - return new Promise((resolve, reject) => { - const stream = new WritableStreamBuffer() - const fileService = storage.createFileService(connectionString); - fileService.getFileToStream(share, directory, filename, stream, err => { - if (err) { - reject(err); - } else { - const contents = stream.getContents(); - if (contents) { - resolve(contents); - } else { - reject(new Error('No content')); - } - } - }); - }); -} - -interface AllChannels { - channels: { - id: string; - name: string; - is_member: boolean; - }[]; -} - -async function listAllMemberships(web: WebClient) { - const groups = await web.conversations.list({ types: 'public_channel,private_channel' }) as unknown as AllChannels; - return groups.channels - .filter(c => c.is_member); -} - -interface Build { - data: RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data']; - previousSourceVersion: string | undefined; - authors: string[]; - buildHtmlUrl: string; - changesHtmlUrl: string; - degraded?: boolean; -} diff --git a/.github/actions/build-chat/tsconfig.json b/.github/actions/build-chat/tsconfig.json deleted file mode 100644 index f4889aab7bd..00000000000 --- a/.github/actions/build-chat/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es2017", - "strict": true, - "noUnusedLocals": true, - "resolveJsonModule": true, - "lib": [ - "es2017" - ], - "sourceMap": true, - "outDir": "./dist", - "rootDir": "./src", - }, - "exclude": [ - "node_modules" - ] -} \ No newline at end of file diff --git a/.github/actions/build-chat/yarn.lock b/.github/actions/build-chat/yarn.lock deleted file mode 100644 index bc2185232d2..00000000000 --- a/.github/actions/build-chat/yarn.lock +++ /dev/null @@ -1,728 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@actions/core@^1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.2.6.tgz#a78d49f41a4def18e88ce47c2cac615d5694bf09" - integrity sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA== - -"@octokit/auth-token@^2.4.4": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56" - integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q== - dependencies: - "@octokit/types" "^6.0.0" - -"@octokit/core@^3.2.3": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106" - integrity sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.4.12" - "@octokit/types" "^6.0.3" - before-after-hook "^2.1.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.10" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.10.tgz#741ce1fa2f4fb77ce8ebe0c6eaf5ce63f565f8e8" - integrity sha512-9+Xef8nT7OKZglfkOMm7IL6VwxXUQyR7DUSU0LH/F7VNqs8vyd7es5pTfz9E7DwUIx7R3pGscxu1EBhYljyu7Q== - dependencies: - "@octokit/types" "^6.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.5.8" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b" - integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA== - dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^6.0.0" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-3.0.0.tgz#f73d48af2d21bf4f97fbf38fae43b54699e0dbba" - integrity sha512-jOp1CVRw+OBJaZtG9QzZggvJXvyzgDXuW948SWsDiwmyDuCjeYCiF3TDD/qvhpF580RfP7iBIos4AVU6yhgMlA== - -"@octokit/plugin-paginate-rest@^2.6.2": - version "2.8.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.8.0.tgz#2b41e12b494e895bf5fb5b12565d2c80a0ecc6ae" - integrity sha512-HtuEQ2AYE4YFEBQN0iHmMsIvVucd5RsnwJmRKIsfAg1/ZeoMaU+jXMnTAZqIUEmcVJA27LjHUm3f1hxf8Fpdxw== - dependencies: - "@octokit/types" "^6.4.0" - -"@octokit/plugin-request-log@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" - integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== - -"@octokit/plugin-rest-endpoint-methods@4.4.1": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63" - integrity sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA== - dependencies: - "@octokit/types" "^6.1.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.4.tgz#07dd5c0521d2ee975201274c472a127917741262" - integrity sha512-LjkSiTbsxIErBiRh5wSZvpZqT4t0/c9+4dOe0PII+6jXR+oj/h66s7E4a/MghV7iT8W9ffoQ5Skoxzs96+gBPA== - dependencies: - "@octokit/types" "^6.0.0" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.12" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc" - integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - once "^1.4.0" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.0.12": - version "18.0.12" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.0.12.tgz#278bd41358c56d87c201e787e8adc0cac132503a" - integrity sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw== - dependencies: - "@octokit/core" "^3.2.3" - "@octokit/plugin-paginate-rest" "^2.6.2" - "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "4.4.1" - -"@octokit/types@^6.0.0", "@octokit/types@^6.0.3", "@octokit/types@^6.1.0", "@octokit/types@^6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.4.0.tgz#f3f47be70bcdb3c26f2c2619f3dd0ced466a265c" - integrity sha512-1FEmuVppZE2zG0rBdQlviRz5cp0udyI63zyhBVPrm0FRNAsQkAXU7IYWQg1XvhChFut8YbFYN1usQpk54D6/4w== - dependencies: - "@octokit/openapi-types" "^3.0.0" - "@types/node" ">= 8" - -"@slack/logger@>=1.0.0 <3.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-2.0.0.tgz#6a4e1c755849bc0f66dac08a8be54ce790ec0e6b" - integrity sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw== - dependencies: - "@types/node" ">=8.9.0" - -"@slack/types@^1.7.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@slack/types/-/types-1.10.0.tgz#cbf7d83e1027f4cbfd13d6b429f120c7fb09127a" - integrity sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg== - -"@slack/web-api@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.0.0.tgz#14c65ed73c66a187e5f20e12c3898dfd8d5cbf7c" - integrity sha512-YD1wqWuzrYPf4RQyD7OnYS5lImUmNWn+G5V6Qt0N97fPYxqhT72YJtRdSnsTc3VkH5R5imKOhYxb+wqI9hiHnA== - dependencies: - "@slack/logger" ">=1.0.0 <3.0.0" - "@slack/types" "^1.7.0" - "@types/is-stream" "^1.1.0" - "@types/node" ">=12.0.0" - axios "^0.21.1" - eventemitter3 "^3.1.0" - form-data "^2.5.0" - is-stream "^1.1.0" - p-queue "^6.6.1" - p-retry "^4.0.0" - -"@types/is-stream@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1" - integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg== - dependencies: - "@types/node" "*" - -"@types/node@*", "@types/node@>= 8", "@types/node@>=12.0.0", "@types/node@>=8.9.0", "@types/node@^14.14.22": - version "14.14.22" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" - integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== - -"@types/retry@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - -"@types/stream-buffers@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.3.tgz#34e565bf64e3e4bdeee23fd4aa58d4636014a02b" - integrity sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ== - dependencies: - "@types/node" "*" - -ajv@^6.12.3: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -axios@^0.21.1: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== - dependencies: - follow-redirects "^1.10.0" - -azure-storage@^2.10.3: - version "2.10.3" - resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.3.tgz#c5966bf929d87587d78f6847040ea9a4b1d4a50a" - integrity sha512-IGLs5Xj6kO8Ii90KerQrrwuJKexLgSwYC4oLWmc11mzKe7Jt2E5IVg+ZQ8K53YWZACtVTMBNO3iGuA+4ipjJxQ== - 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" - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -before-after-hook@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" - integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== - -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= - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -combined-stream@^1.0.6, 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== - dependencies: - delayed-stream "~1.0.0" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== - -eventemitter3@^4.0.4: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -extend@^3.0.2, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -follow-redirects@^1.10.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" - integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - 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" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -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== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -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-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -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" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.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" - -mime-db@1.45.0: - version "1.45.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" - integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.28" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" - integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== - dependencies: - mime-db "1.45.0" - -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-queue@^6.6.1: - version "6.6.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" - integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== - dependencies: - eventemitter3 "^4.0.4" - p-timeout "^3.2.0" - -p-retry@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.2.0.tgz#ea9066c6b44f23cab4cd42f6147cdbbc6604da5d" - integrity sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA== - dependencies: - "@types/retry" "^0.12.0" - retry "^0.12.0" - -p-timeout@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -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= - -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -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" - -request@^2.86.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - 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.3" - 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.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - 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= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stream-buffers@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" - integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -typescript@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== - -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= - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.0.0, 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== - -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== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -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" - -xmlbuilder@^9.0.7: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= diff --git a/.github/workflows/build-chat.yml b/.github/workflows/build-chat.yml index e8a6797b810..f9f146164ba 100644 --- a/.github/workflows/build-chat.yml +++ b/.github/workflows/build-chat.yml @@ -14,23 +14,22 @@ jobs: main: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup Node.js 12.x - uses: actions/setup-node@v1.4.4 - with: - node-version: "12.x" - - - name: Build - run: yarn install && yarn run build - working-directory: .github/actions/build-chat - - - name: Build Chat - uses: ./.github/actions/build-chat - with: - workflow_run_url: ${{ github.event.workflow_run.url }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - BUILD_CHAT_STORAGE_CONNECTION_STRING: ${{ secrets.BUILD_CHAT_STORAGE_CONNECTION_STRING }} + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: "microsoft/vscode-github-triage-actions" + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Install Additional Dependencies + # Pulls in a bunch of other packages that arent needed for the rest of the actions + run: npm install @azure/storage-blob@12.1.1 + - name: Build Chat + uses: ./actions/build-chat + with: + token: ${{ secrets.GITHUB_TOKEN }} + slack_token: ${{ secrets.SLACK_TOKEN }} + storage_connection_string: ${{ secrets.BUILD_CHAT_STORAGE_CONNECTION_STRING }} + workflow_run_url: ${{ github.event.workflow_run.url }} + notification_channel: build + log_channel: bot-log From 2cf80f97cc1265650936f3f5614e29fdfbbc73f8 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 3 Feb 2021 09:13:48 +0100 Subject: [PATCH 18/34] Azure DevOps - Add global variable for VSCODE_QUALITY (#115636) * Add variable for VSCODE_QUALITY --- build/azure-pipelines/product-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 4286f2e8819..4d376246944 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -84,6 +84,8 @@ parameters: variables: - name: ENABLE_TERRAPIN value: ${{ eq(parameters.ENABLE_TERRAPIN, true) }} + - name: VSCODE_QUALITY + value: ${{ parameters.VSCODE_QUALITY }} - name: VSCODE_BUILD_STAGE_WINDOWS value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_LINUX From 60ce9ab9c5a26c413ec417a3fb1bf1a4e587d4b1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Feb 2021 09:25:18 +0100 Subject: [PATCH 19/34] #115590 More tweaks --- src/vs/workbench/api/browser/mainThreadCLICommands.ts | 5 ++--- .../common/extensionManagementService.ts | 9 +-------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCLICommands.ts b/src/vs/workbench/api/browser/mainThreadCLICommands.ts index 156399cdf38..02076eb4492 100644 --- a/src/vs/workbench/api/browser/mainThreadCLICommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCLICommands.ts @@ -20,7 +20,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { canExecuteOnWorkspace, getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IExtensionManifest } from 'vs/workbench/workbench.web.api'; @@ -100,8 +100,7 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean { if (!canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) { - const extensionKinds = getExtensionKind(manifest, this.productService, this.configurationService); - output.log(localize('cannot be installed', "Cannot install '{0}' extension because it is declared as {1} extension kind and does not run in this setup.", getExtensionId(manifest.publisher, manifest.name), extensionKinds.map(e => `"${e}"`).join(', '))); + output.log(localize('cannot be installed', "Cannot install the '{0}' extension because it is declared to not run in this setup.", getExtensionId(manifest.publisher, manifest.name))); return false; } return true; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index c4a198c99a5..a8ee0aa6135 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -272,14 +272,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return Promises.settled(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local); } - const extensionKinds = getExtensionKind(manifest, this.productService, this.configurationService); - if (this.extensionManagementServerService.remoteExtensionManagementServer) { - const error = new Error(localize('cannot be installed', "Cannot install '{0}' extension because it is declared as {1} extension kind and does not run in this setup.", gallery.displayName || gallery.name, extensionKinds.map(e => `"${e}"`).join(', '))); - error.name = INSTALL_ERROR_NOT_SUPPORTED; - return Promise.reject(error); - } - - const error = new Error(localize('cannot be installed', "Cannot install '{0}' extension because it is declared as {1} extension kind and does not run in this setup.", gallery.displayName || gallery.name, extensionKinds.map(e => `"${e}"`).join(', '))); + const error = new Error(localize('cannot be installed', "Cannot install the '{0}' extension because it is declared to not run in this setup.", gallery.displayName || gallery.name)); error.name = INSTALL_ERROR_NOT_SUPPORTED; return Promise.reject(error); } From c399d039a71998e8cef312a8584851a03c8f2f64 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 3 Feb 2021 09:30:30 +0100 Subject: [PATCH 20/34] debt - don't expose MainThreadNotebookShape from NotebookEditorDecorationType --- .../src/singlefolder-tests/rpc.test.ts | 6 ++++++ .../workbench/api/common/extHostNotebook.ts | 21 ++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 0d203040d3a..34948413be6 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -91,4 +91,10 @@ suite('vscode', function () { dispo.push(item); assertNoRpcFromEntry([item, 'TreeView']); }); + + test('no rpc, createNotebookEditorDecorationType(...)', function () { + const item = vscode.notebook.createNotebookEditorDecorationType({ top: {} }); + dispo.push(item); + assertNoRpcFromEntry([item, 'NotebookEditorDecorationType']); + }); }); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index a77e61aac27..49f35f11f9d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -191,21 +191,22 @@ async function withToken(cb: (token: CancellationToken) => any) { } } -export class NotebookEditorDecorationType implements vscode.NotebookEditorDecorationType { +export class NotebookEditorDecorationType { private static readonly _Keys = new IdGenerator('NotebookEditorDecorationType'); - private _proxy: MainThreadNotebookShape; - public key: string; + readonly value: vscode.NotebookEditorDecorationType; constructor(proxy: MainThreadNotebookShape, options: vscode.NotebookDecorationRenderOptions) { - this.key = NotebookEditorDecorationType._Keys.nextId(); - this._proxy = proxy; - this._proxy.$registerNotebookEditorDecorationType(this.key, typeConverters.NotebookDecorationRenderOptions.from(options)); - } + const key = NotebookEditorDecorationType._Keys.nextId(); + proxy.$registerNotebookEditorDecorationType(key, typeConverters.NotebookDecorationRenderOptions.from(options)); - public dispose(): void { - this._proxy.$removeNotebookEditorDecorationType(this.key); + this.value = { + key, + dispose() { + proxy.$removeNotebookEditorDecorationType(key); + } + }; } } @@ -367,7 +368,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType { - return new NotebookEditorDecorationType(this._proxy, options); + return new NotebookEditorDecorationType(this._proxy, options).value; } async openNotebookDocument(uriComponents: UriComponents, viewType?: string): Promise { From a01d16e77e9b7c7f200d6010b78b519e05913d73 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 3 Feb 2021 09:44:52 +0100 Subject: [PATCH 21/34] debt - use a native private property in ExtHostQuickInput --- .../vscode-api-tests/src/singlefolder-tests/rpc.test.ts | 2 -- src/vs/workbench/api/common/extHostQuickOpen.ts | 8 +++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 34948413be6..4cf5f1e0767 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -38,14 +38,12 @@ suite('vscode', function () { }); test('no rpc, createQuickPick(...)', function () { - this.skip(); const item = vscode.window.createQuickPick(); dispo.push(item); assertNoRpcFromEntry([item, 'QuickPick']); }); test('no rpc, createInputBox(...)', function () { - this.skip(); const item = vscode.window.createInputBox(); dispo.push(item); assertNoRpcFromEntry([item, 'InputBox']); diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 2f2fd4cdbdb..26ccfb2fa37 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -233,6 +233,7 @@ class ExtHostQuickInput implements QuickInput { private static _nextId = 1; _id = ExtHostQuickPick._nextId++; + #proxy: MainThreadQuickOpenShape; private _title: string | undefined; private _steps: number | undefined; private _totalSteps: number | undefined; @@ -260,7 +261,8 @@ class ExtHostQuickInput implements QuickInput { this._onDidChangeValueEmitter ]; - constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: ExtensionIdentifier, private _onDidDispose: () => void) { + constructor(proxy: MainThreadQuickOpenShape, protected _extensionId: ExtensionIdentifier, private _onDidDispose: () => void) { + this.#proxy = proxy; } get title() { @@ -409,7 +411,7 @@ class ExtHostQuickInput implements QuickInput { this._updateTimeout = undefined; } this._onDidDispose(); - this._proxy.$dispose(this._id); + this.#proxy.$dispose(this._id); } protected update(properties: Record): void { @@ -437,7 +439,7 @@ class ExtHostQuickInput implements QuickInput { } private dispatchUpdate() { - this._proxy.$createOrUpdate(this._pendingUpdate); + this.#proxy.$createOrUpdate(this._pendingUpdate); this._pendingUpdate = { id: this._id }; } } From 6ca7afa84fbe888b8eb682331fdb1f0036d46079 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 3 Feb 2021 11:27:17 +0100 Subject: [PATCH 22/34] add support for `application/x.notebook.error-traceback` mime type --- .../view/output/transforms/errorTransform.ts | 4 +- .../view/output/transforms/richTransform.ts | 10 +++++ .../contrib/notebook/common/notebookCommon.ts | 45 +++++++++---------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts index 8a0e7216d60..6a52ef64a97 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts @@ -10,9 +10,9 @@ import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/termi import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ICommonNotebookEditor, IErrorOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -class ErrorTransform implements IOutputTransformContribution { +export class ErrorTransform implements IOutputTransformContribution { constructor( - public editor: ICommonNotebookEditor, + _editor: ICommonNotebookEditor, @IThemeService private readonly themeService: IThemeService ) { } 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 420f309b407..88deceb6225 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 @@ -20,6 +20,7 @@ import { dirname } from 'vs/base/common/resources'; import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ErrorTransform } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform'; class RichRenderer implements IOutputTransformContribution { private _richMimeTypeRenderers = new Map IRenderOutput>(); @@ -42,6 +43,7 @@ class RichRenderer implements IOutputTransformContribution { this._richMimeTypeRenderers.set('image/jpeg', this.renderJPEG.bind(this)); this._richMimeTypeRenderers.set('text/plain', this.renderPlainText.bind(this)); this._richMimeTypeRenderers.set('text/x-javascript', this.renderCode.bind(this)); + this._richMimeTypeRenderers.set('application/x.notebook.error-traceback', this._renderErrorTraceback.bind(this)); } render(output: IDisplayOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput { @@ -215,6 +217,14 @@ class RichRenderer implements IOutputTransformContribution { return { type: RenderOutputType.None, hasDynamicHeight: false }; } + private _renderErrorTraceback(outputViewModel: IDisplayOutputViewModel, _notebookUri: URI, container: HTMLElement): IRenderOutput { + const output = outputViewModel.model.data['application/x.notebook.error-traceback'] as any; + const transform = new ErrorTransform(this.notebookEditor, this.themeService); + transform.render(output, container); + transform.dispose(); + return { type: RenderOutputType.None, hasDynamicHeight: false }; + } + dispose(): void { } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index cf462b90514..d596029b2f5 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -524,35 +524,30 @@ export namespace CellUri { } } -export function mimeTypeIsAlwaysSecure(mimeType: string) { - if ([ - 'application/json', - 'text/markdown', - 'image/png', - 'text/plain' - ].indexOf(mimeType) > -1) { - return true; - } +type MimeTypeInfo = { + alwaysSecure?: boolean; + supportedByCore?: boolean; +}; - return false; +const _mimeTypeInfo = new Map([ + ['application/json', { alwaysSecure: true, supportedByCore: true }], + ['text/markdown', { alwaysSecure: true, supportedByCore: true }], + ['image/png', { alwaysSecure: true, supportedByCore: true }], + ['text/plain', { alwaysSecure: true, supportedByCore: true }], + ['application/javascript', { supportedByCore: true }], + ['text/html', { supportedByCore: true }], + ['image/svg+xml', { supportedByCore: true }], + ['image/jpeg', { supportedByCore: true }], + ['text/x-javascript', { supportedByCore: true }], + ['application/x.notebook.error-traceback', { alwaysSecure: true, supportedByCore: true }], +]); + +export function mimeTypeIsAlwaysSecure(mimeType: string): boolean { + return _mimeTypeInfo.get(mimeType)?.alwaysSecure ?? false; } export function mimeTypeSupportedByCore(mimeType: string) { - if ([ - 'application/json', - 'application/javascript', - 'text/html', - 'image/svg+xml', - 'text/markdown', - 'image/png', - 'image/jpeg', - 'text/plain', - 'text/x-javascript' - ].indexOf(mimeType) > -1) { - return true; - } - - return false; + return _mimeTypeInfo.get(mimeType)?.supportedByCore ?? false; } // if (isWindows) { From 73c5b484f340039de733343f6e6de811cb9fd6d8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 3 Feb 2021 11:33:22 +0100 Subject: [PATCH 23/34] fixes #100151 --- src/vs/base/browser/event.ts | 14 +++++----- src/vs/base/browser/ui/list/listWidget.ts | 31 ++++++++--------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/vs/base/browser/event.ts b/src/vs/base/browser/event.ts index 52cc2ae8281..02a8488d51e 100644 --- a/src/vs/base/browser/event.ts +++ b/src/vs/base/browser/event.ts @@ -31,10 +31,12 @@ export interface CancellableEvent { stopPropagation(): void; } +export function stopEvent(event: T): T { + event.preventDefault(); + event.stopPropagation(); + return event; +} + export function stop(event: BaseEvent): BaseEvent { - return BaseEvent.map(event, e => { - e.preventDefault(); - e.stopPropagation(); - return e; - }); -} \ No newline at end of file + return BaseEvent.map(event, stopEvent); +} diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e530b43d36f..4e10f6542f5 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -13,7 +13,7 @@ import { Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; -import { domEvent } from 'vs/base/browser/event'; +import { domEvent, stopEvent } from 'vs/base/browser/event'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list'; import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView'; import { Color } from 'vs/base/common/color'; @@ -1148,35 +1148,26 @@ export class List implements ISpliceable, IThemable, IDisposable { get onTouchStart(): Event> { return this.view.onTouchStart; } get onTap(): Event> { return this.view.onTap; } - private didJustPressContextMenuKey: boolean = false; @memoize get onContextMenu(): Event> { - const fromKeydown = Event.chain(domEvent(this.view.domNode, 'keydown')) - .map(e => new StandardKeyboardEvent(e)) - .filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) - .filter(e => { e.preventDefault(); e.stopPropagation(); return false; }) - .event as Event; - const fromKeyup = Event.chain(domEvent(this.view.domNode, 'keyup')) - .filter(() => { - const didJustPressContextMenuKey = this.didJustPressContextMenuKey; - this.didJustPressContextMenuKey = false; - return didJustPressContextMenuKey; - }) - .filter(() => this.getFocus().length > 0 && !!this.view.domElement(this.getFocus()[0])) - .map(browserEvent => { - const index = this.getFocus()[0]; - const element = this.view.element(index); - const anchor = this.view.domElement(index) as HTMLElement; + .map(e => new StandardKeyboardEvent(e)) + .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) + .map(stopEvent) + .map(({ browserEvent }) => { + const focus = this.getFocus(); + const index = focus.length ? focus[0] : undefined; + const element = typeof index !== 'undefined' ? this.view.element(index) : undefined; + const anchor = typeof index !== 'undefined' ? this.view.domElement(index) as HTMLElement : this.view.domNode; return { index, element, anchor, browserEvent }; }) .event; const fromMouse = Event.chain(this.view.onContextMenu) - .filter(() => !this.didJustPressContextMenuKey) + .filter(e => !(e.browserEvent.button === 0 && e.browserEvent.buttons === 0 && !e.browserEvent.ctrlKey && !e.browserEvent.altKey && !e.browserEvent.metaKey)) .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent })) .event; - return Event.any>(fromKeydown, fromKeyup, fromMouse); + return Event.any>(fromKeyup, fromMouse); } get onKeyDown(): Event { return domEvent(this.view.domNode, 'keydown'); } From 03fd1a32c0f16c70527db66a5c1e16c9784be101 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 3 Feb 2021 11:51:24 +0100 Subject: [PATCH 24/34] :lipstick: --- src/vs/base/browser/ui/list/listWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 4e10f6542f5..9fb258d478f 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1149,7 +1149,7 @@ export class List implements ISpliceable, IThemable, IDisposable { get onTap(): Event> { return this.view.onTap; } @memoize get onContextMenu(): Event> { - const fromKeyup = Event.chain(domEvent(this.view.domNode, 'keyup')) + const fromKeyboard = Event.chain(domEvent(this.view.domNode, 'keyup')) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .map(stopEvent) @@ -1167,7 +1167,7 @@ export class List implements ISpliceable, IThemable, IDisposable { .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent })) .event; - return Event.any>(fromKeyup, fromMouse); + return Event.any>(fromKeyboard, fromMouse); } get onKeyDown(): Event { return domEvent(this.view.domNode, 'keydown'); } From aaf7b923c6c95f4e9363d7657967051b31b52849 Mon Sep 17 00:00:00 2001 From: vagusX Date: Wed, 3 Feb 2021 18:55:32 +0800 Subject: [PATCH 25/34] fix: prevent scroll when list focused in zone-widget (#114912) --- src/vs/base/browser/ui/list/listWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9fb258d478f..dc20926d36b 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1371,7 +1371,7 @@ export class List implements ISpliceable, IThemable, IDisposable { } domFocus(): void { - this.view.domNode.focus(); + this.view.domNode.focus({ preventScroll: true }); } layout(height?: number, width?: number): void { From 7e05f9f0821e85913f6e77c1d75c60ebd75c84f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 3 Feb 2021 11:56:05 +0100 Subject: [PATCH 26/34] add appendCellOutput API proposal and implementation --- src/vs/vscode.proposed.d.ts | 3 +- src/vs/workbench/api/common/extHostTypes.ts | 34 +++++++++----- .../common/model/notebookTextModel.ts | 6 ++- .../contrib/notebook/common/notebookCommon.ts | 7 +-- .../notebook/test/notebookTextModel.test.ts | 44 +++++++++++++++++-- 5 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ee5d812bc69..f48e12c11c3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1501,8 +1501,9 @@ declare module 'vscode' { export interface WorkspaceEdit { replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void; replaceNotebookCells(uri: Uri, start: number, end: number, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; - replaceNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void; replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void; + replaceNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void; + appendNotebookCellOutput(uri: Uri, index: number, outputs: NotebookCellOutput[], metadata?: WorkspaceEditEntryMetadata): void; } export interface NotebookEditorEdit { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5d1bb34cb8c..9c08ed54186 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { addIdToOutput, CellEditType, ICellEditOperation, IDisplayOutput, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, IDisplayOutput, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import type * as vscode from 'vscode'; function es5ClassCompat(target: Function): any { @@ -648,17 +648,27 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } replaceNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ - _type: FileEditType.Cell, metadata, uri, edit: { - editType: CellEditType.Output, index, outputs: outputs.map(output => { - if (NotebookCellOutput.isNotebookCellOutput(output)) { - return addIdToOutput(output.toJSON()); - } else { - return addIdToOutput(output); - } - }) - } - }); + this._editNotebookCellOutput(uri, index, false, outputs, metadata); + } + + appendNotebookCellOutput(uri: URI, index: number, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata?: vscode.WorkspaceEditEntryMetadata): void { + this._editNotebookCellOutput(uri, index, true, outputs, metadata); + } + + private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: (vscode.NotebookCellOutput | vscode.CellOutput)[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void { + const edit: ICellOutputEdit = { + editType: CellEditType.Output, + index, + append, + outputs: outputs.map(output => { + if (NotebookCellOutput.isNotebookCellOutput(output)) { + return addIdToOutput(output.toJSON()); + } else { + return addIdToOutput(output); + } + }) + }; + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit }); } replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 7ad9daf8e00..15832370af0 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -339,7 +339,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel //TODO@jrieken,@rebornix no event, no undo stop (?) this._assertIndex(edit.index); const cell = this._cells[edit.index]; - this._spliceNotebookCellOutputs2(cell.handle, edit.outputs, computeUndoRedo); + if (edit.append) { + this._spliceNotebookCellOutputs(cell.handle, [[cell.outputs.length, 0, edit.outputs]], computeUndoRedo); + } else { + this._spliceNotebookCellOutputs2(cell.handle, edit.outputs, computeUndoRedo); + } break; case CellEditType.OutputsSplice: { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index d596029b2f5..b6ea52aaddf 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -280,9 +280,9 @@ export type NotebookCellTextModelSplice = [ ]; export type NotebookCellOutputsSplice = [ - number /* start */, - number /* delete count */, - IProcessedOutput[] + start: number /* start */, + deleteCount: number /* delete count */, + newOutputs: IProcessedOutput[] ]; export interface IMainCellDto { @@ -411,6 +411,7 @@ export interface ICellOutputEdit { editType: CellEditType.Output; index: number; outputs: IProcessedOutput[]; + append?: boolean } export interface ICellMetadataEdit { diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 93572f226ee..c1c13513e9b 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -9,6 +9,7 @@ import { withTestNotebook, TestCell, setupInstantiationService } from 'vs/workbe import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { assertType } from 'vs/base/common/types'; suite('NotebookTextModel', () => { const instantiationService = setupInstantiationService(); @@ -202,9 +203,46 @@ suite('NotebookTextModel', () => { }] }], true, undefined, () => undefined, undefined); - assert.equal(textModel.cells.length, 1); - assert.equal(textModel.cells[0].outputs.length, 1); - assert.equal(textModel.cells[0].outputs[0].outputKind, CellOutputKind.Rich); + assert.strictEqual(textModel.cells.length, 1); + assert.strictEqual(textModel.cells[0].outputs.length, 1); + assert.strictEqual(textModel.cells[0].outputs[0].outputKind, CellOutputKind.Rich); + + // append + textModel.applyEdits(textModel.versionId, [{ + index: 0, + editType: CellEditType.Output, + append: true, + outputs: [{ + outputKind: CellOutputKind.Rich, + outputId: 'someId2', + data: { 'text/markdown': '_Hello2_' } + }] + }], true, undefined, () => undefined, undefined); + + assert.strictEqual(textModel.cells.length, 1); + assert.strictEqual(textModel.cells[0].outputs.length, 2); + let [first, second] = textModel.cells[0].outputs; + assertType(first.outputKind === CellOutputKind.Rich); + assertType(second.outputKind === CellOutputKind.Rich); + assert.strictEqual(first.outputId, 'someId'); + assert.strictEqual(second.outputId, 'someId2'); + + // replace all + textModel.applyEdits(textModel.versionId, [{ + index: 0, + editType: CellEditType.Output, + outputs: [{ + outputKind: CellOutputKind.Rich, + outputId: 'someId3', + data: { 'text/plain': 'Last, replaced output' } + }] + }], true, undefined, () => undefined, undefined); + + assert.strictEqual(textModel.cells.length, 1); + assert.strictEqual(textModel.cells[0].outputs.length, 1); + [first] = textModel.cells[0].outputs; + assertType(first.outputKind === CellOutputKind.Rich); + assert.strictEqual(first.outputId, 'someId3'); } ); }); From 8d7157ed4517148463b1c98c665a836940be32ae Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 3 Feb 2021 12:16:39 +0100 Subject: [PATCH 27/34] Make task definitions dynamic Fixes #114793 --- .../contrib/tasks/browser/task.contribution.ts | 8 +++++++- .../workbench/contrib/tasks/common/jsonSchema_v2.ts | 13 ++++++++++++- .../contrib/tasks/common/taskDefinitionRegistry.ts | 7 +++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index d938abe46af..772c2e84482 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -28,7 +28,7 @@ import { RunAutomaticTasks, ManageAutomaticTaskRunning } from 'vs/workbench/cont import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import schemaVersion1 from '../common/jsonSchema_v1'; -import schemaVersion2, { updateProblemMatchers } from '../common/jsonSchema_v2'; +import schemaVersion2, { updateProblemMatchers, updateTaskDefinitions } from '../common/jsonSchema_v2'; import { AbstractTaskService, ConfigureTaskAction } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; import { tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; @@ -36,6 +36,7 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; const SHOW_TASKS_COMMANDS_CONTEXT = ContextKeyExpr.or(ShellExecutionSupportedContext, ProcessExecutionSupportedContext); @@ -415,6 +416,11 @@ ProblemMatcherRegistry.onMatcherChanged(() => { jsonRegistry.notifySchemaChanged(tasksSchemaId); }); +TaskDefinitionRegistry.onDefinitionsChanged(() => { + updateTaskDefinitions(); + jsonRegistry.notifySchemaChanged(tasksSchemaId); +}); + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ id: 'task', diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts index 440ab712fbf..fda80c732c9 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts @@ -383,7 +383,18 @@ let taskConfiguration: IJSONSchema = { let taskDefinitions: IJSONSchema[] = []; TaskDefinitionRegistry.onReady().then(() => { + updateTaskDefinitions(); +}); + +export function updateTaskDefinitions() { for (let taskType of TaskDefinitionRegistry.all()) { + // Check that we haven't already added this task type + if (taskDefinitions.find(schema => { + return schema.properties?.type?.enum?.find ? schema.properties?.type.enum.find(element => element === taskType.taskType) : undefined; + })) { + continue; + } + let schema: IJSONSchema = Objects.deepClone(taskConfiguration); const schemaProperties = schema.properties!; // Since we do this after the schema is assigned we need to patch the refs. @@ -408,7 +419,7 @@ TaskDefinitionRegistry.onReady().then(() => { fixReferences(schema); taskDefinitions.push(schema); } -}); +} let customize = Objects.deepClone(taskConfiguration); customize.properties!.customize = { diff --git a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts index 573a3c7a2d0..a3b89c01dc8 100644 --- a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts @@ -14,6 +14,7 @@ import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/serv import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { Emitter, Event } from 'vs/base/common/event'; const taskDefinitionSchema: IJSONSchema = { @@ -95,6 +96,7 @@ export interface ITaskDefinitionRegistry { get(key: string): Tasks.TaskDefinition; all(): Tasks.TaskDefinition[]; getJsonSchema(): IJSONSchema; + onDefinitionsChanged: Event; } class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { @@ -102,6 +104,8 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { private taskTypes: IStringDictionary; private readyPromise: Promise; private _schema: IJSONSchema | undefined; + private _onDefinitionsChanged: Emitter = new Emitter(); + public onDefinitionsChanged: Event = this._onDefinitionsChanged.event; constructor() { this.taskTypes = Object.create(null); @@ -125,6 +129,9 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { } } } + if ((delta.removed.length > 0) || (delta.added.length > 0)) { + this._onDefinitionsChanged.fire(); + } } catch (error) { } resolve(undefined); From 67ccf8c649e776f61c3f07c0f142784e7aeae531 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 3 Feb 2021 12:20:52 +0100 Subject: [PATCH 28/34] Add open preveiw action to port notification and setting (#114572) --- .../common/externalUriOpenerService.ts | 49 ++++++++++---- .../contrib/remote/browser/remoteExplorer.ts | 40 +++++++++--- .../contrib/remote/browser/remoteIcons.ts | 1 + .../contrib/remote/browser/tunnelView.ts | 65 ++++++++++++++++++- .../remote/common/remote.contribution.ts | 3 +- .../remote/common/remoteExplorerService.ts | 1 + 6 files changed, 135 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts b/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts index 069ad5e484a..ad371f4fb0b 100644 --- a/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts +++ b/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts @@ -45,6 +45,12 @@ export interface IExternalUriOpenerService { * Registers a provider for external resources openers. */ registerExternalOpenerProvider(provider: IExternalOpenerProvider): IDisposable; + + /** + * Get the configured IExternalUriOpener for the the uri. + * If there is no opener configured, then returns the first opener that can handle the uri. + */ + getOpener(uri: URI, ctx: { sourceUri: URI, preferredOpenerId?: string }, token: CancellationToken): Promise; } export class ExternalUriOpenerService extends Disposable implements IExternalUriOpenerService, IExternalOpener { @@ -70,26 +76,23 @@ export class ExternalUriOpenerService extends Disposable implements IExternalUri return { dispose: remove }; } - async openExternal(href: string, ctx: { sourceUri: URI, preferredOpenerId?: string }, token: CancellationToken): Promise { - - const targetUri = typeof href === 'string' ? URI.parse(href) : href; - + private async getOpeners(targetUri: URI, allowOptional: boolean, ctx: { sourceUri: URI, preferredOpenerId?: string }, token: CancellationToken): Promise { const allOpeners = await this.getAllOpenersForUri(targetUri); if (allOpeners.size === 0) { - return false; + return []; } // First see if we have a preferredOpener if (ctx.preferredOpenerId) { if (ctx.preferredOpenerId === defaultExternalUriOpenerId) { - return false; + return []; } const preferredOpener = allOpeners.get(ctx.preferredOpenerId); if (preferredOpener) { // Skip the `canOpen` check here since the opener was specifically requested. - return preferredOpener.openExternalUri(targetUri, ctx, token); + return [preferredOpener]; } } @@ -97,7 +100,7 @@ export class ExternalUriOpenerService extends Disposable implements IExternalUri const configuredOpener = this.getConfiguredOpenerForUri(allOpeners, targetUri); if (configuredOpener) { // Skip the `canOpen` check here since the opener was specifically requested. - return configuredOpener === defaultExternalUriOpenerId ? false : configuredOpener.openExternalUri(targetUri, ctx, token); + return configuredOpener === defaultExternalUriOpenerId ? [] : [configuredOpener]; } // Then check to see if there is a valid opener @@ -121,22 +124,44 @@ export class ExternalUriOpenerService extends Disposable implements IExternalUri })); if (validOpeners.length === 0) { - return false; + return []; } // See if we have a preferred opener first const preferred = firstOrDefault(validOpeners.filter(x => x.priority === modes.ExternalUriOpenerPriority.Preferred)); if (preferred) { - return preferred.opener.openExternalUri(targetUri, ctx, token); + return [preferred.opener]; } // See if we only have optional openers, use the default opener - if (validOpeners.every(x => x.priority === modes.ExternalUriOpenerPriority.Option)) { + if (!allowOptional && validOpeners.every(x => x.priority === modes.ExternalUriOpenerPriority.Option)) { + return []; + } + + return validOpeners.map(value => value.opener); + } + + async openExternal(href: string, ctx: { sourceUri: URI, preferredOpenerId?: string }, token: CancellationToken): Promise { + + const targetUri = typeof href === 'string' ? URI.parse(href) : href; + + const allOpeners = await this.getOpeners(targetUri, false, ctx, token); + if (allOpeners.length === 0) { return false; + } else if (allOpeners.length === 1) { + return allOpeners[0].openExternalUri(targetUri, ctx, token); } // Otherwise prompt - return this.showOpenerPrompt(validOpeners.map(x => x.opener), targetUri, ctx, token); + return this.showOpenerPrompt(allOpeners, targetUri, ctx, token); + } + + async getOpener(targetUri: URI, ctx: { sourceUri: URI, preferredOpenerId?: string }, token: CancellationToken): Promise { + const allOpeners = await this.getOpeners(targetUri, true, ctx, token); + if (allOpeners.length >= 1) { + return allOpeners[0]; + } + return undefined; } private async getAllOpenersForUri(targetUri: URI): Promise> { diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index ceed57b9a18..d9e0ea9eb73 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -7,7 +7,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Extensions, IViewContainersRegistry, IViewDescriptorService, IViewsRegistry, IViewsService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PortsAttributes, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { PORT_AUTO_FORWARD_SETTING, forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { PORT_AUTO_FORWARD_SETTING, forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel, OpenPortInPreviewAction } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -29,6 +29,7 @@ import { ITASExperimentService } from 'vs/workbench/services/experiment/common/e import { optional } from 'vs/platform/instantiation/common/instantiation'; import { portsViewIcon } from 'vs/workbench/contrib/remote/browser/remoteIcons'; import { Event } from 'vs/base/common/event'; +import { IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService'; export const VIEWLET_ID = 'workbench.view.remote'; @@ -196,6 +197,7 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon @ITerminalService readonly terminalService: ITerminalService, @INotificationService readonly notificationService: INotificationService, @IOpenerService readonly openerService: IOpenerService, + @IExternalUriOpenerService readonly externalOpenerService: IExternalUriOpenerService, @IViewsService readonly viewsService: IViewsService, @IRemoteExplorerService readonly remoteExplorerService: IRemoteExplorerService, @IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService, @@ -212,11 +214,12 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon remoteAgentService.getEnvironment().then(environment => { if (environment?.os === OperatingSystem.Windows) { - this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, + this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, false)); } else if (environment?.os === OperatingSystem.Linux) { - this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, openerService, tunnelService)); - this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, + this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, + openerService, externalOpenerService, tunnelService)); + this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, true)); } }); @@ -232,6 +235,7 @@ class OnAutoForwardedAction extends Disposable { constructor(private readonly notificationService: INotificationService, private readonly remoteExplorerService: IRemoteExplorerService, private readonly openerService: IOpenerService, + private readonly externalOpenerService: IExternalUriOpenerService, private readonly tunnelService: ITunnelService, private readonly portsAttributes: PortsAttributes) { super(); @@ -248,6 +252,11 @@ class OnAutoForwardedAction extends Disposable { await OpenPortInBrowserAction.run(this.remoteExplorerService.tunnelModel, this.openerService, address); break; } + case OnPortForward.OpenPreview: { + const address = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); + await OpenPortInPreviewAction.run(this.remoteExplorerService.tunnelModel, this.openerService, this.externalOpenerService, address); + break; + } case OnPortForward.Silent: break; default: if (Date.now() - this.lastNotifyTime.getTime() > OnAutoForwardedAction.NOTIFY_COOL_DOWN) { @@ -306,7 +315,7 @@ class OnAutoForwardedAction extends Disposable { this.lastNotification.close(); } let message = this.basicMessage(tunnel); - const choices = [this.openChoice(tunnel)]; + const choices = [this.openBrowserChoice(tunnel), this.openPreviewChoice(tunnel)]; if ((tunnel.tunnelLocalPort !== tunnel.tunnelRemotePort) && this.tunnelService.canElevate && isPortPrivileged(tunnel.tunnelRemotePort)) { // Privileged ports are not on Windows, so it's safe to use "superuser" @@ -325,7 +334,7 @@ class OnAutoForwardedAction extends Disposable { }); } - private openChoice(tunnel: RemoteTunnel): IPromptChoice { + private openBrowserChoice(tunnel: RemoteTunnel): IPromptChoice { const address = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); return { label: OpenPortInBrowserAction.LABEL, @@ -333,6 +342,14 @@ class OnAutoForwardedAction extends Disposable { }; } + private openPreviewChoice(tunnel: RemoteTunnel): IPromptChoice { + const address = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort); + return { + label: OpenPortInPreviewAction.LABEL, + run: () => OpenPortInPreviewAction.run(this.remoteExplorerService.tunnelModel, this.openerService, this.externalOpenerService, address) + }; + } + private elevateChoice(tunnel: RemoteTunnel): IPromptChoice { return { // Privileged ports are not on Windows, so it's ok to stick to just "sudo". @@ -347,7 +364,10 @@ class OnAutoForwardedAction extends Disposable { this.lastNotification.close(); } this.lastShownPort = newTunnel.tunnelRemotePort; - this.lastNotification = this.notificationService.prompt(Severity.Info, this.basicMessage(newTunnel) + this.linkMessage(), [this.openChoice(newTunnel)], { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } }); + this.lastNotification = this.notificationService.prompt(Severity.Info, + this.basicMessage(newTunnel) + this.linkMessage(), + [this.openBrowserChoice(newTunnel), this.openPreviewChoice(tunnel)], + { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } }); this.lastNotification.onDidClose(() => { this.lastNotification = undefined; this.lastShownPort = undefined; @@ -367,6 +387,7 @@ class OutputAutomaticPortForwarding extends Disposable { private readonly terminalService: ITerminalService, readonly notificationService: INotificationService, readonly openerService: IOpenerService, + readonly externalOpenerService: IExternalUriOpenerService, private readonly remoteExplorerService: IRemoteExplorerService, private readonly configurationService: IConfigurationService, private readonly debugService: IDebugService, @@ -376,7 +397,7 @@ class OutputAutomaticPortForwarding extends Disposable { ) { super(); this.portsAttributes = new PortsAttributes(configurationService); - this.notifier = new OnAutoForwardedAction(notificationService, remoteExplorerService, openerService, tunnelService, this.portsAttributes); + this.notifier = new OnAutoForwardedAction(notificationService, remoteExplorerService, openerService, externalOpenerService, tunnelService, this.portsAttributes); this._register(configurationService.onDidChangeConfiguration((e) => { if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) { this.tryStartStopUrlFinder(); @@ -443,11 +464,12 @@ class ProcAutomaticPortForwarding extends Disposable { readonly remoteExplorerService: IRemoteExplorerService, readonly notificationService: INotificationService, readonly openerService: IOpenerService, + readonly externalOpenerService: IExternalUriOpenerService, readonly tunnelService: ITunnelService ) { super(); this.portsAttributes = new PortsAttributes(configurationService); - this.notifier = new OnAutoForwardedAction(notificationService, remoteExplorerService, openerService, tunnelService, this.portsAttributes); + this.notifier = new OnAutoForwardedAction(notificationService, remoteExplorerService, openerService, externalOpenerService, tunnelService, this.portsAttributes); this._register(configurationService.onDidChangeConfiguration(async (e) => { if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) { await this.startStopCandidateListener(); diff --git a/src/vs/workbench/contrib/remote/browser/remoteIcons.ts b/src/vs/workbench/contrib/remote/browser/remoteIcons.ts index 526b7cc0629..e1ebf4235eb 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIcons.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIcons.ts @@ -23,3 +23,4 @@ export const publicPortIcon = registerIcon('public-ports-view-icon', Codicon.eye export const forwardPortIcon = registerIcon('ports-forward-icon', Codicon.plus, nls.localize('forwardPortIcon', 'Icon for the forward action.')); export const stopForwardIcon = registerIcon('ports-stop-forward-icon', Codicon.x, nls.localize('stopForwardIcon', 'Icon for the stop forwarding action.')); export const openBrowserIcon = registerIcon('ports-open-browser-icon', Codicon.globe, nls.localize('openBrowserIcon', 'Icon for the open browser action.')); +export const openPreviewIcon = registerIcon('ports-open-preview-icon', Codicon.openPreview, nls.localize('openPreviewIcon', 'Icon for the open preview action.')); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index d712a510265..05cb916dfbf 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -42,7 +42,9 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { forwardPortIcon, openBrowserIcon, portsViewIcon, privatePortIcon, publicPortIcon, stopForwardIcon } from 'vs/workbench/contrib/remote/browser/remoteIcons'; +import { forwardPortIcon, openBrowserIcon, openPreviewIcon, portsViewIcon, privatePortIcon, publicPortIcon, stopForwardIcon } from 'vs/workbench/contrib/remote/browser/remoteIcons'; +import { IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; @@ -959,7 +961,47 @@ export namespace OpenPortInBrowserAction { if (!address.startsWith('http')) { address = `http://${address}`; } - return openerService.open(URI.parse(address)); + return openerService.open(URI.parse(address), { allowContributedOpeners: false }); + } + return Promise.resolve(); + } +} + +export namespace OpenPortInPreviewAction { + export const ID = 'remote.tunnel.openPreview'; + export const LABEL = nls.localize('remote.tunnel.openPreview', "Preview in Editor"); + + export function handler(): ICommandHandler { + return async (accessor, arg) => { + let key: string | undefined; + if (arg instanceof TunnelItem) { + key = makeAddress(arg.remoteHost, arg.remotePort); + } else if (arg.tunnelRemoteHost && arg.tunnelRemotePort) { + key = makeAddress(arg.tunnelRemoteHost, arg.tunnelRemotePort); + } + if (key) { + const model = accessor.get(IRemoteExplorerService).tunnelModel; + const openerService = accessor.get(IOpenerService); + const externalOpenerService = accessor.get(IExternalUriOpenerService); + return run(model, openerService, externalOpenerService, key); + } + }; + } + + export async function run(model: TunnelModel, openerService: IOpenerService, externalOpenerService: IExternalUriOpenerService, key: string) { + const tunnel = model.forwarded.get(key) || model.detected.get(key); + let address: string | undefined; + if (tunnel && tunnel.localAddress && (address = model.address(tunnel.remoteHost, tunnel.remotePort))) { + if (!address.startsWith('http')) { + address = `http://${address}`; + } + const uri = URI.parse(address); + const sourceUri = URI.parse(`http://${tunnel.remoteHost}:${tunnel.remotePort}`); + const opener = await externalOpenerService.getOpener(uri, { sourceUri }, new CancellationTokenSource().token); + if (opener) { + return opener.openExternalUri(uri, { sourceUri }, new CancellationTokenSource().token); + } + return openerService.open(sourceUri); } return Promise.resolve(); } @@ -1145,6 +1187,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ CommandsRegistry.registerCommand(ClosePortAction.COMMANDPALETTE_ID, ClosePortAction.commandPaletteHandler()); CommandsRegistry.registerCommand(OpenPortInBrowserAction.ID, OpenPortInBrowserAction.handler()); +CommandsRegistry.registerCommand(OpenPortInPreviewAction.ID, OpenPortInPreviewAction.handler()); CommandsRegistry.registerCommand(OpenPortInBrowserCommandPaletteAction.ID, OpenPortInBrowserCommandPaletteAction.handler()); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CopyAddressAction.INLINE_ID, @@ -1226,6 +1269,15 @@ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ group: '0_manage', order: 2, + command: { + id: OpenPortInPreviewAction.ID, + title: OpenPortInPreviewAction.LABEL, + }, + when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) +})); +MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ + group: '0_manage', + order: 3, command: { id: LabelTunnelAction.ID, title: LabelTunnelAction.LABEL, @@ -1287,6 +1339,15 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ }, when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) })); +MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ + order: 1, + command: { + id: OpenPortInPreviewAction.ID, + title: OpenPortInPreviewAction.LABEL, + icon: openPreviewIcon + }, + when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) +})); MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ order: 0, command: { diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index f8ff86aea20..c19433661d3 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -143,10 +143,11 @@ Registry.as(ConfigurationExtensions.Configuration) properties: { 'onAutoForward': { type: 'string', - enum: ['notify', 'openBrowser', 'silent', 'ignore'], + enum: ['notify', 'openBrowser', 'openPreview', 'silent', 'ignore'], enumDescriptions: [ localize('remote.portsAttributes.notify', "Shows a notification when a port is automatically forwarded."), localize('remote.portsAttributes.openBrowser', "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser."), + localize('remote.portsAttributes.openPreview', "Opens a preview in the same window when the port is automatically forwarded."), localize('remote.portsAttributes.silent', "Shows no notification and takes no action when this port is automatically forwarded."), localize('remote.portsAttributes.ignore', "This port will not be automatically forwarded.") ], diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 0242eaf1d07..64d3bcb3f42 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -118,6 +118,7 @@ export function mapHasAddressLocalhostOrAllInterfaces(map: Map, ho export enum OnPortForward { Notify = 'notify', OpenBrowser = 'openBrowser', + OpenPreview = 'openPreview', Silent = 'silent', Ignore = 'ignore' } From 7c90e1f5ba949d201d333306d60a980380760326 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 3 Feb 2021 12:30:37 +0100 Subject: [PATCH 29/34] fixes 115685 --- src/vs/workbench/contrib/files/browser/explorerViewlet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index e56bc902579..8febbfd97c1 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -118,7 +118,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor when: OpenEditorsVisibleContext, canToggleVisibility: true, canMoveView: true, - collapsed: true, + collapsed: false, hideByDefault: true, focusCommand: { id: 'workbench.files.action.focusOpenEditorsView', From 06c0dbe61622979d83afc845efc357ed165f016e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 3 Feb 2021 13:43:03 +0100 Subject: [PATCH 30/34] fix compilation error --- extensions/git/src/repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e85a4f16921..c374e780faf 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1482,7 +1482,7 @@ export class Repository implements Disposable { const maybeRebased = await this.run(Operation.Log, async () => { try { - const result = await this.repository.run(['log', '--oneline', '--cherry', `${currentBranch ?? ''}...${currentBranch ?? ''}@{upstream}`, '--']); + const result = await this.repository.exec(['log', '--oneline', '--cherry', `${currentBranch ?? ''}...${currentBranch ?? ''}@{upstream}`, '--']); if (result.exitCode) { return false; } From 538f72e2a7bbe025363aa4689a2a27d75f5dcac7 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 3 Feb 2021 13:57:18 +0100 Subject: [PATCH 31/34] Fixes #115662: Add support for escaping `@` as `@@` in regular expressions to avoid replacement --- .../common/monarch/monarchCompile.ts | 23 +++-- .../standalone/common/monarch/monarchTypes.ts | 4 + .../standalone/test/monarch/monarch.test.ts | 88 ++++++++++++++----- src/vs/monaco.d.ts | 4 + 4 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts index aa487e7e68a..1357baefea7 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCompile.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -81,12 +81,21 @@ function createKeywordMatcher(arr: string[], caseInsensitive: boolean = false): /** * Compiles a regular expression string, adding the 'i' flag if 'ignoreCase' is set, and the 'u' flag if 'unicode' is set. * Also replaces @\w+ or sequences with the content of the specified attribute + * @\w+ replacement can be avoided by escaping `@` signs with another `@` sign. + * @example /@attr/ will be replaced with the value of lexer[attr] + * @example /@@text/ will not be replaced and will become /@text/. */ function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp { let n = 0; - while (str.indexOf('@') >= 0 && n < 5) { // at most 5 expansions - n++; - str = str.replace(/@(\w+)/g, function (s, attr?) { + let hadExpansion: boolean; + do { + hadExpansion = false; + str = str.replace(/(.|^)@(\w+)/g, function (s, charBeforeAtSign, attr?) { + if (charBeforeAtSign === '@') { + // do not expand @@ + return s; + } + hadExpansion = true; let sub = ''; if (typeof (lexer[attr]) === 'string') { sub = lexer[attr]; @@ -99,9 +108,13 @@ function compileRegExp(lexer: monarchCommon.ILexerMin, str: string): RegExp { throw monarchCommon.createError(lexer, 'attribute reference \'' + attr + '\' must be a string, used at: ' + str); } } - return (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')'); + return charBeforeAtSign + (monarchCommon.empty(sub) ? '' : '(?:' + sub + ')'); }); - } + n++; + } while (hadExpansion && n < 5); + + // handle escaped @@ + str = str.replace(/@@/g, '@'); let flags = (lexer.ignoreCase ? 'i' : '') + (lexer.unicode ? 'u' : ''); return new RegExp(str, flags); diff --git a/src/vs/editor/standalone/common/monarch/monarchTypes.ts b/src/vs/editor/standalone/common/monarch/monarchTypes.ts index 5e3a798c6d0..d424427b23c 100644 --- a/src/vs/editor/standalone/common/monarch/monarchTypes.ts +++ b/src/vs/editor/standalone/common/monarch/monarchTypes.ts @@ -46,6 +46,10 @@ export interface IMonarchLanguage { * Defaults to false */ includeLF?: boolean; + /** + * Other keys that can be referred to by the tokenizer. + */ + [key: string]: any; } /** diff --git a/src/vs/editor/standalone/test/monarch/monarch.test.ts b/src/vs/editor/standalone/test/monarch/monarch.test.ts index 61fcafe555e..fd57288fdca 100644 --- a/src/vs/editor/standalone/test/monarch/monarch.test.ts +++ b/src/vs/editor/standalone/test/monarch/monarch.test.ts @@ -19,6 +19,17 @@ suite('Monarch', () => { return new MonarchTokenizer(modeService, null!, languageId, compile(languageId, language)); } + function getTokens(tokenizer: MonarchTokenizer, lines: string[]): Token[][] { + const actualTokens: Token[][] = []; + let state = tokenizer.getInitialState(); + for (const line of lines) { + const result = tokenizer.tokenize(line, true, state, 0); + actualTokens.push(result.tokens); + state = result.endState; + } + return actualTokens; + } + test('Ensure @rematch and nextEmbedded can be used together in Monarch grammar', () => { const modeService = new ModeServiceImpl(); const innerModeRegistration = ModesRegistry.registerLanguage({ @@ -65,13 +76,7 @@ suite('Monarch', () => { `""")`, ]; - const actualTokens: Token[][] = []; - let state = tokenizer.getInitialState(); - for (const line of lines) { - const result = tokenizer.tokenize(line, true, state, 0); - actualTokens.push(result.tokens); - state = result.endState; - } + const actualTokens = getTokens(tokenizer, lines); assert.deepStrictEqual(actualTokens, [ [ @@ -140,13 +145,7 @@ suite('Monarch', () => { `But the line was empty. This line should not be commented.`, ]; - const actualTokens: Token[][] = []; - let state = tokenizer.getInitialState(); - for (const line of lines) { - const result = tokenizer.tokenize(line, true, state, 0); - actualTokens.push(result.tokens); - state = result.endState; - } + const actualTokens = getTokens(tokenizer, lines); assert.deepStrictEqual(actualTokens, [ [new Token(0, 'comment.test', 'test')], @@ -190,13 +189,7 @@ suite('Monarch', () => { `PRINT 2*3:*FX200, 3` ]; - const actualTokens: Token[][] = []; - let state = tokenizer.getInitialState(); - for (const line of lines) { - const result = tokenizer.tokenize(line, true, state, 0); - actualTokens.push(result.tokens); - state = result.endState; - } + const actualTokens = getTokens(tokenizer, lines); assert.deepStrictEqual(actualTokens, [ [ @@ -218,4 +211,57 @@ suite('Monarch', () => { ]); }); + test('issue #115662: monarchCompile function need an extra option which can control replacement', () => { + const modeService = new ModeServiceImpl(); + + const tokenizer1 = createMonarchTokenizer(modeService, 'test', { + ignoreCase: false, + uselessReplaceKey1: '@uselessReplaceKey2', + uselessReplaceKey2: '@uselessReplaceKey3', + uselessReplaceKey3: '@uselessReplaceKey4', + uselessReplaceKey4: '@uselessReplaceKey5', + uselessReplaceKey5: '@ham' || '', + tokenizer: { + root: [ + { + regex: /@\w+/.test('@ham') + ? new RegExp(`^${'@uselessReplaceKey1'}$`) + : new RegExp(`^${'@ham'}$`), + action: { token: 'ham' } + }, + ], + }, + }); + + const tokenizer2 = createMonarchTokenizer(modeService, 'test', { + ignoreCase: false, + tokenizer: { + root: [ + { + regex: /@@ham/, + action: { token: 'ham' } + }, + ], + }, + }); + + const lines = [ + `@ham` + ]; + + const actualTokens1 = getTokens(tokenizer1, lines); + assert.deepStrictEqual(actualTokens1, [ + [ + new Token(0, 'ham.test', 'test'), + ] + ]); + + const actualTokens2 = getTokens(tokenizer2, lines); + assert.deepStrictEqual(actualTokens2, [ + [ + new Token(0, 'ham.test', 'test'), + ] + ]); + }); + }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 227452ee59b..49442b1ad96 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6505,6 +6505,10 @@ declare namespace monaco.languages { * Defaults to false */ includeLF?: boolean; + /** + * Other keys that can be referred to by the tokenizer. + */ + [key: string]: any; } /** From 376716d1aeef7f64002068308ea45a91fab25a96 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 3 Feb 2021 14:01:09 +0100 Subject: [PATCH 32/34] status bar - don't leak RPC objects from API (#115679) --- .../src/singlefolder-tests/rpc.test.ts | 1 - .../workbench/api/common/extHostStatusBar.ts | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts index 4cf5f1e0767..2b336f09718 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/rpc.test.ts @@ -50,7 +50,6 @@ suite('vscode', function () { }); test('no rpc, createStatusBarItem(...)', function () { - this.skip(); const item = vscode.window.createStatusBarItem(); dispo.push(item); assertNoRpcFromEntry([item, 'StatusBarItem']); diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index da040c1084b..ba9c5b184ae 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -18,6 +18,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { [['statusBarItem.errorBackground', new ThemeColor('statusBarItem.errorForeground')]] ); + #proxy: MainThreadStatusBarShape; + #commands: CommandsConverter; + private _id: number; private _alignment: number; private _priority?: number; @@ -38,14 +41,13 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { }; private _timeoutHandle: any; - private _proxy: MainThreadStatusBarShape; - private _commands: CommandsConverter; private _accessibilityInformation?: vscode.AccessibilityInformation; constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) { + this.#proxy = proxy; + this.#commands = commands; + this._id = ExtHostStatusBarEntry.ID_GEN++; - this._proxy = proxy; - this._commands = commands; this._statusId = id; this._statusName = name; this._alignment = alignment; @@ -122,12 +124,12 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { if (typeof command === 'string') { this._command = { fromApi: command, - internal: this._commands.toInternal({ title: '', command }, this._internalCommandRegistration), + internal: this.#commands.toInternal({ title: '', command }, this._internalCommandRegistration), }; } else if (command) { this._command = { fromApi: command, - internal: this._commands.toInternal(command, this._internalCommandRegistration), + internal: this.#commands.toInternal(command, this._internalCommandRegistration), }; } else { this._command = undefined; @@ -148,7 +150,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { public hide(): void { clearTimeout(this._timeoutHandle); this._visible = false; - this._proxy.$dispose(this.id); + this.#proxy.$dispose(this.id); } private update(): void { @@ -169,7 +171,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { } // Set to status bar - this._proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color, + this.#proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color, this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT, this._priority, this._accessibilityInformation); }, 0); From 4bc335b05abb4bb5d7277bdedf17cee0bbe19257 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 3 Feb 2021 14:04:44 +0100 Subject: [PATCH 33/34] Introduce setting debug.console.collapseIdenticalLines to disable collapsing identical lines in Debug Console fixes #112540 --- .../contrib/debug/browser/debug.contribution.ts | 5 +++++ .../workbench/contrib/debug/browser/debugSession.ts | 2 +- src/vs/workbench/contrib/debug/common/debug.ts | 1 + src/vs/workbench/contrib/debug/common/replModel.ts | 8 ++++++-- .../contrib/debug/test/browser/callStack.test.ts | 3 ++- .../contrib/debug/test/browser/repl.test.ts | 12 +++++++----- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index d8c5fd56fb3..28086409884 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -456,6 +456,11 @@ function registerConfiguration(): void { description: nls.localize('debug.console.historySuggestions', "Controls if the debug console should suggest previously typed input."), default: true }, + 'debug.console.collapseIdenticalLines': { + type: 'boolean', + description: nls.localize('debug.console.collapseIdenticalLines', "Controls if the debug console should collapse identical lines and show a number of occurrences with a badge."), + default: true + }, 'launch': { type: 'object', description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces."), diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 51e31986081..b75495ba50d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -88,7 +88,7 @@ export class DebugSession implements IDebugSession { ) { this._options = options || {}; if (this.hasSeparateRepl()) { - this.repl = new ReplModel(); + this.repl = new ReplModel(this.configurationService); } else { this.repl = (this.parentSession as DebugSession).repl; } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index a0bac814b04..9f9f7b5f508 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -504,6 +504,7 @@ export interface IDebugConfiguration { lineHeight: number; wordWrap: boolean; closeOnEnd: boolean; + collapseIdenticalLines: boolean; historySuggestions: boolean; }; focusWindowOnBreak: boolean; diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index be45bc3742d..306984356bc 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -5,13 +5,14 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; -import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug'; import { ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugModel'; import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { Emitter, Event } from 'vs/base/common/event'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const MAX_REPL_LENGTH = 10000; let topReplElementCounter = 0; @@ -201,6 +202,8 @@ export class ReplModel { private readonly _onDidChangeElements = new Emitter(); readonly onDidChangeElements = this._onDidChangeElements.event; + constructor(private readonly configurationService: IConfigurationService) { } + getReplElements(): IReplElement[] { return this.replElements; } @@ -224,7 +227,8 @@ export class ReplModel { if (typeof data === 'string') { const previousElement = this.replElements.length ? this.replElements[this.replElements.length - 1] : undefined; if (previousElement instanceof SimpleReplElement && previousElement.severity === sev) { - if (previousElement.value === data) { + const config = this.configurationService.getValue('debug'); + if (previousElement.value === data && config.console.collapseIdenticalLines) { previousElement.count++; // No need to fire an event, just the count updates and badge will adjust automatically return; diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index dd694374635..9a1efdb443d 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -19,6 +19,7 @@ import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug import { generateUuid } from 'vs/base/common/uuid'; import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; const mockWorkspaceContextService = { getWorkspace: () => { @@ -37,7 +38,7 @@ export function createMockSession(model: DebugModel, name = 'mockSession', optio } }; } - } as IDebugService, undefined!, undefined!, undefined!, undefined!, mockWorkspaceContextService, undefined!, undefined!, NullOpenerService, undefined!, undefined!, mockUriIdentityService); + } as IDebugService, undefined!, undefined!, new TestConfigurationService({ debug: { console: { collapseIdenticalLines: true } } }), undefined!, mockWorkspaceContextService, undefined!, undefined!, NullOpenerService, undefined!, undefined!, mockUriIdentityService); } function createTwoStackFrames(session: DebugSession): { firstStackFrame: StackFrame, secondStackFrame: StackFrame } { diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index 22d3876bfa3..aa0424413c7 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -14,10 +14,12 @@ import { timeout } from 'vs/base/common/async'; import { createMockSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; import { TreeVisibility } from 'vs/base/browser/ui/tree/tree'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; suite('Debug - REPL', () => { let model: DebugModel; let rawSession: MockRawSession; + const configurationService = new TestConfigurationService({ debug: { console: { collapseIdenticalLines: true } } }); setup(() => { model = createMockDebugModel(); @@ -26,7 +28,7 @@ suite('Debug - REPL', () => { test('repl output', () => { const session = createMockSession(model); - const repl = new ReplModel(); + const repl = new ReplModel(configurationService); repl.appendToRepl(session, 'first line\n', severity.Error); repl.appendToRepl(session, 'second line ', severity.Error); repl.appendToRepl(session, 'third line ', severity.Error); @@ -84,7 +86,7 @@ suite('Debug - REPL', () => { test('repl output count', () => { const session = createMockSession(model); - const repl = new ReplModel(); + const repl = new ReplModel(configurationService); repl.appendToRepl(session, 'first line\n', severity.Info); repl.appendToRepl(session, 'first line\n', severity.Info); repl.appendToRepl(session, 'first line\n', severity.Info); @@ -155,7 +157,7 @@ suite('Debug - REPL', () => { session['raw'] = rawSession; const thread = new Thread(session, 'mockthread', 1); const stackFrame = new StackFrame(thread, 1, undefined, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); - const replModel = new ReplModel(); + const replModel = new ReplModel(configurationService); replModel.addReplExpression(session, stackFrame, 'myVariable').then(); replModel.addReplExpression(session, stackFrame, 'myVariable').then(); replModel.addReplExpression(session, stackFrame, 'myVariable').then(); @@ -193,7 +195,7 @@ suite('Debug - REPL', () => { test('repl groups', async () => { const session = createMockSession(model); - const repl = new ReplModel(); + const repl = new ReplModel(configurationService); repl.appendToRepl(session, 'first global line', severity.Info); repl.startGroup('group_1', true); @@ -231,7 +233,7 @@ suite('Debug - REPL', () => { test('repl filter', async () => { const session = createMockSession(model); - const repl = new ReplModel(); + const repl = new ReplModel(configurationService); const replFilter = new ReplFilter(); const getFilteredElements = () => { From a00411c7e8b3b017870121d758af1adcdde5d44f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 3 Feb 2021 14:17:46 +0100 Subject: [PATCH 34/34] Debug Console, Copy All (to clipboard) do not include source file name fixes #108149 --- src/vs/workbench/contrib/debug/browser/repl.ts | 2 +- src/vs/workbench/contrib/debug/browser/replFilter.ts | 2 +- src/vs/workbench/contrib/debug/common/debug.ts | 2 +- src/vs/workbench/contrib/debug/common/replModel.ts | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 1afbbaf1232..8e0a4c358c8 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -578,7 +578,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { accessibilityProvider: new ReplAccessibilityProvider(), identityProvider, mouseSupport: false, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IReplElement) => e }, + keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IReplElement) => e.toString(true) }, horizontalScrolling: !wordWrap, setRowLineHeight: false, supportDynamicHeights: wordWrap, diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 3cb5e45d844..e18a422896c 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -61,7 +61,7 @@ export class ReplFilter implements ITreeFilter { let includeQueryPresent = false; let includeQueryMatched = false; - const text = element.toString(); + const text = element.toString(true); for (let { type, query } of this._parsedQueries) { if (type === 'exclude' && ReplFilter.matchQuery(query, text)) { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 9f9f7b5f508..b468a45a486 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -107,7 +107,7 @@ export interface ITreeElement { } export interface IReplElement extends ITreeElement { - toString(): string; + toString(includeSource?: boolean): string; readonly sourceData?: IReplElementSource; } diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index 306984356bc..a4a20e491af 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -30,12 +30,12 @@ export class SimpleReplElement implements IReplElement { public sourceData?: IReplElementSource, ) { } - toString(): string { + toString(includeSource = false): string { let valueRespectCount = this.value; for (let i = 1; i < this.count; i++) { valueRespectCount += (valueRespectCount.endsWith('\n') ? '' : '\n') + this.value; } - const sourceStr = this.sourceData ? ` ${this.sourceData.source.name}` : ''; + const sourceStr = (this.sourceData && includeSource) ? ` ${this.sourceData.source.name}` : ''; return valueRespectCount + sourceStr; } @@ -165,8 +165,8 @@ export class ReplGroup implements IReplElement { return this.id; } - toString(): string { - const sourceStr = this.sourceData ? ` ${this.sourceData.source.name}` : ''; + toString(includeSource = false): string { + const sourceStr = (includeSource && this.sourceData) ? ` ${this.sourceData.source.name}` : ''; return this.name + sourceStr; }