Skip to content

Commit 618db14

Browse files
author
Benjamin Pasero
committed
working copy file - add getDirty() method with support for directories
1 parent 2fed7f8 commit 618db14

3 files changed

Lines changed: 65 additions & 13 deletions

File tree

src/vs/workbench/contrib/files/browser/fileActions.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,32 @@ export class GlobalNewUntitledFileAction extends Action {
145145
}
146146
}
147147

148-
async function deleteFiles(workingCopyService: IWorkingCopyService, workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> {
148+
async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> {
149149
let primaryButton: string;
150150
if (useTrash) {
151151
primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash");
152152
} else {
153153
primaryButton = nls.localize({ key: 'deleteButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete");
154154
}
155155

156-
const distinctElements = resources.distinctParents(elements, e => e.resource);
157-
158156
// Handle dirty
157+
const distinctElements = resources.distinctParents(elements, e => e.resource);
158+
const dirtyWorkingCopies = new Set<IWorkingCopy>();
159+
for (const distinctElement of distinctElements) {
160+
for (const dirtyWorkingCopy of workingCopyFileService.getDirty(distinctElement.resource)) {
161+
dirtyWorkingCopies.add(dirtyWorkingCopy);
162+
}
163+
}
159164
let confirmed = true;
160-
const dirtyWorkingCopies = workingCopyService.dirtyWorkingCopies.filter(workingCopy => distinctElements.some(e => resources.isEqualOrParent(workingCopy.resource, e.resource)));
161-
if (dirtyWorkingCopies.length) {
165+
if (dirtyWorkingCopies.size) {
162166
let message: string;
163167
if (distinctElements.length > 1) {
164168
message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?");
165169
} else if (distinctElements[0].isDirectory) {
166-
if (dirtyWorkingCopies.length === 1) {
170+
if (dirtyWorkingCopies.size === 1) {
167171
message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder {0} with unsaved changes in 1 file. Do you want to continue?", distinctElements[0].name);
168172
} else {
169-
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder {0} with unsaved changes in {1} files. Do you want to continue?", distinctElements[0].name, dirtyWorkingCopies.length);
173+
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder {0} with unsaved changes in {1} files. Do you want to continue?", distinctElements[0].name, dirtyWorkingCopies.size);
170174
}
171175
} else {
172176
message = nls.localize('dirtyMessageFileDelete', "You are deleting {0} with unsaved changes. Do you want to continue?", distinctElements[0].name);
@@ -274,7 +278,7 @@ async function deleteFiles(workingCopyService: IWorkingCopyService, workingCopyF
274278

275279
skipConfirm = true;
276280

277-
return deleteFiles(workingCopyService, workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm);
281+
return deleteFiles(workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm);
278282
}
279283
}
280284
}
@@ -967,7 +971,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => {
967971
const explorerService = accessor.get(IExplorerService);
968972
const stats = explorerService.getContext(true).filter(s => !s.isRoot);
969973
if (stats.length) {
970-
await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true);
974+
await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true);
971975
}
972976
};
973977

@@ -976,7 +980,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => {
976980
const stats = explorerService.getContext(true).filter(s => !s.isRoot);
977981

978982
if (stats.length) {
979-
await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false);
983+
await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false);
980984
}
981985
};
982986

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ export interface IWorkingCopyFileService {
118118
delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise<void>;
119119

120120
//#endregion
121+
122+
123+
//#region Path related
124+
125+
/**
126+
* Will return all working copies that are dirty matching the provided resource.
127+
* If the resource is a folder and the scheme supports file operations, a working
128+
* copy that is dirty and is a child of that folder will also be returned.
129+
*/
130+
getDirty(resource: URI): IWorkingCopy[];
131+
132+
//#endregion
121133
}
122134

123135
export class WorkingCopyFileService extends Disposable implements IWorkingCopyFileService {
@@ -167,7 +179,7 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi
167179
// handle dirty working copies depending on the operation:
168180
// - move: revert both source and target (if any)
169181
// - copy: revert target (if any)
170-
const dirtyWorkingCopies = (move ? [...this.getDirtyWorkingCopies(source), ...this.getDirtyWorkingCopies(target)] : this.getDirtyWorkingCopies(target));
182+
const dirtyWorkingCopies = (move ? [...this.getDirty(source), ...this.getDirty(target)] : this.getDirty(target));
171183
await Promise.all(dirtyWorkingCopies.map(dirtyWorkingCopy => dirtyWorkingCopy.revert({ soft: true })));
172184

173185
// now we can rename the source to target via file operation
@@ -202,7 +214,7 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi
202214
// Check for any existing dirty working copies for the resource
203215
// and do a soft revert before deleting to be able to close
204216
// any opened editor with these working copies
205-
const dirtyWorkingCopies = this.getDirtyWorkingCopies(resource);
217+
const dirtyWorkingCopies = this.getDirty(resource);
206218
await Promise.all(dirtyWorkingCopies.map(dirtyWorkingCopy => dirtyWorkingCopy.revert({ soft: true })));
207219

208220
// Now actually delete from disk
@@ -220,7 +232,10 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi
220232
await this._onDidRunWorkingCopyFileOperation.fireAsync(event, CancellationToken.None);
221233
}
222234

223-
private getDirtyWorkingCopies(resource: URI): IWorkingCopy[] {
235+
236+
//#region Path related
237+
238+
getDirty(resource: URI): IWorkingCopy[] {
224239
return this.workingCopyService.dirtyWorkingCopies.filter(dirty => {
225240
if (this.fileService.canHandleResource(resource)) {
226241
// only check for parents if the resource can be handled
@@ -232,6 +247,8 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi
232247
return isEqual(dirty.resource, resource);
233248
});
234249
}
250+
251+
//#endregion
235252
}
236253

237254
registerSingleton(IWorkingCopyFileService, WorkingCopyFileService, true);

src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,35 @@ suite('WorkingCopyFileService', () => {
165165
listener1.dispose();
166166
listener2.dispose();
167167
}
168+
169+
test('getDirty', async function () {
170+
const model1 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-1.txt'), 'utf8', undefined);
171+
(<TextFileEditorModelManager>accessor.textFileService.files).add(model.resource, model);
172+
173+
const model2 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-2.txt'), 'utf8', undefined);
174+
(<TextFileEditorModelManager>accessor.textFileService.files).add(model.resource, model);
175+
176+
let dirty = accessor.workingCopyFileService.getDirty(model1.resource);
177+
assert.equal(dirty.length, 0);
178+
179+
await model1.load();
180+
model1.textEditorModel!.setValue('foo');
181+
182+
dirty = accessor.workingCopyFileService.getDirty(model1.resource);
183+
assert.equal(dirty.length, 1);
184+
assert.equal(dirty[0], model1);
185+
186+
dirty = accessor.workingCopyFileService.getDirty(toResource.call(this, '/path'));
187+
assert.equal(dirty.length, 1);
188+
assert.equal(dirty[0], model1);
189+
190+
await model2.load();
191+
model2.textEditorModel!.setValue('bar');
192+
193+
dirty = accessor.workingCopyFileService.getDirty(toResource.call(this, '/path'));
194+
assert.equal(dirty.length, 2);
195+
196+
model1.dispose();
197+
model2.dispose();
198+
});
168199
});

0 commit comments

Comments
 (0)