Skip to content

Commit 74649ea

Browse files
authored
fix: revise logic of _getIndentIncrementOfNextLine function (#1084)
1 parent 218f82f commit 74649ea

File tree

2 files changed

+136
-143
lines changed

2 files changed

+136
-143
lines changed

server/src/sas/CodeZoneManager.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -669,15 +669,21 @@ export class CodeZoneManager {
669669
token = this._getPrev(context);
670670
if (token && token.text) {
671671
word = token.text.toUpperCase();
672+
token = this._getPrev(context);
672673
if (_isBlockEnd[word]) {
673-
token = this._getPrev(context);
674-
return token?.text === ";";
675-
} else if (word === "CANCEL") {
676-
token = this._getPrev(context);
674+
if (token?.text === ";") {
675+
return true;
676+
}
677+
}
678+
if (word === "CANCEL") {
677679
if (token && token.text && _isBlockEnd[token.text.toUpperCase()]) {
678680
return true;
679681
}
680682
}
683+
if (token?.text.toUpperCase() === "%MEND") {
684+
token = this._getPrev(context);
685+
return token?.text === ";";
686+
}
681687
}
682688
}
683689
return false;
@@ -2262,7 +2268,12 @@ export class CodeZoneManager {
22622268
if (Lexer.isWord[token.type]) {
22632269
text = token.text;
22642270
if (token.pos >= 0) {
2265-
if (this._inBlock(context.block, token)! >= 0) {
2271+
const inBlock = this._inBlock(context.block, token)! >= 0;
2272+
if (
2273+
(inBlock && text !== "%MACRO") ||
2274+
(!inBlock && //not in block
2275+
!this._endedReally(context.block))
2276+
) {
22662277
// in block
22672278
embeddedBlock = this._embeddedBlock(context.block, {
22682279
line: token.line,
@@ -2459,7 +2470,7 @@ export class CodeZoneManager {
24592470
const block = this._syntaxProvider.getFoldingBlock(
24602471
tmpLine,
24612472
tmpCol,
2462-
false,
2473+
true,
24632474
true,
24642475
true,
24652476
);

server/src/sas/FormatOnTypeProvider.ts

Lines changed: 119 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)