@@ -183,13 +183,92 @@ export class CompareView extends AbstractWebview<
183183 message = getErrorMessage ( e ) ;
184184 }
185185
186+ await this . streamResults ( result , currentResultSetDisplayName , message ) ;
187+ }
188+ }
189+
190+ private async streamResults (
191+ result : QueryCompareResult | undefined ,
192+ currentResultSetName : string ,
193+ message : string | undefined ,
194+ ) {
195+ // Since there is a string limit of 1GB in Node.js, the comparison is send as a JSON.stringified string to the webview
196+ // and some comparisons may be larger than that, we sometimes need to stream results. This uses a heuristic of 2,000 results
197+ // to determine if we should stream results.
198+
199+ if ( ! this . shouldStreamResults ( result ) ) {
186200 await this . postMessage ( {
187201 t : "setComparisons" ,
188202 result,
189- currentResultSetName : currentResultSetDisplayName ,
203+ currentResultSetName,
190204 message,
191205 } ) ;
206+ return ;
207+ }
208+
209+ // Streaming itself is implemented like this:
210+ // - 1 setup message which contains the first 1,000 results
211+ // - n "add results" messages which contain 1,000 results each
212+ // - 1 complete message which just tells the webview that we're done
213+
214+ await this . postMessage ( {
215+ t : "streamingComparisonSetup" ,
216+ result : this . chunkResults ( result , 0 , 1000 ) ,
217+ currentResultSetName,
218+ message,
219+ } ) ;
220+
221+ const { from, to } = result ;
222+
223+ const maxResults = Math . max ( from . length , to . length ) ;
224+ for ( let i = 1000 ; i < maxResults ; i += 1000 ) {
225+ const chunk = this . chunkResults ( result , i , i + 1000 ) ;
226+
227+ await this . postMessage ( {
228+ t : "streamingComparisonAddResults" ,
229+ result : chunk ,
230+ } ) ;
231+ }
232+
233+ await this . postMessage ( {
234+ t : "streamingComparisonComplete" ,
235+ } ) ;
236+ }
237+
238+ private shouldStreamResults (
239+ result : QueryCompareResult | undefined ,
240+ ) : result is QueryCompareResult {
241+ if ( result === undefined ) {
242+ return false ;
192243 }
244+
245+ // We probably won't run into limits if we have less than 2,000 total results
246+ const totalResults = result . from . length + result . to . length ;
247+ return totalResults > 2000 ;
248+ }
249+
250+ private chunkResults (
251+ result : QueryCompareResult ,
252+ start : number ,
253+ end : number ,
254+ ) : QueryCompareResult {
255+ if ( result . kind === "raw" ) {
256+ return {
257+ ...result ,
258+ from : result . from . slice ( start , end ) ,
259+ to : result . to . slice ( start , end ) ,
260+ } ;
261+ }
262+
263+ if ( result . kind === "interpreted" ) {
264+ return {
265+ ...result ,
266+ from : result . from . slice ( start , end ) ,
267+ to : result . to . slice ( start , end ) ,
268+ } ;
269+ }
270+
271+ assertNever ( result ) ;
193272 }
194273
195274 protected getPanelConfig ( ) : WebviewPanelConfig {
0 commit comments