Skip to content

Commit feeed17

Browse files
authored
Merge pull request #191 from digma-ai/feature/js-unnamed-functions
Feature/js unnamed functions
2 parents 72083fc + bfec321 commit feeed17

File tree

12 files changed

+226
-115
lines changed

12 files changed

+226
-115
lines changed

src/services/languages/csharp/languageExtractor.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import * as vscode from 'vscode';
22
import { CodeInspector } from '../../codeInspector';
3-
import { IEndpointExtractor, ILanguageExtractor, IMethodExtractor, IParametersExtractor, ISpanExtractor } from '../extractors';
3+
import { IMethodExtractor, IParametersExtractor, ISpanExtractor } from '../extractors';
4+
import { LanguageExtractor } from "../languageExtractor";
45
import { CSharpMethodExtractor } from './methodExtractor';
56
import { CSharpParametersExtractor } from './parametersExtractor';
67
import { CSharpSpanExtractor } from './spanExtractor';
78
// import { AspNetCoreMvcEndpointExtractor } from './AspNetCoreMvcEndpointExtractor';
89

9-
export class CSharpLanguageExtractor implements ILanguageExtractor
10+
export class CSharpLanguageExtractor extends LanguageExtractor
1011
{
1112
public requiredExtensionLoaded: boolean = false;
1213

@@ -28,19 +29,15 @@ export class CSharpLanguageExtractor implements ILanguageExtractor
2829
return new CSharpParametersExtractor();
2930
}
3031

31-
public getEndpointExtractors(codeInspector: CodeInspector): IEndpointExtractor[] {
32-
return [
33-
// new AspNetCoreMvcEndpointExtractor(codeInspector),
34-
];
35-
}
32+
// public getEndpointExtractors(codeInspector: CodeInspector): IEndpointExtractor[] {
33+
// return [
34+
// new AspNetCoreMvcEndpointExtractor(codeInspector),
35+
// ];
36+
// }
3637

3738
public getSpanExtractors(codeInspector: CodeInspector): ISpanExtractor[] {
3839
return [
3940
new CSharpSpanExtractor(codeInspector),
4041
];
4142
}
42-
43-
public async validateConfiguration(): Promise<void>{
44-
45-
}
46-
}
43+
}

src/services/languages/extractors.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as vscode from 'vscode';
22
import { DocumentSymbol } from 'vscode-languageclient';
33
import { DocumentInfoProvider, ParameterInfo } from '../documentInfoProvider';
4-
import { CodeInspector } from '../codeInspector';
54
import { SymbolProvider, SymbolTree } from './symbolProvider';
65
import { Token } from './tokens';
76

@@ -40,6 +39,7 @@ export class EndpointInfo implements CodeObjectLocationInfo {
4039
return [ this.id];
4140
}
4241
}
42+
4343
export class SpanLocationInfo implements CodeObjectLocationInfo {
4444
constructor(
4545
public id: string,
@@ -94,14 +94,3 @@ export interface ISpanExtractor {
9494
symbolProvider: SymbolProvider,
9595
): Promise<SpanLocationInfo[]>;
9696
}
97-
98-
export interface ILanguageExtractor {
99-
requiredExtensionLoaded: boolean;
100-
get requiredExtensionId(): string;
101-
get documentFilter(): vscode.DocumentFilter;
102-
get methodExtractors(): IMethodExtractor[];
103-
get parametersExtractor(): IParametersExtractor;
104-
getEndpointExtractors(codeInspector: CodeInspector): IEndpointExtractor[];
105-
getSpanExtractors(codeInspector: CodeInspector): ISpanExtractor[];
106-
validateConfiguration(): Promise<void>
107-
}

src/services/languages/go/languageExtractor.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import * as vscode from 'vscode';
22
import { CodeInspector } from '../../codeInspector';
33
import { Logger } from '../../logger';
4-
import { BasicParametersExtractor } from '../defaultImpls';
5-
import { IEndpointExtractor, ILanguageExtractor, IMethodExtractor, IParametersExtractor, ISpanExtractor } from '../extractors';
4+
import { IMethodExtractor, ISpanExtractor } from '../extractors';
5+
import { LanguageExtractor } from '../languageExtractor';
66
import { GoMethodExtractor } from './methodExtractor';
77
import { GoSpanExtractor } from './spanExtractor';
88

9-
10-
11-
export class GoLanguageExtractor implements ILanguageExtractor
9+
export class GoLanguageExtractor extends LanguageExtractor
1210
{
1311
public requiredExtensionLoaded: boolean = false;
1412

@@ -26,21 +24,14 @@ export class GoLanguageExtractor implements ILanguageExtractor
2624
];
2725
}
2826

29-
public get parametersExtractor(): IParametersExtractor {
30-
return new BasicParametersExtractor();
31-
}
32-
33-
public getEndpointExtractors(codeInspector: CodeInspector): IEndpointExtractor[] {
34-
return [];
35-
}
36-
3727
public getSpanExtractors(codeInspector: CodeInspector): ISpanExtractor[] {
3828
return [
3929
new GoSpanExtractor(codeInspector)
4030
];
4131
}
32+
4233
public async validateConfiguration(): Promise<void>{
43-
const section:any = vscode.workspace.getConfiguration().get("gopls");
34+
const section: any = vscode.workspace.getConfiguration().get("gopls");
4435
if(section !== undefined && section["ui.semanticTokens"]){
4536
return;
4637
}
Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import * as vscode from 'vscode';
22
import { CodeInspector } from '../../codeInspector';
3-
import { Logger } from '../../logger';
4-
import { BasicParametersExtractor } from '../defaultImpls';
5-
import { IEndpointExtractor, ILanguageExtractor, IMethodExtractor, IParametersExtractor, ISpanExtractor } from '../extractors';
3+
import { IMethodExtractor, ISpanExtractor } from '../extractors';
4+
import { LanguageExtractor } from '../languageExtractor';
5+
import { IMethodPositionSelector } from '../methodPositionSelector';
6+
import { JSMethodPositionSelector } from './methodPositionSelector';
67
import { JSMethodExtractor } from './methodExtractor';
78
import { JSSpanExtractor } from './spanExtractor';
89

9-
10-
11-
12-
export class JSLanguageExtractor implements ILanguageExtractor
10+
export class JSLanguageExtractor extends LanguageExtractor
1311
{
1412
public requiredExtensionLoaded: boolean = false;
1513

@@ -27,20 +25,13 @@ export class JSLanguageExtractor implements ILanguageExtractor
2725
];
2826
}
2927

30-
public get parametersExtractor(): IParametersExtractor {
31-
return new BasicParametersExtractor();
32-
}
33-
34-
public getEndpointExtractors(codeInspector: CodeInspector): IEndpointExtractor[] {
35-
return [];
28+
public get methodPositionSelector(): IMethodPositionSelector {
29+
return new JSMethodPositionSelector();
3630
}
3731

3832
public getSpanExtractors(codeInspector: CodeInspector): ISpanExtractor[] {
3933
return [
4034
new JSSpanExtractor(codeInspector)
4135
];
4236
}
43-
public async validateConfiguration(): Promise<void>{
44-
45-
}
4637
}

src/services/languages/javascript/methodExtractor.ts

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import * as vscode from 'vscode';
22
import * as path from 'path';
3-
import { DocumentSymbol, SymbolKind } from "vscode-languageclient";
4-
import { IMethodExtractor, SymbolInfo } from "../extractors";
3+
import { DocumentSymbol } from 'vscode-languageclient';
4+
import { IMethodExtractor, SymbolInfo } from '../extractors';
55
import { Logger } from '../../logger';
66
import { Token, TokenType } from '../tokens';
7+
import {
8+
MethodSymbolInfoExtractor,
9+
SymbolInfoExtractor,
10+
NamedFunctionDeclarationSymbolInfoExtractor,
11+
AnonymousExpressRequestHandlerSymbolInfoExtractor,
12+
VariableFunctionSymbolInfoExtractor,
13+
} from './symbolInfoExtractors';
714

815
export class JSMethodExtractor implements IMethodExtractor {
916

@@ -45,8 +52,8 @@ export class JSMethodExtractor implements IMethodExtractor {
4552
return pkgjson.name;
4653
}
4754

48-
private extractFunctions(document: vscode.TextDocument, codeObjectPath: string, parentSymPath: string, symbols: DocumentSymbol[], tokens: Token[]): SymbolInfo[] {
49-
let symbolInfos: SymbolInfo[] = [];
55+
private extractFunctions(document: vscode.TextDocument, codeObjectPath: string, parentSymbolPath: string, symbols: DocumentSymbol[], tokens: Token[]): SymbolInfo[] {
56+
const symbolInfos: SymbolInfo[] = [];
5057

5158
/*
5259
declation example:
@@ -76,49 +83,31 @@ export class JSMethodExtractor implements IMethodExtractor {
7683
functionMap[key] = token;
7784
});
7885

86+
const symbolInfoExtractors: SymbolInfoExtractor[] = [
87+
new MethodSymbolInfoExtractor(),
88+
new NamedFunctionDeclarationSymbolInfoExtractor(),
89+
new AnonymousExpressRequestHandlerSymbolInfoExtractor(),
90+
new VariableFunctionSymbolInfoExtractor(functionMap, getKey),
91+
];
92+
7993
for (const symbol of symbols) {
80-
let symPath = (parentSymPath ? parentSymPath + '.' : '') + symbol.name;
81-
const hasChildren = symbol.children && symbol.children.length > 0;
82-
let range = new vscode.Range(
83-
new vscode.Position(symbol.range.start.line, symbol.range.start.character),
84-
new vscode.Position(symbol.range.end.line, symbol.range.end.character));
85-
86-
const id = `${codeObjectPath}$_$${symbol.name}`;
87-
let isMethodCodeObjectRelated = this.isOfKind(symbol, SymbolKind.Method);
88-
89-
if (!isMethodCodeObjectRelated && this.isOfKind(symbol, SymbolKind.Function)) {
90-
const textLine = document.lineAt(symbol.range.start.line);
91-
const functionMatch = `\\s*function\\s*${symbol.name}`; //should handle only function declaration, and filter out function call like db.getAll()
92-
const match = textLine.text.match(functionMatch);
93-
isMethodCodeObjectRelated = match !== undefined && match!== null;
94-
}
95-
if (!isMethodCodeObjectRelated && this.isOfKind(symbol, SymbolKind.Variable)) {
96-
const functionToken = functionMap[getKey(symbol.range.start.line, symbol.range.start.character)];
97-
isMethodCodeObjectRelated = functionToken !== undefined;
94+
const symbolPath = (parentSymbolPath ? parentSymbolPath + '.' : '') + symbol.name;
95+
96+
const symbolInfo: SymbolInfo | undefined = symbolInfoExtractors.reduce(
97+
(info: SymbolInfo | undefined, extractor) => info || extractor.extract(symbol, codeObjectPath, symbol.name, document, symbolPath),
98+
undefined,
99+
);
100+
if(symbolInfo) {
101+
symbolInfos.push(symbolInfo);
98102
}
99103

100-
if (isMethodCodeObjectRelated) {
101-
symbolInfos.push({
102-
id,
103-
name: symbol.name,
104-
codeLocation: codeObjectPath,
105-
displayName: symPath,
106-
range,
107-
documentUri: document.uri
108-
});
109-
}
104+
const hasChildren = symbol.children && symbol.children.length > 0;
110105
if (hasChildren) {
111-
const childFunctions = this.extractFunctions(document, codeObjectPath, symPath, symbol.children!, tokens);
112-
symbolInfos = symbolInfos.concat(childFunctions);
106+
const childFunctions = this.extractFunctions(document, codeObjectPath, symbolPath, symbol.children!, tokens);
107+
symbolInfos.push(...childFunctions);
113108
}
114109
}
115110

116111
return symbolInfos;
117-
118-
}
119-
120-
private isOfKind(symbol: DocumentSymbol, kind: number): boolean {
121-
return symbol.kind + 1 === kind;
122112
}
123-
124-
}
113+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Position } from 'vscode';
2+
import { MethodInfo } from '../../documentInfoProvider';
3+
import { IMethodPositionSelector } from '../methodPositionSelector';
4+
5+
export class JSMethodPositionSelector implements IMethodPositionSelector {
6+
filter(position: Position, methods: MethodInfo[]): MethodInfo | undefined {
7+
const candidates = methods.filter(method => method.range.contains(position));
8+
if (candidates.length === 0) {
9+
return undefined;
10+
}
11+
12+
const method = candidates.reduce(
13+
(nearest, current) => current.range.start.isAfterOrEqual(nearest.range.start)
14+
? current
15+
: nearest
16+
);
17+
return method;
18+
}
19+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import * as vscode from 'vscode';
2+
import { DocumentSymbol, SymbolKind } from 'vscode-languageclient';
3+
import { SymbolInfo } from '../extractors';
4+
import { Token } from '../tokens';
5+
6+
export abstract class SymbolInfoExtractor {
7+
extract(symbol: DocumentSymbol, codeObjectPath: string, name: string, document: vscode.TextDocument, symbolPath: string): SymbolInfo | undefined {
8+
const range = new vscode.Range(
9+
new vscode.Position(symbol.range.start.line, symbol.range.start.character),
10+
new vscode.Position(symbol.range.end.line, symbol.range.end.character)
11+
);
12+
const id = `${codeObjectPath}$_$${name}`;
13+
14+
return {
15+
id,
16+
name,
17+
codeLocation: codeObjectPath,
18+
displayName: symbolPath,
19+
range,
20+
documentUri: document.uri
21+
};
22+
}
23+
24+
protected isOfKind(symbol: DocumentSymbol, kind: number): boolean {
25+
return symbol.kind + 1 === kind;
26+
}
27+
}
28+
29+
export class MethodSymbolInfoExtractor extends SymbolInfoExtractor {
30+
extract(symbol: DocumentSymbol, codeObjectPath: string, name: string, document: vscode.TextDocument, symbolPath: string): SymbolInfo | undefined {
31+
if(this.isOfKind(symbol, SymbolKind.Method)) {
32+
return super.extract(symbol, codeObjectPath, name, document, symbolPath);
33+
}
34+
}
35+
}
36+
37+
export class NamedFunctionDeclarationSymbolInfoExtractor extends SymbolInfoExtractor {
38+
extract(symbol: DocumentSymbol, codeObjectPath: string, name: string, document: vscode.TextDocument, symbolPath: string): SymbolInfo | undefined {
39+
if (this.isOfKind(symbol, SymbolKind.Function)) {
40+
const textLine = document.lineAt(symbol.range.start.line);
41+
const functionMatch = `\\s*function\\s*${symbol.name}`; //should handle only function declaration, and filter out function call like db.getAll()
42+
const match = textLine.text.match(functionMatch);
43+
if(match !== undefined && match !== null) {
44+
return super.extract(symbol, codeObjectPath, name, document, symbolPath);
45+
}
46+
}
47+
}
48+
}
49+
50+
export class AnonymousExpressRequestHandlerSymbolInfoExtractor extends SymbolInfoExtractor {
51+
extract(symbol: DocumentSymbol, codeObjectPath: string, name: string, document: vscode.TextDocument, symbolPath: string): SymbolInfo | undefined {
52+
if (this.isOfKind(symbol, SymbolKind.Function)) {
53+
const pattern = /.*(get|head|post|put|delete|connect|options|trace|patch)\s*\(\s*['"`](.*)['"`]\)\s*callback$/i;
54+
const match = name.match(pattern);
55+
if(match) {
56+
const[ , verb, route ] = match;
57+
name = `${verb.toUpperCase()} ${route}`;
58+
return super.extract(symbol, codeObjectPath, name, document, symbolPath);
59+
}
60+
}
61+
}
62+
}
63+
64+
export class VariableFunctionSymbolInfoExtractor extends SymbolInfoExtractor {
65+
constructor(
66+
private functionMap: Record<string, Token>,
67+
private getKey: (line: number, character: number) => string,
68+
) {
69+
super();
70+
}
71+
72+
extract(symbol: DocumentSymbol, codeObjectPath: string, name: string, document: vscode.TextDocument, symbolPath: string): SymbolInfo | undefined {
73+
if (this.isOfKind(symbol, SymbolKind.Variable)) {
74+
const key = this.getKey(symbol.range.start.line, symbol.range.start.character);
75+
const functionToken = this.functionMap[key];
76+
if(functionToken !== undefined) {
77+
return super.extract(symbol, codeObjectPath, name, document, symbolPath);
78+
}
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)