Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lana/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
},
"dependencies": {
"@apexdevtools/apex-ls": "^6.0.2",
"@apexdevtools/apex-parser": "^4.4.0",
"@apexdevtools/apex-parser": "5.0.0-beta.5",
"@salesforce/apex-node": "^8.4.11",
"@salesforce/core": "^8.26.3"
},
Expand Down
14 changes: 2 additions & 12 deletions lana/src/salesforce/ApexParser/ApexSymbolLocator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
/*
* Copyright (c) 2025 Certinia Inc. All rights reserved.
*/
import {
ApexLexer,
ApexParser,
CaseInsensitiveInputStream,
CommonTokenStream,
} from '@apexdevtools/apex-parser';
import { CharStreams } from 'antlr4ts';
import { ApexParserFactory } from '@apexdevtools/apex-parser';
import {
ApexVisitor,
type ApexConstructorNode,
Expand All @@ -23,11 +17,7 @@ export type SymbolLocation = {
};

export function parseApex(apexCode: string): ApexNode {
const parser = new ApexParser(
new CommonTokenStream(
new ApexLexer(new CaseInsensitiveInputStream(CharStreams.fromString(apexCode))),
),
);
const parser = ApexParserFactory.createParser(apexCode);
return new ApexVisitor().visit(parser.compilationUnit());
}

Expand Down
56 changes: 29 additions & 27 deletions lana/src/salesforce/ApexParser/ApexVisitor.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/*
* Copyright (c) 2025 Certinia Inc. All rights reserved.
*/
import type {
ApexParserVisitor,
ClassDeclarationContext,
ConstructorDeclarationContext,
FormalParametersContext,
MethodDeclarationContext,
import {
ApexParserBaseVisitor,
type ApexErrorNode,
type ApexParserRuleContext,
type ApexParseTree,
type ApexTerminalNode,
type ClassDeclarationContext,
type ConstructorDeclarationContext,
type FormalParametersContext,
type MethodDeclarationContext,
} from '@apexdevtools/apex-parser';
import type { ErrorNode, ParseTree, RuleNode, TerminalNode } from 'antlr4ts/tree';

type ApexNature = 'Constructor' | 'Class' | 'Method';

Expand Down Expand Up @@ -67,19 +70,18 @@ export interface ApexConstructorNode extends ApexParamNode {
nature: 'Constructor';
}

type VisitableApex = ParseTree & {
accept<Result>(visitor: ApexParserVisitor<Result>): Result;
};

export class ApexVisitor implements ApexParserVisitor<ApexNode> {
visit(ctx: ParseTree): ApexNode {
return ctx ? (ctx as VisitableApex).accept(this) : {};
export class ApexVisitor extends ApexParserBaseVisitor<ApexNode> {
override visit(ctx: ApexParseTree): ApexNode {
if (!ctx) {
return {};
}
return super.visit(ctx);
}

visitChildren(ctx: RuleNode): ApexNode {
override visitChildren(ctx: ApexParserRuleContext): ApexNode {
const children: ApexNode[] = [];

for (let index = 0; index < ctx.childCount; index++) {
for (let index = 0; index < ctx.getChildCount(); index++) {
const child = ctx.getChild(index);
const node = this.visit(child);
if (!node) {
Expand All @@ -98,25 +100,25 @@ export class ApexVisitor implements ApexParserVisitor<ApexNode> {

return {
nature: 'Class',
name: ident.text ?? '',
name: ident.getText() ?? '',
children: ctx.children?.length ? this.visitChildren(ctx).children : [],
line: start.line,
idCharacter: ident.start.charPositionInLine ?? 0,
idCharacter: ident.start.column ?? 0,
};
}

visitConstructorDeclaration(ctx: ConstructorDeclarationContext): ApexConstructorNode {
const { start } = ctx;
const idContexts = ctx.qualifiedName().id();
const idContexts = ctx.qualifiedName().id_list();
const constructorName = idContexts[idContexts.length - 1];

return {
nature: 'Constructor',
name: constructorName?.text ?? '',
name: constructorName?.getText() ?? '',
children: ctx.children?.length ? this.visitChildren(ctx).children : [],
params: this.getParameters(ctx.formalParameters()),
line: start.line,
idCharacter: start.charPositionInLine ?? 0,
idCharacter: start.column ?? 0,
};
}

Expand All @@ -126,25 +128,25 @@ export class ApexVisitor implements ApexParserVisitor<ApexNode> {

return {
nature: 'Method',
name: ident.text ?? '',
name: ident.getText() ?? '',
children: ctx.children?.length ? this.visitChildren(ctx).children : [],
params: this.getParameters(ctx.formalParameters()),
line: start.line,
idCharacter: ident.start.charPositionInLine ?? 0,
idCharacter: ident.start.column ?? 0,
};
}

visitTerminal(_ctx: TerminalNode): ApexNode {
override visitTerminal(_ctx: ApexTerminalNode): ApexNode {
return {};
}

visitErrorNode(_ctx: ErrorNode): ApexNode {
override visitErrorNode(_ctx: ApexErrorNode): ApexNode {
return {};
}

private getParameters(ctx: FormalParametersContext): string {
const paramsList = ctx.formalParameterList()?.formalParameter();
return paramsList?.map((param) => param.typeRef().text).join(',') ?? '';
const paramsList = ctx.formalParameterList()?.formalParameter_list();
return paramsList?.map((param) => param.typeRef().getText()).join(',') ?? '';
}

private forNode(node: ApexNode, anonHandler: (n: ApexNode) => void) {
Expand Down
7 changes: 6 additions & 1 deletion lana/src/salesforce/__tests__/ApexSymbolLocator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { getMethodLine, parseApex } from '../ApexParser/ApexSymbolLocator';
import { ApexVisitor, type ApexNode } from '../ApexParser/ApexVisitor';

jest.mock('../ApexParser/ApexVisitor');
jest.mock('@apexdevtools/apex-parser');
jest.mock('@apexdevtools/apex-parser', () => ({
ApexParserFactory: {
createParser: () => ({ compilationUnit: () => ({}) }),
},
ApexParserBaseVisitor: class {},
}));

describe('ApexSymbolLocator', () => {
const mockAST = {
Expand Down
Loading
Loading