serdarciplak / BlazorMonaco

Blazor component for Microsoft's Monaco Editor which powers Visual Studio Code.
https://serdarciplak.github.io/BlazorMonaco/
MIT License
449 stars 100 forks source link

createDiffEditor throws exception when old editor's models are disposed of #102

Closed j-schwar closed 7 months ago

j-schwar commented 1 year ago

I've run into an issue while using StandaloneDiffEditor when it's underlying text models have been disposed.

In my case, I've got a tab system where the editor is shared between tabs with text models being swapped in and out. When a tab is closed, I dispose of the text model. For diff editors, I dispose of the orginal and modified text models individually (some times I only want to dispose of the original model since the modified one is shared with another tab). However, this causes an issue when the diff editor is reused for a different tab. createDiffEditor is called again, it retrieves the oldEditor by id, then fetches the old editor's model which returns an object like { original: null, modified: null } which makes sense since I've disposed of both the original and modified models. At the end of createDiffEditor, it attempts to set the model for the new editor, but since original (and modified) are null it throws an exception.

I think the solution is probably as simple as improving the null check on oldModel to include a check on the original and modified fields as well:

if (oldModel !== null && oldModel?.original !== null && oldModel?.modified !== null)
    editor.setModel(oldModel);

Stack trace of the error:

Error: Microsoft.JSInterop.JSException: DiffEditorWidget.setModel: Original model is null
Error: DiffEditorWidget.setModel: Original model is null
    at M.setModel (http://localhost:5000/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js:700:49890)
    at Object.createDiffEditor (http://localhost:5000/_content/BlazorMonaco/jsInterop.js:122:20)
    at http://localhost:5000/_framework/blazor.server.js:1:3506
    at new Promise (<anonymous>)
    at Ft.beginInvokeJSFromDotNet (http://localhost:5000/_framework/blazor.server.js:1:3480)
    at Ft._invokeClientMethod (http://localhost:5000/_framework/blazor.server.js:1:75072)
    at Ft._processIncomingData (http://localhost:5000/_framework/blazor.server.js:1:72696)
    at Ft.connection.onreceive (http://localhost:5000/_framework/blazor.server.js:1:67009)
    at i.onmessage (http://localhost:5000/_framework/blazor.server.js:1:51322)
   at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
   at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(IJSRuntime jsRuntime, String identifier, Object[] args)
   at BlazorMonaco.Helpers.JsRuntimeExt.SafeInvokeAsync(IJSRuntime jsRuntime, String identifier, Object[] args)
   at BlazorMonaco.Editor.Global.CreateDiffEditor(IJSRuntime jsRuntime, String domElementId, StandaloneDiffEditorConstructionOptions options, EditorOverrideServices overrideServices, DotNetObjectReference`1 dotnetObjectRef, DotNetObjectReference`1 dotnetObjectRefOriginal, DotNetObjectReference`1 dotnetObjectRefModified)
   at BlazorMonaco.Editor.StandaloneDiffEditor.OnAfterRenderAsync(Boolean firstRender)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
serdarciplak commented 7 months ago

Added this in #120 with other small fixes. It will be released in BlazorMonaco v3.2.0