Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/vs/workbench/api/browser/mainThreadCustomEditors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc

const disposeSub = webviewInput.webview.onDidDispose(() => {
disposeSub.dispose();
inputDisposeSub.dispose();

// If the model is still dirty, make sure we have time to save it
if (modelRef.object.isDirty()) {
Expand All @@ -191,6 +192,14 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
modelRef.dispose();
});

// Also listen for when the input is disposed (e.g., during SaveAs when the webview is transferred to a new editor).
// In this case, webview.onDidDispose won't fire because the webview is reused.
const inputDisposeSub = webviewInput.onWillDispose(() => {
inputDisposeSub.dispose();
disposeSub.dispose();
modelRef.dispose();
});

if (capabilities.supportsMove) {
webviewInput.onMove(async (newResource: URI) => {
const oldModel = modelRef;
Expand Down Expand Up @@ -647,7 +656,9 @@ class MainThreadCustomEditorModel extends ResourceWorkingCopy implements ICustom
// TODO: handle cancellation
await createCancelablePromise(token => this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource, token));
this.change(() => {
this._isDirtyFromContentChange = false;
this._savePoint = this._currentEditIndex;
this._fromBackup = false;
});
return true;
} else {
Expand Down
10 changes: 7 additions & 3 deletions src/vs/workbench/api/common/extHostCustomEditors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ class CustomDocumentStore {
return entry;
}

public delete(viewType: string, document: vscode.CustomDocument) {
const key = this.key(viewType, document.uri);
public delete(viewType: string, resource: vscode.Uri) {
// Use the resource parameter directly instead of document.uri, because the document's
// URI may have changed (e.g., after SaveAs from untitled to a file path).
const key = this.key(viewType, resource);
this._documents.delete(key);
}

Expand Down Expand Up @@ -242,7 +244,9 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor

const revivedResource = URI.revive(resource);
const { document } = this.getCustomDocumentEntry(viewType, revivedResource);
this._documents.delete(viewType, document);
// Pass the resource we used to look up the document, not document.uri,
// because the document's URI may have changed (e.g., after SaveAs).
this._documents.delete(viewType, revivedResource);
document.dispose();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,15 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
): EditorInput {
return instantiationService.invokeFunction(accessor => {
// If it's an untitled file we must populate the untitledDocumentData
const untitledString = accessor.get(IUntitledTextEditorService).getValue(init.resource);
const untitledTextEditorService = accessor.get(IUntitledTextEditorService);
const untitledTextModel = untitledTextEditorService.get(init.resource);
const untitledString = untitledTextModel?.textEditorModel?.getValue();
const untitledDocumentData = untitledString ? VSBuffer.fromString(untitledString) : undefined;

// If we're taking over an untitled text editor, revert it so it's no longer
// tracked as a dirty working copy (fixes #125293).
untitledTextModel?.revert();

const webview = accessor.get(IWebviewService).createWebviewOverlay({
providedViewType: init.viewType,
title: init.webviewTitle,
Expand Down
Loading