src/vs/workbench/browser/parts/editor/editorGroupView.ts/EditorGroupView/doHandleDirtyClosing
class EditorGroupView:	private async doHandleDirtyClosing(editor: EditorInput, options?: { skipAutoSave: boolean }): Promise<boolean /* veto */> {
		if (!editor.isDirty() || editor.isSaving()) {
			return false; // editor must be dirty and not saving
		}

		if (editor instanceof SideBySideEditorInput && this.model.contains(editor.primary)) {
			return false; // primary-side of editor is still opened somewhere else
		}

		// Note: we explicitly decide to ask for confirm if closing a normal editor even
		// if it is opened in a side-by-side editor in the group. This decision is made
		// because it may be less obvious that one side of a side by side editor is dirty
		// and can still be changed.

		if (this.accessor.groups.some(groupView => {
			if (groupView === this) {
				return false; // skip this group to avoid false assumptions about the editor being opened still
			}

			const otherGroup = groupView;
			if (otherGroup.contains(editor)) {
				return true; // exact editor still opened
			}

			if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.primary)) {
				return true; // primary side of side by side editor still opened
			}

			return false;
		})) {
			return false; // editor is still editable somewhere else
		}

		// Auto-save on focus change: assume to Save unless the editor is untitled
		// because bringing up a dialog would save in this case anyway.
		// However, make sure to respect `skipAutoSave` option in case the automated
		// save fails which would result in the editor never closing
		// (see https://github.com/microsoft/vscode/issues/108752)
		let confirmation: ConfirmResult;
		let saveReason = SaveReason.EXPLICIT;
		let autoSave = false;
		if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.ON_FOCUS_CHANGE && !editor.hasCapability(EditorInputCapabilities.Untitled) && !options?.skipAutoSave) {
			autoSave = true;
			confirmation = ConfirmResult.SAVE;
			saveReason = SaveReason.FOCUS_CHANGE;
		}

		// No auto-save on focus change: ask user
		else {

			// Switch to editor that we want to handle and confirm to save/revert
			await this.openEditor(editor);

			let name: string;
			if (editor instanceof SideBySideEditorInput) {
				name = editor.primary.getName(); // prefer shorter names by using primary's name in this case
			} else {
				name = editor.getName();
			}

			confirmation = await this.fileDialogService.showSaveConfirm([name]);
		}

		// It could be that the editor saved meanwhile or is saving, so we check
		// again to see if anything needs to happen before closing for good.
		// This can happen for example if autoSave: onFocusChange is configured
		// so that the save happens when the dialog opens.
		if (!editor.isDirty() || editor.isSaving()) {
			return confirmation === ConfirmResult.CANCEL ? true : false;
		}

		// Otherwise, handle accordingly
		switch (confirmation) {
			case ConfirmResult.SAVE:
				const result = await editor.save(this.id, { reason: saveReason });
				if (!result && autoSave) {
					// Save failed and we need to signal this back to the user, so
					// we handle the dirty editor again but this time ensuring to
					// show the confirm dialog
					// (see https://github.com/microsoft/vscode/issues/108752)
					return this.doHandleDirtyClosing(editor, { skipAutoSave: true });
				}

				return editor.isDirty(); // veto if still dirty
			case ConfirmResult.DONT_SAVE:
				try {

					// first try a normal revert where the contents of the editor are restored
					await editor.revert(this.id);

					return editor.isDirty(); // veto if still dirty
				} catch (error) {
					// if that fails, since we are about to close the editor, we accept that
					// the editor cannot be reverted and instead do a soft revert that just
					// enables us to close the editor. With this, a user can always close a
					// dirty editor even when reverting fails.
					await editor.revert(this.id, { soft: true });

					return editor.isDirty(); // veto if still dirty
				}
			case ConfirmResult.CANCEL:
				return true; // veto
		}
	}