@@ -634,24 +634,28 @@ export class JupyterServerBase implements INotebookServer {
634634 } ) ;
635635 }
636636
637+ const clearState : Map < string , boolean > = new Map < string , boolean > ( ) ;
638+
637639 // Listen to the reponse messages and update state as we go
638640 if ( request ) {
639641 request . onIOPub = ( msg : KernelMessage . IIOPubMessage ) => {
640642 try {
641643 if ( jupyterLab . KernelMessage . isExecuteResultMsg ( msg ) ) {
642- this . handleExecuteResult ( msg as KernelMessage . IExecuteResultMsg , subscriber . cell ) ;
644+ this . handleExecuteResult ( msg as KernelMessage . IExecuteResultMsg , clearState , subscriber . cell ) ;
643645 } else if ( jupyterLab . KernelMessage . isExecuteInputMsg ( msg ) ) {
644- this . handleExecuteInput ( msg as KernelMessage . IExecuteInputMsg , subscriber . cell ) ;
646+ this . handleExecuteInput ( msg as KernelMessage . IExecuteInputMsg , clearState , subscriber . cell ) ;
645647 } else if ( jupyterLab . KernelMessage . isStatusMsg ( msg ) ) {
646- this . handleStatusMessage ( msg as KernelMessage . IStatusMsg , subscriber . cell ) ;
648+ this . handleStatusMessage ( msg as KernelMessage . IStatusMsg , clearState , subscriber . cell ) ;
647649 } else if ( jupyterLab . KernelMessage . isStreamMsg ( msg ) ) {
648- this . handleStreamMesssage ( msg as KernelMessage . IStreamMsg , subscriber . cell ) ;
650+ this . handleStreamMesssage ( msg as KernelMessage . IStreamMsg , clearState , subscriber . cell ) ;
649651 } else if ( jupyterLab . KernelMessage . isDisplayDataMsg ( msg ) ) {
650- this . handleDisplayData ( msg as KernelMessage . IDisplayDataMsg , subscriber . cell ) ;
652+ this . handleDisplayData ( msg as KernelMessage . IDisplayDataMsg , clearState , subscriber . cell ) ;
651653 } else if ( jupyterLab . KernelMessage . isUpdateDisplayDataMsg ( msg ) ) {
652- this . handleUpdateDisplayData ( msg as KernelMessage . IUpdateDisplayDataMsg , subscriber . cell ) ;
654+ this . handleUpdateDisplayData ( msg as KernelMessage . IUpdateDisplayDataMsg , clearState , subscriber . cell ) ;
655+ } else if ( jupyterLab . KernelMessage . isClearOutputMsg ( msg ) ) {
656+ this . handleClearOutput ( msg as KernelMessage . IClearOutputMsg , clearState , subscriber . cell ) ;
653657 } else if ( jupyterLab . KernelMessage . isErrorMsg ( msg ) ) {
654- this . handleError ( msg as KernelMessage . IErrorMsg , subscriber . cell ) ;
658+ this . handleError ( msg as KernelMessage . IErrorMsg , clearState , subscriber . cell ) ;
655659 } else {
656660 this . logger . logWarning ( `Unknown message ${ msg . header . msg_type } : hasData=${ 'data' in msg . content } ` ) ;
657661 }
@@ -661,7 +665,7 @@ export class JupyterServerBase implements INotebookServer {
661665 subscriber . cell . data . execution_count = msg . content . execution_count as number ;
662666 }
663667
664- // Show our update if any new output
668+ // Show our update if any new output.
665669 subscriber . next ( this . sessionStartTime ) ;
666670 } catch ( err ) {
667671 // If not a restart error, then tell the subscriber
@@ -707,56 +711,80 @@ export class JupyterServerBase implements INotebookServer {
707711 } ) ;
708712 }
709713
710- private addToCellData = ( cell : ICell , output : nbformat . IUnrecognizedOutput | nbformat . IExecuteResult | nbformat . IDisplayData | nbformat . IStream | nbformat . IError ) => {
711- const data : nbformat . ICodeCell = cell . data as nbformat . ICodeCell ;
712- data . outputs = [ ...data . outputs , output ] ;
713- cell . data = data ;
714+ private addToCellData = ( cell : ICell , output : nbformat . IUnrecognizedOutput | nbformat . IExecuteResult | nbformat . IDisplayData | nbformat . IStream | nbformat . IError , clearState : Map < string , boolean > ) => {
715+ // If a clear is pending, replace the output with the new one
716+ if ( clearState . get ( output . output_type ) ) {
717+ clearState . delete ( output . output_type ) ;
718+ const data : nbformat . ICodeCell = cell . data as nbformat . ICodeCell ;
719+ const index = data . outputs . findIndex ( o => o . output_type === output . output_type ) ;
720+ if ( index >= 0 ) {
721+ data . outputs . splice ( index , 1 , output ) ;
722+ } else {
723+ data . outputs = [ ...data . outputs , output ] ;
724+ }
725+ cell . data = data ;
726+ } else {
727+ // Then append this data onto the end.
728+ const data : nbformat . ICodeCell = cell . data as nbformat . ICodeCell ;
729+ data . outputs = [ ...data . outputs , output ] ;
730+ cell . data = data ;
731+ }
714732 }
715733
716- private handleExecuteResult ( msg : KernelMessage . IExecuteResultMsg , cell : ICell ) {
717- this . addToCellData ( cell , { output_type : 'execute_result' , data : msg . content . data , metadata : msg . content . metadata , execution_count : msg . content . execution_count } ) ;
734+ private handleExecuteResult ( msg : KernelMessage . IExecuteResultMsg , clearState : Map < string , boolean > , cell : ICell ) {
735+ this . addToCellData (
736+ cell ,
737+ { output_type : 'execute_result' , data : msg . content . data , metadata : msg . content . metadata , execution_count : msg . content . execution_count } ,
738+ clearState ) ;
718739 }
719740
720- private handleExecuteInput ( msg : KernelMessage . IExecuteInputMsg , cell : ICell ) {
741+ private handleExecuteInput ( msg : KernelMessage . IExecuteInputMsg , _clearState : Map < string , boolean > , cell : ICell ) {
721742 cell . data . execution_count = msg . content . execution_count ;
722743 }
723744
724- private handleStatusMessage ( msg : KernelMessage . IStatusMsg , cell : ICell ) {
745+ private handleStatusMessage ( msg : KernelMessage . IStatusMsg , _clearState : Map < string , boolean > , cell : ICell ) {
725746 // Status change to idle generally means we finished. Not sure how to
726747 // make sure of this. Maybe only bother if an interrupt
727748 if ( msg . content . execution_state === 'idle' && cell . state !== CellState . error ) {
728749 cell . state = CellState . finished ;
729750 }
730751 }
731752
732- private handleStreamMesssage ( msg : KernelMessage . IStreamMsg , cell : ICell ) {
753+ private handleStreamMesssage ( msg : KernelMessage . IStreamMsg , clearState : Map < string , boolean > , cell : ICell ) {
733754 // Might already have a stream message. If so, just add on to it.
734755 const data : nbformat . ICodeCell = cell . data as nbformat . ICodeCell ;
735756 const existing = data . outputs . find ( o => o . output_type === 'stream' ) ;
736757 if ( existing && existing . name === msg . content . name ) {
737- // tslint:disable-next-line:restrict-plus-operands
738- existing . text = existing . text + msg . content . text ;
758+ // If clear pending, then don't add.
759+ if ( clearState . get ( 'stream' ) ) {
760+ clearState . delete ( 'stream' ) ;
761+ existing . text = msg . content . text ;
762+ } else {
763+ // tslint:disable-next-line:restrict-plus-operands
764+ existing . text = existing . text + msg . content . text ;
765+ }
766+
739767 } else {
740768 // Create a new stream entry
741769 const output : nbformat . IStream = {
742770 output_type : 'stream' ,
743771 name : msg . content . name ,
744772 text : msg . content . text
745773 } ;
746- this . addToCellData ( cell , output ) ;
774+ this . addToCellData ( cell , output , clearState ) ;
747775 }
748776 }
749777
750- private handleDisplayData ( msg : KernelMessage . IDisplayDataMsg , cell : ICell ) {
778+ private handleDisplayData ( msg : KernelMessage . IDisplayDataMsg , clearState : Map < string , boolean > , cell : ICell ) {
751779 const output : nbformat . IDisplayData = {
752780 output_type : 'display_data' ,
753781 data : msg . content . data ,
754782 metadata : msg . content . metadata
755783 } ;
756- this . addToCellData ( cell , output ) ;
784+ this . addToCellData ( cell , output , clearState ) ;
757785 }
758786
759- private handleUpdateDisplayData ( msg : KernelMessage . IUpdateDisplayDataMsg , cell : ICell ) {
787+ private handleUpdateDisplayData ( msg : KernelMessage . IUpdateDisplayDataMsg , _clearState : Map < string , boolean > , cell : ICell ) {
760788 // Should already have a display data output in our cell.
761789 const data : nbformat . ICodeCell = cell . data as nbformat . ICodeCell ;
762790 const output = data . outputs . find ( o => o . output_type === 'display_data' ) ;
@@ -766,6 +794,21 @@ export class JupyterServerBase implements INotebookServer {
766794 }
767795 }
768796
797+ private handleClearOutput ( msg : KernelMessage . IClearOutputMsg , clearState : Map < string , boolean > , cell : ICell ) {
798+ // If the message says wait, add every message type to our clear state. This will
799+ // make us wait for this type of output before we clear it.
800+ if ( msg && msg . content . wait ) {
801+ clearState . set ( 'display_data' , true ) ;
802+ clearState . set ( 'error' , true ) ;
803+ clearState . set ( 'execute_result' , true ) ;
804+ clearState . set ( 'stream' , true ) ;
805+ } else {
806+ // Clear all outputs and start over again.
807+ const data : nbformat . ICodeCell = cell . data as nbformat . ICodeCell ;
808+ data . outputs = [ ] ;
809+ }
810+ }
811+
769812 private handleInterrupted ( cell : ICell ) {
770813 this . handleError ( {
771814 channel : 'iopub' ,
@@ -781,17 +824,17 @@ export class JupyterServerBase implements INotebookServer {
781824 '[1;31mKeyboardInterrupt[0m: '
782825 ]
783826 }
784- } , cell ) ;
827+ } , new Map < string , boolean > ( ) , cell ) ;
785828 }
786829
787- private handleError ( msg : KernelMessage . IErrorMsg , cell : ICell ) {
830+ private handleError ( msg : KernelMessage . IErrorMsg , clearState : Map < string , boolean > , cell : ICell ) {
788831 const output : nbformat . IError = {
789832 output_type : 'error' ,
790833 ename : msg . content . ename ,
791834 evalue : msg . content . evalue ,
792835 traceback : msg . content . traceback
793836 } ;
794- this . addToCellData ( cell , output ) ;
837+ this . addToCellData ( cell , output , clearState ) ;
795838 cell . state = CellState . error ;
796839 }
797840}
0 commit comments