@@ -8,7 +8,7 @@ import * as detectIndent from 'detect-indent';
8
8
import { inject , injectable , multiInject , named } from 'inversify' ;
9
9
import * as path from 'path' ;
10
10
import * as uuid from 'uuid/v4' ;
11
- import { Event , EventEmitter , Memento , Uri , ViewColumn } from 'vscode' ;
11
+ import { CancellationToken , CancellationTokenSource , Event , EventEmitter , Memento , Uri , ViewColumn } from 'vscode' ;
12
12
13
13
import { concatMultilineStringInput , splitMultilineString } from '../../../datascience-ui/common' ;
14
14
import { createCodeCell , createErrorOutput } from '../../../datascience-ui/common/cellFactory' ;
@@ -21,10 +21,11 @@ import {
21
21
IWorkspaceService
22
22
} from '../../common/application/types' ;
23
23
import { ContextKey } from '../../common/contextKey' ;
24
- import { traceError } from '../../common/logger' ;
24
+ import { traceError , traceInfo } from '../../common/logger' ;
25
25
import { IFileSystem , TemporaryFile } from '../../common/platform/types' ;
26
26
import {
27
27
GLOBAL_MEMENTO ,
28
+ IAsyncDisposableRegistry ,
28
29
IConfigurationService ,
29
30
ICryptoUtils ,
30
31
IDisposableRegistry ,
@@ -55,7 +56,7 @@ import {
55
56
IInsertCell ,
56
57
INativeCommand ,
57
58
InteractiveWindowMessages ,
58
- IReExecuteCell ,
59
+ IReExecuteCells ,
59
60
IRemoveCell ,
60
61
ISaveAll ,
61
62
ISubmitNewCell ,
@@ -166,6 +167,8 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
166
167
private indentAmount : string = ' ' ;
167
168
private notebookJson : Partial < nbformat . INotebookContent > = { } ;
168
169
private debouncedWriteToStorage = debounce ( this . writeToStorage . bind ( this ) , 250 ) ;
170
+ private executeCancelTokens = new Set < CancellationTokenSource > ( ) ;
171
+ private _disposed = false ;
169
172
170
173
constructor (
171
174
@multiInject ( IInteractiveWindowListener ) listeners : IInteractiveWindowListener [ ] ,
@@ -195,7 +198,8 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
195
198
@inject ( ICryptoUtils ) private crypto : ICryptoUtils ,
196
199
@inject ( IExtensionContext ) private context : IExtensionContext ,
197
200
@inject ( ProgressReporter ) progressReporter : ProgressReporter ,
198
- @inject ( IExperimentsManager ) experimentsManager : IExperimentsManager
201
+ @inject ( IExperimentsManager ) experimentsManager : IExperimentsManager ,
202
+ @inject ( IAsyncDisposableRegistry ) asyncRegistry : IAsyncDisposableRegistry
199
203
) {
200
204
super (
201
205
progressReporter ,
@@ -231,8 +235,10 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
231
235
ViewColumn . Active ,
232
236
experimentsManager
233
237
) ;
238
+ asyncRegistry . push ( this ) ;
234
239
}
235
240
public dispose ( ) : Promise < void > {
241
+ this . _disposed = true ;
236
242
super . dispose ( ) ;
237
243
return this . close ( ) ;
238
244
}
@@ -279,7 +285,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
279
285
public onMessage ( message : string , payload : any ) {
280
286
super . onMessage ( message , payload ) ;
281
287
switch ( message ) {
282
- case InteractiveWindowMessages . ReExecuteCell :
288
+ case InteractiveWindowMessages . ReExecuteCells :
283
289
this . executedEvent . fire ( this ) ;
284
290
break ;
285
291
@@ -324,6 +330,14 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
324
330
this . handleMessage ( message , payload , this . clearAllOutputs ) ;
325
331
break ;
326
332
333
+ case InteractiveWindowMessages . RestartKernel :
334
+ this . interruptExecution ( ) ;
335
+ break ;
336
+
337
+ case InteractiveWindowMessages . Interrupt :
338
+ this . interruptExecution ( ) ;
339
+ break ;
340
+
327
341
default :
328
342
break ;
329
343
}
@@ -403,11 +417,12 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
403
417
line : number ,
404
418
id ?: string ,
405
419
data ?: nbformat . ICodeCell | nbformat . IRawCell | nbformat . IMarkdownCell ,
406
- debug ?: boolean
420
+ debug ?: boolean ,
421
+ cancelToken ?: CancellationToken
407
422
) : Promise < boolean > {
408
423
const stopWatch = new StopWatch ( ) ;
409
424
const submitCodePromise = super
410
- . submitCode ( code , file , line , id , data , debug )
425
+ . submitCode ( code , file , line , id , data , debug , cancelToken )
411
426
. finally ( ( ) => this . sendPerceivedCellExecute ( stopWatch ) ) ;
412
427
// When code is executed, update the version number in the metadata.
413
428
return submitCodePromise . then ( value => {
@@ -452,45 +467,35 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
452
467
453
468
@captureTelemetry ( Telemetry . ExecuteNativeCell , undefined , true )
454
469
// tslint:disable-next-line:no-any
455
- protected async reexecuteCell ( info : IReExecuteCell ) : Promise < void > {
470
+ protected async reexecuteCells ( info : IReExecuteCells ) : Promise < void > {
471
+ const tokenSource = new CancellationTokenSource ( ) ;
472
+ this . executeCancelTokens . add ( tokenSource ) ;
473
+ let finishedPos = info && info . entries ? info . entries . length : - 1 ;
456
474
try {
457
- // If there's any payload, it has the code and the id
458
- if ( info && info . newCode && info . cell . id && info . cell . data . cell_type !== 'messages' ) {
459
- // Clear the result if we've run before
460
- await this . clearResult ( info . cell . id ) ;
461
-
462
- // Send to ourselves.
463
- await this . submitCode ( info . newCode , Identifiers . EmptyFileName , 0 , info . cell . id , info . cell . data ) ;
464
-
465
- // Activate the other side, and send as if came from a file
466
- await this . ipynbProvider . show ( this . file ) ;
467
- this . shareMessage ( InteractiveWindowMessages . RemoteReexecuteCode , {
468
- code : info . newCode ,
469
- file : Identifiers . EmptyFileName ,
470
- line : 0 ,
471
- id : info . cell . id ,
472
- originator : this . id ,
473
- debug : false
474
- } ) ;
475
+ if ( info && info . entries ) {
476
+ for ( let i = 0 ; i < info . entries . length && ! tokenSource . token . isCancellationRequested ; i += 1 ) {
477
+ await this . reexecuteCell ( info . entries [ i ] , tokenSource . token ) ;
478
+ if ( ! tokenSource . token . isCancellationRequested ) {
479
+ finishedPos = i ;
480
+ }
481
+ }
475
482
}
476
483
} catch ( exc ) {
477
- // Make this error our cell output
478
- this . sendCellsToWebView ( [
479
- {
480
- // tslint:disable-next-line: no-any
481
- data : { ...info . cell . data , outputs : [ createErrorOutput ( exc ) ] } as any , // nyc compiler issue
482
- id : info . cell . id ,
483
- file : Identifiers . EmptyFileName ,
484
- line : 0 ,
485
- state : CellState . error
486
- }
487
- ] ) ;
488
-
489
484
// Tell the other side we restarted the kernel. This will stop all executions
490
485
this . postMessage ( InteractiveWindowMessages . RestartKernel ) . ignoreErrors ( ) ;
491
486
492
487
// Handle an error
493
488
await this . errorHandler . handleError ( exc ) ;
489
+ } finally {
490
+ this . executeCancelTokens . delete ( tokenSource ) ;
491
+
492
+ // Make sure everything is marked as finished or error after the final finished
493
+ // position
494
+ if ( info && info . entries ) {
495
+ for ( let i = finishedPos + 1 ; i < info . entries . length ; i += 1 ) {
496
+ this . finishCell ( info . entries [ i ] ) ;
497
+ }
498
+ }
494
499
}
495
500
}
496
501
@@ -563,6 +568,75 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
563
568
// Actually don't close, just let the error bubble out
564
569
}
565
570
571
+ private interruptExecution ( ) {
572
+ this . executeCancelTokens . forEach ( t => t . cancel ( ) ) ;
573
+ }
574
+
575
+ private finishCell ( entry : { cell : ICell ; code : string } ) {
576
+ this . sendCellsToWebView ( [
577
+ {
578
+ ...entry . cell ,
579
+ // tslint:disable-next-line: no-any
580
+ data : { ...entry . cell . data , source : entry . code } as any , // nyc compiler issue
581
+ state : CellState . finished
582
+ }
583
+ ] ) ;
584
+ }
585
+
586
+ private async reexecuteCell ( entry : { cell : ICell ; code : string } , cancelToken : CancellationToken ) : Promise < void > {
587
+ try {
588
+ // If there's any payload, it has the code and the id
589
+ if ( entry . code && entry . cell . id && entry . cell . data . cell_type !== 'messages' ) {
590
+ traceInfo ( `Executing cell ${ entry . cell . id } ` ) ;
591
+
592
+ // Clear the result if we've run before
593
+ await this . clearResult ( entry . cell . id ) ;
594
+
595
+ // Send to ourselves.
596
+ await this . submitCode (
597
+ entry . code ,
598
+ Identifiers . EmptyFileName ,
599
+ 0 ,
600
+ entry . cell . id ,
601
+ entry . cell . data ,
602
+ false ,
603
+ cancelToken
604
+ ) ;
605
+
606
+ if ( ! cancelToken . isCancellationRequested ) {
607
+ // Activate the other side, and send as if came from a file
608
+ await this . ipynbProvider . show ( this . file ) ;
609
+ this . shareMessage ( InteractiveWindowMessages . RemoteReexecuteCode , {
610
+ code : entry . code ,
611
+ file : Identifiers . EmptyFileName ,
612
+ line : 0 ,
613
+ id : entry . cell . id ,
614
+ originator : this . id ,
615
+ debug : false
616
+ } ) ;
617
+ }
618
+ }
619
+ } catch ( exc ) {
620
+ // Make this error our cell output
621
+ this . sendCellsToWebView ( [
622
+ {
623
+ // tslint:disable-next-line: no-any
624
+ data : { ...entry . cell . data , outputs : [ createErrorOutput ( exc ) ] } as any , // nyc compiler issue
625
+ id : entry . cell . id ,
626
+ file : Identifiers . EmptyFileName ,
627
+ line : 0 ,
628
+ state : CellState . error
629
+ }
630
+ ] ) ;
631
+
632
+ throw exc ;
633
+ } finally {
634
+ if ( entry && entry . cell . id ) {
635
+ traceInfo ( `Finished executing cell ${ entry . cell . id } ` ) ;
636
+ }
637
+ }
638
+ }
639
+
566
640
private sendPerceivedCellExecute ( runningStopWatch ?: StopWatch ) {
567
641
if ( runningStopWatch ) {
568
642
const props = { notebook : true } ;
@@ -882,11 +956,13 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
882
956
883
957
private async writeToStorage ( filePath : string , contents ?: string ) : Promise < void > {
884
958
try {
885
- if ( contents ) {
886
- await this . fileSystem . createDirectory ( path . dirname ( filePath ) ) ;
887
- return this . fileSystem . writeFile ( filePath , contents ) ;
888
- } else {
889
- return this . fileSystem . deleteFile ( filePath ) ;
959
+ if ( ! this . _disposed ) {
960
+ if ( contents ) {
961
+ await this . fileSystem . createDirectory ( path . dirname ( filePath ) ) ;
962
+ return this . fileSystem . writeFile ( filePath , contents ) ;
963
+ } else {
964
+ return this . fileSystem . deleteFile ( filePath ) ;
965
+ }
890
966
}
891
967
} catch ( exc ) {
892
968
traceError ( `Error writing storage for ${ filePath } : ` , exc ) ;
0 commit comments