提交 ecd1c92c 编写于 作者: R rebornix

prompt to revert/override if notebook file on disk is newer

上级 e5ceb315
...@@ -582,6 +582,7 @@ export interface NotebookDocumentBackupData { ...@@ -582,6 +582,7 @@ export interface NotebookDocumentBackupData {
readonly viewType: string; readonly viewType: string;
readonly name: string; readonly name: string;
readonly backupId?: string; readonly backupId?: string;
readonly mtime?: number;
} }
export interface IEditor extends editorCommon.ICompositeCodeEditor { export interface IEditor extends editorCommon.ICompositeCodeEditor {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor'; import { EditorModel, IRevertOptions } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { INotebookEditorModel, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModel, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon';
...@@ -15,6 +16,8 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; ...@@ -15,6 +16,8 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { DefaultEndOfLine, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model'; import { DefaultEndOfLine, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
export interface INotebookEditorModelManager { export interface INotebookEditorModelManager {
models: NotebookEditorModel[]; models: NotebookEditorModel[];
...@@ -40,6 +43,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -40,6 +43,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
private readonly _onDidChangeContent = this._register(new Emitter<void>()); private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event; readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
private _notebook!: NotebookTextModel; private _notebook!: NotebookTextModel;
private _lastResolvedFileStat: IFileStatWithMetadata | undefined;
get notebook() { get notebook() {
return this._notebook; return this._notebook;
...@@ -58,7 +62,9 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -58,7 +62,9 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
public readonly viewType: string, public readonly viewType: string,
@INotebookService private readonly _notebookService: INotebookService, @INotebookService private readonly _notebookService: INotebookService,
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
@IBackupFileService private readonly _backupFileService: IBackupFileService @IBackupFileService private readonly _backupFileService: IBackupFileService,
@IFileService private readonly _fileService: IFileService,
@INotificationService private readonly _notificationService: INotificationService
) { ) {
super(); super();
...@@ -85,9 +91,11 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -85,9 +91,11 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
if (this._notebook.supportBackup) { if (this._notebook.supportBackup) {
const tokenSource = new CancellationTokenSource(); const tokenSource = new CancellationTokenSource();
const backupId = await this._notebookService.backup(this.viewType, this.resource, tokenSource.token); const backupId = await this._notebookService.backup(this.viewType, this.resource, tokenSource.token);
const stats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
return { return {
meta: { meta: {
mtime: stats.mtime || new Date().getTime(),
name: this._name, name: this._name,
viewType: this._notebook.viewType, viewType: this._notebook.viewType,
backupId: backupId backupId: backupId
...@@ -96,6 +104,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -96,6 +104,7 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
} else { } else {
return { return {
meta: { meta: {
mtime: new Date().getTime(),
name: this._name, name: this._name,
viewType: this._notebook.viewType viewType: this._notebook.viewType
}, },
...@@ -111,6 +120,8 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -111,6 +120,8 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
} }
await this.load({ forceReadFromDisk: true }); await this.load({ forceReadFromDisk: true });
const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
this._lastResolvedFileStat = newStats;
this._notebook.setDirty(false); this._notebook.setDirty(false);
this._onDidChangeDirty.fire(); this._onDidChangeDirty.fire();
...@@ -148,6 +159,8 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -148,6 +159,8 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
const notebook = await this._notebookService.createNotebookFromBackup(this.viewType!, this.resource, data.metadata, data.languages, data.cells, editorId); const notebook = await this._notebookService.createNotebookFromBackup(this.viewType!, this.resource, data.metadata, data.languages, data.cells, editorId);
this._notebook = notebook!; this._notebook = notebook!;
const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
this._lastResolvedFileStat = newStats;
this._register(this._notebook); this._register(this._notebook);
this._name = basename(this._notebook!.uri); this._name = basename(this._notebook!.uri);
...@@ -168,6 +181,9 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -168,6 +181,9 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
private async loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { private async loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) {
const notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); const notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId);
this._notebook = notebook!; this._notebook = notebook!;
const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
this._lastResolvedFileStat = newStats;
this._register(this._notebook); this._register(this._notebook);
this._name = basename(this._notebook!.uri); this._name = basename(this._notebook!.uri);
...@@ -199,16 +215,71 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN ...@@ -199,16 +215,71 @@ export class NotebookEditorModel extends EditorModel implements IWorkingCopy, IN
return this.resource.scheme === Schemas.untitled; return this.resource.scheme === Schemas.untitled;
} }
private async _assertStat() {
const stats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
if (this._lastResolvedFileStat && stats.mtime > this._lastResolvedFileStat.mtime) {
return new Promise<'override' | 'revert' | 'none'>(resolve => {
const handle = this._notificationService.prompt(
Severity.Info,
nls.localize('notebook.staleSaveError', "The content of the file is newer. Please revert your version with the file contents or overwrite the content of the file with your changes"),
[{
label: nls.localize('notebook.staleSaveError.revert', "Revert"),
run: () => {
resolve('revert');
}
}, {
label: nls.localize('notebook.staleSaveError.override.', "Override"),
run: () => {
resolve('override');
}
}],
{ sticky: true }
);
Event.once(handle.onDidClose)(() => {
resolve('none');
});
});
}
return 'override';
}
async save(): Promise<boolean> { async save(): Promise<boolean> {
const result = await this._assertStat();
if (result === 'none') {
return false;
}
if (result === 'revert') {
await this.revert();
return true;
}
const tokenSource = new CancellationTokenSource(); const tokenSource = new CancellationTokenSource();
await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token);
const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
this._lastResolvedFileStat = newStats;
this._notebook.setDirty(false); this._notebook.setDirty(false);
return true; return true;
} }
async saveAs(targetResource: URI): Promise<boolean> { async saveAs(targetResource: URI): Promise<boolean> {
const result = await this._assertStat();
if (result === 'none') {
return false;
}
if (result === 'revert') {
await this.revert();
return true;
}
const tokenSource = new CancellationTokenSource(); const tokenSource = new CancellationTokenSource();
await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token);
const newStats = await this._fileService.resolve(this.resource, { resolveMetadata: true });
this._lastResolvedFileStat = newStats;
this._notebook.setDirty(false); this._notebook.setDirty(false);
return true; return true;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册