diff --git a/VHDLFormatter.js b/VHDLFormatter.js index d8fa990..cde5b79 100644 --- a/VHDLFormatter.js +++ b/VHDLFormatter.js @@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); let isTesting = false; const ILEscape = "@@"; const ILCommentPrefix = ILEscape + "comments"; +const ILIndentedReturnPrefix = ILEscape; const ILQuote = "⨵"; const ILSingleQuote = "⦼"; const ILBackslash = "⨸"; @@ -139,16 +140,32 @@ function SetNewLinesAfterSymbols(text, newLineSettings) { } if (newLineSettings.newLineAfter != null) { newLineSettings.newLineAfter.forEach(symbol => { - let regex = new RegExp("(" + symbol.toUpperCase() + ")[ ]?([^ \r\n@])", "g"); + let upper = symbol.toUpperCase(); + var rexString = "(" + upper + ")[ ]?([^ \r\n@])"; + let regex = null; + if (upper.regexStartsWith(/\w/)) { + regex = new RegExp("(? { - let regex = new RegExp("(" + symbol.toUpperCase() + ")[ \r\n]+([^@])", "g"); + let rexString = "(" + symbol.toUpperCase() + ")[ \r\n]+([^@])"; + let regex = null; + if (symbol.regexStartsWith(/\w/)) { + regex = new RegExp("(? function(..\r\n)return type; + input = input.replace(/\)\s*(@@\w+)\r\n\s*RETURN\s+([\w]+;)/g, ') $1\r\n' + ILIndentedReturnPrefix + 'RETURN $2'); //function(..)\r\nreturn type; -> function(..\r\n)return type; let keywordAndSignRegex = new RegExp("(\\b" + KeyWords.join("\\b|\\b") + "\\b) +([\\-+]) +(\\w)", "g"); input = input.replace(keywordAndSignRegex, "$1 $2$3"); // `WHEN - 2` -> `WHEN -2` input = input.replace(/([,|]) +([+\-]) +(\w)/g, '$1 $2$3'); // `1, - 2)` -> `1, -2)` @@ -247,6 +265,7 @@ function beautify(input, settings) { } arr = FormattedLineToString(result, settings.Indentation); input = arr.join("\r\n"); + input = input.replace(/@@RETURN/g, "RETURN"); input = SetKeywordCase(input, settings.KeywordCase, KeyWords); input = SetKeywordCase(input, settings.TypeNameCase, TypeNames); input = replaceEscapedWords(input, quotes, ILQuote); @@ -341,7 +360,7 @@ function beautifyPortGenericBlock(inputs, result, settings, startIndex, parentEn } let endIndex = hasParenthese ? GetCloseparentheseEndIndex(inputs, startIndex) : startIndex; if (endIndex != startIndex && firstLineHasParenthese) { - inputs[startIndex] = inputs[startIndex].replace(/(PORT|GENERIC|PROCEDURE)([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); + inputs[startIndex] = inputs[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); let newInputs = inputs[startIndex].split("\r\n"); if (newInputs.length == 2) { inputs[startIndex] = newInputs[0]; @@ -548,6 +567,7 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { "\\w+\\s+\\w+\\s+IS\\s+RECORD" ]; let blockEndsKeyWords = ["END", ".*\\)\\s*RETURN\\s+[\\w]+;"]; + let indentedEndsKeyWords = [ILIndentedReturnPrefix + "RETURN\\s+\\w+;"]; let blockEndsWithSemicolon = [ "(WITH\\s+[\\w\\s\\\\]+SELECT)", "([\\w\\\\]+[\\s]*<=)", @@ -559,6 +579,7 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { let regexBlockMidKeyWords = blockMidKeyWords.convertToRegexBlockWords(); let regexBlockStartsKeywords = new RegExp("([\\w]+\\s*:\\s*)?(" + newLineAfterKeyWordsStr + ")([^\\w]|$)"); let regexBlockEndsKeyWords = blockEndsKeyWords.convertToRegexBlockWords(); + let regexBlockIndentedEndsKeyWords = indentedEndsKeyWords.convertToRegexBlockWords(); let regexblockEndsWithSemicolon = blockEndsWithSemicolon.convertToRegexBlockWords(); let regexMidKeyWhen = "WHEN".convertToRegexBlockWords(); let regexMidKeyElse = "ELSE|ELSIF".convertToRegexBlockWords(); @@ -570,6 +591,10 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { indent = 0; } let input = inputs[i].trim(); + if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) { + result.push(new FormattedLine(input, indent)); + return i; + } if (input.regexStartsWith(/COMPONENT\s/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; @@ -598,7 +623,7 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { Mode = modeCache; continue; } - if (input.regexStartsWith(/[\w\s:]*PORT([\s]|$)/)) { + if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) { [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PORT"); continue; } @@ -632,7 +657,12 @@ function beautify3(inputs, result, settings, startIndex, indent, endIndex) { && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IMPURE FUNCTION"); if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + if (inputs[i].regexStartsWith(regexBlockIndentedEndsKeyWords)) { + result[i].Indent++; + } + else { + i = beautify3(inputs, result, settings, i + 1, indent + 1); + } } else { result[i].Indent++; diff --git a/VHDLFormatter.ts b/VHDLFormatter.ts index 3a20250..761283b 100644 --- a/VHDLFormatter.ts +++ b/VHDLFormatter.ts @@ -1,6 +1,7 @@ let isTesting = false; const ILEscape = "@@"; const ILCommentPrefix = ILEscape + "comments"; +const ILIndentedReturnPrefix = ILEscape; const ILQuote = "⨵"; const ILSingleQuote = "⦼"; const ILBackslash = "⨸"; @@ -175,16 +176,32 @@ export function SetNewLinesAfterSymbols(text: string, newLineSettings: NewLineSe } if (newLineSettings.newLineAfter != null) { newLineSettings.newLineAfter.forEach(symbol => { - let regex: RegExp = new RegExp("(" + symbol.toUpperCase() + ")[ ]?([^ \r\n@])", "g"); + let upper = symbol.toUpperCase(); + var rexString = "(" + upper + ")[ ]?([^ \r\n@])"; + let regex: RegExp = null; + if (upper.regexStartsWith(/\w/)) { + regex = new RegExp("(? { - let regex: RegExp = new RegExp("(" + symbol.toUpperCase() + ")[ \r\n]+([^@])", "g"); + let rexString = "(" + symbol.toUpperCase() + ")[ \r\n]+([^@])"; + let regex: RegExp = null; + if (symbol.regexStartsWith(/\w/)) { + regex = new RegExp("(? function(..\r\n)return type; + input = input.replace(/\)\s*(@@\w+)\r\n\s*RETURN\s+([\w]+;)/g, ') $1\r\n' + ILIndentedReturnPrefix + 'RETURN $2');//function(..)\r\nreturn type; -> function(..\r\n)return type; let keywordAndSignRegex = new RegExp("(\\b" + KeyWords.join("\\b|\\b") + "\\b) +([\\-+]) +(\\w)", "g"); input = input.replace(keywordAndSignRegex, "$1 $2$3");// `WHEN - 2` -> `WHEN -2` input = input.replace(/([,|]) +([+\-]) +(\w)/g, '$1 $2$3');// `1, - 2)` -> `1, -2)` @@ -304,6 +322,7 @@ export function beautify(input: string, settings: BeautifierSettings) { arr = FormattedLineToString(result, settings.Indentation); input = arr.join("\r\n"); + input = input.replace(/@@RETURN/g, "RETURN"); input = SetKeywordCase(input, settings.KeywordCase, KeyWords); input = SetKeywordCase(input, settings.TypeNameCase, TypeNames); @@ -405,7 +424,7 @@ export function beautifyPortGenericBlock(inputs: Array, result: (Formatt } let endIndex: number = hasParenthese ? GetCloseparentheseEndIndex(inputs, startIndex) : startIndex; if (endIndex != startIndex && firstLineHasParenthese) { - inputs[startIndex] = inputs[startIndex].replace(/(PORT|GENERIC|PROCEDURE)([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); + inputs[startIndex] = inputs[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); let newInputs = inputs[startIndex].split("\r\n"); if (newInputs.length == 2) { inputs[startIndex] = newInputs[0]; @@ -618,6 +637,7 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format "UNITS", "\\w+\\s+\\w+\\s+IS\\s+RECORD"]; let blockEndsKeyWords: Array = ["END", ".*\\)\\s*RETURN\\s+[\\w]+;"]; + let indentedEndsKeyWords: Array = [ILIndentedReturnPrefix + "RETURN\\s+\\w+;"]; let blockEndsWithSemicolon: Array = [ "(WITH\\s+[\\w\\s\\\\]+SELECT)", "([\\w\\\\]+[\\s]*<=)", @@ -629,7 +649,8 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format let newLineAfterKeyWordsStr: string = blockStartsKeyWords.join("|"); let regexBlockMidKeyWords: RegExp = blockMidKeyWords.convertToRegexBlockWords(); let regexBlockStartsKeywords: RegExp = new RegExp("([\\w]+\\s*:\\s*)?(" + newLineAfterKeyWordsStr + ")([^\\w]|$)") - let regexBlockEndsKeyWords: RegExp = blockEndsKeyWords.convertToRegexBlockWords() + let regexBlockEndsKeyWords: RegExp = blockEndsKeyWords.convertToRegexBlockWords(); + let regexBlockIndentedEndsKeyWords: RegExp = indentedEndsKeyWords.convertToRegexBlockWords(); let regexblockEndsWithSemicolon: RegExp = blockEndsWithSemicolon.convertToRegexBlockWords(); let regexMidKeyWhen: RegExp = "WHEN".convertToRegexBlockWords(); let regexMidKeyElse: RegExp = "ELSE|ELSIF".convertToRegexBlockWords(); @@ -641,6 +662,10 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format indent = 0; } let input: string = inputs[i].trim(); + if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) { + result.push(new FormattedLine(input, indent)); + return i; + } if (input.regexStartsWith(/COMPONENT\s/)) { let modeCache = Mode; Mode = FormatMode.EndsWithSemicolon; @@ -669,7 +694,7 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format Mode = modeCache; continue; } - if (input.regexStartsWith(/[\w\s:]*PORT([\s]|$)/)) { + if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) { [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "PORT"); continue; } @@ -702,7 +727,11 @@ export function beautify3(inputs: Array, result: (FormattedLine | Format && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { [i, endIndex] = beautifyPortGenericBlock(inputs, result, settings, i, endIndex, indent, "IMPURE FUNCTION"); if (!inputs[i].regexStartsWith(regexBlockEndsKeyWords)) { - i = beautify3(inputs, result, settings, i + 1, indent + 1); + if (inputs[i].regexStartsWith(regexBlockIndentedEndsKeyWords)) { + (result[i]).Indent++; + } else { + i = beautify3(inputs, result, settings, i + 1, indent + 1); + } } else { (result[i]).Indent++; } diff --git a/tests/VHDLFormatterUnitTests.ts b/tests/VHDLFormatterUnitTests.ts index 2f3dfa1..d6141a2 100644 --- a/tests/VHDLFormatterUnitTests.ts +++ b/tests/VHDLFormatterUnitTests.ts @@ -15,6 +15,7 @@ let testCount: number = 0; var showUnitTests = true;//window.location.href.indexOf("http") < 0; if (showUnitTests) { + //IntegrationTest84(); testCount = 0; IntegrationTest(); @@ -920,6 +921,8 @@ function IntegrationTest() { IntegrationTest79(); IntegrationTest80(); IntegrationTest82(); + IntegrationTest84(); + IntegrationTest85(); } function IntegrationTest23() { @@ -1396,6 +1399,28 @@ function IntegrationTest82() { assertAndCountTest("block alignment local", input, actual); } +function IntegrationTest83() { // TODO + let settings = GetDefaultSettings(); + let input = 'FUNCTION "<=" (l : ufixed; long : NATURAL) return BOOLEAN;\r\nFUNCTION ">" (l : ufixed; r : NATURAL) RETURN BOOLEAN;'; + let actual = beautify(input, settings); + assertAndCountTest("signal in out alignment", input, actual); +} + +function IntegrationTest84() { + let settings = GetDefaultSettings(); + let input = 'PACKAGE f IS\r\n FUNCTION s (\r\n arg : sfixed) -- fp vector\r\n RETURN t;\r\nEND PACKAGE f;'; + let actual = beautify(input, settings); + assertAndCountTest("empty body and return", input, actual); +} + +function IntegrationTest85() { + let settings = GetDefaultSettings(); + settings.NewLineSettings.newLineAfter = ["port"] + let input = 'REPORT "FIXED_GENERIC_PKG: Vector passed using a ""to"" range, expected is ""downto"""\r\n SEVERITY error;'; + let actual = beautify(input, settings); + assertAndCountTest("report and severity", input, actual); +} + function GetDefaultSettings(indentation: string = " "): BeautifierSettings { let new_line_after_symbols = new NewLineSettings(); new_line_after_symbols.newLineAfter = ["then", ";"];