@@ -27,6 +27,7 @@ import { PlaceholderPanel } from './placeholderPanel';
2727export type ConsoleEntry = {
2828 browserMessage ?: {
2929 body : JSX . Element [ ] ;
30+ bodyString : string ;
3031 location : string ;
3132 } ,
3233 browserError ?: channels . SerializedError ;
@@ -36,6 +37,7 @@ export type ConsoleEntry = {
3637 isError : boolean ;
3738 isWarning : boolean ;
3839 timestamp : number ;
40+ repeat : number ;
3941} ;
4042
4143type ConsoleTabModel = {
@@ -50,16 +52,38 @@ export function useConsoleTabModel(model: modelUtil.MultiTraceModel | undefined,
5052 if ( ! model )
5153 return { entries : [ ] } ;
5254 const entries : ConsoleEntry [ ] = [ ] ;
53- for ( const event of model . events ) {
55+ function addEntry ( entry : Omit < ConsoleEntry , 'repeat' > ) {
56+ const lastEntry = entries [ entries . length - 1 ] ;
57+ const isSameAsLast =
58+ lastEntry
59+ && entry . browserMessage ?. bodyString === lastEntry . browserMessage ?. bodyString
60+ && entry . browserMessage ?. location === lastEntry . browserMessage ?. location
61+ && entry . browserError === lastEntry . browserError
62+ && entry . nodeMessage ?. html === lastEntry . nodeMessage ?. html
63+ && entry . isError === lastEntry . isError
64+ && entry . isWarning === lastEntry . isWarning
65+ && entry . timestamp - lastEntry . timestamp < 1000 ;
66+ if ( isSameAsLast )
67+ lastEntry . repeat ++ ;
68+ else
69+ entries . push ( { ...entry , repeat : 1 } ) ;
70+ }
71+ const logEvents = [ ...model . events , ...model . stdio ] . sort ( ( a , b ) => {
72+ const aTimestamp = 'time' in a ? a . time : a . timestamp ;
73+ const bTimestamp = 'time' in b ? b . time : b . timestamp ;
74+ return aTimestamp - bTimestamp ;
75+ } )
76+ for ( const event of logEvents ) {
5477 if ( event . type === 'console' ) {
5578 const body = event . args && event . args . length ? format ( event . args ) : formatAnsi ( event . text ) ;
5679 const url = event . location . url ;
5780 const filename = url ? url . substring ( url . lastIndexOf ( '/' ) + 1 ) : '<anonymous>' ;
5881 const location = `${ filename } :${ event . location . lineNumber } ` ;
5982
60- entries . push ( {
83+ addEntry ( {
6184 browserMessage : {
6285 body,
86+ bodyString : event . text ,
6387 location,
6488 } ,
6589 isError : event . messageType === 'error' ,
@@ -68,29 +92,28 @@ export function useConsoleTabModel(model: modelUtil.MultiTraceModel | undefined,
6892 } ) ;
6993 }
7094 if ( event . type === 'event' && event . method === 'pageError' ) {
71- entries . push ( {
95+ addEntry ( {
7296 browserError : event . params . error ,
7397 isError : true ,
7498 isWarning : false ,
7599 timestamp : event . time ,
76100 } ) ;
77101 }
102+ if ( event . type === 'stderr' || event . type === 'stdout' ) {
103+ let html = '' ;
104+ if ( event . text )
105+ html = ansi2html ( event . text . trim ( ) ) || '' ;
106+ if ( event . base64 )
107+ html = ansi2html ( atob ( event . base64 ) . trim ( ) ) || '' ;
108+
109+ addEntry ( {
110+ nodeMessage : { html } ,
111+ isError : event . type === 'stderr' ,
112+ isWarning : false ,
113+ timestamp : event . timestamp ,
114+ } ) ;
115+ }
78116 }
79- for ( const event of model . stdio ) {
80- let html = '' ;
81- if ( event . text )
82- html = ansi2html ( event . text . trim ( ) ) || '' ;
83- if ( event . base64 )
84- html = ansi2html ( atob ( event . base64 ) . trim ( ) ) || '' ;
85-
86- entries . push ( {
87- nodeMessage : { html } ,
88- isError : event . type === 'stderr' ,
89- isWarning : false ,
90- timestamp : event . timestamp ,
91- } ) ;
92- }
93- entries . sort ( ( a , b ) => a . timestamp - b . timestamp ) ;
94117 return { entries } ;
95118 } , [ model ] ) ;
96119
@@ -154,6 +177,7 @@ export const ConsoleTab: React.FunctionComponent<{
154177 { timestampElement }
155178 { statusElement }
156179 { locationText && < span className = 'console-location' > { locationText } </ span > }
180+ { entry . repeat > 1 && < span className = 'console-repeat' > { entry . repeat } </ span > }
157181 { messageBody && < span className = 'console-line-message' > { messageBody } </ span > }
158182 { messageInnerHTML && < span className = 'console-line-message' dangerouslySetInnerHTML = { { __html : messageInnerHTML } } > </ span > }
159183 { messageStack && < div className = 'console-stack' > { messageStack } </ div > }
0 commit comments