提交 c0f3c664 编写于 作者: B Benjamin Pasero

grid - add and adopt setInput() cancellation tokens

上级 0f151ee4
......@@ -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<IProgressService>('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
......@@ -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<void> {
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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<void>;
create(parent: HTMLElement): TPromise<void> {
......
......@@ -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<void> {
// Return early for same input unless we force to open
const forceOpen = options && options.forceOpen;
if (!forceOpen && input.matches(this.input)) {
return TPromise.wrap<void>(null);
}
// Otherwise set input and resolve
return super.setInput(input, options).then(() => {
public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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<void>(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<void>(null);
return void 0;
});
});
}
......
......@@ -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 {
......
......@@ -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<void> {
public setInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
const oldInput = <SideBySideEditorInput>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(<EditorInput>newInput.details, this.detailsEditorContainer);
const masterEditor = this._createEditor(<EditorInput>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<void> {
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
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 {
......
......@@ -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<void> {
// 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 = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(<IDiffEditor>this.getControl(), ScrollType.Smooth);
}
return TPromise.wrap<void>(null);
}
public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
// 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 = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(<IDiffEditor>this.getControl(), ScrollType.Smooth);
}
}
public supportsCenteredLayout(): boolean {
return false;
}
......
......@@ -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<void> {
return super.setInput(input, options).then(() => {
public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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)
......
......@@ -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<void> {
// 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 = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
}
return TPromise.wrap<void>(null);
}
public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
// 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<void>(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 = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
}
}
protected getConfigurationOverrides(): IEditorOptions {
const options = super.getConfigurationOverrides();
......
......@@ -118,16 +118,23 @@ export class NextEditorControl extends Disposable {
private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): Thenable<boolean> {
// 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);
......
......@@ -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) {
......
......@@ -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<boolean>('extensionEditorWebviewFocus', undefined);
......@@ -262,7 +263,7 @@ export class ExtensionEditor extends BaseEditor {
this.content = append(body, $('.content'));
}
setInput(input: ExtensionsInput, options: EditorOptions): TPromise<void> {
setInput(input: ExtensionsInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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 {
......
......@@ -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<void> {
// 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((<TextEditorOptions>options).apply)) {
(<TextEditorOptions>options).apply(this.getControl(), ScrollType.Smooth);
}
return TPromise.wrap<void>(null);
public setOptions(options: EditorOptions): void {
const textOptions = <TextEditorOptions>options;
if (textOptions && types.isFunction(textOptions.apply)) {
textOptions.apply(this.getControl(), ScrollType.Smooth);
}
}
public setInput(input: FileEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
// 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
}
......
......@@ -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<void> {
public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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<void>(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;
}
......
......@@ -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<void> {
public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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 {
......
......@@ -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<void> {
private onDidPanelOpen(panel: IPanel): Thenable<void> {
if (panel && panel.getId() === OUTPUT_PANEL_ID) {
this._outputPanel = <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<void> {
private doShowChannel(channel: IOutputChannel, preserveFocus: boolean): Thenable<void> {
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();
......
......@@ -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<void>(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<void> {
const oldInput = this.input;
return super.setInput(input)
setInput(input: KeybindingsEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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<any> {
private render(preserveFocus: boolean, token: CancellationToken): TPromise<any> {
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);
}
......
......@@ -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<void> {
public setInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
this.defaultSettingsEditorContextKey.set(true);
const oldInput = <PreferencesEditorInput>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<void> {
return this.sideBySidePreferencesWidget.setInput(<DefaultPreferencesEditorInput>newInput.details, <EditorInput>newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => {
private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
return this.sideBySidePreferencesWidget.setInput(<DefaultPreferencesEditorInput>newInput.details, <EditorInput>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<ISetting>, editablePreferencesRenderer: IPreferencesRenderer<ISetting> }> {
public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer<ISetting>, editablePreferencesRenderer: IPreferencesRenderer<ISetting> }> {
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((<DefaultSettingsEditorModel>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<IPreferencesRenderer<ISetting>> {
return editor.setInput(input, options)
.then(() => (<CodeEditorWidget>editor.getControl()).getContribution<ISettingsEditorContribution>(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri));
private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions, token: CancellationToken): Thenable<IPreferencesRenderer<ISetting>> {
return editor.setInput(input, options, token)
.then(() => {
if (token.isCancellationRequested) {
return void 0;
}
return (<CodeEditorWidget>editor.getControl()).getContribution<ISettingsEditorContribution>(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<void> {
return super.setInput(input, options)
setInput(input: DefaultPreferencesEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
return super.setInput(input, options, token)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
.then(editorModel => {
if (token.isCancellationRequested) {
return void 0;
}
return editorModel.load();
})
.then(editorModel => {
if (token.isCancellationRequested) {
return void 0;
}
this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel);
}));
}
public clearInput(): void {
......
......@@ -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<void> {
const oldInput = this.input;
return super.setInput(input)
setInput(input: SettingsEditor2Input, options: EditorOptions, token: CancellationToken): Thenable<void> {
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<any> {
private render(token: CancellationToken): TPromise<any> {
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);
});
}
......
......@@ -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<void> {
if (this.input && this.input.matches(input)) {
return undefined;
}
async setInput(input: WebviewEditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
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);
}
......
......@@ -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<boolean>('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<void> {
if (this.input instanceof WalkThroughInput && this.input.matches(input)) {
return TPromise.as(undefined);
}
setInput(input: WalkThroughInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
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;
......
......@@ -366,6 +366,14 @@ export class NextEditorService extends Disposable implements INextEditorService
//#endregion
//#region closeEditor()
closeEditor(editor: IEditorInput, group: INextEditorGroup | GroupIdentifier): Thenable<void> {
return (typeof group === 'number' ? this.nextEditorGroupsService.getGroup(group) : group).closeEditor(editor);
}
//#endregion
//#region invokeWithinEditorContext()
invokeWithinEditorContext<T>(fn: (accessor: ServicesAccessor) => T): T {
......
......@@ -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<void>;
/**
* Invoke a function in the context of the services of the active editor.
*/
......
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册