From 40fdd128202257342368303d85f6fceae2f02a05 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 7 Oct 2016 07:46:15 +0200 Subject: [PATCH] history: avoid typed input for editor history (for #13283) --- .../browser/parts/editor/editorActions.ts | 18 ++- .../parts/quickopen/quickOpenController.ts | 60 +++++++--- .../services/history/browser/history.ts | 106 ++++++++---------- .../services/history/common/history.ts | 4 +- 4 files changed, 105 insertions(+), 83 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 52dd1060b64..a70c39c2fdc 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -190,9 +190,13 @@ export class FocusFirstGroupAction extends Action { const history = this.historyService.getHistory(); for (let input of history) { - // For now only support to open resources from history to the side - if (!!getUntitledOrFileResource(input)) { - return this.editorService.openEditor(input, null, Position.LEFT); + // For now only support to open files from history to the side + if (input instanceof EditorInput) { + if (!!getUntitledOrFileResource(input)) { + return this.editorService.openEditor(input, null, Position.LEFT); + } + } else { + return this.editorService.openEditor(input as IResourceInput, Position.LEFT); } } @@ -259,8 +263,12 @@ export abstract class BaseFocusSideGroupAction extends Action { for (let input of history) { // For now only support to open files from history to the side - if (!!getUntitledOrFileResource(input)) { - return this.editorService.openEditor(input, { pinned: true }, this.getTargetEditorSide()); + if (input instanceof EditorInput) { + if (!!getUntitledOrFileResource(input)) { + return this.editorService.openEditor(input, { pinned: true }, this.getTargetEditorSide()); + } + } else { + return this.editorService.openEditor({ resource: (input as IResourceInput).resource, options: { pinned: true } }, this.getTargetEditorSide()); } } } diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index bc81a6712e2..8a20b5da372 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -24,7 +24,9 @@ import {QuickOpenWidget, HideReason} from 'vs/base/parts/quickopen/browser/quick import {ContributableActionProvider} from 'vs/workbench/browser/actionBarRegistry'; import labels = require('vs/base/common/labels'); import paths = require('vs/base/common/paths'); +import {ITextFileService} from 'vs/workbench/services/textfile/common/textfiles'; import {Registry} from 'vs/platform/platform'; +import {IResourceInput, IEditorInput} from 'vs/platform/editor/common/editor'; import {IModeService} from 'vs/editor/common/services/modeService'; import {getIconClasses} from 'vs/workbench/browser/labels'; import {IModelService} from 'vs/editor/common/services/modelService'; @@ -744,14 +746,28 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe const results: QuickOpenEntry[] = []; history.forEach(input => { - const resource = getUntitledOrFileResource(input); + let resource: URI; + if (input instanceof EditorInput) { + resource = getUntitledOrFileResource(input); + } else { + resource = (input as IResourceInput).resource; + } + if (!resource) { return; //For now, only support to match on inputs that provide resource information } + let searchTargetToMatch: string; + if (searchInPath) { + searchTargetToMatch = labels.getPathLabel(resource, this.contextService); + } else if (input instanceof EditorInput) { + searchTargetToMatch = input.getName(); + } else { + searchTargetToMatch = paths.basename((input as IResourceInput).resource.fsPath); + } + // Check if this entry is a match for the search value - const targetToMatch = searchInPath ? labels.getPathLabel(resource, this.contextService) : input.getName(); - if (!filters.matchesFuzzy(searchValue, targetToMatch)) { + if (!filters.matchesFuzzy(searchValue, searchTargetToMatch)) { return; } @@ -1018,28 +1034,42 @@ export class EditorHistoryEntryGroup extends QuickOpenEntryGroup { } export class EditorHistoryEntry extends EditorQuickOpenEntry { - private input: EditorInput; + private input: IEditorInput | IResourceInput; private resource: URI; + private label: string; + private description: string; constructor( - input: EditorInput, + input: IEditorInput | IResourceInput, @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, + @ITextFileService private textFileService: ITextFileService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService ) { super(editorService); this.input = input; - this.resource = getUntitledOrFileResource(input); + + if (input instanceof EditorInput) { + this.resource = getUntitledOrFileResource(input); + this.label = input.getName(); + this.description = input.getDescription(); + } else { + const resourceInput = input as IResourceInput; + this.resource = resourceInput.resource; + this.label = paths.basename(resourceInput.resource.fsPath); + this.description = labels.getPathLabel(paths.dirname(this.resource.fsPath), contextService); + } } public getIcon(): string { - return this.input.isDirty() ? 'dirty' : ''; + return this.resource && this.textFileService.isDirty(this.resource) ? 'dirty' : ''; } public getLabel(): string { - return this.input.getName(); + return this.label; } public getLabelOptions(): IIconLabelOptions { @@ -1053,27 +1083,27 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { } public getDescription(): string { - return this.input.getDescription(); + return this.description; } public getResource(): URI { return this.resource; } - public getInput(): EditorInput { + public getInput(): IEditorInput | IResourceInput { return this.input; } - public matches(input: EditorInput): boolean { - return this.input.matches(input); - } - public run(mode: Mode, context: IEntryRunContext): boolean { if (mode === Mode.OPEN) { const sideBySide = !context.quickNavigateConfiguration && context.keymods.indexOf(KeyMod.CtrlCmd) >= 0; const pinned = !this.configurationService.getConfiguration().workbench.editor.enablePreviewFromQuickOpen; - this.editorService.openEditor(this.input, { pinned }, sideBySide).done(null, errors.onUnexpectedError); + if (this.input instanceof EditorInput) { + this.editorService.openEditor(this.input, { pinned }, sideBySide).done(null, errors.onUnexpectedError); + } else { + this.editorService.openEditor({ resource: (this.input as IResourceInput).resource, options: { pinned: true } }, sideBySide); + } return true; } diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index e0ff2ef18e5..d21eae90c8a 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -70,11 +70,15 @@ export class EditorState { } } -interface ISerializedEditorInput { +interface ILegacySerializedEditorInput { id: string; value: string; } +interface ISerializedFileEditorInput { + resource: string; +} + export abstract class BaseHistoryService { protected toUnbind: IDisposable[]; @@ -246,7 +250,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic private blockStackChanges: boolean; private currentFileEditorState: EditorState; - private history: IEditorInput[]; + private history: (IEditorInput|IResourceInput)[]; private recentlyClosedFiles: IRecentlyClosedFile[]; private loaded: boolean; private registry: IEditorRegistry; @@ -351,8 +355,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic if (entry.input instanceof EditorInput) { openEditorPromise = this.editorService.openEditor(entry.input, options); } else { - const resourceInput = entry.input as IResourceInput; - openEditorPromise = this.editorService.openEditor({ resource: resourceInput.resource, options }); + openEditorPromise = this.editorService.openEditor({ resource: (entry.input as IResourceInput).resource, options }); } openEditorPromise.done(() => { @@ -382,37 +385,24 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic this.ensureLoaded(); + const historyInput = this.preferResourceInput(input); + // Remove any existing entry and add to the beginning this.removeFromHistory(input); - this.history.unshift(input); + this.history.unshift(historyInput); // Respect max entries setting if (this.history.length > HistoryService.MAX_HISTORY_ITEMS) { this.history.pop(); } - // Restore on dispose - const onceDispose = once(input.onDispose); - onceDispose(() => { - this.restoreInHistory(input); - }); - } - - private restoreInHistory(input: IEditorInput): void { - const index = this.indexOf(input); - if (index < 0) { - return; - } - - // Using the factory we try to recreate the input - const restoredInput = this.restoreInput(input); - if (restoredInput) { - this.history[index] = restoredInput; - } - - // Factory failed, just remove entry then - else { - this.removeFromHistory(input, index); + // Remove this from the history unless the history input is a resource + // that can easily be restored even when the input gets disposed + if (historyInput instanceof EditorInput) { + const onceDispose = once(historyInput.onDispose); + onceDispose(() => { + this.removeFromHistory(input); + }); } } @@ -437,7 +427,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic private indexOf(input: IEditorInput): number { for (let i = 0; i < this.history.length; i++) { const entry = this.history[i]; - if (entry.matches(input)) { + if (this.matches(input, entry)) { return i; } } @@ -577,20 +567,6 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return s1.startLineNumber === s2.startLineNumber; // we consider the history entry same if we are on the same line } - private restoreInput(input: IEditorInput): EditorInput { - if (input instanceof EditorInput) { - const factory = this.registry.getEditorInputFactory(input.getTypeId()); - if (factory) { - const inputRaw = factory.serialize(input); - if (inputRaw) { - return factory.deserialize(this.instantiationService, inputRaw); - } - } - } - - return null; - } - private removeFromStack(input: IEditorInput): void { this.stack.forEach((e, i) => { if (this.matches(input, e.input)) { @@ -627,9 +603,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return input.matches(typedInput); } - const resourceInput = input as IResourceInput; - - return this.matchesFile(resourceInput.resource, typedInput); + return this.matchesFile((input as IResourceInput).resource, typedInput); } private matchesFile(resource: URI, input: IEditorInput): boolean { @@ -638,7 +612,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return fileInput && fileInput.getResource().toString() === resource.toString(); } - public getHistory(): IEditorInput[] { + public getHistory(): (IEditorInput|IResourceInput)[] { this.ensureLoaded(); return this.history.slice(0); @@ -657,35 +631,45 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return; // nothing to save because history was not used } - const entries: ISerializedEditorInput[] = this.history.map((input: EditorInput) => { - const factory = this.registry.getEditorInputFactory(input.getTypeId()); - if (factory) { - const value = factory.serialize(input); - if (typeof value === 'string') { - return { - id: input.getTypeId(), - value: value - }; - } + const entries: ISerializedFileEditorInput[] = this.history.map(input => { + if (input instanceof EditorInput) { + return void 0; // only file resource inputs are serializable currently } - return void 0; + return { resource: (input as IResourceInput).resource.toString() }; }).filter(serialized => !!serialized); this.storageService.store(HistoryService.STORAGE_KEY, JSON.stringify(entries), StorageScope.WORKSPACE); } private load(): void { - let entries: ISerializedEditorInput[] = []; + let entries: (ILegacySerializedEditorInput|ISerializedFileEditorInput)[] = []; + const entriesRaw = this.storageService.get(HistoryService.STORAGE_KEY, StorageScope.WORKSPACE); if (entriesRaw) { entries = JSON.parse(entriesRaw); } this.history = entries.map(entry => { - const factory = this.registry.getEditorInputFactory(entry.id); - if (factory && typeof entry.value === 'string') { - return factory.deserialize(this.instantiationService, entry.value); + const serializedLegacyInput = entry as ILegacySerializedEditorInput; + const serializedFileInput = entry as ISerializedFileEditorInput; + + // Legacy support (TODO@Ben remove me - migration) + if (serializedLegacyInput.id) { + const factory = this.registry.getEditorInputFactory(serializedLegacyInput.id); + if (factory && typeof serializedLegacyInput.value === 'string') { + const fileInput = asFileEditorInput(factory.deserialize(this.instantiationService, serializedLegacyInput.value)); + if (fileInput) { + return { resource: fileInput.getResource() } as IResourceInput; + } + + return void 0; + } + } + + // New resource input support + else if (serializedFileInput.resource) { + return { resource: URI.parse(serializedFileInput.resource) } as IResourceInput; } return void 0; diff --git a/src/vs/workbench/services/history/common/history.ts b/src/vs/workbench/services/history/common/history.ts index 67d99afb0f9..968445aab09 100644 --- a/src/vs/workbench/services/history/common/history.ts +++ b/src/vs/workbench/services/history/common/history.ts @@ -5,7 +5,7 @@ 'use strict'; import {createDecorator, ServiceIdentifier} from 'vs/platform/instantiation/common/instantiation'; -import {IEditorInput, ITextEditorOptions} from 'vs/platform/editor/common/editor'; +import {IEditorInput, ITextEditorOptions, IResourceInput} from 'vs/platform/editor/common/editor'; export const IHistoryService = createDecorator('historyService'); @@ -46,5 +46,5 @@ export interface IHistoryService { /** * Get the entire history of opened editors. */ - getHistory(): IEditorInput[]; + getHistory(): (IEditorInput|IResourceInput)[]; } \ No newline at end of file -- GitLab