Skip to content

Commit ed51968

Browse files
committed
add modified member to implement backups for scratchpads which are never dirty
1 parent fb9e06d commit ed51968

File tree

14 files changed

+197
-57
lines changed

14 files changed

+197
-57
lines changed

src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export class SearchEditorInput extends EditorInput {
131131
readonly onDidChangeContent = input.onDidChangeContent;
132132
readonly onDidSave = input.onDidSave;
133133
isDirty(): boolean { return input.isDirty(); }
134+
isModified(): boolean { return input.isDirty(); }
134135
backup(token: CancellationToken): Promise<IWorkingCopyBackup> { return input.backup(token); }
135136
save(options?: ISaveOptions): Promise<boolean> { return input.save(0, options).then(editor => !!editor); }
136137
revert(options?: IRevertOptions): Promise<void> { return input.revert(0, options); }

src/vs/workbench/services/textfile/common/textFileEditorModel.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
674674
return this.dirty;
675675
}
676676

677+
isModified(): boolean {
678+
return this.dirty;
679+
}
680+
677681
setDirty(dirty: boolean): void {
678682
if (!this.isResolved()) {
679683
return; // only resolved models can be marked dirty

src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ suite('Workbench - TextModelResolverService', () => {
157157
test('resolve untitled', async () => {
158158
const service = accessor.untitledTextEditorService;
159159
const untitledModel = service.create();
160+
untitledModel.isModified = untitledModel.isDirty;
160161
const input = instantiationService.createInstance(UntitledTextEditorInput, untitledModel);
161162

162163
await input.resolve();

src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface IUntitledTextEditorModel extends ITextEditorModel, ILanguageSup
6767
resolve(): Promise<void>;
6868
}
6969

70-
export class UntitledTextEditorModel extends BaseTextEditorModel implements IUntitledTextEditorModel {
70+
export class UntitledTextEditorModel extends BaseTextEditorModel implements IUntitledTextEditorModel, IWorkingCopy {
7171

7272
private static readonly FIRST_LINE_NAME_MAX_LENGTH = 40;
7373
private static readonly FIRST_LINE_NAME_CANDIDATE_MAX_LENGTH = UntitledTextEditorModel.FIRST_LINE_NAME_MAX_LENGTH * 10;
@@ -243,6 +243,10 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt
243243
return this.dirty;
244244
}
245245

246+
isModified(): boolean {
247+
return this.dirty;
248+
}
249+
246250
private setDirty(dirty: boolean): void {
247251
if (this.dirty === dirty) {
248252
return;

src/vs/workbench/services/workingCopy/browser/workingCopyBackupTracker.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,27 @@ export class BrowserWorkingCopyBackupTracker extends WorkingCopyBackupTracker im
3232
protected onFinalBeforeShutdown(reason: ShutdownReason): boolean {
3333

3434
// Web: we cannot perform long running in the shutdown phase
35-
// As such we need to check sync if there are any dirty working
35+
// As such we need to check sync if there are any modified working
3636
// copies that have not been backed up yet and then prevent the
3737
// shutdown if that is the case.
3838

39-
const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies;
40-
if (!dirtyWorkingCopies.length) {
41-
return false; // no dirty: no veto
39+
const modifiedWorkingCopies = this.workingCopyService.modifiedWorkingCopies;
40+
if (!modifiedWorkingCopies.length) {
41+
return false; // no unsaved changes: no veto
4242
}
4343

44-
if (!this.filesConfigurationService.isHotExitEnabled) {
44+
if (this.workingCopyService.hasDirty && !this.filesConfigurationService.isHotExitEnabled) {
4545
return true; // dirty without backup: veto
4646
}
4747

48-
for (const dirtyWorkingCopy of dirtyWorkingCopies) {
49-
if (!this.workingCopyBackupService.hasBackupSync(dirtyWorkingCopy, this.getContentVersion(dirtyWorkingCopy))) {
48+
for (const modifiedWorkingCopy of modifiedWorkingCopies) {
49+
if (!this.workingCopyBackupService.hasBackupSync(modifiedWorkingCopy, this.getContentVersion(modifiedWorkingCopy))) {
5050
this.logService.warn('Unload veto: pending backups');
5151

52-
return true; // dirty without backup: veto
52+
return true; // unsaved content without backup: veto
5353
}
5454
}
5555

56-
return false; // dirty with backups: no veto
56+
return false; // unsaved content backed up: no veto
5757
}
5858
}

src/vs/workbench/services/workingCopy/common/resourceWorkingCopy.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ export abstract class ResourceWorkingCopy extends Disposable implements IResourc
143143

144144
//#endregion
145145

146+
//#region Modified Tracking
147+
148+
// Overridden when the Working Copies need to differentiate between
149+
// dirty and modified e.g. scratchpads
150+
isModified(): boolean {
151+
return this.isDirty();
152+
}
153+
154+
//#endregion
146155

147156
//#region Abstract
148157

@@ -161,4 +170,5 @@ export abstract class ResourceWorkingCopy extends Disposable implements IResourc
161170
abstract revert(options?: IRevertOptions): Promise<void>;
162171

163172
//#endregion
173+
164174
}

src/vs/workbench/services/workingCopy/common/untitledFileWorkingCopy.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ export interface IUntitledFileWorkingCopyModelContentChangedEvent {
3737

3838
/**
3939
* Flag that indicates that the content change should
40-
* clear the dirty flag, e.g. because the contents are
40+
* clear the dirty/modified flags, e.g. because the contents are
4141
* back to being empty or back to an initial state that
42-
* should not be considered as dirty.
42+
* should not be considered as modified.
4343
*/
4444
readonly isInitial: boolean;
4545
}
@@ -137,20 +137,25 @@ export class UntitledFileWorkingCopy<M extends IUntitledFileWorkingCopyModel> ex
137137

138138
//#region Dirty
139139

140-
private dirty = !this.isScratchpad &&
141-
(this.hasAssociatedFilePath || Boolean(this.initialContents && this.initialContents.markDirty !== false));
140+
private modified = this.hasAssociatedFilePath || Boolean(this.initialContents && this.initialContents.markDirty !== false);
142141

143142
isDirty(): boolean {
144-
return this.dirty;
143+
return this.modified && !this.isScratchpad; // Scratchpad working copies are never dirty
145144
}
146145

147-
private setDirty(dirty: boolean): void {
148-
if (this.dirty === dirty || this.isScratchpad /* Scratchpad working copies are never dirty */) {
146+
isModified(): boolean {
147+
return this.modified;
148+
}
149+
150+
private setModified(modified: boolean): void {
151+
if (this.modified === modified) {
149152
return;
150153
}
151154

152-
this.dirty = dirty;
153-
this._onDidChangeDirty.fire();
155+
this.modified = modified;
156+
if (!this.isScratchpad) {
157+
this._onDidChangeDirty.fire();
158+
}
154159
}
155160

156161
//#endregion
@@ -191,8 +196,8 @@ export class UntitledFileWorkingCopy<M extends IUntitledFileWorkingCopyModel> ex
191196
// Create model
192197
await this.doCreateModel(untitledContents);
193198

194-
// Untitled associated to file path are dirty right away as well as untitled with content
195-
this.setDirty(this.hasAssociatedFilePath || !!backup || Boolean(this.initialContents && this.initialContents.markDirty !== false));
199+
// Untitled associated to file path are modified right away as well as untitled with content
200+
this.setModified(this.hasAssociatedFilePath || !!backup || Boolean(this.initialContents && this.initialContents.markDirty !== false));
196201

197202
// If we have initial contents, make sure to emit this
198203
// as the appropriate events to the outside.
@@ -226,12 +231,12 @@ export class UntitledFileWorkingCopy<M extends IUntitledFileWorkingCopyModel> ex
226231
// in case provided by the change event and in case we do not
227232
// have an associated path set
228233
if (!this.hasAssociatedFilePath && e.isInitial) {
229-
this.setDirty(false);
234+
this.setModified(false);
230235
}
231236

232237
// Turn dirty otherwise
233238
else {
234-
this.setDirty(true);
239+
this.setModified(true);
235240
}
236241

237242
// Emit as general content change event
@@ -289,8 +294,8 @@ export class UntitledFileWorkingCopy<M extends IUntitledFileWorkingCopyModel> ex
289294
async revert(): Promise<void> {
290295
this.trace('revert()');
291296

292-
// No longer dirty
293-
this.setDirty(false);
297+
// No longer modified
298+
this.setModified(false);
294299

295300
// Emit as event
296301
this._onDidRevert.fire();

src/vs/workbench/services/workingCopy/common/workingCopy.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,19 @@ export interface IWorkingCopy extends IWorkingCopyIdentifier {
163163

164164
//#region Dirty Tracking
165165

166+
/**
167+
* Indicates that the file has unsaved changes
168+
* and should confirm before closing.
169+
*/
166170
isDirty(): boolean;
167171

172+
/**
173+
* Indicates that the file has unsaved changes.
174+
* Used for backup tracking and accounts for
175+
* working copies that are never dirty e.g. scratchpads
176+
*/
177+
isModified(): boolean;
178+
168179
//#endregion
169180

170181

src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,16 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
159159
return;
160160
}
161161

162-
// Schedule backup if dirty or a scratchpad
163-
if (workingCopy.isDirty() || (workingCopy.capabilities & WorkingCopyCapabilities.Scratchpad)) {
162+
// Schedule backup for unsaved content
163+
if (workingCopy.isModified()) {
164164
// this listener will make sure that the backup is
165165
// pushed out for as long as the user is still changing
166166
// the content of the working copy.
167167
this.scheduleBackup(workingCopy);
168168
}
169+
else {
170+
this.discardBackup(workingCopy);
171+
}
169172
}
170173

171174
private scheduleBackup(workingCopy: IWorkingCopy): void {
@@ -183,8 +186,8 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
183186
return;
184187
}
185188

186-
// Backup if dirty
187-
if (workingCopy.isDirty()) {
189+
// Backup if modified
190+
if (workingCopy.isModified()) {
188191
this.logService.trace(`[backup tracker] creating backup`, workingCopy.resource.toString(), workingCopy.typeId);
189192

190193
try {
@@ -193,7 +196,7 @@ export abstract class WorkingCopyBackupTracker extends Disposable {
193196
return;
194197
}
195198

196-
if (workingCopy.isDirty()) {
199+
if (workingCopy.isModified()) {
197200
this.logService.trace(`[backup tracker] storing backup`, workingCopy.resource.toString(), workingCopy.typeId);
198201

199202
await this.workingCopyBackupService.backup(workingCopy, backup.content, this.getContentVersion(workingCopy), backup.meta, cts.token);

src/vs/workbench/services/workingCopy/common/workingCopyService.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ export interface IWorkingCopyService {
6868
*/
6969
readonly dirtyWorkingCopies: readonly IWorkingCopy[];
7070

71+
/**
72+
* All working copies with unsaved changes,
73+
* including scratchpads, which are never dirty
74+
*/
75+
readonly modifiedWorkingCopies: readonly IWorkingCopy[];
76+
7177
/**
7278
* Whether there is any registered working copy that is dirty.
7379
*/
@@ -265,6 +271,10 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic
265271
return this.workingCopies.filter(workingCopy => workingCopy.isDirty());
266272
}
267273

274+
get modifiedWorkingCopies(): IWorkingCopy[] {
275+
return this.workingCopies.filter(workingCopy => workingCopy.isModified);
276+
}
277+
268278
isDirty(resource: URI, typeId?: string): boolean {
269279
const workingCopies = this.mapResourceToWorkingCopies.get(resource);
270280
if (workingCopies) {

0 commit comments

Comments
 (0)