Clean up some editor input debt and lifecycle issues (#24439)
This commit is contained in:
parent
ec89a599b3
commit
5ecf0cda9a
|
@ -128,12 +128,18 @@ suite('editor tests', () => {
|
|||
});
|
||||
|
||||
return Promise.all([
|
||||
commands.executeCommand('workbench.action.closeAllEditors'),
|
||||
delay(800).then(() => commands.executeCommand('workbench.action.closeAllEditors')), // TODO@Ben TODO@Joh this delay is a hack
|
||||
p
|
||||
]).then(() => undefined);
|
||||
});
|
||||
});
|
||||
|
||||
function delay(time) {
|
||||
return new Promise(function (fulfill) {
|
||||
setTimeout(fulfill, time);
|
||||
});
|
||||
}
|
||||
|
||||
test('issue #20867: vscode.window.visibleTextEditors returns closed document 2/2', () => {
|
||||
|
||||
const file10Path = join(workspace.rootPath || '', './10linefile.ts');
|
||||
|
@ -165,8 +171,11 @@ suite('editor tests', () => {
|
|||
|
||||
// hide doesn't what it means because it triggers a close event and because it
|
||||
// detached the editor. For this test that's what we want.
|
||||
editors[0].hide();
|
||||
return p;
|
||||
delay(800).then(() => { // TODO@Ben TODO@Joh this delay is a hack
|
||||
editors[0].hide();
|
||||
|
||||
return p;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ suite('workspace-namespace', () => {
|
|||
|
||||
test('openTextDocument', () => {
|
||||
let len = workspace.textDocuments.length;
|
||||
return workspace.openTextDocument(join(workspace.rootPath || '', './far.js')).then(doc => {
|
||||
return workspace.openTextDocument(join(workspace.rootPath || '', './simple.txt')).then(doc => {
|
||||
assert.ok(doc);
|
||||
assert.equal(workspace.textDocuments.length, len + 1);
|
||||
});
|
||||
|
|
1
extensions/vscode-api-tests/testWorkspace/simple.txt
Normal file
1
extensions/vscode-api-tests/testWorkspace/simple.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Just a simple file...
|
|
@ -69,6 +69,29 @@ export interface IResourceInput extends IBaseResourceInput {
|
|||
encoding?: string;
|
||||
}
|
||||
|
||||
export interface IUntitledResourceInput extends IBaseResourceInput {
|
||||
|
||||
/**
|
||||
* Optional resource. If the resource is not provided a new untitled file is created.
|
||||
*/
|
||||
resource?: URI;
|
||||
|
||||
/**
|
||||
* Optional file path. Using the file resource will associate the file to the untitled resource.
|
||||
*/
|
||||
filePath?: string;
|
||||
|
||||
/**
|
||||
* Optional language of the untitled resource.
|
||||
*/
|
||||
language?: string;
|
||||
|
||||
/**
|
||||
* Optional contents of the untitled resource.
|
||||
*/
|
||||
contents?: string;
|
||||
}
|
||||
|
||||
export interface IResourceDiffInput extends IBaseResourceInput {
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,7 +11,7 @@ import types = require('vs/base/common/types');
|
|||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { Registry } from 'vs/platform/platform';
|
||||
import { Panel } from 'vs/workbench/browser/panel';
|
||||
import { EditorInput, IFileEditorInput, EditorOptions, IEditorDescriptor, IEditorInputFactory, IEditorRegistry, Extensions } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, IEditorDescriptor, IEditorInputFactory, IEditorRegistry, Extensions, IFileInputFactory } from 'vs/workbench/common/editor';
|
||||
import { IEditor, Position, POSITIONS } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SyncDescriptor, AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
|
@ -160,7 +160,7 @@ const INPUT_DESCRIPTORS_PROPERTY = '__$inputDescriptors';
|
|||
class EditorRegistry implements IEditorRegistry {
|
||||
private editors: EditorDescriptor[];
|
||||
private instantiationService: IInstantiationService;
|
||||
private defaultFileInputDescriptor: AsyncDescriptor<IFileEditorInput>;
|
||||
private fileInputFactory: IFileInputFactory;
|
||||
private editorInputFactoryConstructors: { [editorInputId: string]: IConstructorSignature0<IEditorInputFactory> } = Object.create(null);
|
||||
private editorInputFactoryInstances: { [editorInputId: string]: IEditorInputFactory } = Object.create(null);
|
||||
|
||||
|
@ -283,12 +283,12 @@ class EditorRegistry implements IEditorRegistry {
|
|||
return inputClasses;
|
||||
}
|
||||
|
||||
public registerDefaultFileInput(editorInputDescriptor: AsyncDescriptor<IFileEditorInput>): void {
|
||||
this.defaultFileInputDescriptor = editorInputDescriptor;
|
||||
public registerFileInputFactory(factory: IFileInputFactory): void {
|
||||
this.fileInputFactory = factory;
|
||||
}
|
||||
|
||||
public getDefaultFileInput(): AsyncDescriptor<IFileEditorInput> {
|
||||
return this.defaultFileInputDescriptor;
|
||||
public getFileInputFactory(): IFileInputFactory {
|
||||
return this.fileInputFactory;
|
||||
}
|
||||
|
||||
public registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void {
|
||||
|
|
|
@ -8,7 +8,6 @@ import { Registry } from 'vs/platform/platform';
|
|||
import nls = require('vs/nls');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
|
@ -37,6 +36,7 @@ import {
|
|||
NAVIGATE_IN_GROUP_TWO_PREFIX, ShowEditorsInGroupThreeAction, NAVIGATE_IN_GROUP_THREE_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IWorkbenchEditorService } from "vs/workbench/services/editor/common/editorService";
|
||||
|
||||
// Register String Editor
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
|
@ -101,7 +101,6 @@ interface ISerializedUntitledEditorInput {
|
|||
class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
constructor(
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
) {
|
||||
}
|
||||
|
@ -127,10 +126,15 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
|||
return JSON.stringify(serialized);
|
||||
}
|
||||
|
||||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
|
||||
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
|
||||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
|
||||
return instantiationService.invokeFunction<UntitledEditorInput>(accessor => {
|
||||
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
|
||||
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
|
||||
const filePath = resource.scheme === 'file' ? resource.fsPath : void 0;
|
||||
const language = deserialized.modeId;
|
||||
|
||||
return this.untitledEditorService.createOrGet(!!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource), deserialized.modeId);
|
||||
return accessor.get(IWorkbenchEditorService).createInput({ resource, filePath, language }) as UntitledEditorInput;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import { isMacintosh } from 'vs/base/common/platform';
|
|||
import { MIME_BINARY } from 'vs/base/common/mime';
|
||||
import { shorten } from 'vs/base/common/labels';
|
||||
import { ActionRunner, IAction } from 'vs/base/common/actions';
|
||||
import { Position, IEditorInput, Verbosity } from 'vs/platform/editor/common/editor';
|
||||
import { Position, IEditorInput, Verbosity, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
@ -23,7 +23,6 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
|||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -66,7 +65,6 @@ export class TabsTitleControl extends TitleControl {
|
|||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
|
@ -143,7 +141,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
|
||||
const group = this.context;
|
||||
if (group) {
|
||||
this.editorService.openEditor(this.untitledEditorService.createOrGet(), { pinned: true, index: group.count /* always at the end */ }).done(null, errors.onUnexpectedError); // untitled are always pinned
|
||||
this.editorService.openEditor({ options: { pinned: true, index: group.count /* always at the end */ } } as IUntitledResourceInput).done(null, errors.onUnexpectedError); // untitled are always pinned
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -14,7 +14,7 @@ import { IEditor, ICommonCodeEditor, IEditorViewState, IEditorOptions as ICodeEd
|
|||
import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, Position, Verbosity } from 'vs/platform/editor/common/editor';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { SyncDescriptor, AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
|
@ -49,6 +49,10 @@ export const TEXT_DIFF_EDITOR_ID = 'workbench.editors.textDiffEditor';
|
|||
*/
|
||||
export const BINARY_DIFF_EDITOR_ID = 'workbench.editors.binaryResourceDiffEditor';
|
||||
|
||||
export interface IFileInputFactory {
|
||||
createFileInput(resource: URI, encoding: string, instantiationService: IInstantiationService): IFileEditorInput;
|
||||
}
|
||||
|
||||
export interface IEditorRegistry {
|
||||
|
||||
/**
|
||||
|
@ -79,20 +83,14 @@ export interface IEditorRegistry {
|
|||
getEditors(): IEditorDescriptor[];
|
||||
|
||||
/**
|
||||
* Registers the default input to be used for files in the workbench.
|
||||
*
|
||||
* @param editorInputDescriptor a descriptor that resolves to an instance of EditorInput that
|
||||
* should be used to handle file inputs.
|
||||
* Registers the file input factory to use for file inputs.
|
||||
*/
|
||||
registerDefaultFileInput(editorInputDescriptor: AsyncDescriptor<IFileEditorInput>): void;
|
||||
registerFileInputFactory(factory: IFileInputFactory): void;
|
||||
|
||||
/**
|
||||
* Returns a descriptor of the default input to be used for files in the workbench.
|
||||
*
|
||||
* @return a descriptor that resolves to an instance of EditorInput that should be used to handle
|
||||
* file inputs.
|
||||
* Returns the file input factory to use for file inputs.
|
||||
*/
|
||||
getDefaultFileInput(): AsyncDescriptor<IFileEditorInput>;
|
||||
getFileInputFactory(): IFileInputFactory;
|
||||
|
||||
/**
|
||||
* Registers a editor input factory for the given editor input to the registry. An editor input factory
|
||||
|
@ -329,11 +327,6 @@ export interface IFileEditorInput extends IEditorInput, IEncodingSupport {
|
|||
*/
|
||||
getResource(): URI;
|
||||
|
||||
/**
|
||||
* Sets the absolute file resource URI this input is about.
|
||||
*/
|
||||
setResource(resource: URI): void;
|
||||
|
||||
/**
|
||||
* Sets the preferred encodingt to use for this input.
|
||||
*/
|
||||
|
@ -851,7 +844,6 @@ export interface IEditorStacksModel {
|
|||
next(jumpGroups: boolean, cycleAtEnd?: boolean): IEditorIdentifier;
|
||||
previous(jumpGroups: boolean, cycleAtStart?: boolean): IEditorIdentifier;
|
||||
|
||||
isOpen(editor: IEditorInput): boolean;
|
||||
isOpen(resource: URI): boolean;
|
||||
|
||||
toString(): string;
|
||||
|
@ -869,8 +861,7 @@ export interface IEditorGroup {
|
|||
getEditor(resource: URI): IEditorInput;
|
||||
indexOf(editor: IEditorInput): number;
|
||||
|
||||
contains(editor: IEditorInput): boolean;
|
||||
contains(resource: URI): boolean;
|
||||
contains(editorOrResource: IEditorInput | URI): boolean;
|
||||
|
||||
getEditors(mru?: boolean): IEditorInput[];
|
||||
isActive(editor: IEditorInput): boolean;
|
||||
|
|
|
@ -587,14 +587,12 @@ export class EditorGroup implements IEditorGroup {
|
|||
return -1;
|
||||
}
|
||||
|
||||
public contains(candidate: EditorInput): boolean;
|
||||
public contains(resource: URI): boolean;
|
||||
public contains(arg1: any): boolean {
|
||||
if (arg1 instanceof EditorInput) {
|
||||
return this.indexOf(arg1) >= 0;
|
||||
public contains(editorOrResource: EditorInput | URI): boolean {
|
||||
if (editorOrResource instanceof EditorInput) {
|
||||
return this.indexOf(editorOrResource) >= 0;
|
||||
}
|
||||
|
||||
const counter = this.mapResourceToEditorCount.get(arg1);
|
||||
const counter = this.mapResourceToEditorCount.get(editorOrResource);
|
||||
|
||||
return typeof counter === 'number' && counter > 0;
|
||||
}
|
||||
|
@ -1174,32 +1172,28 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
|
||||
private handleOnEditorClosed(event: GroupEvent): void {
|
||||
const editor = event.editor;
|
||||
const editorsToClose = [editor];
|
||||
|
||||
// Close the editor when it is no longer open in any group
|
||||
if (!this.isOpen(editor)) {
|
||||
editor.close();
|
||||
|
||||
// Also take care of side by side editor inputs that wrap around 2 editors
|
||||
if (editor instanceof SideBySideEditorInput) {
|
||||
[editor.master, editor.details].forEach(editor => {
|
||||
if (!this.isOpen(editor)) {
|
||||
editor.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
// Include both sides of side by side editors when being closed and not opened multiple times
|
||||
if (editor instanceof SideBySideEditorInput && !this.isOpen(editor)) {
|
||||
editorsToClose.push(editor.master, editor.details);
|
||||
}
|
||||
|
||||
// Close the editor when it is no longer open in any group including diff editors
|
||||
editorsToClose.forEach(editorToClose => {
|
||||
const resource = toResource(editorToClose); // prefer resource to not close right-hand side editors of a diff editor
|
||||
if (!this.isOpen(resource || editorToClose)) {
|
||||
editorToClose.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public isOpen(resource: URI): boolean;
|
||||
public isOpen(editor: EditorInput): boolean;
|
||||
public isOpen(arg1: any): boolean {
|
||||
return this._groups.some(group => group.contains(arg1));
|
||||
public isOpen(editorOrResource: URI | EditorInput): boolean {
|
||||
return this._groups.some(group => group.contains(editorOrResource));
|
||||
}
|
||||
|
||||
public count(resource: URI): number;
|
||||
public count(editor: EditorInput): number;
|
||||
public count(arg1: any): number {
|
||||
return this._groups.filter(group => group.contains(arg1)).length;
|
||||
public count(editor: EditorInput): number {
|
||||
return this._groups.filter(group => group.contains(editor)).length;
|
||||
}
|
||||
|
||||
private onShutdown(): void {
|
||||
|
|
|
@ -20,9 +20,8 @@ export class ResourceEditorInput extends EditorInput {
|
|||
|
||||
static ID: string = 'workbench.editors.resourceEditorInput';
|
||||
|
||||
protected promise: TPromise<IReference<ResourceEditorModel>>;
|
||||
protected resource: URI;
|
||||
|
||||
private modelReference: TPromise<IReference<ResourceEditorModel>>;
|
||||
private resource: URI;
|
||||
private name: string;
|
||||
private description: string;
|
||||
|
||||
|
@ -39,54 +38,54 @@ export class ResourceEditorInput extends EditorInput {
|
|||
this.resource = resource;
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
public getTypeId(): string {
|
||||
return ResourceEditorInput.ID;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
public getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
setName(name: string): void {
|
||||
public setName(name: string): void {
|
||||
if (this.name !== name) {
|
||||
this.name = name;
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
public getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
setDescription(description: string): void {
|
||||
public setDescription(description: string): void {
|
||||
if (this.description !== description) {
|
||||
this.description = description;
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
}
|
||||
|
||||
getTelemetryDescriptor(): { [key: string]: any; } {
|
||||
public getTelemetryDescriptor(): { [key: string]: any; } {
|
||||
const descriptor = super.getTelemetryDescriptor();
|
||||
descriptor['resource'] = telemetryURIDescriptor(this.resource);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
resolve(refresh?: boolean): TPromise<ITextEditorModel> {
|
||||
if (!this.promise) {
|
||||
this.promise = this.textModelResolverService.createModelReference(this.resource);
|
||||
public resolve(refresh?: boolean): TPromise<ITextEditorModel> {
|
||||
if (!this.modelReference) {
|
||||
this.modelReference = this.textModelResolverService.createModelReference(this.resource);
|
||||
}
|
||||
|
||||
return this.promise.then(ref => {
|
||||
return this.modelReference.then(ref => {
|
||||
const model = ref.object;
|
||||
|
||||
if (!(model instanceof ResourceEditorModel)) {
|
||||
ref.dispose();
|
||||
this.promise = null;
|
||||
this.modelReference = null;
|
||||
return TPromise.wrapError(`Unexpected model for ResourceInput: ${this.resource}`); // TODO@Ben eventually also files should be supported, but we guard due to the dangerous dispose of the model in dispose()
|
||||
}
|
||||
|
||||
|
@ -94,7 +93,7 @@ export class ResourceEditorInput extends EditorInput {
|
|||
});
|
||||
}
|
||||
|
||||
matches(otherInput: any): boolean {
|
||||
public matches(otherInput: any): boolean {
|
||||
if (super.matches(otherInput) === true) {
|
||||
return true;
|
||||
}
|
||||
|
@ -109,10 +108,10 @@ export class ResourceEditorInput extends EditorInput {
|
|||
return false;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.promise) {
|
||||
this.promise.done(ref => ref.dispose());
|
||||
this.promise = null;
|
||||
public dispose(): void {
|
||||
if (this.modelReference) {
|
||||
this.modelReference.done(ref => ref.dispose());
|
||||
this.modelReference = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
|
|
|
@ -22,14 +22,13 @@ import { Builder, $ } from 'vs/base/browser/builder';
|
|||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { AutoSaveConfiguration } from 'vs/platform/files/common/files';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IWindowsService, IWindowService, IWindowSettings } from 'vs/platform/windows/common/windows';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IPath, IOpenFileRequest, IWindowConfiguration } from 'vs/workbench/electron-browser/common';
|
||||
|
@ -44,7 +43,7 @@ import { ReloadWindowAction, ToggleDevToolsAction, ShowStartupPerformance, OpenR
|
|||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { Position, IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { Position, IResourceInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
|
||||
import { Themable, EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
@ -91,8 +90,7 @@ export class ElectronWindow extends Themable {
|
|||
@IViewletService private viewletService: IViewletService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
|
@ -384,7 +382,7 @@ export class ElectronWindow extends Themable {
|
|||
}
|
||||
|
||||
private onOpenFiles(request: IOpenFileRequest): void {
|
||||
let inputs: IResourceInput[] = [];
|
||||
let inputs: IResourceInputType[] = [];
|
||||
let diffMode = (request.filesToDiff.length === 2);
|
||||
|
||||
if (!diffMode && request.filesToOpen) {
|
||||
|
@ -404,7 +402,7 @@ export class ElectronWindow extends Themable {
|
|||
}
|
||||
}
|
||||
|
||||
private openResources(resources: IResourceInput[], diffMode: boolean): TPromise<any> {
|
||||
private openResources(resources: (IResourceInput | IUntitledResourceInput)[], diffMode: boolean): TPromise<any> {
|
||||
return this.partService.joinCreation().then(() => {
|
||||
|
||||
// In diffMode we open 2 resources as diff
|
||||
|
@ -428,14 +426,15 @@ export class ElectronWindow extends Themable {
|
|||
});
|
||||
}
|
||||
|
||||
private toInputs(paths: IPath[], isNew: boolean): IResourceInput[] {
|
||||
private toInputs(paths: IPath[], isNew: boolean): IResourceInputType[] {
|
||||
return paths.map(p => {
|
||||
let input = <IResourceInput>{
|
||||
resource: isNew ? this.untitledEditorService.createOrGet(URI.file(p.filePath)).getResource() : URI.file(p.filePath),
|
||||
options: {
|
||||
pinned: true
|
||||
}
|
||||
};
|
||||
const resource = URI.file(p.filePath);
|
||||
let input: IResourceInput | IUntitledResourceInput;
|
||||
if (isNew) {
|
||||
input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput;
|
||||
} else {
|
||||
input = { resource, options: { pinned: true } } as IResourceInput;
|
||||
}
|
||||
|
||||
if (!isNew && p.lineNumber) {
|
||||
input.options.selection = {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
|
|||
import { Registry } from 'vs/platform/platform';
|
||||
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
|
||||
import { IOptions } from 'vs/workbench/common/options';
|
||||
import { Position as EditorPosition, IResourceInput, IResourceDiffInput } from 'vs/platform/editor/common/editor';
|
||||
import { Position as EditorPosition, IResourceDiffInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
|
||||
|
@ -39,7 +39,6 @@ import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbe
|
|||
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
|
||||
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
|
||||
import { getServices } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService';
|
||||
import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
|
@ -200,7 +199,6 @@ export class Workbench implements IPartService {
|
|||
options: IOptions,
|
||||
serviceCollection: ServiceCollection,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
|
@ -382,14 +380,14 @@ export class Workbench implements IPartService {
|
|||
|
||||
// Otherwise: Open/Create files
|
||||
else {
|
||||
const filesToCreateInputs: IResourceInput[] = filesToCreate.map(resourceInput => {
|
||||
return <IResourceInput>{
|
||||
resource: this.untitledEditorService.createOrGet(resourceInput.resource).getResource(),
|
||||
const filesToCreateInputs: IUntitledResourceInput[] = filesToCreate.map(resourceInput => {
|
||||
return <IUntitledResourceInput>{
|
||||
filePath: resourceInput.resource.fsPath,
|
||||
options: { pinned: true }
|
||||
};
|
||||
});
|
||||
|
||||
return TPromise.as(filesToOpen.concat(filesToCreateInputs));
|
||||
return TPromise.as([].concat(filesToOpen).concat(filesToCreateInputs));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,7 +398,7 @@ export class Workbench implements IPartService {
|
|||
return TPromise.as([]); // do not open any empty untitled file if we have backups to restore
|
||||
}
|
||||
|
||||
return TPromise.as([<IResourceInput>{ resource: this.untitledEditorService.createOrGet().getResource() }]);
|
||||
return TPromise.as([<IUntitledResourceInput>{}]);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import { IPartService } from 'vs/workbench/services/part/common/partService';
|
|||
import errors = require('vs/base/common/errors');
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Position, IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { Position, IResourceInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { ITextFileService } from "vs/workbench/services/textfile/common/textfiles";
|
||||
|
||||
export class BackupRestorer implements IWorkbenchContribution {
|
||||
|
||||
|
@ -28,7 +28,7 @@ export class BackupRestorer implements IWorkbenchContribution {
|
|||
@IPartService private partService: IPartService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IBackupFileService private backupFileService: IBackupFileService,
|
||||
@ITextModelResolverService private textModelResolverService: ITextModelResolverService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IEditorGroupService private groupService: IEditorGroupService
|
||||
) {
|
||||
this.restoreBackups();
|
||||
|
@ -68,7 +68,7 @@ export class BackupRestorer implements IWorkbenchContribution {
|
|||
backups.forEach(backup => {
|
||||
if (stacks.isOpen(backup)) {
|
||||
if (backup.scheme === 'file') {
|
||||
restorePromises.push(this.textModelResolverService.createModelReference(backup).then(null, () => unresolved.push(backup)));
|
||||
restorePromises.push(this.textFileService.models.loadOrCreate(backup).then(null, () => unresolved.push(backup)));
|
||||
} else if (backup.scheme === 'untitled') {
|
||||
restorePromises.push(this.untitledEditorService.get(backup).resolve().then(null, () => unresolved.push(backup)));
|
||||
}
|
||||
|
@ -80,30 +80,27 @@ export class BackupRestorer implements IWorkbenchContribution {
|
|||
return TPromise.join(restorePromises).then(() => unresolved, () => unresolved);
|
||||
}
|
||||
|
||||
private doOpenEditors(inputs: URI[]): TPromise<void> {
|
||||
private doOpenEditors(resources: URI[]): TPromise<void> {
|
||||
const stacks = this.groupService.getStacksModel();
|
||||
const hasOpenedEditors = stacks.groups.length > 0;
|
||||
|
||||
return TPromise.join(inputs.map(resource => this.resolveInput(resource))).then(inputs => {
|
||||
const openEditorsArgs = inputs.map((input, index) => {
|
||||
return { input, options: { pinned: true, preserveFocus: true, inactive: index > 0 || hasOpenedEditors }, position: Position.ONE };
|
||||
});
|
||||
|
||||
// Open all remaining backups as editors and resolve them to load their backups
|
||||
return this.editorService.openEditors(openEditorsArgs).then(() => void 0);
|
||||
const inputs = resources.map(resource => this.resolveInput(resource));
|
||||
const openEditorsArgs = inputs.map((input, index) => {
|
||||
return { input, options: { pinned: true, preserveFocus: true, inactive: index > 0 || hasOpenedEditors }, position: Position.ONE };
|
||||
});
|
||||
|
||||
// Open all remaining backups as editors and resolve them to load their backups
|
||||
return this.editorService.openEditors(openEditorsArgs).then(() => void 0);
|
||||
}
|
||||
|
||||
private resolveInput(resource: URI): TPromise<IResourceInput> {
|
||||
private resolveInput(resource: URI): IResourceInput | IUntitledResourceInput {
|
||||
if (resource.scheme === 'untitled' && !BackupRestorer.UNTITLED_REGEX.test(resource.fsPath)) {
|
||||
// TODO@Ben debt: instead of guessing if an untitled file has an associated file path or not
|
||||
// this information should be provided by the backup service and stored as meta data within
|
||||
return TPromise.as({
|
||||
resource: this.untitledEditorService.createOrGet(URI.file(resource.fsPath)).getResource()
|
||||
});
|
||||
return { filePath: resource.fsPath };
|
||||
}
|
||||
|
||||
return TPromise.as({ resource });
|
||||
return { resource };
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/e
|
|||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IUntitledResourceInput } from "vs/platform/editor/common/editor";
|
||||
|
||||
@editorAction
|
||||
class InspectKeyMap extends EditorAction {
|
||||
|
@ -27,11 +27,9 @@ class InspectKeyMap extends EditorAction {
|
|||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const untitledEditorService = accessor.get(IUntitledEditorService);
|
||||
|
||||
if (keybindingService instanceof WorkbenchKeybindingService) {
|
||||
const input = untitledEditorService.createOrGet(undefined, null, keybindingService.dumpDebugInfo());
|
||||
editorService.openEditor(input, { pinned: true });
|
||||
editorService.openEditor({ contents: keybindingService.dumpDebugInfo(), options: { pinned: true } } as IUntitledResourceInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,14 +37,13 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer
|
|||
import { IQuickOpenService, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { Position, IResourceInput, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { Position, IResourceInput, IEditorInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService, IConstructorSignature2, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMessageService, IMessageWithAction, IConfirmation, Severity, CancelAction } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
|
||||
import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { withFocussedFilesExplorer, revealInOSCommand, revealInExplorerCommand, copyPathCommand } from 'vs/workbench/parts/files/browser/fileCommands';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
@ -277,7 +276,6 @@ class RenameFileAction extends BaseRenameAction {
|
|||
@IFileService fileService: IFileService,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@ITextModelResolverService private textModelResolverService: ITextModelResolverService,
|
||||
@IBackupFileService private backupFileService: IBackupFileService
|
||||
) {
|
||||
super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, messageService, textFileService);
|
||||
|
@ -323,7 +321,7 @@ class RenameFileAction extends BaseRenameAction {
|
|||
|
||||
// 4.) resolve those that were dirty to load their previous dirty contents from disk
|
||||
.then(() => {
|
||||
return TPromise.join(dirtyRenamed.map(t => this.textModelResolverService.createModelReference(t)));
|
||||
return TPromise.join(dirtyRenamed.map(t => this.textFileService.models.loadOrCreate(t)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -517,16 +515,13 @@ export class GlobalNewUntitledFileAction extends Action {
|
|||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
const input = this.untitledEditorService.createOrGet();
|
||||
|
||||
return this.editorService.openEditor(input, { pinned: true }); // untitled are always pinned
|
||||
return this.editorService.openEditor({ options: { pinned: true } } as IUntitledResourceInput); // untitled are always pinned
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEdi
|
|||
import { TextFileEditor } from 'vs/workbench/parts/files/browser/editors/textFileEditor';
|
||||
import { BinaryFileEditor } from 'vs/workbench/parts/files/browser/editors/binaryFileEditor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SyncDescriptor, AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
@ -96,12 +96,12 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
|||
]
|
||||
);
|
||||
|
||||
// Register default file input handler
|
||||
// Note: because of service injection, the descriptor needs to have the exact count
|
||||
// of arguments as the FileEditorInput constructor. Otherwise when creating an
|
||||
// instance through the instantiation service he will inject the services wrong!
|
||||
const descriptor = new AsyncDescriptor<IFileEditorInput>('vs/workbench/parts/files/common/editors/fileEditorInput', 'FileEditorInput', /* DO NOT REMOVE */ void 0, /* DO NOT REMOVE */ void 0);
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerDefaultFileInput(descriptor);
|
||||
// Register default file input factory
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerFileInputFactory({
|
||||
createFileInput: (resource, encoding, instantiationService): IFileEditorInput => {
|
||||
return instantiationService.createInstance(FileEditorInput, resource, encoding);
|
||||
}
|
||||
});
|
||||
|
||||
interface ISerializedFileInput {
|
||||
resource: string;
|
||||
|
@ -145,10 +145,14 @@ class FileEditorInputFactory implements IEditorInputFactory {
|
|||
return JSON.stringify(fileInput);
|
||||
}
|
||||
|
||||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
|
||||
const fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput);
|
||||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileEditorInput {
|
||||
return instantiationService.invokeFunction<FileEditorInput>(accessor => {
|
||||
const fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput);
|
||||
const resource = !!fileInput.resourceJSON ? URI.revive(fileInput.resourceJSON) : URI.parse(fileInput.resource);
|
||||
const encoding = fileInput.encoding;
|
||||
|
||||
return instantiationService.createInstance(FileEditorInput, !!fileInput.resourceJSON ? URI.revive(fileInput.resourceJSON) : URI.parse(fileInput.resource), fileInput.encoding);
|
||||
return accessor.get(IWorkbenchEditorService).createInput({ resource, encoding }) as FileEditorInput;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
|||
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
|
@ -605,7 +604,6 @@ export class FileDragAndDrop implements IDragAndDrop {
|
|||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@ITextModelResolverService private textModelResolverService: ITextModelResolverService,
|
||||
@IBackupFileService private backupFileService: IBackupFileService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
|
@ -761,7 +759,7 @@ export class FileDragAndDrop implements IDragAndDrop {
|
|||
|
||||
// Success: load all files that are dirty again to restore their dirty contents
|
||||
// Error: discard any backups created during the process
|
||||
const onSuccess = () => TPromise.join(dirtyMoved.map(t => this.textModelResolverService.createModelReference(t)));
|
||||
const onSuccess = () => TPromise.join(dirtyMoved.map(t => this.textFileService.models.loadOrCreate(t)));
|
||||
const onError = (error?: Error, showError?: boolean) => {
|
||||
if (showError) {
|
||||
this.messageService.show(Severity.Error, error);
|
||||
|
|
|
@ -17,10 +17,11 @@ import { BINARY_FILE_EDITOR_ID, TEXT_FILE_EDITOR_ID, FILE_EDITOR_INPUT_ID } from
|
|||
import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { Verbosity } from 'vs/platform/editor/common/editor';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ITextModelResolverService } from "vs/editor/common/services/resolverService";
|
||||
|
||||
/**
|
||||
* A file editor input is the input type for the file editor of file system resources.
|
||||
|
@ -30,6 +31,8 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
private preferredEncoding: string;
|
||||
private forceOpenAsBinary: boolean;
|
||||
|
||||
private textModelReference: TPromise<IReference<TextFileEditorModel>>;
|
||||
|
||||
private name: string;
|
||||
private description: string;
|
||||
|
||||
|
@ -48,16 +51,15 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ITextModelResolverService private textModelResolverService: ITextModelResolverService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.toUnbind = [];
|
||||
|
||||
if (resource) {
|
||||
this.setResource(resource);
|
||||
this.preferredEncoding = preferredEncoding;
|
||||
}
|
||||
this.resource = resource;
|
||||
this.preferredEncoding = preferredEncoding;
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
@ -84,17 +86,6 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
}
|
||||
}
|
||||
|
||||
public setResource(resource: URI): void {
|
||||
this.resource = resource;
|
||||
|
||||
// Reset resource dependent properties
|
||||
this.name = null;
|
||||
this.description = null;
|
||||
this.shortTitle = null;
|
||||
this.mediumTitle = null;
|
||||
this.longTitle = null;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
@ -216,7 +207,18 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
}
|
||||
|
||||
// Resolve as text
|
||||
return this.textFileService.models.loadOrCreate(this.resource, this.preferredEncoding, refresh).then(null, error => {
|
||||
return this.textFileService.models.loadOrCreate(this.resource, { encoding: this.preferredEncoding, reload: refresh }).then(model => {
|
||||
|
||||
// TODO@Ben this is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary
|
||||
// or very large files do not resolve to a text file model but should be opened as binary files without text. First calling into
|
||||
// loadOrCreate ensures we are not creating model references for these kind of resources.
|
||||
// In addition we have a bit of payload to take into account (encoding, reload) that the text resolver does not handle yet.
|
||||
if (!this.textModelReference) {
|
||||
this.textModelReference = this.textModelResolverService.createModelReference(this.resource);
|
||||
}
|
||||
|
||||
return this.textModelReference.then(ref => ref.object);
|
||||
}, error => {
|
||||
|
||||
// In case of an error that indicates that the file is binary or too large, just return with the binary editor model
|
||||
if ((<IFileOperationResult>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY || (<IFileOperationResult>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) {
|
||||
|
@ -245,6 +247,12 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
|
||||
public dispose(): void {
|
||||
|
||||
// Model reference
|
||||
if (this.textModelReference) {
|
||||
this.textModelReference.done(ref => ref.dispose());
|
||||
this.textModelReference = null;
|
||||
}
|
||||
|
||||
// Listeners
|
||||
this.toUnbind = dispose(this.toUnbind);
|
||||
|
||||
|
|
|
@ -9,13 +9,15 @@ import URI from 'vs/base/common/uri';
|
|||
import { join } from 'vs/base/common/paths';
|
||||
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestTextFileService, TestEditorGroupService, createFileInput } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EncodingMode } from 'vs/workbench/common/editor';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { FileOperationResult, IFileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { Verbosity } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorGroupService } from "vs/workbench/services/group/common/groupService";
|
||||
import { IModelService } from "vs/editor/common/services/modelService";
|
||||
|
||||
function toResource(path) {
|
||||
return URI.file(join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path));
|
||||
|
@ -24,7 +26,9 @@ function toResource(path) {
|
|||
class ServiceAccessor {
|
||||
constructor(
|
||||
@IWorkbenchEditorService public editorService: IWorkbenchEditorService,
|
||||
@ITextFileService public textFileService: TestTextFileService
|
||||
@ITextFileService public textFileService: TestTextFileService,
|
||||
@IModelService public modelService: IModelService,
|
||||
@IEditorGroupService public editorGroupService: TestEditorGroupService
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -182,4 +186,29 @@ suite('Files - FileEditorInput', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('disposes model when not open anymore', function (done) {
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
||||
const input = createFileInput(instantiationService, resource);
|
||||
|
||||
input.resolve().then((model: TextFileEditorModel) => {
|
||||
const stacks = accessor.editorGroupService.getStacksModel();
|
||||
const group = stacks.openGroup('group', true);
|
||||
group.openEditor(input);
|
||||
|
||||
accessor.editorGroupService.fireChange();
|
||||
|
||||
assert.ok(!model.isDisposed());
|
||||
|
||||
group.closeEditor(input);
|
||||
accessor.editorGroupService.fireChange();
|
||||
assert.ok(model.isDisposed());
|
||||
|
||||
model.dispose();
|
||||
assert.ok(!accessor.modelService.getModel(model.getResource()));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -193,10 +193,10 @@ class EditorInputCache {
|
|||
resource = URI.file(paths.join(model.getRepositoryRoot(), indexStatus.getRename()));
|
||||
}
|
||||
|
||||
return this.editorService.createInput({ resource });
|
||||
return TPromise.as(this.editorService.createInput({ resource }));
|
||||
|
||||
case Status.BOTH_MODIFIED:
|
||||
return this.editorService.createInput({ resource });
|
||||
return TPromise.as(this.editorService.createInput({ resource }));
|
||||
|
||||
default:
|
||||
return TPromise.as(null);
|
||||
|
|
|
@ -38,7 +38,6 @@ import { IModeService } from 'vs/editor/common/services/modeService';
|
|||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
|
||||
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
@ -461,7 +460,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
|
|||
@IStorageService storageService: IStorageService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IModeService modeService: IModeService,
|
||||
|
|
|
@ -10,18 +10,19 @@ import network = require('vs/base/common/network');
|
|||
import { Registry } from 'vs/platform/platform';
|
||||
import { basename, dirname } from 'vs/base/common/paths';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { EditorInput, EditorOptions, IFileEditorInput, TextEditorOptions, IEditorRegistry, Extensions, SideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, TextEditorOptions, IEditorRegistry, Extensions, SideBySideEditorInput, IFileEditorInput, IFileInputFactory } from 'vs/workbench/common/editor';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IEditor, IResourceInput, IResourceDiffInput, IResourceSideBySideInput } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IEditor, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { AsyncDescriptor0 } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import nls = require('vs/nls');
|
||||
import { getPathLabel, IWorkspaceProvider } from 'vs/base/common/labels';
|
||||
import { ResourceMap } from "vs/base/common/map";
|
||||
import { once } from "vs/base/common/event";
|
||||
import { IEnvironmentService } from "vs/platform/environment/common/environment";
|
||||
|
||||
export interface IEditorPart {
|
||||
|
@ -37,12 +38,16 @@ export interface IEditorPart {
|
|||
getActiveEditorInput(): IEditorInput;
|
||||
}
|
||||
|
||||
type ICachedEditorInput = ResourceEditorInput | IFileEditorInput;
|
||||
|
||||
export class WorkbenchEditorService implements IWorkbenchEditorService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private static CACHE: ResourceMap<ICachedEditorInput> = new ResourceMap<ICachedEditorInput>();
|
||||
|
||||
private editorPart: IEditorPart | IWorkbenchEditorService;
|
||||
private fileInputDescriptor: AsyncDescriptor0<IFileEditorInput>;
|
||||
private fileInputFactory: IFileInputFactory;
|
||||
|
||||
constructor(
|
||||
editorPart: IEditorPart | IWorkbenchEditorService,
|
||||
|
@ -52,7 +57,7 @@ export class WorkbenchEditorService implements IWorkbenchEditorService {
|
|||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
this.editorPart = editorPart;
|
||||
this.fileInputDescriptor = Registry.as<IEditorRegistry>(Extensions.Editors).getDefaultFileInput();
|
||||
this.fileInputFactory = Registry.as<IEditorRegistry>(Extensions.Editors).getFileInputFactory();
|
||||
}
|
||||
|
||||
public getActiveEditor(): IEditor {
|
||||
|
@ -117,13 +122,12 @@ export class WorkbenchEditorService implements IWorkbenchEditorService {
|
|||
|
||||
// Untyped Text Editor Support (required for code that uses this service below workbench level)
|
||||
const textInput = <IResourceInputType>input;
|
||||
return this.createInput(textInput).then(typedInput => {
|
||||
if (typedInput) {
|
||||
return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2);
|
||||
}
|
||||
const typedInput = this.createInput(textInput);
|
||||
if (typedInput) {
|
||||
return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2);
|
||||
}
|
||||
|
||||
return TPromise.as<IEditor>(null);
|
||||
});
|
||||
return TPromise.as<IEditor>(null);
|
||||
}
|
||||
|
||||
private toOptions(arg1?: any): EditorOptions {
|
||||
|
@ -151,39 +155,36 @@ export class WorkbenchEditorService implements IWorkbenchEditorService {
|
|||
public openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise<IEditor[]>;
|
||||
public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise<IEditor[]>;
|
||||
public openEditors(editors: any[]): TPromise<IEditor[]> {
|
||||
return TPromise.join(editors.map(editor => this.createInput(editor.input))).then(inputs => {
|
||||
const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => {
|
||||
const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input);
|
||||
const inputs = editors.map(editor => this.createInput(editor.input));
|
||||
const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => {
|
||||
const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input);
|
||||
|
||||
return {
|
||||
input,
|
||||
options,
|
||||
position: editors[index].position
|
||||
};
|
||||
});
|
||||
|
||||
return this.editorPart.openEditors(typedInputs);
|
||||
return {
|
||||
input,
|
||||
options,
|
||||
position: editors[index].position
|
||||
};
|
||||
});
|
||||
|
||||
return this.editorPart.openEditors(typedInputs);
|
||||
}
|
||||
|
||||
public replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise<BaseEditor[]>;
|
||||
public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise<BaseEditor[]>;
|
||||
public replaceEditors(editors: any[], position?: Position): TPromise<BaseEditor[]> {
|
||||
return TPromise.join(editors.map(editor => this.createInput(editor.toReplace))).then(toReplaceInputs => {
|
||||
return TPromise.join(editors.map(editor => this.createInput(editor.replaceWith))).then(replaceWithInputs => {
|
||||
const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => {
|
||||
const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith);
|
||||
const toReplaceInputs = editors.map(editor => this.createInput(editor.toReplace));
|
||||
const replaceWithInputs = editors.map(editor => this.createInput(editor.replaceWith));
|
||||
const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => {
|
||||
const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith);
|
||||
|
||||
return {
|
||||
toReplace: toReplaceInputs[index],
|
||||
replaceWith: replaceWithInputs[index],
|
||||
options
|
||||
};
|
||||
});
|
||||
|
||||
return this.editorPart.replaceEditors(typedReplacements, position);
|
||||
});
|
||||
return {
|
||||
toReplace: toReplaceInputs[index],
|
||||
replaceWith: replaceWithInputs[index],
|
||||
options
|
||||
};
|
||||
});
|
||||
|
||||
return this.editorPart.replaceEditors(typedReplacements, position);
|
||||
}
|
||||
|
||||
public closeEditor(position: Position, input: IEditorInput): TPromise<void> {
|
||||
|
@ -202,51 +203,48 @@ export class WorkbenchEditorService implements IWorkbenchEditorService {
|
|||
return this.editorPart.closeAllEditors(except);
|
||||
}
|
||||
|
||||
public createInput(input: IEditorInput): TPromise<EditorInput>;
|
||||
public createInput(input: IResourceInputType): TPromise<EditorInput>;
|
||||
public createInput(input: any): TPromise<IEditorInput> {
|
||||
public createInput(input: IEditorInput): EditorInput;
|
||||
public createInput(input: IResourceInputType): EditorInput;
|
||||
public createInput(input: any): IEditorInput {
|
||||
|
||||
// Workbench Input Support
|
||||
if (input instanceof EditorInput) {
|
||||
return TPromise.as<EditorInput>(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
// Side by Side Support
|
||||
const resourceSideBySideInput = <IResourceSideBySideInput>input;
|
||||
if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) {
|
||||
return this.createInput({ resource: resourceSideBySideInput.masterResource }).then(masterInput => {
|
||||
return this.createInput({ resource: resourceSideBySideInput.detailResource }).then(detailInput => {
|
||||
return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput);
|
||||
});
|
||||
});
|
||||
const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource });
|
||||
const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource });
|
||||
|
||||
return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput);
|
||||
}
|
||||
|
||||
// Diff Editor Support
|
||||
const resourceDiffInput = <IResourceDiffInput>input;
|
||||
if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) {
|
||||
return this.createInput({ resource: resourceDiffInput.leftResource }).then(leftInput => {
|
||||
return this.createInput({ resource: resourceDiffInput.rightResource }).then(rightInput => {
|
||||
const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService);
|
||||
const leftInput = this.createInput({ resource: resourceDiffInput.leftResource });
|
||||
const rightInput = this.createInput({ resource: resourceDiffInput.rightResource });
|
||||
const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService);
|
||||
|
||||
return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput);
|
||||
});
|
||||
});
|
||||
return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput);
|
||||
}
|
||||
|
||||
// Base Text Editor Support for inmemory resources
|
||||
const resourceInput = <IResourceInput>input;
|
||||
|
||||
// Untitled file support
|
||||
if (resourceInput.resource instanceof URI && (resourceInput.resource.scheme === UntitledEditorInput.SCHEMA)) {
|
||||
return TPromise.as<EditorInput>(this.untitledEditorService.createOrGet(resourceInput.resource));
|
||||
const untitledInput = <IUntitledResourceInput>input;
|
||||
if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === UntitledEditorInput.SCHEMA)) {
|
||||
return this.untitledEditorService.createOrGet(untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource, untitledInput.language, untitledInput.contents);
|
||||
}
|
||||
|
||||
// Base Text Editor Support for file resources
|
||||
else if (this.fileInputDescriptor && resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) {
|
||||
return this.createFileInput(resourceInput.resource, resourceInput.encoding);
|
||||
const resourceInput = <IResourceInput>input;
|
||||
|
||||
// Files support
|
||||
if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) {
|
||||
return this.createOrGet(resourceInput.resource, this.instantiationService, resourceInput.label, resourceInput.description, resourceInput.encoding);
|
||||
}
|
||||
|
||||
// Treat an URI as ResourceEditorInput
|
||||
// Any other resource
|
||||
else if (resourceInput.resource instanceof URI) {
|
||||
const label = resourceInput.label || basename(resourceInput.resource.fsPath);
|
||||
let description: string;
|
||||
|
@ -256,19 +254,38 @@ export class WorkbenchEditorService implements IWorkbenchEditorService {
|
|||
description = dirname(resourceInput.resource.fsPath);
|
||||
}
|
||||
|
||||
return TPromise.as(this.instantiationService.createInstance(ResourceEditorInput, label, description, resourceInput.resource));
|
||||
return this.createOrGet(resourceInput.resource, this.instantiationService, label, description);
|
||||
}
|
||||
|
||||
return TPromise.as<EditorInput>(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
private createFileInput(resource: URI, encoding?: string): TPromise<IFileEditorInput> {
|
||||
return this.instantiationService.createInstance(this.fileInputDescriptor).then(typedFileInput => {
|
||||
typedFileInput.setResource(resource);
|
||||
typedFileInput.setPreferredEncoding(encoding);
|
||||
private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput {
|
||||
if (WorkbenchEditorService.CACHE.has(resource)) {
|
||||
const input = WorkbenchEditorService.CACHE.get(resource);
|
||||
if (input instanceof ResourceEditorInput) {
|
||||
input.setName(label);
|
||||
input.setDescription(description);
|
||||
} else {
|
||||
input.setPreferredEncoding(encoding);
|
||||
}
|
||||
|
||||
return typedFileInput;
|
||||
return input;
|
||||
}
|
||||
|
||||
let input: ICachedEditorInput;
|
||||
if (resource.scheme === network.Schemas.file) {
|
||||
input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService);
|
||||
} else {
|
||||
input = instantiationService.createInstance(ResourceEditorInput, label, description, resource);
|
||||
}
|
||||
|
||||
WorkbenchEditorService.CACHE.set(resource, input);
|
||||
once(input.onDispose)(() => {
|
||||
WorkbenchEditorService.CACHE.delete(resource);
|
||||
});
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private toDiffLabel(res1: URI, res2: URI, context: IWorkspaceProvider, environment: IEnvironmentService): string {
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorService, IEditor, IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IResourceInput, IResourceDiffInput, IResourceSideBySideInput } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorService, IEditor, IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export const IWorkbenchEditorService = createDecorator<IWorkbenchEditorService>('editorService');
|
||||
|
||||
export type IResourceInputType = IResourceInput | IResourceDiffInput | IResourceSideBySideInput;
|
||||
export type IResourceInputType = IResourceInput | IUntitledResourceInput | IResourceDiffInput | IResourceSideBySideInput;
|
||||
|
||||
/**
|
||||
* The editor service allows to open editors and work on the active
|
||||
|
@ -90,5 +90,5 @@ export interface IWorkbenchEditorService extends IEditorService {
|
|||
/**
|
||||
* Allows to resolve an untyped input to a workbench typed instanceof editor input
|
||||
*/
|
||||
createInput(input: IResourceInputType): TPromise<IEditorInput>;
|
||||
createInput(input: IResourceInputType): IEditorInput;
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { Position, Direction, IEditor } from 'vs/platform/editor/common/editor';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { EditorInput, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor';
|
||||
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
|
||||
import { StringEditorModel } from 'vs/workbench/common/editor/stringEditorModel';
|
||||
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
|
||||
import { workbenchInstantiationService, TestThemeService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/browser/editorService';
|
||||
import { UntitledEditorInput } from "vs/workbench/common/editor/untitledEditorInput";
|
||||
|
||||
let activeEditor: BaseEditor = <any>{
|
||||
getSelection: function () {
|
||||
return 'test.selection';
|
||||
}
|
||||
};
|
||||
|
||||
let openedEditorInput;
|
||||
let openedEditorOptions;
|
||||
let openedEditorPosition;
|
||||
|
||||
function toResource(path) {
|
||||
return URI.from({ scheme: 'custom', path });
|
||||
}
|
||||
|
||||
function toFileResource(path) {
|
||||
return URI.file(paths.join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path));
|
||||
}
|
||||
|
||||
class TestEditorPart implements IEditorPart {
|
||||
private activeInput;
|
||||
|
||||
public getId(): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
public openEditors(args: any[]): Promise {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: any }[]): TPromise<IEditor[]> {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
public closeEditors(position: Position, except?: EditorInput, direction?: Direction): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public closeAllEditors(except?: Position): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public closeEditor(position: Position, input: EditorInput): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public openEditor(input?: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise<BaseEditor>;
|
||||
public openEditor(input?: EditorInput, options?: EditorOptions, position?: Position): TPromise<BaseEditor>;
|
||||
public openEditor(input?: EditorInput, options?: EditorOptions, arg?: any): TPromise<BaseEditor> {
|
||||
openedEditorInput = input;
|
||||
openedEditorOptions = options;
|
||||
openedEditorPosition = arg;
|
||||
|
||||
return TPromise.as(activeEditor);
|
||||
}
|
||||
|
||||
public getActiveEditor(): BaseEditor {
|
||||
return activeEditor;
|
||||
}
|
||||
|
||||
public setActiveEditorInput(input: EditorInput) {
|
||||
this.activeInput = input;
|
||||
}
|
||||
|
||||
public getActiveEditorInput(): EditorInput {
|
||||
return this.activeInput;
|
||||
}
|
||||
|
||||
public getVisibleEditors(): IEditor[] {
|
||||
return [activeEditor];
|
||||
}
|
||||
}
|
||||
|
||||
suite('WorkbenchEditorService', () => {
|
||||
|
||||
test('basics', function () {
|
||||
let instantiationService = workbenchInstantiationService();
|
||||
|
||||
let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toFileResource.call(this, '/something.js'), void 0);
|
||||
|
||||
let testEditorPart = new TestEditorPart();
|
||||
testEditorPart.setActiveEditorInput(activeInput);
|
||||
let service: WorkbenchEditorService = <any>instantiationService.createInstance(<any>WorkbenchEditorService, testEditorPart);
|
||||
|
||||
assert.strictEqual(service.getActiveEditor(), activeEditor);
|
||||
assert.strictEqual(service.getActiveEditorInput(), activeInput);
|
||||
|
||||
// Open EditorInput
|
||||
service.openEditor(activeInput, null).then((editor) => {
|
||||
assert.strictEqual(openedEditorInput, activeInput);
|
||||
assert.strictEqual(openedEditorOptions, null);
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
assert.strictEqual(service.getVisibleEditors().length, 1);
|
||||
assert(service.getVisibleEditors()[0] === editor);
|
||||
});
|
||||
|
||||
service.openEditor(activeInput, null, Position.ONE).then((editor) => {
|
||||
assert.strictEqual(openedEditorInput, activeInput);
|
||||
assert.strictEqual(openedEditorOptions, null);
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
assert.strictEqual(service.getVisibleEditors().length, 1);
|
||||
assert(service.getVisibleEditors()[0] === editor);
|
||||
});
|
||||
|
||||
// Open Untyped Input (file)
|
||||
service.openEditor({ resource: toFileResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => {
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
|
||||
assert(openedEditorInput instanceof FileEditorInput);
|
||||
let contentInput = <FileEditorInput>openedEditorInput;
|
||||
assert.strictEqual(contentInput.getResource().fsPath, toFileResource.call(this, '/index.html').fsPath);
|
||||
|
||||
assert(openedEditorOptions instanceof TextEditorOptions);
|
||||
let textEditorOptions = <TextEditorOptions>openedEditorOptions;
|
||||
assert(textEditorOptions.hasOptionsDefined());
|
||||
});
|
||||
|
||||
// Open Untyped Input (file, encoding)
|
||||
service.openEditor({ resource: toFileResource.call(this, '/index.html'), encoding: 'utf16le', options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => {
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
|
||||
assert(openedEditorInput instanceof FileEditorInput);
|
||||
let contentInput = <FileEditorInput>openedEditorInput;
|
||||
assert.equal(contentInput.getPreferredEncoding(), 'utf16le');
|
||||
});
|
||||
|
||||
// Open Untyped Input (untitled)
|
||||
service.openEditor({ options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => {
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
|
||||
assert(openedEditorInput instanceof UntitledEditorInput);
|
||||
|
||||
assert(openedEditorOptions instanceof TextEditorOptions);
|
||||
let textEditorOptions = <TextEditorOptions>openedEditorOptions;
|
||||
assert(textEditorOptions.hasOptionsDefined());
|
||||
});
|
||||
|
||||
// Open Untyped Input (untitled with contents)
|
||||
service.openEditor({ contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => {
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
|
||||
assert(openedEditorInput instanceof UntitledEditorInput);
|
||||
|
||||
const untitledInput = openedEditorInput as UntitledEditorInput;
|
||||
untitledInput.resolve().then(model => {
|
||||
assert.equal(model.getValue(), 'Hello Untitled');
|
||||
});
|
||||
});
|
||||
|
||||
// Open Untyped Input (untitled with file path)
|
||||
service.openEditor({ filePath: '/some/path.txt', options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => {
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
|
||||
assert(openedEditorInput instanceof UntitledEditorInput);
|
||||
|
||||
const untitledInput = openedEditorInput as UntitledEditorInput;
|
||||
assert.ok(untitledInput.hasAssociatedFilePath);
|
||||
});
|
||||
|
||||
// Resolve Editor Model (Typed EditorInput)
|
||||
let input = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'hello world', 'text/plain', false);
|
||||
input.resolve(true).then((model: StringEditorModel) => {
|
||||
assert(model instanceof StringEditorModel);
|
||||
|
||||
assert(model.isResolved());
|
||||
|
||||
input.resolve().then((otherModel) => {
|
||||
assert(model === otherModel);
|
||||
|
||||
input.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('caching', function () {
|
||||
let instantiationService = workbenchInstantiationService();
|
||||
|
||||
let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toFileResource.call(this, '/something.js'), void 0);
|
||||
|
||||
let testEditorPart = new TestEditorPart();
|
||||
testEditorPart.setActiveEditorInput(activeInput);
|
||||
let service: WorkbenchEditorService = <any>instantiationService.createInstance(<any>WorkbenchEditorService, testEditorPart);
|
||||
|
||||
// Cached Input (Files)
|
||||
const fileResource1 = toFileResource.call(this, '/foo/bar/cache1.js');
|
||||
const fileInput1 = service.createInput({ resource: fileResource1 });
|
||||
assert.ok(fileInput1);
|
||||
|
||||
const fileResource2 = toFileResource.call(this, '/foo/bar/cache2.js');
|
||||
const fileInput2 = service.createInput({ resource: fileResource2 });
|
||||
assert.ok(fileInput2);
|
||||
|
||||
assert.notEqual(fileInput1, fileInput2);
|
||||
|
||||
const fileInput1Again = service.createInput({ resource: fileResource1 });
|
||||
assert.equal(fileInput1Again, fileInput1);
|
||||
|
||||
fileInput1Again.dispose();
|
||||
|
||||
assert.ok(fileInput1.isDisposed());
|
||||
|
||||
const fileInput1AgainAndAgain = service.createInput({ resource: fileResource1 });
|
||||
assert.notEqual(fileInput1AgainAndAgain, fileInput1);
|
||||
assert.ok(!fileInput1AgainAndAgain.isDisposed());
|
||||
|
||||
// Cached Input (Resource)
|
||||
const resource1 = toResource.call(this, '/foo/bar/cache1.js');
|
||||
const input1 = service.createInput({ resource: resource1 });
|
||||
assert.ok(input1);
|
||||
|
||||
const resource2 = toResource.call(this, '/foo/bar/cache2.js');
|
||||
const input2 = service.createInput({ resource: resource2 });
|
||||
assert.ok(input2);
|
||||
|
||||
assert.notEqual(input1, input2);
|
||||
|
||||
const input1Again = service.createInput({ resource: resource1 });
|
||||
assert.equal(input1Again, input1);
|
||||
|
||||
input1Again.dispose();
|
||||
|
||||
assert.ok(input1.isDisposed());
|
||||
|
||||
const input1AgainAndAgain = service.createInput({ resource: resource1 });
|
||||
assert.notEqual(input1AgainAndAgain, input1);
|
||||
assert.ok(!input1AgainAndAgain.isDisposed());
|
||||
});
|
||||
|
||||
test('delegate', function (done) {
|
||||
let instantiationService = workbenchInstantiationService();
|
||||
let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toFileResource.call(this, '/something.js'), void 0);
|
||||
|
||||
let testEditorPart = new TestEditorPart();
|
||||
testEditorPart.setActiveEditorInput(activeInput);
|
||||
|
||||
instantiationService.createInstance(<any>WorkbenchEditorService, testEditorPart);
|
||||
class MyEditor extends BaseEditor {
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, null, new TestThemeService());
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return 'myEditor';
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
|
||||
}
|
||||
|
||||
public createEditor(): any {
|
||||
|
||||
}
|
||||
}
|
||||
let ed = instantiationService.createInstance(MyEditor, 'my.editor');
|
||||
|
||||
let inp = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'hello world', 'text/plain', false);
|
||||
let delegate = instantiationService.createInstance(DelegatingWorkbenchEditorService);
|
||||
delegate.setEditorOpenHandler((input, options?) => {
|
||||
assert.strictEqual(input, inp);
|
||||
|
||||
return TPromise.as(ed);
|
||||
});
|
||||
|
||||
delegate.setEditorCloseHandler((position, input) => {
|
||||
assert.strictEqual(input, inp);
|
||||
|
||||
done();
|
||||
|
||||
return TPromise.as(void 0);
|
||||
});
|
||||
|
||||
delegate.openEditor(inp);
|
||||
delegate.closeEditor(0, inp);
|
||||
});
|
||||
});
|
|
@ -8,92 +8,16 @@
|
|||
import * as assert from 'assert';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { IEditorControl, Position, Direction, IEditor } from 'vs/platform/editor/common/editor';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { EditorInput, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor';
|
||||
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
|
||||
import { StringEditorModel } from 'vs/workbench/common/editor/stringEditorModel';
|
||||
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
|
||||
import { workbenchInstantiationService, TestThemeService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IEditorControl } from 'vs/platform/editor/common/editor';
|
||||
import { Viewlet, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { IPanel } from 'vs/workbench/common/panel';
|
||||
import { WorkbenchProgressService, ScopedService } from 'vs/workbench/services/progress/browser/progressService';
|
||||
import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/browser/editorService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
let activeViewlet: Viewlet = <any>{};
|
||||
let activeEditor: BaseEditor = <any>{
|
||||
getSelection: function () {
|
||||
return 'test.selection';
|
||||
}
|
||||
};
|
||||
|
||||
let openedEditorInput;
|
||||
let openedEditorOptions;
|
||||
let openedEditorPosition;
|
||||
|
||||
function toResource(path) {
|
||||
return URI.file(paths.join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path));
|
||||
}
|
||||
|
||||
class TestEditorPart implements IEditorPart {
|
||||
private activeInput;
|
||||
|
||||
public getId(): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
public openEditors(args: any[]): Promise {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: any }[]): TPromise<IEditor[]> {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
public closeEditors(position: Position, except?: EditorInput, direction?: Direction): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public closeAllEditors(except?: Position): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public closeEditor(position: Position, input: EditorInput): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public openEditor(input?: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise<BaseEditor>;
|
||||
public openEditor(input?: EditorInput, options?: EditorOptions, position?: Position): TPromise<BaseEditor>;
|
||||
public openEditor(input?: EditorInput, options?: EditorOptions, arg?: any): TPromise<BaseEditor> {
|
||||
openedEditorInput = input;
|
||||
openedEditorOptions = options;
|
||||
openedEditorPosition = arg;
|
||||
|
||||
return TPromise.as(activeEditor);
|
||||
}
|
||||
|
||||
public getActiveEditor(): BaseEditor {
|
||||
return activeEditor;
|
||||
}
|
||||
|
||||
public setActiveEditorInput(input: EditorInput) {
|
||||
this.activeInput = input;
|
||||
}
|
||||
|
||||
public getActiveEditorInput(): EditorInput {
|
||||
return this.activeInput;
|
||||
}
|
||||
|
||||
public getVisibleEditors(): IEditor[] {
|
||||
return [activeEditor];
|
||||
}
|
||||
}
|
||||
|
||||
class TestViewletService implements IViewletService {
|
||||
public _serviceBrand: any;
|
||||
|
@ -284,112 +208,7 @@ class TestProgressBar {
|
|||
}
|
||||
}
|
||||
|
||||
suite('Workbench UI Services', () => {
|
||||
|
||||
test('WorkbenchEditorService', function () {
|
||||
let instantiationService = workbenchInstantiationService();
|
||||
|
||||
let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/something.js'), void 0);
|
||||
|
||||
let testEditorPart = new TestEditorPart();
|
||||
testEditorPart.setActiveEditorInput(activeInput);
|
||||
let service: WorkbenchEditorService = <any>instantiationService.createInstance(<any>WorkbenchEditorService, testEditorPart);
|
||||
|
||||
assert.strictEqual(service.getActiveEditor(), activeEditor);
|
||||
assert.strictEqual(service.getActiveEditorInput(), activeInput);
|
||||
|
||||
// Open EditorInput
|
||||
service.openEditor(activeInput, null).then((editor) => {
|
||||
assert.strictEqual(openedEditorInput, activeInput);
|
||||
assert.strictEqual(openedEditorOptions, null);
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
assert.strictEqual(service.getVisibleEditors().length, 1);
|
||||
assert(service.getVisibleEditors()[0] === editor);
|
||||
});
|
||||
|
||||
service.openEditor(activeInput, null, Position.ONE).then((editor) => {
|
||||
assert.strictEqual(openedEditorInput, activeInput);
|
||||
assert.strictEqual(openedEditorOptions, null);
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
assert.strictEqual(service.getVisibleEditors().length, 1);
|
||||
assert(service.getVisibleEditors()[0] === editor);
|
||||
});
|
||||
|
||||
// Open Untyped Input
|
||||
service.openEditor({ resource: toResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } }).then((editor) => {
|
||||
assert.strictEqual(editor, activeEditor);
|
||||
|
||||
assert(openedEditorInput instanceof FileEditorInput);
|
||||
let contentInput = <FileEditorInput>openedEditorInput;
|
||||
assert.strictEqual(contentInput.getResource().fsPath, toResource.call(this, '/index.html').fsPath);
|
||||
|
||||
assert(openedEditorOptions instanceof TextEditorOptions);
|
||||
let textEditorOptions = <TextEditorOptions>openedEditorOptions;
|
||||
assert(textEditorOptions.hasOptionsDefined());
|
||||
});
|
||||
|
||||
// Resolve Editor Model (Typed EditorInput)
|
||||
let input = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'hello world', 'text/plain', false);
|
||||
input.resolve(true).then((model: StringEditorModel) => {
|
||||
assert(model instanceof StringEditorModel);
|
||||
|
||||
assert(model.isResolved());
|
||||
|
||||
input.resolve().then((otherModel) => {
|
||||
assert(model === otherModel);
|
||||
|
||||
input.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('DelegatingWorkbenchEditorService', function (done) {
|
||||
let instantiationService = workbenchInstantiationService();
|
||||
let activeInput: EditorInput = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/something.js'), void 0);
|
||||
|
||||
let testEditorPart = new TestEditorPart();
|
||||
testEditorPart.setActiveEditorInput(activeInput);
|
||||
|
||||
instantiationService.createInstance(<any>WorkbenchEditorService, testEditorPart);
|
||||
class MyEditor extends BaseEditor {
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, null, new TestThemeService());
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return 'myEditor';
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
|
||||
}
|
||||
|
||||
public createEditor(): any {
|
||||
|
||||
}
|
||||
}
|
||||
let ed = instantiationService.createInstance(MyEditor, 'my.editor');
|
||||
|
||||
let inp = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'hello world', 'text/plain', false);
|
||||
let delegate = instantiationService.createInstance(DelegatingWorkbenchEditorService);
|
||||
delegate.setEditorOpenHandler((input, options?) => {
|
||||
assert.strictEqual(input, inp);
|
||||
|
||||
return TPromise.as(ed);
|
||||
});
|
||||
|
||||
delegate.setEditorCloseHandler((position, input) => {
|
||||
assert.strictEqual(input, inp);
|
||||
|
||||
done();
|
||||
|
||||
return TPromise.as(void 0);
|
||||
});
|
||||
|
||||
delegate.openEditor(inp);
|
||||
delegate.closeEditor(0, inp);
|
||||
});
|
||||
suite('Progress Service', () => {
|
||||
|
||||
test('ScopedService', () => {
|
||||
let viewletService = new TestViewletService();
|
|
@ -9,8 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
|||
import URI from 'vs/base/common/uri';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeEvent, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeEvent, StateChange, IModelLoadOrCreateOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
|
@ -40,8 +39,7 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
|||
|
||||
constructor(
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
this.toUnbind = [];
|
||||
|
||||
|
@ -74,62 +72,10 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
|||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Editors changing/closing
|
||||
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
|
||||
this.toUnbind.push(this.editorGroupService.getStacksModel().onEditorClosed(() => this.onEditorClosed()));
|
||||
|
||||
// Lifecycle
|
||||
this.lifecycleService.onShutdown(this.dispose, this);
|
||||
}
|
||||
|
||||
private onEditorsChanged(): void {
|
||||
this.disposeUnusedModels();
|
||||
}
|
||||
|
||||
private onEditorClosed(): void {
|
||||
this.disposeUnusedModels();
|
||||
}
|
||||
|
||||
private disposeUnusedModels(): void {
|
||||
|
||||
// To not grow our text file model cache infinitly, we dispose models that
|
||||
// are not showing up in any opened editor.
|
||||
// TODO@Ben this is a workaround until we have adopted model references from
|
||||
// the resolver service (https://github.com/Microsoft/vscode/issues/17888)
|
||||
|
||||
this.getAll(void 0, model => this.canDispose(model)).forEach(model => {
|
||||
model.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private canDispose(model: ITextFileEditorModel): boolean {
|
||||
if (!model) {
|
||||
return false; // we need data!
|
||||
}
|
||||
|
||||
if (model.isDisposed()) {
|
||||
return false; // already disposed
|
||||
}
|
||||
|
||||
if (this.mapResourceToPendingModelLoaders.has(model.getResource())) {
|
||||
return false; // not yet loaded
|
||||
}
|
||||
|
||||
if (model.isDirty()) {
|
||||
return false; // not saved
|
||||
}
|
||||
|
||||
if (model.textEditorModel && model.textEditorModel.isAttachedToEditor()) {
|
||||
return false; // never dispose when attached to editor (e.g. viewzones)
|
||||
}
|
||||
|
||||
if (this.editorGroupService.getStacksModel().isOpen(model.getResource())) {
|
||||
return false; // never dispose when opened inside an editor (e.g. tabs)
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public get onModelDisposed(): Event<URI> {
|
||||
return this._onModelDisposed.event;
|
||||
}
|
||||
|
@ -213,7 +159,7 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
|||
return this.mapResourceToModel.get(resource);
|
||||
}
|
||||
|
||||
public loadOrCreate(resource: URI, encoding?: string, refresh?: boolean): TPromise<ITextFileEditorModel> {
|
||||
public loadOrCreate(resource: URI, options?: IModelLoadOrCreateOptions): TPromise<ITextFileEditorModel> {
|
||||
|
||||
// Return early if model is currently being loaded
|
||||
const pendingLoad = this.mapResourceToPendingModelLoaders.get(resource);
|
||||
|
@ -226,7 +172,7 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
|||
// Model exists
|
||||
let model = this.get(resource);
|
||||
if (model) {
|
||||
if (!refresh) {
|
||||
if (!options || !options.reload) {
|
||||
modelPromise = TPromise.as(model);
|
||||
} else {
|
||||
modelPromise = model.load();
|
||||
|
@ -235,7 +181,7 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
|||
|
||||
// Model does not exist
|
||||
else {
|
||||
model = this.instantiationService.createInstance(TextFileEditorModel, resource, encoding);
|
||||
model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : void 0);
|
||||
modelPromise = model.load();
|
||||
|
||||
// Install state change listener
|
||||
|
@ -376,6 +322,26 @@ export class TextFileEditorModelManager implements ITextFileEditorModelManager {
|
|||
this.mapResourceToModelContentChangeListener.clear();
|
||||
}
|
||||
|
||||
public disposeModel(model: TextFileEditorModel): void {
|
||||
if (!model) {
|
||||
return; // we need data!
|
||||
}
|
||||
|
||||
if (model.isDisposed()) {
|
||||
return; // already disposed
|
||||
}
|
||||
|
||||
if (this.mapResourceToPendingModelLoaders.has(model.getResource())) {
|
||||
return; // not yet loaded
|
||||
}
|
||||
|
||||
if (model.isDirty()) {
|
||||
return; // not saved
|
||||
}
|
||||
|
||||
model.dispose();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toUnbind = dispose(this.toUnbind);
|
||||
}
|
||||
|
|
|
@ -141,6 +141,11 @@ export interface IRawTextContent extends IBaseStat {
|
|||
encoding: string;
|
||||
}
|
||||
|
||||
export interface IModelLoadOrCreateOptions {
|
||||
encoding?: string;
|
||||
reload?: boolean;
|
||||
}
|
||||
|
||||
export interface ITextFileEditorModelManager {
|
||||
|
||||
onModelDisposed: Event<URI>;
|
||||
|
@ -162,7 +167,9 @@ export interface ITextFileEditorModelManager {
|
|||
|
||||
getAll(resource?: URI): ITextFileEditorModel[];
|
||||
|
||||
loadOrCreate(resource: URI, preferredEncoding?: string, refresh?: boolean): TPromise<ITextEditorModel>;
|
||||
loadOrCreate(resource: URI, options?: IModelLoadOrCreateOptions): TPromise<ITextEditorModel>;
|
||||
|
||||
disposeModel(model: ITextFileEditorModel): void;
|
||||
}
|
||||
|
||||
export interface IModelSaveOptions {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { workbenchInstantiationService, TestEditorGroupService, createFileInput, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestEditorGroupService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { onError } from 'vs/base/test/common/utils';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
|
@ -108,17 +108,17 @@ suite('Files - TextFileEditorModelManager', () => {
|
|||
const resource = URI.file('/test.html');
|
||||
const encoding = 'utf8';
|
||||
|
||||
manager.loadOrCreate(resource, encoding, true).done(model => {
|
||||
manager.loadOrCreate(resource, { encoding, reload: true }).done(model => {
|
||||
assert.ok(model);
|
||||
assert.equal(model.getEncoding(), encoding);
|
||||
assert.equal(manager.get(resource), model);
|
||||
|
||||
return manager.loadOrCreate(resource, encoding).then(model2 => {
|
||||
return manager.loadOrCreate(resource, { encoding }).then(model2 => {
|
||||
assert.equal(model2, model);
|
||||
|
||||
model.dispose();
|
||||
|
||||
return manager.loadOrCreate(resource, encoding).then(model3 => {
|
||||
return manager.loadOrCreate(resource, { encoding }).then(model3 => {
|
||||
assert.notEqual(model3, model2);
|
||||
assert.equal(manager.get(resource), model3);
|
||||
|
||||
|
@ -150,34 +150,6 @@ suite('Files - TextFileEditorModelManager', () => {
|
|||
model3.dispose();
|
||||
});
|
||||
|
||||
test('disposes model when not open anymore', function () {
|
||||
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
|
||||
|
||||
const resource = toResource('/path/index.txt');
|
||||
|
||||
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, resource, 'utf8');
|
||||
manager.add(resource, model);
|
||||
|
||||
const input = createFileInput(instantiationService, resource);
|
||||
|
||||
const stacks = accessor.editorGroupService.getStacksModel();
|
||||
const group = stacks.openGroup('group', true);
|
||||
group.openEditor(input);
|
||||
|
||||
accessor.editorGroupService.fireChange();
|
||||
|
||||
assert.ok(!model.isDisposed());
|
||||
|
||||
group.closeEditor(input);
|
||||
accessor.editorGroupService.fireChange();
|
||||
assert.ok(model.isDisposed());
|
||||
|
||||
model.dispose();
|
||||
assert.ok(!accessor.modelService.getModel(model.getResource()));
|
||||
|
||||
manager.dispose();
|
||||
});
|
||||
|
||||
test('events', function (done) {
|
||||
TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = 0;
|
||||
TextFileEditorModel.DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 0;
|
||||
|
@ -235,11 +207,11 @@ suite('Files - TextFileEditorModelManager', () => {
|
|||
disposeCounter++;
|
||||
});
|
||||
|
||||
manager.loadOrCreate(resource1, 'utf8').done(model1 => {
|
||||
manager.loadOrCreate(resource1, { encoding: 'utf8' }).done(model1 => {
|
||||
accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }]));
|
||||
accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }]));
|
||||
|
||||
return manager.loadOrCreate(resource2, 'utf8').then(model2 => {
|
||||
return manager.loadOrCreate(resource2, { encoding: 'utf8' }).then(model2 => {
|
||||
model1.textEditorModel.setValue('changed');
|
||||
model1.updatePreferredEncoding('utf16');
|
||||
|
||||
|
@ -304,8 +276,8 @@ suite('Files - TextFileEditorModelManager', () => {
|
|||
assert.equal(e[0].resource.toString(), resource1.toString());
|
||||
});
|
||||
|
||||
manager.loadOrCreate(resource1, 'utf8').done(model1 => {
|
||||
return manager.loadOrCreate(resource2, 'utf8').then(model2 => {
|
||||
manager.loadOrCreate(resource1, { encoding: 'utf8' }).done(model1 => {
|
||||
return manager.loadOrCreate(resource2, { encoding: 'utf8' }).then(model2 => {
|
||||
model1.textEditorModel.setValue('changed');
|
||||
model1.updatePreferredEncoding('utf16');
|
||||
|
||||
|
@ -342,7 +314,7 @@ suite('Files - TextFileEditorModelManager', () => {
|
|||
|
||||
const resource = toResource('/path/index_something.txt');
|
||||
|
||||
manager.loadOrCreate(resource, 'utf8').done(model => {
|
||||
manager.loadOrCreate(resource, { encoding: 'utf8' }).done(model => {
|
||||
model.dispose();
|
||||
|
||||
assert.ok(!manager.get(resource));
|
||||
|
@ -352,4 +324,25 @@ suite('Files - TextFileEditorModelManager', () => {
|
|||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
|
||||
test('dispose prevents dirty model from getting disposed', function (done) {
|
||||
const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager);
|
||||
|
||||
const resource = toResource('/path/index_something.txt');
|
||||
|
||||
manager.loadOrCreate(resource, { encoding: 'utf8' }).done((model: TextFileEditorModel) => {
|
||||
model.textEditorModel.setValue('make dirty');
|
||||
|
||||
manager.disposeModel(model);
|
||||
assert.ok(!model.isDisposed());
|
||||
|
||||
model.revert(true);
|
||||
|
||||
manager.disposeModel(model);
|
||||
assert.ok(model.isDisposed());
|
||||
|
||||
manager.dispose();
|
||||
done();
|
||||
}, error => onError(error, done));
|
||||
});
|
||||
});
|
|
@ -17,29 +17,40 @@ import network = require('vs/base/common/network');
|
|||
import { ITextModelResolverService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { TextFileEditorModel } from "vs/workbench/services/textfile/common/textFileEditorModel";
|
||||
|
||||
class ResourceModelCollection extends ReferenceCollection<TPromise<ITextEditorModel>> {
|
||||
|
||||
private providers: { [scheme: string]: ITextModelContentProvider[] } = Object.create(null);
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
createReferencedObject(key: string): TPromise<ITextEditorModel> {
|
||||
public createReferencedObject(key: string): TPromise<ITextEditorModel> {
|
||||
const resource = URI.parse(key);
|
||||
|
||||
return this.resolveTextModelContent(key)
|
||||
.then(() => this.instantiationService.createInstance(ResourceEditorModel, resource));
|
||||
if (resource.scheme === network.Schemas.file) {
|
||||
return this.textFileService.models.loadOrCreate(resource);
|
||||
}
|
||||
|
||||
return this.resolveTextModelContent(key).then(() => this.instantiationService.createInstance(ResourceEditorModel, resource));
|
||||
}
|
||||
|
||||
destroyReferencedObject(modelPromise: TPromise<ITextEditorModel>): void {
|
||||
modelPromise.done(model => model.dispose());
|
||||
public destroyReferencedObject(modelPromise: TPromise<ITextEditorModel>): void {
|
||||
modelPromise.done(model => {
|
||||
if (model instanceof TextFileEditorModel) {
|
||||
this.textFileService.models.disposeModel(model);
|
||||
} else {
|
||||
model.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable {
|
||||
public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable {
|
||||
const registry = this.providers;
|
||||
const providers = registry[scheme] || (registry[scheme] = []);
|
||||
|
||||
|
@ -98,7 +109,7 @@ export class TextModelResolverService implements ITextModelResolverService {
|
|||
this.resourceModelCollection = instantiationService.createInstance(ResourceModelCollection);
|
||||
}
|
||||
|
||||
createModelReference(resource: URI): TPromise<IReference<ITextEditorModel>> {
|
||||
public createModelReference(resource: URI): TPromise<IReference<ITextEditorModel>> {
|
||||
const uri = resource.toString();
|
||||
let promise = this.promiseCache[uri];
|
||||
|
||||
|
@ -112,12 +123,6 @@ export class TextModelResolverService implements ITextModelResolverService {
|
|||
}
|
||||
|
||||
private _createModelReference(resource: URI): TPromise<IReference<ITextEditorModel>> {
|
||||
// File Schema: use text file service
|
||||
// TODO ImmortalReference is a hack
|
||||
if (resource.scheme === network.Schemas.file) {
|
||||
return this.textFileService.models.loadOrCreate(resource)
|
||||
.then(model => new ImmortalReference(model));
|
||||
}
|
||||
|
||||
// Untitled Schema: go through cached input
|
||||
// TODO ImmortalReference is a hack
|
||||
|
@ -144,12 +149,13 @@ export class TextModelResolverService implements ITextModelResolverService {
|
|||
model => ({ object: model, dispose: () => ref.dispose() }),
|
||||
err => {
|
||||
ref.dispose();
|
||||
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable {
|
||||
public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable {
|
||||
return this.resourceModelCollection.registerTextModelContentProvider(scheme, provider);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textF
|
|||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
import { once } from "vs/base/common/event";
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
|
@ -74,6 +75,14 @@ suite('Workbench - TextModelResolverService', () => {
|
|||
assert.ok(model);
|
||||
assert.equal(model.getValue(), 'Hello Test');
|
||||
|
||||
let disposed = false;
|
||||
once(model.onDispose)(() => {
|
||||
disposed = true;
|
||||
});
|
||||
|
||||
input.dispose();
|
||||
assert.equal(disposed, true);
|
||||
|
||||
dispose.dispose();
|
||||
done();
|
||||
});
|
||||
|
@ -90,7 +99,14 @@ suite('Workbench - TextModelResolverService', () => {
|
|||
|
||||
assert.ok(editorModel);
|
||||
assert.equal(editorModel.getValue(), 'Hello Html');
|
||||
|
||||
let disposed = false;
|
||||
once(model.onDispose)(() => {
|
||||
disposed = true;
|
||||
});
|
||||
|
||||
ref.dispose();
|
||||
assert.equal(disposed, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn
|
|||
import { IFilesConfiguration } from 'vs/platform/files/common/files';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import Event, { Emitter, once } from 'vs/base/common/event';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
|
||||
export const IUntitledEditorService = createDecorator<IUntitledEditorService>('untitledEditorService');
|
||||
|
||||
|
@ -82,8 +83,8 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private static CACHE: { [resource: string]: UntitledEditorInput } = Object.create(null);
|
||||
private static KNOWN_ASSOCIATED_FILE_PATHS: { [resource: string]: boolean } = Object.create(null);
|
||||
private static CACHE: ResourceMap<UntitledEditorInput> = new ResourceMap<UntitledEditorInput>();
|
||||
private static KNOWN_ASSOCIATED_FILE_PATHS: ResourceMap<boolean> = new ResourceMap<boolean>();
|
||||
|
||||
private _onDidChangeContent: Emitter<URI>;
|
||||
private _onDidChangeDirty: Emitter<URI>;
|
||||
|
@ -117,15 +118,15 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
}
|
||||
|
||||
public get(resource: URI): UntitledEditorInput {
|
||||
return UntitledEditorService.CACHE[resource.toString()];
|
||||
return UntitledEditorService.CACHE.get(resource);
|
||||
}
|
||||
|
||||
public getAll(resources?: URI[]): UntitledEditorInput[] {
|
||||
if (resources) {
|
||||
return arrays.coalesce(resources.map((r) => this.get(r)));
|
||||
return arrays.coalesce(resources.map(r => this.get(r)));
|
||||
}
|
||||
|
||||
return Object.keys(UntitledEditorService.CACHE).map((key) => UntitledEditorService.CACHE[key]);
|
||||
return UntitledEditorService.CACHE.values();
|
||||
}
|
||||
|
||||
public revertAll(resources?: URI[], force?: boolean): URI[] {
|
||||
|
@ -151,10 +152,9 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
}
|
||||
|
||||
public getDirty(): URI[] {
|
||||
return Object.keys(UntitledEditorService.CACHE)
|
||||
.map((key) => UntitledEditorService.CACHE[key])
|
||||
.filter((i) => i.isDirty())
|
||||
.map((i) => i.getResource());
|
||||
return UntitledEditorService.CACHE.values()
|
||||
.filter(i => i.isDirty())
|
||||
.map(i => i.getResource());
|
||||
}
|
||||
|
||||
public createOrGet(resource?: URI, modeId?: string, initialValue?: string): UntitledEditorInput {
|
||||
|
@ -164,13 +164,13 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
resource = this.resourceToUntitled(resource); // ensure we have the right scheme
|
||||
|
||||
if (hasAssociatedFilePath) {
|
||||
UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS[resource.toString()] = true; // remember for future lookups
|
||||
UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS.set(resource, true); // remember for future lookups
|
||||
}
|
||||
}
|
||||
|
||||
// Return existing instance if asked for it
|
||||
if (resource && UntitledEditorService.CACHE[resource.toString()]) {
|
||||
return UntitledEditorService.CACHE[resource.toString()];
|
||||
if (resource && UntitledEditorService.CACHE.has(resource)) {
|
||||
return UntitledEditorService.CACHE.get(resource);
|
||||
}
|
||||
|
||||
// Create new otherwise
|
||||
|
@ -181,11 +181,11 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
if (!resource) {
|
||||
|
||||
// Create new taking a resource URI that is not already taken
|
||||
let counter = Object.keys(UntitledEditorService.CACHE).length + 1;
|
||||
let counter = UntitledEditorService.CACHE.size + 1;
|
||||
do {
|
||||
resource = URI.from({ scheme: UntitledEditorInput.SCHEMA, path: `Untitled-${counter}` });
|
||||
counter++;
|
||||
} while (Object.keys(UntitledEditorService.CACHE).indexOf(resource.toString()) >= 0);
|
||||
} while (UntitledEditorService.CACHE.has(resource));
|
||||
}
|
||||
|
||||
// Look up default language from settings if any
|
||||
|
@ -217,8 +217,8 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
// Remove from cache on dispose
|
||||
const onceDispose = once(input.onDispose);
|
||||
onceDispose(() => {
|
||||
delete UntitledEditorService.CACHE[input.getResource().toString()];
|
||||
delete UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS[input.getResource().toString()];
|
||||
UntitledEditorService.CACHE.delete(input.getResource());
|
||||
UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS.delete(input.getResource());
|
||||
contentListener.dispose();
|
||||
dirtyListener.dispose();
|
||||
encodingListener.dispose();
|
||||
|
@ -226,7 +226,7 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
});
|
||||
|
||||
// Add to cache
|
||||
UntitledEditorService.CACHE[resource.toString()] = input;
|
||||
UntitledEditorService.CACHE.set(resource, input);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ export class UntitledEditorService implements IUntitledEditorService {
|
|||
}
|
||||
|
||||
public hasAssociatedFilePath(resource: URI): boolean {
|
||||
return !!UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS[resource.toString()];
|
||||
return UntitledEditorService.KNOWN_ASSOCIATED_FILE_PATHS.has(resource);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
|
|
@ -145,8 +145,6 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
return other && this.id === other.id && other instanceof TestFileEditorInput;
|
||||
}
|
||||
|
||||
public setResource(r: URI): void {
|
||||
}
|
||||
|
||||
public setEncoding(encoding: string) {
|
||||
}
|
||||
|
@ -1568,11 +1566,10 @@ suite('Editor Stacks Model', () => {
|
|||
|
||||
assert.ok(model.isOpen(input1Resource));
|
||||
assert.ok(group1.contains(input1Resource));
|
||||
assert.equal(model.count(input1Resource), 1);
|
||||
assert.equal(model.count(input1), 1);
|
||||
assert.equal(group1.getEditor(input1Resource), input1);
|
||||
|
||||
assert.ok(!group1.getEditor(input1ResourceUpper));
|
||||
assert.equal(model.count(input1ResourceUpper), 0);
|
||||
assert.ok(!model.isOpen(input1ResourceUpper));
|
||||
assert.ok(!group1.contains(input1ResourceUpper));
|
||||
|
||||
|
@ -1585,7 +1582,7 @@ suite('Editor Stacks Model', () => {
|
|||
assert.ok(!group1.getEditor(input1ResourceUpper));
|
||||
assert.ok(group2.contains(input1Resource));
|
||||
assert.equal(group2.getEditor(input1Resource), input1);
|
||||
assert.equal(model.count(input1Resource), 1);
|
||||
assert.equal(model.count(input1), 1);
|
||||
|
||||
const input1ResourceClone = URI.file('/hello/world.txt');
|
||||
const input1Clone = input(void 0, false, input1ResourceClone);
|
||||
|
@ -1733,6 +1730,72 @@ suite('Editor Stacks Model', () => {
|
|||
assert.equal(input1.isDisposed(), false);
|
||||
});
|
||||
|
||||
test('Stack - Multiple Editors - Editor Disposed on Close (same input, files)', function () {
|
||||
const model = create();
|
||||
|
||||
const group1 = model.openGroup('group1');
|
||||
const group2 = model.openGroup('group2');
|
||||
|
||||
const input1 = input(void 0, void 0, URI.file('/hello/world.txt'));
|
||||
|
||||
group1.openEditor(input1, { pinned: true, active: true });
|
||||
group2.openEditor(input1, { pinned: true, active: true });
|
||||
|
||||
group2.closeEditor(input1);
|
||||
assert.equal(input1.isDisposed(), false);
|
||||
|
||||
group1.closeEditor(input1);
|
||||
assert.equal(input1.isDisposed(), true);
|
||||
});
|
||||
|
||||
test('Stack - Multiple Editors - Editor Disposed on Close (same input, files, diff)', function () {
|
||||
const model = create();
|
||||
|
||||
const group1 = model.openGroup('group1');
|
||||
const group2 = model.openGroup('group2');
|
||||
|
||||
const input1 = input(void 0, void 0, URI.file('/hello/world.txt'));
|
||||
const input2 = input(void 0, void 0, URI.file('/hello/world_other.txt'));
|
||||
|
||||
const diffInput = new DiffEditorInput('name', 'description', input2, input1);
|
||||
|
||||
group1.openEditor(input1, { pinned: true, active: true });
|
||||
group2.openEditor(diffInput, { pinned: true, active: true });
|
||||
|
||||
group1.closeEditor(input1);
|
||||
assert.equal(input1.isDisposed(), false);
|
||||
assert.equal(input2.isDisposed(), false);
|
||||
assert.equal(diffInput.isDisposed(), false);
|
||||
|
||||
group2.closeEditor(diffInput);
|
||||
assert.equal(input1.isDisposed(), true);
|
||||
assert.equal(input2.isDisposed(), true);
|
||||
assert.equal(diffInput.isDisposed(), true);
|
||||
});
|
||||
|
||||
test('Stack - Multiple Editors - Editor Disposed on Close (same input, files, diff, close diff)', function () {
|
||||
const model = create();
|
||||
|
||||
const group1 = model.openGroup('group1');
|
||||
const group2 = model.openGroup('group2');
|
||||
|
||||
const input1 = input(void 0, void 0, URI.file('/hello/world.txt'));
|
||||
const input2 = input(void 0, void 0, URI.file('/hello/world_other.txt'));
|
||||
|
||||
const diffInput = new DiffEditorInput('name', 'description', input2, input1);
|
||||
|
||||
group1.openEditor(input1, { pinned: true, active: true });
|
||||
group2.openEditor(diffInput, { pinned: true, active: true });
|
||||
|
||||
group2.closeEditor(diffInput);
|
||||
assert.equal(input1.isDisposed(), false);
|
||||
assert.equal(input2.isDisposed(), true);
|
||||
assert.equal(diffInput.isDisposed(), true);
|
||||
|
||||
group1.closeEditor(input1);
|
||||
assert.equal(input1.isDisposed(), true);
|
||||
});
|
||||
|
||||
test('Stack - Multiple Editors - Editor Emits Dirty and Label Changed', function () {
|
||||
const model = create();
|
||||
|
||||
|
|
|
@ -568,8 +568,8 @@ export class TestEditorService implements IWorkbenchEditorService {
|
|||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public createInput(input: IResourceInput): TPromise<IEditorInput> {
|
||||
return TPromise.as(null);
|
||||
public createInput(input: IResourceInput): IEditorInput {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue