1+ import { nextTick } from 'process' ;
12import * as vscode from 'vscode' ;
2- import { DocumentInfoProvider , LineInfo } from '../../../services/documentInfoProvider' ;
3+ import { CodeInspector } from '../../../services/codeInspector' ;
4+ import { DocumentInfo , DocumentInfoProvider , LineInfo , MethodInfo } from '../../../services/documentInfoProvider' ;
5+ import { Token } from '../../../services/languages/tokens' ;
36import { WorkspaceState } from '../../../state' ;
47import { SpanDurationsInsight } from '../InsightListView/SpanInsight' ;
58
@@ -13,9 +16,11 @@ export class PerformanceDecorator implements vscode.Disposable
1316 private _iconDecorationType : vscode . TextEditorDecorationType ;
1417 private _textDecorationType : vscode . TextEditorDecorationType ;
1518 private _disposables : vscode . Disposable [ ] = [ ] ;
19+ readonly recursionLimit = 2 ;
1620
1721 constructor ( private _documentInfoProvider : DocumentInfoProvider ,
18- private _workspaceState : WorkspaceState
22+ private _workspaceState : WorkspaceState ,
23+ private _codeInspector : CodeInspector
1924 )
2025 {
2126 this . _iconDecorationType = vscode . window . createTextEditorDecorationType ( {
@@ -37,80 +42,246 @@ export class PerformanceDecorator implements vscode.Disposable
3742 this . _disposables . push ( vscode . commands . registerCommand ( PerformanceDecorator . Commands . Hide , this . onHide . bind ( this ) ) ) ;
3843 }
3944
40- private async onShow ( )
41- {
45+ private getDisplayDuration ( totalDuration : number ) :string {
4246
43- const editor = vscode . window . activeTextEditor ;
44- if ( ! editor )
47+ let rawValue = ( totalDuration / 1000000 ) ;
48+ let unit = "ms" ;
49+ if ( rawValue > 1000 ) {
50+ rawValue = rawValue / 1000 ;
51+ unit = "sec" ;
52+
53+ if ( rawValue > 60 ) {
54+ unit = "min" ;
55+ rawValue = rawValue / 60 ;
56+
57+ }
58+ }
59+ return `~${ rawValue . toFixed ( 2 ) } ${ unit } ` ;
60+ }
61+
62+ private getTotalDurationFromInsights ( docInfo :DocumentInfo , method :MethodInfo ) : number | undefined {
63+
64+ const insights = docInfo . insights . forMethod ( method , this . _workspaceState . environment )
65+ . filter ( x => x . name === "Performance Stats" ) ;
66+
67+ const percentileInfo = insights . map ( x => x as SpanDurationsInsight )
68+ . flatMap ( x => x . percentiles ) . filter ( x => x . currentDuration ) ;
69+
70+ if ( percentileInfo . length === 0 ) {
4571 return ;
72+ }
4673
47- const docInfo = await this . _documentInfoProvider . getDocumentInfo ( editor . document ) ;
48- if ( ! docInfo )
74+ const totalDuration = percentileInfo . filter ( x => x . percentile == 0.5 ) . map ( x => x . currentDuration . raw )
75+ . reduce ( ( acc , val ) => acc + val , 0 ) ;
76+
77+ if ( totalDuration === 0 ) {
4978 return ;
79+ }
80+
81+ return totalDuration ;
82+ }
83+
84+ private async discoverIndirectDurationRecursive ( document : vscode . TextDocument ,
85+ docInfo :DocumentInfo ,
86+ methodInfo : MethodInfo ,
87+ functions :Token [ ] , recursionDepth : number ) : Promise < number | undefined > {
88+ if ( recursionDepth > this . recursionLimit ) {
89+ return undefined ;
90+ }
91+
92+ let totalDurationSum : number | undefined = undefined ;
93+
94+ for ( const func of functions . filter ( x => x . range . intersection ( methodInfo . range ) ) ) {
95+
96+ if ( func . text === methodInfo . name && func . range === methodInfo . nameRange ) {
97+ const completeDuration = this . getTotalDurationFromInsights ( docInfo , methodInfo ) ;
98+ if ( completeDuration ) {
99+ return completeDuration ;
100+ }
101+ continue ;
102+ }
103+
104+ const remoteMethodInfo =
105+ await this . _codeInspector . getExecuteDefinitionMethodInfo ( document , func . range . start , this . _documentInfoProvider ) ;
50106
51- let decorators : vscode . DecorationOptions [ ] = [ ] ;
107+ if ( ! remoteMethodInfo ) {
108+ continue ;
109+ }
110+ const remoteDoc = await this . _codeInspector . getDocumentInfo ( document , func . range . start , this . _documentInfoProvider ) ;
111+ if ( ! remoteDoc ) {
112+ continue ;
113+ }
114+
115+ let totalDuration = this . getTotalDurationFromInsights ( remoteDoc , remoteMethodInfo ) ;
116+ if ( ! totalDuration ) {
117+
118+ const nextDoc = await vscode . workspace . openTextDocument ( remoteDoc . uri ) ;
119+ if ( nextDoc ) {
120+ totalDuration = await this . discoverIndirectDurationRecursive ( nextDoc ,
121+ remoteDoc ,
122+ remoteMethodInfo , remoteDoc . tokens . filter ( x => x . type === 'function' ) ,
123+ recursionDepth ++ ) ;
124+ }
52125
53- for ( const method of docInfo . methods ) {
126+ }
127+
128+ if ( totalDuration && ! totalDurationSum ) {
129+ totalDurationSum = totalDuration ;
130+ }
131+ else if ( totalDuration && totalDurationSum ) {
132+ totalDurationSum += totalDuration ;
133+ }
134+ }
135+
136+
137+ return totalDurationSum ;
138+
139+
140+ }
141+
142+
143+ private async discoverPerfDecorators ( decorators : vscode . DecorationOptions [ ] ,
144+ document : vscode . TextDocument ,
145+ documentInfo : DocumentInfo ,
146+ methodInfo : MethodInfo ,
147+ functions :Token [ ] ,
148+ memoised : { [ token :string ] : number | undefined } ) {
149+
150+
151+ for ( const func of functions . filter ( x => x . range . intersection ( methodInfo . range ) ) ) {
54152
55- const insights = docInfo . insights . forMethod ( method , this . _workspaceState . environment )
56- . filter ( x => x . name == "Performance Stats" ) ;
153+ let totalDuration : undefined | number = undefined ;
57154
58- const percentileInfo = insights . map ( x => x as SpanDurationsInsight )
59- . flatMap ( x => x . percentiles ) . filter ( x => x . currentDuration ) ;
60-
61- const totalDuration = percentileInfo . filter ( x => x . percentile == 0.5 ) . map ( x => x . currentDuration . raw )
62- . reduce ( ( acc , val ) => acc + val , 0 ) ;
63-
64- const lines = docInfo . lines . getAllByCurrentEnv ( ) . filter ( l => method . range . contains ( l . range . start ) ) ;
65- for ( const lineInfo of lines ) {
66- decorators . push ( {
67- hoverMessage : "" ,
68- range : new vscode . Range ( lineInfo . range . end , lineInfo . range . end ) ,
69- renderOptions : {
70- after :{
71- contentText : `Duration: ${ totalDuration } `
155+ if ( func . text === methodInfo . name && func . range === methodInfo . nameRange ) {
156+ totalDuration = this . getTotalDurationFromInsights ( documentInfo , methodInfo ) ;
157+ }
158+
159+ else {
160+
161+ const mem = memoised [ document . uri + "$$" + func . text ] ;
162+ if ( mem ) {
163+ if ( mem === - 1 ) {
164+ totalDuration = undefined ;
165+ }
166+ else {
167+ totalDuration = mem ;
168+ }
169+ }
170+
171+ else {
172+
173+
174+ const remoteMethodInfo =
175+ await this . _codeInspector . getExecuteDefinitionMethodInfo ( document , func . range . start , this . _documentInfoProvider ) ;
176+
177+ if ( ! remoteMethodInfo ) {
178+ continue ;
179+ }
180+ const remoteDoc = await this . _codeInspector . getDocumentInfo ( document , func . range . start , this . _documentInfoProvider ) ;
181+ if ( ! remoteDoc ) {
182+ continue ;
183+ }
184+ totalDuration = this . getTotalDurationFromInsights ( remoteDoc , remoteMethodInfo ) ;
185+
186+ if ( ! totalDuration ) {
187+
188+ const remoteTextDoc = await vscode . workspace . openTextDocument ( remoteDoc . uri ) ;
189+ if ( remoteTextDoc ) {
190+ totalDuration = await this . discoverIndirectDurationRecursive ( remoteTextDoc ,
191+ remoteDoc ,
192+ remoteMethodInfo ,
193+ remoteDoc . tokens . filter ( x => x . type === 'function' ) ,
194+ 0 ) ;
195+ if ( totalDuration ) {
196+ memoised [ document . uri + "$$" + func . text ] = totalDuration ;
197+
198+ }
72199 }
200+
201+ }
202+ if ( ! totalDuration ) {
203+ memoised [ func . text ] = - 1 ;
204+ }
205+ else {
206+ memoised [ document . uri + "$$" + func . text ] = totalDuration ;
73207 }
208+ }
209+
210+ }
211+
212+
213+ if ( totalDuration ) {
214+
215+ const textLine = document . lineAt ( func . range . start . line ) ;
216+
217+ this . addPerformanceDecorator ( totalDuration , decorators , new vscode . Range ( textLine . range . end , textLine . range . end ) ) ;
74218
75- } )
76219 }
77-
78-
79-
80- // hoverMessage: this.getTooltip(lineInfo),
81- // range: new vscode.Range(lineInfo.range.end, lineInfo.range.end),
82- // renderOptions: {
83- // after:{
84- // contentText: [...new Set( lineInfo.exceptions.map(e => e.type))].join('\xB7')
85- // }
86- // }
87- // }
88220
89221 }
222+
223+ }
224+ private async onShow ( )
225+ {
226+
227+ const editor = vscode . window . activeTextEditor ;
228+ if ( ! editor ) { return ; } ;
229+
230+ const docInfo = await this . _documentInfoProvider . getDocumentInfo ( editor . document ) ;
231+
232+ if ( ! docInfo ) { return ; } ;
233+
234+ let decorators : vscode . DecorationOptions [ ] = [ ] ;
235+ const mem : { [ token :string ] : number | undefined } = { } ;
236+ const functions = docInfo . tokens . filter ( x => x . type === 'function' ) ;
237+ for ( const method of docInfo . methods ) {
238+
239+ await this . discoverPerfDecorators ( decorators , editor . document , docInfo , method , functions , mem ) ;
240+ }
90241
91- // const textDecorationOptions: vscode.DecorationOptions[] = lines
92- // .map(lineInfo => {
93- // return <vscode.DecorationOptions>{
94- // hoverMessage: this.getTooltip(lineInfo),
95- // range: new vscode.Range(lineInfo.range.end, lineInfo.range.end),
242+
243+ // for (const method of docInfo.methods){
244+
245+ // const totalDuration = this.getTotalDurationFromInsights(docInfo, method);
246+
247+ // const textLine = editor.document.lineAt(method.range.start.line);
248+
249+ // if (totalDuration){
250+ // decorators.push({
251+ // hoverMessage: "",
252+ // range: new vscode.Range(textLine.rangeIncludingLineBreak.end,
253+ // textLine.rangeIncludingLineBreak.end),
96254 // renderOptions: {
97255 // after:{
98- // contentText: [...new Set( lineInfo.exceptions.map(e => e.type))].join('\xB7' )
256+ // contentText: this.getDisplayDuration(totalDuration )
99257 // }
100258 // }
101- // }
102- // });
103- // const iconDecorationOptions = lines
104- // .map(lineInfo => {
105- // return <vscode.DecorationOptions>{
106- // range: new vscode.Range(lineInfo.range.end, lineInfo.range.end),
107- // }
108- // });
259+
260+ // });
261+ // }
262+
263+
109264
265+ // }
266+
110267 editor . setDecorations ( this . _iconDecorationType , decorators ) ;
111268 //editor.setDecorations(this._textDecorationType, textDecorationOptions);
112269 }
113270
271+ private addPerformanceDecorator ( totalDuration : number | undefined , decorators : vscode . DecorationOptions [ ] , range : vscode . Range ) {
272+ if ( totalDuration ) {
273+ decorators . push ( {
274+ hoverMessage : "" ,
275+ range : range ,
276+ renderOptions : {
277+ after : {
278+ contentText : this . getDisplayDuration ( totalDuration )
279+ }
280+ }
281+ } ) ;
282+ }
283+ }
284+
114285 private async onHide ( )
115286 {
116287 const editor = vscode . window . activeTextEditor ;
0 commit comments