diff --git a/extensions/git/package.json b/extensions/git/package.json index a7f9e33f31d..c1cd8b5135c 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -26,11 +26,6 @@ }, "contributes": { "commands": [ - { - "command": "git.activate", - "title": "%command.activate%", - "category": "Git" - }, { "command": "git.setLogLevel", "title": "%command.setLogLevel%", @@ -456,10 +451,6 @@ ], "menus": { "commandPalette": [ - { - "command": "git.activate", - "when": "false" - }, { "command": "git.clone", "when": "config.git.enabled" diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 8cd65b4e477..f3d75519653 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -1,7 +1,6 @@ { "displayName": "Git", "description": "Git SCM Integration", - "command.activate": "Activate", "command.setLogLevel": "Set Log Level...", "command.clone": "Clone", "command.init": "Initialize Repository", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 25090369b71..be0f5387d15 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -253,11 +253,6 @@ export class CommandCenter { }); } - @command('git.activate') - async activate(): Promise { - // noop - } - @command('git.setLogLevel') async setLogLevel(): Promise { const createItem = (logLevel: LogLevel) => ({ diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index ce99e15cfe2..9e0933776a3 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -49,8 +49,9 @@ suite('git smoke test', function () { cp.execSync('git commit -m "initial commit"', { cwd }); // make sure git is activated - await commands.executeCommand('git.activate'); - git = extensions.getExtension('vscode.git')!.exports.getAPI(1); + const ext = extensions.getExtension('vscode.git'); + await ext?.activate(); + git = ext!.exports.getAPI(1); if (git.repositories.length === 0) { await eventToPromise(git.onDidOpenRepository); 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 ed8f4555cc3..e55c3e3d2a5 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { debug, workspace, commands, window, Disposable } from 'vscode'; import { basename } from 'path'; -import { disposeAll, createRandomFile } from '../utils'; +import { disposeAll } from '../utils'; suite('Debug', function () { @@ -14,13 +14,9 @@ suite('Debug', function () { assert.equal(debug.breakpoints.length, 0); let onDidChangeBreakpointsCounter = 0; const toDispose: Disposable[] = []; - let onBreakpointsChange: () => void; toDispose.push(debug.onDidChangeBreakpoints(() => { onDidChangeBreakpointsCounter++; - if (onBreakpointsChange) { - onBreakpointsChange(); - } })); debug.addBreakpoints([{ id: '1', enabled: true }, { id: '2', enabled: false, condition: '2 < 5' }]); @@ -38,21 +34,6 @@ suite('Debug', function () { assert.equal(onDidChangeBreakpointsCounter, 3); assert.equal(debug.breakpoints.length, 0); - const file = await createRandomFile('let i = 5;', undefined, '.js'); - const doc = await workspace.openTextDocument(file); - await window.showTextDocument(doc, undefined, false); - const breakpointsChanged = new Promise(resolve => onBreakpointsChange = resolve); - await commands.executeCommand('editor.debug.action.toggleBreakpoint'); - await breakpointsChanged; - assert.equal(debug.breakpoints.length, 1); - assert.equal(debug.breakpoints[0].enabled, true); - - await window.showTextDocument(doc, undefined, false); - const breakpointsChangedSecond = new Promise(resolve => onBreakpointsChange = resolve); - await commands.executeCommand('editor.debug.action.toggleBreakpoint'); - await breakpointsChangedSecond; - assert.equal(debug.breakpoints.length, 0); - disposeAll(toDispose); }); diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index dc558a4bbc0..8dea0ab03da 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -10,6 +10,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import * as errors from 'vs/base/common/errors'; import { VSBuffer } from 'vs/base/common/buffer'; import { getRandomElement } from 'vs/base/common/arrays'; +import { isFunction } from 'vs/base/common/types'; /** * An `IChannel` is an abstraction over a collection of commands. @@ -707,24 +708,26 @@ export class IPCServer implements IChannelServer, I * be selected and when listening without a router, every client * will be listened to. */ - getChannel(channelName: string, router?: IClientRouter): T { + getChannel(channelName: string, router: IClientRouter): T; + getChannel(channelName: string, clientFilter: (client: Client) => boolean): T; + getChannel(channelName: string, routerOrClientFilter: IClientRouter | ((client: Client) => boolean)): T { const that = this; return { call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { let connectionPromise: Promise>; - if (router) { - connectionPromise = router.routeCall(that, command, arg); - } else { + if (isFunction(routerOrClientFilter)) { // when no router is provided, we go random client picking - let connection = getRandomElement(that.connections); + let connection = getRandomElement(that.connections.filter(routerOrClientFilter)); connectionPromise = connection // if we found a client, let's call on it ? Promise.resolve(connection) // else, let's wait for a client to come along - : Event.toPromise(that.onDidAddConnection); + : Event.toPromise(Event.filter(that.onDidAddConnection, routerOrClientFilter)); + } else { + connectionPromise = routerOrClientFilter.routeCall(that, command, arg); } const channelPromise = connectionPromise @@ -734,11 +737,11 @@ export class IPCServer implements IChannelServer, I .call(command, arg, cancellationToken); }, listen(event: string, arg: any): Event { - if (!router) { - return that.getMulticastEvent(channelName, event, arg); + if (isFunction(routerOrClientFilter)) { + return that.getMulticastEvent(channelName, routerOrClientFilter, event, arg); } - const channelPromise = router.routeEvent(that, event, arg) + const channelPromise = routerOrClientFilter.routeEvent(that, event, arg) .then(connection => (connection as Connection).channelClient.getChannel(channelName)); return getDelayedChannel(channelPromise) @@ -747,7 +750,7 @@ export class IPCServer implements IChannelServer, I } as T; } - private getMulticastEvent(channelName: string, eventName: string, arg: any): Event { + private getMulticastEvent(channelName: string, clientFilter: (client: Client) => boolean, eventName: string, arg: any): Event { const that = this; let disposables = new DisposableStore(); @@ -784,8 +787,8 @@ export class IPCServer implements IChannelServer, I map.delete(connection); }; - that.connections.forEach(onDidAddConnection); - that.onDidAddConnection(onDidAddConnection, undefined, disposables); + that.connections.filter(clientFilter).forEach(onDidAddConnection); + Event.filter(that.onDidAddConnection, clientFilter)(onDidAddConnection, undefined, disposables); that.onDidRemoveConnection(onDidRemoveConnection, undefined, disposables); eventMultiplexer.event(emitter.fire, emitter, disposables); diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index 5839c45dab7..3539f5dea8c 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -454,7 +454,7 @@ suite('Base IPC', function () { client1.registerChannel('channel', clientChannel1); const pings: string[] = []; - const channel = server.getChannel('channel'); + const channel = server.getChannel('channel', () => true); const service = new TestChannelClient(channel); service.onPong(msg => pings.push(msg)); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 710fdb817c1..3418ece001e 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -26,7 +26,6 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; -import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker'; import { ipcRenderer } from 'electron'; import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/log'; import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; @@ -131,9 +130,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const electronService = createChannelSender(mainProcessService.getChannel('electron'), { context: configuration.windowId }); services.set(IElectronService, electronService); - const activeWindowManager = new ActiveWindowManager(electronService); - const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); - // Files const fileService = new FileService(logService); services.set(IFileService, fileService); @@ -184,8 +180,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); - services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter))); - services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', activeWindowRouter))); + services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); + services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', client => client.ctx !== 'main'))); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 00b20578698..8df47a030f8 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -589,6 +589,14 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, if (boxEndY - boxStartY > viewportHeight) { // the box is larger than the viewport ... scroll to its top newScrollTop = boxStartY; + } else if (verticalType === viewEvents.VerticalRevealType.NearTop) { + // We want a gap that is 20% of the viewport, but with a minimum of 5 lines + const desiredGapAbove = Math.max(5 * this._lineHeight, viewportHeight * 0.2); + // Try to scroll just above the box with the desired gap + const desiredScrollTop = boxStartY - desiredGapAbove; + // But ensure that the box is not pushed out of viewport + const minScrollTop = boxEndY - viewportHeight; + newScrollTop = Math.max(minScrollTop, desiredScrollTop); } else if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) { if (verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) { // Box is already in the viewport... do nothing diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 957b673c369..793ebe48c2e 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -557,6 +557,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._revealLine(lineNumber, VerticalRevealType.CenterIfOutsideViewport, scrollType); } + public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLine(lineNumber, VerticalRevealType.NearTop, scrollType); + } + private _revealLine(lineNumber: number, revealType: VerticalRevealType, scrollType: editorCommon.ScrollType): void { if (typeof lineNumber !== 'number') { throw new Error('Invalid arguments'); @@ -597,6 +601,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE ); } + public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealPosition( + position, + VerticalRevealType.NearTop, + true, + scrollType + ); + } + private _revealPosition(position: IPosition, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void { if (!Position.isIPosition(position)) { throw new Error('Invalid arguments'); @@ -685,6 +698,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE ); } + public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealLines( + startLineNumber, + endLineNumber, + VerticalRevealType.NearTop, + scrollType + ); + } + private _revealLines(startLineNumber: number, endLineNumber: number, verticalType: VerticalRevealType, scrollType: editorCommon.ScrollType): void { if (typeof startLineNumber !== 'number' || typeof endLineNumber !== 'number') { throw new Error('Invalid arguments'); @@ -725,6 +747,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE ); } + public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this._revealRange( + range, + VerticalRevealType.NearTop, + true, + scrollType + ); + } + public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { this._revealRange( range, diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ffd852ccc1a..ffbe2519541 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -763,6 +763,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); } + public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLineNearTop(lineNumber, scrollType); + } + public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { this.modifiedEditor.revealPosition(position, scrollType); } @@ -775,6 +779,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); } + public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealPositionNearTop(position, scrollType); + } + public getSelection(): Selection | null { return this.modifiedEditor.getSelection(); } @@ -807,6 +815,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); } + public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); + } + public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { this.modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); } @@ -819,6 +831,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this.modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); } + public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { + this.modifiedEditor.revealRangeNearTop(range, scrollType); + } + public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { this.modifiedEditor.revealRangeAtTop(range, scrollType); } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index c66c92d66f9..4d7afb226fb 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2666,7 +2666,7 @@ class EditorSuggest extends BaseEditorOption { if (!editor) { return undefined; diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index f060f6aaf5c..f1857f14862 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -37,6 +37,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ScrollType, IEditorAction } from 'vs/editor/common/editorCommon'; import { assertType } from 'vs/base/common/types'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; MenuRegistry.appendMenuItem(MenuId.EditorContext, { @@ -166,7 +167,7 @@ abstract class SymbolNavigationAction extends EditorAction { resource: reference.uri, options: { selection: Range.collapseToStart(range), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport } }, editor, sideBySide); diff --git a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts index 879f3b9aaf9..0385d330427 100644 --- a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts @@ -19,6 +19,7 @@ import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isEqual } from 'vs/base/common/resources'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; export const ctxHasSymbols = new RawContextKey('hasSymbols', false); @@ -127,7 +128,7 @@ class SymbolNavigationService implements ISymbolNavigationService { resource: reference.uri, options: { selection: Range.collapseToStart(reference.range), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport } }, source).finally(() => { this._ignoreEditorChange = false; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c4aa3e03146..32e1852b2fb 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2171,6 +2171,11 @@ declare namespace monaco.editor { * Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. */ revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType?: ScrollType): void; + /** + * Scroll vertically as necessary and reveal a line close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealLineNearTop(lineNumber: number, scrollType?: ScrollType): void; /** * Scroll vertically or horizontally as necessary and reveal a position. */ @@ -2183,6 +2188,11 @@ declare namespace monaco.editor { * Scroll vertically or horizontally as necessary and reveal a position centered vertically only if it lies outside the viewport. */ revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType?: ScrollType): void; + /** + * Scroll vertically or horizontally as necessary and reveal a position close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealPositionNearTop(position: IPosition, scrollType?: ScrollType): void; /** * Returns the primary selection of the editor. */ @@ -2228,6 +2238,11 @@ declare namespace monaco.editor { * Scroll vertically as necessary and reveal lines centered vertically only if it lies outside the viewport. */ revealLinesInCenterIfOutsideViewport(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; + /** + * Scroll vertically as necessary and reveal lines close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealLinesNearTop(lineNumber: number, endLineNumber: number, scrollType?: ScrollType): void; /** * Scroll vertically or horizontally as necessary and reveal a range. */ @@ -2244,6 +2259,11 @@ declare namespace monaco.editor { * Scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. */ revealRangeInCenterIfOutsideViewport(range: IRange, scrollType?: ScrollType): void; + /** + * Scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport, + * optimized for viewing a code definition. + */ + revealRangeNearTop(range: IRange, scrollType?: ScrollType): void; /** * Directly trigger a handler or an editor action. * @param source The source of the call. diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index fcbef52e0f3..f6018b7bd19 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -215,6 +215,21 @@ export interface ITextEditorSelection { readonly endColumn?: number; } +export const enum TextEditorSelectionRevealType { + /** + * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically. + */ + Center = 0, + /** + * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. + */ + CenterIfOutsideViewport = 1, + /** + * Option to scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport, but not quite at the top. + */ + NearTop = 2, +} + export interface ITextEditorOptions extends IEditorOptions { /** @@ -228,7 +243,8 @@ export interface ITextEditorOptions extends IEditorOptions { readonly viewState?: object; /** - * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. + * Option to control the text editor selection reveal type. + * Defaults to TextEditorSelectionRevealType.Center */ - readonly revealInCenterIfOutsideViewport?: boolean; + readonly selectionRevealType?: TextEditorSelectionRevealType; } diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 9cc6dff8ed3..bccd1c8d1b0 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -98,7 +98,18 @@ export class DiskFileSystemProvider extends Disposable implements try { let type: FileType; if (child.isSymbolicLink()) { - type = (await this.stat(joinPath(resource, child.name))).type; // always resolve target the link points to if any + try { + type = (await this.stat(joinPath(resource, child.name))).type; // always resolve target the link points to if any + } catch (error) { + if (error.code !== FileSystemProviderErrorCode.FileNotFound) { + throw error; // any error that is not file not found is unexpected + } + + // a symbolic link can point to a target that does + // not exist on the file system. in that case we + // still want to return the element as UNKNOWN. + type = FileType.SymbolicLink | FileType.Unknown; + } } else { type = this.toType(child); } diff --git a/src/vs/platform/files/test/node/diskFileService.test.ts b/src/vs/platform/files/test/node/diskFileService.test.ts index 8c3ae0bef46..c58e7045e14 100644 --- a/src/vs/platform/files/test/node/diskFileService.test.ts +++ b/src/vs/platform/files/test/node/diskFileService.test.ts @@ -440,7 +440,7 @@ suite('Disk File Service', function () { assert.equal(resolved.isSymbolicLink, true); }); - test('resolve - invalid symbolic link does not break', async () => { + test('resolve - symbolic link pointing to non-existing file does not break', async () => { if (isWindows) { return; // not reliable on windows } @@ -450,7 +450,13 @@ suite('Disk File Service', function () { const resolved = await service.resolve(URI.file(testDir)); assert.equal(resolved.isDirectory, true); - assert.equal(resolved.children!.length, 8); + assert.equal(resolved.children!.length, 9); + + const resolvedLink = resolved.children?.filter(child => child.name === 'bar' && child.isSymbolicLink)[0]; + assert.ok(resolvedLink); + + assert.ok(!resolvedLink?.isDirectory); + assert.ok(!resolvedLink?.isFile); }); test('deleteFile', async () => { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index b58850125ba..db819929569 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -48,6 +48,7 @@ import { onDidChangeZoomLevel } from 'vs/base/browser/browser'; import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; class Item extends BreadcrumbsItem { @@ -490,7 +491,7 @@ export class BreadcrumbsControl { resource: model.textModel.uri, options: { selection: Range.collapseToStart(element.symbol.selectionRange), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport } }, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP); } diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index ff494235a74..4235bf53368 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -310,6 +310,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return this.paneItems.map(i => i.pane); } + get views(): IView[] { + return this.panes; + } + get length(): number { return this.paneItems.length; } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 566e186ca84..97167ee9659 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -5,11 +5,11 @@ import 'vs/css!./media/views'; import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService, IViewPaneContainer } from 'vs/workbench/common/views'; +import { IViewDescriptorService, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, ViewContainerLocation, IViewsService, IViewPaneContainer, getVisbileViewContextKey } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; import { firstIndex, move } from 'vs/base/common/arrays'; import { isUndefinedOrNull, isUndefined, isString } from 'vs/base/common/types'; @@ -464,15 +464,19 @@ export class ViewsService extends Disposable implements IViewsService { private readonly _onDidChangeViewVisibility: Emitter<{ id: string, visible: boolean }> = this._register(new Emitter<{ id: string, visible: boolean }>()); readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }> = this._onDidChangeViewVisibility.event; + private readonly visibleViewContextKeys: Map>; + constructor( @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IPanelService private readonly panelService: IPanelService, - @IViewletService private readonly viewletService: IViewletService + @IViewletService private readonly viewletService: IViewletService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); this.viewContainersRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); this.viewDisposable = new Map(); + this.visibleViewContextKeys = new Map>(); this._register(toDisposable(() => { this.viewDisposable.forEach(disposable => disposable.dispose()); @@ -483,16 +487,50 @@ export class ViewsService extends Disposable implements IViewsService { this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => this.onDidRegisterViewContainer(viewContainer, viewContainerLocation))); } + registerViewPaneContainer(viewPaneContainer: ViewPaneContainer): ViewPaneContainer { + this._register(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views))); + this._register(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible()))); + this._register(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views))); + return viewPaneContainer; + } + + private onViewsAdded(added: IView[]): void { + for (const view of added) { + this.onViewsVisibilityChanged(view, view.isBodyVisible()); + } + } + + private onViewsVisibilityChanged(view: IView, visible: boolean): void { + this.getOrCreateActiveViewContextKey(view).set(visible); + this._onDidChangeViewVisibility.fire({ id: view.id, visible: visible }); + } + + private onViewsRemoved(removed: IView[]): void { + for (const view of removed) { + this.onViewsVisibilityChanged(view, false); + } + } + + private getOrCreateActiveViewContextKey(view: IView): IContextKey { + const visibleContextKeyId = getVisbileViewContextKey(view.id); + let contextKey = this.visibleViewContextKeys.get(visibleContextKeyId); + if (!contextKey) { + contextKey = new RawContextKey(visibleContextKeyId, false).bindTo(this.contextKeyService); + this.visibleViewContextKeys.set(visibleContextKeyId, contextKey); + } + return contextKey; + } + private onDidRegisterViewContainer(viewContainer: ViewContainer, location: ViewContainerLocation): void { const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); - this.onViewsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer); + this.onViewDescriptorsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer); this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => { - this.onViewsAdded(added, viewContainer); - this.onViewsRemoved(removed); + this.onViewDescriptorsAdded(added, viewContainer); + this.onViewDescriptorsRemoved(removed); })); } - private onViewsAdded(views: IViewDescriptor[], container: ViewContainer): void { + private onViewDescriptorsAdded(views: IViewDescriptor[], container: ViewContainer): void { const location = this.viewContainersRegistry.getViewContainerLocation(container); if (location === undefined) { return; @@ -561,7 +599,7 @@ export class ViewsService extends Disposable implements IViewsService { } } - private onViewsRemoved(views: IViewDescriptor[]): void { + private onViewDescriptorsRemoved(views: IViewDescriptor[]): void { for (const view of views) { const disposable = this.viewDisposable.get(view); if (disposable) { @@ -629,7 +667,7 @@ export class ViewsService extends Disposable implements IViewsService { if (activeViewPaneContainer) { const view = activeViewPaneContainer.getView(id); if (view) { - if (activeViewPaneContainer.length === 1) { + if (activeViewPaneContainer.views.length === 1) { const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer); if (location === ViewContainerLocation.Sidebar) { this.viewletService.hideActiveViewlet(); @@ -661,13 +699,6 @@ export class ViewsService extends Disposable implements IViewsService { return null; } - - registerViewPaneContainer(viewPaneContainer: ViewPaneContainer): ViewPaneContainer { - this._register(viewPaneContainer.onDidAddViews(views => views.forEach(view => this._onDidChangeViewVisibility.fire({ id: view.id, visible: view.isBodyVisible() })))); - this._register(viewPaneContainer.onDidChangeViewVisibility(view => this._onDidChangeViewVisibility.fire({ id: view.id, visible: view.isBodyVisible() }))); - this._register(viewPaneContainer.onDidRemoveViews(views => views.forEach(view => this._onDidChangeViewVisibility.fire({ id: view.id, visible: false })))); - return viewPaneContainer; - } } export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable { diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index a34c440587c..579b413c3a6 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -10,7 +10,7 @@ import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext, ITextEditorSelection } from 'vs/platform/editor/common/editor'; +import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext, ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -1110,9 +1110,9 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio editorViewState: IEditorViewState | undefined; /** - * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. + * Option to control the text editor selection reveal type. */ - revealInCenterIfOutsideViewport: boolean | undefined; + selectionRevealType: TextEditorSelectionRevealType | undefined; static from(input?: IBaseResourceInput): TextEditorOptions | undefined { if (!input || !input.options) { @@ -1151,8 +1151,8 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio this.editorViewState = options.viewState as IEditorViewState; } - if (typeof options.revealInCenterIfOutsideViewport === 'boolean') { - this.revealInCenterIfOutsideViewport = options.revealInCenterIfOutsideViewport; + if (typeof options.selectionRevealType !== 'undefined') { + this.selectionRevealType = options.selectionRevealType; } return this; @@ -1162,7 +1162,7 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio * Returns if this options object has objects defined for the editor. */ hasOptionsDefined(): boolean { - return !!this.editorViewState || !!this.revealInCenterIfOutsideViewport || !!this.selection; + return !!this.editorViewState || !!this.selectionRevealType || !!this.selection; } /** @@ -1202,7 +1202,9 @@ export class TextEditorOptions extends EditorOptions implements ITextEditorOptio editor.setSelection(range); - if (this.revealInCenterIfOutsideViewport) { + if (this.selectionRevealType === TextEditorSelectionRevealType.NearTop) { + editor.revealRangeNearTop(range, scrollType); + } else if (this.selectionRevealType === TextEditorSelectionRevealType.CenterIfOutsideViewport) { editor.revealRangeInCenterIfOutsideViewport(range, scrollType); } else { editor.revealRangeInCenter(range, scrollType); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 59bfebf08c8..eab075361f5 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -21,7 +21,6 @@ import { flatten } from 'vs/base/common/arrays'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; -export const FocusedViewContext = new RawContextKey('focusedView', ''); export namespace Extensions { export const ViewContainersRegistry = 'workbench.registry.view.containers'; @@ -358,6 +357,7 @@ export interface IViewsViewlet extends IViewlet { export const IViewsService = createDecorator('viewsService'); export interface IViewsService { + _serviceBrand: undefined; readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }>; @@ -372,6 +372,12 @@ export interface IViewsService { } +/** + * View Contexts + */ +export const FocusedViewContext = new RawContextKey('focusedView', ''); +export function getVisbileViewContextKey(viewId: string): string { return `${viewId}.visible`; } + export const IViewDescriptorService = createDecorator('viewDescriptorService'); export interface IViewDescriptorService { @@ -529,7 +535,7 @@ export interface IViewPaneContainer { onDidRemoveViews: Event; onDidChangeViewVisibility: Event; - readonly length: number; + readonly views: IView[]; setVisible(visible: boolean): void; isVisible(): boolean; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index babb3753773..ae2df49c344 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -34,6 +34,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { Gesture } from 'vs/base/browser/touch'; import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; const $ = dom.$; @@ -633,7 +634,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea preserveFocus, selection, revealIfOpened: true, - revealInCenterIfOutsideViewport: true, + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, pinned: !preserveFocus } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 277c7ea8004..d84f499b404 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -883,44 +883,36 @@ export class DebugService implements IDebugService { await this.sendExceptionBreakpoints(session); } - private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { + private async sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { const breakpointsToSend = this.model.getBreakpoints({ uri: modelUri, enabledOnly: true }); - return this.sendToOneOrAllSessions(session, s => - s.sendBreakpoints(modelUri, breakpointsToSend, sourceModified) - ); + await sendToOneOrAllSessions(this.model, session, s => s.sendBreakpoints(modelUri, breakpointsToSend, sourceModified)); } - private sendFunctionBreakpoints(session?: IDebugSession): Promise { + private async sendFunctionBreakpoints(session?: IDebugSession): Promise { const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); - return this.sendToOneOrAllSessions(session, s => { - return s.capabilities.supportsFunctionBreakpoints ? s.sendFunctionBreakpoints(breakpointsToSend) : Promise.resolve(undefined); + await sendToOneOrAllSessions(this.model, session, async s => { + if (s.capabilities.supportsFunctionBreakpoints) { + await s.sendFunctionBreakpoints(breakpointsToSend); + } }); } private sendDataBreakpoints(session?: IDebugSession): Promise { const breakpointsToSend = this.model.getDataBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); - return this.sendToOneOrAllSessions(session, s => { - return s.capabilities.supportsDataBreakpoints ? s.sendDataBreakpoints(breakpointsToSend) : Promise.resolve(undefined); + return sendToOneOrAllSessions(this.model, session, async s => { + if (s.capabilities.supportsDataBreakpoints) { + await s.sendDataBreakpoints(breakpointsToSend); + } }); } private sendExceptionBreakpoints(session?: IDebugSession): Promise { const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); - return this.sendToOneOrAllSessions(session, s => { - return s.sendExceptionBreakpoints(enabledExceptionBps); - }); - } - - private async sendToOneOrAllSessions(session: IDebugSession | undefined, send: (session: IDebugSession) => Promise): Promise { - if (session) { - await send(session); - } else { - await Promise.all(this.model.getSessions().map(s => send(s))); - } + return sendToOneOrAllSessions(this.model, session, s => s.sendExceptionBreakpoints(enabledExceptionBps)); } private onFileChanges(fileChangesEvent: FileChangesEvent): void { @@ -1134,3 +1126,11 @@ export function getStackFrameThreadAndSessionToFocus(model: IDebugModel, stackFr return { session, thread, stackFrame }; } + +async function sendToOneOrAllSessions(model: DebugModel, session: IDebugSession | undefined, send: (session: IDebugSession) => Promise): Promise { + if (session) { + await send(session); + } else { + await Promise.all(model.getSessions().map(s => send(s))); + } +} diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index a6c75b4d2ae..11c7ef8329c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -190,7 +190,7 @@ export class DebugSession implements IDebugSession { try { const customTelemetryService = await dbgr.getCustomTelemetryService(); const debugAdapter = await dbgr.createDebugAdapter(this); - this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService); + this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService, this.notificationService); await this.raw.start(); this.registerListeners(); @@ -693,7 +693,6 @@ export class DebugSession implements IDebugSession { await this.raw.configurationDone(); } catch (e) { // Disconnect the debug session on configuration done error #10596 - this.notificationService.error(e); if (this.raw) { this.raw.disconnect(); } diff --git a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts index ce8d83b187b..87549aaf25c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts +++ b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts @@ -8,7 +8,6 @@ import severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks'; @@ -18,6 +17,7 @@ import { withUndefinedAsNull } from 'vs/base/common/types'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; +import { IViewsService } from 'vs/workbench/common/views'; function once(match: (e: TaskEvent) => boolean, event: Event): Event { return (listener, thisArgs = null, disposables?) => { @@ -44,7 +44,7 @@ export class DebugTaskRunner { @ITaskService private readonly taskService: ITaskService, @IMarkerService private readonly markerService: IMarkerService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IPanelService private readonly panelService: IPanelService, + @IViewsService private readonly viewsService: IViewsService, @IDialogService private readonly dialogService: IDialogService, ) { } @@ -68,7 +68,7 @@ export class DebugTaskRunner { return TaskRunResult.Success; } if (onTaskErrors === 'showErrors') { - await this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + await this.viewsService.openView(Constants.MARKERS_VIEW_ID); return Promise.resolve(TaskRunResult.Failure); } @@ -97,7 +97,7 @@ export class DebugTaskRunner { return TaskRunResult.Success; } - await this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + await this.viewsService.openView(Constants.MARKERS_VIEW_ID); return Promise.resolve(TaskRunResult.Failure); } catch (err) { await onError(err.message, [this.taskService.configureAction()]); diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index f7bbb068852..f751bebda62 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -19,6 +19,7 @@ import { env as processEnv } from 'vs/base/common/process'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; /** * This interface represents a single command line argument split into a "prefix" and a "path" half. @@ -79,7 +80,8 @@ export class RawDebugSession implements IDisposable { private readonly telemetryService: ITelemetryService, public readonly customTelemetryService: ITelemetryService | undefined, private readonly extensionHostDebugService: IExtensionHostDebugService, - private readonly openerService: IOpenerService + private readonly openerService: IOpenerService, + private readonly notificationService: INotificationService ) { this.debugAdapter = debugAdapter; this._capabilities = Object.create(null); @@ -632,8 +634,8 @@ export class RawDebugSession implements IDisposable { return errors.canceled(); } - const error = errorResponse && errorResponse.body ? errorResponse.body.error : null; - const errorMessage = errorResponse ? errorResponse.message || '' : ''; + const error: DebugProtocol.Message | undefined = errorResponse?.body?.error; + const errorMessage = errorResponse?.message || ''; if (error && error.sendTelemetry) { const telemetryMessage = error ? formatPII(error.format, true, error.variables) : errorMessage; @@ -641,15 +643,19 @@ export class RawDebugSession implements IDisposable { } const userMessage = error ? formatPII(error.format, false, error.variables) : errorMessage; - if (error && error.url) { + const url = error?.url; + if (error && url) { const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info"); return createErrorWithActions(userMessage, { actions: [new Action('debug.moreInfo', label, undefined, true, () => { - this.openerService.open(URI.parse(error.url)); + this.openerService.open(URI.parse(url)); return Promise.resolve(null); })] }); } + if (error && error.format && error.showUser) { + this.notificationService.error(error.format); + } return new Error(userMessage); } diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index c4ac1470595..cf4a9dfe80b 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -13,6 +13,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/ import { Schemas } from 'vs/base/common/network'; import { isUri } from 'vs/workbench/contrib/debug/common/debugUtils'; import { ITextEditor } from 'vs/workbench/common/editor'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); @@ -101,7 +102,7 @@ export class Source { preserveFocus, selection, revealIfOpened: true, - revealInCenterIfOutsideViewport: true, + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, pinned: pinned || (!preserveFocus && !this.inMemory) } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 0b287f5a12c..5f7e0b2b8ae 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -11,7 +11,7 @@ import { deepClone } from 'vs/base/common/objects'; const _formatPIIRegexp = /{([^}]+)}/g; -export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string }): string { +export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string } | undefined): string { return value.replace(_formatPIIRegexp, function (match, group) { if (excludePII && group.length > 0 && group[0] !== '_') { return match; 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 c155eca7669..297496be064 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -135,7 +135,7 @@ suite('Debug - REPL', () => { model.addSession(session); const adapter = new MockDebugAdapter(); - const raw = new RawDebugSession(adapter, undefined!, undefined!, undefined!, undefined!, undefined!); + const raw = new RawDebugSession(adapter, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!); session.initializeForTest(raw); await session.addReplExpression(undefined, 'before.1'); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts similarity index 100% rename from src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts rename to src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts diff --git a/src/vs/workbench/contrib/markers/browser/constants.ts b/src/vs/workbench/contrib/markers/browser/constants.ts index 470a762569e..6a06908d88a 100644 --- a/src/vs/workbench/contrib/markers/browser/constants.ts +++ b/src/vs/workbench/contrib/markers/browser/constants.ts @@ -6,22 +6,23 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export default { - MARKERS_PANEL_ID: 'workbench.panel.markers', - MARKERS_PANEL_STORAGE_ID: 'workbench.panel.markers', + MARKERS_CONTAINER_ID: 'workbench.panel.markers', MARKERS_VIEW_ID: 'workbench.panel.markers.view', + MARKERS_VIEW_STORAGE_ID: 'workbench.panel.markers', MARKER_COPY_ACTION_ID: 'problems.action.copy', MARKER_COPY_MESSAGE_ACTION_ID: 'problems.action.copyMessage', RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID: 'problems.action.copyRelatedInformationMessage', FOCUS_PROBLEMS_FROM_FILTER: 'problems.action.focusProblemsFromFilter', - MARKERS_PANEL_FOCUS_FILTER: 'problems.action.focusFilter', - MARKERS_PANEL_SHOW_MULTILINE_MESSAGE: 'problems.action.showMultilineMessage', - MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE: 'problems.action.showSinglelineMessage', + MARKERS_VIEW_FOCUS_FILTER: 'problems.action.focusFilter', + MARKERS_VIEW_SHOW_MULTILINE_MESSAGE: 'problems.action.showMultilineMessage', + MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE: 'problems.action.showSinglelineMessage', MARKER_OPEN_SIDE_ACTION_ID: 'problems.action.openToSide', MARKER_SHOW_PANEL_ID: 'workbench.action.showErrorsWarnings', MARKER_SHOW_QUICK_FIX: 'problems.action.showQuickFixes', + TOGGLE_MARKERS_VIEW_ACTION_ID: 'workbench.actions.view.toggleProblems', - MarkerPanelFocusContextKey: new RawContextKey('problemsViewFocus', false), + MarkerViewFocusContextKey: new RawContextKey('problemsViewFocus', false), MarkerFocusContextKey: new RawContextKey('problemFocus', false), - MarkerPanelFilterFocusContextKey: new RawContextKey('problemsFilterFocus', false), + MarkerViewFilterFocusContextKey: new RawContextKey('problemsFilterFocus', false), RelatedInformationFocusContextKey: new RawContextKey('relatedInformationFocus', false) }; diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 728ec69438b..30e05092dfe 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -12,7 +12,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; -import { MarkersView, getMarkersView } from 'vs/workbench/contrib/markers/browser/markersView'; +import { MarkersView } from 'vs/workbench/contrib/markers/browser/markersView'; import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -24,12 +24,11 @@ import { IMarkersWorkbenchService, MarkersWorkbenchService, ActivityUpdater } fr import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { ActivePanelContext } from 'vs/workbench/common/panel'; import { Disposable } from 'vs/base/common/lifecycle'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; +import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, getVisbileViewContextKey } from 'vs/workbench/common/views'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -46,7 +45,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyMod.WinCtrl | KeyCode.Enter }, handler: (accessor, args: any) => { - const markersView = getMarkersView(accessor.get(IPanelService))!; + const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; markersView.openFileAtElement(markersView.getFocusElement(), false, true, true); } }); @@ -57,7 +56,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: undefined, primary: undefined, handler: async (accessor, args: any) => { - await accessor.get(IPanelService).openPanel(Constants.MARKERS_PANEL_ID); + await accessor.get(IViewsService).openView(Constants.MARKERS_VIEW_ID); } }); @@ -67,7 +66,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: Constants.MarkerFocusContextKey, primary: KeyMod.CtrlCmd | KeyCode.US_DOT, handler: (accessor, args: any) => { - const markersView = getMarkersView(accessor.get(IPanelService))!; + const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; const focusedElement = markersView.getFocusElement(); if (focusedElement instanceof Marker) { markersView.showQuickFixes(focusedElement); @@ -104,15 +103,15 @@ class ToggleMarkersPanelAction extends TogglePanelAction { @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IPanelService panelService: IPanelService ) { - super(id, label, Constants.MARKERS_PANEL_ID, panelService, layoutService); + super(id, label, Constants.MARKERS_CONTAINER_ID, panelService, layoutService); } } // markers view container const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ - id: Constants.MARKERS_PANEL_ID, + id: Constants.MARKERS_CONTAINER_ID, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_PANEL_ID, Constants.MARKERS_PANEL_STORAGE_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_CONTAINER_ID, Constants.MARKERS_VIEW_STORAGE_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), focusCommand: { id: ToggleMarkersPanelAction.ID, keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M @@ -155,7 +154,7 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor) { - await copyMarker(accessor.get(IPanelService), accessor.get(IClipboardService)); + await copyMarker(accessor.get(IViewsService), accessor.get(IClipboardService)); } }); registerAction2(class extends Action2 { @@ -171,7 +170,7 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor) { - await copyMessage(accessor.get(IPanelService), accessor.get(IClipboardService)); + await copyMessage(accessor.get(IViewsService), accessor.get(IClipboardService)); } }); registerAction2(class extends Action2 { @@ -187,7 +186,7 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor) { - await copyRelatedInformationMessage(accessor.get(IPanelService), accessor.get(IClipboardService)); + await copyRelatedInformationMessage(accessor.get(IViewsService), accessor.get(IClipboardService)); } }); registerAction2(class extends Action2 { @@ -196,46 +195,46 @@ registerAction2(class extends Action2 { id: Constants.FOCUS_PROBLEMS_FROM_FILTER, title: localize('focusProblemsList', "Focus problems view"), keybinding: { - when: Constants.MarkerPanelFilterFocusContextKey, + when: Constants.MarkerViewFilterFocusContextKey, weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.DownArrow } }); } run(accessor: ServicesAccessor) { - focusProblemsView(accessor.get(IPanelService)); + focusProblemsView(accessor.get(IViewsService)); } }); registerAction2(class extends Action2 { constructor() { super({ - id: Constants.MARKERS_PANEL_FOCUS_FILTER, + id: Constants.MARKERS_VIEW_FOCUS_FILTER, title: localize('focusProblemsFilter', "Focus problems filter"), keybinding: { - when: Constants.MarkerPanelFocusContextKey, + when: Constants.MarkerViewFocusContextKey, weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KEY_F } }); } run(accessor: ServicesAccessor) { - focusProblemsFilter(accessor.get(IPanelService)); + focusProblemsFilter(accessor.get(IViewsService)); } }); registerAction2(class extends Action2 { constructor() { super({ - id: Constants.MARKERS_PANEL_SHOW_MULTILINE_MESSAGE, + id: Constants.MARKERS_VIEW_SHOW_MULTILINE_MESSAGE, title: { value: localize('show multiline', "Show message in multiple lines"), original: 'Problems: Show message in multiple lines' }, category: localize('problems', "Problems"), menu: { id: MenuId.CommandPalette, - when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) + when: ContextKeyExpr.has(getVisbileViewContextKey(Constants.MARKERS_VIEW_ID)) } }); } run(accessor: ServicesAccessor) { - const markersView = getMarkersView(accessor.get(IPanelService)); + const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; if (markersView) { markersView.markersViewModel.multiline = true; } @@ -244,25 +243,25 @@ registerAction2(class extends Action2 { registerAction2(class extends Action2 { constructor() { super({ - id: Constants.MARKERS_PANEL_SHOW_SINGLELINE_MESSAGE, + id: Constants.MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE, title: { value: localize('show singleline', "Show message in single line"), original: 'Problems: Show message in single line' }, category: localize('problems', "Problems"), menu: { id: MenuId.CommandPalette, - when: ActivePanelContext.isEqualTo(Constants.MARKERS_PANEL_ID) + when: ContextKeyExpr.has(getVisbileViewContextKey(Constants.MARKERS_VIEW_ID)) } }); } run(accessor: ServicesAccessor) { - const markersView = getMarkersView(accessor.get(IPanelService)); + const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID); if (markersView) { markersView.markersViewModel.multiline = false; } } }); -async function copyMarker(panelService: IPanelService, clipboardService: IClipboardService) { - const markersView = getMarkersView(panelService); +async function copyMarker(viewsService: IViewsService, clipboardService: IClipboardService) { + const markersView = viewsService.getActiveViewWithId(Constants.MARKERS_VIEW_ID); if (markersView) { const element = markersView.getFocusElement(); if (element instanceof Marker) { @@ -271,8 +270,8 @@ async function copyMarker(panelService: IPanelService, clipboardService: IClipbo } } -async function copyMessage(panelService: IPanelService, clipboardService: IClipboardService) { - const markersView = getMarkersView(panelService); +async function copyMessage(viewsService: IViewsService, clipboardService: IClipboardService) { + const markersView = viewsService.getActiveViewWithId(Constants.MARKERS_VIEW_ID); if (markersView) { const element = markersView.getFocusElement(); if (element instanceof Marker) { @@ -281,8 +280,8 @@ async function copyMessage(panelService: IPanelService, clipboardService: IClipb } } -async function copyRelatedInformationMessage(panelService: IPanelService, clipboardService: IClipboardService) { - const markersView = getMarkersView(panelService); +async function copyRelatedInformationMessage(viewsService: IViewsService, clipboardService: IClipboardService) { + const markersView = viewsService.getActiveViewWithId(Constants.MARKERS_VIEW_ID); if (markersView) { const element = markersView.getFocusElement(); if (element instanceof RelatedInformation) { @@ -291,15 +290,15 @@ async function copyRelatedInformationMessage(panelService: IPanelService, clipbo } } -function focusProblemsView(panelService: IPanelService) { - const markersView = getMarkersView(panelService); +function focusProblemsView(viewsService: IViewsService) { + const markersView = viewsService.getActiveViewWithId(Constants.MARKERS_VIEW_ID); if (markersView) { markersView.focus(); } } -function focusProblemsFilter(panelService: IPanelService): void { - const markersView = getMarkersView(panelService); +function focusProblemsFilter(viewsService: IViewsService): void { + const markersView = viewsService.getActiveViewWithId(Constants.MARKERS_VIEW_ID); if (markersView) { markersView.focusFilter(); } @@ -314,13 +313,12 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { order: 4 }); -CommandsRegistry.registerCommand('workbench.actions.view.toggleProblems', async (accessor) => { - const panelService = accessor.get(IPanelService); - const panel = accessor.get(IPanelService).getActivePanel(); - if (panel && panel.getId() === Constants.MARKERS_PANEL_ID) { - panelService.hideActivePanel(); +CommandsRegistry.registerCommand(Constants.TOGGLE_MARKERS_VIEW_ACTION_ID, async (accessor) => { + const viewsService = accessor.get(IViewsService); + if (viewsService.isViewVisible(Constants.MARKERS_VIEW_ID)) { + viewsService.closeView(Constants.MARKERS_VIEW_ID); } else { - await panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + viewsService.openView(Constants.MARKERS_VIEW_ID, true); } }); diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index cdcf72b64cd..cd1fdf7c351 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -70,6 +70,6 @@ export class ActivityUpdater extends Disposable implements IWorkbenchContributio const { errors, warnings, infos } = this.markerService.getStatistics(); const total = errors + warnings + infos; const message = localize('totalProblems', 'Total {0} Problems', total); - this.activity.value = this.activityService.showActivity(Constants.MARKERS_PANEL_ID, new NumberBadge(total, () => message)); + this.activity.value = this.activityService.showActivity(Constants.MARKERS_CONTAINER_ID, new NumberBadge(total, () => message)); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 95398693f7e..32dff982d89 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -9,7 +9,6 @@ import { URI } from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; import { IAction, IActionViewItem, Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { PaneCompositePanel } from 'vs/workbench/browser/panel'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent } from 'vs/workbench/contrib/markers/browser/markersModel'; @@ -49,17 +48,8 @@ import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { KeyCode } from 'vs/base/common/keyCodes'; import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -export function getMarkersView(panelService: IPanelService): MarkersView | undefined { - const activePanel = panelService.getActivePanel(); - if (activePanel instanceof PaneCompositePanel) { - return activePanel.getViewPaneContainer().getView(Constants.MARKERS_VIEW_ID); - } - return undefined; -} - function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterator> { const markersIt = Iterator.fromArray(resourceMarkers.markers); @@ -116,8 +106,8 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { @IStorageService storageService: IStorageService, ) { super({ ...(options as IViewPaneOptions), id: Constants.MARKERS_VIEW_ID, ariaHeaderLabel: Messages.MARKERS_PANEL_TITLE_PROBLEMS }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService); - this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService); - this.panelState = new Memento(Constants.MARKERS_PANEL_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE); + this.panelFoucusContextKey = Constants.MarkerViewFocusContextKey.bindTo(contextKeyService); + this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'])); this._register(this.markersViewModel.onDidChange(marker => this.onDidChangeViewState(marker))); this.setCurrentActiveEditor(); diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index e8b570efdbe..3130ecaee2e 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -12,7 +12,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IThemeService, registerThemingParticipant, ICssStyleCollector, ITheme } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { toDisposable } from 'vs/base/common/lifecycle'; @@ -27,6 +26,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { FilterOptions } from 'vs/workbench/contrib/markers/browser/markersFilterOptions'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { IViewsService } from 'vs/workbench/common/views'; export class ShowProblemsPanelAction extends Action { @@ -34,13 +34,13 @@ export class ShowProblemsPanelAction extends Action { public static readonly LABEL = Messages.MARKERS_PANEL_SHOW_LABEL; constructor(id: string, label: string, - @IPanelService private readonly panelService: IPanelService + @IViewsService private readonly viewsService: IViewsService ) { super(id, label); } public run(): Promise { - return this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + return this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); } } @@ -271,7 +271,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { @IContextKeyService contextKeyService: IContextKeyService ) { super(null, action); - this.focusContextKey = Constants.MarkerPanelFilterFocusContextKey.bindTo(contextKeyService); + this.focusContextKey = Constants.MarkerViewFilterFocusContextKey.bindTo(contextKeyService); this.delayedFilterUpdate = new Delayer(200); this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); this._register(action.onFocus(() => this.focus())); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index f2b0d0af50c..15fe25144de 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -27,7 +27,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IResourceInput, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; @@ -630,7 +630,7 @@ export class OutlinePane extends ViewPane { options: { preserveFocus: !focus, selection: Range.collapseToStart(element.symbol.selectionRange), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.NearTop, } } as IResourceInput, aside ? SIDE_GROUP : ACTIVE_GROUP); } diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 5af9dc00800..d5321cc6945 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -27,7 +27,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IProgressService, IProgressOptions, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -41,7 +41,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; @@ -76,10 +75,11 @@ import { format } from 'vs/base/common/jsonFormatter'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { ITextEditor } from 'vs/workbench/common/editor'; -import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; +import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { find } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IViewsService } from 'vs/workbench/common/views'; const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; @@ -238,6 +238,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IMarkerService protected readonly markerService: IMarkerService, @IOutputService protected readonly outputService: IOutputService, @IPanelService private readonly panelService: IPanelService, + @IViewsService private readonly viewsService: IViewsService, + @ICommandService private readonly commandService: ICommandService, @IEditorService private readonly editorService: IEditorService, @IFileService protected readonly fileService: IFileService, @IWorkspaceContextService protected readonly contextService: IWorkspaceContextService, @@ -257,7 +259,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @INotificationService private readonly notificationService: INotificationService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService, @IRemotePathService private readonly remotePathService: IRemotePathService, @ITextModelService private readonly textModelResolverService: ITextModelService, @@ -413,14 +414,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this.runShowTasks(); }); - CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', async () => { - const panel = this.panelService.getActivePanel(); - if (panel && panel.getId() === Constants.MARKERS_PANEL_ID) { - this.layoutService.setPanelHidden(true); - } else { - await this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); - } - }); + CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => this.commandService.executeCommand(Constants.TOGGLE_MARKERS_VIEW_ACTION_ID)); CommandsRegistry.registerCommand('workbench.action.tasks.openUserTasks', async () => { const resource = this.getResourceForKind(TaskSourceKind.User); @@ -968,7 +962,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer pinned: false, forceReload: true, // because content might have changed selection, - revealInCenterIfOutsideViewport: !!selection + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport } }); }); @@ -1283,14 +1277,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (active && active.same) { if (this._taskSystem?.isTaskVisible(executeResult.task)) { const message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', executeResult.task.getQualifiedLabel()); + let lastInstance = this.getTaskSystem().getLastInstance(executeResult.task) ?? executeResult.task; this.notificationService.prompt(Severity.Info, message, [{ label: nls.localize('terminateTask', "Terminate Task"), - run: () => this.terminate(executeResult.task) + run: () => this.terminate(lastInstance) }, { label: nls.localize('restartTask', "Restart Task"), - run: () => this.restart(executeResult.task) + run: () => this.restart(lastInstance) }], { sticky: true } ); @@ -1336,7 +1331,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected createTerminalTaskSystem(): ITaskSystem { return new TerminalTaskSystem( - this.terminalService, this.outputService, this.panelService, this.markerService, + this.terminalService, this.outputService, this.panelService, this.viewsService, this.markerService, this.modelService, this.configurationResolverService, this.telemetryService, this.contextService, this.environmentService, AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService, @@ -1973,11 +1968,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TaskQuickPickEntry[] { + let count: { [key: string]: number; } = {}; if (tasks === undefined || tasks === null || tasks.length === 0) { return []; } const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => { - return { label: task._label, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; + let entryLabel = task._label; + let commonKey = task._id.split('|')[0]; + if (count[commonKey]) { + entryLabel = entryLabel + ' (' + count[commonKey].toString() + ')'; + count[commonKey]++; + } else { + count[commonKey] = 1; + } + return { label: entryLabel, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; + }; function fillEntries(entries: QuickPickInput[], tasks: Task[], groupLabel: string): void { if (tasks.length) { @@ -2040,6 +2045,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } entries = tasks.map(task => TaskQuickPickEntry(task)); } + count = {}; return entries; } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 9a17b4e2cf9..b35ef9cc087 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -45,6 +45,7 @@ import { Schemas } from 'vs/base/common/network'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; +import { IViewsService } from 'vs/workbench/common/views'; interface TerminalData { terminal: ITerminalInstance; @@ -58,6 +59,25 @@ interface ActiveTerminalData { promise: Promise; } +class InstanceManager { + private _currentInstances: number = 0; + private _counter: number = 0; + + addInstance() { + this._currentInstances++; + this._counter++; + } + removeInstance() { + this._currentInstances--; + } + get instances() { + return this._currentInstances; + } + get counter() { + return this._counter; + } +} + class VariableResolver { constructor(public workspaceFolder: IWorkspaceFolder | undefined, public taskSystemInfo: TaskSystemInfo | undefined, private _values: Map, private _service: IConfigurationResolverService | undefined) { @@ -152,6 +172,7 @@ export class TerminalTaskSystem implements ITaskSystem { }; private activeTasks: IStringDictionary; + private instances: IStringDictionary; private busyTasks: IStringDictionary; private terminals: IStringDictionary; private idleTaskTerminals: LinkedMap; @@ -170,6 +191,7 @@ export class TerminalTaskSystem implements ITaskSystem { private terminalService: ITerminalService, private outputService: IOutputService, private panelService: IPanelService, + private viewsService: IViewsService, private markerService: IMarkerService, private modelService: IModelService, private configurationResolverService: IConfigurationResolverService, private telemetryService: ITelemetryService, @@ -183,6 +205,7 @@ export class TerminalTaskSystem implements ITaskSystem { ) { this.activeTasks = Object.create(null); + this.instances = Object.create(null); this.busyTasks = Object.create(null); this.terminals = Object.create(null); this.idleTaskTerminals = new LinkedMap(); @@ -205,18 +228,32 @@ export class TerminalTaskSystem implements ITaskSystem { } public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult { + let commonKey = task._id.split('|')[0]; + let validInstance = task.runOptions && task.runOptions.instanceLimit && this.instances[commonKey] && this.instances[commonKey].instances < task.runOptions.instanceLimit; + let instance = this.instances[commonKey] ? this.instances[commonKey].instances : 0; this.currentTask = new VerifiedTask(task, resolver, trigger); - let terminalData = this.activeTasks[task.getMapKey()]; - if (terminalData && terminalData.promise) { + let taskClone = undefined; + if (instance > 0) { + taskClone = task.clone(); + taskClone._id += '|' + this.instances[commonKey].counter.toString(); + } + let taskToExecute = taskClone ?? task; + let lastTaskInstance = this.getLastInstance(task); + let terminalData = lastTaskInstance ? this.activeTasks[lastTaskInstance.getMapKey()] : undefined; + if (terminalData && terminalData.promise && !validInstance) { this.lastTask = this.currentTask; - return { kind: TaskExecuteKind.Active, task, active: { same: true, background: task.configurationProperties.isBackground! }, promise: terminalData.promise }; + return { kind: TaskExecuteKind.Active, task: terminalData.task, active: { same: true, background: task.configurationProperties.isBackground! }, promise: terminalData.promise }; } try { - const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger) }; + const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(taskToExecute, resolver, trigger) }; executeResult.promise.then(summary => { this.lastTask = this.currentTask; }); + if (!this.instances[commonKey]) { + this.instances[commonKey] = new InstanceManager(); + } + this.instances[commonKey].addInstance(); return executeResult; } catch (error) { if (error instanceof TaskError) { @@ -302,6 +339,17 @@ export class TerminalTaskSystem implements ITaskSystem { return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task); } + public getLastInstance(task: Task): Task | undefined { + let lastInstance = undefined; + let commonId = task._id.split('|')[0]; + Object.keys(this.activeTasks).forEach((key) => { + if (commonId === this.activeTasks[key].task._id.split('|')[0]) { + lastInstance = this.activeTasks[key].task; + } + }); + return lastInstance; + } + public getBusyTasks(): Task[] { return Object.keys(this.busyTasks).map(key => this.busyTasks[key]); } @@ -318,6 +366,20 @@ export class TerminalTaskSystem implements ITaskSystem { }); } + private removeFromActiveTasks(task: Task): void { + if (!this.activeTasks[task.getMapKey()]) { + return; + } + delete this.activeTasks[task.getMapKey()]; + let commonKey = task._id.split('|')[0]; + if (this.instances[commonKey]) { + this.instances[commonKey].removeInstance(); + if (this.instances[commonKey].instances === 0) { + delete this.instances[commonKey]; + } + } + } + public terminate(task: Task): Promise { let activeTerminal = this.activeTasks[task.getMapKey()]; if (!activeTerminal) { @@ -619,7 +681,7 @@ export class TerminalTaskSystem implements ITaskSystem { let reveal = task.command.presentation!.reveal; let revealProblems = task.command.presentation!.revealProblems; if (revealProblems === RevealProblemKind.OnProblem) { - this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); } else if (reveal === RevealKind.Silent) { this.terminalService.setActiveInstance(terminal!); this.terminalService.showPanel(false); @@ -673,7 +735,7 @@ export class TerminalTaskSystem implements ITaskSystem { if (this.busyTasks[mapKey]) { delete this.busyTasks[mapKey]; } - delete this.activeTasks[key]; + this.removeFromActiveTasks(task); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); if (exitCode !== undefined) { // Only keep a reference to the terminal if it is not being disposed. @@ -751,7 +813,7 @@ export class TerminalTaskSystem implements ITaskSystem { onData.dispose(); onExit.dispose(); let key = task.getMapKey(); - delete this.activeTasks[key]; + this.removeFromActiveTasks(task); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed)); if (exitCode !== undefined) { // Only keep a reference to the terminal if it is not being disposed. @@ -768,7 +830,7 @@ export class TerminalTaskSystem implements ITaskSystem { let revealProblems = task.command.presentation!.revealProblems; let revealProblemPanel = terminal && (revealProblems === RevealProblemKind.OnProblem) && (startStopProblemMatcher.numberOfMatches > 0); if (revealProblemPanel) { - this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + this.viewsService.openView(Constants.MARKERS_VIEW_ID); } else if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { this.terminalService.setActiveInstance(terminal); @@ -799,7 +861,7 @@ export class TerminalTaskSystem implements ITaskSystem { let showProblemPanel = task.command.presentation && (task.command.presentation.revealProblems === RevealProblemKind.Always); if (showProblemPanel) { - this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + this.viewsService.openView(Constants.MARKERS_VIEW_ID); } else if (task.command.presentation && (task.command.presentation.reveal === RevealKind.Always)) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(task.command.presentation.focus); @@ -1102,7 +1164,7 @@ export class TerminalTaskSystem implements ITaskSystem { // For correct terminal re-use, the task needs to be deleted immediately. // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate. const mapKey = task.getMapKey(); - delete this.activeTasks[mapKey]; + this.removeFromActiveTasks(task); if (this.busyTasks[mapKey]) { delete this.busyTasks[mapKey]; } diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts index 1fcd569b476..3f9ca21ede2 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts @@ -316,6 +316,7 @@ const identifier: IJSONSchema = { const runOptions: IJSONSchema = { type: 'object', + additionalProperties: false, properties: { reevaluateOnRerun: { type: 'boolean', @@ -328,6 +329,11 @@ const runOptions: IJSONSchema = { description: nls.localize('JsonSchema.tasks.runOn', 'Configures when the task should be run. If set to folderOpen, then the task will be run automatically when the folder is opened.'), default: 'default' }, + instanceLimit: { + type: 'number', + description: nls.localize('JsonSchema.tasks.instanceLimit', 'The number of instances of the task that are allowed to run simultaneously.'), + default: 1 + }, }, description: nls.localize('JsonSchema.tasks.runOptions', 'The task\'s run related options') }; diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 2677f6bd2ce..5e6b05391ab 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -133,6 +133,7 @@ export interface PresentationOptionsConfig { export interface RunOptionsConfig { reevaluateOnRerun?: boolean; runOn?: string; + instanceLimit?: number; } export interface TaskIdentifier { @@ -681,7 +682,8 @@ export namespace RunOptions { export function fromConfiguration(value: RunOptionsConfig | undefined): Tasks.RunOptions { return { reevaluateOnRerun: value ? value.reevaluateOnRerun : true, - runOn: value ? RunOnOptions.fromString(value.runOn) : Tasks.RunOnOptions.default + runOn: value ? RunOnOptions.fromString(value.runOn) : Tasks.RunOnOptions.default, + instanceLimit: value ? value.instanceLimit : 1 }; } } diff --git a/src/vs/workbench/contrib/tasks/common/taskSystem.ts b/src/vs/workbench/contrib/tasks/common/taskSystem.ts index c6d2c0bc03e..888d79b912c 100644 --- a/src/vs/workbench/contrib/tasks/common/taskSystem.ts +++ b/src/vs/workbench/contrib/tasks/common/taskSystem.ts @@ -133,6 +133,7 @@ export interface ITaskSystem { isActive(): Promise; isActiveSync(): boolean; getActiveTasks(): Task[]; + getLastInstance(task: Task): Task | undefined; getBusyTasks(): Task[]; canAutoTerminate(): boolean; terminate(task: Task): Promise; diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 6cde38187b1..8130ab56092 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -523,10 +523,11 @@ export enum RunOnOptions { export interface RunOptions { reevaluateOnRerun?: boolean; runOn?: RunOnOptions; + instanceLimit?: number; } export namespace RunOptions { - export const defaults: RunOptions = { reevaluateOnRerun: true, runOn: RunOnOptions.default }; + export const defaults: RunOptions = { reevaluateOnRerun: true, runOn: RunOnOptions.default, instanceLimit: 1 }; } export abstract class CommonTask { diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index 73d947db159..ebc9c929933 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -94,6 +94,14 @@ export class ProcessTaskSystem implements ITaskSystem { return result; } + public getLastInstance(task: Task): Task | undefined { + let result = undefined; + if (this.activeTask) { + result = this.activeTask; + } + return result; + } + public getBusyTasks(): Task[] { return []; } diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 34b84fcff5a..b05c5830535 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -5,7 +5,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IEditor } from 'vs/editor/common/editorCommon'; -import { ITextEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; +import { ITextEditorOptions, IResourceInput, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IEditorInput, IEditor as IBaseEditor, Extensions as EditorExtensions, EditorInput, IEditorCloseEvent, IEditorInputFactoryRegistry, toResource, IEditorIdentifier, GroupIdentifier, EditorsOrder } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -351,7 +351,7 @@ export class HistoryService extends Disposable implements IHistoryService { const options: ITextEditorOptions = { revealIfOpened: true, // support to navigate across editor groups, selection: location.selection, - revealInCenterIfOutsideViewport: !!location.selection + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport }; if (location.input instanceof EditorInput) { diff --git a/test/assert.js b/test/assert.js index 94971d6b49b..228016e796c 100644 --- a/test/assert.js +++ b/test/assert.js @@ -273,25 +273,24 @@ assert.notEqual = function notEqual(actual, expected, message) { // assert.deepEqual(actual, expected, message_opt); assert.deepEqual = function deepEqual(actual, expected, message) { - if (!_deepEqual(actual, expected)) { + if (!_deepEqual(actual, expected, false)) { fail(actual, expected, message, 'deepEqual', assert.deepEqual); } }; -function _deepEqual(actual, expected) { +assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); + } +}; + +function _deepEqual(actual, expected, strict) { // 7.1. All identical values are equivalent, as determined by ===. if (actual === expected) { return true; + // } else if (actual instanceof Buffer && expected instanceof Buffer) { + // return compare(actual, expected) === 0; - // } else if (util.isBuffer(actual) && util.isBuffer(expected)) { - // if (actual.length != expected.length) return false; - // - // for (var i = 0; i < actual.length; i++) { - // if (actual[i] !== expected[i]) return false; - // } - // - // return true; - // // 7.2. If the expected value is a Date object, the actual value is // equivalent if it is also a Date object that refers to the same time. } else if (util.isDate(actual) && util.isDate(expected)) { @@ -309,8 +308,9 @@ function _deepEqual(actual, expected) { // 7.4. Other pairs that do not both pass typeof value == 'object', // equivalence is determined by ==. - } else if (!util.isObject(actual) && !util.isObject(expected)) { - return actual == expected; + } else if ((actual === null || typeof actual !== 'object') && + (expected === null || typeof expected !== 'object')) { + return strict ? actual === expected : actual == expected; // 7.5 For all other Object pairs, including Array objects, equivalence is // determined by having the same number of owned properties (as verified @@ -319,32 +319,22 @@ function _deepEqual(actual, expected) { // corresponding key, and an identical 'prototype' property. Note: this // accounts for both named and indexed properties on Arrays. } else { - return objEquiv(actual, expected); + return objEquiv(actual, expected, strict); } } -var isArguments = function(object) { +function isArguments(object) { return Object.prototype.toString.call(object) == '[object Arguments]'; -}; +} -(function() { - if (!isArguments(arguments)) { - isArguments = function(object) { - return object != null && - typeof object === 'object' && - typeof object.callee === 'function' && - typeof object.length === 'number' || false; - }; - } -})(); - -function objEquiv(a, b) { - if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) +function objEquiv(a, b, strict) { + if (a === null || a === undefined || b === null || b === undefined) + return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) + return a === b; + if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false; - // an identical 'prototype' property. - if (a.prototype !== b.prototype) return false; - //~~~I've managed to break Object.keys through screwy arguments passing. - // Converting to array solves the problem. var aIsArgs = isArguments(a), bIsArgs = isArguments(b); if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) @@ -352,32 +342,28 @@ function objEquiv(a, b) { if (aIsArgs) { a = pSlice.call(a); b = pSlice.call(b); - return _deepEqual(a, b); - } - try { - var ka = Object_keys(a), - kb = Object_keys(b), - key, i; - } catch (e) {//happens when one is a string literal and the other isn't - return false; + return _deepEqual(a, b, strict); } + var ka = Object.keys(a), + kb = Object.keys(b), + key, i; // having the same number of owned properties (keys incorporates // hasOwnProperty) - if (ka.length != kb.length) + if (ka.length !== kb.length) return false; //the same set of keys (although not necessarily the same order), ka.sort(); kb.sort(); //~~~cheap key test for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] != kb[i]) + if (ka[i] !== kb[i]) return false; } //equivalent values for every corresponding key, and //~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; - if (!_deepEqual(a[key], b[key])) return false; + if (!_deepEqual(a[key], b[key], strict)) return false; } return true; } @@ -386,11 +372,19 @@ function objEquiv(a, b) { // assert.notDeepEqual(actual, expected, message_opt); assert.notDeepEqual = function notDeepEqual(actual, expected, message) { - if (_deepEqual(actual, expected)) { + if (_deepEqual(actual, expected, false)) { fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); } }; +assert.notDeepStrictEqual = notDeepStrictEqual; +function notDeepStrictEqual(actual, expected, message) { + if (_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); + } +} + + // 9. The strict equality assertion tests strict equality, as determined by ===. // assert.strictEqual(actual, expected, message_opt); @@ -428,7 +422,11 @@ function expectedException(actual, expected) { function _throws(shouldThrow, block, expected, message) { var actual; - if (util.isString(expected)) { + if (typeof block !== 'function') { + throw new TypeError('block must be a function'); + } + + if (typeof expected === 'string') { message = expected; expected = null; } @@ -468,10 +466,6 @@ assert.doesNotThrow = function(block, /*optional*/message) { _throws.apply(this, [false].concat(pSlice.call(arguments))); }; -// VSCODE-CHANGE, todo@joh this isn't correct!! -assert.deepStrictEqual = assert.deepEqual; - -assert.ifError = function (err) { if (err) { throw err; } }; - +assert.ifError = function(err) { if (err) {throw err;}}; return assert; }); diff --git a/test/browser/index.js b/test/browser/index.js index a89053b562c..19471196973 100644 --- a/test/browser/index.js +++ b/test/browser/index.js @@ -29,26 +29,31 @@ const optimist = require('optimist') // logic const argv = optimist.argv; -const Reporter = (function () { +const withReporter = (function () { const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); - let Reporter; + let ctor; try { - Reporter = require(reporterPath); + ctor = require(reporterPath); } catch (err) { try { - Reporter = require(argv.reporter); + ctor = require(argv.reporter); } catch (err) { - Reporter = process.platform === 'win32' ? mocha.reporters.List : mocha.reporters.Spec; - console.warn(`could not load reporter: ${argv.reporter}, using ${Reporter.name}`); + ctor = process.platform === 'win32' ? mocha.reporters.List : mocha.reporters.Spec; + console.warn(`could not load reporter: ${argv.reporter}, using ${ctor.name}`); } } - // let reporterOptions = argv['reporter-options']; - // reporterOptions = typeof reporterOptions === 'string' ? [reporterOptions] : reporterOptions; - // reporterOptions = reporterOptions.reduce((r, o) => Object.assign(r, parseReporterOption(o)), {}); + function parseReporterOption(value) { + let r = /^([^=]+)=(.*)$/.exec(value); + return r ? { [r[1]]: r[2] } : {}; + } - return Reporter + let reporterOptions = argv['reporter-options']; + reporterOptions = typeof reporterOptions === 'string' ? [reporterOptions] : reporterOptions; + reporterOptions = reporterOptions.reduce((r, o) => Object.assign(r, parseReporterOption(o)), {}); + + return (runner) => new ctor(runner, { reporterOptions }) })() const outdir = argv.build ? 'out-build' : 'out'; @@ -104,7 +109,7 @@ async function runTestsInBrowser(testModules, browserType) { console[msg.type()](msg.text(), await Promise.all(msg.args().map(async arg => await arg.jsonValue()))); }); - new Reporter(new EchoRunner(emitter, browserType.toUpperCase())); + withReporter(new EchoRunner(emitter, browserType.toUpperCase())); try { // @ts-ignore