refactor: Document formatting
This commit is contained in:
parent
3596b6e592
commit
d2f9863373
|
@ -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'
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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([
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue