Change:
Set auto save: afterDelay Edit a notebook See the dirty indicator briefly appear after editing
I guess this is
We never did that for custom editors because it requires code to ensure that an editor that is closed but pending to be saved via auto save is really waited upon on shutdown. To further explain: the behaviour is driven by method if you return from that method no dirty indicator will show up this means a user can close an editor with dirty changes as such, components have to ensure that the save continues in the background and even on shutdown Files implement this here: I think the reason we never adopted this for notebooks as that notebooks have a dual model: complex and simple. With simple (which is based on file working copies), we can probably implement this now similar to what files do, but it needs careful testing what happens when you close a notebook quickly enough before auto save finished.
We could revisit this once we remove complex notebook model and friends but the saving of notebooks is more complex than files and I wonder if it's worth it to implement this. Also the dirty indicator is also an indicator that saving is not done yet, which is valuable as saving notebooks is not necessarily instant. what do you think ?
I think it's ok to close if this is complicated. It's also a bit odd that if I edit and immediately close the tab, it asks me whether I want to save, when I've enabled autosave.
with your recent changes in and please take note of Specifically: being able to close an editor that is now not dirty because of auto-save may result in data loss if closing the editor disposes state that is needed for saving. Filed as planned for July. Nevertheless, I appreciate that you are willing to look into this issue

Discussion:
readonly resource: URI; readonly viewType: string; readonly notebook: INotebookTextModel | undefined; <ins> readonly hasErrorState: boolean; readonly hasConflictState: boolean; </ins> isResolved(): this is IResolvedNotebookEditorModel; isDirty(): boolean; isModified(): boolean;

import { IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { NotebookPerfMarks } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; <del> import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; </del> <ins> import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; </ins> import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

return this._editorModelReference.object.isDirty(); } <ins> override isSaving(): boolean { const model = this._editorModelReference?.object; if (!model || !model.isDirty() || model.hasErrorState || model.hasConflictState) { return false; // require the model to be dirty and not in error or conflict state } // if a short auto save is configured, treat this as being saved return this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY; } </ins> override async save(group: GroupIdentifier, options?: ISaveOptions): Promise<EditorInput | IUntypedEditorInput | undefined> { if (this._editorModelReference) {

} } <ins> get hasErrorState(): boolean { if (this._workingCopy && 'hasState' in this._workingCopy) { return this._workingCopy.hasState(StoredFileWorkingCopyState.ERROR); } return false; } get hasConflictState(): boolean { if (this._workingCopy && 'hasState' in this._workingCopy) { return this._workingCopy.hasState(StoredFileWorkingCopyState.CONFLICT); } return false; } </ins> revert(options?: IRevertOptions): Promise<void> { assertType(this.isResolved()); return this._workingCopy!.revert(options);

this._workingCopyListeners.add(this._workingCopy.onDidChangeOrphaned(() => this._onDidChangeOrphaned.fire())); this._workingCopyListeners.add(this._workingCopy.onDidChangeReadonly(() => this._onDidChangeReadonly.fire())); } <del> this._workingCopy.onDidChangeDirty(() => this._onDidChangeDirty.fire(), undefined, this._workingCopyListeners); </del> <ins> this._workingCopyListeners.add(this._workingCopy.onDidChangeDirty(() => this._onDidChangeDirty.fire(), undefined, this._workingCopyListeners)); </ins> this._workingCopyListeners.add(this._workingCopy.onWillDispose(() => { this._workingCopyListeners.clear();

return this._dirty; } <ins> get hasErrorState() { return false; } get hasConflictState() { return false; } </ins> isModified(): boolean { return this._dirty; }