diff --git a/VHDLFormatter.js b/VHDLFormatter.js index 4c2307c..6ed10cd 100644 --- a/VHDLFormatter.js +++ b/VHDLFormatter.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.RemoveAsserts = exports.ApplyNoNewLineAfter = exports.beautify3 = exports.beautifySemicolonBlock = exports.beautifyVariableInitialiseBlock = exports.beautifyPackageIsNewBlock = exports.beautifyComponentBlock = exports.beautifyCaseBlock = exports.AlignSign = exports.AlignSigns = exports.beautifyPortGenericBlock = exports.FormattedLineToString = exports.FormattedLine = exports.beautify = exports.BeautifierSettings = exports.signAlignSettings = exports.SetNewLinesAfterSymbols = exports.NewLineSettings = void 0; +exports.RemoveAsserts = exports.ApplyNoNewLineAfter = exports.beautify3 = exports.beautifySemicolonBlock = exports.beautifyVariableInitialiseBlock = exports.beautifyPackageIsNewBlock = exports.beautifyComponentBlock = exports.beautifyCaseBlock = exports.AlignSign = exports.AlignSigns = exports.beautifyPortGenericBlock = exports.FormattedLineToString = exports.CodeBlock = exports.FormattedLine = exports.beautify = exports.BeautifierSettings = exports.signAlignSettings = exports.SetNewLinesAfterSymbols = exports.NewLineSettings = void 0; let isTesting = false; const ILEscape = "@@"; const ILCommentPrefix = ILEscape + "comments"; @@ -335,7 +335,8 @@ function beautify(input, settings) { input = input.replace(/(\() +([+\-]) +(\w)/g, '$1$2$3'); // `( - 2)` -> `(-2)` arr = input.split("\r\n"); let result = []; - beautify3(arr, result, settings, 0, 0); + let block = new CodeBlock(arr); + beautify3(block, result, settings, 0); var alignSettings = settings.SignAlignSettings; if (alignSettings != null && alignSettings.isAll) { AlignSigns(result, 0, result.length - 1, alignSettings.mode, alignSettings.alignComments); @@ -386,6 +387,36 @@ class FormattedLine { } } exports.FormattedLine = FormattedLine; +class CodeBlock { + constructor(lines, start = 0, end = lines.length - 1) { + this.lines = lines; + this.start = start; + this.end = end; + this.parent = null; + this.cursor = start; + } + _notifySplit(atLine) { + if (this.start > atLine) + this.start++; + if (this.end >= atLine) + this.end++; + if (this.cursor >= atLine) + this.cursor++; + if (this.parent) + this.parent._notifySplit(atLine); + } + splitLine(atLine, firstText, secondText) { + this.lines[atLine] = firstText; + this.lines.splice(atLine + 1, 0, secondText); + this._notifySplit(atLine); + } + subBlock(start, end) { + let newBlock = new CodeBlock(this.lines, start, end); + newBlock.parent = this; + return newBlock; + } +} +exports.CodeBlock = CodeBlock; function FormattedLineToString(arr, indentation) { let result = []; if (arr == null) { @@ -410,82 +441,75 @@ function FormattedLineToString(arr, indentation) { return result; } exports.FormattedLineToString = FormattedLineToString; -function GetCloseparentheseEndIndex(inputs, startIndex) { +function GetCloseparentheseEndIndex(block) { let openParentheseCount = 0; let closeParentheseCount = 0; - for (let i = startIndex; i < inputs.length; i++) { - let input = inputs[i]; + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + let input = block.lines[block.cursor]; openParentheseCount += input.count("("); closeParentheseCount += input.count(")"); if (openParentheseCount > 0 && openParentheseCount <= closeParentheseCount) { - return i; + return; } } - return startIndex; + block.cursor = startIndex; } -function beautifyPortGenericBlock(inputs, result, settings, startIndex, parentEndIndex, indent, mode) { - let firstLine = inputs[startIndex]; +function beautifyPortGenericBlock(block, result, settings, indent, mode) { + let startIndex = block.cursor; + let firstLine = block.lines[startIndex]; let regex = new RegExp("[\\w\\s:]*(" + mode + ")([\\s]|$)"); if (!firstLine.regexStartsWith(regex)) { - return [startIndex, parentEndIndex]; + return; } let firstLineHasParenthese = firstLine.indexOf("(") >= 0; - let hasParenthese = firstLineHasParenthese; - let blockBodyStartIndex = startIndex; - let secondLineHasParenthese = startIndex + 1 < inputs.length && inputs[startIndex + 1].startsWith("("); - if (secondLineHasParenthese) { - hasParenthese = true; - blockBodyStartIndex++; - } - let endIndex = hasParenthese ? GetCloseparentheseEndIndex(inputs, startIndex) : startIndex; + let secondLineHasParenthese = startIndex + 1 <= block.end && block.lines[startIndex + 1].startsWith("("); + let hasParenthese = firstLineHasParenthese || secondLineHasParenthese; + let blockBodyStartIndex = startIndex + (secondLineHasParenthese ? 1 : 0); + if (hasParenthese) { + GetCloseparentheseEndIndex(block); + } + let endIndex = block.cursor; + let bodyBlock = block.subBlock(blockBodyStartIndex, endIndex); if (endIndex != startIndex && firstLineHasParenthese) { - inputs[startIndex] = inputs[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); - let newInputs = inputs[startIndex].split("\r\n"); + block.lines[startIndex] = block.lines[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); + let newInputs = block.lines[startIndex].split("\r\n"); if (newInputs.length == 2) { - inputs[startIndex] = newInputs[0]; - inputs.splice(startIndex + 1, 0, newInputs[1]); - endIndex++; - parentEndIndex++; + bodyBlock.splitLine(startIndex, newInputs[0], newInputs[1]); } } else if (endIndex > startIndex + 1 && secondLineHasParenthese) { - inputs[startIndex + 1] = inputs[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1'); - let newInputs = inputs[startIndex + 1].split("\r\n"); + block.lines[startIndex + 1] = block.lines[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1'); + let newInputs = block.lines[startIndex + 1].split("\r\n"); if (newInputs.length == 2) { - inputs[startIndex + 1] = newInputs[0]; - inputs.splice(startIndex + 2, 0, newInputs[1]); - endIndex++; - parentEndIndex++; + bodyBlock.splitLine(startIndex + 1, newInputs[0], newInputs[1]); } } - if (firstLineHasParenthese && inputs[startIndex].indexOf("MAP") > 0) { - inputs[startIndex] = inputs[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2('); + if (firstLineHasParenthese && block.lines[startIndex].indexOf("MAP") > 0) { + block.lines[startIndex] = block.lines[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2('); } - result.push(new FormattedLine(inputs[startIndex], indent)); + result.push(new FormattedLine(block.lines[startIndex], indent)); if (secondLineHasParenthese) { let secondLineIndent = indent; if (endIndex == startIndex + 1) { secondLineIndent++; } - result.push(new FormattedLine(inputs[startIndex + 1], secondLineIndent)); + result.push(new FormattedLine(block.lines[startIndex + 1], secondLineIndent)); } - let blockBodyEndIndex = endIndex; - let i = beautify3(inputs, result, settings, blockBodyStartIndex + 1, indent + 1, endIndex); - if (inputs[i].startsWith(")")) { - result[i].Indent--; - blockBodyEndIndex--; + beautify3(bodyBlock.subBlock(bodyBlock.start + 1, bodyBlock.end), result, settings, indent + 1); + if (block.lines[block.cursor].startsWith(")")) { + result[block.cursor].Indent--; + bodyBlock.end--; } var alignSettings = settings.SignAlignSettings; if (alignSettings != null) { if (alignSettings.isRegional && !alignSettings.isAll && alignSettings.keyWords != null && alignSettings.keyWords.indexOf(mode) >= 0) { - blockBodyStartIndex++; - AlignSigns(result, blockBodyStartIndex, blockBodyEndIndex, alignSettings.mode, alignSettings.alignComments); + AlignSigns(result, bodyBlock.start + 1, bodyBlock.end, alignSettings.mode, alignSettings.alignComments); } } - return [i, parentEndIndex]; } exports.beautifyPortGenericBlock = beautifyPortGenericBlock; function AlignSigns(result, startIndex, endIndex, mode, alignComments = false) { @@ -580,22 +604,22 @@ function AlignSign(result, startIndex, endIndex, symbol, maxSymbolIndex = -1, sy } } exports.AlignSign = AlignSign; -function beautifyCaseBlock(inputs, result, settings, startIndex, indent) { - if (!inputs[startIndex].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { - return startIndex; +function beautifyCaseBlock(block, result, settings, indent) { + if (!block.lines[block.cursor].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { + return; } - result.push(new FormattedLine(inputs[startIndex], indent)); - let i = beautify3(inputs, result, settings, startIndex + 1, indent + 2); - result[i].Indent = indent; - return i; + result.push(new FormattedLine(block.lines[block.cursor], indent)); + block.cursor++; + beautify3(block, result, settings, indent + 2); + result[block.cursor].Indent = indent; } exports.beautifyCaseBlock = beautifyCaseBlock; -function getSemicolonBlockEndIndex(inputs, settings, startIndex, parentEndIndex) { - let endIndex = 0; +function getSemicolonBlockEndIndex(block, settings) { + let endIndex = block.cursor; let openBracketsCount = 0; let closeBracketsCount = 0; - for (let i = startIndex; i < inputs.length; i++) { - let input = inputs[i]; + for (; block.cursor <= block.end; block.cursor++) { + let input = block.lines[block.cursor]; let indexOfSemicolon = input.indexOf(";"); let splitIndex = indexOfSemicolon < 0 ? input.length : indexOfSemicolon + 1; let stringBeforeSemicolon = input.substring(0, splitIndex); @@ -607,80 +631,62 @@ function getSemicolonBlockEndIndex(inputs, settings, startIndex, parentEndIndex) continue; } if (openBracketsCount == closeBracketsCount) { - endIndex = i; + endIndex = block.cursor; if (stringAfterSemicolon.trim().length > 0 && settings.NewLineSettings.newLineAfter.indexOf(";") >= 0) { - inputs[i] = stringBeforeSemicolon; - inputs.splice(i, 0, stringAfterSemicolon); - parentEndIndex++; + block.splitLine(block.cursor, stringBeforeSemicolon, stringAfterSemicolon); } break; } } - return [endIndex, parentEndIndex]; + block.cursor = endIndex; } -function beautifyComponentBlock(inputs, result, settings, startIndex, parentEndIndex, indent) { - let endIndex = startIndex; - for (let i = startIndex; i < inputs.length; i++) { - if (inputs[i].regexStartsWith(/END(\s|$)/)) { - endIndex = i; +function beautifyComponentBlock(block, result, settings, indent) { + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + if (block.lines[block.cursor].regexStartsWith(/END(\s|$)/)) { break; } } - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - let incremental = actualEndIndex - endIndex; - endIndex += incremental; - parentEndIndex += incremental; + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); } - return [endIndex, parentEndIndex]; } exports.beautifyComponentBlock = beautifyComponentBlock; -function beautifyPackageIsNewBlock(inputs, result, settings, startIndex, parentEndIndex, indent) { - let endIndex = startIndex; - for (let i = startIndex; i < inputs.length; i++) { - if (inputs[i].regexIndexOf(/;(\s|$)/) >= 0) { - endIndex = i; +function beautifyPackageIsNewBlock(block, result, settings, indent) { + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { break; } } - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - let incremental = actualEndIndex - endIndex; - endIndex += incremental; - parentEndIndex += incremental; + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); } - return [endIndex, parentEndIndex]; } exports.beautifyPackageIsNewBlock = beautifyPackageIsNewBlock; -function beautifyVariableInitialiseBlock(inputs, result, settings, startIndex, parentEndIndex, indent) { - let endIndex = startIndex; - for (let i = startIndex; i < inputs.length; i++) { - if (inputs[i].regexIndexOf(/;(\s|$)/) >= 0) { - endIndex = i; +function beautifyVariableInitialiseBlock(block, result, settings, indent) { + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { break; } } - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - let incremental = actualEndIndex - endIndex; - endIndex += incremental; - parentEndIndex += incremental; + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); } - return [endIndex, parentEndIndex]; } exports.beautifyVariableInitialiseBlock = beautifyVariableInitialiseBlock; -function beautifySemicolonBlock(inputs, result, settings, startIndex, parentEndIndex, indent) { - let endIndex = startIndex; - [endIndex, parentEndIndex] = getSemicolonBlockEndIndex(inputs, settings, startIndex, parentEndIndex); - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - alignSignalAssignmentBlock(settings, inputs, startIndex, endIndex, result); +function beautifySemicolonBlock(block, result, settings, indent) { + let startIndex = block.cursor; + getSemicolonBlockEndIndex(block, settings); + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); + alignSignalAssignmentBlock(settings, block.lines, startIndex, block.cursor, result); } - return [endIndex, parentEndIndex]; } exports.beautifySemicolonBlock = beautifySemicolonBlock; function alignSignalAssignmentBlock(settings, inputs, startIndex, endIndex, result) { @@ -698,8 +704,7 @@ function alignSignalAssignmentBlock(settings, inputs, startIndex, endIndex, resu } } } -function beautify3(inputs, result, settings, startIndex, indent, endIndex) { - let i; +function beautify3(block, result, settings, indent) { let regexOneLineBlockKeyWords = new RegExp(/(PROCEDURE)[^\w](?!.+[^\w]IS([^\w]|$))/); //match PROCEDURE..; but not PROCEDURE .. IS; let regexFunctionMultiLineBlockKeyWords = new RegExp(/(FUNCTION|IMPURE FUNCTION)[^\w](?=.+[^\w]IS([^\w]|$))/); //match FUNCTION .. IS; but not FUNCTION let blockMidKeyWords = ["BEGIN"]; @@ -741,29 +746,26 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { let regexblockEndsWithSemicolon = blockEndsWithSemicolon.convertToRegexBlockWords(); let regexMidKeyWhen = "WHEN".convertToRegexBlockWords(); let regexMidKeyElse = "ELSE|ELSIF".convertToRegexBlockWords(); - if (endIndex == null) { - endIndex = inputs.length - 1; - } - for (i = startIndex; i <= endIndex; i++) { + for (; block.cursor <= block.end; block.cursor++) { if (indent < 0) { indent = 0; } - let input = inputs[i].trim(); + let input = block.lines[block.cursor].trim(); if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) { result.push(new FormattedLine(input, indent)); - return i; + return; } if (input.regexStartsWith(/COMPONENT\s/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifyComponentBlock(inputs, result, settings, i, endIndex, indent); + beautifyComponentBlock(block, result, settings, indent); Mode = modeCache; continue; } if (input.regexStartsWith(/PACKAGE[\s\w]+IS\s+NEW/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifyPackageIsNewBlock(inputs, result, settings, i, endIndex, indent); + beautifyPackageIsNewBlock(block, result, settings, indent); Mode = modeCache; continue; } @@ -771,10 +773,10 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; let endsWithBracket = input.regexIndexOf(/:\s*=\s*\(/) > 0; - let startIndex = i; - [i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent); - if (endsWithBracket && startIndex != i) { - let fl = result[endIndex]; + let startIndex = block.cursor; + beautifySemicolonBlock(block, result, settings, indent); + if (endsWithBracket && startIndex != block.cursor) { + let fl = result[block.end]; if (fl.Line.regexStartsWith(/\);$/)) { fl.Indent--; } @@ -785,96 +787,99 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { if (input.regexStartsWith(/\w+\s*:\s*ENTITY/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent); + beautifySemicolonBlock(block, result, settings, indent); Mode = modeCache; continue; } if (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexblockEndsWithSemicolon)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent); + beautifySemicolonBlock(block, result, settings, indent); Mode = modeCache; continue; } if (input.regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { let modeCache = Mode; Mode = FormatMode.CaseWhen; - i = beautifyCaseBlock(inputs, result, settings, i, indent); + beautifyCaseBlock(block, result, settings, indent); Mode = modeCache; continue; } if (input.regexStartsWith(/[\w\s:]*(:=)([\s]|$)/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, ":="); + beautifyPortGenericBlock(block, result, settings, indent, ":="); continue; } if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PORT"); + beautifyPortGenericBlock(block, result, settings, indent, "PORT"); continue; } if (input.regexStartsWith(/TYPE\s+\w+\s+IS\s+\(/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IS"); + beautifyPortGenericBlock(block, result, settings, indent, "IS"); continue; } if (input.regexStartsWith(/[\w\s:]*GENERIC([\s]|$)/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "GENERIC"); + beautifyPortGenericBlock(block, result, settings, indent, "GENERIC"); continue; } if (input.regexStartsWith(/[\w\s:]*PROCEDURE[\s\w]+\($/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PROCEDURE"); - if (inputs[i].regexStartsWith(/.*\)[\s]*IS/)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + beautifyPortGenericBlock(block, result, settings, indent, "PROCEDURE"); + if (block.lines[block.cursor].regexStartsWith(/.*\)[\s]*IS/)) { + block.cursor++; + beautify3(block, result, settings, indent + 1); } continue; } if (input.regexStartsWith(/FUNCTION[^\w]/) && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "FUNCTION"); - if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + beautifyPortGenericBlock(block, result, settings, indent, "FUNCTION"); + if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { + block.cursor++; + beautify3(block, result, settings, indent + 1); } else { - result[i].Indent++; + result[block.cursor].Indent++; } continue; } if (input.regexStartsWith(/IMPURE FUNCTION[^\w]/) && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IMPURE FUNCTION"); - if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) { - if (inputs[i].regexStartsWith(regexBlockIndentedEndsKeyWords)) { - result[i].Indent++; + beautifyPortGenericBlock(block, result, settings, indent, "IMPURE FUNCTION"); + if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { + if (block.lines[block.cursor].regexStartsWith(regexBlockIndentedEndsKeyWords)) { + result[block.cursor].Indent++; } else { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + block.cursor++; + beautify3(block, result, settings, indent + 1); } } else { - result[i].Indent++; + result[block.cursor].Indent++; } continue; } result.push(new FormattedLine(input, indent)); - if (startIndex != 0 + if (indent > 0 && (input.regexStartsWith(regexBlockMidKeyWords) || (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexMidKeyElse)) || (Mode == FormatMode.CaseWhen && input.regexStartsWith(regexMidKeyWhen)))) { - result[i].Indent--; + result[block.cursor].Indent--; } - else if (startIndex != 0 + else if (indent > 0 && (input.regexStartsWith(regexBlockEndsKeyWords))) { - result[i].Indent--; - return i; + result[block.cursor].Indent--; + return; } if (input.regexStartsWith(regexOneLineBlockKeyWords)) { continue; } if (input.regexStartsWith(regexFunctionMultiLineBlockKeyWords) || input.regexStartsWith(regexBlockStartsKeywords)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + block.cursor++; + beautify3(block, result, settings, indent + 1); } } - i--; - return i; + block.cursor--; } exports.beautify3 = beautify3; function ReserveSemicolonInKeywords(arr) { diff --git a/VHDLFormatter.ts b/VHDLFormatter.ts index 816815d..51906da 100644 --- a/VHDLFormatter.ts +++ b/VHDLFormatter.ts @@ -390,7 +390,8 @@ export function beautify(input: string, settings: BeautifierSettings) { input = input.replace(/(\() +([+\-]) +(\w)/g, '$1$2$3');// `( - 2)` -> `(-2)` arr = input.split("\r\n"); let result: (FormattedLine | FormattedLine[])[] = []; - beautify3(arr, result, settings, 0, 0); + let block = new CodeBlock(arr); + beautify3(block, result, settings, 0); var alignSettings = settings.SignAlignSettings; if (alignSettings != null && alignSettings.isAll) { AlignSigns(result, 0, result.length - 1, alignSettings.mode, alignSettings.alignComments); @@ -448,6 +449,45 @@ export class FormattedLine { } } +export class CodeBlock { + lines: Array; + start: number; // index of first line of range + end: number; // index of last line of range + cursor: number; // line currently being processed + parent: CodeBlock; + + constructor(lines: Array, start = 0, end = lines.length - 1) { + this.lines = lines; + this.start = start; + this.end = end; + this.parent = null; + this.cursor = start; + } + + _notifySplit(atLine: number) { + if (this.start > atLine) + this.start++; + if (this.end >= atLine) + this.end++; + if (this.cursor >= atLine) + this.cursor++; + if (this.parent) + this.parent._notifySplit(atLine); + } + + splitLine(atLine: number, firstText: string, secondText: string) { + this.lines[atLine] = firstText; + this.lines.splice(atLine + 1, 0, secondText); + this._notifySplit(atLine); + } + + subBlock(start: number, end: number): CodeBlock { + let newBlock = new CodeBlock(this.lines, start, end); + newBlock.parent = this; + return newBlock; + } +} + export function FormattedLineToString(arr: (FormattedLine | FormattedLine[])[], indentation: string): Array { let result: Array = []; if (arr == null) { @@ -472,83 +512,81 @@ export function FormattedLineToString(arr: (FormattedLine | FormattedLine[])[], return result; } -function GetCloseparentheseEndIndex(inputs: Array, startIndex: number): number { +function GetCloseparentheseEndIndex(block: CodeBlock) { let openParentheseCount: number = 0; let closeParentheseCount: number = 0; - for (let i = startIndex; i < inputs.length; i++) { - let input = inputs[i]; + let startIndex = block.cursor; + for (;block.cursor <= block.end; block.cursor++) { + let input = block.lines[block.cursor]; openParentheseCount += input.count("("); closeParentheseCount += input.count(")"); if (openParentheseCount > 0 && openParentheseCount <= closeParentheseCount) { - return i; + return; } } - return startIndex; + block.cursor = startIndex; } -export function beautifyPortGenericBlock(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, parentEndIndex: number, indent: number, mode: string): [number, number] { - let firstLine: string = inputs[startIndex]; +export function beautifyPortGenericBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number, mode: string) { + let startIndex = block.cursor; + let firstLine: string = block.lines[startIndex]; let regex: RegExp = new RegExp("[\\w\\s:]*(" + mode + ")([\\s]|$)"); if (!firstLine.regexStartsWith(regex)) { - return [startIndex, parentEndIndex]; + return; } + let firstLineHasParenthese: boolean = firstLine.indexOf("(") >= 0; - let hasParenthese: boolean = firstLineHasParenthese; - let blockBodyStartIndex = startIndex; - let secondLineHasParenthese: boolean = startIndex + 1 < inputs.length && inputs[startIndex + 1].startsWith("("); - if (secondLineHasParenthese) { - hasParenthese = true; - blockBodyStartIndex++; + let secondLineHasParenthese: boolean = startIndex + 1 <= block.end && block.lines[startIndex + 1].startsWith("("); + let hasParenthese: boolean = firstLineHasParenthese || secondLineHasParenthese; + let blockBodyStartIndex = startIndex + (secondLineHasParenthese ? 1 : 0); + if (hasParenthese) { + GetCloseparentheseEndIndex(block); } - let endIndex: number = hasParenthese ? GetCloseparentheseEndIndex(inputs, startIndex) : startIndex; + let endIndex: number = block.cursor; + let bodyBlock = block.subBlock(blockBodyStartIndex, endIndex); + if (endIndex != startIndex && firstLineHasParenthese) { - inputs[startIndex] = inputs[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); - let newInputs = inputs[startIndex].split("\r\n"); + block.lines[startIndex] = block.lines[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); + let newInputs = block.lines[startIndex].split("\r\n"); if (newInputs.length == 2) { - inputs[startIndex] = newInputs[0]; - inputs.splice(startIndex + 1, 0, newInputs[1]); - endIndex++; - parentEndIndex++; + bodyBlock.splitLine(startIndex, newInputs[0], newInputs[1]); } } else if (endIndex > startIndex + 1 && secondLineHasParenthese) { - inputs[startIndex + 1] = inputs[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1'); - let newInputs = inputs[startIndex + 1].split("\r\n"); + block.lines[startIndex + 1] = block.lines[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1'); + let newInputs = block.lines[startIndex + 1].split("\r\n"); if (newInputs.length == 2) { - inputs[startIndex + 1] = newInputs[0]; - inputs.splice(startIndex + 2, 0, newInputs[1]); - endIndex++; - parentEndIndex++; + bodyBlock.splitLine(startIndex + 1, newInputs[0], newInputs[1]); } } - if (firstLineHasParenthese && inputs[startIndex].indexOf("MAP") > 0) { - inputs[startIndex] = inputs[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2('); + + if (firstLineHasParenthese && block.lines[startIndex].indexOf("MAP") > 0) { + block.lines[startIndex] = block.lines[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2('); } - result.push(new FormattedLine(inputs[startIndex], indent)); + + result.push(new FormattedLine(block.lines[startIndex], indent)); if (secondLineHasParenthese) { let secondLineIndent = indent; if (endIndex == startIndex + 1) { secondLineIndent++; } - result.push(new FormattedLine(inputs[startIndex + 1], secondLineIndent)); + result.push(new FormattedLine(block.lines[startIndex + 1], secondLineIndent)); } - let blockBodyEndIndex = endIndex; - let i = beautify3(inputs, result, settings, blockBodyStartIndex + 1, indent + 1, endIndex); - if (inputs[i].startsWith(")")) { - (result[i]).Indent--; - blockBodyEndIndex--; + + beautify3(bodyBlock.subBlock(bodyBlock.start + 1, bodyBlock.end), result, settings, indent + 1); + if (block.lines[block.cursor].startsWith(")")) { + (result[block.cursor]).Indent--; + bodyBlock.end--; } var alignSettings = settings.SignAlignSettings; if (alignSettings != null) { if (alignSettings.isRegional && !alignSettings.isAll && alignSettings.keyWords != null && alignSettings.keyWords.indexOf(mode) >= 0) { - blockBodyStartIndex++; - AlignSigns(result, blockBodyStartIndex, blockBodyEndIndex, alignSettings.mode, alignSettings.alignComments); + AlignSigns(result, bodyBlock.start + 1, bodyBlock.end, alignSettings.mode, alignSettings.alignComments); } } - return [i, parentEndIndex]; } export function AlignSigns(result: (FormattedLine | FormattedLine[])[], startIndex: number, endIndex: number, mode: string, alignComments: boolean = false) { @@ -646,23 +684,22 @@ export function AlignSign(result: (FormattedLine | FormattedLine[])[], startInde } } -export function beautifyCaseBlock(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, indent: number): number { - if (!inputs[startIndex].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { - return startIndex; +export function beautifyCaseBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { + if (!block.lines[block.cursor].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { + return; } - result.push(new FormattedLine(inputs[startIndex], indent)); - - let i = beautify3(inputs, result, settings, startIndex + 1, indent + 2); - (result[i]).Indent = indent; - return i; + result.push(new FormattedLine(block.lines[block.cursor], indent)); + block.cursor++; + beautify3(block, result, settings, indent + 2); + (result[block.cursor]).Indent = indent; } -function getSemicolonBlockEndIndex(inputs: Array, settings: BeautifierSettings, startIndex: number, parentEndIndex: number): [number, number] { - let endIndex = 0; +function getSemicolonBlockEndIndex(block: CodeBlock, settings: BeautifierSettings) { + let endIndex = block.cursor; let openBracketsCount = 0; let closeBracketsCount = 0; - for (let i = startIndex; i < inputs.length; i++) { - let input = inputs[i]; + for (; block.cursor <= block.end; block.cursor++) { + let input = block.lines[block.cursor]; let indexOfSemicolon = input.indexOf(";"); let splitIndex = indexOfSemicolon < 0 ? input.length : indexOfSemicolon + 1; let stringBeforeSemicolon = input.substring(0, splitIndex); @@ -674,85 +711,63 @@ function getSemicolonBlockEndIndex(inputs: Array, settings: BeautifierSe continue; } if (openBracketsCount == closeBracketsCount) { - endIndex = i; + endIndex = block.cursor; if (stringAfterSemicolon.trim().length > 0 && settings.NewLineSettings.newLineAfter.indexOf(";") >= 0) { - inputs[i] = stringBeforeSemicolon; - inputs.splice(i, 0, stringAfterSemicolon); - parentEndIndex++; + block.splitLine(block.cursor, stringBeforeSemicolon, stringAfterSemicolon); } break; } } - return [endIndex, parentEndIndex]; + block.cursor = endIndex; } -export function beautifyComponentBlock(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, parentEndIndex: number, indent: number): [number, number] { - let endIndex = startIndex; - for (let i = startIndex; i < inputs.length; i++) { - if (inputs[i].regexStartsWith(/END(\s|$)/)) { - endIndex = i; +export function beautifyComponentBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + if (block.lines[block.cursor].regexStartsWith(/END(\s|$)/)) { break; } } - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - let incremental = actualEndIndex - endIndex; - endIndex += incremental; - parentEndIndex += incremental; + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); } - - return [endIndex, parentEndIndex]; } -export function beautifyPackageIsNewBlock(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, parentEndIndex: number, indent: number): [number, number] { - let endIndex = startIndex; - for (let i = startIndex; i < inputs.length; i++) { - if (inputs[i].regexIndexOf(/;(\s|$)/) >= 0) { - endIndex = i; +export function beautifyPackageIsNewBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { break; } } - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - let incremental = actualEndIndex - endIndex; - endIndex += incremental; - parentEndIndex += incremental; + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); } - - return [endIndex, parentEndIndex]; } -export function beautifyVariableInitialiseBlock(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, parentEndIndex: number, indent: number): [number, number] { - let endIndex = startIndex; - for (let i = startIndex; i < inputs.length; i++) { - if (inputs[i].regexIndexOf(/;(\s|$)/) >= 0) { - endIndex = i; +export function beautifyVariableInitialiseBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { + let startIndex = block.cursor; + for (; block.cursor <= block.end; block.cursor++) { + if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { break; } } - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - let actualEndIndex = beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - let incremental = actualEndIndex - endIndex; - endIndex += incremental; - parentEndIndex += incremental; + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); } - - return [endIndex, parentEndIndex]; } -export function beautifySemicolonBlock(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, parentEndIndex: number, indent: number): [number, number] { - let endIndex = startIndex; - [endIndex, parentEndIndex] = getSemicolonBlockEndIndex(inputs, settings, startIndex, parentEndIndex); - result.push(new FormattedLine(inputs[startIndex], indent)); - if (endIndex != startIndex) { - beautify3(inputs, result, settings, startIndex + 1, indent + 1, endIndex); - alignSignalAssignmentBlock(settings, inputs, startIndex, endIndex, result); +export function beautifySemicolonBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { + let startIndex = block.cursor; + getSemicolonBlockEndIndex(block, settings); + result.push(new FormattedLine(block.lines[startIndex], indent)); + if (block.cursor != startIndex) { + beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); + alignSignalAssignmentBlock(settings, block.lines, startIndex, block.cursor, result); } - - return [endIndex, parentEndIndex]; } function alignSignalAssignmentBlock(settings: BeautifierSettings, inputs: string[], startIndex: number, endIndex: number, result: (FormattedLine | FormattedLine[])[]) { @@ -771,8 +786,7 @@ function alignSignalAssignmentBlock(settings: BeautifierSettings, inputs: string } } -export function beautify3(inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, indent: number, endIndex?: number): number { - let i: number; +export function beautify3(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { let regexOneLineBlockKeyWords: RegExp = new RegExp(/(PROCEDURE)[^\w](?!.+[^\w]IS([^\w]|$))/);//match PROCEDURE..; but not PROCEDURE .. IS; let regexFunctionMultiLineBlockKeyWords: RegExp = new RegExp(/(FUNCTION|IMPURE FUNCTION)[^\w](?=.+[^\w]IS([^\w]|$))/);//match FUNCTION .. IS; but not FUNCTION let blockMidKeyWords: Array = ["BEGIN"]; @@ -814,29 +828,26 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format let regexblockEndsWithSemicolon: RegExp = blockEndsWithSemicolon.convertToRegexBlockWords(); let regexMidKeyWhen: RegExp = "WHEN".convertToRegexBlockWords(); let regexMidKeyElse: RegExp = "ELSE|ELSIF".convertToRegexBlockWords(); - if (endIndex == null) { - endIndex = inputs.length - 1; - } - for (i = startIndex; i <= endIndex; i++) { + for (; block.cursor <= block.end; block.cursor++) { if (indent < 0) { indent = 0; } - let input: string = inputs[i].trim(); + let input: string = block.lines[block.cursor].trim(); if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) { result.push(new FormattedLine(input, indent)); - return i; + return; } if (input.regexStartsWith(/COMPONENT\s/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifyComponentBlock(inputs, result, settings, i, endIndex, indent); + beautifyComponentBlock(block, result, settings, indent); Mode = modeCache; continue; } if (input.regexStartsWith(/PACKAGE[\s\w]+IS\s+NEW/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifyPackageIsNewBlock(inputs, result, settings, i, endIndex, indent); + beautifyPackageIsNewBlock(block, result, settings, indent); Mode = modeCache; continue; } @@ -844,10 +855,10 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; let endsWithBracket = input.regexIndexOf(/:\s*=\s*\(/) > 0; - let startIndex = i; - [i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent); - if (endsWithBracket && startIndex != i) { - let fl = result[endIndex] as FormattedLine; + let startIndex = block.cursor; + beautifySemicolonBlock(block, result, settings, indent); + if (endsWithBracket && startIndex != block.cursor) { + let fl = result[block.end] as FormattedLine; if (fl.Line.regexStartsWith(/\);$/)) { fl.Indent--; } @@ -858,93 +869,96 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format if (input.regexStartsWith(/\w+\s*:\s*ENTITY/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent); + beautifySemicolonBlock(block, result, settings, indent); Mode = modeCache; continue; } if (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexblockEndsWithSemicolon)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; - [i, endIndex] = beautifySemicolonBlock(inputs, result, settings, i, endIndex, indent); + beautifySemicolonBlock(block, result, settings, indent); Mode = modeCache; continue; } if (input.regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { let modeCache = Mode; Mode = FormatMode.CaseWhen; - i = beautifyCaseBlock(inputs, result, settings, i, indent); + beautifyCaseBlock(block, result, settings, indent); Mode = modeCache; continue; } if (input.regexStartsWith(/[\w\s:]*(:=)([\s]|$)/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, ":="); + beautifyPortGenericBlock(block, result, settings, indent, ":="); continue; } if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PORT"); + beautifyPortGenericBlock(block, result, settings, indent, "PORT"); continue; } if (input.regexStartsWith(/TYPE\s+\w+\s+IS\s+\(/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IS"); + beautifyPortGenericBlock(block, result, settings, indent, "IS"); continue; } if (input.regexStartsWith(/[\w\s:]*GENERIC([\s]|$)/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "GENERIC"); + beautifyPortGenericBlock(block, result, settings, indent, "GENERIC"); continue; } if (input.regexStartsWith(/[\w\s:]*PROCEDURE[\s\w]+\($/)) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PROCEDURE"); - if (inputs[i].regexStartsWith(/.*\)[\s]*IS/)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + beautifyPortGenericBlock(block, result, settings, indent, "PROCEDURE"); + if (block.lines[block.cursor].regexStartsWith(/.*\)[\s]*IS/)) { + block.cursor++; + beautify3(block, result, settings, indent + 1); } continue; } if (input.regexStartsWith(/FUNCTION[^\w]/) && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "FUNCTION"); - if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + beautifyPortGenericBlock(block, result, settings, indent, "FUNCTION"); + if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { + block.cursor++; + beautify3(block, result, settings, indent + 1); } else { - (result[i]).Indent++; + (result[block.cursor]).Indent++; } continue; } if (input.regexStartsWith(/IMPURE FUNCTION[^\w]/) && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { - [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IMPURE FUNCTION"); - if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) { - if (inputs[i].regexStartsWith(regexBlockIndentedEndsKeyWords)) { - (result[i]).Indent++; + beautifyPortGenericBlock(block, result, settings, indent, "IMPURE FUNCTION"); + if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { + if (block.lines[block.cursor].regexStartsWith(regexBlockIndentedEndsKeyWords)) { + (result[block.cursor]).Indent++; } else { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + block.cursor++; + beautify3(block, result, settings, indent + 1); } } else { - (result[i]).Indent++; + (result[block.cursor]).Indent++; } continue; } result.push(new FormattedLine(input, indent)); - if (startIndex != 0 + if (indent > 0 && (input.regexStartsWith(regexBlockMidKeyWords) || (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexMidKeyElse)) || (Mode == FormatMode.CaseWhen && input.regexStartsWith(regexMidKeyWhen)))) { - (result[i]).Indent--; + (result[block.cursor]).Indent--; } - else if (startIndex != 0 + else if (indent > 0 && (input.regexStartsWith(regexBlockEndsKeyWords))) { - (result[i]).Indent--; - return i; + (result[block.cursor]).Indent--; + return; } if (input.regexStartsWith(regexOneLineBlockKeyWords)) { continue; } if (input.regexStartsWith(regexFunctionMultiLineBlockKeyWords) || input.regexStartsWith(regexBlockStartsKeywords)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + block.cursor++; + beautify3(block, result, settings, indent + 1); } } - i--; - return i; + block.cursor--; } function ReserveSemicolonInKeywords(arr: Array) { diff --git a/tests/VHDLFormatterUnitTests.ts b/tests/VHDLFormatterUnitTests.ts index 4c0cf48..dd21b7a 100644 --- a/tests/VHDLFormatterUnitTests.ts +++ b/tests/VHDLFormatterUnitTests.ts @@ -4,7 +4,7 @@ import { BeautifierSettings } from "../VHDLFormatter"; import { RemoveAsserts } from "../VHDLFormatter"; import { ApplyNoNewLineAfter } from "../VHDLFormatter"; import { SetNewLinesAfterSymbols } from "../VHDLFormatter"; -import { beautify3 } from "../VHDLFormatter"; +import { CodeBlock, beautify3 } from "../VHDLFormatter"; import { FormattedLine } from "../VHDLFormatter"; import { FormattedLineToString } from "../VHDLFormatter"; import { CompareString } from "./assert"; @@ -75,6 +75,7 @@ function UnitTestbeautify3() { Beautify3Case19(); Beautify3Case20(); Beautify3Case21(); + Beautify3Case22(); } function Beautify3Case1() { @@ -471,8 +472,8 @@ function Beautify3Case16() { ]; let expected: (FormattedLine | FormattedLine[])[] = [ new FormattedLine("x <= 1 WHEN foo", 0), - new FormattedLine("ELSE 2 WHEN bar", 1), - new FormattedLine("ELSE 3;", 1), + new FormattedLine(" ELSE 2 WHEN bar", 1), + new FormattedLine(" ELSE 3;", 1), new FormattedLine("y <= 2;", 0) ]; UnitTest6(beautify3, "one line ends with ;", settings, inputs, expected, 0, expected.length - 1, 0); @@ -562,7 +563,7 @@ function Beautify3Case20() { ]; let expected: (FormattedLine | FormattedLine[])[] = [ new FormattedLine("m <= ((1, 2, 3, 4)", 0), - new FormattedLine("(5, 6, 7, 8));", 1), + new FormattedLine(" (5, 6, 7, 8));", 1), new FormattedLine("y <= 2;", 0) ]; UnitTest6(beautify3, "function", settings, inputs, expected, 0, expected.length - 1, 0); @@ -590,6 +591,39 @@ function Beautify3Case21() { UnitTest6(beautify3, "function", settings, inputs, expected, 0, expected.length - 1, 0); } +function Beautify3Case22() { + let new_line_after_symbols: NewLineSettings = new NewLineSettings(); + new_line_after_symbols.newLineAfter = ["then", ";"]; + let settings = getDefaultBeautifierSettings(new_line_after_symbols); + let inputs: Array = [ + "test: ENTITY work.testing", + "GENERIC MAP( x => 1,", + "y => 2", + ")", + "PORT MAP( z => OPEN,", + "w => '1'", + ");", + "", + "foo <= '1';", + "bar <= '1';", + ]; + let expected: (FormattedLine | FormattedLine[])[] = [ + new FormattedLine("test: ENTITY work.testing", 0), + new FormattedLine("GENERIC MAP(", 1), + new FormattedLine("x => 1,", 2), + new FormattedLine("y => 2", 2), + new FormattedLine(")", 1), + new FormattedLine("PORT MAP(", 1), + new FormattedLine("z => OPEN,", 2), + new FormattedLine("w => '1'", 2), + new FormattedLine(");", 1), + new FormattedLine("", 0), + new FormattedLine("foo <= '1';", 0), + new FormattedLine("bar <= '1';", 0), + ]; + UnitTest6(beautify3, "inserted line breaks in generic maps (issue #43)", settings, inputs, expected, 0, expected.length - 1, 0); +} + function UnitTestSetNewLinesAfterSymbols() { console.log("=== SetNewLinesAfterSymbols ==="); let input = "a; @@comments1\r\nb;" @@ -712,7 +746,7 @@ type Array2Callback = (arr: Array, parameters: Array) => void; type String2Callback = (text: string, parameters: NewLineSettings) => string; -type BeautifyCallback = (inputs: Array, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, startIndex: number, indent: number) => number; +type BeautifyCallback = (block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) => void; type FormattedLinesCallback = (inputs: (FormattedLine | FormattedLine[])[], indentation: string) => Array; @@ -723,9 +757,11 @@ function UnitTest7(func: FormattedLinesCallback, testName: string, indentation: function UnitTest6(func: BeautifyCallback, testName: string, parameters: BeautifierSettings, inputs: Array, expected: (FormattedLine | FormattedLine[])[], startIndex: number, expectedEndIndex: number, indent: number) { let actual: (FormattedLine | FormattedLine[])[] = [] - let endIndex: number = func(inputs, actual, parameters, startIndex, indent); - if (endIndex != expectedEndIndex) { - console.log(testName + " failed;\nend index, actual: " + endIndex + "; expected: " + expectedEndIndex) + let block = new CodeBlock(inputs); + block.cursor = startIndex; + func(block, actual, parameters, indent); + if (block.cursor != expectedEndIndex) { + console.log(testName + " failed;\nend index, actual: " + block.cursor + "; expected: " + expectedEndIndex) } assertFormattedLines(testName, expected, actual); } @@ -759,7 +795,7 @@ function IntegrationTest() { new_line_after_symbols.noNewLineAfter = ["port", "generic"]; let settings = getDefaultBeautifierSettings(new_line_after_symbols); let input = "architecture TB of TB_CPU is\r\n component CPU_IF\r\n port -- port list\r\n end component;\r\n signal CPU_DATA_VALID: std_ulogic;\r\n signal CLK, RESET: std_ulogic := '0';\r\n constant PERIOD : time := 10 ns;\r\n constant MAX_SIM: time := 50 * PERIOD;\r\n begin\r\n -- concurrent statements\r\n end TB;" - let expected = "ARCHITECTURE TB OF TB_CPU IS\r\n COMPONENT CPU_IF\r\n PORT -- port list\r\n END COMPONENT;\r\n SIGNAL CPU_DATA_VALID : std_ulogic;\r\n SIGNAL CLK, RESET : std_ulogic := '0';\r\n CONSTANT PERIOD : TIME := 10 ns;\r\n CONSTANT MAX_SIM : TIME := 50 * PERIOD;\r\nBEGIN\r\n -- concurrent statements\r\nEND TB;"; + let expected = "ARCHITECTURE TB OF TB_CPU IS\r\n COMPONENT CPU_IF\r\n PORT -- port list\r\n END COMPONENT;\r\n SIGNAL CPU_DATA_VALID : STD_ULOGIC;\r\n SIGNAL CLK, RESET : STD_ULOGIC := '0';\r\n CONSTANT PERIOD : TIME := 10 ns;\r\n CONSTANT MAX_SIM : TIME := 50 * PERIOD;\r\nBEGIN\r\n -- concurrent statements\r\nEND TB;"; let actual = beautify(input, settings); assertAndCountTest("General", expected, actual); @@ -818,17 +854,17 @@ function IntegrationTest() { assertAndCountTest("WHEN CASE & IF", expected, actual); input = "entity aa is\r\n port (a : in std_logic;\r\n b : in std_logic;\r\n );\r\nend aa;\r\narchitecture bb of aa is\r\n component cc\r\n port(\r\n a : in std_logic;\r\n b : in std_logic;\r\n );\r\n end cc;\r\n\r\nbegin\r\n C : cc port map (\r\n long => a,\r\n b => b\r\n );\r\nend;"; - expected = "ENTITY aa IS\r\n PORT (\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n );\r\nEND aa;\r\nARCHITECTURE bb OF aa IS\r\n COMPONENT cc\r\n PORT (\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n );\r\n END cc;\r\n\r\nBEGIN\r\n C : cc PORT MAP(\r\n long => a,\r\n b => b\r\n );\r\nEND;"; + expected = "ENTITY aa IS\r\n PORT (\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n );\r\nEND aa;\r\nARCHITECTURE bb OF aa IS\r\n COMPONENT cc\r\n PORT (\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n );\r\n END cc;\r\n\r\nBEGIN\r\n C : cc PORT MAP(\r\n long => a,\r\n b => b\r\n );\r\nEND;"; actual = beautify(input, settings); assertAndCountTest("PORT MAP", expected, actual); input = "entity aa is\r\n port (a : in std_logic;\r\n b : in std_logic;\r\n );\r\n port (a : in std_logic;\r\n b : in std_logic;\r\n );\r\nend aa;\r\narchitecture bb of aa is\r\n component cc\r\n port(\r\n a : in std_logic;\r\n b : in std_logic;\r\n );\r\n port(\r\n a : in std_logic;\r\n b : in std_logic;\r\n );\r\n end cc;\r\n\r\nbegin\r\n C : cc port map (\r\n long => a,\r\n b => b\r\n );\r\n D : cc port map (\r\n long => a,\r\n b => b\r\n );\r\nend;"; - expected = "ENTITY aa IS\r\n PORT (\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n );\r\n PORT (\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n );\r\nEND aa;\r\nARCHITECTURE bb OF aa IS\r\n COMPONENT cc\r\n PORT (\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n );\r\n PORT (\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n );\r\n END cc;\r\n\r\nBEGIN\r\n C : cc PORT MAP(\r\n long => a,\r\n b => b\r\n );\r\n D : cc PORT MAP(\r\n long => a,\r\n b => b\r\n );\r\nEND;"; + expected = "ENTITY aa IS\r\n PORT (\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n );\r\n PORT (\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n );\r\nEND aa;\r\nARCHITECTURE bb OF aa IS\r\n COMPONENT cc\r\n PORT (\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n );\r\n PORT (\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n );\r\n END cc;\r\n\r\nBEGIN\r\n C : cc PORT MAP(\r\n long => a,\r\n b => b\r\n );\r\n D : cc PORT MAP(\r\n long => a,\r\n b => b\r\n );\r\nEND;"; actual = beautify(input, settings); assertAndCountTest("Multiple PORT MAPs", expected, actual); input = "port (a : in std_logic;\r\n b : in std_logic;\r\n);"; - expected = "PORT\r\n(\r\n a : IN std_logic;\r\n b : IN std_logic;\r\n);"; + expected = "PORT\r\n(\r\n a : IN STD_LOGIC;\r\n b : IN STD_LOGIC;\r\n);"; new_line_after_symbols_2 = new NewLineSettings(); new_line_after_symbols_2.newLineAfter = ["then", ";", "generic", "port"]; newSettings = deepCopy(settings); @@ -839,14 +875,14 @@ function IntegrationTest() { newSettings = deepCopy(settings); newSettings.NewLineSettings.newLineAfter = []; input = "component a is\r\nport( Data : inout Std_Logic_Vector(7 downto 0););\r\nend component a;"; - expected = "COMPONENT a IS\r\n PORT (Data : INOUT Std_Logic_Vector(7 DOWNTO 0););\r\nEND COMPONENT a;"; + expected = "COMPONENT a IS\r\n PORT (Data : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0););\r\nEND COMPONENT a;"; actual = beautify(input, newSettings); assertAndCountTest("New line after PORT (single line)", expected, actual); //IntegrationTest20(); input = "architecture a of b is\r\nbegin\r\n process (w)\r\n variable t : std_logic_vector (4 downto 0) ;\r\nbegin\r\n a := (others => '0') ;\r\nend process ;\r\nend a;"; - expected = "ARCHITECTURE a OF b IS\r\nBEGIN\r\n PROCESS (w)\r\n VARIABLE t : std_logic_vector (4 DOWNTO 0);\r\n BEGIN\r\n a := (OTHERS => '0');\r\n END PROCESS;\r\nEND a;"; + expected = "ARCHITECTURE a OF b IS\r\nBEGIN\r\n PROCESS (w)\r\n VARIABLE t : STD_LOGIC_VECTOR (4 DOWNTO 0);\r\n BEGIN\r\n a := (OTHERS => '0');\r\n END PROCESS;\r\nEND a;"; actual = beautify(input, newSettings); assertAndCountTest("Double BEGIN", expected, actual); @@ -1459,7 +1495,7 @@ function IntegrationTest2() { let settings = getDefaultBeautifierSettings(new_line_after_symbols); settings.RemoveComments = true; let input = "architecture TB of TB_CPU is\r\n component CPU_IF\r\n port -- port list\r\n end component;\r\n signal CPU_DATA_VALID: std_ulogic;\r\n signal CLK, RESET: std_ulogic := '0';\r\n constant PERIOD : time := 10 ns;\r\n constant MAX_SIM: time := 50 * PERIOD;\r\n begin\r\n -- concurrent statements\r\n end TB;" - let expected = "ARCHITECTURE TB OF TB_CPU IS\r\n COMPONENT CPU_IF\r\n PORT\r\n END COMPONENT;\r\n SIGNAL CPU_DATA_VALID : std_ulogic;\r\n SIGNAL CLK, RESET : std_ulogic := '0';\r\n CONSTANT PERIOD : TIME := 10 ns;\r\n CONSTANT MAX_SIM : TIME := 50 * PERIOD;\r\nBEGIN\r\nEND TB;"; + let expected = "ARCHITECTURE TB OF TB_CPU IS\r\n COMPONENT CPU_IF\r\n PORT\r\n END COMPONENT;\r\n SIGNAL CPU_DATA_VALID : STD_ULOGIC;\r\n SIGNAL CLK, RESET : STD_ULOGIC := '0';\r\n CONSTANT PERIOD : TIME := 10 ns;\r\n CONSTANT MAX_SIM : TIME := 50 * PERIOD;\r\nBEGIN\r\nEND TB;"; let actual = beautify(input, settings); assertAndCountTest("Remove comments", expected, actual); } @@ -1490,4 +1526,4 @@ function CompareArray(actual: Array, expected: Array) { } } return true; -} \ No newline at end of file +}