├── .gitignore ├── package.json ├── jest.config.js ├── tsconfig.json ├── .github ├── workflows │ └── ci.yml └── issue_template.md ├── VHDLFiles ├── indent.vhd ├── mixed_if_case.vhd └── instance.vhd ├── tests ├── descriptiveCounter.test.ts ├── assert.ts └── VHDLFormatter.test.ts ├── LICENSE ├── README.md ├── highlight.css ├── descriptiveCounter.ts ├── descriptiveCounter.js ├── main.js ├── main.ts ├── highlight.js ├── style.css ├── index.html ├── VHDLFormatter.js └── VHDLFormatter.ts /.gitignore: -------------------------------------------------------------------------------- 1 | *.map 2 | /.vscode/* 3 | /tests/*.js 4 | node_modules 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test": "jest" 4 | }, 5 | "devDependencies": { 6 | "@types/jest": "^26.0.20", 7 | "jest": "^26.6.3", 8 | "ts-jest": "^26.4.4", 9 | "typescript": "^4.1.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | transform: { 4 | '^.+\\.ts?$': 'ts-jest' 5 | }, 6 | testEnvironment: 'node', 7 | testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx|js)$', 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] 9 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "lib": ["es6","dom"], 6 | "sourceMap": true 7 | }, 8 | "files": [ 9 | "main.ts", 10 | "descriptiveCounter.ts", 11 | "VHDLFormatter.ts", 12 | "tests/VHDLFormatterUnitTests.ts", 13 | "tests/assert.ts", 14 | ], 15 | "exclude": [ 16 | "node_modules", 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Test using Node.js 9 | uses: actions/setup-node@v1 10 | with: 11 | node-version: '12' 12 | - name: npm install, build, and test 13 | run: | 14 | npm ci 15 | npm run build --if-present 16 | npm test 17 | env: 18 | CI: true -------------------------------------------------------------------------------- /VHDLFiles/indent.vhd: -------------------------------------------------------------------------------- 1 | package run_base_pkg is 2 | signal runner : runner_sync_t := (phase => test_runner_entry, 3 | locks => ((false, false), 4 | (false, false), 5 | (false, false)), 6 | exit_without_errors => false, 7 | exit_simulation => false); 8 | 9 | shared variable runner_trace_logger : logger_t; 10 | 11 | procedure runner_init; 12 | 13 | impure function get_phase 14 | return runner_phase_t; 15 | 16 | procedure set_test_case_name ( 17 | constant index : in positive; 18 | constant new_name : in string); 19 | 20 | impure function get_test_case_name ( 21 | constant index : positive) 22 | return string; 23 | 24 | procedure set_num_of_test_cases ( 25 | constant new_value : in integer); 26 | end package; -------------------------------------------------------------------------------- /tests/descriptiveCounter.test.ts: -------------------------------------------------------------------------------- 1 | import { descriptiveCounter } from "../descriptiveCounter"; 2 | 3 | describe('descriptiveCounter', function () { 4 | it('one blankspace', function () { 5 | let result = descriptiveCounter(" "); 6 | expect(result).toBe("one blankspace"); 7 | }); 8 | 9 | it('mixed chars', function () { 10 | let result = descriptiveCounter(" A "); 11 | expect(result).toBe("one blankspace & one 'A' & one blankspace"); 12 | }); 13 | 14 | it('4 blankspaces', function () { 15 | let result = descriptiveCounter(" "); 16 | expect(result).toBe("four blankspaces"); 17 | }); 18 | 19 | it('9 blankspaces', function () { 20 | let result = descriptiveCounter(" "); 21 | expect(result).toBe("many blankspaces"); 22 | }); 23 | 24 | it('2 As', function () { 25 | let result = descriptiveCounter("AA"); 26 | expect(result).toBe("two 'A's"); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /VHDLFiles/mixed_if_case.vhd: -------------------------------------------------------------------------------- 1 | ARCHITECTURE ARCH OF ENTITY IS 2 | BEGIN 3 | PROC_1 : PROCESS (a, b, c, d) IS 4 | BEGIN 5 | IF (a = 1) THEN 6 | CASE b 7 | WHEN 1 => 8 | c <= d; 9 | CASE b 10 | WHEN 1 => 11 | c <= d; 12 | WHEN 2 => 13 | d <= f; 14 | END CASE; 15 | WHEN 2 => 16 | d <= f; 17 | END CASE; 18 | ELSIF (b = 1) THEN 19 | CASE b 20 | WHEN 1 => 21 | c <= d; 22 | WHEN 2 => 23 | d <= f; 24 | END CASE; 25 | ELSE 26 | CASE b 27 | WHEN 1 => 28 | c <= d; 29 | WHEN 2 => 30 | d <= f; 31 | END CASE; 32 | END IF; 33 | END PROCESS PROC_1; 34 | END ARCHITECTURE ARCH; -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## Report a bug 5 | 6 | ### Input 7 | 8 | 9 | ```vhdl 10 | -- sample code here 11 | ``` 12 | 13 | ### Expected Behavior 14 | 15 | 16 | ```vhdl 17 | -- sample code here 18 | ``` 19 | 20 | ### Actual Behavior 21 | 22 | 23 | ```vhdl 24 | -- sample code here 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | --- 32 | 33 | ## Request a new feature 34 | 35 | ### As a user, I want to... 36 | 37 | 38 | #### Example Input: 39 | 40 | 41 | ```vhdl 42 | -- sample code here 43 | ``` 44 | 45 | #### Example Output: 46 | 47 | 48 | ```vhdl 49 | -- sample code here 50 | ``` 51 | 52 | 53 | -------------------------------------------------------------------------------- /tests/assert.ts: -------------------------------------------------------------------------------- 1 | export function assert(testName: string, expected: string, actual: string, message?: undefined) { 2 | var result = CompareString(actual, expected); 3 | if (result != true) { 4 | console.log('"' + testName + "\" failed: \n" + result); 5 | } 6 | else { 7 | //console.log(testName + " pass"); 8 | } 9 | } 10 | 11 | export function CompareString(actual: string, expected: string) { 12 | var l = Math.min(actual.length, expected.length); 13 | for (var i = 0; i < l; i++) { 14 | if (actual[i] != expected[i]) { 15 | var toEnd = Math.min(i + 50, l); 16 | return '\ndifferent at ' + i.toString() + 17 | '\nactual: "\n' + actual.substring(i, toEnd) + 18 | '\nexpected: "\n' + expected.substring(i, toEnd) + '"\n---' + 19 | "\nactual (full): \n" + actual + "\n---" + 20 | "\nexpected (full): \n" + expected + "\n====\n"; 21 | } 22 | } 23 | if (actual != expected) { 24 | return 'actual: \n"' + actual + '"\nexpected: \n"' + expected + '"'; 25 | } 26 | return true; 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /VHDLFiles/instance.vhd: -------------------------------------------------------------------------------- 1 | architecture test of instance is 2 | begin 3 | 4 | a: foo; 5 | 6 | b: entity work.foo; 7 | 8 | b1: entity work.foo(goo); 9 | 10 | c: configuration work.bar; 11 | 12 | d: component foo; 13 | 14 | e: entity work.foo 15 | port map ( a, b, c ); 16 | 17 | f: entity work.foo 18 | port map ( a, b, x => c ); 19 | 20 | g: entity work.foo 21 | generic map ( X => 1 ) 22 | port map ( a, b ); 23 | 24 | h: entity work.foo 25 | port map ( a => open ); 26 | 27 | i: foo port map ( x ); 28 | 29 | UUT : ENTITY work.csa_adder 30 | PORT MAP( 31 | osum => sum, 32 | ocarry => carry 33 | ); 34 | 35 | UUT_S : ENTITY work.csa_adder 36 | PORT MAP( 37 | osum => sums, 38 | ocarry => carrys 39 | ); 40 | 41 | end architecture; 42 | 43 | ARCHITECTURE test3 OF test IS 44 | COMPONENT comp IS PORT (a : BOOLEAN); 45 | END COMPONENT; 46 | SIGNAL s_ok : BOOLEAN; 47 | BEGIN 48 | comp PORT MAP(a => s_ok); -- unlabeled component instantiation 49 | END ARCHITECTURE; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VHDL Fomatter 2 | 3 | Online VHDL formatter written in Typescript 4 | 5 | [Try it here: https://g2384.github.io/VHDLFormatter/](https://g2384.github.io/VHDLFormatter/) 6 | 7 | Build Status: [![Main Branch](https://github.com/g2384/VHDLFormatter/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/g2384/VHDLFormatter/actions/workflows/ci.yml) 8 | 9 | --- 10 | 11 | ## Run Locally 12 | 13 | Simply download/clone this repository, and open the `index.html` file. 14 | 15 | --- 16 | 17 | ## Contribute 18 | 19 | ### Structure 20 | 21 | - `index.html`: html + javascript. Collect settings from UI, call the beautify function, load/save/update cached settings. 22 | - `main.ts`: typescript. Some code required by `index.html` are moved here. 23 | - `VHDLFormatter.ts`: typescript. Define classes, format VHDL code 24 | - `tests`: folder. Contain all test files 25 | - *.test.ts: typescript. Proper test files. 26 | - `VHDLFormatterUnitTests.ts`: typescript. Handcrafted, crude tests. 27 | - `VHDLFiles`: vhdl. Contain complicated VHDL files which I don't want to lose. 28 | 29 | ### Develop 30 | 31 | Use Visual Studio Code to open the repo folder. 32 | 33 | ### Run Tests 34 | 35 | Tests must be run before each commit. 36 | 37 | #### Run Unit/Integration Tests 38 | 39 | 1. open repo folder with Visua Studio Code 40 | 2. click `Run (Ctrl + Shift + D)` 41 | 3. select `Run Unit Tests` configuration 42 | 4. click `Start Debugging` button 43 | 44 | #### Run Jest Tests 45 | 46 | 1. open repo folder with Visua Studio Code 47 | 2. click `Terminal` -> `Run Task...` 48 | 3. select `npm: test jest` 49 | -------------------------------------------------------------------------------- /highlight.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;-webkit-text-size-adjust:none}.hljs,.hljs-subst,.hljs-tag .hljs-title,.nginx .hljs-title{color:#000}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rules .hljs-value,.hljs-preprocessor,.hljs-pragma,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.hljs-javadoc,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88f}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.css .hljs-tag,.hljs-javadoctag,.hljs-phpdoc,.hljs-dartdoc,.hljs-yardoctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.apache .hljs-tag,.hljs-type,.hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status{font-weight:700}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis{font-style:italic}.nginx .hljs-built_in{font-weight:400}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5} -------------------------------------------------------------------------------- /descriptiveCounter.ts: -------------------------------------------------------------------------------- 1 | function counterDecode(inputId: string, outputId: string) { 2 | var custom_indent: string = (document.getElementById(inputId)).value; 3 | var result: string = descriptiveCounter(custom_indent); 4 | document.getElementById(outputId).innerHTML = result; 5 | } 6 | 7 | export function descriptiveCounter(input: string): string { 8 | input = input.replace(/\\t/g, " "); 9 | input = input.replace(/\\r/g, "\r"); 10 | input = input.replace(/\\n/g, "\n"); 11 | 12 | var tokens: Array = input.split(""); 13 | var result = ""; 14 | var repeatedCharCount = 0; 15 | for (var i = 0; i < tokens.length; i++) { 16 | var char = input.substr(i, 1); 17 | if (char == input.substr(i + 1, 1)) { 18 | repeatedCharCount++; 19 | } else { 20 | switch (char) { 21 | case " ": 22 | char = "blankspace"; 23 | break; 24 | case "\t": 25 | char = "tab"; 26 | break; 27 | case "\n": 28 | char = "\\n"; 29 | break; 30 | case "\r": 31 | char = "\\r"; 32 | break; 33 | default: 34 | char = "'" + char + "'"; 35 | } 36 | repeatedCharCount = repeatedCharCount > 8 ? 8 : repeatedCharCount; 37 | if (repeatedCharCount > 0) { 38 | char += "s"; 39 | } 40 | result += getCountText(repeatedCharCount, char); 41 | repeatedCharCount = 0; 42 | } 43 | } 44 | 45 | if (result.length < 0) { 46 | switch (char) { 47 | case " ": 48 | char = "blankspace"; 49 | break; 50 | case "\t": 51 | char = "tab"; 52 | } 53 | repeatedCharCount = repeatedCharCount > 8 ? 8 : repeatedCharCount; 54 | result = getCountText(repeatedCharCount, char); 55 | } 56 | 57 | result = result.replace(/^ & /, "") 58 | return result; 59 | } 60 | 61 | function getCountText(count: number, char: string): string { 62 | const dict = ["one", "two", "three", "four", "five", "six", "seven", "eight", "many"]; 63 | const ampersand = " & "; 64 | return ampersand + dict[count] + " " + char; 65 | } -------------------------------------------------------------------------------- /descriptiveCounter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.descriptiveCounter = void 0; 4 | function counterDecode(inputId, outputId) { 5 | var custom_indent = document.getElementById(inputId).value; 6 | var result = descriptiveCounter(custom_indent); 7 | document.getElementById(outputId).innerHTML = result; 8 | } 9 | function descriptiveCounter(input) { 10 | input = input.replace(/\\t/g, " "); 11 | input = input.replace(/\\r/g, "\r"); 12 | input = input.replace(/\\n/g, "\n"); 13 | var tokens = input.split(""); 14 | var result = ""; 15 | var repeatedCharCount = 0; 16 | for (var i = 0; i < tokens.length; i++) { 17 | var char = input.substr(i, 1); 18 | if (char == input.substr(i + 1, 1)) { 19 | repeatedCharCount++; 20 | } 21 | else { 22 | switch (char) { 23 | case " ": 24 | char = "blankspace"; 25 | break; 26 | case "\t": 27 | char = "tab"; 28 | break; 29 | case "\n": 30 | char = "\\n"; 31 | break; 32 | case "\r": 33 | char = "\\r"; 34 | break; 35 | default: 36 | char = "'" + char + "'"; 37 | } 38 | repeatedCharCount = repeatedCharCount > 8 ? 8 : repeatedCharCount; 39 | if (repeatedCharCount > 0) { 40 | char += "s"; 41 | } 42 | result += getCountText(repeatedCharCount, char); 43 | repeatedCharCount = 0; 44 | } 45 | } 46 | if (result.length < 0) { 47 | switch (char) { 48 | case " ": 49 | char = "blankspace"; 50 | break; 51 | case "\t": 52 | char = "tab"; 53 | } 54 | repeatedCharCount = repeatedCharCount > 8 ? 8 : repeatedCharCount; 55 | result = getCountText(repeatedCharCount, char); 56 | } 57 | result = result.replace(/^ & /, ""); 58 | return result; 59 | } 60 | exports.descriptiveCounter = descriptiveCounter; 61 | function getCountText(count, char) { 62 | const dict = ["one", "two", "three", "four", "five", "six", "seven", "eight", "many"]; 63 | const ampersand = " & "; 64 | return ampersand + dict[count] + " " + char; 65 | } 66 | //# sourceMappingURL=descriptiveCounter.js.map -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | function noFormat() { 2 | let elements = [ 3 | "remove_comments", 4 | "remove_lines", 5 | "remove_report", 6 | "check_alias", 7 | "sign_align_in", 8 | "sign_align_port", 9 | "sign_align_generic", 10 | "sign_align_function", 11 | "sign_align_procedure", 12 | "sign_align_all", 13 | "new_line_after", 14 | "use_space", 15 | "customise_indentation", 16 | "compress", 17 | "mix_letter", 18 | "cust_eol", 19 | "sign_align_mode", 20 | "keyword", 21 | "typename", 22 | "align_comments", 23 | "add_extraEOL" 24 | ]; 25 | var isDisabled = getHTMLInputElement("no_format").checked; 26 | changeStateOfElements(elements, isDisabled); 27 | let radioButtons = document.getElementsByTagName("input"); 28 | for (let i = 0; i < radioButtons.length; i++) { 29 | if (radioButtons[i].type == "radio") { 30 | radioButtons[i].disabled = isDisabled; 31 | } 32 | } 33 | } 34 | function changeStateOfElements(elements, isDisabled) { 35 | elements.forEach(element => { 36 | var htmlElement = getHTMLInputElement(element + "_div"); 37 | try { 38 | getHTMLInputElement(element).disabled = isDisabled; 39 | } 40 | catch (_a) { } 41 | if (isDisabled) { 42 | htmlElement.className += " disabled"; 43 | } 44 | else { 45 | htmlElement.className = htmlElement.className.replace(/\bdisabled\b/g, ""); 46 | } 47 | }); 48 | } 49 | function getHTMLInputElement(id) { 50 | return document.getElementById(id); 51 | } 52 | function Compress(input) { 53 | input = input.replace(/\r\n/g, ''); 54 | input = input.replace(/[\t ]+/g, ' '); 55 | input = input.replace(/[ ]?([&=:\-<>\+|])[ ]?/g, '$1'); 56 | return input; 57 | } 58 | function MixLetters(input) { 59 | let arr = input.split(""); 60 | for (var k = 0; k < arr.length; k++) { 61 | if (arr[k] === arr[k].toUpperCase() && Math.random() > 0.5) { 62 | arr[k] = arr[k].toLowerCase(); 63 | } 64 | else if (Math.random() > 0.5) { 65 | arr[k] = arr[k].toUpperCase(); 66 | } 67 | } 68 | return arr.join(""); 69 | } 70 | function wordWrap() { 71 | let d = getHTMLInputElement("result"); 72 | if (d.className == "") { 73 | d.className = "wordwrap"; 74 | } 75 | else { 76 | d.className = ""; 77 | } 78 | } 79 | function alignAllSigns(alignAll) { 80 | if (alignAll) { 81 | getHTMLInputElement("sign_align_port").checked = false; 82 | getHTMLInputElement("sign_align_generic").checked = false; 83 | getHTMLInputElement("sign_align_procedure").checked = false; 84 | getHTMLInputElement("sign_align_function").checked = false; 85 | getHTMLInputElement("sign_align_mode_div").disabled = false; 86 | } 87 | else { 88 | getHTMLInputElement("sign_align_all").checked = false; 89 | } 90 | let isDisabled = !alignAll; 91 | changeStateOfElements(["sign_align_mode"], isDisabled); 92 | let radioButtons = document.querySelectorAll("#sign_align_mode_div input[type=radio]"); 93 | for (let i = 0; i < radioButtons.length; i++) { 94 | if (radioButtons[i].type == "radio") { 95 | radioButtons[i].disabled = isDisabled; 96 | } 97 | } 98 | } 99 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | function noFormat() { 2 | let elements: Array = [ 3 | "remove_comments", 4 | "remove_lines", 5 | "remove_report", 6 | "check_alias", 7 | "sign_align_in", 8 | "sign_align_port", 9 | "sign_align_generic", 10 | "sign_align_function", 11 | "sign_align_procedure", 12 | "sign_align_all", 13 | "new_line_after", 14 | "use_space", 15 | "customise_indentation", 16 | "compress", 17 | "mix_letter", 18 | "cust_eol", 19 | "sign_align_mode", 20 | "keyword", 21 | "typename", 22 | "align_comments", 23 | "add_extraEOL" 24 | ]; 25 | var isDisabled = getHTMLInputElement("no_format").checked; 26 | changeStateOfElements(elements, isDisabled); 27 | let radioButtons = >document.getElementsByTagName("input"); 28 | for (let i = 0; i < radioButtons.length; i++) { 29 | if ((radioButtons[i]).type == "radio") { 30 | (radioButtons[i]).disabled = isDisabled; 31 | } 32 | } 33 | } 34 | 35 | function changeStateOfElements(elements: string[], isDisabled: boolean) { 36 | elements.forEach(element => { 37 | var htmlElement = getHTMLInputElement(element + "_div"); 38 | try { 39 | getHTMLInputElement(element).disabled = isDisabled; 40 | } 41 | catch { } 42 | if (isDisabled) { 43 | htmlElement.className += " disabled"; 44 | } 45 | else { 46 | htmlElement.className = htmlElement.className.replace(/\bdisabled\b/g, ""); 47 | } 48 | }); 49 | } 50 | 51 | function getHTMLInputElement(id: string): HTMLInputElement { 52 | return document.getElementById(id); 53 | } 54 | 55 | function Compress(input: string) { 56 | input = input.replace(/\r\n/g, ''); 57 | input = input.replace(/[\t ]+/g, ' '); 58 | input = input.replace(/[ ]?([&=:\-<>\+|])[ ]?/g, '$1'); 59 | return input; 60 | } 61 | 62 | function MixLetters(input: string) { 63 | let arr = input.split(""); 64 | for (var k = 0; k < arr.length; k++) { 65 | if (arr[k] === arr[k].toUpperCase() && Math.random() > 0.5) { 66 | arr[k] = arr[k].toLowerCase(); 67 | } else if (Math.random() > 0.5) { 68 | arr[k] = arr[k].toUpperCase(); 69 | } 70 | } 71 | return arr.join(""); 72 | } 73 | 74 | function wordWrap() { 75 | let d = getHTMLInputElement("result"); 76 | if (d.className == "") { 77 | d.className = "wordwrap"; 78 | } else { 79 | d.className = ""; 80 | } 81 | } 82 | 83 | function alignAllSigns(alignAll: boolean) { 84 | if (alignAll) { 85 | getHTMLInputElement("sign_align_port").checked = false; 86 | getHTMLInputElement("sign_align_generic").checked = false; 87 | getHTMLInputElement("sign_align_procedure").checked = false; 88 | getHTMLInputElement("sign_align_function").checked = false; 89 | getHTMLInputElement("sign_align_mode_div").disabled = false; 90 | } 91 | else { 92 | getHTMLInputElement("sign_align_all").checked = false; 93 | } 94 | let isDisabled = !alignAll; 95 | changeStateOfElements(["sign_align_mode"], isDisabled); 96 | let radioButtons = document.querySelectorAll("#sign_align_mode_div input[type=radio]"); 97 | for (let i = 0; i < radioButtons.length; i++) { 98 | if ((radioButtons[i]).type == "radio") { 99 | (radioButtons[i]).disabled = isDisabled; 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /highlight.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("vhdl",function(e){var r="\\d(_|\\d)*",t="[eE][-+]?"+r,n=r+"(\\."+r+")?("+t+")?",o="\\w+",i=r+"#"+o+"(\\."+o+")?#("+t+")?",a="\\b("+i+"|"+n+")";return{cI:!0,k:{keyword:"abs access after alias all and architecture array assert assume assume_guarantee attribute begin block body buffer bus case component configuration constant context cover disconnect downto default else elsif end entity exit fairness file for force function generate generic group guarded if impure in inertial inout is label library linkage literal loop map mod nand new next nor not null of on open or others out package port postponed procedure process property protected pure range record register reject release rem report restrict restrict_guarantee return rol ror select sequence severity shared signal sla sll sra srl strong subtype then to transport type unaffected units until use variable vmode vprop vunit wait when while with xnor xor",built_in:"boolean bit character integer time delay_length natural positive string bit_vector file_open_kind file_open_status std_logic std_logic_vector unsigned signed boolean_vector integer_vector std_ulogic std_ulogic_vector unresolved_unsigned u_unsigned unresolved_signed u_signedreal_vector time_vector",literal:"false true note warning error failure line text side width"},i:"{",c:[e.CBCM,e.C("--","$"),e.QSM,{cN:"number",b:a,r:0},{cN:"string",b:"'(U|X|0|1|Z|W|L|H|-)'",c:[e.BE]},{cN:"symbol",b:"'[A-Za-z](_?[A-Za-z0-9])*",c:[e.BE]}]}}); -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0 auto; 3 | max-width: 960px; 4 | tab-size: 4; 5 | font-family: arial, sans-serif; 6 | } 7 | 8 | pre, code, textarea { 9 | font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; 10 | } 11 | 12 | textarea { 13 | width: 100%; 14 | padding: 3px; 15 | resize: vertical; 16 | min-height: 100px; 17 | } 18 | 19 | input[type="text"] { 20 | padding: 2px 4px; 21 | } 22 | 23 | input[type="text"], 24 | textarea { 25 | border: solid 1px #dbdbdb; 26 | box-shadow: inset 0 1px 2px rgba(10, 10, 10, .1); 27 | border-radius: 4px 4px 0px 0px; 28 | font-size: 1rem; 29 | } 30 | 31 | input[type="text"]:hover, 32 | textarea:hover { 33 | border-color: #b5b5b5; 34 | } 35 | 36 | ul { 37 | flex-wrap: wrap; 38 | max-width: 100%; 39 | list-style: none; 40 | flex-grow: 1; 41 | padding: 0px; 42 | } 43 | 44 | li { 45 | display: block; 46 | margin: 0; 47 | padding: 0; 48 | box-sizing: inherit; 49 | user-select: none; 50 | } 51 | 52 | .top-button { 53 | color: rgba(0, 0, 0, 0.7); 54 | font-size: 1rem; 55 | line-height: 1rem; 56 | padding: 1em 0.5em; 57 | margin: 0; 58 | cursor: pointer; 59 | border-radius: 0px; 60 | background-color: #f5f5f5; 61 | } 62 | 63 | .top-button:hover { 64 | color: #000; 65 | background: #ededed; 66 | -webkit-transition: .5s background-color ease, .5s color ease; 67 | transition: .5s background-color ease, .5s color ease; 68 | } 69 | 70 | .top-bar { 71 | position: fixed; 72 | top: 0; 73 | left: 0; 74 | width: 100%; 75 | background-color: #f5f5f5; 76 | box-sizing: border-box; 77 | box-shadow: 0 1px 0 rgba(12, 13, 14, 0.1), 0 1px 6px rgba(59, 64, 69, 0.1); 78 | } 79 | 80 | .top-bar .container { 81 | margin: 0 auto; 82 | max-width: 940px; 83 | display: flex; 84 | box-sizing: border-box; 85 | flex-flow: row wrap; 86 | align-items: center; 87 | } 88 | 89 | .top-bar h2 { 90 | flex-shrink: 0; 91 | display: flex; 92 | justify-content: flex-start; 93 | font-size: 1.3em; 94 | line-height: 1.3em; 95 | margin: 0px; 96 | cursor: default; 97 | } 98 | 99 | .top-bar ul { 100 | flex-shrink: 0; 101 | display: flex; 102 | align-items: center; 103 | justify-content: flex-end; 104 | margin: 0px; 105 | } 106 | 107 | #in { 108 | margin-top: 70px; 109 | } 110 | 111 | .inline-emoji { 112 | font-size: 0.8em; 113 | } 114 | 115 | .setting-field { 116 | padding: 1.25rem 1.5rem; 117 | background-color: #f5f5f5; 118 | border-radius: 0px 0px 6px 6px; 119 | } 120 | 121 | fieldset { 122 | width: fit-content; 123 | border: 1px solid #ccc; 124 | margin: 25px 0 5px 0; 125 | padding: 5px; 126 | } 127 | 128 | legend { 129 | padding: 2px 5px; 130 | background-color: #ccc; 131 | border-radius: 2px; 132 | margin: -27px 0 0 -6px; 133 | position: absolute; 134 | } 135 | 136 | .subtitle { 137 | font-weight: normal; 138 | } 139 | 140 | .btn { 141 | margin: 10px 0px; 142 | padding: .5em 1em; 143 | font-size: 1rem; 144 | border-radius: 3px; 145 | color: #333; 146 | border: solid 1px #dbdbdb; 147 | cursor: pointer; 148 | text-align: center; 149 | white-space: nowrap; 150 | background-color: #fff; 151 | } 152 | 153 | .btn:hover { 154 | border-color: #b5b5b5; 155 | background: rgba(0, 0, 0, .03); 156 | } 157 | 158 | .show { 159 | display: none; 160 | } 161 | 162 | .wordwrap { 163 | white-space: pre-wrap; 164 | white-space: -moz-pre-wrap; 165 | white-space: -pre-wrap; 166 | white-space: -o-pre-wrap; 167 | word-wrap: break-word; 168 | } 169 | 170 | label { 171 | cursor: pointer; 172 | line-height: 1.5em; 173 | } 174 | 175 | label:hover { 176 | color: #555; 177 | text-decoration: underline; 178 | } 179 | 180 | form { 181 | margin-bottom: 0px; 182 | } 183 | 184 | form span { 185 | display: inline-block; 186 | } 187 | 188 | a { 189 | color: #4b9aff; 190 | text-decoration: none; 191 | background-color: #f0f8ff; 192 | padding: .5em 1em; 193 | border-radius: 3px; 194 | font-size: 0.8em; 195 | cursor: pointer; 196 | } 197 | 198 | a:hover { 199 | text-decoration: none; 200 | background: #d6e8ff; 201 | } 202 | 203 | .plain-link { 204 | color: #000; 205 | background: none; 206 | text-decoration: none; 207 | } 208 | 209 | .plain-link :hover { 210 | text-decoration: none; 211 | background: none; 212 | } 213 | 214 | p { 215 | line-height: 1em; 216 | margin: 0.5em 0em; 217 | } 218 | 219 | .content { 220 | margin: 10px; 221 | } 222 | 223 | .code { 224 | font-family: 'Consolas', Courier, monospace; 225 | } 226 | 227 | .inline { 228 | display: inline-block; 229 | } 230 | 231 | .disabled { 232 | color: #ccc; 233 | cursor: default; 234 | } 235 | 236 | .disabled label:hover { 237 | color: #ccc; 238 | text-decoration: none; 239 | cursor: default; 240 | } 241 | 242 | .checkbox input[type="checkbox"] { 243 | opacity: 0; 244 | display: none; 245 | } 246 | 247 | .checkbox label { 248 | position: relative; 249 | display: inline-block; 250 | /*16px width of fake checkbox + 6px distance between fake checkbox and text*/ 251 | padding-left: 22px; 252 | } 253 | 254 | .checkbox label::before, 255 | .checkbox label::after { 256 | position: absolute; 257 | content: ""; 258 | /*Needed for the line-height to take effect*/ 259 | display: inline-block; 260 | } 261 | 262 | /*Outer box of the fake checkbox*/ 263 | 264 | .checkbox label::before { 265 | height: 16px; 266 | width: 16px; 267 | border: 1px solid #9A9A9A; 268 | left: 0px; 269 | /*(24px line-height - 16px height of fake checkbox) / 2 - 1px for the border to vertically center it.*/ 270 | top: 3px; 271 | } 272 | 273 | /*Checkmark of the fake checkbox*/ 274 | 275 | .checkbox label::after { 276 | height: 5px; 277 | width: 9px; 278 | border-left: 2px solid; 279 | border-bottom: 2px solid; 280 | transform: rotate(-45deg); 281 | left: 4px; 282 | top: 7px; 283 | color: #333; 284 | } 285 | 286 | /*Hide the checkmark by default*/ 287 | 288 | .checkbox input[type="checkbox"]+label::after { 289 | content: none; 290 | } 291 | 292 | /*Unhide on the checked state*/ 293 | 294 | .checkbox input[type="checkbox"]:checked+label::after { 295 | content: ""; 296 | } 297 | 298 | /*Adding focus styles on the outer-box of the fake checkbox*/ 299 | 300 | .checkbox input[type="checkbox"]:hover+label::after { 301 | border-color: #888; 302 | outline: #ccc; 303 | } 304 | 305 | .checkbox input[type="checkbox"]:hover+label::before { 306 | outline: #888; 307 | border-color: #888; 308 | background-color: #eee; 309 | } 310 | 311 | .checkbox input[type="checkbox"]:disabled+label::before { 312 | outline: #ccc; 313 | border-color: #ccc; 314 | background-color: #fff; 315 | color: #ccc; 316 | } 317 | 318 | .checkbox input[type="checkbox"]:disabled+label::after { 319 | color: #888; 320 | } 321 | 322 | .checkbox input[type="checkbox"]:focus+label::before { 323 | outline: #333 auto 5px; 324 | } 325 | 326 | .inline-note { 327 | color: #888; 328 | margin-left: 10px; 329 | display: inline-block; 330 | } 331 | 332 | .hide { 333 | display: none; 334 | } 335 | 336 | #settings_control { 337 | margin: 10px 0px; 338 | display: inline-block; 339 | } 340 | 341 | .modal { 342 | display: none; 343 | position: fixed; 344 | z-index: 1; 345 | left: 0; 346 | top: 0; 347 | width: 100%; 348 | height: 100%; 349 | overflow: auto; 350 | background-color: rgb(0, 0, 0); 351 | background-color: rgba(0, 0, 0, 0.4); 352 | } 353 | 354 | .modal-content { 355 | background-color: #fefefe; 356 | margin: 15% auto; 357 | padding: 20px; 358 | border: 1px solid #888; 359 | width: 80%; 360 | } 361 | 362 | .close { 363 | color: #aaa; 364 | float: right; 365 | font-size: 28px; 366 | font-weight: bold; 367 | } 368 | 369 | .close:hover, 370 | .close:focus { 371 | color: black; 372 | text-decoration: none; 373 | cursor: pointer; 374 | } 375 | 376 | .tooltip { 377 | cursor: pointer; 378 | position: relative; 379 | display: inline-block; 380 | } 381 | 382 | .tooltip .tooltiptext { 383 | visibility: hidden; 384 | opacity: 0; 385 | width: 220px; 386 | background-color: #fff; 387 | color: #000; 388 | text-align: left; 389 | padding: .5em 1em; 390 | border: 1px solid #cecece; 391 | border-radius: 3px; 392 | top: 120%; 393 | left: 0%; 394 | position: absolute; 395 | z-index: 1; 396 | -webkit-transition: opacity 0.5s ease-in-out; 397 | transition: opacity 0.5s ease-in-out; 398 | box-shadow: 1px 0px 5px #bcbcbc; 399 | } 400 | 401 | .tooltip:hover .tooltiptext { 402 | visibility: visible; 403 | opacity: 1; 404 | } 405 | 406 | .tooltip .tooltiptext::after { 407 | content: " "; 408 | position: absolute; 409 | bottom: 100%; 410 | left: 6%; 411 | margin-left: -5px; 412 | border-width: 5px; 413 | border-style: solid; 414 | border-width: 0 0.5em 0.5em 0.5em; 415 | border-color: transparent transparent white transparent; 416 | -webkit-filter: drop-shadow(1px 2px 1px #bcbcbc); 417 | filter: drop-shadow(1px -1px 1px #bcbcbc); 418 | } 419 | 420 | .emoji { 421 | font-family: apple color emoji, segoe ui emoji, noto color emoji, android emoji, emojisymbols, emojione mozilla, twemoji mozilla, segoe ui symbol; 422 | color: #fff; 423 | } -------------------------------------------------------------------------------- /tests/VHDLFormatter.test.ts: -------------------------------------------------------------------------------- 1 | import { beautify, signAlignSettings } from "../VHDLFormatter"; 2 | import { NewLineSettings } from "../VHDLFormatter"; 3 | import { BeautifierSettings } from "../VHDLFormatter"; 4 | import { RemoveAsserts } from "../VHDLFormatter"; 5 | import { ApplyNoNewLineAfter } from "../VHDLFormatter"; 6 | import { SetNewLinesAfterSymbols } from "../VHDLFormatter"; 7 | import { beautify3 } from "../VHDLFormatter"; 8 | import { FormattedLine } from "../VHDLFormatter"; 9 | import { FormattedLineToString } from "../VHDLFormatter"; 10 | 11 | describe('VHDLFormatter', function () { 12 | it('do not format block comments', function () { 13 | let settings = GetDefaultSettings(); 14 | let input = 'a;\r\n/*a; \r\n b;\r\nport ( ) ;\r\n/*\r\n*/c;'; 15 | let result = beautify(input, settings); 16 | expect(result).toBe(input); 17 | }); 18 | 19 | it('do not format block comments 2', function () { 20 | let settings = GetDefaultSettings(); 21 | let input = 'a;/*a;*/b;\r\nc;'; 22 | let result = beautify(input, settings); 23 | expect(result).toBe(input); 24 | }); 25 | 26 | it('do not format block comments 3', function () { 27 | let settings = GetDefaultSettings(); 28 | let input = 'a;\r\n/*a;*//*\r\n*/c;'; 29 | let result = beautify(input, settings); 30 | expect(result).toBe(input); 31 | }); 32 | 33 | it('do not format block comments 4', function () { 34 | let settings = GetDefaultSettings(); 35 | let input = '/*\r\nwoof\r\n*//*\r\nwoof\r\n*//*\r\nwoof\r\n*/'; 36 | let result = beautify(input, settings); 37 | expect(result).toBe(input); 38 | }); 39 | 40 | it('add new line at the end of the file', function () { 41 | let settings = GetDefaultSettings(); 42 | settings.AddNewLine = true; 43 | let input = 'test'; 44 | let result = beautify(input, settings); 45 | expect(result).toBe("test\r\n"); 46 | }); 47 | 48 | it('upper case types', function () { 49 | let settings = GetDefaultSettings(); 50 | let input = "x : string;\r\ny : std_logic_vector;"; 51 | let result = beautify(input, settings); 52 | expect(result).toBe("x : STRING;\r\ny : STD_LOGIC_VECTOR;"); 53 | }); 54 | 55 | it('package ends with ;', function () { 56 | let settings = GetDefaultSettings(); 57 | let input = "PACKAGE p IS NEW work.p_template\r\n GENERIC MAP(N => N);\r\nUSE p.ALL;"; 58 | let result = beautify(input, settings); 59 | expect(result).toBe(input); 60 | }); 61 | 62 | it('package ends with ; (same line)', function () { 63 | let settings = GetDefaultSettings(); 64 | let input = "PACKAGE p IS NEW work.p_template;\r\nUSE p.ALL;"; 65 | let result = beautify(input, settings); 66 | expect(result).toBe(input); 67 | }); 68 | 69 | it('align in, out, inout, buffer', function () { 70 | let settings = GetDefaultSettings(); 71 | settings.SignAlignSettings.isAll = true; 72 | let input = "Incr, Load, Clock : IN BIT;\r\nCarry : OUT BIT;\r\nData_Out : BUFFER bit_vector(7 DOWNTO 0);\r\nData_In : IN bit_vector(7 DOWNTO 0)"; 73 | let result = beautify(input, settings); 74 | expect(result).toBe(input); 75 | }); 76 | 77 | it('align sign in PORT & GENERIC', function () { 78 | let new_line_after_symbols: NewLineSettings = new NewLineSettings(); 79 | new_line_after_symbols.newLineAfter = ["then", ";"]; 80 | let settings = getDefaultBeautifierSettings(new_line_after_symbols); 81 | settings.SignAlignSettings = new signAlignSettings(true, false, "global", ["PORT", "GENERIC"]); 82 | let input = "entity p is\r\n generic\r\n (\r\n -- INCLK\r\n INCLK0_INPUT_FREQUENCY : natural;\r\n\r\n -- CLK1\r\n CLK1_DIVIDE_BY : natural := 1;\r\n CLK1_MULTIPLY_BY : unnatural:= 1;\r\n CLK1_PHASE_SHIFT : string := \"0\"\r\n );\r\n port\r\n (\r\n inclk0 : in bit := '0';\r\n c0 : out bit;\r\n c1 : out bit \r\n );\r\nEND pll;"; 83 | let expected = "ENTITY p IS\r\n GENERIC (\r\n -- INCLK\r\n INCLK0_INPUT_FREQUENCY : NATURAL;\r\n\r\n -- CLK1\r\n CLK1_DIVIDE_BY : NATURAL := 1;\r\n CLK1_MULTIPLY_BY : unnatural := 1;\r\n CLK1_PHASE_SHIFT : STRING := \"0\"\r\n );\r\n PORT (\r\n inclk0 : IN BIT := '0';\r\n c0 : OUT BIT;\r\n c1 : OUT BIT\r\n );\r\nEND pll;"; 84 | let result = beautify(input, settings); 85 | expect(result).toBe(expected); 86 | }); 87 | 88 | it('align signs in all places', function () { 89 | let setting = getDefaultBeautifierSettings(new NewLineSettings()); 90 | setting.SignAlignSettings = new signAlignSettings(false, true, "", []); 91 | setting.NewLineSettings.newLineAfter = ["then", ";", "generic", "port"]; 92 | setting.NewLineSettings.noNewLineAfter = []; 93 | let input = "entity a is\r\n port ( w : in bit;\r\n w_s : out bit; ) ;\r\nend a ;\r\narchitecture b of a is\r\nbegin\r\n process ( w )\r\n variable t : bit;\r\n variable bcd : bit;\r\nbegin\r\n b(2 downto 0) := w(7 downto 5) ;\r\n t := w(4 downto 0) ;\r\n w_s <= b(11 downto 8) ;\r\n w <= b(3 downto 0) ;\r\nend process ;\r\nend b;"; 94 | let expected = "ENTITY a IS\r\n PORT\r\n (\r\n w : IN BIT;\r\n w_s : OUT BIT;\r\n );\r\nEND a;\r\nARCHITECTURE b OF a IS\r\nBEGIN\r\n PROCESS (w)\r\n VARIABLE t : BIT;\r\n VARIABLE bcd : BIT;\r\n BEGIN\r\n b(2 DOWNTO 0) := w(7 DOWNTO 5);\r\n t := w(4 DOWNTO 0);\r\n w_s <= b(11 DOWNTO 8);\r\n w <= b(3 DOWNTO 0);\r\n END PROCESS;\r\nEND b;"; 95 | let result = beautify(input, setting); 96 | expect(result).toBe(expected); 97 | }); 98 | 99 | it('semicolon blocks are aligned', function () { 100 | let settings = GetDefaultSettings(); 101 | let input = 'OUT <= In0 AFTER 2ns WHEN "00",\r\n In1 AFTER 2ns WHEN "01",\r\n In2 AFTER 2ns WHEN "10",\r\n In3 AFTER 2ns WHEN "11";'; 102 | let result = beautify(input, settings); 103 | expect(result).toBe(input); 104 | }); 105 | 106 | it('align comments', function () { 107 | let settings = GetDefaultSettings(); 108 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], true); 109 | let input = 'test := loooong; -- test\r\ntest := short; -- test'; 110 | let expected = 'test := loooong; -- test\r\ntest := short; -- test'; 111 | let result = beautify(input, settings); 112 | expect(result).toBe(expected); 113 | }); 114 | 115 | it('do not align comments', function () { 116 | let settings = GetDefaultSettings(); 117 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 118 | let input = 'test := loooong; -- test\r\ntest := short; -- test'; 119 | let result = beautify(input, settings); 120 | expect(result).toBe(input); 121 | }); 122 | 123 | it('support invalid line', function () { 124 | let settings = GetDefaultSettings(); 125 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 126 | let input = '(5 * 32 - 1 DOWNTO 0) := (\r\n);'; 127 | let result = beautify(input, settings); 128 | expect(result).toBe(input); 129 | }); 130 | 131 | it('align initial values for constant', function () { 132 | let settings = GetDefaultSettings(); 133 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 134 | let input = 'CONSTANT ADDR_MATCH : STD_LOGIC_VECTOR(5 * 32 - 1 DOWNTO 0) := (\r\n X"00000000" &\r\n X"00010000" &\r\n X"00020000" &\r\n X"00030000" &\r\n X"00040000"\r\n);'; 135 | let result = beautify(input, settings); 136 | expect(result).toBe(input); 137 | }); 138 | 139 | it('one-line initial values for constant', function () { 140 | let settings = GetDefaultSettings(); 141 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 142 | let input = "CONSTANT Vcc : SIGNAL := '1'; --logic 1 constant\r\nCONSTANT zero4 : bit_vector(0 TO 3) := ('0', '0', '0', '0');\r\nCONSTANT Vcc : SIGNAL := '1'; --logic 1 constant"; 143 | let result = beautify(input, settings); 144 | expect(result).toBe(input); 145 | }); 146 | 147 | it('indent assignment statement (multiline, no comment)', function () { 148 | let settings = GetDefaultSettings(); 149 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 150 | let input = "CONSTANT Vcc : SIGNAL :=\r\n'1'; --logic 1 constant\r\nCONSTANT zero4 : bit_vector(0 TO 3) :=\r\n('0', '0', '0', '0');"; 151 | let output = "CONSTANT Vcc : SIGNAL :=\r\n '1'; --logic 1 constant\r\nCONSTANT zero4 : bit_vector(0 TO 3) :=\r\n ('0', '0', '0', '0');"; 152 | let result = beautify(input, settings); 153 | expect(result).toBe(output); 154 | }) 155 | 156 | it('indent assignment statement (with comment)', function () { 157 | let settings = GetDefaultSettings(); 158 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 159 | let input = "CONSTANT Vcc : SIGNAL := --logic 1 constant\r\n'1';\r\nCONSTANT zero4 : bit_vector(0 TO 3) :=--test\r\n('0', '0', '0', '0');"; 160 | let output = "CONSTANT Vcc : SIGNAL := --logic 1 constant\r\n '1';\r\nCONSTANT zero4 : bit_vector(0 TO 3) := --test\r\n ('0', '0', '0', '0');"; 161 | let result = beautify(input, settings); 162 | expect(result).toBe(output); 163 | }) 164 | 165 | it('indent assignment statement (multi line)', function () { 166 | let settings = GetDefaultSettings(); 167 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 168 | let input = [ 169 | "CONSTANT ALMOST_EMPTY_OFFSET : bit_vector(12 DOWNTO 0) :=", 170 | " to_bitvector(STD_ULOGIC_VECTOR(to_unsigned(C_M_AXI_BURST_LEN - 1, 13)));", 171 | "CONSTANT ALMOST_FULL_OFFSET : bit_vector(12 DOWNTO 0) :=", 172 | " to_bitvector(STD_ULOGIC_VECTOR(to_unsigned(C_M_AXI_BURST_LEN - 1, 13)));" 173 | ]; 174 | let output = [ 175 | "CONSTANT ALMOST_EMPTY_OFFSET : bit_vector(12 DOWNTO 0) :=", 176 | " to_bitvector(STD_ULOGIC_VECTOR(to_unsigned(C_M_AXI_BURST_LEN - 1, 13)));", 177 | "CONSTANT ALMOST_FULL_OFFSET : bit_vector(12 DOWNTO 0) :=", 178 | " to_bitvector(STD_ULOGIC_VECTOR(to_unsigned(C_M_AXI_BURST_LEN - 1, 13)));" 179 | ]; 180 | let result = beautifyTestHelper(input, settings); 181 | expect(result).toStrictEqual(output); 182 | }) 183 | 184 | it('indent assignment statement (multi line (2))', function () { 185 | let settings = GetDefaultSettings(); 186 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 187 | let input = [ 188 | "CONSTANT ALMOST_EMPTY_OFFSET : bit_vector(12 DOWNTO 0) :=", 189 | "to_bitvector(", 190 | "STD_ULOGIC_VECTOR(", 191 | "to_unsigned(", 192 | "C_M_AXI_BURST_LEN - 1, 13)));", 193 | "CONSTANT ALMOST_FULL_OFFSET : bit_vector(12 DOWNTO 0) :=", 194 | "to_bitvector(", 195 | "STD_ULOGIC_VECTOR(", 196 | "to_unsigned(", 197 | "C_M_AXI_BURST_LEN - 1, 13)));" 198 | ]; 199 | let output = [ 200 | "CONSTANT ALMOST_EMPTY_OFFSET : bit_vector(12 DOWNTO 0) :=", 201 | " to_bitvector(", 202 | " STD_ULOGIC_VECTOR(", 203 | " to_unsigned(", 204 | " C_M_AXI_BURST_LEN - 1, 13)));", 205 | "CONSTANT ALMOST_FULL_OFFSET : bit_vector(12 DOWNTO 0) :=", 206 | " to_bitvector(", 207 | " STD_ULOGIC_VECTOR(", 208 | " to_unsigned(", 209 | " C_M_AXI_BURST_LEN - 1, 13)));" 210 | ]; 211 | let result = beautifyTestHelper(input, settings); 212 | expect(result).toStrictEqual(output); 213 | }) 214 | 215 | it('indent PORT map', function () { 216 | let settings = GetDefaultSettings(); 217 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 218 | let input = [ 219 | "test_iobuf : component IOBUF", 220 | "port map(", 221 | " I => '0',", 222 | " T => '0'", 223 | ");", 224 | "test_iobuf : component IOBUF", 225 | " port map(", 226 | " I => '0',", 227 | " T => '0'", 228 | ");" 229 | ]; 230 | let output = [ 231 | "test_iobuf : COMPONENT IOBUF", 232 | " PORT MAP(", 233 | " I => '0',", 234 | " T => '0'", 235 | " );", 236 | "test_iobuf : COMPONENT IOBUF", 237 | " PORT MAP(", 238 | " I => '0',", 239 | " T => '0'", 240 | " );" 241 | ]; 242 | let result = beautifyTestHelper(input, settings); 243 | expect(result).toStrictEqual(output); 244 | }) 245 | 246 | it('indent PORT map (2)', function () { 247 | let settings = GetDefaultSettings(); 248 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 249 | let input = [ 250 | "i_Mux1 : entity work.T15_Mux(rtl) port map(", 251 | " Sel => Sel,", 252 | "Sig1 => Sig1,", 253 | "Output => Output);", 254 | "", 255 | "-- Testbench process", 256 | "process is", 257 | "begin", 258 | "wait for 10 ns;", 259 | "Sel <= Sel + 1;", 260 | "wait for 10 ns;", 261 | "Sel <= \"UU\";", 262 | "wait;", 263 | "end process;" 264 | ]; 265 | let output = [ 266 | "i_Mux1 : ENTITY work.T15_Mux(rtl) PORT MAP(", 267 | " Sel => Sel,", 268 | " Sig1 => Sig1,", 269 | " Output => Output);", 270 | "", 271 | "-- Testbench process", 272 | "PROCESS IS", 273 | "BEGIN", 274 | " WAIT FOR 10 ns;", 275 | " Sel <= Sel + 1;", 276 | " WAIT FOR 10 ns;", 277 | " Sel <= \"UU\";", 278 | " WAIT;", 279 | "END PROCESS;" 280 | ]; 281 | let result = beautifyTestHelper(input, settings); 282 | expect(result).toStrictEqual(output); 283 | 284 | }); 285 | 286 | it('multiline assignment', function () { 287 | let settings = GetDefaultSettings(); 288 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 289 | let input = [ 290 | "test", 291 | " := test" 292 | ]; 293 | let output = [ 294 | "test", 295 | " := test" // is this desired? 296 | ]; 297 | let result = beautifyTestHelper(input, settings); 298 | expect(result).toStrictEqual(output); 299 | }); 300 | 301 | it('align <= => signs', function () { 302 | let settings = GetDefaultSettings(); 303 | settings.SignAlignSettings = new signAlignSettings(false, true, "", [], false); 304 | let input = [ 305 | "a <= (b => '000'); -- test", 306 | "looong <= (others => '0'); -- test" 307 | ]; 308 | let output = [ 309 | "a <= (b => '000'); -- test", 310 | "looong <= (OTHERS => '0'); -- test" // does comment need to be aligned? 311 | ]; 312 | let result = beautifyTestHelper(input, settings); 313 | expect(result).toStrictEqual(output); 314 | }); 315 | }); 316 | 317 | function beautifyTestHelper(array: Array, settings: BeautifierSettings): Array { 318 | let input = array.join("\r\n"); 319 | let result = beautify(input, settings); 320 | return result.split("\r\n"); 321 | } 322 | 323 | function GetDefaultSettings(indentation: string = " "): BeautifierSettings { 324 | let new_line_after_symbols = new NewLineSettings(); 325 | new_line_after_symbols.newLineAfter = ["then", ";"]; 326 | new_line_after_symbols.noNewLineAfter = ["generic"]; 327 | let signAligns = new signAlignSettings(false, false, "", []); 328 | let settings = getDefaultBeautifierSettings(new_line_after_symbols, signAligns, indentation); 329 | return settings; 330 | } 331 | 332 | function getDefaultBeautifierSettings(newLineSettings: NewLineSettings, signAlignSettings: signAlignSettings = null, indentation: string = " "): BeautifierSettings { 333 | return new BeautifierSettings(false, false, false, signAlignSettings, "uppercase", "uppercase", indentation, newLineSettings, "\r\n", false); 334 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | VHDL Beautifier, Formatter Online 6 | 7 | 8 | 9 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |

VHDL Beautifier, Formatter

34 |
    35 |
  • 36 |
    ℹ 37 | Beautify and format your VHDL code online 38 |
    39 | 45 |
  • 46 |
  • 47 |
    ⚠ 48 | Please make a backup before you replace your code! 49 |
    50 | 56 |
  • 57 |
  • 58 |
    😸 59 | Proper formatting makes code easier to read and understand. 60 |
    61 | 67 |
  • 68 |
  • 69 | Report 70 | 🐞 bug 71 |
  • 72 |
  • 73 | Source code 74 |
  • 75 |
76 |
77 |
78 | 79 |
80 |
81 |
82 | 83 | 84 |
85 |
Keyword case: 86 | | 88 | | 90 | 92 |
93 | e.g. begin, case, when 94 |
95 |
96 |
Type name case: 97 | | 99 | | 101 | 103 |
104 | e.g. boolean, natural, string 105 |
106 |
107 |
108 |
109 | New line after 110 |
111 | THEN 112 | 114 | 116 | 118 |
119 |
120 | semicolon ";" 121 | 123 | 125 | 127 |
128 |
129 | ELSE 130 | 132 | 134 | 136 |
137 |
138 | PORT | PORT MAP 139 | 141 | 143 | 145 |
146 |
147 | GENERIC 148 | 150 | 152 | 154 |
155 |
156 |
157 | 158 | | 159 |
160 |
161 | 162 | | 163 |
164 |
165 | 166 | 167 |
168 |
169 |
170 | 171 | 172 |
173 |
174 | Sign Alignment 175 |
176 | Align signs in 177 |
178 | 179 | 180 |
181 |
182 | 183 |
184 |
185 | 186 | 187 |
188 |
189 | 190 | 191 |
192 |
193 |
194 | 195 | 196 |
197 |
198 | | Mode: 199 | 201 | 203 |
204 |
205 |
206 | 207 | 208 |
209 |
210 |
211 |
212 |
213 | 214 | 215 |
(tab is \t) 216 | ( 217 | four blankspaces) 218 |
219 |
220 | End of line: 221 | ( 222 | one \r & one \n) 223 |
224 |
225 |
226 | 227 | 228 |
229 |
230 |
231 | 232 | 233 |
234 |
235 | 236 | 237 |
238 |
239 |
240 | 244 | 245 | 246 | 247 |
248 | 249 | 250 |
251 |
252 |
253 |

254 |  output sample 
255 | 
256 | LIBRARY IEEE; -- declare the library
257 | USE IEEE.std_logic_1164.ALL;
258 | USE IEEE.std_logic_arith.ALL;
259 |  (All reserved words are in capital) 
260 |  (All indents are in the right place) 
261 | ---------------------------------------------------------------
262 | 
263 | ENTITY example IS
264 | 	PORT (
265 | 		rst                   : IN std_logic;
266 | 		clk                   : IN std_logic;
267 | 		example_of_long_words : OUT std_logic_vector(3 DOWNTO 0)
268 | 		 (Align signs in PORT() aligns these colons) 
269 | 	);
270 | END example;
271 | 
272 | ARCHITECTURE EXA OF example IS
273 | 	ALIAS slv IS std_logic_vector;
274 | 	SUBTYPE bit4 IS slv(3 DOWNTO 0);  (Check ALIAS replaces all "std_logic_vector" with "slv") 
275 | 	 
276 | BEGIN
277 | 	REPORT "Hello World";  (Remove REPORT) 
278 | 	 
279 | 	stages : PROCESS (rst, clk)
280 | 	BEGIN
281 | 		 
282 | 		IF (rst = '0') THEN
283 | 			CASE bit4 IS
284 | 				WHEN "0000" => bit4 <= "0001";
285 | 				WHEN "0001" => bit4 <= "0100";
286 | 				WHEN "0010" => bit4 <= "0010";
287 | 				WHEN "0100" => bit4 <= "0000";
288 | 				WHEN OTHERS => 
289 | 					REPORT "Are there any more cases?";  (Remove REPORT) 
290 | 			END CASE;
291 | 		ELSIF (clk'event AND clk = '1') THEN
292 | 			IF (bit4 = '0111') THEN
293 | 				bit4 <= "0000";
294 | 			ELSE
295 | 				bit4 <= "1111";
296 | 			END IF;
297 | 			-- Sample comments 1;  
298 | 			 -- Sample comments 2;   (Remove comments) 
299 | 		END IF;
300 | 		 
301 | 	END PROCESS;
302 | END EXA;
303 | 304 |
305 | 308 | 309 | 310 | 311 | 595 | 596 | 597 | -------------------------------------------------------------------------------- /VHDLFormatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | 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; 4 | let isTesting = false; 5 | const ILEscape = "@@"; 6 | const ILCommentPrefix = ILEscape + "comments"; 7 | const ILIndentedReturnPrefix = ILEscape; 8 | const ILQuote = "⨵"; 9 | const ILSingleQuote = "⦼"; 10 | const ILBackslash = "⨸"; 11 | const ILSemicolon = "⨴"; 12 | var FormatMode; 13 | (function (FormatMode) { 14 | FormatMode[FormatMode["Default"] = 0] = "Default"; 15 | FormatMode[FormatMode["EndsWithSemicolon"] = 1] = "EndsWithSemicolon"; 16 | FormatMode[FormatMode["CaseWhen"] = 2] = "CaseWhen"; 17 | FormatMode[FormatMode["IfElse"] = 3] = "IfElse"; 18 | FormatMode[FormatMode["PortGeneric"] = 4] = "PortGeneric"; 19 | })(FormatMode || (FormatMode = {})); 20 | let Mode = FormatMode.Default; 21 | class NewLineSettings { 22 | constructor() { 23 | this.newLineAfter = []; 24 | this.noNewLineAfter = []; 25 | } 26 | newLineAfterPush(keyword) { 27 | this.newLineAfter.push(keyword); 28 | } 29 | noNewLineAfterPush(keyword) { 30 | this.noNewLineAfter.push(keyword); 31 | } 32 | push(keyword, addNewLine) { 33 | let str = addNewLine.toLowerCase(); 34 | if (str == "none") { 35 | return; 36 | } 37 | else if (!str.startsWith("no")) { 38 | this.newLineAfterPush(keyword); 39 | } 40 | else { 41 | this.noNewLineAfterPush(keyword); 42 | } 43 | } 44 | } 45 | exports.NewLineSettings = NewLineSettings; 46 | function ConstructNewLineSettings(dict) { 47 | let settings = new NewLineSettings(); 48 | for (let key in dict) { 49 | settings.push(key, dict[key]); 50 | } 51 | return settings; 52 | } 53 | String.prototype.regexCount = function (pattern) { 54 | if (pattern.flags.indexOf("g") < 0) { 55 | pattern = new RegExp(pattern.source, pattern.flags + "g"); 56 | } 57 | return (this.match(pattern) || []).length; 58 | }; 59 | String.prototype.count = function (text) { 60 | return this.split(text).length - 1; 61 | }; 62 | String.prototype.regexStartsWith = function (pattern) { 63 | var searchResult = this.search(pattern); 64 | return searchResult == 0; 65 | }; 66 | String.prototype.regexIndexOf = function (pattern, startIndex) { 67 | startIndex = startIndex || 0; 68 | var searchResult = this.substr(startIndex).search(pattern); 69 | return (-1 === searchResult) ? -1 : searchResult + startIndex; 70 | }; 71 | String.prototype.regexLastIndexOf = function (pattern, startIndex) { 72 | pattern = (pattern.global) ? pattern : 73 | new RegExp(pattern.source, 'g' + (pattern.ignoreCase ? 'i' : '') + (pattern.multiline ? 'm' : '')); 74 | if (typeof (startIndex) === 'undefined') { 75 | startIndex = this.length; 76 | } 77 | else if (startIndex < 0) { 78 | startIndex = 0; 79 | } 80 | const stringToWorkWith = this.substring(0, startIndex + 1); 81 | let lastIndexOf = -1; 82 | let nextStop = 0; 83 | let result; 84 | while ((result = pattern.exec(stringToWorkWith)) != null) { 85 | lastIndexOf = result.index; 86 | pattern.lastIndex = ++nextStop; 87 | } 88 | return lastIndexOf; 89 | }; 90 | String.prototype.reverse = function () { 91 | return this.split('').reverse().join(''); 92 | }; 93 | String.prototype.convertToRegexBlockWords = function () { 94 | let result = new RegExp("(" + this + ")([^\\w]|$)"); 95 | return result; 96 | }; 97 | Array.prototype.convertToRegexBlockWords = function () { 98 | let wordsStr = this.join("|"); 99 | let result = new RegExp("(" + wordsStr + ")([^\\w]|$)"); 100 | return result; 101 | }; 102 | function EscapeComments(arr) { 103 | var comments = []; 104 | var count = 0; 105 | for (var i = 0; i < arr.length; i++) { 106 | var line = arr[i]; 107 | var commentStartIndex = line.indexOf("--"); 108 | if (commentStartIndex >= 0) { 109 | comments.push(line.substr(commentStartIndex)); 110 | arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count; 111 | count++; 112 | } 113 | } 114 | var isInComment = false; 115 | var commentRegex = new RegExp("(?<=" + ILCommentPrefix + "[\\d]+)."); 116 | for (var i = 0; i < arr.length; i++) { 117 | var commentStartIndex = 0; 118 | var hasComment = true; 119 | var commentEndInlineIndex = 0; 120 | while (hasComment) { 121 | var line = arr[i]; 122 | if (!isInComment) { 123 | commentStartIndex = line.indexOf("/*"); 124 | var commentEndIndex = line.indexOf("*/", commentStartIndex); 125 | if (commentStartIndex >= 0) { 126 | if (commentEndIndex >= 0) { 127 | commentEndInlineIndex = commentEndIndex + 2; 128 | isInComment = false; 129 | comments.push(line.substring(commentStartIndex, commentEndInlineIndex)); 130 | arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count + line.substr(commentEndInlineIndex); 131 | count++; 132 | hasComment = true; 133 | if (commentStartIndex + 2 == line.length) { 134 | hasComment = false; 135 | } 136 | } 137 | else { 138 | isInComment = true; 139 | comments.push(line.substr(commentStartIndex)); 140 | arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count; 141 | count++; 142 | hasComment = false; 143 | } 144 | } 145 | else { 146 | hasComment = false; 147 | } 148 | continue; 149 | } 150 | if (isInComment) { 151 | var lastCommentEndIndex = line.regexLastIndexOf(commentRegex, line.length); 152 | if (commentStartIndex == 0) { 153 | var commentEndIndex = line.indexOf("*/", lastCommentEndIndex); 154 | } 155 | else { 156 | var commentEndIndex = line.indexOf("*/", commentStartIndex); 157 | } 158 | if (commentEndIndex >= 0) { 159 | isInComment = false; 160 | comments.push(line.substr(0, commentEndIndex + 2)); 161 | arr[i] = ILCommentPrefix + count + line.substr(commentEndIndex + 2); 162 | count++; 163 | hasComment = true; 164 | } 165 | else { 166 | comments.push(line); 167 | arr[i] = ILCommentPrefix + count; 168 | count++; 169 | hasComment = false; 170 | } 171 | } 172 | } 173 | } 174 | return comments; 175 | } 176 | function ToLowerCases(arr) { 177 | for (var i = 0; i < arr.length; i++) { 178 | arr[i] = arr[i].toLowerCase(); 179 | } 180 | } 181 | function ToUpperCases(arr) { 182 | for (var i = 0; i < arr.length; i++) { 183 | arr[i] = arr[i].toUpperCase(); 184 | } 185 | } 186 | function ToCamelCases(arr) { 187 | for (var i = 0; i < arr.length; i++) { 188 | arr[i] = arr[i].charAt(0) + arr[i].slice(1).toLowerCase(); 189 | } 190 | } 191 | function ReplaceKeyWords(text, keywords) { 192 | for (var k = 0; k < keywords.length; k++) { 193 | text = text.replace(new RegExp("([^a-zA-Z0-9_@]|^)" + keywords[k] + "([^a-zA-Z0-9_]|$)", 'gi'), "$1" + keywords[k] + "$2"); 194 | } 195 | return text; 196 | } 197 | function SetKeywordCase(input, keywordcase, keywords) { 198 | let inputcase = keywordcase.toLowerCase(); 199 | switch (inputcase) { 200 | case "lowercase": 201 | ToLowerCases(keywords); 202 | break; 203 | case "defaultcase": 204 | ToCamelCases(keywords); 205 | break; 206 | case "uppercase": 207 | ToUpperCases(keywords); 208 | } 209 | input = ReplaceKeyWords(input, keywords); 210 | return input; 211 | } 212 | function SetNewLinesAfterSymbols(text, newLineSettings) { 213 | if (newLineSettings == null) { 214 | return text; 215 | } 216 | if (newLineSettings.newLineAfter != null) { 217 | newLineSettings.newLineAfter.forEach(symbol => { 218 | let upper = symbol.toUpperCase(); 219 | var rexString = "(" + upper + ")[ ]?([^ \r\n@])"; 220 | let regex = null; 221 | if (upper.regexStartsWith(/\w/)) { 222 | regex = new RegExp("\\b" + rexString, "g"); 223 | } 224 | else { 225 | regex = new RegExp(rexString, "g"); 226 | } 227 | text = text.replace(regex, '$1\r\n$2'); 228 | if (upper == "PORT") { 229 | text = text.replace(/\bPORT\b\s+MAP/, "PORT MAP"); 230 | } 231 | }); 232 | } 233 | if (newLineSettings.noNewLineAfter != null) { 234 | newLineSettings.noNewLineAfter.forEach(symbol => { 235 | let rexString = "(" + symbol.toUpperCase() + ")[ \r\n]+([^@])"; 236 | let regex = null; 237 | if (symbol.regexStartsWith(/\w/)) { 238 | regex = new RegExp("\\b" + rexString, "g"); 239 | text = text.replace(regex, '$1 $2'); 240 | } 241 | else { 242 | regex = new RegExp(rexString, "g"); 243 | } 244 | text = text.replace(regex, '$1 $2'); 245 | }); 246 | } 247 | return text; 248 | } 249 | exports.SetNewLinesAfterSymbols = SetNewLinesAfterSymbols; 250 | class signAlignSettings { 251 | constructor(isRegional, isAll, mode, keyWords, alignComments = false) { 252 | this.isRegional = isRegional; 253 | this.isAll = isAll; 254 | this.mode = mode; 255 | this.keyWords = keyWords; 256 | this.alignComments = alignComments; 257 | } 258 | } 259 | exports.signAlignSettings = signAlignSettings; 260 | class BeautifierSettings { 261 | constructor(removeComments, removeReport, checkAlias, signAlignSettings, keywordCase, typeNameCase, indentation, newLineSettings, endOfLine, addNewLine) { 262 | this.RemoveComments = removeComments; 263 | this.RemoveAsserts = removeReport; 264 | this.CheckAlias = checkAlias; 265 | this.SignAlignSettings = signAlignSettings; 266 | this.KeywordCase = keywordCase; 267 | this.TypeNameCase = typeNameCase; 268 | this.Indentation = indentation; 269 | this.NewLineSettings = newLineSettings; 270 | this.EndOfLine = endOfLine; 271 | this.AddNewLine = addNewLine; 272 | } 273 | } 274 | exports.BeautifierSettings = BeautifierSettings; 275 | let KeyWords = ["ABS", "ACCESS", "AFTER", "ALIAS", "ALL", "AND", "ARCHITECTURE", "ARRAY", "ASSERT", "ATTRIBUTE", "BEGIN", "BLOCK", "BODY", "BUFFER", "BUS", "CASE", "COMPONENT", "CONFIGURATION", "CONSTANT", "CONTEXT", "COVER", "DISCONNECT", "DOWNTO", "DEFAULT", "ELSE", "ELSIF", "END", "ENTITY", "EXIT", "FAIRNESS", "FILE", "FOR", "FORCE", "FUNCTION", "GENERATE", "GENERIC", "GROUP", "GUARDED", "IF", "IMPURE", "IN", "INERTIAL", "INOUT", "IS", "LABEL", "LIBRARY", "LINKAGE", "LITERAL", "LOOP", "MAP", "MOD", "NAND", "NEW", "NEXT", "NOR", "NOT", "NULL", "OF", "ON", "OPEN", "OR", "OTHERS", "OUT", "PACKAGE", "PORT", "POSTPONED", "PROCEDURE", "PROCESS", "PROPERTY", "PROTECTED", "PURE", "RANGE", "RECORD", "REGISTER", "REJECT", "RELEASE", "REM", "REPORT", "RESTRICT", "RESTRICT_GUARANTEE", "RETURN", "ROL", "ROR", "SELECT", "SEQUENCE", "SEVERITY", "SHARED", "SIGNAL", "SLA", "SLL", "SRA", "SRL", "STRONG", "SUBTYPE", "THEN", "TO", "TRANSPORT", "TYPE", "UNAFFECTED", "UNITS", "UNTIL", "USE", "VARIABLE", "VMODE", "VPROP", "VUNIT", "WAIT", "WHEN", "WHILE", "WITH", "XNOR", "XOR"]; 276 | let TypeNames = ["BOOLEAN", "BIT", "CHARACTER", "INTEGER", "TIME", "NATURAL", "POSITIVE", "STD_LOGIC", "STD_LOGIC_VECTOR", "STD_ULOGIC", "STD_ULOGIC_VECTOR", "STRING"]; 277 | function beautify(input, settings) { 278 | input = input.replace(/\r\n/g, "\n"); 279 | input = input.replace(/\n/g, "\r\n"); 280 | var arr = input.split("\r\n"); 281 | var comments = EscapeComments(arr); 282 | var backslashes = escapeText(arr, "\\\\[^\\\\]+\\\\", ILBackslash); 283 | let quotes = escapeText(arr, '"([^"]+)"', ILQuote); 284 | let singleQuotes = escapeText(arr, "'[^']'", ILSingleQuote); 285 | RemoveLeadingWhitespaces(arr); 286 | input = arr.join("\r\n"); 287 | if (settings.RemoveComments) { 288 | input = input.replace(/\r\n[ \t]*@@comments[0-9]+[ \t]*\r\n/g, '\r\n'); 289 | input = input.replace(/@@comments[0-9]+/g, ''); 290 | comments = []; 291 | } 292 | input = SetKeywordCase(input, "uppercase", KeyWords); 293 | input = SetKeywordCase(input, "uppercase", TypeNames); 294 | input = RemoveExtraNewLines(input); 295 | input = input.replace(/[\t ]+/g, ' '); 296 | input = input.replace(/\([\t ]+/g, '\('); 297 | input = input.replace(/[ ]+;/g, ';'); 298 | input = input.replace(/:[ ]*(PROCESS|ENTITY)/gi, ':$1'); 299 | arr = input.split("\r\n"); 300 | if (settings.RemoveAsserts) { 301 | RemoveAsserts(arr); //RemoveAsserts must be after EscapeQuotes 302 | } 303 | ReserveSemicolonInKeywords(arr); 304 | input = arr.join("\r\n"); 305 | input = input.replace(/\b(PORT|GENERIC)\b\s+MAP/g, '$1 MAP'); 306 | input = input.replace(/\b(PORT|PROCESS|GENERIC)\b[\s]*\(/g, '$1 ('); 307 | let newLineSettings = settings.NewLineSettings; 308 | if (newLineSettings != null) { 309 | input = SetNewLinesAfterSymbols(input, newLineSettings); 310 | arr = input.split("\r\n"); 311 | ApplyNoNewLineAfter(arr, newLineSettings.noNewLineAfter); 312 | input = arr.join("\r\n"); 313 | } 314 | input = input.replace(/([a-zA-Z0-9\); ])\);(@@comments[0-9]+)?@@end/g, '$1\r\n);$2@@end'); 315 | input = input.replace(/[ ]?([&=:\-\+|\*]|[<>]+)[ ]?/g, ' $1 '); 316 | input = input.replace(/(\d+e) +([+\-]) +(\d+)/g, '$1$2$3'); // fix exponential notation format broken by previous step 317 | input = input.replace(/[ ]?([,])[ ]?/g, '$1 '); 318 | input = input.replace(/[ ]?(['"])(THEN)/g, '$1 $2'); 319 | input = input.replace(/[ ]?(\?)?[ ]?(<|:|>|\/)?[ ]+(=)?[ ]?/g, ' $1$2$3 '); 320 | input = input.replace(/(IF)[ ]?([\(\)])/g, '$1 $2'); 321 | input = input.replace(/([\(\)])[ ]?(THEN)/gi, '$1 $2'); 322 | input = input.replace(/(^|[\(\)])[ ]?(AND|OR|XOR|XNOR)[ ]*([\(])/g, '$1 $2 $3'); 323 | input = input.replace(/ ([\-\*\/=+<>])[ ]*([\-\*\/=+<>]) /g, " $1$2 "); 324 | //input = input.replace(/\r\n[ \t]+--\r\n/g, "\r\n"); 325 | input = input.replace(/[ ]+/g, ' '); 326 | input = input.replace(/[ \t]+\r\n/g, "\r\n"); 327 | input = input.replace(/\r\n\r\n\r\n/g, '\r\n'); 328 | input = input.replace(/[\r\n\s]+$/g, ''); 329 | input = input.replace(/[ \t]+\)/g, ')'); 330 | input = input.replace(/\s*\)\s+RETURN\s+([\w]+;)/g, '\r\n) RETURN $1'); //function(..)\r\nreturn type; -> function(..\r\n)return type; 331 | 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; 332 | let keywordAndSignRegex = new RegExp("(\\b" + KeyWords.join("\\b|\\b") + "\\b) +([\\-+]) +(\\w)", "g"); 333 | input = input.replace(keywordAndSignRegex, "$1 $2$3"); // `WHEN - 2` -> `WHEN -2` 334 | input = input.replace(/([,|]) +([+\-]) +(\w)/g, '$1 $2$3'); // `1, - 2)` -> `1, -2)` 335 | input = input.replace(/(\() +([+\-]) +(\w)/g, '$1$2$3'); // `( - 2)` -> `(-2)` 336 | arr = input.split("\r\n"); 337 | let result = []; 338 | let block = new CodeBlock(arr); 339 | beautify3(block, result, settings, 0); 340 | var alignSettings = settings.SignAlignSettings; 341 | if (alignSettings != null && alignSettings.isAll) { 342 | AlignSigns(result, 0, result.length - 1, alignSettings.mode, alignSettings.alignComments); 343 | } 344 | arr = FormattedLineToString(result, settings.Indentation); 345 | input = arr.join("\r\n"); 346 | input = input.replace(/@@RETURN/g, "RETURN"); 347 | input = SetKeywordCase(input, settings.KeywordCase, KeyWords); 348 | input = SetKeywordCase(input, settings.TypeNameCase, TypeNames); 349 | input = replaceEscapedWords(input, quotes, ILQuote); 350 | input = replaceEscapedWords(input, singleQuotes, ILSingleQuote); 351 | input = replaceEscapedComments(input, comments, ILCommentPrefix); 352 | input = replaceEscapedWords(input, backslashes, ILBackslash); 353 | input = input.replace(new RegExp(ILSemicolon, "g"), ";"); 354 | input = input.replace(/@@[a-z]+/g, ""); 355 | var escapedTexts = new RegExp("[" + ILBackslash + ILQuote + ILSingleQuote + "]", "g"); 356 | input = input.replace(escapedTexts, ""); 357 | input = input.replace(/\r\n/g, settings.EndOfLine); 358 | if (settings.AddNewLine && !input.endsWith(settings.EndOfLine)) { 359 | input += settings.EndOfLine; 360 | } 361 | return input; 362 | } 363 | exports.beautify = beautify; 364 | function replaceEscapedWords(input, arr, prefix) { 365 | for (var i = 0; i < arr.length; i++) { 366 | var text = arr[i]; 367 | var regex = new RegExp("(" + prefix + "){" + text.length + "}"); 368 | input = input.replace(regex, text); 369 | } 370 | return input; 371 | } 372 | function replaceEscapedComments(input, arr, prefix) { 373 | for (var i = 0; i < arr.length; i++) { 374 | input = input.replace(prefix + i, arr[i]); 375 | } 376 | return input; 377 | } 378 | function RemoveLeadingWhitespaces(arr) { 379 | for (var i = 0; i < arr.length; i++) { 380 | arr[i] = arr[i].replace(/^\s+/, ""); 381 | } 382 | } 383 | class FormattedLine { 384 | constructor(line, indent) { 385 | this.Line = line; 386 | this.Indent = indent; 387 | } 388 | } 389 | exports.FormattedLine = FormattedLine; 390 | class CodeBlock { 391 | constructor(lines, start = 0, end = lines.length - 1) { 392 | this.lines = lines; 393 | this.start = start; 394 | this.end = end; 395 | this.parent = null; 396 | this.cursor = start; 397 | } 398 | _notifySplit(atLine) { 399 | if (this.start > atLine) 400 | this.start++; 401 | if (this.end >= atLine) 402 | this.end++; 403 | if (this.cursor >= atLine) 404 | this.cursor++; 405 | if (this.parent) 406 | this.parent._notifySplit(atLine); 407 | } 408 | splitLine(atLine, firstText, secondText) { 409 | this.lines[atLine] = firstText; 410 | this.lines.splice(atLine + 1, 0, secondText); 411 | this._notifySplit(atLine); 412 | } 413 | subBlock(start, end) { 414 | let newBlock = new CodeBlock(this.lines, start, end); 415 | newBlock.parent = this; 416 | return newBlock; 417 | } 418 | } 419 | exports.CodeBlock = CodeBlock; 420 | function FormattedLineToString(arr, indentation) { 421 | let result = []; 422 | if (arr == null) { 423 | return result; 424 | } 425 | if (indentation == null) { 426 | indentation = ""; 427 | } 428 | arr.forEach(i => { 429 | if (i instanceof FormattedLine) { 430 | if (i.Line.length > 0) { 431 | result.push((Array(i.Indent + 1).join(indentation)) + i.Line); 432 | } 433 | else { 434 | result.push(""); 435 | } 436 | } 437 | else { 438 | result = result.concat(FormattedLineToString(i, indentation)); 439 | } 440 | }); 441 | return result; 442 | } 443 | exports.FormattedLineToString = FormattedLineToString; 444 | function GetCloseparentheseEndIndex(block) { 445 | let openParentheseCount = 0; 446 | let closeParentheseCount = 0; 447 | let startIndex = block.cursor; 448 | for (; block.cursor <= block.end; block.cursor++) { 449 | let input = block.lines[block.cursor]; 450 | openParentheseCount += input.count("("); 451 | closeParentheseCount += input.count(")"); 452 | if (openParentheseCount > 0 453 | && openParentheseCount <= closeParentheseCount) { 454 | return; 455 | } 456 | } 457 | block.cursor = startIndex; 458 | } 459 | function beautifyPortGenericBlock(block, result, settings, indent, mode) { 460 | let startIndex = block.cursor; 461 | let firstLine = block.lines[startIndex]; 462 | let regex = new RegExp("[\\w\\s:]*(" + mode + ")([\\s]|$)"); 463 | if (!firstLine.regexStartsWith(regex)) { 464 | return; 465 | } 466 | let firstLineHasParenthese = firstLine.indexOf("(") >= 0; 467 | let secondLineHasParenthese = startIndex + 1 <= block.end && block.lines[startIndex + 1].startsWith("("); 468 | let hasParenthese = firstLineHasParenthese || secondLineHasParenthese; 469 | let blockBodyStartIndex = startIndex + (secondLineHasParenthese ? 1 : 0); 470 | if (hasParenthese) { 471 | GetCloseparentheseEndIndex(block); 472 | } 473 | let endIndex = block.cursor; 474 | let bodyBlock = block.subBlock(blockBodyStartIndex, endIndex); 475 | if (endIndex != startIndex && firstLineHasParenthese) { 476 | block.lines[startIndex] = block.lines[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); 477 | let newInputs = block.lines[startIndex].split("\r\n"); 478 | if (newInputs.length == 2) { 479 | bodyBlock.splitLine(startIndex, newInputs[0], newInputs[1]); 480 | } 481 | } 482 | else if (endIndex > startIndex + 1 && secondLineHasParenthese) { 483 | block.lines[startIndex + 1] = block.lines[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1'); 484 | let newInputs = block.lines[startIndex + 1].split("\r\n"); 485 | if (newInputs.length == 2) { 486 | bodyBlock.splitLine(startIndex + 1, newInputs[0], newInputs[1]); 487 | } 488 | } 489 | if (firstLineHasParenthese && block.lines[startIndex].indexOf("MAP") > 0) { 490 | block.lines[startIndex] = block.lines[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2('); 491 | } 492 | result.push(new FormattedLine(block.lines[startIndex], indent)); 493 | if (secondLineHasParenthese) { 494 | let secondLineIndent = indent; 495 | if (endIndex == startIndex + 1) { 496 | secondLineIndent++; 497 | } 498 | result.push(new FormattedLine(block.lines[startIndex + 1], secondLineIndent)); 499 | } 500 | beautify3(bodyBlock.subBlock(bodyBlock.start + 1, bodyBlock.end), result, settings, indent + 1); 501 | if (block.lines[block.cursor].startsWith(")")) { 502 | result[block.cursor].Indent--; 503 | bodyBlock.end--; 504 | } 505 | var alignSettings = settings.SignAlignSettings; 506 | if (alignSettings != null) { 507 | if (alignSettings.isRegional && !alignSettings.isAll 508 | && alignSettings.keyWords != null 509 | && alignSettings.keyWords.indexOf(mode) >= 0) { 510 | AlignSigns(result, bodyBlock.start + 1, bodyBlock.end, alignSettings.mode, alignSettings.alignComments); 511 | } 512 | } 513 | } 514 | exports.beautifyPortGenericBlock = beautifyPortGenericBlock; 515 | function AlignSigns(result, startIndex, endIndex, mode, alignComments = false) { 516 | AlignSign_(result, startIndex, endIndex, ":", mode); 517 | AlignSign_(result, startIndex, endIndex, ":=", mode); 518 | AlignSign_(result, startIndex, endIndex, "<=", mode); 519 | AlignSign_(result, startIndex, endIndex, "=>", mode); 520 | AlignSign_(result, startIndex, endIndex, "direction", mode); 521 | if (alignComments) { 522 | AlignSign_(result, startIndex, endIndex, "@@comments", mode); 523 | } 524 | } 525 | exports.AlignSigns = AlignSigns; 526 | function indexOfGroup(regex, input, group) { 527 | var match = regex.exec(input); 528 | if (match == null) { 529 | return -1; 530 | } 531 | var index = match.index; 532 | for (let i = 1; i < group; i++) { 533 | index += match[i].length; 534 | } 535 | return index; 536 | } 537 | function AlignSign_(result, startIndex, endIndex, symbol, mode) { 538 | let maxSymbolIndex = -1; 539 | let symbolIndices = {}; 540 | let startLine = startIndex; 541 | let labelAndKeywords = [ 542 | "([\\w\\s]*:(\\s)*PROCESS)", 543 | "([\\w\\s]*:(\\s)*POSTPONED PROCESS)", 544 | "([\\w\\s]*:\\s*$)", 545 | "([\\w\\s]*:.*\\s+GENERATE)" 546 | ]; 547 | let labelAndKeywordsStr = labelAndKeywords.join("|"); 548 | let labelAndKeywordsRegex = new RegExp("(" + labelAndKeywordsStr + ")([^\\w]|$)"); 549 | for (let i = startIndex; i <= endIndex; i++) { 550 | let line = result[i].Line; 551 | if (symbol == ":" && line.regexStartsWith(labelAndKeywordsRegex)) { 552 | continue; 553 | } 554 | let regex; 555 | if (symbol == "direction") { 556 | regex = new RegExp("(:\\s*)(IN|OUT|INOUT|BUFFER)(\\s+)(\\w)"); 557 | } 558 | else { 559 | regex = new RegExp("([\\s\\w\\\\]|^)" + symbol + "([\\s\\w\\\\]|$)"); 560 | } 561 | if (line.regexCount(regex) > 1) { 562 | continue; 563 | } 564 | let colonIndex; 565 | if (symbol == "direction") { 566 | colonIndex = indexOfGroup(regex, line, 4); 567 | } 568 | else { 569 | colonIndex = line.regexIndexOf(regex); 570 | } 571 | if (colonIndex > 0) { 572 | maxSymbolIndex = Math.max(maxSymbolIndex, colonIndex); 573 | symbolIndices[i] = colonIndex; 574 | } 575 | else if ((mode != "local" && !line.startsWith(ILCommentPrefix) && line.length != 0) 576 | || (mode == "local")) { 577 | if (startLine < i - 1) // if cannot find the symbol, a block of symbols ends 578 | { 579 | AlignSign(result, startLine, i - 1, symbol, maxSymbolIndex, symbolIndices); 580 | } 581 | maxSymbolIndex = -1; 582 | symbolIndices = {}; 583 | startLine = i; 584 | } 585 | } 586 | if (startLine < endIndex) // if cannot find the symbol, a block of symbols ends 587 | { 588 | AlignSign(result, startLine, endIndex, symbol, maxSymbolIndex, symbolIndices); 589 | } 590 | } 591 | function AlignSign(result, startIndex, endIndex, symbol, maxSymbolIndex = -1, symbolIndices = {}) { 592 | if (maxSymbolIndex < 0) { 593 | return; 594 | } 595 | for (let lineIndex in symbolIndices) { 596 | let symbolIndex = symbolIndices[lineIndex]; 597 | if (symbolIndex == maxSymbolIndex) { 598 | continue; 599 | } 600 | let line = result[lineIndex].Line; 601 | result[lineIndex].Line = line.substring(0, symbolIndex) 602 | + (Array(maxSymbolIndex - symbolIndex + 1).join(" ")) 603 | + line.substring(symbolIndex); 604 | } 605 | } 606 | exports.AlignSign = AlignSign; 607 | function beautifyCaseBlock(block, result, settings, indent) { 608 | if (!block.lines[block.cursor].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { 609 | return; 610 | } 611 | result.push(new FormattedLine(block.lines[block.cursor], indent)); 612 | block.cursor++; 613 | beautify3(block, result, settings, indent + 2); 614 | result[block.cursor].Indent = indent; 615 | } 616 | exports.beautifyCaseBlock = beautifyCaseBlock; 617 | function getSemicolonBlockEndIndex(block, settings) { 618 | let endIndex = block.cursor; 619 | let openBracketsCount = 0; 620 | let closeBracketsCount = 0; 621 | for (; block.cursor <= block.end; block.cursor++) { 622 | let input = block.lines[block.cursor]; 623 | let indexOfSemicolon = input.indexOf(";"); 624 | let splitIndex = indexOfSemicolon < 0 ? input.length : indexOfSemicolon + 1; 625 | let stringBeforeSemicolon = input.substring(0, splitIndex); 626 | let stringAfterSemicolon = input.substring(splitIndex); 627 | stringAfterSemicolon = stringAfterSemicolon.replace(new RegExp(ILCommentPrefix + "[0-9]+"), ""); 628 | openBracketsCount += stringBeforeSemicolon.count("("); 629 | closeBracketsCount += stringBeforeSemicolon.count(")"); 630 | if (indexOfSemicolon < 0) { 631 | continue; 632 | } 633 | if (openBracketsCount == closeBracketsCount) { 634 | endIndex = block.cursor; 635 | if (stringAfterSemicolon.trim().length > 0 && settings.NewLineSettings.newLineAfter.indexOf(";") >= 0) { 636 | block.splitLine(block.cursor, stringBeforeSemicolon, stringAfterSemicolon); 637 | } 638 | break; 639 | } 640 | } 641 | block.cursor = endIndex; 642 | } 643 | function beautifyComponentBlock(block, result, settings, indent) { 644 | let startIndex = block.cursor; 645 | for (; block.cursor <= block.end; block.cursor++) { 646 | if (block.lines[block.cursor].regexStartsWith(/END(\s|$)/)) { 647 | break; 648 | } 649 | } 650 | result.push(new FormattedLine(block.lines[startIndex], indent)); 651 | if (block.cursor != startIndex) { 652 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 653 | } 654 | } 655 | exports.beautifyComponentBlock = beautifyComponentBlock; 656 | function beautifyPackageIsNewBlock(block, result, settings, indent) { 657 | let startIndex = block.cursor; 658 | for (; block.cursor <= block.end; block.cursor++) { 659 | if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { 660 | break; 661 | } 662 | } 663 | result.push(new FormattedLine(block.lines[startIndex], indent)); 664 | if (block.cursor != startIndex) { 665 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 666 | } 667 | } 668 | exports.beautifyPackageIsNewBlock = beautifyPackageIsNewBlock; 669 | function beautifyVariableInitialiseBlock(block, result, settings, indent) { 670 | let startIndex = block.cursor; 671 | for (; block.cursor <= block.end; block.cursor++) { 672 | if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { 673 | break; 674 | } 675 | } 676 | result.push(new FormattedLine(block.lines[startIndex], indent)); 677 | if (block.cursor != startIndex) { 678 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 679 | } 680 | } 681 | exports.beautifyVariableInitialiseBlock = beautifyVariableInitialiseBlock; 682 | function beautifySemicolonBlock(block, result, settings, indent) { 683 | let startIndex = block.cursor; 684 | getSemicolonBlockEndIndex(block, settings); 685 | result.push(new FormattedLine(block.lines[startIndex], indent)); 686 | if (block.cursor != startIndex) { 687 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 688 | alignSignalAssignmentBlock(settings, block.lines, startIndex, block.cursor, result); 689 | } 690 | } 691 | exports.beautifySemicolonBlock = beautifySemicolonBlock; 692 | function alignSignalAssignmentBlock(settings, inputs, startIndex, endIndex, result) { 693 | if (settings.Indentation.replace(/ +/g, "").length == 0) { 694 | let reg = new RegExp("^([\\w\\\\]+[\\s]*<=\\s*)"); 695 | let match = reg.exec(inputs[startIndex]); 696 | if (match != null) { 697 | let length = match[0].length; 698 | let prefixLength = length - settings.Indentation.length; 699 | let prefix = new Array(prefixLength + 1).join(" "); 700 | for (let i = startIndex + 1; i <= endIndex; i++) { 701 | let fl = result[i]; 702 | fl.Line = prefix + fl.Line; 703 | } 704 | } 705 | } 706 | } 707 | function beautify3(block, result, settings, indent) { 708 | let regexOneLineBlockKeyWords = new RegExp(/(PROCEDURE)[^\w](?!.+[^\w]IS([^\w]|$))/); //match PROCEDURE..; but not PROCEDURE .. IS; 709 | let regexFunctionMultiLineBlockKeyWords = new RegExp(/(FUNCTION|IMPURE FUNCTION)[^\w](?=.+[^\w]IS([^\w]|$))/); //match FUNCTION .. IS; but not FUNCTION 710 | let blockMidKeyWords = ["BEGIN"]; 711 | let blockStartsKeyWords = [ 712 | "IF", 713 | "CASE", 714 | "ARCHITECTURE", 715 | "PROCEDURE", 716 | "PACKAGE", 717 | "(([\\w\\s]*:)?(\\s)*PROCESS)", 718 | "(([\\w\\s]*:)?(\\s)*POSTPONED PROCESS)", 719 | "(.*\\s*PROTECTED)", 720 | "(COMPONENT)", 721 | "(ENTITY(?!.+;))", 722 | "FOR", 723 | "WHILE", 724 | "LOOP", 725 | "(.*\\s*GENERATE)", 726 | "(CONTEXT[\\w\\s\\\\]+IS)", 727 | "(CONFIGURATION(?!.+;))", 728 | "BLOCK", 729 | "UNITS", 730 | "\\w+\\s+\\w+\\s+IS\\s+RECORD" 731 | ]; 732 | let blockEndsKeyWords = ["END", ".*\\)\\s*RETURN\\s+[\\w]+;"]; 733 | let indentedEndsKeyWords = [ILIndentedReturnPrefix + "RETURN\\s+\\w+;"]; 734 | let blockEndsWithSemicolon = [ 735 | "(WITH\\s+[\\w\\s\\\\]+SELECT)", 736 | "([\\w\\\\]+[\\s]*<=)", 737 | "([\\w\\\\]+[\\s]*:=)", 738 | "FOR\\s+[\\w\\s,]+:\\s*\\w+\\s+USE", 739 | "REPORT" 740 | ]; 741 | let newLineAfterKeyWordsStr = blockStartsKeyWords.join("|"); 742 | let regexBlockMidKeyWords = blockMidKeyWords.convertToRegexBlockWords(); 743 | let regexBlockStartsKeywords = new RegExp("([\\w]+\\s*:\\s*)?(" + newLineAfterKeyWordsStr + ")([^\\w]|$)"); 744 | let regexBlockEndsKeyWords = blockEndsKeyWords.convertToRegexBlockWords(); 745 | let regexBlockIndentedEndsKeyWords = indentedEndsKeyWords.convertToRegexBlockWords(); 746 | let regexblockEndsWithSemicolon = blockEndsWithSemicolon.convertToRegexBlockWords(); 747 | let regexMidKeyWhen = "WHEN".convertToRegexBlockWords(); 748 | let regexMidKeyElse = "ELSE|ELSIF".convertToRegexBlockWords(); 749 | for (; block.cursor <= block.end; block.cursor++) { 750 | if (indent < 0) { 751 | indent = 0; 752 | } 753 | let input = block.lines[block.cursor].trim(); 754 | if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) { 755 | result.push(new FormattedLine(input, indent)); 756 | return; 757 | } 758 | if (input.regexStartsWith(/COMPONENT\s/)) { 759 | let modeCache = Mode; 760 | Mode = FormatMode.EndsWithSemicolon; 761 | beautifyComponentBlock(block, result, settings, indent); 762 | Mode = modeCache; 763 | continue; 764 | } 765 | if (input.regexStartsWith(/PACKAGE[\s\w]+IS\s+NEW/)) { 766 | let modeCache = Mode; 767 | Mode = FormatMode.EndsWithSemicolon; 768 | beautifyPackageIsNewBlock(block, result, settings, indent); 769 | Mode = modeCache; 770 | continue; 771 | } 772 | if (input.regexStartsWith(/\w+\s+\w+\s*:.+:\s*=\s*\(([^;]|$)/)) { // 'variable symbol: type [:= initial_value];' 773 | let modeCache = Mode; 774 | Mode = FormatMode.EndsWithSemicolon; 775 | let endsWithBracket = input.regexIndexOf(/:\s*=\s*\(/) > 0; 776 | let startIndex = block.cursor; 777 | beautifySemicolonBlock(block, result, settings, indent); 778 | if (endsWithBracket && startIndex != block.cursor) { 779 | let fl = result[block.end]; 780 | if (fl.Line.regexStartsWith(/\);$/)) { 781 | fl.Indent--; 782 | } 783 | } 784 | Mode = modeCache; 785 | continue; 786 | } 787 | if (input.regexIndexOf(/:=(\s*@@comments\d+\s*)?$/) > 0) { 788 | let modeCache = Mode; 789 | Mode = FormatMode.EndsWithSemicolon; 790 | beautifySemicolonBlock(block, result, settings, indent); 791 | Mode = modeCache; 792 | continue; 793 | } 794 | if (input.regexStartsWith(/\w+\s*:\s*ENTITY/)) { 795 | let modeCache = Mode; 796 | Mode = FormatMode.EndsWithSemicolon; 797 | beautifySemicolonBlock(block, result, settings, indent); 798 | Mode = modeCache; 799 | continue; 800 | } 801 | if (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexblockEndsWithSemicolon)) { 802 | let modeCache = Mode; 803 | Mode = FormatMode.EndsWithSemicolon; 804 | beautifySemicolonBlock(block, result, settings, indent); 805 | Mode = modeCache; 806 | continue; 807 | } 808 | if (input.regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { 809 | let modeCache = Mode; 810 | Mode = FormatMode.CaseWhen; 811 | beautifyCaseBlock(block, result, settings, indent); 812 | Mode = modeCache; 813 | continue; 814 | } 815 | if (input.regexStartsWith(/[\w\s:]*(:=)([\s]|$)/)) { 816 | beautifyPortGenericBlock(block, result, settings, indent, ":="); 817 | continue; 818 | } 819 | if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) { 820 | var preCursor = block.cursor; 821 | beautifyPortGenericBlock(block, result, settings, indent, "PORT"); 822 | var preLine = preCursor - 1; 823 | if (preLine >= 0) { 824 | var preL = block.lines[preLine]; 825 | if (preL.regexIndexOf(/:\s+(COMPONENT|ENTITY)/) >= 0) { 826 | indent--; 827 | } 828 | } 829 | continue; 830 | } 831 | if (input.regexStartsWith(/TYPE\s+\w+\s+IS\s+\(/)) { 832 | beautifyPortGenericBlock(block, result, settings, indent, "IS"); 833 | continue; 834 | } 835 | if (input.regexStartsWith(/[\w\s:]*GENERIC([\s]|$)/)) { 836 | beautifyPortGenericBlock(block, result, settings, indent, "GENERIC"); 837 | continue; 838 | } 839 | if (input.regexStartsWith(/[\w\s:]*PROCEDURE[\s\w]+\($/)) { 840 | beautifyPortGenericBlock(block, result, settings, indent, "PROCEDURE"); 841 | if (block.lines[block.cursor].regexStartsWith(/.*\)[\s]*IS/)) { 842 | block.cursor++; 843 | beautify3(block, result, settings, indent + 1); 844 | } 845 | continue; 846 | } 847 | if (input.regexStartsWith(/FUNCTION[^\w]/) 848 | && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { 849 | beautifyPortGenericBlock(block, result, settings, indent, "FUNCTION"); 850 | if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { 851 | block.cursor++; 852 | beautify3(block, result, settings, indent + 1); 853 | } 854 | else { 855 | result[block.cursor].Indent++; 856 | } 857 | continue; 858 | } 859 | if (input.regexStartsWith(/IMPURE FUNCTION[^\w]/) 860 | && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { 861 | beautifyPortGenericBlock(block, result, settings, indent, "IMPURE FUNCTION"); 862 | if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { 863 | if (block.lines[block.cursor].regexStartsWith(regexBlockIndentedEndsKeyWords)) { 864 | result[block.cursor].Indent++; 865 | } 866 | else { 867 | block.cursor++; 868 | beautify3(block, result, settings, indent + 1); 869 | } 870 | } 871 | else { 872 | result[block.cursor].Indent++; 873 | } 874 | continue; 875 | } 876 | result.push(new FormattedLine(input, indent)); 877 | if (indent > 0 878 | && (input.regexStartsWith(regexBlockMidKeyWords) 879 | || (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexMidKeyElse)) 880 | || (Mode == FormatMode.CaseWhen && input.regexStartsWith(regexMidKeyWhen)))) { 881 | result[block.cursor].Indent--; 882 | } 883 | else if (indent > 0 884 | && (input.regexStartsWith(regexBlockEndsKeyWords))) { 885 | result[block.cursor].Indent--; 886 | return; 887 | } 888 | if (input.regexStartsWith(regexOneLineBlockKeyWords)) { 889 | continue; 890 | } 891 | if (input.regexStartsWith(regexFunctionMultiLineBlockKeyWords) 892 | || input.regexStartsWith(regexBlockStartsKeywords)) { 893 | block.cursor++; 894 | beautify3(block, result, settings, indent + 1); 895 | } 896 | } 897 | block.cursor--; 898 | } 899 | exports.beautify3 = beautify3; 900 | function ReserveSemicolonInKeywords(arr) { 901 | for (let i = 0; i < arr.length; i++) { 902 | if (arr[i].match(/FUNCTION|PROCEDURE/) != null) { 903 | arr[i] = arr[i].replace(/;/g, ILSemicolon); 904 | } 905 | } 906 | } 907 | function ApplyNoNewLineAfter(arr, noNewLineAfter) { 908 | if (noNewLineAfter == null) { 909 | return; 910 | } 911 | for (let i = 0; i < arr.length; i++) { 912 | noNewLineAfter.forEach(n => { 913 | let regex = new RegExp("(" + n.toUpperCase + ")[ a-z0-9]+[a-z0-9]+"); 914 | if (arr[i].regexIndexOf(regex) >= 0) { 915 | arr[i] += "@@singleline"; 916 | } 917 | }); 918 | } 919 | } 920 | exports.ApplyNoNewLineAfter = ApplyNoNewLineAfter; 921 | function RemoveAsserts(arr) { 922 | let need_semi = false; 923 | let inAssert = false; 924 | let n = 0; 925 | for (let i = 0; i < arr.length; i++) { 926 | let has_semi = arr[i].indexOf(";") >= 0; 927 | if (need_semi) { 928 | arr[i] = ''; 929 | } 930 | n = arr[i].indexOf("ASSERT "); 931 | if (n >= 0) { 932 | inAssert = true; 933 | arr[i] = ''; 934 | } 935 | if (!has_semi) { 936 | if (inAssert) { 937 | need_semi = true; 938 | } 939 | } 940 | else { 941 | need_semi = false; 942 | } 943 | } 944 | } 945 | exports.RemoveAsserts = RemoveAsserts; 946 | function escapeText(arr, regex, escapedChar) { 947 | let quotes = []; 948 | let regexEpr = new RegExp(regex, "g"); 949 | for (let i = 0; i < arr.length; i++) { 950 | let matches = arr[i].match(regexEpr); 951 | if (matches != null) { 952 | for (var j = 0; j < matches.length; j++) { 953 | var match = matches[j]; 954 | arr[i] = arr[i].replace(match, escapedChar.repeat(match.length)); 955 | quotes.push(match); 956 | } 957 | } 958 | } 959 | return quotes; 960 | } 961 | function RemoveExtraNewLines(input) { 962 | input = input.replace(/(?:\r\n|\r|\n)/g, '\r\n'); 963 | input = input.replace(/ \r\n/g, '\r\n'); 964 | input = input.replace(/\r\n\r\n\r\n/g, '\r\n'); 965 | return input; 966 | } 967 | //# sourceMappingURL=VHDLFormatter.js.map -------------------------------------------------------------------------------- /VHDLFormatter.ts: -------------------------------------------------------------------------------- 1 | let isTesting = false; 2 | const ILEscape = "@@"; 3 | const ILCommentPrefix = ILEscape + "comments"; 4 | const ILIndentedReturnPrefix = ILEscape; 5 | const ILQuote = "⨵"; 6 | const ILSingleQuote = "⦼"; 7 | const ILBackslash = "⨸"; 8 | const ILSemicolon = "⨴"; 9 | 10 | enum FormatMode { 11 | Default, 12 | EndsWithSemicolon, 13 | CaseWhen, 14 | IfElse, 15 | PortGeneric, 16 | } 17 | 18 | let Mode: FormatMode = FormatMode.Default; 19 | 20 | export class NewLineSettings { 21 | newLineAfter: Array; 22 | noNewLineAfter: Array; 23 | constructor() { 24 | this.newLineAfter = []; 25 | this.noNewLineAfter = []; 26 | } 27 | 28 | newLineAfterPush(keyword: string) { 29 | this.newLineAfter.push(keyword); 30 | } 31 | 32 | noNewLineAfterPush(keyword: string) { 33 | this.noNewLineAfter.push(keyword); 34 | } 35 | 36 | push(keyword: string, addNewLine: string) { 37 | let str = addNewLine.toLowerCase(); 38 | if (str == "none") { 39 | return; 40 | } 41 | else if (!str.startsWith("no")) { 42 | this.newLineAfterPush(keyword); 43 | } 44 | else { 45 | this.noNewLineAfterPush(keyword); 46 | } 47 | } 48 | } 49 | 50 | function ConstructNewLineSettings(dict): NewLineSettings { 51 | let settings: NewLineSettings = new NewLineSettings(); 52 | for (let key in dict) { 53 | settings.push(key, dict[key]); 54 | } 55 | return settings; 56 | } 57 | 58 | declare global { 59 | interface String { 60 | regexIndexOf: (pattern: RegExp, startIndex?: number) => number; 61 | regexLastIndexOf: (pattern: RegExp, startIndex: number) => number; 62 | reverse: () => string; 63 | regexStartsWith: (pattern: RegExp) => boolean; 64 | count: (text: string) => number; 65 | regexCount: (pattern: RegExp) => number; 66 | convertToRegexBlockWords: () => RegExp; 67 | } 68 | interface Array { 69 | convertToRegexBlockWords: () => RegExp; 70 | } 71 | } 72 | 73 | String.prototype.regexCount = function (pattern): number { 74 | if (pattern.flags.indexOf("g") < 0) { 75 | pattern = new RegExp(pattern.source, pattern.flags + "g"); 76 | } 77 | return (this.match(pattern) || []).length; 78 | } 79 | 80 | String.prototype.count = function (text): number { 81 | return this.split(text).length - 1; 82 | } 83 | 84 | String.prototype.regexStartsWith = function (pattern): boolean { 85 | var searchResult = this.search(pattern); 86 | return searchResult == 0; 87 | } 88 | 89 | String.prototype.regexIndexOf = function (pattern, startIndex) { 90 | startIndex = startIndex || 0; 91 | var searchResult = this.substr(startIndex).search(pattern); 92 | return (-1 === searchResult) ? -1 : searchResult + startIndex; 93 | } 94 | 95 | String.prototype.regexLastIndexOf = function (pattern, startIndex) { 96 | pattern = (pattern.global) ? pattern : 97 | new RegExp(pattern.source, 'g' + (pattern.ignoreCase ? 'i' : '') + (pattern.multiline ? 'm' : '')); 98 | if (typeof (startIndex) === 'undefined') { 99 | startIndex = this.length; 100 | } else if (startIndex < 0) { 101 | startIndex = 0; 102 | } 103 | const stringToWorkWith = this.substring(0, startIndex + 1); 104 | let lastIndexOf = -1; 105 | let nextStop = 0; 106 | let result: RegExpExecArray; 107 | while ((result = pattern.exec(stringToWorkWith)) != null) { 108 | lastIndexOf = result.index; 109 | pattern.lastIndex = ++nextStop; 110 | } 111 | return lastIndexOf; 112 | } 113 | 114 | String.prototype.reverse = function () { 115 | return this.split('').reverse().join(''); 116 | } 117 | 118 | String.prototype.convertToRegexBlockWords = function (): RegExp { 119 | let result: RegExp = new RegExp("(" + this + ")([^\\w]|$)"); 120 | return result; 121 | } 122 | 123 | Array.prototype.convertToRegexBlockWords = function (): RegExp { 124 | let wordsStr: string = this.join("|"); 125 | let result: RegExp = new RegExp("(" + wordsStr + ")([^\\w]|$)"); 126 | return result; 127 | } 128 | 129 | function EscapeComments(arr: Array): Array { 130 | var comments = []; 131 | var count = 0; 132 | for (var i = 0; i < arr.length; i++) { 133 | var line = arr[i]; 134 | var commentStartIndex = line.indexOf("--"); 135 | if (commentStartIndex >= 0) { 136 | comments.push(line.substr(commentStartIndex)); 137 | arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count; 138 | count++; 139 | } 140 | } 141 | var isInComment = false; 142 | var commentRegex = new RegExp("(?<=" + ILCommentPrefix + "[\\d]+)."); 143 | for (var i = 0; i < arr.length; i++) { 144 | var commentStartIndex = 0; 145 | var hasComment = true; 146 | var commentEndInlineIndex = 0; 147 | while (hasComment) { 148 | var line = arr[i]; 149 | if (!isInComment) { 150 | commentStartIndex = line.indexOf("/*"); 151 | var commentEndIndex = line.indexOf("*/", commentStartIndex); 152 | if (commentStartIndex >= 0) { 153 | if (commentEndIndex >= 0) { 154 | commentEndInlineIndex = commentEndIndex + 2; 155 | isInComment = false; 156 | comments.push(line.substring(commentStartIndex, commentEndInlineIndex)); 157 | arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count + line.substr(commentEndInlineIndex); 158 | count++; 159 | hasComment = true; 160 | if (commentStartIndex + 2 == line.length) { 161 | hasComment = false; 162 | } 163 | } 164 | else { 165 | isInComment = true; 166 | comments.push(line.substr(commentStartIndex)); 167 | arr[i] = line.substr(0, commentStartIndex) + ILCommentPrefix + count; 168 | count++; 169 | hasComment = false; 170 | } 171 | } 172 | else { 173 | hasComment = false; 174 | } 175 | continue; 176 | } 177 | if (isInComment) { 178 | var lastCommentEndIndex = line.regexLastIndexOf(commentRegex, line.length); 179 | if (commentStartIndex == 0) { 180 | var commentEndIndex = line.indexOf("*/", lastCommentEndIndex); 181 | } 182 | else { 183 | var commentEndIndex = line.indexOf("*/", commentStartIndex); 184 | } 185 | if (commentEndIndex >= 0) { 186 | isInComment = false; 187 | comments.push(line.substr(0, commentEndIndex + 2)); 188 | arr[i] = ILCommentPrefix + count + line.substr(commentEndIndex + 2); 189 | count++; 190 | hasComment = true; 191 | } else { 192 | comments.push(line); 193 | arr[i] = ILCommentPrefix + count; 194 | count++; 195 | hasComment = false; 196 | } 197 | } 198 | } 199 | } 200 | return comments 201 | } 202 | 203 | function ToLowerCases(arr: Array) { 204 | for (var i = 0; i < arr.length; i++) { 205 | arr[i] = arr[i].toLowerCase(); 206 | } 207 | } 208 | 209 | function ToUpperCases(arr: Array) { 210 | for (var i = 0; i < arr.length; i++) { 211 | arr[i] = arr[i].toUpperCase(); 212 | } 213 | } 214 | 215 | function ToCamelCases(arr: Array) { 216 | for (var i = 0; i < arr.length; i++) { 217 | arr[i] = arr[i].charAt(0) + arr[i].slice(1).toLowerCase(); 218 | } 219 | } 220 | 221 | function ReplaceKeyWords(text: string, keywords: Array): string { 222 | for (var k = 0; k < keywords.length; k++) { 223 | text = text.replace(new RegExp("([^a-zA-Z0-9_@]|^)" + keywords[k] + "([^a-zA-Z0-9_]|$)", 'gi'), "$1" + keywords[k] + "$2"); 224 | } 225 | return text; 226 | } 227 | 228 | function SetKeywordCase(input: string, keywordcase: string, keywords: string[]): string { 229 | let inputcase: string = keywordcase.toLowerCase(); 230 | switch (inputcase) { 231 | case "lowercase": 232 | ToLowerCases(keywords); 233 | break; 234 | case "defaultcase": 235 | ToCamelCases(keywords); 236 | break; 237 | case "uppercase": 238 | ToUpperCases(keywords); 239 | } 240 | 241 | input = ReplaceKeyWords(input, keywords); 242 | return input; 243 | } 244 | 245 | export function SetNewLinesAfterSymbols(text: string, newLineSettings: NewLineSettings): string { 246 | if (newLineSettings == null) { 247 | return text; 248 | } 249 | if (newLineSettings.newLineAfter != null) { 250 | newLineSettings.newLineAfter.forEach(symbol => { 251 | let upper = symbol.toUpperCase(); 252 | var rexString = "(" + upper + ")[ ]?([^ \r\n@])"; 253 | let regex: RegExp = null; 254 | if (upper.regexStartsWith(/\w/)) { 255 | regex = new RegExp("\\b" + rexString, "g"); 256 | } 257 | else { 258 | regex = new RegExp(rexString, "g"); 259 | } 260 | text = text.replace(regex, '$1\r\n$2'); 261 | if (upper == "PORT") { 262 | text = text.replace(/\bPORT\b\s+MAP/, "PORT MAP"); 263 | } 264 | }); 265 | } 266 | if (newLineSettings.noNewLineAfter != null) { 267 | newLineSettings.noNewLineAfter.forEach(symbol => { 268 | let rexString = "(" + symbol.toUpperCase() + ")[ \r\n]+([^@])"; 269 | let regex: RegExp = null; 270 | if (symbol.regexStartsWith(/\w/)) { 271 | regex = new RegExp("\\b" + rexString, "g"); 272 | text = text.replace(regex, '$1 $2'); 273 | } 274 | else { 275 | regex = new RegExp(rexString, "g"); 276 | } 277 | text = text.replace(regex, '$1 $2'); 278 | }); 279 | } 280 | return text; 281 | } 282 | 283 | export class signAlignSettings { 284 | isRegional: boolean; 285 | isAll: boolean; 286 | mode: string; 287 | keyWords: Array; 288 | alignComments: boolean; 289 | constructor(isRegional: boolean, isAll: boolean, mode: string, keyWords: Array, alignComments: boolean = false) { 290 | this.isRegional = isRegional; 291 | this.isAll = isAll; 292 | this.mode = mode; 293 | this.keyWords = keyWords; 294 | this.alignComments = alignComments; 295 | } 296 | } 297 | 298 | export class BeautifierSettings { 299 | RemoveComments: boolean; 300 | RemoveAsserts: boolean; 301 | CheckAlias: boolean; 302 | SignAlignSettings: signAlignSettings; 303 | KeywordCase: string; 304 | TypeNameCase: string; 305 | Indentation: string; 306 | NewLineSettings: NewLineSettings; 307 | EndOfLine: string; 308 | AddNewLine: boolean; 309 | constructor(removeComments: boolean, removeReport: boolean, checkAlias: boolean, 310 | signAlignSettings: signAlignSettings, keywordCase: string, typeNameCase: string, indentation: string, 311 | newLineSettings: NewLineSettings, endOfLine: string, addNewLine: boolean) { 312 | this.RemoveComments = removeComments; 313 | this.RemoveAsserts = removeReport; 314 | this.CheckAlias = checkAlias; 315 | this.SignAlignSettings = signAlignSettings; 316 | this.KeywordCase = keywordCase; 317 | this.TypeNameCase = typeNameCase; 318 | this.Indentation = indentation; 319 | this.NewLineSettings = newLineSettings; 320 | this.EndOfLine = endOfLine; 321 | this.AddNewLine = addNewLine; 322 | } 323 | } 324 | 325 | let KeyWords: Array = ["ABS", "ACCESS", "AFTER", "ALIAS", "ALL", "AND", "ARCHITECTURE", "ARRAY", "ASSERT", "ATTRIBUTE", "BEGIN", "BLOCK", "BODY", "BUFFER", "BUS", "CASE", "COMPONENT", "CONFIGURATION", "CONSTANT", "CONTEXT", "COVER", "DISCONNECT", "DOWNTO", "DEFAULT", "ELSE", "ELSIF", "END", "ENTITY", "EXIT", "FAIRNESS", "FILE", "FOR", "FORCE", "FUNCTION", "GENERATE", "GENERIC", "GROUP", "GUARDED", "IF", "IMPURE", "IN", "INERTIAL", "INOUT", "IS", "LABEL", "LIBRARY", "LINKAGE", "LITERAL", "LOOP", "MAP", "MOD", "NAND", "NEW", "NEXT", "NOR", "NOT", "NULL", "OF", "ON", "OPEN", "OR", "OTHERS", "OUT", "PACKAGE", "PORT", "POSTPONED", "PROCEDURE", "PROCESS", "PROPERTY", "PROTECTED", "PURE", "RANGE", "RECORD", "REGISTER", "REJECT", "RELEASE", "REM", "REPORT", "RESTRICT", "RESTRICT_GUARANTEE", "RETURN", "ROL", "ROR", "SELECT", "SEQUENCE", "SEVERITY", "SHARED", "SIGNAL", "SLA", "SLL", "SRA", "SRL", "STRONG", "SUBTYPE", "THEN", "TO", "TRANSPORT", "TYPE", "UNAFFECTED", "UNITS", "UNTIL", "USE", "VARIABLE", "VMODE", "VPROP", "VUNIT", "WAIT", "WHEN", "WHILE", "WITH", "XNOR", "XOR"]; 326 | let TypeNames: Array = ["BOOLEAN", "BIT", "CHARACTER", "INTEGER", "TIME", "NATURAL", "POSITIVE", "STD_LOGIC", "STD_LOGIC_VECTOR", "STD_ULOGIC", "STD_ULOGIC_VECTOR", "STRING"]; 327 | 328 | export function beautify(input: string, settings: BeautifierSettings) { 329 | input = input.replace(/\r\n/g, "\n"); 330 | input = input.replace(/\n/g, "\r\n"); 331 | var arr = input.split("\r\n"); 332 | var comments = EscapeComments(arr); 333 | var backslashes = escapeText(arr, "\\\\[^\\\\]+\\\\", ILBackslash); 334 | let quotes = escapeText(arr, '"([^"]+)"', ILQuote); 335 | let singleQuotes = escapeText(arr, "'[^']'", ILSingleQuote); 336 | RemoveLeadingWhitespaces(arr); 337 | 338 | input = arr.join("\r\n"); 339 | if (settings.RemoveComments) { 340 | input = input.replace(/\r\n[ \t]*@@comments[0-9]+[ \t]*\r\n/g, '\r\n'); 341 | input = input.replace(/@@comments[0-9]+/g, ''); 342 | comments = []; 343 | } 344 | 345 | input = SetKeywordCase(input, "uppercase", KeyWords); 346 | input = SetKeywordCase(input, "uppercase", TypeNames); 347 | input = RemoveExtraNewLines(input); 348 | input = input.replace(/[\t ]+/g, ' '); 349 | input = input.replace(/\([\t ]+/g, '\('); 350 | input = input.replace(/[ ]+;/g, ';'); 351 | input = input.replace(/:[ ]*(PROCESS|ENTITY)/gi, ':$1'); 352 | 353 | arr = input.split("\r\n"); 354 | if (settings.RemoveAsserts) { 355 | RemoveAsserts(arr);//RemoveAsserts must be after EscapeQuotes 356 | } 357 | ReserveSemicolonInKeywords(arr); 358 | input = arr.join("\r\n"); 359 | input = input.replace(/\b(PORT|GENERIC)\b\s+MAP/g, '$1 MAP'); 360 | input = input.replace(/\b(PORT|PROCESS|GENERIC)\b[\s]*\(/g, '$1 ('); 361 | let newLineSettings = settings.NewLineSettings; 362 | if (newLineSettings != null) { 363 | input = SetNewLinesAfterSymbols(input, newLineSettings); 364 | arr = input.split("\r\n"); 365 | ApplyNoNewLineAfter(arr, newLineSettings.noNewLineAfter); 366 | input = arr.join("\r\n"); 367 | } 368 | 369 | input = input.replace(/([a-zA-Z0-9\); ])\);(@@comments[0-9]+)?@@end/g, '$1\r\n);$2@@end'); 370 | input = input.replace(/[ ]?([&=:\-\+|\*]|[<>]+)[ ]?/g, ' $1 '); 371 | input = input.replace(/(\d+e) +([+\-]) +(\d+)/g, '$1$2$3');// fix exponential notation format broken by previous step 372 | input = input.replace(/[ ]?([,])[ ]?/g, '$1 '); 373 | input = input.replace(/[ ]?(['"])(THEN)/g, '$1 $2'); 374 | input = input.replace(/[ ]?(\?)?[ ]?(<|:|>|\/)?[ ]+(=)?[ ]?/g, ' $1$2$3 '); 375 | input = input.replace(/(IF)[ ]?([\(\)])/g, '$1 $2'); 376 | input = input.replace(/([\(\)])[ ]?(THEN)/gi, '$1 $2'); 377 | input = input.replace(/(^|[\(\)])[ ]?(AND|OR|XOR|XNOR)[ ]*([\(])/g, '$1 $2 $3'); 378 | input = input.replace(/ ([\-\*\/=+<>])[ ]*([\-\*\/=+<>]) /g, " $1$2 "); 379 | //input = input.replace(/\r\n[ \t]+--\r\n/g, "\r\n"); 380 | input = input.replace(/[ ]+/g, ' '); 381 | input = input.replace(/[ \t]+\r\n/g, "\r\n"); 382 | input = input.replace(/\r\n\r\n\r\n/g, '\r\n'); 383 | input = input.replace(/[\r\n\s]+$/g, ''); 384 | input = input.replace(/[ \t]+\)/g, ')'); 385 | input = input.replace(/\s*\)\s+RETURN\s+([\w]+;)/g, '\r\n) RETURN $1');//function(..)\r\nreturn type; -> function(..\r\n)return type; 386 | 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; 387 | let keywordAndSignRegex = new RegExp("(\\b" + KeyWords.join("\\b|\\b") + "\\b) +([\\-+]) +(\\w)", "g"); 388 | input = input.replace(keywordAndSignRegex, "$1 $2$3");// `WHEN - 2` -> `WHEN -2` 389 | input = input.replace(/([,|]) +([+\-]) +(\w)/g, '$1 $2$3');// `1, - 2)` -> `1, -2)` 390 | input = input.replace(/(\() +([+\-]) +(\w)/g, '$1$2$3');// `( - 2)` -> `(-2)` 391 | arr = input.split("\r\n"); 392 | let result: (FormattedLine | FormattedLine[])[] = []; 393 | let block = new CodeBlock(arr); 394 | beautify3(block, result, settings, 0); 395 | var alignSettings = settings.SignAlignSettings; 396 | if (alignSettings != null && alignSettings.isAll) { 397 | AlignSigns(result, 0, result.length - 1, alignSettings.mode, alignSettings.alignComments); 398 | } 399 | 400 | arr = FormattedLineToString(result, settings.Indentation); 401 | input = arr.join("\r\n"); 402 | input = input.replace(/@@RETURN/g, "RETURN"); 403 | input = SetKeywordCase(input, settings.KeywordCase, KeyWords); 404 | input = SetKeywordCase(input, settings.TypeNameCase, TypeNames); 405 | 406 | input = replaceEscapedWords(input, quotes, ILQuote); 407 | input = replaceEscapedWords(input, singleQuotes, ILSingleQuote); 408 | input = replaceEscapedComments(input, comments, ILCommentPrefix); 409 | input = replaceEscapedWords(input, backslashes, ILBackslash); 410 | input = input.replace(new RegExp(ILSemicolon, "g"), ";"); 411 | input = input.replace(/@@[a-z]+/g, ""); 412 | var escapedTexts = new RegExp("[" + ILBackslash + ILQuote + ILSingleQuote + "]", "g"); 413 | input = input.replace(escapedTexts, ""); 414 | input = input.replace(/\r\n/g, settings.EndOfLine); 415 | if (settings.AddNewLine && !input.endsWith(settings.EndOfLine)) { 416 | input += settings.EndOfLine; 417 | } 418 | return input; 419 | } 420 | 421 | function replaceEscapedWords(input: string, arr: Array, prefix: string): string { 422 | for (var i = 0; i < arr.length; i++) { 423 | var text = arr[i]; 424 | var regex = new RegExp("(" + prefix + "){" + text.length + "}"); 425 | input = input.replace(regex, text); 426 | } 427 | return input; 428 | } 429 | 430 | function replaceEscapedComments(input: string, arr: Array, prefix: string): string { 431 | for (var i = 0; i < arr.length; i++) { 432 | input = input.replace(prefix + i, arr[i]); 433 | } 434 | return input; 435 | } 436 | 437 | function RemoveLeadingWhitespaces(arr: Array) { 438 | for (var i = 0; i < arr.length; i++) { 439 | arr[i] = arr[i].replace(/^\s+/, ""); 440 | } 441 | } 442 | 443 | export class FormattedLine { 444 | Line: string; 445 | Indent: number; 446 | constructor(line: string, indent: number) { 447 | this.Line = line; 448 | this.Indent = indent; 449 | } 450 | } 451 | 452 | export class CodeBlock { 453 | lines: Array; 454 | start: number; // index of first line of range 455 | end: number; // index of last line of range 456 | cursor: number; // line currently being processed 457 | parent: CodeBlock; 458 | 459 | constructor(lines: Array, start = 0, end = lines.length - 1) { 460 | this.lines = lines; 461 | this.start = start; 462 | this.end = end; 463 | this.parent = null; 464 | this.cursor = start; 465 | } 466 | 467 | _notifySplit(atLine: number) { 468 | if (this.start > atLine) 469 | this.start++; 470 | if (this.end >= atLine) 471 | this.end++; 472 | if (this.cursor >= atLine) 473 | this.cursor++; 474 | if (this.parent) 475 | this.parent._notifySplit(atLine); 476 | } 477 | 478 | splitLine(atLine: number, firstText: string, secondText: string) { 479 | this.lines[atLine] = firstText; 480 | this.lines.splice(atLine + 1, 0, secondText); 481 | this._notifySplit(atLine); 482 | } 483 | 484 | subBlock(start: number, end: number): CodeBlock { 485 | let newBlock = new CodeBlock(this.lines, start, end); 486 | newBlock.parent = this; 487 | return newBlock; 488 | } 489 | } 490 | 491 | export function FormattedLineToString(arr: (FormattedLine | FormattedLine[])[], indentation: string): Array { 492 | let result: Array = []; 493 | if (arr == null) { 494 | return result; 495 | } 496 | if (indentation == null) { 497 | indentation = ""; 498 | } 499 | arr.forEach(i => { 500 | if (i instanceof FormattedLine) { 501 | if (i.Line.length > 0) { 502 | result.push((Array(i.Indent + 1).join(indentation)) + i.Line); 503 | } 504 | else { 505 | result.push(""); 506 | } 507 | } 508 | else { 509 | result = result.concat(FormattedLineToString(i, indentation)); 510 | } 511 | }); 512 | return result; 513 | } 514 | 515 | function GetCloseparentheseEndIndex(block: CodeBlock) { 516 | let openParentheseCount: number = 0; 517 | let closeParentheseCount: number = 0; 518 | let startIndex = block.cursor; 519 | for (; block.cursor <= block.end; block.cursor++) { 520 | let input = block.lines[block.cursor]; 521 | openParentheseCount += input.count("("); 522 | closeParentheseCount += input.count(")"); 523 | if (openParentheseCount > 0 524 | && openParentheseCount <= closeParentheseCount) { 525 | return; 526 | } 527 | } 528 | block.cursor = startIndex; 529 | } 530 | 531 | export function beautifyPortGenericBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number, mode: string) { 532 | let startIndex = block.cursor; 533 | let firstLine: string = block.lines[startIndex]; 534 | let regex: RegExp = new RegExp("[\\w\\s:]*(" + mode + ")([\\s]|$)"); 535 | if (!firstLine.regexStartsWith(regex)) { 536 | return; 537 | } 538 | 539 | let firstLineHasParenthese: boolean = firstLine.indexOf("(") >= 0; 540 | let secondLineHasParenthese: boolean = startIndex + 1 <= block.end && block.lines[startIndex + 1].startsWith("("); 541 | let hasParenthese: boolean = firstLineHasParenthese || secondLineHasParenthese; 542 | let blockBodyStartIndex = startIndex + (secondLineHasParenthese ? 1 : 0); 543 | if (hasParenthese) { 544 | GetCloseparentheseEndIndex(block); 545 | } 546 | let endIndex: number = block.cursor; 547 | let bodyBlock = block.subBlock(blockBodyStartIndex, endIndex); 548 | 549 | if (endIndex != startIndex && firstLineHasParenthese) { 550 | block.lines[startIndex] = block.lines[startIndex].replace(/\b(PORT|GENERIC|PROCEDURE)\b([\w ]+)\(([\w\(\) ]+)/, '$1$2(\r\n$3'); 551 | let newInputs = block.lines[startIndex].split("\r\n"); 552 | if (newInputs.length == 2) { 553 | bodyBlock.splitLine(startIndex, newInputs[0], newInputs[1]); 554 | } 555 | } 556 | else if (endIndex > startIndex + 1 && secondLineHasParenthese) { 557 | block.lines[startIndex + 1] = block.lines[startIndex + 1].replace(/\(([\w\(\) ]+)/, '(\r\n$1'); 558 | let newInputs = block.lines[startIndex + 1].split("\r\n"); 559 | if (newInputs.length == 2) { 560 | bodyBlock.splitLine(startIndex + 1, newInputs[0], newInputs[1]); 561 | } 562 | } 563 | 564 | if (firstLineHasParenthese && block.lines[startIndex].indexOf("MAP") > 0) { 565 | block.lines[startIndex] = block.lines[startIndex].replace(/([^\w])(MAP)\s+\(/g, '$1$2('); 566 | } 567 | 568 | result.push(new FormattedLine(block.lines[startIndex], indent)); 569 | if (secondLineHasParenthese) { 570 | let secondLineIndent = indent; 571 | if (endIndex == startIndex + 1) { 572 | secondLineIndent++; 573 | } 574 | result.push(new FormattedLine(block.lines[startIndex + 1], secondLineIndent)); 575 | } 576 | 577 | beautify3(bodyBlock.subBlock(bodyBlock.start + 1, bodyBlock.end), result, settings, indent + 1); 578 | if (block.lines[block.cursor].startsWith(")")) { 579 | (result[block.cursor]).Indent--; 580 | bodyBlock.end--; 581 | } 582 | var alignSettings = settings.SignAlignSettings; 583 | if (alignSettings != null) { 584 | if (alignSettings.isRegional && !alignSettings.isAll 585 | && alignSettings.keyWords != null 586 | && alignSettings.keyWords.indexOf(mode) >= 0) { 587 | AlignSigns(result, bodyBlock.start + 1, bodyBlock.end, alignSettings.mode, alignSettings.alignComments); 588 | } 589 | } 590 | } 591 | 592 | export function AlignSigns(result: (FormattedLine | FormattedLine[])[], startIndex: number, endIndex: number, mode: string, alignComments: boolean = false) { 593 | AlignSign_(result, startIndex, endIndex, ":", mode); 594 | AlignSign_(result, startIndex, endIndex, ":=", mode); 595 | AlignSign_(result, startIndex, endIndex, "<=", mode); 596 | AlignSign_(result, startIndex, endIndex, "=>", mode); 597 | AlignSign_(result, startIndex, endIndex, "direction", mode); 598 | if (alignComments) { 599 | AlignSign_(result, startIndex, endIndex, "@@comments", mode); 600 | } 601 | } 602 | 603 | function indexOfGroup(regex: RegExp, input: string, group: number) { 604 | var match = regex.exec(input); 605 | if (match == null) { 606 | return -1; 607 | } 608 | var index = match.index; 609 | for (let i = 1; i < group; i++) { 610 | index += match[i].length; 611 | } 612 | return index; 613 | } 614 | 615 | function AlignSign_(result: (FormattedLine | FormattedLine[])[], startIndex: number, endIndex: number, symbol: string, mode: string) { 616 | let maxSymbolIndex: number = -1; 617 | let symbolIndices = {}; 618 | let startLine = startIndex; 619 | let labelAndKeywords: Array = [ 620 | "([\\w\\s]*:(\\s)*PROCESS)", 621 | "([\\w\\s]*:(\\s)*POSTPONED PROCESS)", 622 | "([\\w\\s]*:\\s*$)", 623 | "([\\w\\s]*:.*\\s+GENERATE)" 624 | ]; 625 | let labelAndKeywordsStr: string = labelAndKeywords.join("|"); 626 | let labelAndKeywordsRegex = new RegExp("(" + labelAndKeywordsStr + ")([^\\w]|$)"); 627 | for (let i = startIndex; i <= endIndex; i++) { 628 | let line = (result[i]).Line; 629 | if (symbol == ":" && line.regexStartsWith(labelAndKeywordsRegex)) { 630 | continue; 631 | } 632 | let regex: RegExp; 633 | if (symbol == "direction") { 634 | regex = new RegExp("(:\\s*)(IN|OUT|INOUT|BUFFER)(\\s+)(\\w)"); 635 | } 636 | else { 637 | regex = new RegExp("([\\s\\w\\\\]|^)" + symbol + "([\\s\\w\\\\]|$)"); 638 | } 639 | if (line.regexCount(regex) > 1) { 640 | continue; 641 | } 642 | let colonIndex: number; 643 | if (symbol == "direction") { 644 | colonIndex = indexOfGroup(regex, line, 4); 645 | } 646 | else { 647 | colonIndex = line.regexIndexOf(regex); 648 | } 649 | if (colonIndex > 0) { 650 | maxSymbolIndex = Math.max(maxSymbolIndex, colonIndex); 651 | symbolIndices[i] = colonIndex; 652 | } 653 | else if ((mode != "local" && !line.startsWith(ILCommentPrefix) && line.length != 0) 654 | || (mode == "local")) { 655 | if (startLine < i - 1) // if cannot find the symbol, a block of symbols ends 656 | { 657 | AlignSign(result, startLine, i - 1, symbol, maxSymbolIndex, symbolIndices); 658 | } 659 | maxSymbolIndex = -1; 660 | symbolIndices = {}; 661 | startLine = i; 662 | } 663 | } 664 | if (startLine < endIndex) // if cannot find the symbol, a block of symbols ends 665 | { 666 | AlignSign(result, startLine, endIndex, symbol, maxSymbolIndex, symbolIndices); 667 | } 668 | } 669 | 670 | export function AlignSign(result: (FormattedLine | FormattedLine[])[], startIndex: number, endIndex: number, symbol: string, maxSymbolIndex: number = -1, symbolIndices = {}) { 671 | if (maxSymbolIndex < 0) { 672 | return; 673 | } 674 | 675 | for (let lineIndex in symbolIndices) { 676 | let symbolIndex = symbolIndices[lineIndex]; 677 | if (symbolIndex == maxSymbolIndex) { 678 | continue; 679 | } 680 | let line = (result[lineIndex]).Line; 681 | (result[lineIndex]).Line = line.substring(0, symbolIndex) 682 | + (Array(maxSymbolIndex - symbolIndex + 1).join(" ")) 683 | + line.substring(symbolIndex); 684 | } 685 | } 686 | 687 | export function beautifyCaseBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { 688 | if (!block.lines[block.cursor].regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { 689 | return; 690 | } 691 | result.push(new FormattedLine(block.lines[block.cursor], indent)); 692 | block.cursor++; 693 | beautify3(block, result, settings, indent + 2); 694 | (result[block.cursor]).Indent = indent; 695 | } 696 | 697 | function getSemicolonBlockEndIndex(block: CodeBlock, settings: BeautifierSettings) { 698 | let endIndex = block.cursor; 699 | let openBracketsCount = 0; 700 | let closeBracketsCount = 0; 701 | for (; block.cursor <= block.end; block.cursor++) { 702 | let input = block.lines[block.cursor]; 703 | let indexOfSemicolon = input.indexOf(";"); 704 | let splitIndex = indexOfSemicolon < 0 ? input.length : indexOfSemicolon + 1; 705 | let stringBeforeSemicolon = input.substring(0, splitIndex); 706 | let stringAfterSemicolon = input.substring(splitIndex); 707 | stringAfterSemicolon = stringAfterSemicolon.replace(new RegExp(ILCommentPrefix + "[0-9]+"), ""); 708 | openBracketsCount += stringBeforeSemicolon.count("("); 709 | closeBracketsCount += stringBeforeSemicolon.count(")"); 710 | if (indexOfSemicolon < 0) { 711 | continue; 712 | } 713 | if (openBracketsCount == closeBracketsCount) { 714 | endIndex = block.cursor; 715 | if (stringAfterSemicolon.trim().length > 0 && settings.NewLineSettings.newLineAfter.indexOf(";") >= 0) { 716 | block.splitLine(block.cursor, stringBeforeSemicolon, stringAfterSemicolon); 717 | } 718 | break; 719 | } 720 | } 721 | block.cursor = endIndex; 722 | } 723 | 724 | export function beautifyComponentBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { 725 | let startIndex = block.cursor; 726 | for (; block.cursor <= block.end; block.cursor++) { 727 | if (block.lines[block.cursor].regexStartsWith(/END(\s|$)/)) { 728 | break; 729 | } 730 | } 731 | result.push(new FormattedLine(block.lines[startIndex], indent)); 732 | if (block.cursor != startIndex) { 733 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 734 | } 735 | } 736 | 737 | export function beautifyPackageIsNewBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { 738 | let startIndex = block.cursor; 739 | for (; block.cursor <= block.end; block.cursor++) { 740 | if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { 741 | break; 742 | } 743 | } 744 | result.push(new FormattedLine(block.lines[startIndex], indent)); 745 | if (block.cursor != startIndex) { 746 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 747 | } 748 | } 749 | 750 | export function beautifyVariableInitialiseBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { 751 | let startIndex = block.cursor; 752 | for (; block.cursor <= block.end; block.cursor++) { 753 | if (block.lines[block.cursor].regexIndexOf(/;(\s|$)/) >= 0) { 754 | break; 755 | } 756 | } 757 | result.push(new FormattedLine(block.lines[startIndex], indent)); 758 | if (block.cursor != startIndex) { 759 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 760 | } 761 | } 762 | 763 | export function beautifySemicolonBlock(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { 764 | let startIndex = block.cursor; 765 | getSemicolonBlockEndIndex(block, settings); 766 | result.push(new FormattedLine(block.lines[startIndex], indent)); 767 | if (block.cursor != startIndex) { 768 | beautify3(block.subBlock(startIndex + 1, block.cursor), result, settings, indent + 1); 769 | alignSignalAssignmentBlock(settings, block.lines, startIndex, block.cursor, result); 770 | } 771 | } 772 | 773 | function alignSignalAssignmentBlock(settings: BeautifierSettings, inputs: string[], startIndex: number, endIndex: number, result: (FormattedLine | FormattedLine[])[]) { 774 | if (settings.Indentation.replace(/ +/g, "").length == 0) { 775 | let reg: RegExp = new RegExp("^([\\w\\\\]+[\\s]*<=\\s*)"); 776 | let match = reg.exec(inputs[startIndex]); 777 | if (match != null) { 778 | let length = match[0].length; 779 | let prefixLength = length - settings.Indentation.length; 780 | let prefix = new Array(prefixLength + 1).join(" "); 781 | for (let i = startIndex + 1; i <= endIndex; i++) { 782 | let fl = (result[i] as FormattedLine); 783 | fl.Line = prefix + fl.Line; 784 | } 785 | } 786 | } 787 | } 788 | 789 | export function beautify3(block: CodeBlock, result: (FormattedLine | FormattedLine[])[], settings: BeautifierSettings, indent: number) { 790 | let regexOneLineBlockKeyWords: RegExp = new RegExp(/(PROCEDURE)[^\w](?!.+[^\w]IS([^\w]|$))/);//match PROCEDURE..; but not PROCEDURE .. IS; 791 | let regexFunctionMultiLineBlockKeyWords: RegExp = new RegExp(/(FUNCTION|IMPURE FUNCTION)[^\w](?=.+[^\w]IS([^\w]|$))/);//match FUNCTION .. IS; but not FUNCTION 792 | let blockMidKeyWords: Array = ["BEGIN"]; 793 | let blockStartsKeyWords: Array = [ 794 | "IF", 795 | "CASE", 796 | "ARCHITECTURE", 797 | "PROCEDURE", 798 | "PACKAGE", 799 | "(([\\w\\s]*:)?(\\s)*PROCESS)",// with label 800 | "(([\\w\\s]*:)?(\\s)*POSTPONED PROCESS)",// with label 801 | "(.*\\s*PROTECTED)", 802 | "(COMPONENT)", 803 | "(ENTITY(?!.+;))", 804 | "FOR", 805 | "WHILE", 806 | "LOOP", 807 | "(.*\\s*GENERATE)", 808 | "(CONTEXT[\\w\\s\\\\]+IS)", 809 | "(CONFIGURATION(?!.+;))", 810 | "BLOCK", 811 | "UNITS", 812 | "\\w+\\s+\\w+\\s+IS\\s+RECORD"]; 813 | let blockEndsKeyWords: Array = ["END", ".*\\)\\s*RETURN\\s+[\\w]+;"]; 814 | let indentedEndsKeyWords: Array = [ILIndentedReturnPrefix + "RETURN\\s+\\w+;"]; 815 | let blockEndsWithSemicolon: Array = [ 816 | "(WITH\\s+[\\w\\s\\\\]+SELECT)", 817 | "([\\w\\\\]+[\\s]*<=)", 818 | "([\\w\\\\]+[\\s]*:=)", 819 | "FOR\\s+[\\w\\s,]+:\\s*\\w+\\s+USE", 820 | "REPORT" 821 | ]; 822 | 823 | let newLineAfterKeyWordsStr: string = blockStartsKeyWords.join("|"); 824 | let regexBlockMidKeyWords: RegExp = blockMidKeyWords.convertToRegexBlockWords(); 825 | let regexBlockStartsKeywords: RegExp = new RegExp("([\\w]+\\s*:\\s*)?(" + newLineAfterKeyWordsStr + ")([^\\w]|$)") 826 | let regexBlockEndsKeyWords: RegExp = blockEndsKeyWords.convertToRegexBlockWords(); 827 | let regexBlockIndentedEndsKeyWords: RegExp = indentedEndsKeyWords.convertToRegexBlockWords(); 828 | let regexblockEndsWithSemicolon: RegExp = blockEndsWithSemicolon.convertToRegexBlockWords(); 829 | let regexMidKeyWhen: RegExp = "WHEN".convertToRegexBlockWords(); 830 | let regexMidKeyElse: RegExp = "ELSE|ELSIF".convertToRegexBlockWords(); 831 | for (; block.cursor <= block.end; block.cursor++) { 832 | if (indent < 0) { 833 | indent = 0; 834 | } 835 | let input: string = block.lines[block.cursor].trim(); 836 | if (input.regexStartsWith(regexBlockIndentedEndsKeyWords)) { 837 | result.push(new FormattedLine(input, indent)); 838 | return; 839 | } 840 | if (input.regexStartsWith(/COMPONENT\s/)) { 841 | let modeCache = Mode; 842 | Mode = FormatMode.EndsWithSemicolon; 843 | beautifyComponentBlock(block, result, settings, indent); 844 | Mode = modeCache; 845 | continue; 846 | } 847 | if (input.regexStartsWith(/PACKAGE[\s\w]+IS\s+NEW/)) { 848 | let modeCache = Mode; 849 | Mode = FormatMode.EndsWithSemicolon; 850 | beautifyPackageIsNewBlock(block, result, settings, indent); 851 | Mode = modeCache; 852 | continue; 853 | } 854 | if (input.regexStartsWith(/\w+\s+\w+\s*:.+:\s*=\s*\(([^;]|$)/)) { // 'variable symbol: type [:= initial_value];' 855 | let modeCache = Mode; 856 | Mode = FormatMode.EndsWithSemicolon; 857 | let endsWithBracket = input.regexIndexOf(/:\s*=\s*\(/) > 0; 858 | let startIndex = block.cursor; 859 | beautifySemicolonBlock(block, result, settings, indent); 860 | if (endsWithBracket && startIndex != block.cursor) { 861 | let fl = result[block.end] as FormattedLine; 862 | if (fl.Line.regexStartsWith(/\);$/)) { 863 | fl.Indent--; 864 | } 865 | } 866 | Mode = modeCache; 867 | continue; 868 | } 869 | if (input.regexIndexOf(/:=(\s*@@comments\d+\s*)?$/) > 0) { 870 | let modeCache = Mode; 871 | Mode = FormatMode.EndsWithSemicolon; 872 | beautifySemicolonBlock(block, result, settings, indent); 873 | Mode = modeCache; 874 | continue; 875 | } 876 | if (input.regexStartsWith(/\w+\s*:\s*ENTITY/)) { 877 | let modeCache = Mode; 878 | Mode = FormatMode.EndsWithSemicolon; 879 | beautifySemicolonBlock(block, result, settings, indent); 880 | Mode = modeCache; 881 | continue; 882 | } 883 | if (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexblockEndsWithSemicolon)) { 884 | let modeCache = Mode; 885 | Mode = FormatMode.EndsWithSemicolon; 886 | beautifySemicolonBlock(block, result, settings, indent); 887 | Mode = modeCache; 888 | continue; 889 | } 890 | if (input.regexStartsWith(/(.+:\s*)?(CASE)([\s]|$)/)) { 891 | let modeCache = Mode; 892 | Mode = FormatMode.CaseWhen; 893 | beautifyCaseBlock(block, result, settings, indent); 894 | Mode = modeCache; 895 | continue; 896 | } 897 | if (input.regexStartsWith(/[\w\s:]*(:=)([\s]|$)/)) { 898 | beautifyPortGenericBlock(block, result, settings, indent, ":="); 899 | continue; 900 | } 901 | if (input.regexStartsWith(/[\w\s:]*\bPORT\b([\s]|$)/)) { 902 | var preCursor = block.cursor; 903 | beautifyPortGenericBlock(block, result, settings, indent, "PORT"); 904 | var preLine = preCursor - 1; 905 | if (preLine >= 0) { 906 | var preL = block.lines[preLine]; 907 | if (preL.regexIndexOf(/:\s+(COMPONENT|ENTITY)/) >= 0) { 908 | indent--; 909 | } 910 | } 911 | continue; 912 | } 913 | if (input.regexStartsWith(/TYPE\s+\w+\s+IS\s+\(/)) { 914 | beautifyPortGenericBlock(block, result, settings, indent, "IS"); 915 | continue; 916 | } 917 | if (input.regexStartsWith(/[\w\s:]*GENERIC([\s]|$)/)) { 918 | beautifyPortGenericBlock(block, result, settings, indent, "GENERIC"); 919 | continue; 920 | } 921 | if (input.regexStartsWith(/[\w\s:]*PROCEDURE[\s\w]+\($/)) { 922 | beautifyPortGenericBlock(block, result, settings, indent, "PROCEDURE"); 923 | if (block.lines[block.cursor].regexStartsWith(/.*\)[\s]*IS/)) { 924 | block.cursor++; 925 | beautify3(block, result, settings, indent + 1); 926 | } 927 | continue; 928 | } 929 | if (input.regexStartsWith(/FUNCTION[^\w]/) 930 | && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { 931 | beautifyPortGenericBlock(block, result, settings, indent, "FUNCTION"); 932 | if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { 933 | block.cursor++; 934 | beautify3(block, result, settings, indent + 1); 935 | } else { 936 | (result[block.cursor]).Indent++; 937 | } 938 | continue; 939 | } 940 | if (input.regexStartsWith(/IMPURE FUNCTION[^\w]/) 941 | && input.regexIndexOf(/[^\w]RETURN[^\w]/) < 0) { 942 | beautifyPortGenericBlock(block, result, settings, indent, "IMPURE FUNCTION"); 943 | if (!block.lines[block.cursor].regexStartsWith(regexBlockEndsKeyWords)) { 944 | if (block.lines[block.cursor].regexStartsWith(regexBlockIndentedEndsKeyWords)) { 945 | (result[block.cursor]).Indent++; 946 | } else { 947 | block.cursor++; 948 | beautify3(block, result, settings, indent + 1); 949 | } 950 | } else { 951 | (result[block.cursor]).Indent++; 952 | } 953 | continue; 954 | } 955 | result.push(new FormattedLine(input, indent)); 956 | if (indent > 0 957 | && (input.regexStartsWith(regexBlockMidKeyWords) 958 | || (Mode != FormatMode.EndsWithSemicolon && input.regexStartsWith(regexMidKeyElse)) 959 | || (Mode == FormatMode.CaseWhen && input.regexStartsWith(regexMidKeyWhen)))) { 960 | (result[block.cursor]).Indent--; 961 | } 962 | else if (indent > 0 963 | && (input.regexStartsWith(regexBlockEndsKeyWords))) { 964 | (result[block.cursor]).Indent--; 965 | return; 966 | } 967 | if (input.regexStartsWith(regexOneLineBlockKeyWords)) { 968 | continue; 969 | } 970 | if (input.regexStartsWith(regexFunctionMultiLineBlockKeyWords) 971 | || input.regexStartsWith(regexBlockStartsKeywords)) { 972 | block.cursor++; 973 | beautify3(block, result, settings, indent + 1); 974 | } 975 | } 976 | block.cursor--; 977 | } 978 | 979 | function ReserveSemicolonInKeywords(arr: Array) { 980 | for (let i = 0; i < arr.length; i++) { 981 | if (arr[i].match(/FUNCTION|PROCEDURE/) != null) { 982 | arr[i] = arr[i].replace(/;/g, ILSemicolon); 983 | } 984 | } 985 | } 986 | 987 | export function ApplyNoNewLineAfter(arr: Array, noNewLineAfter: Array) { 988 | if (noNewLineAfter == null) { 989 | return; 990 | } 991 | for (let i = 0; i < arr.length; i++) { 992 | noNewLineAfter.forEach(n => { 993 | let regex = new RegExp("(" + n.toUpperCase + ")[ a-z0-9]+[a-z0-9]+"); 994 | if (arr[i].regexIndexOf(regex) >= 0) { 995 | arr[i] += "@@singleline"; 996 | } 997 | }); 998 | } 999 | } 1000 | 1001 | export function RemoveAsserts(arr: Array) { 1002 | let need_semi: boolean = false; 1003 | let inAssert: boolean = false; 1004 | let n: number = 0; 1005 | for (let i = 0; i < arr.length; i++) { 1006 | let has_semi: boolean = arr[i].indexOf(";") >= 0; 1007 | if (need_semi) { 1008 | arr[i] = ''; 1009 | } 1010 | n = arr[i].indexOf("ASSERT "); 1011 | if (n >= 0) { 1012 | inAssert = true; 1013 | arr[i] = ''; 1014 | } 1015 | if (!has_semi) { 1016 | if (inAssert) { 1017 | need_semi = true; 1018 | } 1019 | } 1020 | else { 1021 | need_semi = false; 1022 | } 1023 | } 1024 | } 1025 | 1026 | function escapeText(arr: Array, regex: string, escapedChar: string): Array { 1027 | let quotes: Array = []; 1028 | let regexEpr = new RegExp(regex, "g"); 1029 | for (let i = 0; i < arr.length; i++) { 1030 | let matches = arr[i].match(regexEpr); 1031 | if (matches != null) { 1032 | for (var j = 0; j < matches.length; j++) { 1033 | var match = matches[j]; 1034 | arr[i] = arr[i].replace(match, escapedChar.repeat(match.length)); 1035 | quotes.push(match); 1036 | } 1037 | } 1038 | } 1039 | return quotes; 1040 | } 1041 | 1042 | function RemoveExtraNewLines(input: any) { 1043 | input = input.replace(/(?:\r\n|\r|\n)/g, '\r\n'); 1044 | input = input.replace(/ \r\n/g, '\r\n'); 1045 | input = input.replace(/\r\n\r\n\r\n/g, '\r\n'); 1046 | return input; 1047 | } 1048 | --------------------------------------------------------------------------------