diff --git a/src/analyticsCodeLens.ts b/src/analyticsCodeLens.ts index f05cf1d..c7bfbec 100644 --- a/src/analyticsCodeLens.ts +++ b/src/analyticsCodeLens.ts @@ -89,6 +89,9 @@ class CodelensProvider implements vscode.CodeLensProvider if (!insight.decorators){ continue; } + if (insight.environment=="PRODUCTIONS"){ + continue; + } for (const decorator of insight.decorators){ let envComponent = ""; @@ -128,7 +131,7 @@ class CodelensProvider implements vscode.CodeLensProvider }) ); } public async getLensForCodeLocationObject(methodInfo: MethodInfo, codeObjects: CodeObjectLocationInfo[], - usageStatus:UsageStatusResults, allInsights: CodeObjectInsight[] ) :Promise { + usageStatus:UsageStatusResults, allInsights: CodeObjectInsight[], showNoData:boolean ) :Promise { let codelens: vscode.CodeLens[] = []; @@ -139,10 +142,14 @@ class CodelensProvider implements vscode.CodeLensProvider const insights = allInsights.filter(x=>x.codeObjectId== codeObject.id); const codeObjectUsage = usageStatus.codeObjectStatuses.filter(x=>x.codeObjectId==codeObject.id); if (insights.length==0 && - codeObjectUsage.length==0){ + codeObjectUsage.length==0 && showNoData){ const emptyLenses = await this.getNoDataCodeLens(methodInfo,codeObject); for (const lens of emptyLenses){ + if (codelens.any(x=>x.command?.title=="Never reached")){ + continue; + } + codelens.push(lens); } } @@ -202,6 +209,7 @@ class CodelensProvider implements vscode.CodeLensProvider let spans = documentInfo.spans.filter(e => e.range.intersection(methodInfo.range) != undefined); let duplicates = spans.filter(x=>documentInfo.spans.any(span=>span!=x && span.name==x.name && span.range!=x.range)); spans=spans.filter(span=>!duplicates.includes(span)); + spans = spans.filter((v, i, a) => a.findIndex(x=>x.name==v.name) === i); if(duplicates.length>0){ const lenses = await this.getDuplicateSpanLens(methodInfo, duplicates); @@ -213,9 +221,13 @@ class CodelensProvider implements vscode.CodeLensProvider } if(spans.length>0){ const lenses = await this.getLensForCodeLocationObject(methodInfo, - spans,documentInfo.usageData.getAll(),documentInfo.insights.all.filter(x=>x.scope=="Span")); + spans,documentInfo.usageData.getAll(),documentInfo.insights.all.filter(x=>x.scope=="Span"), true); for (const lens of lenses){ + if (codelens.any(x=>x.command?.title==lens.command?.title + && x.range.end==lens.range.end && x.range.start== lens.range.start)){ + continue; + } codelens.push(lens); } @@ -224,10 +236,13 @@ class CodelensProvider implements vscode.CodeLensProvider const endpoints = documentInfo.endpoints.filter(e => e.range.intersection(methodInfo.range) != undefined); if(endpoints.length>0){ const lenses = await this.getLensForCodeLocationObject(methodInfo, - endpoints,documentInfo.usageData.getAll(),documentInfo.insights.all.filter(x=>x.scope=="EntrySpan"|| x.scope=="Span"), + endpoints,documentInfo.usageData.getAll(),documentInfo.insights.all.filter(x=>x.scope=="EntrySpan"|| x.scope=="Span"),false ); for (const lens of lenses){ + if (codelens.find(x=>x.command?.title==lens.command?.title)){ + continue; + } codelens.push(lens); } diff --git a/src/services/documentInfoProvider.ts b/src/services/documentInfoProvider.ts index 6d584f3..91b667a 100644 --- a/src/services/documentInfoProvider.ts +++ b/src/services/documentInfoProvider.ts @@ -547,7 +547,7 @@ export class CodeObjectInsightsAccessor{ } - public byMethod(env: string, doc:DocumentInfo):InsightCodeObjectLink[] | undefined + public forDocument(env: string, doc:DocumentInfo):InsightCodeObjectLink[] | undefined { let result: InsightCodeObjectLink[] = []; let insights = this._codeObjectInsights; @@ -619,14 +619,14 @@ export class MethodInfo implements CodeObjectLocationInfo public aliases: string[], public relatedCodeObjects: CodeObjectLocationInfo[], public documentUri: vscode.Uri){} - - get idWithType(): string { - return 'method:' + this.id; - } - - get idsWithType(): string[] { - return this.ids.map(x=> 'method:' + x); - } + public codeObjectType:string= "method"; + + get idWithType(): string { + return `${this.codeObjectType}:${this.id}`; + } + get idsWithType(): string[] { + return this.aliases.map(x=> `${this.codeObjectType}:${x}`); + } get ids(): string[] { return [ diff --git a/src/services/languages/extractors.ts b/src/services/languages/extractors.ts index 38bafa9..1ca2f4b 100644 --- a/src/services/languages/extractors.ts +++ b/src/services/languages/extractors.ts @@ -14,6 +14,7 @@ export interface SymbolInfo { documentUri: vscode.Uri; } + export interface CodeObjectLocationInfo extends CodeObjectInfo { range: vscode.Range; documentUri: vscode.Uri; @@ -25,22 +26,21 @@ export class EndpointInfo implements CodeObjectLocationInfo { public method: string, public path: string, public range: vscode.Range, - public documentUri: vscode.Uri, - ) { } + public documentUri: vscode.Uri){} - get displayName(): string { - return this.method; + public codeObjectType: string = "endpoint"; + + get idsWithType() { + return [`${this.codeObjectType}:${this.id}`]; + } + get displayName(): string { + return this.method; + } + get ids() { + return [ this.id]; + } } - get idsWithType() { - return ['endpoint:' + this.id]; - } - - get ids() { - return [ this.id]; - } -} - export class SpanLocationInfo implements CodeObjectLocationInfo { constructor( public id: string, @@ -48,16 +48,19 @@ export class SpanLocationInfo implements CodeObjectLocationInfo { public aliases: string[], public duplicates: SpanLocationInfo[], public range: vscode.Range, - public documentUri: vscode.Uri, - ) { } + public documentUri: vscode.Uri) + { } + + public codeObjectType: string="span"; + get idsWithType() { + return this.ids.map(x=> `${this.codeObjectType}:${x}`); + } + get displayName(): string { return this.name; } - get idsWithType() { - return this.ids.map(x=> 'span:' + x); - } get ids() { return [ diff --git a/src/views/codeAnalytics/InsightListView/EndpointInsight.ts b/src/views/codeAnalytics/InsightListView/EndpointInsight.ts index 4c4a7ac..bc40024 100644 --- a/src/views/codeAnalytics/InsightListView/EndpointInsight.ts +++ b/src/views/codeAnalytics/InsightListView/EndpointInsight.ts @@ -56,7 +56,7 @@ export class UsageViewItemsTemplate { private viewUris: WebViewUris ) { } - public generateHtml( +public generateHtml( maxCallsIn1Min: number, header: string, description: string, @@ -122,6 +122,8 @@ export interface HighUsageInsight extends EndpointInsight { maxCallsIn1Min: number; } + + export interface SlowEndpointInsight extends EndpointInsight { endpointsMedian: Duration; endpointsMedianOfMedians: Duration; diff --git a/src/views/codeAnalytics/InsightListView/IInsightListViewItemsCreator.ts b/src/views/codeAnalytics/InsightListView/IInsightListViewItemsCreator.ts index 3a4ea83..184fbfc 100644 --- a/src/views/codeAnalytics/InsightListView/IInsightListViewItemsCreator.ts +++ b/src/views/codeAnalytics/InsightListView/IInsightListViewItemsCreator.ts @@ -7,6 +7,10 @@ import { EndpointInsight, adjustHttpRouteIfNeeded, adjustHttpInsightIfNeeded } f export interface CodeObjectInsight extends Insight{ codeObjectId: string, environment: string, + category: string, + specificity: integer, + + shortDisplayInfo: InsightShortDisplayInfo, scope: string, name: string, importance: InsightImporance, @@ -14,6 +18,13 @@ export interface CodeObjectInsight extends Insight{ decorators: CodeObjectDecorator[] } +export interface InsightShortDisplayInfo{ + title:string, + targetDisplayName :string, + subtitle :string, + description :string +} + export enum InsightImporance { spam = 9, clutter = 8, diff --git a/src/views/codeAnalytics/StatusBar/insightFilterBar.ts b/src/views/codeAnalytics/StatusBar/insightFilterBar.ts new file mode 100644 index 0000000..989ed27 --- /dev/null +++ b/src/views/codeAnalytics/StatusBar/insightFilterBar.ts @@ -0,0 +1,34 @@ +import * as vscode from "vscode"; +import { DigmaCommands } from "../../../commands"; +import { WorkspaceState } from "../../../state"; + +export class InsightFilterStatusBar implements vscode.Disposable { + + private _disposables: vscode.Disposable[] = []; + private _statusBar : vscode.StatusBarItem; + + public constructor(private _state: WorkspaceState ){ + + this._statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000); + this._disposables = [ + + this._statusBar + ]; + + this._statusBar.text=`Usage Insights`; + this._statusBar.show(); + + } + dispose() { + for (let dis of this._disposables) + { + dis.dispose(); + } + } + + public refreshEnvironment(){ + + } + + +} \ No newline at end of file diff --git a/src/views/codeAnalytics/StatusBar/insightsStatusBar.ts b/src/views/codeAnalytics/StatusBar/insightsStatusBar.ts index ceb8a8c..b22d15c 100644 --- a/src/views/codeAnalytics/StatusBar/insightsStatusBar.ts +++ b/src/views/codeAnalytics/StatusBar/insightsStatusBar.ts @@ -4,8 +4,11 @@ import { DocumentInfo, DocumentInfoProvider } from "../../../services/documentIn import { WorkspaceState } from "../../../state"; import { CodeObjectInsight, InsightImporance } from "../InsightListView/IInsightListViewItemsCreator"; import { QuickPickItem } from "vscode"; -import { CodeObjectLocationInfo } from "../../../services/languages/extractors"; +import { CodeObjectLocationInfo, SpanLocationInfo } from "../../../services/languages/extractors"; import { EditorHelper } from "../../../services/EditorHelper"; +import { CodeObjectInfo } from "../codeAnalyticsView"; +import { SlowestSpansInsight } from "../InsightListView/EndpointInsight"; +import { Percentile } from "../InsightListView/CommonInsightObjects"; export interface InsightPickItem extends QuickPickItem{ location: CodeObjectLocationInfo; @@ -20,6 +23,7 @@ export class InsightsStatusBar implements vscode.Disposable { + public constructor(private _state: WorkspaceState, private _documentInfoProvider: DocumentInfoProvider, private _editorHelper: EditorHelper, @@ -33,22 +37,78 @@ export class InsightsStatusBar implements vscode.Disposable { if (doc!=null){ const docInfo = await _documentInfoProvider.getDocumentInfo(doc) - const insightsByMethod = (docInfo)?.insights.byMethod(_state.environment,docInfo); + const documentInsights = (docInfo)?.insights.forDocument(_state.environment,docInfo); + const insightsByType = documentInsights?.groupBy(x=>x.insight.type); + const insightsSorted = documentInsights?.sort((a,b)=>a.insight.importance-b.insight.importance) + .map(x=>x.insight.type); - if (insightsByMethod!=null){ - + const distinctInsights = [...new Set(insightsSorted)]; + + let items : QuickPickItem[]= []; + + if (insightsByType!=null && documentInsights!=null){ + for (const insightType of distinctInsights){ + let insightsForType = insightsByType[insightType]; + const insight= insightsForType.firstOrDefault(); + + items.push({ + label: `${this.getInsightEmoji(insight.insight)} ${insight.insight.name}`, + kind: vscode.QuickPickItemKind.Separator, + + }); + + if (insight.insight.shortDisplayInfo){ + const insightItems = insightsForType.map(x=>({ + label: `${this.getCodeObjectTypeEmoji(x.insight)} ${x.insight.shortDisplayInfo.targetDisplayName}`, + description: `${x.insight.shortDisplayInfo.subtitle}`, + detail: `${x.insight.shortDisplayInfo.description}`, + location: x.codeObject + })); + + items=items.concat(insightItems); + + + } + + // if (insightType=="SlowestSpans"){ + // const slowestSpans = insightsForType.map(x=> ( { + // insight: x.insight as SlowestSpansInsight, + // method: x.method, + // codeObject: x.codeObject + // })) + + + // .map(x=> ({ + // label: `${this.getCodeObjectTypeEmoji(x.insight)} ${x.insight.endpointSpan}`, + // description: `${x.insight.spans.length} spans`, + // detail: `${this.getBottleneckString(x.insight.spans.flatMap(y=>[({y:y,p:y.p50}), ({y:y,p:y.p95}), ({y:y,p:y.p99})]) + // .reduce((prev, current)=>(prev.p.fraction>current.p.fraction) ? prev : current))}`, + // location: x.codeObject + // })); + // items=items.concat(slowestSpans); + + // } + else{ + const typeInsightsItems = insightsForType.map(x=> ({ + label: `${this.getCodeObjectTypeEmoji(x.insight)} ${x.insight.name}`, + description: x.codeObject.id.split("$_$")[1], + detail: `method: ${x.method.name}`, + location: x.codeObject + })); + items=items.concat(typeInsightsItems); + + } + + + + } const quickPick = vscode.window.createQuickPick(); - quickPick.items = insightsByMethod.map(x=> ({ - label: `${this.getInsightEmoji(x.insight)} ${x.insight.name}`, - description: x.codeObject.id.split("$_$")[1], - detail: `method: ${x.method.name}`, - location: x.codeObject - })); + quickPick.items =items await quickPick.onDidChangeSelection(async selection => { if (selection[0]) { const item = selection[0] as InsightPickItem; const file = await _editorHelper.openTextDocumentFromUri(item.location.documentUri); - _editorHelper.openFileAndLine(file, item.location.range.start.line); + _editorHelper.openFileAndLine(file, item.location.range.start.line+1); await vscode.commands.executeCommand("workbench.view.extension.digma"); @@ -103,6 +163,9 @@ export class InsightsStatusBar implements vscode.Disposable { } } +// private getBottleneckString(percentile: any):string{ +// return `${percentile.p.fraction.toFixed(2)*100}% ${percentile.y.spanInfo.displayName} ${ percentile.p.maxDuration.value} ${ percentile.p.maxDuration.unit}`; +// } private getInsightEmoji(insight:CodeObjectInsight) :string{ if (this.isInteresting(insight)){ return this.interestingEmoji; @@ -113,6 +176,16 @@ export class InsightsStatusBar implements vscode.Disposable { return ''; } + + private getCodeObjectTypeEmoji(insight:CodeObjectInsight): string{ + if (insight.scope=="Span"){ + return '🔭'; + } + if (insight.scope=="EntrySpan"){ + return '⚆'; + } + return ''; + } private isInteresting(insight:CodeObjectInsight) :boolean{ return insight.importance<=InsightImporance.interesting &&