@@ -58,7 +58,7 @@ export class FormatOnTypeProvider {
5858 line ,
5959 semicolonCol + 1 ,
6060 ) ;
61- let curBlockZoneType : "proc" | "data" | "macro" | undefined ;
61+ let curBlockZoneType : "proc" | "data" | undefined ;
6262 let shouldDecIndent = false ;
6363 if (
6464 zoneAfterSemicolon === ZONE_TYPE . GBL_STMT ||
@@ -78,26 +78,12 @@ export class FormatOnTypeProvider {
7878 case ZT . DATA_STEP_STMT_OPT :
7979 case ZT . DATA_STEP_STMT_OPT_VALUE :
8080 ! curBlockZoneType && ( curBlockZoneType = "data" ) ;
81- // eslint-disable-next-line no-fallthrough
82- case ZT . MACRO_STMT :
83- case ZT . MACRO_STMT_OPT :
84- case ZT . MACRO_STMT_OPT_VALUE :
85- case ZT . MACRO_STMT_BODY : {
86- ! curBlockZoneType && ( curBlockZoneType = "macro" ) ;
8781 shouldDecIndent = true ;
88- break ;
89- }
9082 }
9183 }
9284
9385 const foldingBlock : FoldingBlock | null =
94- this . syntaxProvider . getFoldingBlock (
95- line ,
96- semicolonCol ,
97- false ,
98- true ,
99- true ,
100- ) ;
86+ this . syntaxProvider . getFoldingBlock ( line , semicolonCol , true , true , true ) ;
10187 // Detect recursive block, which is not supported yet
10288 switch ( curBlockZoneType ) {
10389 case "data" : {
@@ -112,12 +98,6 @@ export class FormatOnTypeProvider {
11298 }
11399 break ;
114100 }
115- case "macro" : {
116- if ( foldingBlock ?. type !== LexerEx . SEC_TYPE . MACRO ) {
117- return [ ] ;
118- }
119- break ;
120- }
121101 }
122102
123103 let referLine ;
@@ -135,7 +115,7 @@ export class FormatOnTypeProvider {
135115 this . syntaxProvider . getFoldingBlock (
136116 lastNotEmptyLine ,
137117 prevLineText . length - 1 ,
138- false ,
118+ true ,
139119 true ,
140120 true ,
141121 ) ;
@@ -149,19 +129,21 @@ export class FormatOnTypeProvider {
149129 } else {
150130 referLine = foldingBlock . startLine ;
151131 }
152- // when the ending word is in the separate line as following cases, the indentationRules cannot match it,
153- // we need to ajust the line indentation to the same as the last line.
132+
154133 if ( ! shouldDecIndent ) {
134+ // when the ending word is in the separate line as following cases, the indentationRules cannot match it,
135+ // we need to ajust the line indentation to the same as the last line.
136+ let [ tokenText , tokenStyle , tokenCol ] = this . _getPrevValidTokenInfo (
137+ line ,
138+ semicolonCol - 1 ,
139+ false ,
140+ true ,
141+ ) ;
155142 /*
156143 * if the ending word is part of a string or comment and is in the next line.
157144 * a ='
158145 * run;
159146 */
160- const [ tokenText , tokenCol , tokenStyle ] = this . _getPrevValidTokenInfo (
161- line ,
162- semicolonCol - 1 ,
163- false ,
164- ) ;
165147 if (
166148 tokenStyle &&
167149 [
@@ -179,6 +161,11 @@ export class FormatOnTypeProvider {
179161 shouldDecIndent = true ;
180162 }
181163 }
164+ [ tokenText , tokenStyle , tokenCol ] = this . _getPrevValidTokenInfo (
165+ line ,
166+ semicolonCol - 1 ,
167+ false ,
168+ ) ;
182169 /*
183170 * a =
184171 * run;
@@ -206,14 +193,52 @@ export class FormatOnTypeProvider {
206193 }
207194 }
208195 }
196+ if ( shouldDecIndent ) {
197+ referLine = lastNotEmptyLine ;
198+ // if the last line is the start line of the block, need to add extra indentation.
199+ // it's impossible to be the end line of the block here.
200+ if ( foldingBlock ?. startLine === lastNotEmptyLine ) {
201+ extraIndent = tabSize ;
202+ }
203+ }
204+
205+ // macro
209206 if ( ! shouldDecIndent ) {
210- return [ ] ;
207+ if (
208+ tokenStyle === Lexer . TOKEN_TYPES . MSKEYWORD &&
209+ tokenText ?. toUpperCase ( ) === "%MEND"
210+ ) {
211+ shouldDecIndent = true ;
212+ }
213+ if ( tokenText ?. toUpperCase ( ) !== "%MEND" && tokenCol ) {
214+ const [ prevTokenText , prevTokenStyle ] = this . _getPrevValidTokenInfo (
215+ line ,
216+ tokenCol - 1 ,
217+ ) ;
218+ if (
219+ ( prevTokenStyle === Lexer . TOKEN_TYPES . MKEYWORD ||
220+ prevTokenStyle === Lexer . TOKEN_TYPES . MSKEYWORD ) &&
221+ prevTokenText ?. toUpperCase ( ) === "%MEND"
222+ ) {
223+ shouldDecIndent = true ;
224+ }
225+ }
211226 }
212- referLine = lastNotEmptyLine ;
213- // if the last line is the start line of the block, need to add extra indentation.
214- // it's impossible to be the end line of the block here.
215- if ( foldingBlock ?. startLine === lastNotEmptyLine ) {
216- extraIndent = tabSize ;
227+
228+ // multiple run
229+ if (
230+ zoneBeforeSemicolon === ZT . GBL_STMT &&
231+ zoneAfterSemicolon === ZT . GBL_STMT &&
232+ ( tokenStyle === Lexer . TOKEN_TYPES . SKEYWORD ||
233+ tokenStyle === Lexer . TOKEN_TYPES . KEYWORD ) &&
234+ tokenText ?. toUpperCase ( ) === "RUN"
235+ ) {
236+ shouldDecIndent = true ;
237+ referLine = lastNotEmptyLine ;
238+ }
239+
240+ if ( ! shouldDecIndent ) {
241+ return [ ] ;
217242 }
218243 }
219244
@@ -300,51 +325,57 @@ export class FormatOnTypeProvider {
300325 curIndent : number ,
301326 tabSize : number ,
302327 ) : number | undefined {
303- // find semicolon token
304- const tokens : SyntaxToken [ ] = this . syntaxProvider . getSyntax ( line ) ;
305- const cleanedTokens = this . _cleanTokens ( line , tokens ) ;
306- if ( cleanedTokens . length === 0 ) {
307- return 0 ;
308- }
309- // find patterns of "data xxx;", "proc xxx;" or "%macro xxx;"
310- const lineText = this . model . getLine ( line ) ;
311- let curIndex = cleanedTokens . length ;
312- do {
313- curIndex = this . _findSemicolonTokenRightToLeft (
314- line ,
315- cleanedTokens ,
316- curIndex - 1 ,
317- ) ;
318- if ( curIndex <= 0 ) {
319- return 0 ;
320- }
321- const tokenBeforeSemicolon = cleanedTokens [ curIndex - 1 ] ; // curIndex must be > 0
322- const tokenBeforeSemicolonText = this . _getTokenText (
323- cleanedTokens ,
324- curIndex - 1 ,
325- lineText ,
326- ) . trim ( ) ;
327- if (
328- ( tokenBeforeSemicolon . style === Lexer . TOKEN_TYPES . SKEYWORD ||
329- tokenBeforeSemicolon . style === Lexer . TOKEN_TYPES . MSKEYWORD ) &&
330- ( tokenBeforeSemicolonText . toLowerCase ( ) === "run" ||
331- tokenBeforeSemicolonText . toLowerCase ( ) === "quit" ||
332- tokenBeforeSemicolonText . toLowerCase ( ) === "%mend" )
333- ) {
334- return 0 ;
335- }
336- // calculate indent
337- const prevSemicolonZone : number = this . czMgr . getCurrentZone (
338- line ,
339- tokenBeforeSemicolon . start ,
340- ) ;
341- if ( prevSemicolonZone === CodeZoneManager . ZONE_TYPE . RESTRICTED ) {
342- return undefined ;
328+ let curLine = line ;
329+ let isFoundSemicolon = false ;
330+ while ( curLine >= 0 ) {
331+ const tokens : SyntaxToken [ ] = this . syntaxProvider . getSyntax ( curLine ) ;
332+ const cleanedTokens = this . _cleanTokens ( curLine , tokens ) ;
333+ const lineText = this . model . getLine ( curLine ) ;
334+ let curIndex = cleanedTokens . length - 1 ;
335+ while ( curIndex >= 0 ) {
336+ const curToken = cleanedTokens [ curIndex ] ;
337+ const curTokenText = this . _getTokenText (
338+ cleanedTokens ,
339+ curIndex ,
340+ lineText ,
341+ ) . trim ( ) ;
342+ curIndex -- ;
343+ // nothing should be done before matching ";"
344+ if ( ! isFoundSemicolon && curTokenText !== ";" ) {
345+ continue ;
346+ }
347+ isFoundSemicolon = true ;
348+ if (
349+ curToken . style === Lexer . TOKEN_TYPES . SKEYWORD ||
350+ curToken . style === Lexer . TOKEN_TYPES . KEYWORD ||
351+ curToken . style === Lexer . TOKEN_TYPES . MKEYWORD ||
352+ curToken . style === Lexer . TOKEN_TYPES . MSKEYWORD
353+ ) {
354+ if (
355+ curTokenText . toUpperCase ( ) === "DATA" ||
356+ curTokenText . toUpperCase ( ) === "PROC" ||
357+ curTokenText . toUpperCase ( ) === "%MACRO"
358+ ) {
359+ return tabSize - ( curIndent % tabSize ) ;
360+ }
361+ if (
362+ curTokenText . toUpperCase ( ) === "RUN" ||
363+ curTokenText . toUpperCase ( ) === "QUIT" ||
364+ curTokenText . toUpperCase ( ) === "%MEND"
365+ ) {
366+ return 0 ;
367+ }
368+ }
369+
370+ // previous lines
371+ if ( curLine < line && curTokenText === ";" ) {
372+ return 0 ;
373+ }
343374 }
344- if ( this . _isDefZone ( prevSemicolonZone ) ) {
345- return tabSize - ( curIndent % tabSize ) ;
375+ if ( curIndex < 0 ) {
376+ curLine -- ;
346377 }
347- } while ( curIndex > 0 ) ;
378+ }
348379 return 0 ;
349380 }
350381
@@ -365,57 +396,6 @@ export class FormatOnTypeProvider {
365396 return cleanedTokens ;
366397 }
367398
368- private _findSemicolonTokenRightToLeft (
369- line : number ,
370- tokens : SyntaxToken [ ] ,
371- startIndex : number ,
372- ) : number {
373- const lineText = this . model . getLine ( line ) ;
374- if ( startIndex < 0 ) {
375- return - 1 ;
376- } else if ( startIndex >= tokens . length ) {
377- startIndex = tokens . length - 1 ;
378- }
379- let semicolonTokenIdx = - 1 ;
380- for ( let i = startIndex ; i >= 0 ; i -- ) {
381- const curToken = tokens [ i ] ;
382- if ( lineText [ curToken . start ] === ";" ) {
383- semicolonTokenIdx = i ;
384- break ;
385- }
386- }
387- return semicolonTokenIdx ;
388- }
389-
390- private _isDefZone ( codeZode : number ) {
391- const ZT = CodeZoneManager . ZONE_TYPE ;
392- switch ( codeZode ) {
393- case ZT . DATA_STEP_DEF :
394- case ZT . DATA_STEP_DEF_OPT :
395- case ZT . DATA_STEP_OPT_NAME :
396- case ZT . DATA_STEP_OPT_VALUE :
397- case ZT . DATA_SET_NAME :
398- case ZT . VIEW_OR_DATA_SET_NAME :
399- case ZT . DATA_SET_OPT_NAME :
400- case ZT . DATA_SET_OPT_VALUE :
401- case ZT . VIEW_OR_PGM_NAME :
402- case ZT . VIEW_OR_PGM_OPT_NAME :
403- case ZT . VIEW_OR_PGM_OPT_VALUE :
404- case ZT . VIEW_OR_PGM_SUB_OPT_NAME :
405- case ZT . PROC_DEF :
406- case ZT . PROC_OPT :
407- case ZT . PROC_OPT_VALUE :
408- case ZT . PROC_SUB_OPT_NAME :
409- case ZT . MACRO_DEF :
410- case ZT . MACRO_DEF_OPT :
411- case ZT . MACRO_FUNC :
412- case ZT . MACRO_VAR :
413- return true ;
414- default :
415- return false ;
416- }
417- }
418-
419399 private _getLastNotEmptyLine ( line : number ) : number | undefined {
420400 let lastNotEmptyLine = undefined ;
421401 let lastNotEmptyLineText = "" ;
@@ -498,23 +478,25 @@ export class FormatOnTypeProvider {
498478 line : number ,
499479 col : number | undefined ,
500480 needMultiLine = true ,
501- ) : [ string | undefined , number | undefined , string ] | [ ] {
502- const tokens : SyntaxToken [ ] = this . syntaxProvider . getSyntax ( line ) ;
481+ returnComment = false ,
482+ ) : [ string | undefined , string , number | undefined ] | [ ] {
503483 let _line = line ;
504484 while ( _line >= 0 ) {
505- const lineText = this . model . getLine ( line ) ;
485+ const tokens : SyntaxToken [ ] = this . syntaxProvider . getSyntax ( _line ) ;
486+ const lineText = this . model . getLine ( _line ) ;
506487 for ( let i = tokens . length - 1 ; i >= 0 ; i -- ) {
507488 const curToken = tokens [ i ] ;
508489 const text = this . _getTokenText ( tokens , i , lineText ) ;
509490 if ( curToken . start <= ( _line < line || ! col ? lineText . length : col ) ) {
510491 if ( ! this . _isCommentOrBlankToken ( curToken , text ) ) {
511- return [ text , curToken . start , curToken . style ] ;
492+ return [ text , curToken . style , curToken . start ] ;
512493 }
513494 if (
514- curToken . style === Lexer . TOKEN_TYPES . COMMENT ||
515- curToken . style === Lexer . TOKEN_TYPES . MCOMMENT
495+ returnComment &&
496+ ( curToken . style === Lexer . TOKEN_TYPES . COMMENT ||
497+ curToken . style === Lexer . TOKEN_TYPES . MCOMMENT )
516498 ) {
517- return [ undefined , undefined , curToken . style ] ;
499+ return [ undefined , curToken . style , undefined ] ;
518500 }
519501 }
520502 }
0 commit comments