diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 689bc2bfc28fa31653b870100ee99b3e36a2c568..cec5184b05f0c38d29d8872256fd092d8e5f975c 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -6,6 +6,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; export const IProgressService = createDecorator('progressService'); @@ -99,11 +101,13 @@ export interface IProgressService2 { export interface IOperation { id: number; isCurrent: () => boolean; + token: CancellationToken; stop(): void; } export class LongRunningOperation { private currentOperationId = 0; + private currentOperationDisposables: IDisposable[] = []; private currentProgressRunner: IProgressRunner; private currentProgressTimeout: number; @@ -113,33 +117,39 @@ export class LongRunningOperation { start(progressDelay: number): IOperation { - // Clear previous - if (this.currentProgressTimeout) { - clearTimeout(this.currentProgressTimeout); - } + // Stop any previous operation + this.stop(); // Start new const newOperationId = ++this.currentOperationId; + const newOperationToken = new CancellationTokenSource(); this.currentProgressTimeout = setTimeout(() => { if (newOperationId === this.currentOperationId) { this.currentProgressRunner = this.progressService.show(true); } }, progressDelay); + this.currentOperationDisposables.push( + toDisposable(() => clearTimeout(this.currentProgressTimeout)), + toDisposable(() => newOperationToken.cancel()), + toDisposable(() => this.currentProgressRunner ? this.currentProgressRunner.done() : void 0) + ); + return { id: newOperationId, - stop: () => this.stop(newOperationId), + token: newOperationToken.token, + stop: () => this.doStop(newOperationId), isCurrent: () => this.currentOperationId === newOperationId }; } - private stop(operationId: number): void { - if (this.currentOperationId === operationId) { - clearTimeout(this.currentProgressTimeout); + stop(): void { + this.doStop(this.currentOperationId); + } - if (this.currentProgressRunner) { - this.currentProgressRunner.done(); - } + private doStop(operationId: number): void { + if (this.currentOperationId === operationId) { + this.currentOperationDisposables = dispose(this.currentOperationDisposables); } } } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index de2f20701608311dc05d258042f20e52cbf75e02..c9a547c0d283872ca3d34c3b3331378e6db6b1c4 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -10,6 +10,7 @@ import { EditorInput, EditorOptions, GroupIdentifier } from 'vs/workbench/common import { IEditor } from 'vs/platform/editor/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * The base class of editors in the workbench. Editors register themselves for specific editor inputs. @@ -55,10 +56,14 @@ export abstract class BaseEditor extends Panel implements IEditor { * Note: Clients should not call this method, the workbench calls this * method. Calling it otherwise may result in unexpected behavior. * - * Sets the given input with the options to the part. An editor has to deal with the - * situation that the same input is being set with different options. + * Sets the given input with the options to the editor. The input is guaranteed + * to be different from the previous input that was set using the input.matches() + * method. + * + * The provided cancellation token should be used to test if the operation + * was cancelled. */ - setInput(input: EditorInput, options?: EditorOptions): TPromise { + setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { this._input = input; this._options = options; @@ -74,6 +79,17 @@ export abstract class BaseEditor extends Panel implements IEditor { this._options = null; } + /** + * Note: Clients should not call this method, the workbench calls this + * method. Calling it otherwise may result in unexpected behavior. + * + * Sets the given options to the editor. Clients should apply the options + * to the current input. + */ + setOptions(options: EditorOptions): void { + this._options = options; + } + create(parent: HTMLElement): void; // create is sync for editors create(parent: HTMLElement): TPromise; create(parent: HTMLElement): TPromise { diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 8975f6e77b251505bacfe412a347a51efb5f7f9d..47de56f97916b5091bdf6ead2c723e1ce19f55ba 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -20,6 +20,7 @@ import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/part import URI from 'vs/base/common/uri'; import { Dimension } from 'vs/base/browser/dom'; import { IFileService } from 'vs/platform/files/common/files'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IOpenCallbacks { openInternal: (input: EditorInput, options: EditorOptions) => void; @@ -76,28 +77,20 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { parent.appendChild(this.scrollbar.getDomNode()); } - public setInput(input: EditorInput, options?: EditorOptions): TPromise { - - // Return early for same input unless we force to open - const forceOpen = options && options.forceOpen; - if (!forceOpen && input.matches(this.input)) { - return TPromise.wrap(null); - } - - // Otherwise set input and resolve - return super.setInput(input, options).then(() => { + public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { + return super.setInput(input, options, token).then(() => { return input.resolve(true).then(model => { + // Check for cancellation + if (token.isCancellationRequested) { + return void 0; + } + // Assert Model instance if (!(model instanceof BinaryEditorModel)) { return TPromise.wrapError(new Error('Unable to open file as binary')); } - // Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile - if (!this.input || this.input !== input) { - return null; - } - // Render Input this.resourceViewerContext = ResourceViewer.show( { name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, @@ -109,7 +102,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { meta => this.handleMetadataChanged(meta) ); - return TPromise.as(null); + return void 0; }); }); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index ab56b8ee8252cccf530673272fc2925b75f8300d..47ca77cd93a6835229c84fface6b45387156d922 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -1167,7 +1167,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService const inputChanged = (!previousInput || !previousInput.matches(input) || (options && options.forceOpen)); // Call into Editor - return editor.setInput(input, options).then(() => { + return editor.setInput(input, options, null).then(() => { // Stop loading promise if any monitor.cancel(); @@ -1197,7 +1197,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService }, e => { this.doHandleSetInputError(e, group, editor, input, options, monitor); return null; - }); + }) as TPromise; } private doHandleSetInputError(error: Error, group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): void { diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 1e0af6b38aa8fd000f2f367d7069f11cfd589a4c..09e849cf0f0a072547322de1cd4f608648187de7 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -15,6 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IThemeService } from 'vs/platform/theme/common/themeService'; import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class SideBySideEditor extends BaseEditor { @@ -43,10 +44,16 @@ export class SideBySideEditor extends BaseEditor { this.createSash(parent); } - public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise { + public setInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable { const oldInput = this.input; - return super.setInput(newInput, options) - .then(() => this.updateInput(oldInput, newInput, options)); + return super.setInput(newInput, options, token) + .then(() => this.updateInput(oldInput, newInput, options, token)); + } + + public setOptions(options: EditorOptions): void { + if (this.masterEditor) { + this.masterEditor.setOptions(options); + } } protected setEditorVisible(visible: boolean, group: GroupIdentifier): void { @@ -100,27 +107,27 @@ export class SideBySideEditor extends BaseEditor { return false; } - private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): void { + private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): void { if (!newInput.matches(oldInput)) { if (oldInput) { this.disposeEditors(); } this.createEditorContainers(); - return this.setNewInput(newInput, options); + return this.setNewInput(newInput, options, token); } else { - this.detailsEditor.setInput(newInput.details); - this.masterEditor.setInput(newInput.master, options); + this.detailsEditor.setInput(newInput.details, null, token); + this.masterEditor.setInput(newInput.master, options, token); return void 0; } } - private setNewInput(newInput: SideBySideEditorInput, options?: EditorOptions): void { + private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): void { const detailsEditor = this._createEditor(newInput.details, this.detailsEditorContainer); const masterEditor = this._createEditor(newInput.master, this.masterEditorContainer); - this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options); + this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token); } private _createEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor { @@ -133,11 +140,11 @@ export class SideBySideEditor extends BaseEditor { return editor; } - private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions): TPromise { + private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise { this.detailsEditor = details; this.masterEditor = master; this.dolayout(this.sash.getVerticalSashLeft()); - return TPromise.join([this.detailsEditor.setInput(detailsInput), this.masterEditor.setInput(masterInput, options)]).then(() => this.focus()); + return TPromise.join([this.detailsEditor.setInput(detailsInput, null, token), this.masterEditor.setInput(masterInput, options, token)]).then(() => this.focus()); } private createEditorContainers(): void { diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index daafb321916e7739bd58febae538217b1e08403b..b20edfe2e3d43123df037b08f84981021d135d6c 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -41,6 +41,7 @@ import { once } from 'vs/base/common/event'; import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/nextEditorService'; import { INextEditorGroup, INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; import { INextEditorService } from 'vs/workbench/services/editor/common/nextEditorService'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * The text editor that leverages the diff text editor for the editing experience. @@ -104,7 +105,7 @@ export class TextDiffEditor extends BaseTextEditor { // Input matches modified side of the diff editor: perform the action on modified side if (input.matches(activeDiffInput.modifiedInput)) { - return this.setInput(this.input, options).then(() => this); + return this.setInput(this.input, options, CancellationToken.None).then(() => this); } // Input matches original side of the diff editor: perform the action on original side @@ -128,20 +129,7 @@ export class TextDiffEditor extends BaseTextEditor { return diffEditorInstantiator.createInstance(DiffEditorWidget, parent, configuration); } - public setInput(input: EditorInput, options?: EditorOptions): TPromise { - - // Return early for same input unless we force to open - const forceOpen = options && options.forceOpen; - if (!forceOpen && input.matches(this.input)) { - - // Still apply options if any (avoiding instanceof here for a reason, do not change!) - const textOptions = options; - if (textOptions && types.isFunction(textOptions.apply)) { - textOptions.apply(this.getControl(), ScrollType.Smooth); - } - - return TPromise.wrap(null); - } + public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { // Dispose previous diff navigator this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables); @@ -150,17 +138,17 @@ export class TextDiffEditor extends BaseTextEditor { this.saveTextDiffEditorViewState(this.input); // Set input and resolve - return super.setInput(input, options).then(() => { + return super.setInput(input, options, token).then(() => { return input.resolve(true).then(resolvedModel => { - // Assert Model Instance - if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) { - return null; + // Check for cancellation + if (token.isCancellationRequested) { + return void 0; } - // Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile - if (!this.input || this.input !== input) { - return null; + // Assert Model Instance + if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) { + return void 0; } // Set Editor Model @@ -203,6 +191,13 @@ export class TextDiffEditor extends BaseTextEditor { }); } + public setOptions(options: EditorOptions): void { + const textOptions = options; + if (textOptions && types.isFunction(textOptions.apply)) { + textOptions.apply(this.getControl(), ScrollType.Smooth); + } + } + public supportsCenteredLayout(): boolean { return false; } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 74ce217eed831db7d485daca83fab6c28135daa8..114436fb2cfb28507a12281f3304f9c086626f0f 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -6,7 +6,6 @@ 'use strict'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import * as objects from 'vs/base/common/objects'; import * as types from 'vs/base/common/types'; @@ -28,6 +27,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState'; @@ -109,7 +109,7 @@ export abstract class BaseTextEditor extends BaseEditor { // Apply group information to help identify in which group we are if (ariaLabel && typeof this.group === 'number') { - ariaLabel = nls.localize('editorLabelWithGroup', "{0}, Group {1}.", ariaLabel, this.group + 1); + ariaLabel = nls.localize('editorLabelWithGroup', "{0}, Group {1}.", ariaLabel, this.group + 1); // TODO@grid do not use group ID in ARIA labels } return ariaLabel; @@ -186,8 +186,8 @@ export abstract class BaseTextEditor extends BaseEditor { return this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {}); } - public setInput(input: EditorInput, options?: EditorOptions): TPromise { - return super.setInput(input, options).then(() => { + public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { + return super.setInput(input, options, token).then(() => { // Update editor options after having set the input. We do this because there can be // editor input specific options (e.g. an ARIA label depending on the input showing) diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index f26757e9293f94a6a3097621bef265a6bc1070c6..d5b619c28b2038bd20f32b41c3d41df8f458d338 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -24,6 +24,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { once } from 'vs/base/common/event'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * An editor implementation that is capable of showing the contents of resource inputs. Uses @@ -53,38 +54,25 @@ export class AbstractTextResourceEditor extends BaseTextEditor { return nls.localize('textEditor', "Text Editor"); } - public setInput(input: EditorInput, options?: EditorOptions): TPromise { - - // Return early for same input unless we force to open - const forceOpen = options && options.forceOpen; - if (!forceOpen && input.matches(this.input)) { - - // Still apply options if any (avoiding instanceof here for a reason, do not change!) - const textOptions = options; - if (textOptions && types.isFunction(textOptions.apply)) { - textOptions.apply(this.getControl(), ScrollType.Smooth); - } - - return TPromise.wrap(null); - } + public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { // Remember view settings if input changes this.saveTextResourceEditorViewState(this.input); // Set input and resolve - return super.setInput(input, options).then(() => { + return super.setInput(input, options, token).then(() => { return input.resolve(true).then((resolvedModel: EditorModel) => { + // Check for cancellation + if (token.isCancellationRequested) { + return void 0; + } + // Assert Model instance if (!(resolvedModel instanceof BaseTextEditorModel)) { return TPromise.wrapError(new Error('Unable to open file as text')); } - // Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile - if (!this.input || this.input !== input) { - return null; - } - // Set Editor Model const textEditor = this.getControl(); const textEditorModel = resolvedModel.textEditorModel; @@ -116,6 +104,13 @@ export class AbstractTextResourceEditor extends BaseTextEditor { } } + public setOptions(options: EditorOptions): void { + const textOptions = options; + if (textOptions && types.isFunction(textOptions.apply)) { + textOptions.apply(this.getControl(), ScrollType.Smooth); + } + } + protected getConfigurationOverrides(): IEditorOptions { const options = super.getConfigurationOverrides(); diff --git a/src/vs/workbench/browser/parts/editor2/nextEditorControl.ts b/src/vs/workbench/browser/parts/editor2/nextEditorControl.ts index a1477b1e104593758bf5c53fe29235bda7965752..a350fb215a5772359cb182fe43f5a12202ad4f25 100644 --- a/src/vs/workbench/browser/parts/editor2/nextEditorControl.ts +++ b/src/vs/workbench/browser/parts/editor2/nextEditorControl.ts @@ -118,16 +118,23 @@ export class NextEditorControl extends Disposable { private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): Thenable { + // If the input did not change, return early and only apply the options + // unless the options instruct us to force open it even if it is the same + const forceOpen = options && options.forceOpen; + const inputMatches = control.input && control.input.matches(editor); + if (inputMatches && !forceOpen) { + control.setOptions(options); + + return TPromise.as(false); + } + // Show progress while setting input after a certain timeout. If the workbench is opening // be more relaxed about progress showing by increasing the delay a little bit to reduce flicker. const operation = this.editorOperation.start(this.partService.isCreated() ? 800 : 3200); // Call into editor control - const editorWillChange = (!control.input || !control.input.matches(editor) || (options && options.forceOpen)); - return control.setInput(editor, options).then(() => { - - // Operation done - operation.stop(); + const editorWillChange = !inputMatches || forceOpen; + return control.setInput(editor, options, operation.token).then(() => { // Focus (unless prevented or another operation is running) if (operation.isCurrent()) { @@ -137,6 +144,9 @@ export class NextEditorControl extends Disposable { } } + // Operation done + operation.stop(); + return editorWillChange; }, e => { @@ -152,6 +162,9 @@ export class NextEditorControl extends Disposable { return; } + // Stop any running operation + this.editorOperation.stop(); + // Remove control from parent and hide const controlInstanceContainer = this._activeControl.getContainer(); this.parent.removeChild(controlInstanceContainer); diff --git a/src/vs/workbench/browser/parts/editor2/nextEditorDropTarget.ts b/src/vs/workbench/browser/parts/editor2/nextEditorDropTarget.ts index 6a31b9f8d9f8756fbda605784c6375de1fc49caf..2c849d51a5a19646a33d360ea9d652525bd846fb 100644 --- a/src/vs/workbench/browser/parts/editor2/nextEditorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor2/nextEditorDropTarget.ts @@ -100,7 +100,7 @@ class DropOverlay extends Themable { } // Find out if operation is valid - const isCopy = isDraggingGroup ? this.isCopyOperation(e) : this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier); + const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true; if (!isCopy) { const sourceGroupView = this.findSourceGroupView(); if (sourceGroupView === this.groupView) { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 4bbffb7287dfdface3b0623d9cad3bb948a7e4f9..999428f2caef9ef712bb2538f9c9e4373e6151b5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -49,6 +49,7 @@ import { Color } from 'vs/base/common/color'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { assign } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** A context key that is set when an extension editor webview has focus. */ export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey('extensionEditorWebviewFocus', undefined); @@ -262,7 +263,7 @@ export class ExtensionEditor extends BaseEditor { this.content = append(body, $('.content')); } - setInput(input: ExtensionsInput, options: EditorOptions): TPromise { + setInput(input: ExtensionsInput, options: EditorOptions, token: CancellationToken): Thenable { this.editorLoadComplete = false; const extension = input.extension; @@ -376,7 +377,7 @@ export class ExtensionEditor extends BaseEditor { this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies")); this.editorLoadComplete = true; - return super.setInput(input, options); + return super.setInput(input, options, token); } showFind(): void { diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index 8c066aab845e4b8898bb39c43042dc14ed9a153b..e1ab80797949238d4d609af2be63feb183507565 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -26,13 +26,13 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { INextEditorService } from 'vs/workbench/services/editor/common/nextEditorService'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * An implementation of editor for file system resources. @@ -49,8 +49,7 @@ export class TextFileEditor extends BaseTextEditor { @IWorkspaceContextService private contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @INextEditorService nextEditorService: INextEditorService, + @INextEditorService private editorService: INextEditorService, @IThemeService themeService: IThemeService, @IEditorGroupService editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService, @@ -64,7 +63,7 @@ export class TextFileEditor extends BaseTextEditor { this.toUnbind.push(this.fileService.onFileChanges(e => this.onFilesChanged(e))); // React to editors closing to preserve view state - this.toUnbind.push(nextEditorService.onWillCloseEditor(e => this.onWillCloseEditor(e))); + this.toUnbind.push(editorService.onWillCloseEditor(e => this.onWillCloseEditor(e))); } private onFilesChanged(e: FileChangesEvent): void { @@ -88,27 +87,27 @@ export class TextFileEditor extends BaseTextEditor { return this._input as FileEditorInput; } - public setInput(input: FileEditorInput, options?: EditorOptions): TPromise { - - // Return early for same input unless we force to open - const forceOpen = options && options.forceOpen; - if (!forceOpen && input.matches(this.input)) { - - // Still apply options if any (avoiding instanceof here for a reason, do not change!) - if (options && types.isFunction((options).apply)) { - (options).apply(this.getControl(), ScrollType.Smooth); - } - - return TPromise.wrap(null); + public setOptions(options: EditorOptions): void { + const textOptions = options; + if (textOptions && types.isFunction(textOptions.apply)) { + textOptions.apply(this.getControl(), ScrollType.Smooth); } + } + + public setInput(input: FileEditorInput, options: EditorOptions, token: CancellationToken): Thenable { // Remember view settings if input changes this.doSaveTextEditorViewState(this.input); // Set input and resolve - return super.setInput(input, options).then(() => { + return super.setInput(input, options, token).then(() => { return input.resolve(true).then(resolvedModel => { + // Check for cancellation + if (token.isCancellationRequested) { + return void 0; + } + // There is a special case where the text editor has to handle binary file editor input: if a binary file // has been resolved and cached before, it maybe an actual instance of BinaryEditorModel. In this case our text // editor has to open this model using the binary editor. We return early in this case. @@ -127,7 +126,7 @@ export class TextFileEditor extends BaseTextEditor { modelDisposed || // input got disposed meanwhile inputChanged // a different input was set meanwhile ) { - return null; + return void 0; } // Editor @@ -205,13 +204,13 @@ export class TextFileEditor extends BaseTextEditor { private openAsBinary(input: FileEditorInput, options: EditorOptions): void { input.setForceOpenAsBinary(); - this.editorService.openEditor(input, options, this.group).done(null, errors.onUnexpectedError); + this.editorService.openEditor(input, options, this.group); } private openAsFolder(input: FileEditorInput): boolean { // Since we cannot open a folder, we have to restore the previous input if any and close the editor - this.editorService.closeEditor(this.group, this.input).done(() => { + this.editorService.closeEditor(this.input, this.group).then(() => { // Best we can do is to reveal the folder in the explorer if (this.contextService.isInsideWorkspace(input.getResource())) { @@ -219,7 +218,7 @@ export class TextFileEditor extends BaseTextEditor { return (viewlet as IExplorerViewlet).getExplorerView().select(input.getResource(), true); }, errors.onUnexpectedError); } - }, errors.onUnexpectedError); + }); return true; // in any case we handled it } diff --git a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts index 48fd59d9a5a8885d94da87fa1d9b695bfb044bfa..ab257ae2c32dc04caaf26f8345e844472deab8e4 100644 --- a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts @@ -26,6 +26,7 @@ import { BaseWebviewEditor } from 'vs/workbench/parts/webview/electron-browser/b import { WebviewElement, WebviewOptions } from 'vs/workbench/parts/webview/electron-browser/webviewElement'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface HtmlPreviewEditorViewState { scrollYPercentage: number; @@ -180,7 +181,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { this.webview.sendMessage(data); } - public setInput(input: EditorInput, options?: EditorOptions): TPromise { + public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { if (this.input && this.input.matches(input) && this._hasValidModel() && this.input instanceof HtmlInput && input instanceof HtmlInput && areHtmlInputOptionsEqual(this.input.options, input.options)) { return TPromise.as(undefined); @@ -204,11 +205,14 @@ export class HtmlPreviewPart extends BaseWebviewEditor { return TPromise.wrapError(new Error('Invalid input')); } - return super.setInput(input, options).then(() => { + return super.setInput(input, options, token).then(() => { const resourceUri = input.getResource(); return this._textModelResolverService.createModelReference(resourceUri).then(ref => { - const model = ref.object; + if (token.isCancellationRequested) { + return undefined; + } + const model = ref.object; if (model instanceof BaseTextEditorModel) { this._modelRef = ref; } diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index 61b8cd4809e19baad9a870c149052d234a929b83..7831ef2f277794e3acada8b29e27c3ce71d9900e 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -24,6 +24,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class OutputPanel extends AbstractTextResourceEditor { private actions: IAction[]; @@ -106,7 +107,7 @@ export class OutputPanel extends AbstractTextResourceEditor { return channel ? nls.localize('outputPanelWithInputAriaLabel', "{0}, Output panel", channel.label) : nls.localize('outputPanelAriaLabel', "Output panel"); } - public setInput(input: EditorInput, options?: EditorOptions): TPromise { + public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { if (input.matches(this.input)) { return TPromise.as(null); } @@ -115,7 +116,7 @@ export class OutputPanel extends AbstractTextResourceEditor { // Dispose previous input (Output panel is not a workbench editor) this.input.dispose(); } - return super.setInput(input, options).then(() => this.revealLastLine()); + return super.setInput(input, options, token).then(() => this.revealLastLine()); } public clearInput(): void { diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index 769103f1d49bf363c78bf884c71e07e995444cf8..5e6a6dd369b10c70c043805baeb73dee7e3f7af8 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -40,6 +40,7 @@ import { binarySearch } from 'vs/base/common/arrays'; import { Schemas } from 'vs/base/common/network'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CancellationToken } from 'vs/base/common/cancellation'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; @@ -515,7 +516,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } } - private onDidPanelOpen(panel: IPanel): TPromise { + private onDidPanelOpen(panel: IPanel): Thenable { if (panel && panel.getId() === OUTPUT_PANEL_ID) { this._outputPanel = this.panelService.getActivePanel(); if (this.activeChannel) { @@ -580,10 +581,10 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } } - private doShowChannel(channel: IOutputChannel, preserveFocus: boolean): TPromise { + private doShowChannel(channel: IOutputChannel, preserveFocus: boolean): Thenable { if (this._outputPanel) { CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(channel instanceof FileOutputChannel); - return this._outputPanel.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus: preserveFocus })) + return this._outputPanel.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus: preserveFocus }), CancellationToken.None) .then(() => { if (!preserveFocus) { this._outputPanel.focus(); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 9511b94d628a2ef2eea9982ff7d90e1685a7f9ea..498202d501a1ca21cb02fe141560fec1b32605a2 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -44,6 +44,7 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; +import { CancellationToken } from 'vs/base/common/cancellation'; let $ = DOM.$; @@ -88,7 +89,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor ) { super(KeybindingsEditor.ID, telemetryService, themeService); this.delayedFiltering = new Delayer(300); - this._register(keybindingsService.onDidUpdateKeybindings(() => this.render())); + this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(false, CancellationToken.None))); this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService); this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); @@ -108,13 +109,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this._register(focusTracker.onDidBlur(() => this.keybindingsEditorContextKey.reset())); } - setInput(input: KeybindingsEditorInput, options: EditorOptions): TPromise { - const oldInput = this.input; - return super.setInput(input) + setInput(input: KeybindingsEditorInput, options: EditorOptions, token: CancellationToken): Thenable { + return super.setInput(input, options, token) .then(() => { - if (!input.matches(oldInput)) { - this.render(options && options.preserveFocus); - } + this.render(options && options.preserveFocus, token); }); } @@ -356,18 +354,30 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor })); } - private render(preserveFocus?: boolean): TPromise { + private render(preserveFocus: boolean, token: CancellationToken): TPromise { if (this.input) { return this.input.resolve() - .then((keybindingsModel: KeybindingsEditorModel) => this.keybindingsEditorModel = keybindingsModel) - .then(() => { + .then((keybindingsModel: KeybindingsEditorModel) => { + if (token.isCancellationRequested) { + return void 0; + } + + this.keybindingsEditorModel = keybindingsModel; + const editorActionsLabels: { [id: string]: string; } = EditorExtensionsRegistry.getEditorActions().reduce((editorActions, editorAction) => { editorActions[editorAction.id] = editorAction.label; return editorActions; }, {}); + return this.keybindingsEditorModel.resolve(editorActionsLabels); }) - .then(() => this.renderKeybindingsEntries(false, preserveFocus)); + .then(() => { + if (token.isCancellationRequested) { + return void 0; + } + + this.renderKeybindingsEntries(false, preserveFocus); + }); } return TPromise.as(null); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 3396cffcdb56be2915eada8251349647542258c7..956b2abdc7625ff1eae88f6a3b3d14b25faf2a1a 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -63,6 +63,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { PreferencesEditorInput, DefaultPreferencesEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { PREFERENCES_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class PreferencesEditor extends BaseEditor { @@ -146,10 +147,9 @@ export class PreferencesEditor extends BaseEditor { this.preferencesRenderers.editFocusedPreference(); } - public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { + public setInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): Thenable { this.defaultSettingsEditorContextKey.set(true); - const oldInput = this.input; - return super.setInput(newInput, options).then(() => this.updateInput(oldInput, newInput, options)); + return super.setInput(newInput, options, token).then(() => this.updateInput(newInput, options, token)); } public layout(dimension: DOM.Dimension): void { @@ -199,8 +199,12 @@ export class PreferencesEditor extends BaseEditor { super.setEditorVisible(visible, group); } - private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { - return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { + private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): TPromise { + return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { + if (token.isCancellationRequested) { + return void 0; + } + this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer; this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer; this.onInputChanged(); @@ -802,13 +806,19 @@ class SideBySidePreferencesWidget extends Widget { this._register(focusTracker.onDidFocus(() => this._onFocus.fire())); } - public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer, editablePreferencesRenderer: IPreferencesRenderer }> { + public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer, editablePreferencesRenderer: IPreferencesRenderer }> { this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput); this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource()); this.dolayout(this.sash.getVerticalSashLeft()); - return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options), - this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)]) + return TPromise.join([ + this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options, token), + this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options, token) + ]) .then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => { + if (token.isCancellationRequested) { + return void 0; + } + this.defaultPreferencesHeader.textContent = defaultPreferencesRenderer && this.getDefaultPreferencesHeaderText((defaultPreferencesRenderer.preferencesModel).target); return { defaultPreferencesRenderer, editablePreferencesRenderer }; }); @@ -875,9 +885,15 @@ class SideBySidePreferencesWidget extends Widget { return editor; } - private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions): TPromise> { - return editor.setInput(input, options) - .then(() => (editor.getControl()).getContribution(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri)); + private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions, token: CancellationToken): Thenable> { + return editor.setInput(input, options, token) + .then(() => { + if (token.isCancellationRequested) { + return void 0; + } + + return (editor.getControl()).getContribution(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri); + }); } private createSash(parentElement: HTMLElement): void { @@ -1000,11 +1016,23 @@ export class DefaultPreferencesEditor extends BaseTextEditor { return options; } - setInput(input: DefaultPreferencesEditorInput, options: EditorOptions): TPromise { - return super.setInput(input, options) + setInput(input: DefaultPreferencesEditorInput, options: EditorOptions, token: CancellationToken): Thenable { + return super.setInput(input, options, token) .then(() => this.input.resolve() - .then(editorModel => editorModel.load()) - .then(editorModel => this.getControl().setModel((editorModel).textEditorModel))); + .then(editorModel => { + if (token.isCancellationRequested) { + return void 0; + } + + return editorModel.load(); + }) + .then(editorModel => { + if (token.isCancellationRequested) { + return void 0; + } + + this.getControl().setModel((editorModel).textEditorModel); + })); } public clearInput(): void { diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index f114fcefdeda5b4c7a22e015b9e24b907bcf2173..0fb4f4ed34913f8cd1e56ba6331e8e2b0e0fbf5c 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -39,6 +39,7 @@ import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/p import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IPreferencesSearchService, ISearchProvider } from '../common/preferences'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; +import { CancellationToken } from 'vs/base/common/cancellation'; const SETTINGS_ENTRY_TEMPLATE_ID = 'settings.entry.template'; const SETTINGS_GROUP_ENTRY_TEMPLATE_ID = 'settings.group.template'; @@ -149,13 +150,10 @@ export class SettingsEditor2 extends BaseEditor { this.createBody(prefsEditorElement); } - setInput(input: SettingsEditor2Input, options: EditorOptions): TPromise { - const oldInput = this.input; - return super.setInput(input) + setInput(input: SettingsEditor2Input, options: EditorOptions, token: CancellationToken): Thenable { + return super.setInput(input, options, token) .then(() => { - if (!input.matches(oldInput)) { - this.render(); - } + this.render(token); }); } @@ -168,7 +166,7 @@ export class SettingsEditor2 extends BaseEditor { this.searchWidget.layout(dimension); this.layoutSettingsList(); - this.render(); + this.render(CancellationToken.None); } focus(): void { @@ -286,12 +284,12 @@ export class SettingsEditor2 extends BaseEditor { private onShowAllSettingsClicked(): void { this.showAllSettings = !this.showAllSettings; - this.render(); + this.render(CancellationToken.None); } private onShowConfiguredOnlyClicked(): void { this.showConfiguredSettingsOnly = this.showConfiguredSettingsOnlyCheckbox.checked; - this.render(); + this.render(CancellationToken.None); } private onDidChangeSetting(key: string, value: any): void { @@ -374,11 +372,18 @@ export class SettingsEditor2 extends BaseEditor { this.telemetryService.publicLog('settingEditor.settingModified', data); } - private render(): TPromise { + private render(token: CancellationToken): TPromise { if (this.input) { return this.input.resolve() - .then((model: DefaultSettingsEditorModel) => this.defaultSettingsEditorModel = model) - .then(() => this.renderEntries()); + .then((model: DefaultSettingsEditorModel) => { + if (token.isCancellationRequested) { + return void 0; + } + + this.defaultSettingsEditorModel = model; + + this.renderEntries(); + }); } return TPromise.as(null); } @@ -462,7 +467,7 @@ export class SettingsEditor2 extends BaseEditor { return TPromise.join(filterPs).then(results => { const [result] = results; this.searchResultModel.setResult(type, result); - return this.render(); + return this.render(CancellationToken.None); }); } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts index e6bd9e592b0b53a27d46c8742a55fd666e7f8862..b9ea57aaeac62d32225e1c22ed63308b8753dad7 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts @@ -18,6 +18,7 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { BaseWebviewEditor, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor'; import { WebviewElement } from './webviewElement'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class WebviewEditor extends BaseWebviewEditor { @@ -132,20 +133,21 @@ export class WebviewEditor extends BaseWebviewEditor { super.clearInput(); } - async setInput(input: WebviewEditorInput, options: EditorOptions): TPromise { - if (this.input && this.input.matches(input)) { - return undefined; - } - + async setInput(input: WebviewEditorInput, options: EditorOptions, token: CancellationToken): TPromise { if (this.input) { (this.input as WebviewEditorInput).releaseWebview(this); this._webview = undefined; this.webviewContent = undefined; } - await super.setInput(input, options); + await super.setInput(input, options, token); await input.resolve(); - await input.updateGroup(this.group); + + if (token.isCancellationRequested) { + return; + } + + input.updateGroup(this.group); this.updateWebview(input); } diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts index 8742b8005c20f545250ce9a7486d5af9fa9dcf5c..602475e3bfcf2c99d4229c6b68bd2413d0907702 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts @@ -10,7 +10,6 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { EditorOptions, EditorViewStateMemento } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; @@ -40,6 +39,7 @@ import { deepClone } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Dimension, size } from 'vs/base/browser/dom'; import { INextEditorGroupsService } from 'vs/workbench/services/group/common/nextEditorGroupsService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const WALK_THROUGH_FOCUS = new RawContextKey('interactivePlaygroundFocus', false); @@ -250,11 +250,7 @@ export class WalkThroughPart extends BaseEditor { this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop + scrollDimensions.height }); } - setInput(input: WalkThroughInput, options: EditorOptions): TPromise { - if (this.input instanceof WalkThroughInput && this.input.matches(input)) { - return TPromise.as(undefined); - } - + setInput(input: WalkThroughInput, options: EditorOptions, token: CancellationToken): Thenable { if (this.input instanceof WalkThroughInput) { this.saveTextEditorViewState(this.input); } @@ -262,11 +258,15 @@ export class WalkThroughPart extends BaseEditor { this.contentDisposables = dispose(this.contentDisposables); this.content.innerHTML = ''; - return super.setInput(input, options) + return super.setInput(input, options, token) .then(() => { return input.resolve(true); }) .then(model => { + if (token.isCancellationRequested) { + return; + } + const content = model.main.textEditorModel.getLinesContent().join('\n'); if (!strings.endsWith(input.getResource().path, '.md')) { this.content.innerHTML = content; diff --git a/src/vs/workbench/services/editor/browser/nextEditorService.ts b/src/vs/workbench/services/editor/browser/nextEditorService.ts index b5e5743236f48cd1e6bec4a85e3787359a59b241..4c212eef279c2e09804dfe9619a7fe017118bae4 100644 --- a/src/vs/workbench/services/editor/browser/nextEditorService.ts +++ b/src/vs/workbench/services/editor/browser/nextEditorService.ts @@ -366,6 +366,14 @@ export class NextEditorService extends Disposable implements INextEditorService //#endregion + //#region closeEditor() + + closeEditor(editor: IEditorInput, group: INextEditorGroup | GroupIdentifier): Thenable { + return (typeof group === 'number' ? this.nextEditorGroupsService.getGroup(group) : group).closeEditor(editor); + } + + //#endregion + //#region invokeWithinEditorContext() invokeWithinEditorContext(fn: (accessor: ServicesAccessor) => T): T { diff --git a/src/vs/workbench/services/editor/common/nextEditorService.ts b/src/vs/workbench/services/editor/common/nextEditorService.ts index c3dbb5814b046e60d85d8a715ae2bb773377a1d6..869ae052b996373454526dee6266814ac6e63aa6 100644 --- a/src/vs/workbench/services/editor/common/nextEditorService.ts +++ b/src/vs/workbench/services/editor/common/nextEditorService.ts @@ -130,6 +130,14 @@ export interface INextEditorService { */ isOpen(editor: IEditorInput | IResourceInput | IUntitledResourceInput): boolean; + /** + * Closes an editor in an editor group. + * + * @param editor the editor to close + * @param group the target group of the editor + */ + closeEditor(editor: IEditorInput, group: INextEditorGroup | GroupIdentifier): Thenable; + /** * Invoke a function in the context of the services of the active editor. */ diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 4a3e23ffe67090bd885dba0ee05e9ebceadf444b..32c321b25e6dcc485ecc63fc81e76f54c0409d03 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -19,6 +19,7 @@ import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorIn import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import URI from 'vs/base/common/uri'; import { IEditorRegistry, Extensions, EditorDescriptor } from 'vs/workbench/browser/editor'; +import { CancellationToken } from 'vs/base/common/cancellation'; const NullThemeService = new TestThemeService(); @@ -109,7 +110,7 @@ suite('Workbench BaseEditor', () => { assert(!e.isVisible()); assert(!e.input); assert(!e.options); - return e.setInput(input, options).then(() => { + return e.setInput(input, options, CancellationToken.None).then(() => { assert.strictEqual(input, e.input); assert.strictEqual(options, e.options);