Open amunger opened 1 year ago
After digging into the code a bit, I found a similar issue https://github.com/microsoft/vscode/issues/138850, which was fixed by checking if the stored working copy is created by the right owner to avoid it being disposed unexpected.
Here it happens in a similar fashion:
untitled 1
), reopen in Jupyter Notebook
test.ipynb
) test.ipynb
(let's call it workingCopy1
) created by https://github.com/microsoft/vscode/blob/86528d8df2c142c9cf8ccf80913b88f97c5e97bd/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts#L389TextDocument
NotebooEditorModel
, which will load a working copy, and since we don't have any ref-counting, it loads workingCopy1
NotebookEditorModel
workingCopy1
https://github.com/microsoft/vscode/blob/86528d8df2c142c9cf8ccf80913b88f97c5e97bd/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts#L67workingCopy1
is already disposed, and it quits early https://github.com/microsoft/vscode/blob/86528d8df2c142c9cf8ccf80913b88f97c5e97bd/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts#L934-L942Since we are not ref-counting working copy objects, and we share the same object for the resource, it can get disposed by the wrong caller (like https://github.com/microsoft/vscode/issues/138850). I'm not sure what's the easy fix for this case though, as we definitely want to dispose the working copy from the editor model (this is actually the most common scenario). @bpasero any suggestion here?
Note that, there isn't any data loss. The new file is created and saved to disk properly, it just doesn't replace the dirty untitled editor with the new resource because of the early return.
@rebornix this is a consequence of the notebook model collection behaving slightly different from the text model collection.
The text model collection has an extra safe guard, to never allow to destroy a dirty working copy:
This is provided by a canDispose
method here:
We provide the same method for stored file working copies here:
But it needs to be called from the notebook model collection in a similar fashion here:
The only alternative I can think of is to somehow pass the reference collection down to the file working copy manager so that the ref counting becomes correct, but I would actually suggest to align the behaviour here and do the same as text files do.
Oh well, I had forgotten about this code:
So we do have a mechanism in place to auto-reference when the notebook becomes dirty to ensure we never destroy a dirty notebook working copy. I think here in this case the notebook is not dirty and thus gets disposed for good.
The real issue here is that a notebook working copy gets into existence without being ref counted:
This will only happen from "Save As" invocations, or when saving an untitled notebook. Already back then I was not a big fan of having to do that because it means the lifecycle of the working copy is not connected to the outside world that is ref counting.
I am not sure how to fix this actually...
I think it boils down to teaching the notebook collection about a +1 on the reference from within the file working copy manager.
@bpasero thanks for the explanation, I tried to leverage "canDispose" and found it work great https://github.com/microsoft/vscode/pull/180560 . My understanding is while saving+formatting, the notebook model is still dirty so it can't be disposed, but it will only dispose after the StoredFileWorkingCopy finish saving (save participants + fs save).
However I'm not sure how to handle untitled https://github.com/microsoft/vscode/pull/180560/files#diff-9a4a06f8a593491389b6ab180d612e734522b33432a84c30d58485e09dd864f3R87 as canDispose
is implemented in IStoredFileWorkingCopyManager but not IUntitledFileWorkingCopyManager
.
Yes, the gist of that code is to prolong the dispose for as long as the notebook is dirty. But I wonder if this bug can still reproduce when you:
This would very likely reproduce for files as well but I guess so far we never had to resolve the model again as part of the save participant. Instead we are passing around the model:
I wonder if we could do the same for notebooks as a mitigation? In the end the working copy one also passes the working copy around, so no need to resolve it again:
The change you did will work for when the notebook is dirty, and I think you already figured out the subtle nuances that are required to track a reference being created while you are waiting for canDispose
, specifically the modelsToDispose
Set:
If we go down this path I wonder if there should be a common base class that we can reuse for both files and notebooks so that we do not duplicate this logic.
I still think it would be cleaner to pass down the ref-counted-collection to the manager.
As for untitled working copies / files: I think I never had the need to implement canDispose
there because there were no issues with the way how untitled copies are managed. Again, I am not super happy with canDispose
and would not want to spread it further if we can avoid it. To clarify, the auto-ref from dirty working copies present today in the notebook resolver was explicitly done to avoid this hack and I liked it (I think Jo did it).
I looked at your change and it is ✅ , meaning its the same as text files now. Maybe we leave it like that for now...
I looked into where we resolve a working copy from within the manager and there are 2 locations:
Save As (this issue): https://github.com/microsoft/vscode/blob/3a69e153f6c68b2b855fb2f1e5bdb798a16a1ee4/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts#L467-L471
Restore from move/copy Ops https://github.com/microsoft/vscode/blob/3a69e153f6c68b2b855fb2f1e5bdb798a16a1ee4/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts#L417-L420
Even if we had the reference collection being passed down all the way to there, I would not even know when to dispose the reference in these 2 places :-/. Here I have to assume that the components that create the working copy managers own the lifecycle of all working copies.
notebook.formatOnSave.enabled
= truesaved.ipynb
:bug: a blank notebook filesaved.ipynb
is created and the untitled notebook remains open expected: the saved notebook should be open with correct contents.