@@ -716,8 +716,18 @@ async function webviewPreloads(ctx: PreloadContext) {
716
716
} ;
717
717
}
718
718
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 ;
721
731
const errList = document . createElement ( 'ul' ) ;
722
732
for ( const result of errors ) {
723
733
console . error ( result ) ;
@@ -1087,8 +1097,8 @@ async function webviewPreloads(ctx: PreloadContext) {
1087
1097
1088
1098
case 'html' : {
1089
1099
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 ) ;
1092
1102
} ) ;
1093
1103
break ;
1094
1104
}
@@ -1336,27 +1346,32 @@ async function webviewPreloads(ctx: PreloadContext) {
1336
1346
} ;
1337
1347
1338
1348
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 > } > ( ) ;
1340
1350
1341
1351
/**
1342
1352
* Pushes the action onto the list of actions for the given output ID,
1343
1353
* ensuring that it's run in-order.
1344
1354
*/
1345
- public enqueue ( outputId : string , action : ( record : { cancelled : boolean } ) => unknown ) {
1355
+ public enqueue ( outputId : string , action : ( cancelSignal : AbortSignal ) => unknown ) {
1346
1356
const record = this . outputs . get ( outputId ) ;
1347
1357
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 ) ) ) } ) ;
1349
1360
} 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
+ } ) ;
1351
1366
}
1352
1367
}
1353
1368
1354
1369
/**
1355
1370
* Cancels the rendering of all outputs.
1356
1371
*/
1357
1372
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 ( ) ;
1360
1375
}
1361
1376
this . outputs . clear ( ) ;
1362
1377
}
@@ -1367,7 +1382,7 @@ async function webviewPreloads(ctx: PreloadContext) {
1367
1382
public cancelOutput ( outputId : string ) {
1368
1383
const output = this . outputs . get ( outputId ) ;
1369
1384
if ( output ) {
1370
- output . cancelled = true ;
1385
+ output . abort . abort ( ) ;
1371
1386
this . outputs . delete ( outputId ) ;
1372
1387
}
1373
1388
}
@@ -1458,8 +1473,8 @@ async function webviewPreloads(ctx: PreloadContext) {
1458
1473
}
1459
1474
1460
1475
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 ) ;
1463
1478
1464
1479
if ( ! renderers . length ) {
1465
1480
const errorContainer = document . createElement ( 'div' ) ;
@@ -1482,12 +1497,23 @@ async function webviewPreloads(ctx: PreloadContext) {
1482
1497
}
1483
1498
1484
1499
// 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 ] ;
1486
1502
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 ;
1490
1514
}
1515
+
1516
+ await renderOutputItem ( renderApi , renderer [ 0 ] , info , element , signal ) ;
1491
1517
}
1492
1518
} ( ) ;
1493
1519
@@ -1610,22 +1636,18 @@ async function webviewPreloads(ctx: PreloadContext) {
1610
1636
}
1611
1637
}
1612
1638
1613
- public async renderOutputCell ( data : webviewMessages . ICreationRequestMessage , state : { cancelled : boolean } ) : Promise < void > {
1639
+ public async renderOutputCell ( data : webviewMessages . ICreationRequestMessage , signal : AbortSignal ) : Promise < void > {
1614
1640
const preloadsAndErrors = await Promise . all < unknown > ( [
1615
1641
data . rendererId ? renderers . load ( data . rendererId ) : undefined ,
1616
1642
...data . requiredPreloads . map ( p => kernelPreloads . waitFor ( p . uri ) ) ,
1617
1643
] . map ( p => p ?. catch ( err => err ) ) ) ;
1618
1644
1619
- if ( state . cancelled ) {
1645
+ if ( signal . aborted ) {
1620
1646
return ;
1621
1647
}
1622
1648
1623
1649
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 ) ;
1629
1651
}
1630
1652
1631
1653
public ensureOutputCell ( cellId : string , cellTop : number , skipCellTopUpdateIfExist : boolean ) : OutputCell {
@@ -1927,7 +1949,6 @@ async function webviewPreloads(ctx: PreloadContext) {
1927
1949
}
1928
1950
1929
1951
class OutputCell {
1930
-
1931
1952
public readonly element : HTMLElement ;
1932
1953
private readonly outputElements = new Map < /*outputId*/ string , OutputContainer > ( ) ;
1933
1954
@@ -1957,15 +1978,23 @@ async function webviewPreloads(ctx: PreloadContext) {
1957
1978
this . outputElements . clear ( ) ;
1958
1979
}
1959
1980
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 ) ;
1962
1983
if ( ! outputContainer ) {
1963
- outputContainer = new OutputContainer ( outputId ) ;
1984
+ outputContainer = new OutputContainer ( data . outputId ) ;
1964
1985
this . element . appendChild ( outputContainer . element ) ;
1965
- this . outputElements . set ( outputId , outputContainer ) ;
1986
+ this . outputElements . set ( data . outputId , outputContainer ) ;
1966
1987
}
1967
1988
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' ;
1969
1998
}
1970
1999
1971
2000
public clearOutput ( outputId : string , rendererId : string | undefined ) {
@@ -2091,7 +2120,11 @@ async function webviewPreloads(ctx: PreloadContext) {
2091
2120
2092
2121
class OutputElement {
2093
2122
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
+ } ;
2095
2128
private hasResizeObserver = false ;
2096
2129
2097
2130
private renderTaskAbort ?: AbortController ;
@@ -2122,34 +2155,34 @@ async function webviewPreloads(ctx: PreloadContext) {
2122
2155
this . renderTaskAbort = undefined ;
2123
2156
}
2124
2157
2125
- public async render ( content : webviewMessages . ICreationContent , preloadsAndErrors : unknown [ ] ) {
2158
+ public async render ( content : webviewMessages . ICreationContent , rendererId : string , preloadsAndErrors : unknown [ ] , signal ?: AbortSignal ) {
2126
2159
this . renderTaskAbort ?. abort ( ) ;
2127
2160
this . renderTaskAbort = undefined ;
2128
2161
2129
- this . _content = { content, preloadsAndErrors } ;
2162
+ this . _content = { content, rendererId , preloadsAndErrors } ;
2130
2163
if ( content . type === 0 /* RenderOutputType.Html */ ) {
2131
2164
const trustedHtml = ttPolicy ?. createHTML ( content . htmlContent ) ?? content . htmlContent ;
2132
2165
this . element . innerHTML = trustedHtml as string ;
2133
2166
domEval ( this . element ) ;
2134
2167
} else if ( preloadsAndErrors . some ( e => e instanceof Error ) ) {
2135
2168
const errors = preloadsAndErrors . filter ( ( e ) : e is Error => e instanceof Error ) ;
2136
- showPreloadErrors ( this . element , ... errors ) ;
2169
+ showRenderError ( `Error loading preloads` , this . element , errors ) ;
2137
2170
} else {
2138
2171
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 ) ;
2141
2173
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 ;
2150
2185
}
2151
- } catch ( e ) {
2152
- showPreloadErrors ( this . element , e ) ;
2153
2186
}
2154
2187
}
2155
2188
@@ -2188,14 +2221,14 @@ async function webviewPreloads(ctx: PreloadContext) {
2188
2221
2189
2222
public rerender ( ) {
2190
2223
if ( this . _content ) {
2191
- this . render ( this . _content . content , this . _content . preloadsAndErrors ) ;
2224
+ this . render ( this . _content . content , this . _content . rendererId , this . _content . preloadsAndErrors ) ;
2192
2225
}
2193
2226
}
2194
2227
2195
2228
public updateAndRerender ( content : webviewMessages . ICreationContent ) {
2196
2229
if ( this . _content ) {
2197
2230
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 ) ;
2199
2232
}
2200
2233
}
2201
2234
}
0 commit comments