@@ -716,8 +716,18 @@ async function webviewPreloads(ctx: PreloadContext) {
716716 } ;
717717 }
718718
719- function showPreloadErrors ( outputNode : HTMLElement , ...errors : readonly Error [ ] ) {
720- outputNode . innerText = `Error loading preloads:` ;
719+ async function renderOutputItem ( rendererApi : rendererApi . RendererApi , rendererId : string , item : rendererApi . OutputItem , element : HTMLElement , signal : AbortSignal ) {
720+ try {
721+ await rendererApi . renderOutputItem ( item , element , signal ) ;
722+ } catch ( e ) {
723+ if ( ! signal . aborted ) {
724+ showRenderError ( `Error rendering output item using '${ rendererId } '` , element , e instanceof Error ? [ e ] : [ ] ) ;
725+ }
726+ }
727+ }
728+
729+ function showRenderError ( errorText : string , outputNode : HTMLElement , errors : readonly Error [ ] ) {
730+ outputNode . innerText = errorText ;
721731 const errList = document . createElement ( 'ul' ) ;
722732 for ( const result of errors ) {
723733 console . error ( result ) ;
@@ -1087,8 +1097,8 @@ async function webviewPreloads(ctx: PreloadContext) {
10871097
10881098 case 'html' : {
10891099 const data = event . data ;
1090- outputRunner . enqueue ( data . outputId , ( state ) => {
1091- return viewModel . renderOutputCell ( data , state ) ;
1100+ outputRunner . enqueue ( data . outputId , signal => {
1101+ return viewModel . renderOutputCell ( data , signal ) ;
10921102 } ) ;
10931103 break ;
10941104 }
@@ -1336,27 +1346,32 @@ async function webviewPreloads(ctx: PreloadContext) {
13361346 } ;
13371347
13381348 const outputRunner = new class {
1339- private readonly outputs = new Map < string , { cancelled : boolean ; queue : Promise < unknown > } > ( ) ;
1349+ private readonly outputs = new Map < string , { abort : AbortController ; queue : Promise < unknown > } > ( ) ;
13401350
13411351 /**
13421352 * Pushes the action onto the list of actions for the given output ID,
13431353 * ensuring that it's run in-order.
13441354 */
1345- public enqueue ( outputId : string , action : ( record : { cancelled : boolean } ) => unknown ) {
1355+ public enqueue ( outputId : string , action : ( cancelSignal : AbortSignal ) => unknown ) {
13461356 const record = this . outputs . get ( outputId ) ;
13471357 if ( ! record ) {
1348- this . outputs . set ( outputId , { cancelled : false , queue : new Promise ( r => r ( action ( { cancelled : false } ) ) ) } ) ;
1358+ const controller = new AbortController ( ) ;
1359+ this . outputs . set ( outputId , { abort : controller , queue : new Promise ( r => r ( action ( controller . signal ) ) ) } ) ;
13491360 } else {
1350- record . queue = record . queue . then ( r => ! record . cancelled && action ( record ) ) ;
1361+ record . queue = record . queue . then ( r => {
1362+ if ( ! record . abort . signal . aborted ) {
1363+ return action ( record . abort . signal ) ;
1364+ }
1365+ } ) ;
13511366 }
13521367 }
13531368
13541369 /**
13551370 * Cancels the rendering of all outputs.
13561371 */
13571372 public cancelAll ( ) {
1358- for ( const record of this . outputs . values ( ) ) {
1359- record . cancelled = true ;
1373+ for ( const { abort } of this . outputs . values ( ) ) {
1374+ abort . abort ( ) ;
13601375 }
13611376 this . outputs . clear ( ) ;
13621377 }
@@ -1367,7 +1382,7 @@ async function webviewPreloads(ctx: PreloadContext) {
13671382 public cancelOutput ( outputId : string ) {
13681383 const output = this . outputs . get ( outputId ) ;
13691384 if ( output ) {
1370- output . cancelled = true ;
1385+ output . abort . abort ( ) ;
13711386 this . outputs . delete ( outputId ) ;
13721387 }
13731388 }
@@ -1458,8 +1473,8 @@ async function webviewPreloads(ctx: PreloadContext) {
14581473 }
14591474
14601475 public async render ( info : rendererApi . OutputItem , element : HTMLElement , signal : AbortSignal ) : Promise < void > {
1461- const renderers = Array . from ( this . _renderers . values ( ) )
1462- . filter ( renderer => renderer . data . mimeTypes . includes ( info . mime ) && ! renderer . data . extends ) ;
1476+ const renderers = Array . from ( this . _renderers . entries ( ) )
1477+ . filter ( ( [ _ , renderer ] ) => renderer . data . mimeTypes . includes ( info . mime ) && ! renderer . data . extends ) ;
14631478
14641479 if ( ! renderers . length ) {
14651480 const errorContainer = document . createElement ( 'div' ) ;
@@ -1482,12 +1497,23 @@ async function webviewPreloads(ctx: PreloadContext) {
14821497 }
14831498
14841499 // De-prioritize built-in renderers
1485- renderers . sort ( ( a , b ) => + a . data . isBuiltin - + b . data . isBuiltin ) ;
1500+ renderers . sort ( ( a , b ) => + a [ 1 ] . data . isBuiltin - + b [ 1 ] . data . isBuiltin ) ;
1501+ const renderer = renderers [ 0 ] ;
14861502
1487- const renderer = await renderers [ 0 ] . load ( ) ;
1488- if ( renderer ) {
1489- await renderer . renderOutputItem ( info , element , signal ) ;
1503+ let renderApi : rendererApi . RendererApi | undefined ;
1504+ try {
1505+ renderApi = await renderer [ 1 ] . load ( ) ;
1506+ if ( ! renderApi ) {
1507+ return ;
1508+ }
1509+ } catch ( e ) {
1510+ if ( ! signal . aborted ) {
1511+ showRenderError ( `Error loading renderer '${ renderer [ 0 ] } '` , element , e instanceof Error ? [ e ] : [ ] ) ;
1512+ }
1513+ return ;
14901514 }
1515+
1516+ await renderOutputItem ( renderApi , renderer [ 0 ] , info , element , signal ) ;
14911517 }
14921518 } ( ) ;
14931519
@@ -1610,22 +1636,18 @@ async function webviewPreloads(ctx: PreloadContext) {
16101636 }
16111637 }
16121638
1613- public async renderOutputCell ( data : webviewMessages . ICreationRequestMessage , state : { cancelled : boolean } ) : Promise < void > {
1639+ public async renderOutputCell ( data : webviewMessages . ICreationRequestMessage , signal : AbortSignal ) : Promise < void > {
16141640 const preloadsAndErrors = await Promise . all < unknown > ( [
16151641 data . rendererId ? renderers . load ( data . rendererId ) : undefined ,
16161642 ...data . requiredPreloads . map ( p => kernelPreloads . waitFor ( p . uri ) ) ,
16171643 ] . map ( p => p ?. catch ( err => err ) ) ) ;
16181644
1619- if ( state . cancelled ) {
1645+ if ( signal . aborted ) {
16201646 return ;
16211647 }
16221648
16231649 const cellOutput = this . ensureOutputCell ( data . cellId , data . cellTop , false ) ;
1624- const outputNode = cellOutput . createOutputElement ( data . outputId , data . outputOffset , data . left , data . cellId ) ;
1625- outputNode . render ( data . content , preloadsAndErrors ) ;
1626-
1627- // don't hide until after this step so that the height is right
1628- cellOutput . element . style . visibility = data . initiallyHidden ? 'hidden' : 'visible' ;
1650+ await cellOutput . renderOutputElement ( data , data . rendererId ?? 'preload' , preloadsAndErrors , signal ) ;
16291651 }
16301652
16311653 public ensureOutputCell ( cellId : string , cellTop : number , skipCellTopUpdateIfExist : boolean ) : OutputCell {
@@ -1927,7 +1949,6 @@ async function webviewPreloads(ctx: PreloadContext) {
19271949 }
19281950
19291951 class OutputCell {
1930-
19311952 public readonly element : HTMLElement ;
19321953 private readonly outputElements = new Map < /*outputId*/ string , OutputContainer > ( ) ;
19331954
@@ -1957,15 +1978,23 @@ async function webviewPreloads(ctx: PreloadContext) {
19571978 this . outputElements . clear ( ) ;
19581979 }
19591980
1960- public createOutputElement ( outputId : string , outputOffset : number , left : number , cellId : string ) : OutputElement {
1961- let outputContainer = this . outputElements . get ( outputId ) ;
1981+ private createOutputElement ( data : webviewMessages . ICreationRequestMessage ) : OutputElement {
1982+ let outputContainer = this . outputElements . get ( data . outputId ) ;
19621983 if ( ! outputContainer ) {
1963- outputContainer = new OutputContainer ( outputId ) ;
1984+ outputContainer = new OutputContainer ( data . outputId ) ;
19641985 this . element . appendChild ( outputContainer . element ) ;
1965- this . outputElements . set ( outputId , outputContainer ) ;
1986+ this . outputElements . set ( data . outputId , outputContainer ) ;
19661987 }
19671988
1968- return outputContainer . createOutputElement ( outputId , outputOffset , left , cellId ) ;
1989+ return outputContainer . createOutputElement ( data . outputId , data . outputOffset , data . left , data . cellId ) ;
1990+ }
1991+
1992+ public async renderOutputElement ( data : webviewMessages . ICreationRequestMessage , rendererId : string , preloadsAndErrors : unknown [ ] , signal : AbortSignal ) {
1993+ const outputElement = this . createOutputElement ( data ) ;
1994+ await outputElement . render ( data . content , rendererId , preloadsAndErrors , signal ) ;
1995+
1996+ // don't hide until after this step so that the height is right
1997+ outputElement . element . style . visibility = data . initiallyHidden ? 'hidden' : 'visible' ;
19691998 }
19701999
19712000 public clearOutput ( outputId : string , rendererId : string | undefined ) {
@@ -2091,7 +2120,11 @@ async function webviewPreloads(ctx: PreloadContext) {
20912120
20922121 class OutputElement {
20932122 public readonly element : HTMLElement ;
2094- private _content ?: { content : webviewMessages . ICreationContent ; preloadsAndErrors : unknown [ ] } ;
2123+ private _content ?: {
2124+ content : webviewMessages . ICreationContent ;
2125+ rendererId : string ;
2126+ preloadsAndErrors : unknown [ ] ;
2127+ } ;
20952128 private hasResizeObserver = false ;
20962129
20972130 private renderTaskAbort ?: AbortController ;
@@ -2122,34 +2155,34 @@ async function webviewPreloads(ctx: PreloadContext) {
21222155 this . renderTaskAbort = undefined ;
21232156 }
21242157
2125- public async render ( content : webviewMessages . ICreationContent , preloadsAndErrors : unknown [ ] ) {
2158+ public async render ( content : webviewMessages . ICreationContent , rendererId : string , preloadsAndErrors : unknown [ ] , signal ?: AbortSignal ) {
21262159 this . renderTaskAbort ?. abort ( ) ;
21272160 this . renderTaskAbort = undefined ;
21282161
2129- this . _content = { content, preloadsAndErrors } ;
2162+ this . _content = { content, rendererId , preloadsAndErrors } ;
21302163 if ( content . type === 0 /* RenderOutputType.Html */ ) {
21312164 const trustedHtml = ttPolicy ?. createHTML ( content . htmlContent ) ?? content . htmlContent ;
21322165 this . element . innerHTML = trustedHtml as string ;
21332166 domEval ( this . element ) ;
21342167 } else if ( preloadsAndErrors . some ( e => e instanceof Error ) ) {
21352168 const errors = preloadsAndErrors . filter ( ( e ) : e is Error => e instanceof Error ) ;
2136- showPreloadErrors ( this . element , ... errors ) ;
2169+ showRenderError ( `Error loading preloads` , this . element , errors ) ;
21372170 } else {
21382171 const rendererApi = preloadsAndErrors [ 0 ] as rendererApi . RendererApi ;
2139- try {
2140- const item = createOutputItem ( this . outputId , content . mimeType , content . metadata , content . valueBytes ) ;
2172+ const item = createOutputItem ( this . outputId , content . mimeType , content . metadata , content . valueBytes ) ;
21412173
2142- const controller = new AbortController ( ) ;
2143- this . renderTaskAbort = controller ;
2144- try {
2145- await rendererApi . renderOutputItem ( item , this . element , this . renderTaskAbort . signal ) ;
2146- } finally {
2147- if ( this . renderTaskAbort === controller ) {
2148- this . renderTaskAbort = undefined ;
2149- }
2174+ const controller = new AbortController ( ) ;
2175+ this . renderTaskAbort = controller ;
2176+
2177+ // Abort rendering if caller aborts
2178+ signal ?. addEventListener ( 'abort' , ( ) => controller . abort ( ) ) ;
2179+
2180+ try {
2181+ await renderOutputItem ( rendererApi , rendererId , item , this . element , controller . signal ) ;
2182+ } finally {
2183+ if ( this . renderTaskAbort === controller ) {
2184+ this . renderTaskAbort = undefined ;
21502185 }
2151- } catch ( e ) {
2152- showPreloadErrors ( this . element , e ) ;
21532186 }
21542187 }
21552188
@@ -2188,14 +2221,14 @@ async function webviewPreloads(ctx: PreloadContext) {
21882221
21892222 public rerender ( ) {
21902223 if ( this . _content ) {
2191- this . render ( this . _content . content , this . _content . preloadsAndErrors ) ;
2224+ this . render ( this . _content . content , this . _content . rendererId , this . _content . preloadsAndErrors ) ;
21922225 }
21932226 }
21942227
21952228 public updateAndRerender ( content : webviewMessages . ICreationContent ) {
21962229 if ( this . _content ) {
21972230 this . _content . content = content ;
2198- this . render ( this . _content . content , this . _content . preloadsAndErrors ) ;
2231+ this . render ( this . _content . content , this . _content . rendererId , this . _content . preloadsAndErrors ) ;
21992232 }
22002233 }
22012234 }
0 commit comments