refactor: Document formatting

This commit is contained in:
全卓 2022-12-19 11:14:46 +08:00
parent 3596b6e592
commit d2f9863373
5 changed files with 235 additions and 9 deletions

View File

@ -67,11 +67,11 @@ LineComment
// 1. NL between '(' and ')'
// 2. NL between command invocations
IgnoreNLBetweenArgs
: '\r'? '\n' { this.nesting > 0 }? -> skip
: '\r'? '\n' { this.nesting > 0 }? -> channel(HIDDEN)
;
IgnoreExtraNLBetweenCmds
: '\r'? '\n' { this.newLineCount > 0 }? -> skip
: '\r'? '\n' { this.newLineCount > 0 }? -> channel(HIDDEN)
;
NL : {this.newLineCount++;} '\r'? '\n'

View File

@ -47,7 +47,7 @@ export class FormatListener extends CMakeListener {
} else {
const tokenType: number = this._tokenStream.get(tokenIndex).type;
if (tokenType === CMakeLexer.NL) {
result += ' '.repeat(this.getIndent()) + t.text
result += ' '.repeat(this.getIndent()) + t.text;
} else {
result += ' '.repeat(this.getIndent() + 4) + t.text;
}

225
server/src/format_.ts Normal file
View File

@ -0,0 +1,225 @@
import CMakeLexer from "./parser/CMakeLexer";
import CMakeListener from "./parser/CMakeListener";
import { incToBaseDir } from "./symbolTable/goToDefination";
export class Formatter extends CMakeListener {
private _indent: number;
private _indentLevel: number;
private _tokenStream: any;
private _formatted: string;
constructor(_indent: number, tokenStream: any) {
super();
this._indent = _indent;
this._indentLevel = 0;
this._tokenStream = tokenStream;
this._formatted = "";
}
getFormatedText(): string {
return this._formatted;
}
enterFile(ctx: any): void {
for (let token of this._tokenStream.tokens) {
if (token.channel !== CMakeLexer.HIDDEN) {
break;
}
this._formatted += token.text;
}
}
enterAddSubDirCmd(ctx: any): void {
this.enterCommand("add_subdirectory", ctx);
}
enterContinueCmd(ctx: any): void {
this.enterCommand("continue", ctx);
}
enterBreakCmd(ctx: any): void {
this.enterCommand("break", ctx);
}
enterElseCmd(ctx: any): void {
this.enterCommand("else", ctx);
}
enterElseIfCmd(ctx: any): void {
this.enterCommand("elseif", ctx);
}
enterEndForeachCmd(ctx: any): void {
this.enterCommand("endforeach", ctx);
}
enterEndFunctionCmd(ctx: any): void {
this.enterCommand("endfunction", ctx);
}
enterEndIfCmd(ctx: any): void {
this.enterCommand("endif", ctx);
}
enterEndMacroCmd(ctx: any): void {
this.enterCommand("endmacro", ctx);
}
enterEndWhileCmd(ctx: any): void {
this.enterCommand("endwhile", ctx);
}
enterFunctionCmd(ctx: any): void {
this.enterCommand("function", ctx);
}
enterForeachCmd(ctx: any): void {
this.enterCommand("foreach", ctx);
}
enterIfCmd(ctx: any): void {
this.enterCommand("if", ctx);
}
enterIncludeCmd(ctx: any): void {
this.enterCommand("include", ctx);
}
enterMacroCmd(ctx: any): void {
this.enterCommand("macro", ctx);
}
enterOptionCmd(ctx: any): void {
this.enterCommand("option", ctx);
}
enterSetCmd(ctx: any): void {
this.enterCommand("set", ctx);
}
enterWhileCmd(ctx: any): void {
this.enterCommand("while", ctx);
}
enterOtherCmd(ctx: any): void {
const token = ctx.ID().symbol;
this.enterCommand(ctx.ID().symbol.text, ctx);
}
private getIndent(): number {
return this._indent * this._indentLevel;
}
private isBlockCmd(cmd: string): boolean {
return ['if', 'elseif', 'else', 'while', 'foreach', 'function', 'macro'].includes(cmd.toLowerCase());
}
private isEndBlockCmd(cmd: string): boolean {
return ['elseif', 'else', 'endif', 'endwhile', 'endforeach', 'endfunction', 'endmacro'].includes(cmd.toLowerCase());
}
private getHiddenTextOnRight(tokenIndex: number, indent: number): string {
const token = this._tokenStream.get(tokenIndex);
let result = "";
const hiddenTokens = this._tokenStream.getHiddenTokensToRight(tokenIndex, CMakeLexer.HIDDEN);
if (hiddenTokens === null) {
return result;
}
if ((hiddenTokens.length > 0) &&
(hiddenTokens[0].type === CMakeLexer.LineComment || hiddenTokens[0].type === CMakeLexer.BracketComment) &&
hiddenTokens[0].line === token.line) {
result += ' ';
}
// for (const t of hiddenTokens) {
// result += t.text;
// }
let prevLineNo: number = token.line;
hiddenTokens.forEach((t, index) => {
const curLineNo: number = t.line;
if ((curLineNo !== prevLineNo) &&
(t.type === CMakeLexer.LineComment || t.type === CMakeLexer.BracketComment)) {
result += ' '.repeat(indent);
}
result += t.text;
prevLineNo = t.line;
});
return result;
}
private getArgumentText(argCtx: any, indent: number): string {
let result = "";
const cnt: number = argCtx.getChildCount();
if (cnt === 1) {
result += argCtx.stop.text;
result += this.getHiddenTextOnRight(argCtx.stop.tokenIndex, indent);
} else {
result += '(';
const lParenIndex: number = argCtx.LParen().symbol.tokenIndex;
result += this.getHiddenTextOnRight(lParenIndex, indent);
const innerCnt = argCtx.argument().length;
argCtx.argument().forEach((innerCtx, index) => {
result += this.getArgumentText(innerCtx, indent);
if (index < innerCnt - 1) {
result += ' ';
}
});
result += ')';
const rParenIndex: number = argCtx.RParen().symbol.tokenIndex;
result += this.getHiddenTextOnRight(rParenIndex, indent);
}
return result;
}
private enterCommand(cmd: string, ctx: any) {
if (this.isEndBlockCmd(cmd)) {
--this._indentLevel;
if (this._indentLevel < 0) {
this._indentLevel = 0;
}
}
// indent
this._formatted += " ".repeat(this.getIndent());
// the command name and '('
this._formatted += cmd + '(';
// comment and newline can be placed after '('
this._formatted += this.getHiddenTextOnRight(ctx.LParen().symbol.tokenIndex,
this.getIndent() + this._indent
);
// all arguments
if (ctx.hasOwnProperty('argument')) {
const cnt: number = ctx.argument().length;
const indent = (this._indentLevel + 1) * this._indent;
const cmdLineNo: number = ctx.LParen().symbol.line;
let prevLineNo: number = cmdLineNo;
let curLineNo: number = -1;
ctx.argument().forEach((argCtx, index, array) => {
curLineNo = argCtx.start.line;
if (curLineNo !== prevLineNo) {
this._formatted += ' '.repeat(indent);
}
this._formatted += this.getArgumentText(argCtx, indent);
const next = index + 1;
if (next < cnt) {
if (array[next].start.line === curLineNo) {
this._formatted += ' ';
}
}
prevLineNo = curLineNo;
});
}
// ')'
this._formatted += ')';
// get comment on right of command
const rParenIndex: number = ctx.RParen().symbol.tokenIndex;
this._formatted += this.getHiddenTextOnRight(rParenIndex, this.getIndent());
// command terminator
this._formatted += '\n';
const newLineIndex = rParenIndex + 1;
// consider increase or decrease indent level
if (this.isBlockCmd(cmd)) {
++this._indentLevel;
}
// get all comments and newlines after command terminator
this._formatted += this.getHiddenTextOnRight(newLineIndex, this.getIndent());
}
}

View File

@ -89,13 +89,13 @@ const serializedATN = [4,0,31,337,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,
254,5,35,0,0,251,253,8,2,0,0,252,251,1,0,0,0,253,256,1,0,0,0,254,252,1,0,
0,0,254,255,1,0,0,0,255,257,1,0,0,0,256,254,1,0,0,0,257,258,6,23,0,0,258,
48,1,0,0,0,259,261,5,13,0,0,260,259,1,0,0,0,260,261,1,0,0,0,261,262,1,0,
0,0,262,263,5,10,0,0,263,264,4,24,0,0,264,265,1,0,0,0,265,266,6,24,1,0,266,
0,0,262,263,5,10,0,0,263,264,4,24,0,0,264,265,1,0,0,0,265,266,6,24,0,0,266,
50,1,0,0,0,267,269,5,13,0,0,268,267,1,0,0,0,268,269,1,0,0,0,269,270,1,0,
0,0,270,271,5,10,0,0,271,272,4,25,1,0,272,273,1,0,0,0,273,274,6,25,1,0,274,
52,1,0,0,0,275,277,6,26,2,0,276,278,5,13,0,0,277,276,1,0,0,0,277,278,1,0,
0,0,270,271,5,10,0,0,271,272,4,25,1,0,272,273,1,0,0,0,273,274,6,25,0,0,274,
52,1,0,0,0,275,277,6,26,1,0,276,278,5,13,0,0,277,276,1,0,0,0,277,278,1,0,
0,0,278,279,1,0,0,0,279,280,5,10,0,0,280,54,1,0,0,0,281,283,7,3,0,0,282,
281,1,0,0,0,283,284,1,0,0,0,284,282,1,0,0,0,284,285,1,0,0,0,285,286,1,0,
0,0,286,287,6,27,1,0,287,56,1,0,0,0,288,289,5,40,0,0,289,290,6,28,3,0,290,
0,0,286,287,6,27,2,0,287,56,1,0,0,0,288,289,5,40,0,0,289,290,6,28,3,0,290,
58,1,0,0,0,291,292,5,41,0,0,292,293,6,29,4,0,293,60,1,0,0,0,294,298,3,63,
31,0,295,298,3,65,32,0,296,298,3,67,33,0,297,294,1,0,0,0,297,295,1,0,0,0,
297,296,1,0,0,0,298,62,1,0,0,0,299,300,5,92,0,0,300,301,8,4,0,0,301,64,1,
@ -109,7 +109,7 @@ const serializedATN = [4,0,31,337,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,
61,30,0,329,330,5,92,0,0,330,332,3,53,26,0,331,327,1,0,0,0,331,328,1,0,0,
0,331,329,1,0,0,0,332,72,1,0,0,0,333,336,8,6,0,0,334,336,3,61,30,0,335,333,
1,0,0,0,335,334,1,0,0,0,336,74,1,0,0,0,15,0,221,232,240,254,260,268,277,
284,297,308,321,325,331,335,5,0,1,0,6,0,0,1,26,0,1,28,1,1,29,2];
284,297,308,321,325,331,335,5,0,1,0,1,26,0,6,0,0,1,28,1,1,29,2];
const atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN);

View File

@ -33,6 +33,7 @@ import { DefinationListener, incToBaseDir, parsedFiles, refToDef, topScope } fro
import { getFileContext } from './utils';
import { createLogger } from './logging';
import SemanticDiagnosticsListener, { cmdNameCase } from './semanticDiagnostics';
import { Formatter } from './format_';
type Word = {
text: string,
@ -258,7 +259,7 @@ connection.onDocumentFormatting((params: DocumentFormattingParams) => {
const tokenStream = new antlr4.CommonTokenStream(lexer);
const parser = new CMakeParser(tokenStream);
const tree = parser.file();
const formatListener = new FormatListener(tabSize, tokenStream);
const formatListener = new Formatter(tabSize, tokenStream);
antlr4.tree.ParseTreeWalker.DEFAULT.walk(formatListener, tree);
resolve([
{