66 * repo root or https://opensource.org/licenses/BSD-3-Clause
77 */
88
9- import { CharStreams , CommonTokenStream } from 'antlr4ts' ;
9+ import { CharStreams , CommonTokenStream , DefaultErrorStrategy } from 'antlr4ts' ;
1010import {
1111 ApexLexer ,
1212 ApexParser ,
@@ -56,6 +56,18 @@ export interface CompilationResultWithAssociations<T>
5656 commentAssociations : CommentAssociation [ ] ;
5757}
5858
59+ /**
60+ * Result of creating a parse tree.
61+ */
62+ export interface ParseTreeResult {
63+ fileName : string ;
64+ parseTree : CompilationUnitContext | TriggerUnitContext ;
65+ errorListener : ApexErrorListener ;
66+ lexer : ApexLexer ;
67+ tokenStream : CommonTokenStream ;
68+ parser : ApexParser ;
69+ }
70+
5971/**
6072 * Options for compilation behavior
6173 */
@@ -89,6 +101,52 @@ export class CompilerService {
89101 ) ;
90102 }
91103
104+ /**
105+ * Creates a parse tree from the given file content.
106+ * This method handles the setup of the lexer, parser, and error listeners.
107+ * @param fileContent The content of the Apex file.
108+ * @param fileName The name of the file, used for error reporting and trigger detection.
109+ * @returns A ParseTreeResult containing the parse tree and related objects.
110+ */
111+ private createParseTree (
112+ fileContent : string ,
113+ fileName : string = 'unknown.cls' ,
114+ ) : ParseTreeResult {
115+ this . logger . debug ( ( ) => `Creating parse tree for ${ fileName } ` ) ;
116+
117+ // Create error listener
118+ const errorListener = new ApexErrorListener ( fileName ) ;
119+
120+ // Set up parsing infrastructure
121+ const inputStream = CharStreams . fromString ( fileContent ) ;
122+ const lexer = new ApexLexer ( new CaseInsensitiveInputStream ( inputStream ) ) ;
123+ const tokenStream = new CommonTokenStream ( lexer ) ;
124+ const parser = new ApexParser ( tokenStream ) ;
125+ parser . errorHandler = new DefaultErrorStrategy ( ) ;
126+
127+ // Set up error listeners
128+ parser . removeErrorListeners ( ) ;
129+ lexer . removeErrorListeners ( ) ;
130+ parser . addErrorListener ( errorListener ) ;
131+ const lexerErrorListener = new ApexLexerErrorListener ( errorListener ) ;
132+ lexer . addErrorListener ( lexerErrorListener ) ;
133+
134+ // Parse the compilation unit
135+ const isTrigger = fileName . endsWith ( '.trigger' ) ;
136+ const parseTree = isTrigger
137+ ? parser . triggerUnit ( )
138+ : parser . compilationUnit ( ) ;
139+
140+ return {
141+ fileName,
142+ parseTree,
143+ errorListener,
144+ lexer,
145+ tokenStream,
146+ parser,
147+ } ;
148+ }
149+
92150 /**
93151 * Parse and compile a single Apex file.
94152 * @param fileContent The content of the Apex file to parse
@@ -109,8 +167,11 @@ export class CompilerService {
109167 this . logger . debug ( ( ) => `Starting compilation of ${ fileName } ` ) ;
110168
111169 try {
112- // Create error listener
113- const errorListener = new ApexErrorListener ( fileName ) ;
170+ // Create parse tree and get associated components
171+ const { parseTree, errorListener, tokenStream } = this . createParseTree (
172+ fileContent ,
173+ fileName ,
174+ ) ;
114175
115176 // Create comment collector by default (opt-out behavior)
116177 let commentCollector : ApexCommentCollectorListener | null = null ;
@@ -120,19 +181,6 @@ export class CompilerService {
120181 ) ;
121182 }
122183
123- // Set up parsing infrastructure
124- const inputStream = CharStreams . fromString ( fileContent ) ;
125- const lexer = new ApexLexer ( new CaseInsensitiveInputStream ( inputStream ) ) ;
126- const tokenStream = new CommonTokenStream ( lexer ) ;
127- const parser = new ApexParser ( tokenStream ) ;
128-
129- // Set up error listeners
130- parser . removeErrorListeners ( ) ;
131- lexer . removeErrorListeners ( ) ;
132- parser . addErrorListener ( errorListener ) ;
133- const lexerErrorListener = new ApexLexerErrorListener ( errorListener ) ;
134- lexer . addErrorListener ( lexerErrorListener ) ;
135-
136184 // Set up the main listener
137185 listener . setErrorListener ( errorListener ) ;
138186 const namespace = options . projectNamespace || this . projectNamespace ;
@@ -146,20 +194,14 @@ export class CompilerService {
146194 commentCollector . setTokenStream ( tokenStream ) ;
147195 }
148196
149- // Parse the compilation unit
150- const isTrigger = fileName . endsWith ( '.trigger' ) ;
151- const compilationUnitContext = isTrigger
152- ? parser . triggerUnit ( )
153- : parser . compilationUnit ( ) ;
154-
155197 // Walk the tree with the main listener
156198 const walker = new ParseTreeWalker ( ) ;
157- walker . walk ( listener , compilationUnitContext ) ;
199+ walker . walk ( listener , parseTree ) ;
158200
159201 // Walk the tree with comment collector if requested
160202 let comments : ApexComment [ ] = [ ] ;
161203 if ( commentCollector ) {
162- walker . walk ( commentCollector , compilationUnitContext ) ;
204+ walker . walk ( commentCollector , parseTree ) ;
163205 comments = commentCollector . getResult ( ) ;
164206 }
165207
@@ -404,40 +446,4 @@ export class CompilerService {
404446
405447 return results ;
406448 }
407-
408- private getCompilationUnit (
409- source : string ,
410- errorListener ?: ApexErrorListener ,
411- ) : CompilationUnitContext | TriggerUnitContext {
412- this . logger . debug ( 'Creating compilation unit' ) ;
413- const inputStream = CharStreams . fromString ( source ) ;
414- const lexer = new ApexLexer ( new CaseInsensitiveInputStream ( inputStream ) ) ;
415- const tokenStream = new CommonTokenStream ( lexer ) ;
416- const parser = new ApexParser ( tokenStream ) ;
417-
418- // Add our custom error listener if provided
419- if ( errorListener ) {
420- this . logger . debug ( 'Setting up custom error listeners' ) ;
421- // Remove default error listeners that print to console
422- parser . removeErrorListeners ( ) ;
423- lexer . removeErrorListeners ( ) ;
424-
425- // Add our custom error listener
426- parser . addErrorListener ( errorListener ) ;
427- // Create and add lexer-specific error listener
428- const lexerErrorListener = new ApexLexerErrorListener ( errorListener ) ;
429- lexer . addErrorListener ( lexerErrorListener ) ;
430- }
431-
432- // Check if this is a trigger file based on the file extension
433- const isTrigger =
434- errorListener ?. getFilePath ( ) ?. endsWith ( '.trigger' ) ?? false ;
435-
436- // Parse the compilation unit or trigger based on file type
437- this . logger . debug ( 'Parsing compilation unit' ) ;
438- const compilationUnitContext = isTrigger
439- ? parser . triggerUnit ( )
440- : parser . compilationUnit ( ) ;
441- return compilationUnitContext ;
442- }
443449}
0 commit comments