4
4
5
5
import { Fragment , FunctionComponent , h } from 'preact' ;
6
6
import { useCallback , useContext , useEffect , useMemo , useRef , useState } from 'preact/hooks' ;
7
+ import * as GoToFileIcon from 'vscode-codicons/src/icons/go-to-file.svg' ;
7
8
import { binarySearch } from 'vscode-js-profile-core/out/esm/array' ;
9
+ import { Icon } from 'vscode-js-profile-core/out/esm/client/icons' ;
8
10
import { MiddleOut } from 'vscode-js-profile-core/out/esm/client/middleOutCompression' ;
9
11
import { useCssVariables } from 'vscode-js-profile-core/out/esm/client/useCssVariables' ;
10
12
import { usePersistedState } from 'vscode-js-profile-core/out/esm/client/usePersistedState' ;
@@ -26,7 +28,8 @@ export const enum Constants {
26
28
TimelineHeight = 22 ,
27
29
TimelineLabelSpacing = 200 ,
28
30
MinWindow = 0.005 ,
29
- ExtraYBuffer = 30 ,
31
+ ExtraYBuffer = 300 ,
32
+ DefaultStackLimit = 7 ,
30
33
}
31
34
32
35
const pickColor = ( location : IColumnLocation ) : number => {
@@ -683,6 +686,15 @@ export const FlameGraph: FunctionComponent<{
683
686
location = { hovered . box . loc }
684
687
/>
685
688
) }
689
+ { focused && (
690
+ < InfoBox
691
+ columns = { columns }
692
+ boxes = { rawBoxes . boxById }
693
+ box = { focused }
694
+ model = { model }
695
+ setFocused = { setFocused }
696
+ />
697
+ ) }
686
698
</ Fragment >
687
699
) ;
688
700
} ;
@@ -793,3 +805,114 @@ const Tooltip: FunctionComponent<{
793
805
</ div >
794
806
) ;
795
807
} ;
808
+
809
+ const InfoBox : FunctionComponent < {
810
+ box : IBox ;
811
+ model : IProfileModel ;
812
+ columns : ReadonlyArray < IColumn > ;
813
+ boxes : ReadonlyMap < number , IBox > ;
814
+ setFocused ( box : IBox ) : void ;
815
+ } > = ( { columns, boxes, box, model, setFocused } ) => {
816
+ const originalLocation = model . locations [ box . loc . id ] ;
817
+ const localLocation = box . loc ;
818
+ const [ limitedStack , setLimitedStack ] = useState ( true ) ;
819
+
820
+ useEffect ( ( ) => setLimitedStack ( true ) , [ box ] ) ;
821
+
822
+ const stack = useMemo ( ( ) => {
823
+ const stack : IBox [ ] = [ box ] ;
824
+ for ( let row = box . row - 1 ; row >= 0 && stack . length ; row -- ) {
825
+ const b = getBoxInRowColumn ( columns , boxes , box . column , row ) ;
826
+ if ( b ) {
827
+ stack . push ( b ) ;
828
+ }
829
+ }
830
+
831
+ return stack ;
832
+ } , [ box , columns , boxes ] ) ;
833
+
834
+ const shouldTruncateStack = stack . length >= Constants . DefaultStackLimit + 3 && limitedStack ;
835
+
836
+ return (
837
+ < div className = { styles . info } >
838
+ < dl >
839
+ < dt > Self Time</ dt >
840
+ < dd > { decimalFormat . format ( localLocation . selfTime / 1000 ) } ms</ dd >
841
+ < dt > Total Time</ dt >
842
+ < dd > { decimalFormat . format ( localLocation . aggregateTime / 1000 ) } ms</ dd >
843
+ < dt >
844
+ Self Time< small > Entire Profile</ small >
845
+ </ dt >
846
+ < dd > { decimalFormat . format ( originalLocation . selfTime / 1000 ) } ms</ dd >
847
+ < dt >
848
+ Total Time< small > Entire Profile</ small >
849
+ </ dt >
850
+ < dd > { decimalFormat . format ( originalLocation . aggregateTime / 1000 ) } ms</ dd >
851
+ </ dl >
852
+ < ol start = { 0 } >
853
+ { stack . map (
854
+ ( b , i ) =>
855
+ ( ! shouldTruncateStack || i < Constants . DefaultStackLimit ) && (
856
+ < li key = { i } >
857
+ < BoxLink box = { b } onClick = { setFocused } link = { i > 0 } />
858
+ </ li >
859
+ ) ,
860
+ ) }
861
+ { shouldTruncateStack && (
862
+ < li >
863
+ < a onClick = { ( ) => setLimitedStack ( false ) } >
864
+ < em > { stack . length - Constants . DefaultStackLimit } more...</ em >
865
+ </ a >
866
+ </ li >
867
+ ) }
868
+ </ ol >
869
+ </ div >
870
+ ) ;
871
+ } ;
872
+
873
+ const BoxLink : FunctionComponent < { box : IBox ; onClick ( box : IBox ) : void ; link ?: boolean } > = ( {
874
+ box,
875
+ onClick,
876
+ link,
877
+ } ) => {
878
+ const vscode = useContext ( VsCodeApi ) as IVscodeApi ;
879
+ const open = useCallback (
880
+ ( evt : { altKey : boolean } ) => {
881
+ const src = box . loc . src ;
882
+ if ( ! src ?. source . path ) {
883
+ return ;
884
+ }
885
+
886
+ vscode . postMessage < IOpenDocumentMessage > ( {
887
+ type : 'openDocument' ,
888
+ location : src ,
889
+ callFrame : box . loc . callFrame ,
890
+ toSide : evt . altKey ,
891
+ } ) ;
892
+ } ,
893
+ [ vscode , box ] ,
894
+ ) ;
895
+
896
+ const click = useCallback ( ( ) => onClick ( box ) , [ box , onClick ] ) ;
897
+ const locText = getLocationText ( box . loc ) ;
898
+ const linkContent = (
899
+ < Fragment >
900
+ { box . loc . callFrame . functionName } < em > ({ locText } )</ em >
901
+ </ Fragment >
902
+ ) ;
903
+
904
+ return (
905
+ < Fragment >
906
+ { link ? < a onClick = { click } > { linkContent } </ a > : linkContent }
907
+ { box . loc . src ?. source . path && (
908
+ < Icon
909
+ i = { GoToFileIcon }
910
+ className = { styles . goToFile }
911
+ onClick = { open }
912
+ role = "button"
913
+ title = "Go to File"
914
+ />
915
+ ) }
916
+ </ Fragment >
917
+ ) ;
918
+ } ;
0 commit comments