history - allow to rename entries

This commit is contained in:
Benjamin Pasero
2022-03-16 07:56:34 +01:00
parent eb8825f6c1
commit 4261e7ccd2
6 changed files with 142 additions and 4 deletions
@@ -21,6 +21,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ResourceContextKey } from 'vs/workbench/common/contextkeys';
import { Codicon } from 'vs/base/common/codicons';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
export const LOCAL_HISTORY_MENU_CONTEXT_VALUE = 'localHistory:item';
export const LOCAL_HISTORY_MENU_CONTEXT_KEY = ContextKeyExpr.equals('timelineItem', LOCAL_HISTORY_MENU_CONTEXT_VALUE);
@@ -230,6 +231,45 @@ async function restore(accessor: ServicesAccessor, item: ITimelineCommandArgumen
//#endregion
//#region Rename
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.localHistory.rename',
title: { value: localize('localHistory.rename', "Rename"), original: 'Rename' },
menu: {
id: MenuId.TimelineItemContext,
group: '3_edit',
order: 1,
when: LOCAL_HISTORY_MENU_CONTEXT_KEY
}
});
}
async run(accessor: ServicesAccessor, item: ITimelineCommandArgument): Promise<void> {
const workingCopyHistoryService = accessor.get(IWorkingCopyHistoryService);
const quickInputService = accessor.get(IQuickInputService);
const { entry } = await findLocalHistoryEntry(workingCopyHistoryService, item);
if (entry) {
const inputBox = quickInputService.createInputBox();
inputBox.title = localize('renameLocalHistoryEntryTitle', "Rename Local History Entry");
inputBox.ignoreFocusOut = true;
inputBox.placeholder = localize('renameLocalHistoryPlaceholder', "Enter the new name of the local history entry");
inputBox.value = SaveSourceRegistry.getSourceLabel(entry.source) ?? entry.source;
inputBox.show();
inputBox.onDidAccept(() => {
if (inputBox.value) {
workingCopyHistoryService.updateEntry(entry, { source: inputBox.value }, CancellationToken.None);
}
inputBox.dispose();
});
}
}
});
//#endregion
//#region Delete
registerAction2(class extends Action2 {
@@ -240,7 +280,7 @@ registerAction2(class extends Action2 {
menu: {
id: MenuId.TimelineItemContext,
group: '3_edit',
order: 1,
order: 2,
when: LOCAL_HISTORY_MENU_CONTEXT_KEY
}
});
@@ -78,6 +78,7 @@ export class LocalHistoryTimeline extends Disposable implements IWorkbenchContri
// History changes
this._register(this.workingCopyHistoryService.onDidAddEntry(e => this.onDidChangeWorkingCopyHistoryEntry(e.entry, false /* entry added */)));
this._register(this.workingCopyHistoryService.onDidChangeEntry(e => this.onDidChangeWorkingCopyHistoryEntry(e.entry, false /* entry changed */)));
this._register(this.workingCopyHistoryService.onDidRemoveEntry(e => this.onDidChangeWorkingCopyHistoryEntry(e.entry, true /* entry removed */)));
this._register(this.workingCopyHistoryService.onDidRemoveAllEntries(() => this.onDidChangeWorkingCopyHistoryEntry(undefined /* all history */, true /* entry removed */)));
@@ -28,6 +28,13 @@ class BrowserWorkingCopyHistoryModel extends WorkingCopyHistoryModel {
return entry;
}
override async updateEntry(entry: IWorkingCopyHistoryEntry, properties: { source: SaveSource }, token: CancellationToken): Promise<void> {
await super.updateEntry(entry, properties, token);
if (!token.isCancellationRequested) {
await this.store(); // need to store on each remove because we do not have long running shutdown support in web
}
}
override async removeEntry(entry: IWorkingCopyHistoryEntry, token: CancellationToken): Promise<boolean> {
const removed = await super.removeEntry(entry, token);
if (removed && !token.isCancellationRequested) {
@@ -53,7 +60,7 @@ export class BrowserWorkingCopyHistoryService extends WorkingCopyHistoryService
}
protected override createModel(resource: URI, historyHome: URI): WorkingCopyHistoryModel {
return new BrowserWorkingCopyHistoryModel(resource, historyHome, this._onDidAddEntry, this._onDidRemoveEntry, this.fileService, this.labelService, this.logService, this.configurationService);
return new BrowserWorkingCopyHistoryModel(resource, historyHome, this._onDidAddEntry, this._onDidChangeEntry, this._onDidRemoveEntry, this.fileService, this.labelService, this.logService, this.configurationService);
}
}
@@ -58,7 +58,7 @@ export interface IWorkingCopyHistoryEntry {
/**
* Associated source with the history entry.
*/
readonly source: SaveSource;
source: SaveSource;
}
export interface IWorkingCopyHistoryEntryDescriptor {
@@ -90,6 +90,11 @@ export interface IWorkingCopyHistoryService {
*/
onDidAddEntry: Event<IWorkingCopyHistoryEvent>;
/**
* An event when entries are changed in the history.
*/
onDidChangeEntry: Event<IWorkingCopyHistoryEvent>;
/**
* An event when entries are removed from the history.
*/
@@ -106,6 +111,11 @@ export interface IWorkingCopyHistoryService {
*/
addEntry(descriptor: IWorkingCopyHistoryEntryDescriptor, token: CancellationToken): Promise<IWorkingCopyHistoryEntry | undefined>;
/**
* Updates an entry in the local history if found.
*/
updateEntry(entry: IWorkingCopyHistoryEntry, properties: { source: SaveSource }, token: CancellationToken): Promise<void>;
/**
* Removes an entry from the local history if found.
*/
@@ -64,6 +64,7 @@ export class WorkingCopyHistoryModel {
private readonly workingCopyResource: URI,
private readonly historyHome: URI,
private readonly entryAddedEmitter: Emitter<IWorkingCopyHistoryEvent>,
private readonly entryChangedEmitter: Emitter<IWorkingCopyHistoryEvent>,
private readonly entryRemovedEmitter: Emitter<IWorkingCopyHistoryEvent>,
private readonly fileService: IFileService,
private readonly labelService: ILabelService,
@@ -125,6 +126,19 @@ export class WorkingCopyHistoryModel {
return true;
}
async updateEntry(entry: IWorkingCopyHistoryEntry, properties: { source: SaveSource }, token: CancellationToken): Promise<void> {
const index = this.entries.indexOf(entry);
if (index === -1) {
return;
}
// Update entry
entry.source = properties.source;
// Events
this.entryChangedEmitter.fire({ entry });
}
async getEntries(): Promise<readonly IWorkingCopyHistoryEntry[]> {
// Make sure to await resolving when all entries are asked for
@@ -322,6 +336,9 @@ export abstract class WorkingCopyHistoryService extends Disposable implements IW
protected readonly _onDidAddEntry = this._register(new Emitter<IWorkingCopyHistoryEvent>());
readonly onDidAddEntry = this._onDidAddEntry.event;
protected readonly _onDidChangeEntry = this._register(new Emitter<IWorkingCopyHistoryEvent>());
readonly onDidChangeEntry = this._onDidChangeEntry.event;
protected readonly _onDidRemoveEntry = this._register(new Emitter<IWorkingCopyHistoryEvent>());
readonly onDidRemoveEntry = this._onDidRemoveEntry.event;
@@ -382,6 +399,18 @@ export abstract class WorkingCopyHistoryService extends Disposable implements IW
return model.addEntry(source, timestamp, token);
}
async updateEntry(entry: IWorkingCopyHistoryEntry, properties: { source: SaveSource }, token: CancellationToken): Promise<void> {
// Resolve history model for working copy
const model = await this.getModel(entry.workingCopy.resource);
if (token.isCancellationRequested) {
return;
}
// Rename in model
return model.updateEntry(entry, properties, token);
}
async removeEntry(entry: IWorkingCopyHistoryEntry, token: CancellationToken): Promise<boolean> {
// Resolve history model for working copy
@@ -433,7 +462,7 @@ export abstract class WorkingCopyHistoryService extends Disposable implements IW
}
protected createModel(resource: URI, historyHome: URI): WorkingCopyHistoryModel {
return new WorkingCopyHistoryModel(resource, historyHome, this._onDidAddEntry, this._onDidRemoveEntry, this.fileService, this.labelService, this.logService, this.configurationService);
return new WorkingCopyHistoryModel(resource, historyHome, this._onDidAddEntry, this._onDidChangeEntry, this._onDidRemoveEntry, this.fileService, this.labelService, this.logService, this.configurationService);
}
}
@@ -173,6 +173,42 @@ flakySuite('WorkingCopyHistoryService', () => {
assert.strictEqual(addEvents.length, 4);
});
test('renameEntry', async () => {
let changeEvents: IWorkingCopyHistoryEvent[] = [];
service.onDidChangeEntry(e => changeEvents.push(e));
const workingCopy1 = new TestWorkingCopy(URI.file(testFile1Path));
const entry = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None);
await addEntry({ resource: workingCopy1.resource }, CancellationToken.None);
await addEntry({ resource: workingCopy1.resource, source: 'My Source' }, CancellationToken.None);
let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None);
assert.strictEqual(entries.length, 3);
await service.updateEntry(entry, { source: 'Hello Rename' }, CancellationToken.None);
assert.strictEqual(changeEvents.length, 1);
assert.strictEqual(changeEvents[0].entry, entry);
entries = await service.getEntries(workingCopy1.resource, CancellationToken.None);
assert.strictEqual(entries[0].source, 'Hello Rename');
// Simulate shutdown
const event = new TestWillShutdownEvent();
service._lifecycleService.fireWillShutdown(event);
await Promise.allSettled(event.value);
// Resolve from disk fresh and verify again
service.dispose();
service = new TestWorkingCopyHistoryService(testDir);
entries = await service.getEntries(workingCopy1.resource, CancellationToken.None);
assert.strictEqual(entries.length, 3);
assert.strictEqual(entries[0].source, 'Hello Rename');
});
test('removeEntry', async () => {
let removeEvents: IWorkingCopyHistoryEvent[] = [];
service.onDidRemoveEntry(e => removeEvents.push(e));
@@ -199,6 +235,19 @@ flakySuite('WorkingCopyHistoryService', () => {
entries = await service.getEntries(workingCopy1.resource, CancellationToken.None);
assert.strictEqual(entries.length, 3);
// Simulate shutdown
const event = new TestWillShutdownEvent();
service._lifecycleService.fireWillShutdown(event);
await Promise.allSettled(event.value);
// Resolve from disk fresh and verify again
service.dispose();
service = new TestWorkingCopyHistoryService(testDir);
entries = await service.getEntries(workingCopy1.resource, CancellationToken.None);
assert.strictEqual(entries.length, 3);
});
test('removeAll', async () => {
@@ -220,6 +269,8 @@ flakySuite('WorkingCopyHistoryService', () => {
await service.removeAll(CancellationToken.None);
assert.strictEqual(removed, true);
entries = await service.getEntries(workingCopy1.resource, CancellationToken.None);
assert.strictEqual(entries.length, 0);
entries = await service.getEntries(workingCopy2.resource, CancellationToken.None);