Skip to content

Commit b7350ea

Browse files
authored
Merge pull request #257 from digma-ai/feature/add_method_info_features
Feature/add method info features
2 parents aeea2e5 + f232a70 commit b7350ea

File tree

6 files changed

+494
-20
lines changed

6 files changed

+494
-20
lines changed

src/analyticsCodeLens.ts

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,44 @@
11
import * as vscode from 'vscode';
22
import { Settings } from './settings';
3-
import { DocumentInfoProvider, MethodInfo } from './services/documentInfoProvider';
3+
import { DocumentInfo, DocumentInfoProvider, MethodInfo } from './services/documentInfoProvider';
44
import { CodeAnalyticsView } from './views/codeAnalytics/codeAnalyticsView';
55
import { WorkspaceState } from './state';
66
import { CodeObjectInsight, InsightImportance } from './views/codeAnalytics/InsightListView/IInsightListViewItemsCreator';
77
import { CodeObjectLocationInfo } from './services/languages/extractors';
88
import { UsageStatusResults } from './services/analyticsProvider';
9+
import { CodeInspector } from './services/codeInspector';
10+
import { Token } from './services/languages/tokens';
911

12+
export interface CodeLensData{
13+
14+
methodInfo : MethodInfo;
15+
insights: CodeObjectInsight[];
16+
17+
}
1018
export class AnalyticsCodeLens implements vscode.Disposable
1119
{
1220

1321
private _provider: CodelensProvider;
1422
private _disposables: vscode.Disposable[] = [];
1523

1624
constructor(documentInfoProvider: DocumentInfoProvider,
17-
state: WorkspaceState)
25+
state: WorkspaceState, condeInspector: CodeInspector)
1826
{
19-
this._provider = new CodelensProvider(documentInfoProvider,state);
27+
this._provider = new CodelensProvider(documentInfoProvider,state,condeInspector);
2028

2129
this._disposables.push(vscode.commands.registerCommand(CodelensProvider.clickCommand, async (methodInfo: MethodInfo, environment:string) => {
30+
2231
if(vscode.window.activeTextEditor) {
32+
if (methodInfo.documentUri!== vscode.window.activeTextEditor.document.uri){
33+
34+
const doc = await vscode.workspace.openTextDocument(methodInfo.documentUri);
35+
if (doc){
36+
await vscode.window.showTextDocument(doc, { preview: true });
37+
38+
}
39+
40+
}
41+
2342
vscode.window.activeTextEditor.selection = new vscode.Selection(methodInfo.range.start, methodInfo.range.start);
2443
}
2544

@@ -55,7 +74,7 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
5574
public readonly onDidChangeCodeLenses: vscode.Event<void> = this._onDidChangeCodeLenses.event;
5675

5776
constructor(private _documentInfoProvider: DocumentInfoProvider,
58-
private _state: WorkspaceState)
77+
private _state: WorkspaceState, private _codeInspector: CodeInspector)
5978
{
6079
}
6180

@@ -92,7 +111,6 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
92111
private async getCodeLens(methodInfo: MethodInfo,
93112
codeObjectInfo:CodeObjectLocationInfo,
94113
insights: CodeObjectInsight[],
95-
state: UsageStatusResults,
96114
environmentPrefix: boolean): Promise<vscode.CodeLens[]> {
97115

98116
const lens: vscode.CodeLens[] = [];
@@ -165,7 +183,7 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
165183
= insights
166184
.filter(x=>x.environment==this._state.environment);
167185

168-
const lenses = await this.getCodeLens(methodInfo,codeObject,currentEnvInsights,usageStatus,false);
186+
const lenses = await this.getCodeLens(methodInfo,codeObject,currentEnvInsights,false);
169187
for (const lens of lenses){
170188
codelens.push(lens);
171189
}
@@ -176,7 +194,7 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
176194
.filter(x=>x.decorators && x.importance<InsightImportance.important)
177195
.filter(x=>!currentEnvInsights.some(i=>i.type==x.type && i.importance==x.importance));
178196

179-
const otherEnvLenses = await this.getCodeLens(methodInfo,codeObject,otherEnvsInsightsToShow, usageStatus,true);
197+
const otherEnvLenses = await this.getCodeLens(methodInfo,codeObject,otherEnvsInsightsToShow,true);
180198
for (const lens of otherEnvLenses){
181199
codelens.push(lens);
182200
}
@@ -185,7 +203,67 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
185203
return codelens;
186204

187205
}
206+
207+
public async getCodeLensesForFunctions (document: vscode.TextDocument, documentInfo: DocumentInfo, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> {
208+
209+
210+
const memoised: {[token:string]: CodeLensData} = {};
211+
const functions = documentInfo.tokens.filter(x=>x.type==='function');
212+
const lens : vscode.CodeLens[]= [];
213+
for (const methodInfo of documentInfo.methods){
214+
215+
for (const func of functions.filter(x=>x.range.intersection(methodInfo.range))){
216+
217+
const funcKey = tokenKey(func.text, document.uri);
218+
if (funcKey in memoised){
219+
const data = memoised[funcKey];
220+
const newLens = await this.getCodeLens(data.methodInfo,getFakeCodeLocation(func),
221+
data.insights,false);
222+
lens.push(...newLens);
223+
}
188224

225+
else {
226+
227+
if (func.text === methodInfo.name && func.range === methodInfo.nameRange){
228+
continue;
229+
}
230+
const remoteMethodInfo =
231+
await this._codeInspector.getExecuteDefinitionMethodInfo(document, func.range.start, this._documentInfoProvider);
232+
233+
if(!remoteMethodInfo) {
234+
continue;
235+
}
236+
237+
const remoteDoc = await this._codeInspector.getDocumentInfo(document, func.range.start, this._documentInfoProvider);
238+
if(!remoteDoc) {
239+
continue;
240+
}
241+
242+
const insights = remoteDoc.insights.forMethod(remoteMethodInfo,this._state.environment);
243+
const funcLens = await this.getCodeLens(remoteMethodInfo,
244+
getFakeCodeLocation(func),insights,false);
245+
246+
lens.push(...funcLens);
247+
memoised[funcKey]={ insights: insights, methodInfo: remoteMethodInfo};
248+
}
249+
250+
}
251+
}
252+
253+
return lens;
254+
255+
256+
function getFakeCodeLocation(func: Token): CodeObjectLocationInfo {
257+
return {
258+
range: func.range, documentUri: documentInfo.uri, ids: [], displayName: func.text,
259+
id: func.text, idsWithType: [func.text]
260+
};
261+
}
262+
263+
function tokenKey(token:string, uri: vscode.Uri) {
264+
return `${uri}$$${token}`
265+
}
266+
}
189267
public async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]>
190268
{
191269
if (!Settings.enableCodeLens.value)
@@ -209,7 +287,7 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
209287

210288
const lenses = await this.getCodeLens(methodInfo,methodInfo,
211289
thisEnvInsights.filter(x=>x.scope=="Function"),
212-
documentInfo.usageData.getAll(),false);
290+
false);
213291

214292
for (const lens of lenses){
215293
methodCodeLens.push(lens);
@@ -272,7 +350,7 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
272350
.filter(x=>x.decorators && x.importance<InsightImportance.important);
273351

274352
const otherEnvLenses = await this.getCodeLens(methodInfo,methodInfo,otherEnvsInsights,
275-
documentInfo.usageData.getAll(),true);
353+
true);
276354
for (const lens of otherEnvLenses){
277355
methodCodeLens.push(lens);
278356
}
@@ -287,7 +365,8 @@ class CodelensProvider implements vscode.CodeLensProvider<vscode.CodeLens>
287365

288366
}
289367

290-
368+
const funcLenses = await this.getCodeLensesForFunctions(document,documentInfo,token);
369+
codelens.push(...funcLenses);
291370

292371
return codelens;
293372
}

src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { EventManager } from './services/EventManager';
2323
import { Scheduler } from './services/Scheduler';
2424
import { DocumentInfoCache } from './services/DocumentInfoCache';
2525
import { SpanLinkResolver } from './services/spanLinkResolver';
26+
import { PerformanceDecorator } from './views/codeAnalytics/decorators/performanceDecorator';
2627

2728
export async function activate(context: vscode.ExtensionContext)
2829
{
@@ -47,7 +48,7 @@ export async function activate(context: vscode.ExtensionContext)
4748
const documentInfoCache = new DocumentInfoCache(symbolProvider, analyticsProvider);
4849
const documentInfoProvider = new DocumentInfoProvider(analyticsProvider, symbolProvider, workspaceState, documentInfoCache);
4950
const editorHelper = new EditorHelper(sourceControl, documentInfoProvider);
50-
const codeLensProvider = new AnalyticsCodeLens(documentInfoProvider, workspaceState);
51+
const codeLensProvider = new AnalyticsCodeLens(documentInfoProvider, workspaceState, codeInspector);
5152
const spanLinkResolver = new SpanLinkResolver(symbolProvider,documentInfoProvider);
5253
const environmentManager = new EnvironmentManager(analyticsProvider, workspaceState);
5354
await environmentManager.initializeCurrentEnvironment();
@@ -61,12 +62,13 @@ export async function activate(context: vscode.ExtensionContext)
6162

6263
context.subscriptions.push(codeLensProvider);
6364
//context.subscriptions.push(new ContextView(analyticsProvider, context.extensionUri));
64-
context.subscriptions.push(new MethodCallErrorTooltip(documentInfoProvider, codeInspector));
65+
context.subscriptions.push(new MethodCallErrorTooltip(documentInfoProvider, codeInspector,workspaceState));
6566
context.subscriptions.push(sourceControl);
6667
context.subscriptions.push(documentInfoProvider);
6768
context.subscriptions.push(new CodeAnalyticsView(analyticsProvider, documentInfoProvider,
6869
context.extensionUri, editorHelper, workspaceState, codeLensProvider, envStatusbar, environmentManager,spanLinkResolver, documentInfoCache));
6970
context.subscriptions.push(new ErrorsLineDecorator(documentInfoProvider));
71+
context.subscriptions.push(new PerformanceDecorator(documentInfoProvider,workspaceState, codeInspector ));
7072
context.subscriptions.push(new HotspotMarkerDecorator(documentInfoProvider));
7173
context.subscriptions.push(new VsCodeDebugInstrumentation(analyticsProvider));
7274

src/services/codeInspector.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as vscode from 'vscode';
22

3-
import { DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
3+
import { DocumentInfo, DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
44
import { SymbolProvider, SymbolTree } from './languages/symbolProvider';
55
import { Token } from './languages/tokens';
66

@@ -15,7 +15,7 @@ export type DefinitionWithTokens = Definition & {
1515

1616
export class CodeInspector {
1717

18-
public async getExecuteDefinitionMethodInfo(
18+
public async getExecuteDefinitionMethodInfo(
1919
usageDocument: vscode.TextDocument,
2020
usagePosition: vscode.Position,
2121
documentInfoProvider: DocumentInfoProvider,
@@ -34,6 +34,19 @@ export class CodeInspector {
3434
return methodInfo;
3535
}
3636

37+
public async getDocumentInfo(
38+
usageDocument: vscode.TextDocument,
39+
usagePosition: vscode.Position,
40+
documentInfoProvider: DocumentInfoProvider,
41+
): Promise<DocumentInfo | undefined> {
42+
const definition = await this.getDefinition(usageDocument, usagePosition);
43+
if (!definition)
44+
return;
45+
46+
const docInfo = await documentInfoProvider.getDocumentInfo(definition.document);
47+
return docInfo;
48+
}
49+
3750
public async getDefinitionWithTokens(
3851
usageDocument: vscode.TextDocument,
3952
usagePosition: vscode.Position,

src/services/methodCallErrorTooltip.ts

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import * as vscode from 'vscode';
22
import { ErrorsViewTab } from '../views/codeAnalytics/errorsViewTab';
3-
import { DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
3+
import { DocumentInfo, DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
44
import { TokenType } from './languages/tokens';
55
import { CodeInspector } from './codeInspector';
6+
import { WorkspaceState } from '../state';
7+
import { SpanDurationsInsight } from '../views/codeAnalytics/InsightListView/SpanInsight';
68

79
export class MethodCallErrorTooltip implements vscode.Disposable
810
{
@@ -14,10 +16,11 @@ export class MethodCallErrorTooltip implements vscode.Disposable
1416
constructor(
1517
documentInfoProvider: DocumentInfoProvider,
1618
codeInspector: CodeInspector,
19+
workspaceState: WorkspaceState
1720
) {
1821
this._disposables.push(vscode.languages.registerHoverProvider(
1922
documentInfoProvider.symbolProvider.languageExtractors.map(x => x.documentFilter),
20-
new MethodCallErrorHoverProvider(documentInfoProvider, codeInspector))
23+
new MethodCallErrorHoverProvider(documentInfoProvider, codeInspector,workspaceState))
2124
);
2225
this._disposables.push(vscode.commands.registerCommand(MethodCallErrorTooltip.Commands.ShowErrorView, async (args) => {
2326
await vscode.commands.executeCommand(ErrorsViewTab.Commands.ShowErrorView, args.codeObjectId, args.codeObjectDisplayName, args.errorFlowId);
@@ -37,17 +40,19 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
3740
constructor(
3841
private _documentInfoProvider: DocumentInfoProvider,
3942
private _codeInspector: CodeInspector,
43+
private _workspaceState: WorkspaceState
4044
) {
4145
}
4246

43-
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | undefined>
47+
public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Hover | undefined>
4448
{
4549
const sourceDocInfo = await this._documentInfoProvider.getDocumentInfo(document);
4650
if(!sourceDocInfo) {
4751
return;
4852
}
4953

5054
let methodInfo: MethodInfo | undefined = sourceDocInfo?.methods.firstOrDefault((m) => m.nameRange?.contains(position) ?? false);
55+
let remoteDoc: DocumentInfo | undefined = undefined;
5156
if(!methodInfo){
5257
if(!sourceDocInfo.tokens.some(t => (t.type == TokenType.function || t.type == TokenType.method) && t.range.contains(position))) {
5358
return;
@@ -56,8 +61,16 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
5661
if(!methodInfo) {
5762
return;
5863
}
64+
remoteDoc = await this._codeInspector.getDocumentInfo(document, position, this._documentInfoProvider);
65+
if(!methodInfo) {
66+
return;
67+
}
68+
5969
}
60-
const markdown = await this.getMethodMarkdown(methodInfo);
70+
if (!remoteDoc){
71+
return;
72+
}
73+
const markdown = await this.getMethodMarkdown(methodInfo,remoteDoc);
6174
if(markdown)
6275
{
6376
return new vscode.Hover(markdown);
@@ -82,14 +95,49 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
8295
//return new vscode.Hover(markdown);
8396
}
8497

85-
private async getMethodMarkdown(methodInfo: MethodInfo): Promise<vscode.MarkdownString | undefined>
98+
private async getMethodMarkdown(methodInfo: MethodInfo, remoteDoc: DocumentInfo): Promise<vscode.MarkdownString | undefined>
8699
{
100+
let markdown = new vscode.MarkdownString('', true);
101+
102+
103+
const insights = remoteDoc.insights.forMethod(methodInfo, this._workspaceState.environment);
104+
const importantInsights = insights.filter(x=>x.importance<4);
105+
if (importantInsights.length>0){
106+
markdown.appendMarkdown(` <span style="color:#FF0000;"><i>${importantInsights.map(x=>x.name).join(' ')}</i></span>`);
107+
markdown.appendText('\n');
108+
109+
}
110+
111+
const perfStats = insights.filter(x=>x.name=="Performance Stats");
112+
for (var stat of perfStats){
113+
114+
const durationInsight : SpanDurationsInsight = stat as SpanDurationsInsight;
115+
116+
const p50 =durationInsight.percentiles.filter(x=>x.percentile==0.5)
117+
.firstOrDefault();
118+
const p95 =durationInsight.percentiles.filter(x=>x.percentile==0.95).firstOrDefault();
119+
const spanName = durationInsight.span.displayName;
120+
121+
if (p50 || p95){
122+
markdown.appendText(`${spanName} Duration: `);
123+
if (p50!=null){
124+
markdown.appendText(`${p50.currentDuration.value} ${p50.currentDuration.unit} (Median) `);
125+
}
126+
127+
if (p95!=null){
128+
markdown.appendText(`${p95.currentDuration.value} ${p95.currentDuration.unit} (P95) `);
129+
}
130+
markdown.appendText('\n');
131+
}
132+
133+
134+
135+
}
87136
const errors = await this._documentInfoProvider.analyticsProvider.getCodeObjectsErrors(methodInfo.idsWithType);
88137
if(!errors?.length) {
89138
return;
90139
}
91140

92-
const markdown = new vscode.MarkdownString('', true);
93141
markdown.appendText('Throws:\n');
94142
for(const error of errors)
95143
{
@@ -100,6 +148,8 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
100148
markdown.appendMarkdown(` \u00B7 [$(link-external)](command:${command}?${args} "Show in side panel") `);
101149
markdown.appendText('\n');
102150
}
151+
152+
103153
markdown.supportHtml = true;
104154
markdown.isTrusted = true;
105155
return markdown;

src/views/codeAnalytics/codeAnalyticsView.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { EnvironmentManager } from '../../services/EnvironmentManager';
3636
import { Action } from "./InsightListView/Actions/Action";
3737
import { SpanLinkResolver } from "../../services/spanLinkResolver";
3838
import { DocumentInfoCache, ScanningStatus } from "../../services/DocumentInfoCache";
39+
import { PerformanceDecorator } from "./decorators/performanceDecorator";
3940
//import { DigmaFileDecorator } from "../../decorators/fileDecorator";
4041

4142

@@ -444,6 +445,8 @@ class CodeAnalyticsViewProvider implements vscode.WebviewViewProvider,vscode.Dis
444445
this._overlay.hide();
445446
}
446447
this._currentCodeObject = codeObject;
448+
//await vscode.commands.executeCommand(PerformanceDecorator.Commands.Show);
449+
447450

448451
}
449452

0 commit comments

Comments
 (0)