@@ -12,16 +12,20 @@ import {
1212 BuiltinTypeContext ,
1313 ClassTypeNameContext ,
1414 ConstItemContext ,
15+ EndOfStatementContext ,
16+ EndOfStatementNoWsContext ,
1517 GlobalVariableDeclarationContext ,
1618 PrivateConstDeclarationContext ,
1719 PrivateVariableDeclarationContext ,
20+ ProcedureTailContext ,
1821 PublicConstDeclarationContext ,
1922 PublicVariableDeclarationContext ,
2023 TypeSpecContext ,
2124 TypeSuffixContext ,
2225 VariableDclContext ,
2326 WitheventsVariableDclContext
2427} from '../antlr/out/vbaParser' ;
28+ import { LineEndingContext } from '../antlr/out/vbafmtParser' ;
2529
2630
2731declare module 'antlr4ng' {
@@ -31,6 +35,8 @@ declare module 'antlr4ng' {
3135 startIndex ( ) : number ;
3236 stopIndex ( ) : number ;
3337 hasPositionOf ( ctx : ParserRuleContext ) : boolean ;
38+ endsWithLineEnding : boolean ;
39+ countTrailingLineEndings ( ) : number ;
3440 }
3541
3642 interface TerminalNode {
@@ -125,6 +131,88 @@ ParserRuleContext.prototype.hasPositionOf = function (ctx: ParserRuleContext): b
125131 return this . startIndex ( ) === ctx . startIndex ( ) && this . stopIndex ( ) === ctx . stopIndex ( ) ;
126132}
127133
134+ Object . defineProperty ( ParserRuleContext . prototype , 'endsWithLineEnding' , {
135+ get : function endsWithLineEnding ( ) {
136+ // Ensure we have a context.
137+ if ( ! ( this instanceof ParserRuleContext ) )
138+ return false ;
139+
140+ // Check last child is a line ending.
141+ const child = this . children . at ( - 1 ) ;
142+ if ( ! child )
143+ return false ;
144+
145+ // Check the various line ending contexts.
146+ if ( child instanceof LineEndingContext )
147+ return true ;
148+ if ( child instanceof EndOfStatementContext )
149+ return true ;
150+ if ( child instanceof EndOfStatementNoWsContext )
151+ return true ;
152+ if ( child instanceof ProcedureTailContext )
153+ return true ;
154+
155+ // Run it again!
156+ if ( child . getChildCount ( ) > 0 )
157+ return ( child as ParserRuleContext ) . endsWithLineEnding ;
158+
159+ // Not a line ending and no more children.
160+ return false ;
161+ }
162+ } )
163+
164+ interface LineEndingParserRuleContext {
165+ NEWLINE ( ) : TerminalNode | null ;
166+ }
167+
168+ function isLineEndingParserRuleContext ( ctx : unknown ) : ctx is LineEndingParserRuleContext {
169+ return typeof ctx === 'object'
170+ && ctx !== null
171+ && typeof ( ctx as any ) . NEWLINE === 'function' ;
172+ }
173+
174+ function countTrailingLineEndings ( ctx : ParserRuleContext ) : number {
175+ // This function recursively loops through last child of
176+ // the context to find one that has a NEWLINE terminal node.
177+
178+ // Check if we have a NEWLINE node.
179+ if ( isLineEndingParserRuleContext ( ctx ) ) {
180+ const lines = ctx . NEWLINE ( ) ?. getText ( ) ;
181+ if ( ! lines ) {
182+ return 0 ;
183+ }
184+
185+ let i = 0 ;
186+ let result = 0 ;
187+ while ( i < lines . length ) {
188+ const char = lines [ i ] ;
189+
190+ if ( char === '\r' ) {
191+ result ++ ;
192+ i += lines [ i + 1 ] === '\n' ? 2 : 1 ;
193+ } else if ( char === '\n' ) {
194+ result ++ ;
195+ i ++ ;
196+ }
197+ }
198+
199+ return result ;
200+ }
201+
202+ // Recursive call on last child.
203+ const lastChild = ctx . children . at ( - 1 ) ;
204+ if ( ! ! ( lastChild instanceof ParserRuleContext ) ) {
205+ return countTrailingLineEndings ( lastChild ) ;
206+ }
207+
208+ // If we get here, we have no trailing lines.
209+ return 0 ;
210+ }
211+
212+ ParserRuleContext . prototype . countTrailingLineEndings = function ( ) : number {
213+ return countTrailingLineEndings ( this ) ;
214+ }
215+
128216
129217TerminalNode . prototype . toRange = function ( doc : TextDocument ) : Range {
130218 return Range . create (
0 commit comments