@@ -29,27 +29,96 @@ export interface CreateFunctionCodeOptions {
2929 contextLength ?: number ;
3030 /** Number of lines to include before and after the function. Stacks with `contextLength`. */
3131 contextLineLength ?: number ;
32+ /** If true, appends profile data from the trace at the end of every line of the function in `codeWithContext`. This should match what is seen in the formatted view in the Sources panel. */
33+ appendProfileData ?: boolean ;
34+ }
35+
36+ interface InputData {
37+ text : TextUtils . Text . Text ;
38+ formattedContent : Formatter . ScriptFormatter . FormattedContent | null ;
39+ performanceData : Workspace . UISourceCode . LineColumnProfileMap | undefined ;
40+ }
41+
42+ const inputCache = new WeakMap < Workspace . UISourceCode . UISourceCode , Promise < InputData > > ( ) ;
43+
44+ async function prepareInput ( uiSourceCode : Workspace . UISourceCode . UISourceCode , content : string ) : Promise < InputData > {
45+ const formattedContent = await format ( uiSourceCode , content ) ;
46+ const text = new TextUtils . Text . Text ( formattedContent ? formattedContent . formattedContent : content ) ;
47+ let performanceData = uiSourceCode . getDecorationData ( Workspace . UISourceCode . DecoratorType . PERFORMANCE ) as
48+ Workspace . UISourceCode . LineColumnProfileMap |
49+ undefined ;
50+
51+ // Map profile data to the formatted view of the text.
52+ if ( formattedContent && performanceData ) {
53+ performanceData = Workspace . UISourceCode . createMappedProfileData ( performanceData , ( line , column ) => {
54+ return formattedContent . formattedMapping . originalToFormatted ( line , column ) ;
55+ } ) ;
56+ }
57+
58+ return { text, formattedContent, performanceData} ;
59+ }
60+
61+ /** Formatting and parsing line endings for Text is expensive, so cache it. */
62+ async function prepareInputAndCache (
63+ uiSourceCode : Workspace . UISourceCode . UISourceCode , content : string ) : Promise < InputData > {
64+ let cachedPromise = inputCache . get ( uiSourceCode ) ;
65+ if ( cachedPromise ) {
66+ return await cachedPromise ;
67+ }
68+
69+ cachedPromise = prepareInput ( uiSourceCode , content ) ;
70+ inputCache . set ( uiSourceCode , cachedPromise ) ;
71+ return await cachedPromise ;
72+ }
73+
74+ function extractPerformanceDataByLine (
75+ textRange : TextUtils . TextRange . TextRange , performanceData : Workspace . UISourceCode . LineColumnProfileMap ) : number [ ] {
76+ const { startLine, startColumn, endLine, endColumn} = textRange ;
77+ const byLine = new Array ( endLine - startLine + 1 ) . fill ( 0 ) ;
78+
79+ for ( let line = startLine ; line <= endLine ; line ++ ) {
80+ const lineData = performanceData . get ( line + 1 ) ;
81+ if ( ! lineData ) {
82+ continue ;
83+ }
84+
85+ // Fast-path for when the entire line's data is relevant.
86+ if ( line !== startLine && line !== endLine ) {
87+ byLine [ line - startLine ] = lineData . values ( ) . reduce ( ( acc , cur ) => acc + cur ) ;
88+ continue ;
89+ }
90+
91+ const column0 = line === startLine ? startColumn + 1 : 0 ;
92+ const column1 = line === endLine ? endColumn + 1 : Number . POSITIVE_INFINITY ;
93+
94+ let totalData = 0 ;
95+ for ( const [ column , data ] of lineData ) {
96+ if ( column >= column0 && column <= column1 ) {
97+ totalData += data ;
98+ }
99+ }
100+
101+ byLine [ line - startLine ] = totalData ;
102+ }
103+
104+ return byLine . map ( data => Math . round ( data * 10 ) / 10 ) ;
32105}
33106
34107function createFunctionCode (
35- uiSourceCodeContent : string , formattedContent : Formatter . ScriptFormatter . FormattedContent | null ,
36- functionBounds : Workspace . UISourceCode . UIFunctionBounds , options ?: CreateFunctionCodeOptions ) : FunctionCode {
108+ inputData : InputData , functionBounds : Workspace . UISourceCode . UIFunctionBounds ,
109+ options ?: CreateFunctionCodeOptions ) : FunctionCode {
37110 let { startLine, startColumn, endLine, endColumn} = functionBounds . range ;
38- let text ;
39- if ( formattedContent ) {
40- text = new TextUtils . Text . Text ( formattedContent . formattedContent ) ;
41-
42- const startMapped = formattedContent . formattedMapping . originalToFormatted ( startLine , startColumn ) ;
111+ if ( inputData . formattedContent ) {
112+ const startMapped = inputData . formattedContent . formattedMapping . originalToFormatted ( startLine , startColumn ) ;
43113 startLine = startMapped [ 0 ] ;
44114 startColumn = startMapped [ 1 ] ;
45115
46- const endMapped = formattedContent . formattedMapping . originalToFormatted ( endLine , endColumn ) ;
116+ const endMapped = inputData . formattedContent . formattedMapping . originalToFormatted ( endLine , endColumn ) ;
47117 endLine = endMapped [ 0 ] ;
48118 endColumn = endMapped [ 1 ] ;
49- } else {
50- text = new TextUtils . Text . Text ( uiSourceCodeContent ) ;
51119 }
52120
121+ const text = inputData . text ;
53122 const content = text . value ( ) ;
54123
55124 // Define two ranges - the first is just the function bounds, the second includes
@@ -91,7 +160,37 @@ function createFunctionCode(
91160 const code = content . substring ( functionStartOffset , functionEndOffset ) ;
92161 const before = content . substring ( contextStartOffset , functionStartOffset ) ;
93162 const after = content . substring ( functionEndOffset , contextEndOffset ) ;
94- const codeWithContext = before + `<FUNCTION_START>${ code } <FUNCTION_END>` + after ;
163+
164+ let codeWithContext ;
165+ if ( options ?. appendProfileData && inputData . performanceData ) {
166+ const performanceDataByLine = extractPerformanceDataByLine ( range , inputData . performanceData ) ;
167+ const lines = performanceDataByLine . map ( ( data , i ) => {
168+ let line = text . lineAt ( startLine + i ) ;
169+
170+ const isLastLine = i === performanceDataByLine . length - 1 ;
171+ if ( i === 0 ) {
172+ if ( isLastLine ) {
173+ line = line . substring ( startColumn , endColumn ) ;
174+ } else {
175+ line = line . substring ( startColumn ) ;
176+ }
177+ } else if ( isLastLine ) {
178+ line = line . substring ( 0 , endColumn ) ;
179+ }
180+
181+ if ( isLastLine ) {
182+ // Don't ever annotate the last line - it could make the rest of the code on
183+ // that line get commented out.
184+ data = 0 ;
185+ }
186+
187+ return data ? `${ line } // ${ data } ms` : line ;
188+ } ) ;
189+ const annotatedCode = lines . join ( '\n' ) ;
190+ codeWithContext = before + `<FUNCTION_START>${ annotatedCode } <FUNCTION_END>` + after ;
191+ } else {
192+ codeWithContext = before + `<FUNCTION_START>${ code } <FUNCTION_END>` + after ;
193+ }
95194
96195 return {
97196 functionBounds,
@@ -137,26 +236,16 @@ export async function getFunctionCodeFromLocation(
137236 return await getFunctionCodeFromRawLocation ( rawLocation , options ) ;
138237}
139238
140- const formatCache =
141- new WeakMap < Workspace . UISourceCode . UISourceCode , Promise < Formatter . ScriptFormatter . FormattedContent | null > > ( ) ;
142-
143- async function formatAndCache ( uiSourceCode : Workspace . UISourceCode . UISourceCode , content : string ) :
239+ async function format ( uiSourceCode : Workspace . UISourceCode . UISourceCode , content : string ) :
144240 Promise < Formatter . ScriptFormatter . FormattedContent | null > {
145- let cachedPromise = formatCache . get ( uiSourceCode ) ;
146- if ( cachedPromise ) {
147- return await cachedPromise ;
148- }
149-
150241 const contentType = uiSourceCode . contentType ( ) ;
151242 const shouldFormat = ! contentType . isFromSourceMap ( ) && ( contentType . isDocument ( ) || contentType . isScript ( ) ) &&
152243 TextUtils . TextUtils . isMinified ( content ) ;
153244 if ( ! shouldFormat ) {
154245 return null ;
155246 }
156247
157- cachedPromise = Formatter . ScriptFormatter . formatScriptContent ( contentType . canonicalMimeType ( ) , content , '\t' ) ;
158- formatCache . set ( uiSourceCode , cachedPromise ) ;
159- return await cachedPromise ;
248+ return await Formatter . ScriptFormatter . formatScriptContent ( contentType . canonicalMimeType ( ) , content , '\t' ) ;
160249}
161250
162251/**
@@ -176,6 +265,6 @@ export async function getFunctionCodeFromRawLocation(
176265 return null ;
177266 }
178267
179- const formattedContent = await formatAndCache ( functionBounds . uiSourceCode , content ) ;
180- return createFunctionCode ( content , formattedContent , functionBounds , options ) ;
268+ const inputData = await prepareInputAndCache ( functionBounds . uiSourceCode , content ) ;
269+ return createFunctionCode ( inputData , functionBounds , options ) ;
181270}
0 commit comments