Skip to content

Commit 92e0ef3

Browse files
authored
Merge pull request #245 from digma-ai/bug/folder_difference_makes_exception_links_not_work
Endpoint navigation and various bugs
2 parents e6478b5 + 14c68e3 commit 92e0ef3

28 files changed

+705
-240
lines changed

src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { InsightsStatusBar } from './views/codeAnalytics/StatusBar/insightsStatu
2121
import { EnvironmentManager } from './services/EnvironmentManager';
2222
import { EventManager } from './services/EventManager';
2323
import { Scheduler } from './services/Scheduler';
24+
import { SpanLinkResolver } from './services/spanLinkResolver';
2425

2526
export async function activate(context: vscode.ExtensionContext)
2627
{
@@ -45,7 +46,7 @@ export async function activate(context: vscode.ExtensionContext)
4546
const documentInfoProvider = new DocumentInfoProvider(analyticsProvider, symbolProvider, workspaceState);
4647
const editorHelper = new EditorHelper(sourceControl, documentInfoProvider);
4748
const codeLensProvider = new AnalyticsCodeLens(documentInfoProvider, workspaceState);
48-
49+
const spanLinkResolver = new SpanLinkResolver(symbolProvider,documentInfoProvider);
4950
const environmentManager = new EnvironmentManager(analyticsProvider, workspaceState);
5051
await environmentManager.initializeCurrentEnvironment();
5152

@@ -62,7 +63,7 @@ export async function activate(context: vscode.ExtensionContext)
6263
context.subscriptions.push(sourceControl);
6364
context.subscriptions.push(documentInfoProvider);
6465
context.subscriptions.push(new CodeAnalyticsView(analyticsProvider, documentInfoProvider,
65-
context.extensionUri, editorHelper, workspaceState, codeLensProvider, envStatusbar, environmentManager));
66+
context.extensionUri, editorHelper, workspaceState, codeLensProvider, envStatusbar, environmentManager,spanLinkResolver));
6667
context.subscriptions.push(new ErrorsLineDecorator(documentInfoProvider));
6768
context.subscriptions.push(new HotspotMarkerDecorator(documentInfoProvider));
6869
context.subscriptions.push(new VsCodeDebugInstrumentation(analyticsProvider));
@@ -73,6 +74,7 @@ export async function activate(context: vscode.ExtensionContext)
7374
environmentManager,
7475
documentInfoProvider,
7576
editorHelper,
77+
spanLinkResolver
7678
));
7779
}
7880

src/services/EditorHelper.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { Logger } from './logger';
55
import { SourceControl } from './sourceControl';
66
import { DocumentInfoProvider } from './documentInfoProvider';
77
import { Settings } from './../settings';
8-
import { ModulePathInfo } from './languages/modulePathToUriConverters';
8+
import { CodeObjectLocationInfo } from './languages/extractors';
9+
import { PossibleCodeObjectLocation } from './languages/modulePathToUriConverters';
910

1011
export interface EditorInfo {
1112
workspaceUri?: vscode.Uri;
@@ -18,9 +19,15 @@ export interface EditorInfo {
1819
}
1920

2021
export interface InstrumentationInfo {
21-
instrumentationName?: string;
22-
spanName?: string;
23-
fullName?: string
22+
instrumentationName: string;
23+
spanName: string;
24+
fullName?: string;
25+
codeObjectId: string | undefined;
26+
27+
}
28+
29+
export interface LocateEndpointInfo extends InstrumentationInfo {
30+
route?: string;
2431
}
2532

2633
export class EditorHelper {
@@ -143,7 +150,7 @@ export class EditorHelper {
143150
}
144151

145152
public async getWorkspaceFileUri(
146-
pathInfo: ModulePathInfo,
153+
pathInfo: PossibleCodeObjectLocation,
147154
document?: vscode.TextDocument,
148155
) : Promise<vscode.Uri | undefined> {
149156
if(!document) {
@@ -155,11 +162,11 @@ export class EditorHelper {
155162
return;
156163
}
157164

158-
const converters = await languageExtractor.getModulePathToUriConverters();
165+
const converters = await languageExtractor.getModulePathToUriConverters(this._documentInfoProvider);
159166
let uri: vscode.Uri | undefined;
160167
for (let index = 0; !uri && index < converters.length; index++) {
161168
const converter = converters[index];
162-
uri = await converter.convert(pathInfo);
169+
uri = (await converter.convert(pathInfo))?.uri;
163170
}
164171

165172
return uri;

src/services/EventManager.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import * as vscode from 'vscode';
22
import { EditorHelper } from './EditorHelper';
33
import { DocumentInfoProvider } from './documentInfoProvider';
4-
import { SpanSearchInfo } from './../views/codeAnalytics/InsightListView/Common/SpanSearch';
54
import { AnalyticsProvider, CodeObjectDurationChangeEvent } from './analyticsProvider';
65
import { Scheduler } from './Scheduler';
76
import { EnvironmentManager } from './EnvironmentManager';
8-
import { SpanSearch } from '../views/codeAnalytics/InsightListView/Common/SpanSearch';
97
import { Settings } from '../settings';
8+
import { CodeObjectLocationHints } from './languages/modulePathToUriConverters';
9+
import { SpanLinkResolver } from './spanLinkResolver';
1010

1111
export class EventManager implements vscode.Disposable {
1212
private lastFetch = new Date();
@@ -17,6 +17,7 @@ export class EventManager implements vscode.Disposable {
1717
private environmentManager: EnvironmentManager,
1818
private documentInfoProvider: DocumentInfoProvider,
1919
private editorHelper: EditorHelper,
20+
private spanLinkResolves: SpanLinkResolver
2021
) {
2122
if(Settings.enableNotifications.value) {
2223
scheduler.schedule(15, this.fetchEvents.bind(this));
@@ -36,13 +37,12 @@ export class EventManager implements vscode.Disposable {
3637
const item = 'Go';
3738
const response = await vscode.window.showInformationMessage(message, item);
3839
if(response === item) {
39-
const span: SpanSearchInfo = {
40+
const span: CodeObjectLocationHints = {
4041
instrumentationLibrary: eventData.span.instrumentationLibrary,
41-
name: eventData.span.name,
42+
spanName: eventData.span.name,
43+
codeObjectId: undefined
4244
};
43-
const spanSearch = new SpanSearch(this.documentInfoProvider);
44-
const spanLocations = await spanSearch.searchForSpans([span]);
45-
const spanLocation = spanLocations[0];
45+
const spanLocation = await this.spanLinkResolves.searchForSpanByHints(span);
4646
if(spanLocation !== undefined) {
4747
const uri = spanLocation.documentUri;
4848
const line = spanLocation.range.end.line + 1;

src/services/common/pathUtils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as vscode from 'vscode';
2+
import * as fs from 'fs';
3+
4+
export class PathUtils{
5+
6+
public static async getPathWithoutRootFolder(path:string): Promise<string>{
7+
let pathComponents = path.split('/');
8+
if (pathComponents.length>1){
9+
pathComponents = pathComponents.slice(1);
10+
}
11+
const modulePhysicalPathWithoutRoot = pathComponents.join('/');
12+
return modulePhysicalPathWithoutRoot;
13+
}
14+
15+
public static async getFileName(path:string): Promise<string>{
16+
let name = path.split('/').lastOrDefault();
17+
return name;
18+
}
19+
20+
public static async flexibleFileSearch(path:string, specialFolders: string[]): Promise<vscode.Uri|undefined>{
21+
22+
const modulePhysicalPathWithoutRoot = await PathUtils.getPathWithoutRootFolder(path);
23+
const fileName = await PathUtils.getFileName(path);
24+
25+
const specialFoldersGlob = specialFolders.map(x=>`**/${x}/**`).join(",");
26+
const files = await vscode.workspace.findFiles(`**/${fileName}}`, `{${specialFoldersGlob}}`);
27+
var relevantFiles = files.filter(x=>x.fsPath.endsWith(modulePhysicalPathWithoutRoot));
28+
let fileUri: vscode.Uri | undefined = undefined ;
29+
30+
//This is a workaround, in case of multiple matches we find the one closest to us
31+
//todo: find a better way
32+
if (relevantFiles){
33+
return relevantFiles.sort((x,y)=>x.fsPath.length-y.fsPath.length).firstOrDefault();
34+
}
35+
return fileUri;
36+
}
37+
38+
}

src/services/documentInfoProvider.ts

Lines changed: 8 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import { SymbolProvider } from './languages/symbolProvider';
66
import { Token, TokenType } from './languages/tokens';
77
import { Dictionary, Future } from './utils';
88
import { EndpointInfo, SpanLocationInfo as SpanLocationInfo, SymbolInfo, IParametersExtractor, CodeObjectLocationInfo, ISymbolAliasExtractor } from './languages/extractors';
9-
import { InstrumentationInfo } from './EditorHelper';
9+
import { InstrumentationInfo, LocateEndpointInfo } from './EditorHelper';
1010
import { SymbolInformation } from 'vscode';
1111
import { WorkspaceState } from '../state';
1212
import { CodeObjectInsight } from '../views/codeAnalytics/InsightListView/IInsightListViewItemsCreator';
13+
import { PathUtils } from './common/pathUtils';
14+
import { PossibleCodeObjectLocation } from './languages/modulePathToUriConverters';
1315

1416
export class DocumentInfoProvider implements vscode.Disposable
1517
{
@@ -67,86 +69,15 @@ export class DocumentInfoProvider implements vscode.Disposable
6769
}
6870
}
6971

72+
public async searchForEndpoint(locationInfo: LocateEndpointInfo): Promise<SpanLocationInfo|undefined>{
7073

71-
public async searchForSpan(instrumentationInfo: InstrumentationInfo): Promise<SpanLocationInfo|undefined>{
72-
const codeFileHint = instrumentationInfo.instrumentationName;
73-
74-
if (codeFileHint ){
75-
76-
//try to find code object by span name
77-
if (vscode.window.activeTextEditor?.document.fileName.toLocaleLowerCase().endsWith(".go")){
78-
79-
//TODO: change to use document info we alrady scanned
80-
let regex = /(\(\*?.*\).*)/;
81-
//workaround for GO
82-
let match = instrumentationInfo.fullName?.match(regex)?.firstOrDefault();
83-
if (match){
84-
match =match?.replace("(*","").replace(")","");
85-
let codeLocations:SymbolInformation[] = await vscode.commands.executeCommand("vscode.executeWorkspaceSymbolProvider", match);
86-
if (codeLocations){
87-
codeLocations=codeLocations.filter(x=>x.kind===vscode.SymbolKind.Method && x.name===match);
88-
if (codeLocations.length===1){
89-
return new SpanLocationInfo(instrumentationInfo.fullName!,instrumentationInfo.spanName!, [instrumentationInfo.spanName!],[], codeLocations[0].location.range, codeLocations[0].location.uri);
90-
}
91-
}
92-
}
93-
}
94-
95-
//try to find code object by instrumentation name
96-
97-
98-
99-
if (codeFileHint==='__main__'){
100-
101-
let doc = vscode.window.activeTextEditor?.document;
102-
if (doc){
103-
const docInfo = await this.getDocumentInfo(doc);
104-
if (docInfo){
105-
let spanInfos = docInfo.spans.filter(span => span.aliases.any(x=> x===instrumentationInfo.spanName));
106-
return spanInfos.firstOrDefault(x=>x!== undefined);
107-
108-
}
109-
110-
}
111-
112-
}
113-
else{
114-
115-
let codeHintFiles = codeFileHint.split(' ');
116-
let head = codeHintFiles[0];
117-
let folder = await vscode.workspace.workspaceFolders?.find(w => w.name === head);
118-
let tail= codeHintFiles;
119-
120-
if (folder) {
121-
tail = codeHintFiles.slice(1);
122-
}
123-
124-
if (codeHintFiles.length>=1){
125-
const files = await vscode.workspace.findFiles(`**/${tail.join('/')}.*`);
126-
127-
const spansPromises = files.map(async file =>{
128-
try{
129-
const doc = await vscode.workspace.openTextDocument(file);
130-
const docInfo = await this.getDocumentInfo(doc);
131-
if(docInfo){
132-
return docInfo.spans.filter(span => span.name===instrumentationInfo.spanName);
133-
}
134-
}
135-
catch(error){
136-
Logger.warn(`Searching for span "${instrumentationInfo.spanName}" skipped ${file.fsPath}`, error);
137-
}
138-
return [];
139-
});
140-
141-
const spnaInfos = (await Promise.all(spansPromises)).flat().firstOrDefault(x=>x!== undefined);
142-
return spnaInfos;
143-
}
144-
}
74+
if (locationInfo.codeObjectId){
14575

146-
14776
}
148-
}
14977

78+
return undefined;
79+
}
80+
15081
public async refresh(doc: vscode.TextDocument) {
15182

15283
const docRelativePath = doc.uri.toModulePath();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { PossibleCodeObjectLocation, CodeObjectLocationHints, ICodeObjectLocationGuesser } from "./modulePathToUriConverters";
2+
3+
export class GuessLocationByDefaultCodeObjectIdSchema implements ICodeObjectLocationGuesser {
4+
5+
constructor(private extension:string, private _defaultLogical:boolean){
6+
7+
}
8+
async guessLocation(codeObjectInfo: CodeObjectLocationHints ): Promise<PossibleCodeObjectLocation> {
9+
10+
if (codeObjectInfo.codeObjectId){
11+
const codeObjectParts= codeObjectInfo.codeObjectId.split("$_$");
12+
let idType="method";
13+
14+
if (codeObjectParts.length!==2){
15+
16+
let codeObjectPath = codeObjectInfo.codeObjectId;
17+
if (this._defaultLogical){
18+
codeObjectPath= codeObjectInfo.codeObjectId.replaceAll(":",".");
19+
return {
20+
modulePhysicalPath: undefined,
21+
spanName: codeObjectInfo.spanName,
22+
moduleLogicalPath: codeObjectPath
23+
};
24+
}
25+
else{
26+
return {
27+
modulePhysicalPath: codeObjectPath,
28+
spanName: codeObjectInfo.spanName,
29+
moduleLogicalPath: undefined
30+
};
31+
}
32+
}
33+
34+
let codeObjectPath = codeObjectParts[0];
35+
36+
const codeObjectIdAndType =codeObjectPath.split(`:`);
37+
if (codeObjectIdAndType.length===2){
38+
idType = codeObjectIdAndType[0];
39+
codeObjectPath=codeObjectIdAndType[1];
40+
}
41+
42+
if (idType==='span'){
43+
if (codeObjectParts.length===2){
44+
return {
45+
modulePhysicalPath: !this._defaultLogical ?
46+
codeObjectParts[0].replaceAll(`.`,`/`)+this.extension : undefined,
47+
spanName: codeObjectParts[1],
48+
moduleLogicalPath: this._defaultLogical ? codeObjectParts[0] : undefined
49+
};
50+
}
51+
}
52+
53+
else if (idType==='method'){
54+
return {
55+
modulePhysicalPath: !this._defaultLogical ? codeObjectParts[0] : undefined,
56+
methodName: codeObjectParts[1],
57+
moduleLogicalPath: this._defaultLogical? codeObjectParts[0] : undefined
58+
};
59+
60+
}
61+
62+
}
63+
64+
return {};
65+
}
66+
}
67+
68+
export class GuessLocationIfInstrumentationLibraryIsClass implements ICodeObjectLocationGuesser {
69+
70+
async guessLocation(codeObjectInfo: CodeObjectLocationHints ): Promise<PossibleCodeObjectLocation> {
71+
72+
return {
73+
moduleLogicalPath: codeObjectInfo.instrumentationLibrary,
74+
spanName: codeObjectInfo.spanName
75+
};
76+
77+
78+
}
79+
}
80+

src/services/languages/csharp/languageExtractor.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
import * as vscode from 'vscode';
22
import { CodeInspector } from '../../codeInspector';
3+
import { DocumentInfo, DocumentInfoProvider } from '../../documentInfoProvider';
4+
import { GuessLocationByDefaultCodeObjectIdSchema, GuessLocationIfInstrumentationLibraryIsClass } from '../codeObjectLocationGuesser';
35
import { IMethodExtractor, IParametersExtractor, ISpanExtractor } from '../extractors';
46
import { LanguageExtractor } from '../languageExtractor';
5-
import { IModulePathToUriConverter, LogicalModulePathToUriConverter, PhysicalModulePathToUriConverter } from '../modulePathToUriConverters';
7+
import { ICodeObjectLocationGuesser, IModulePathToUriConverter, LogicalModulePathToUriConverter, PhysicalModulePathToUriConverter } from '../modulePathToUriConverters';
8+
import { GuessLocationByInstrumentationLibrary } from '../python/codeObjectLocationGuesser';
69
import { CSharpMethodExtractor } from './methodExtractor';
710
import { CSharpParametersExtractor } from './parametersExtractor';
811
import { CSharpSpanExtractor } from './spanExtractor';
912
// import { AspNetCoreMvcEndpointExtractor } from './AspNetCoreMvcEndpointExtractor';
1013

1114
export class CSharpLanguageExtractor extends LanguageExtractor {
15+
16+
readonly defaultExtension=".cs";
17+
public get guessCodeObjectLocation(): ICodeObjectLocationGuesser[] {
18+
return [new GuessLocationByDefaultCodeObjectIdSchema(this.defaultExtension, true),
19+
new GuessLocationIfInstrumentationLibraryIsClass()];
20+
}
1221
public requiredExtensionLoaded: boolean = false;
1322

1423
public get requiredExtensionId(): string {
@@ -41,10 +50,10 @@ export class CSharpLanguageExtractor extends LanguageExtractor {
4150
];
4251
}
4352

44-
public async getModulePathToUriConverters(): Promise<IModulePathToUriConverter[]> {
53+
public async getModulePathToUriConverters(docInfoProvider: DocumentInfoProvider): Promise<IModulePathToUriConverter[]> {
4554
return [
46-
new LogicalModulePathToUriConverter(),
47-
new PhysicalModulePathToUriConverter(),
55+
new LogicalModulePathToUriConverter(docInfoProvider),
56+
new PhysicalModulePathToUriConverter([],docInfoProvider),
4857
];
4958
}
5059
}

0 commit comments

Comments
 (0)