提交 de264a8b 编写于 作者: B Benjamin Pasero

files - working copy service for explorer delete

上级 33c79d5a
...@@ -133,8 +133,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution ...@@ -133,8 +133,8 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
} }
private saveAllDirty(options?: ISaveOptions): void { private saveAllDirty(options?: ISaveOptions): void {
for (const workingCopy of this.workingCopyService.workingCopies) { for (const workingCopy of this.workingCopyService.dirtyWorkingCopies) {
if (workingCopy.isDirty() && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { if (!(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) {
workingCopy.save(options); workingCopy.save(options);
} }
} }
......
...@@ -34,7 +34,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi ...@@ -34,7 +34,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi
// copies that have not been backed up yet and then prevent the // copies that have not been backed up yet and then prevent the
// shutdown if that is the case. // shutdown if that is the case.
const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies;
if (!dirtyWorkingCopies.length) { if (!dirtyWorkingCopies.length) {
return false; // no dirty: no veto return false; // no dirty: no veto
} }
......
...@@ -46,7 +46,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi ...@@ -46,7 +46,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi
private onBeforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> { private onBeforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> {
// Dirty working copies need treatment on shutdown // Dirty working copies need treatment on shutdown
const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies;
if (dirtyWorkingCopies.length) { if (dirtyWorkingCopies.length) {
// If auto save is enabled, save all working copies and then check again for dirty copies // If auto save is enabled, save all working copies and then check again for dirty copies
...@@ -55,7 +55,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi ...@@ -55,7 +55,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi
return this.doSaveAll(dirtyWorkingCopies, false /* not untitled */, { skipSaveParticipants: true }).then(() => { return this.doSaveAll(dirtyWorkingCopies, false /* not untitled */, { skipSaveParticipants: true }).then(() => {
// If we still have dirty working copies, we either have untitled ones or working copies that cannot be saved // If we still have dirty working copies, we either have untitled ones or working copies that cannot be saved
const remainingDirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); const remainingDirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies;
if (remainingDirtyWorkingCopies.length) { if (remainingDirtyWorkingCopies.length) {
return this.handleDirtyBeforeShutdown(remainingDirtyWorkingCopies, reason); return this.handleDirtyBeforeShutdown(remainingDirtyWorkingCopies, reason);
} }
...@@ -143,7 +143,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi ...@@ -143,7 +143,7 @@ export class BackupOnShutdown extends Disposable implements IWorkbenchContributi
private async confirmBeforeShutdown(): Promise<boolean> { private async confirmBeforeShutdown(): Promise<boolean> {
// Show confirm dialog for all dirty working copies // Show confirm dialog for all dirty working copies
const dirtyWorkingCopies = this.workingCopyService.workingCopies.filter(workingCopy => workingCopy.isDirty()); const dirtyWorkingCopies = this.workingCopyService.dirtyWorkingCopies;
const confirm = await this.fileDialogService.showSaveConfirm(dirtyWorkingCopies.map(w => w.resource)); const confirm = await this.fileDialogService.showSaveConfirm(dirtyWorkingCopies.map(w => w.resource));
// Save // Save
......
...@@ -143,7 +143,7 @@ export class GlobalNewUntitledFileAction extends Action { ...@@ -143,7 +143,7 @@ export class GlobalNewUntitledFileAction extends Action {
} }
} }
async function deleteFiles(textFileService: ITextFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> { async function deleteFiles(workingCopyService: IWorkingCopyService, textFileService: ITextFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> {
let primaryButton: string; let primaryButton: string;
if (useTrash) { if (useTrash) {
primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash"); primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash");
...@@ -155,16 +155,16 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi ...@@ -155,16 +155,16 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi
// Handle dirty // Handle dirty
let confirmed = true; let confirmed = true;
const dirty = textFileService.getDirty().filter(d => distinctElements.some(e => resources.isEqualOrParent(d, e.resource))); const dirtyWorkingCopies = workingCopyService.dirtyWorkingCopies.filter(workingCopy => distinctElements.some(e => resources.isEqualOrParent(workingCopy.resource, e.resource)));
if (dirty.length) { if (dirtyWorkingCopies.length) {
let message: string; let message: string;
if (distinctElements.length > 1) { if (distinctElements.length > 1) {
message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?"); message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?");
} else if (distinctElements[0].isDirectory) { } else if (distinctElements[0].isDirectory) {
if (dirty.length === 1) { if (dirtyWorkingCopies.length === 1) {
message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?"); message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?");
} else { } else {
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length); message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirtyWorkingCopies.length);
} }
} else { } else {
message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?"); message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?");
...@@ -181,7 +181,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi ...@@ -181,7 +181,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi
confirmed = false; confirmed = false;
} else { } else {
skipConfirm = true; skipConfirm = true;
await textFileService.revertAll(dirty); await Promise.all(dirtyWorkingCopies.map(dirty => dirty.revert()));
} }
} }
...@@ -190,11 +190,11 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi ...@@ -190,11 +190,11 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi
return; return;
} }
let confirmDeletePromise: Promise<IConfirmationResult>; let confirmation: IConfirmationResult;
// Check if we need to ask for confirmation at all // Check if we need to ask for confirmation at all
if (skipConfirm || (useTrash && configurationService.getValue<boolean>(CONFIRM_DELETE_SETTING_KEY) === false)) { if (skipConfirm || (useTrash && configurationService.getValue<boolean>(CONFIRM_DELETE_SETTING_KEY) === false)) {
confirmDeletePromise = Promise.resolve({ confirmed: true }); confirmation = { confirmed: true };
} }
// Confirm for moving to trash // Confirm for moving to trash
...@@ -207,7 +207,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi ...@@ -207,7 +207,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi
detail += distinctElements.length > 1 ? nls.localize('undoTrashFiles', "You can restore these files from the Trash.") : nls.localize('undoTrash', "You can restore this file from the Trash."); detail += distinctElements.length > 1 ? nls.localize('undoTrashFiles', "You can restore these files from the Trash.") : nls.localize('undoTrash', "You can restore this file from the Trash.");
} }
confirmDeletePromise = dialogService.confirm({ confirmation = await dialogService.confirm({
message, message,
detail, detail,
primaryButton, primaryButton,
...@@ -223,7 +223,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi ...@@ -223,7 +223,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi
let { message, detail } = getDeleteMessage(distinctElements); let { message, detail } = getDeleteMessage(distinctElements);
detail += detail ? '\n' : ''; detail += detail ? '\n' : '';
detail += nls.localize('irreversible', "This action is irreversible!"); detail += nls.localize('irreversible', "This action is irreversible!");
confirmDeletePromise = dialogService.confirm({ confirmation = await dialogService.confirm({
message, message,
detail, detail,
primaryButton, primaryButton,
...@@ -231,61 +231,54 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi ...@@ -231,61 +231,54 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi
}); });
} }
return confirmDeletePromise.then(confirmation => {
// Check for confirmation checkbox
let updateConfirmSettingsPromise: Promise<void> = Promise.resolve(undefined);
if (confirmation.confirmed && confirmation.checkboxChecked === true) {
updateConfirmSettingsPromise = configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER);
}
return updateConfirmSettingsPromise.then(() => { // Check for confirmation checkbox
if (confirmation.confirmed && confirmation.checkboxChecked === true) {
await configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER);
}
// Check for confirmation
if (!confirmation.confirmed) {
return Promise.resolve(undefined);
}
// Call function // Check for confirmation
const servicePromise = Promise.all(distinctElements.map(e => textFileService.delete(e.resource, { useTrash: useTrash, recursive: true }))) if (!confirmation.confirmed) {
.then(undefined, (error: any) => { return;
// Handle error to delete file(s) from a modal confirmation dialog }
let errorMessage: string;
let detailMessage: string | undefined;
let primaryButton: string;
if (useTrash) {
errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?");
detailMessage = nls.localize('irreversible', "This action is irreversible!");
primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently");
} else {
errorMessage = toErrorMessage(error, false);
primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry");
}
return dialogService.confirm({ // Call function
message: errorMessage, try {
detail: detailMessage, await Promise.all(distinctElements.map(e => textFileService.delete(e.resource, { useTrash: useTrash, recursive: true })));
type: 'warning', } catch (error) {
primaryButton
}).then(res => {
if (res.confirmed) { // Handle error to delete file(s) from a modal confirmation dialog
if (useTrash) { let errorMessage: string;
useTrash = false; // Delete Permanently let detailMessage: string | undefined;
} let primaryButton: string;
if (useTrash) {
errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?");
detailMessage = nls.localize('irreversible', "This action is irreversible!");
primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently");
} else {
errorMessage = toErrorMessage(error, false);
primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry");
}
skipConfirm = true; const res = await dialogService.confirm({
message: errorMessage,
detail: detailMessage,
type: 'warning',
primaryButton
});
return deleteFiles(textFileService, dialogService, configurationService, elements, useTrash, skipConfirm); if (res.confirmed) {
} if (useTrash) {
useTrash = false; // Delete Permanently
}
return Promise.resolve(); skipConfirm = true;
});
});
return servicePromise.then(undefined); return deleteFiles(workingCopyService, textFileService, dialogService, configurationService, elements, useTrash, skipConfirm);
}); }
}); }
} }
function getMoveToTrashMessage(distinctElements: ExplorerItem[]): { message: string, detail: string } { function getMoveToTrashMessage(distinctElements: ExplorerItem[]): { message: string, detail: string } {
...@@ -648,7 +641,7 @@ export class ShowActiveFileInExplorer extends Action { ...@@ -648,7 +641,7 @@ export class ShowActiveFileInExplorer extends Action {
super(id, label); super(id, label);
} }
run(): Promise<any> { async run(): Promise<any> {
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) { if (resource) {
this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource); this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, resource);
...@@ -656,7 +649,7 @@ export class ShowActiveFileInExplorer extends Action { ...@@ -656,7 +649,7 @@ export class ShowActiveFileInExplorer extends Action {
this.notificationService.info(nls.localize('openFileToShow', "Open a file first to show it in the explorer")); this.notificationService.info(nls.localize('openFileToShow', "Open a file first to show it in the explorer"));
} }
return Promise.resolve(true); return true;
} }
} }
...@@ -726,7 +719,7 @@ export class ShowOpenedFileInNewWindow extends Action { ...@@ -726,7 +719,7 @@ export class ShowOpenedFileInNewWindow extends Action {
super(id, label); super(id, label);
} }
run(): Promise<any> { async run(): Promise<any> {
const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (fileResource) { if (fileResource) {
if (this.fileService.canHandleResource(fileResource)) { if (this.fileService.canHandleResource(fileResource)) {
...@@ -738,7 +731,7 @@ export class ShowOpenedFileInNewWindow extends Action { ...@@ -738,7 +731,7 @@ export class ShowOpenedFileInNewWindow extends Action {
this.notificationService.info(nls.localize('openFileToShowInNewWindow.nofile', "Open a file first to open in new window")); this.notificationService.info(nls.localize('openFileToShowInNewWindow.nofile', "Open a file first to open in new window"));
} }
return Promise.resolve(true); return true;
} }
} }
...@@ -822,7 +815,7 @@ export class CompareWithClipboardAction extends Action { ...@@ -822,7 +815,7 @@ export class CompareWithClipboardAction extends Action {
this.enabled = true; this.enabled = true;
} }
run(): Promise<any> { async run(): Promise<any> {
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }); const resource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) { if (resource && (this.fileService.canHandleResource(resource) || resource.scheme === Schemas.untitled)) {
if (!this.registrationDisposal) { if (!this.registrationDisposal) {
...@@ -839,7 +832,7 @@ export class CompareWithClipboardAction extends Action { ...@@ -839,7 +832,7 @@ export class CompareWithClipboardAction extends Action {
}); });
} }
return Promise.resolve(true); return true;
} }
dispose(): void { dispose(): void {
...@@ -902,15 +895,17 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole ...@@ -902,15 +895,17 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
folder.addChild(newStat); folder.addChild(newStat);
const onSuccess = (value: string): Promise<void> => { const onSuccess = async (value: string): Promise<void> => {
const createPromise = isFolder ? fileService.createFolder(resources.joinPath(folder.resource, value)) : textFileService.create(resources.joinPath(folder.resource, value)); try {
return createPromise.then(created => { const created = isFolder ? await fileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value));
refreshIfSeparator(value, explorerService); refreshIfSeparator(value, explorerService);
return isFolder ? explorerService.select(created.resource, true)
: editorService.openEditor({ resource: created.resource, options: { pinned: true } }).then(() => undefined); isFolder ?
}, error => { await explorerService.select(created.resource, true) :
await editorService.openEditor({ resource: created.resource, options: { pinned: true } });
} catch (error) {
onErrorWithRetry(notificationService, error, () => onSuccess(value)); onErrorWithRetry(notificationService, error, () => onSuccess(value));
}); }
}; };
explorerService.setEditable(newStat, { explorerService.setEditable(newStat, {
...@@ -974,7 +969,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => { ...@@ -974,7 +969,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => {
const explorerService = accessor.get(IExplorerService); const explorerService = accessor.get(IExplorerService);
const stats = explorerService.getContext(true).filter(s => !s.isRoot); const stats = explorerService.getContext(true).filter(s => !s.isRoot);
if (stats.length) { if (stats.length) {
await deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true); await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true);
} }
}; };
...@@ -983,7 +978,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => { ...@@ -983,7 +978,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => {
const stats = explorerService.getContext(true).filter(s => !s.isRoot); const stats = explorerService.getContext(true).filter(s => !s.isRoot);
if (stats.length) { if (stats.length) {
await deleteFiles(accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false); await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(ITextFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false);
} }
}; };
......
...@@ -200,7 +200,7 @@ CommandsRegistry.registerCommand({ ...@@ -200,7 +200,7 @@ CommandsRegistry.registerCommand({
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: COMPARE_SELECTED_COMMAND_ID, id: COMPARE_SELECTED_COMMAND_ID,
handler: (accessor, resource: URI | object) => { handler: async (accessor, resource: URI | object) => {
const editorService = accessor.get(IEditorService); const editorService = accessor.get(IEditorService);
const explorerService = accessor.get(IExplorerService); const explorerService = accessor.get(IExplorerService);
const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService, explorerService); const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService, explorerService);
...@@ -212,7 +212,7 @@ CommandsRegistry.registerCommand({ ...@@ -212,7 +212,7 @@ CommandsRegistry.registerCommand({
}); });
} }
return Promise.resolve(true); return true;
} }
}); });
......
...@@ -81,6 +81,8 @@ export interface IWorkingCopyService { ...@@ -81,6 +81,8 @@ export interface IWorkingCopyService {
readonly dirtyCount: number; readonly dirtyCount: number;
readonly dirtyWorkingCopies: IWorkingCopy[];
readonly hasDirty: boolean; readonly hasDirty: boolean;
isDirty(resource: URI): boolean; isDirty(resource: URI): boolean;
...@@ -118,46 +120,6 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic ...@@ -118,46 +120,6 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic
//#endregion //#endregion
//#region Dirty Tracking
isDirty(resource: URI): boolean {
const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString());
if (workingCopies) {
for (const workingCopy of workingCopies) {
if (workingCopy.isDirty()) {
return true;
}
}
}
return false;
}
get hasDirty(): boolean {
for (const workingCopy of this._workingCopies) {
if (workingCopy.isDirty()) {
return true;
}
}
return false;
}
get dirtyCount(): number {
let totalDirtyCount = 0;
for (const workingCopy of this._workingCopies) {
if (workingCopy.isDirty()) {
totalDirtyCount++;
}
}
return totalDirtyCount;
}
//#endregion
//#region Registry //#region Registry
private mapResourceToWorkingCopy = TernarySearchTree.forPaths<Set<IWorkingCopy>>(); private mapResourceToWorkingCopy = TernarySearchTree.forPaths<Set<IWorkingCopy>>();
...@@ -216,6 +178,50 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic ...@@ -216,6 +178,50 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic
} }
//#endregion //#endregion
//#region Dirty Tracking
get hasDirty(): boolean {
for (const workingCopy of this._workingCopies) {
if (workingCopy.isDirty()) {
return true;
}
}
return false;
}
get dirtyCount(): number {
let totalDirtyCount = 0;
for (const workingCopy of this._workingCopies) {
if (workingCopy.isDirty()) {
totalDirtyCount++;
}
}
return totalDirtyCount;
}
get dirtyWorkingCopies(): IWorkingCopy[] {
return this.workingCopies.filter(workingCopy => workingCopy.isDirty());
}
isDirty(resource: URI): boolean {
const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString());
if (workingCopies) {
for (const workingCopy of workingCopies) {
if (workingCopy.isDirty()) {
return true;
}
}
}
return false;
}
//#endregion
} }
registerSingleton(IWorkingCopyService, WorkingCopyService, true); registerSingleton(IWorkingCopyService, WorkingCopyService, true);
...@@ -105,7 +105,10 @@ suite('WorkingCopyService', () => { ...@@ -105,7 +105,10 @@ suite('WorkingCopyService', () => {
copy1.setDirty(true); copy1.setDirty(true);
assert.equal(copy1.isDirty(), true);
assert.equal(service.dirtyCount, 1); assert.equal(service.dirtyCount, 1);
assert.equal(service.dirtyWorkingCopies.length, 1);
assert.equal(service.dirtyWorkingCopies[0], copy1);
assert.equal(service.isDirty(resource1), true); assert.equal(service.isDirty(resource1), true);
assert.equal(service.hasDirty, true); assert.equal(service.hasDirty, true);
assert.equal(onDidChangeDirty.length, 1); assert.equal(onDidChangeDirty.length, 1);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册