From 6740b62c4136ea0f994ae29858d482d67733c99e Mon Sep 17 00:00:00 2001 From: Gustavo Martin Date: Sun, 12 Oct 2025 11:49:58 +0200 Subject: [PATCH 1/3] Add unordered list support and related tests - Updated the markdown output to include an example of an unordered list. - Created a new test suite for unordered list conversion, covering various scenarios including basic lists, mixed markers, multi-line items, and edge cases. --- src/colibri/documenter/utils.ts | 91 +++++++++- .../documenter/complete/entity.verilogSource | 6 + tests/documenter/complete/entity.vhdlSource | 5 + .../entity_verilogSource_html/output.html | 2 +- .../output.markdown | 12 +- .../entity_vhdlSource_html/output.html | 2 +- .../output.markdown | 11 +- tests/documenter/unordered_list.spec.ts | 161 ++++++++++++++++++ 8 files changed, 282 insertions(+), 8 deletions(-) create mode 100644 tests/documenter/unordered_list.spec.ts diff --git a/src/colibri/documenter/utils.ts b/src/colibri/documenter/utils.ts index 301176c8..efdb1e28 100644 --- a/src/colibri/documenter/utils.ts +++ b/src/colibri/documenter/utils.ts @@ -22,11 +22,94 @@ import * as translator_lib from "./translator"; import * as md from "./markdown_table"; const showdown = require('showdown'); +/** + * Detect and convert unordered list patterns in comments to markdown format + * @param description The description text to process + * @returns The description with unordered lists converted to markdown format + */ +export function convert_unordered_lists_to_markdown(description: string): string { + if (!description) { + return ""; + } + + const lines = description.split('\n'); + const result: string[] = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + const trimmedLine = line.trim(); + + // Check if current line starts a list (- or * followed by space) + const listMatch = trimmedLine.match(/^[*-]\s+(.+)$/); + if (listMatch) { + // Found start of a list, collect all list items + const listItems: string[] = []; + let j = i; // Store start position for backtracking + + while (i < lines.length) { + const currentLine = lines[i]; + const currentTrimmed = currentLine.trim(); + const currentListMatch = currentTrimmed.match(/^[*-]\s+(.+)$/); + + if (currentListMatch) { + // This is a list item + listItems.push(currentListMatch[1]); + i++; + } else if (currentTrimmed === '') { + // Empty line ends the list + break; + } else if (listItems.length > 0 && /^\s+/.test(currentLine) && currentTrimmed !== '') { + // Continuation of previous list item (indented line with content) + listItems[listItems.length - 1] += ' ' + currentTrimmed; + i++; + } else { + // Non-list line, end the list + break; + } + } + + // Only convert to list if we have at least 2 items + if (listItems.length >= 2) { + // Add empty line before list if needed + if (result.length > 0 && result[result.length - 1].trim() !== '') { + result.push(''); + } + + // Add list items in markdown format + for (const item of listItems) { + result.push(`* ${item}`); + } + + // Add empty line after list if there's more content + if (i < lines.length) { + result.push(''); + } + } else { + // Not enough items for a list, treat as regular lines by rewinding + i = j; + result.push(lines[i]); + i++; + } + } else { + // Regular line, add as-is + result.push(lines[i]); + i++; + } + } + + return result.join('\n'); +} + export function normalize_description(description: string): string { if(!description){ return ""; } - let desc_inst = description.replace(/\n\s*\n/g, '
'); + + // Convert unordered lists to markdown format first + let desc_inst = convert_unordered_lists_to_markdown(description); + + desc_inst = desc_inst.replace(/\n\s*\n/g, '
'); desc_inst = desc_inst.replace(/\n/g, ' '); desc_inst = desc_inst.replace(/
/g, ' '); return desc_inst; @@ -36,7 +119,11 @@ export function normalize_description_markdown(description: string): string { if(!description){ return ""; } - const sections = description.split(/(```[\s\S]*?```)/); + + // Convert unordered lists to markdown format first + let processed_description = convert_unordered_lists_to_markdown(description); + + const sections = processed_description.split(/(```[\s\S]*?```)/); for (let i = 0; i < sections.length; i++) { if (!sections[i].startsWith('```')) { diff --git a/tests/documenter/complete/entity.verilogSource b/tests/documenter/complete/entity.verilogSource index 2d57193b..48cffbe8 100644 --- a/tests/documenter/complete/entity.verilogSource +++ b/tests/documenter/complete/entity.verilogSource @@ -6,6 +6,12 @@ //! //! Example of description beakline //! +//! Example of unordered list: +//! * Normal operation mode +//! * Low power mode +//! * Debug mode +//! * Test mode +//! //! Example of Wavedrom //! image: //! { signal: [ diff --git a/tests/documenter/complete/entity.vhdlSource b/tests/documenter/complete/entity.vhdlSource index b1c987bb..d06a4205 100644 --- a/tests/documenter/complete/entity.vhdlSource +++ b/tests/documenter/complete/entity.vhdlSource @@ -7,6 +7,11 @@ use ieee.std_logic_1164.all; --! @version 1.0.1 --! @brief Some description can be added here --! +--! Example of unordered list: +--! * Normal operation mode +--! * Low power mode +--! * Debug mode +--! * Test mode --! --! Example of multiline code snipet: --! ``` C diff --git a/tests/documenter/complete/expected/entity_verilogSource_html/output.html b/tests/documenter/complete/expected/entity_verilogSource_html/output.html index db3ace7d..7980fbd9 100644 --- a/tests/documenter/complete/expected/entity_verilogSource_html/output.html +++ b/tests/documenter/complete/expected/entity_verilogSource_html/output.html @@ -1101,7 +1101,7 @@

Entity: test_entity_name

Diagram

a b c d modport smc_client_tts modport smc_pk modport smc_rt e g wire h ['A:0] macro_ex1 ['BCD:0] macro_ex2 ['XYZ_OP:0] macro_ex3 [7:0] i wire [9:0] k f wire [9:0] l -

Description

This is an entity description. also in multi-lines

Example of description beakline

Example of Wavedrom image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Generics

+

Description

This is an entity description. also in multi-lines

Example of description beakline

Example of unordered list:

Example of Wavedrom image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Generics

diff --git a/tests/documenter/complete/expected/entity_verilogSource_markdown/output.markdown b/tests/documenter/complete/expected/entity_verilogSource_markdown/output.markdown index 569902c8..362b070e 100644 --- a/tests/documenter/complete/expected/entity_verilogSource_markdown/output.markdown +++ b/tests/documenter/complete/expected/entity_verilogSource_markdown/output.markdown @@ -14,12 +14,20 @@ also in multi-lines Example of description beakline +Example of unordered list: + +* Normal operation mode +* Low power mode +* Debug mode +* Test mode + + Example of Wavedrom image: -![alt text](wavedrom_fpUS0.svg "title") +![alt text](wavedrom_fgD90.svg "title") @@ -27,7 +35,7 @@ Example of bitfield: -![alt text](wavedrom_jn9w1.svg "title") +![alt text](wavedrom_D2eS1.svg "title") diff --git a/tests/documenter/complete/expected/entity_vhdlSource_html/output.html b/tests/documenter/complete/expected/entity_vhdlSource_html/output.html index 2c591ab7..8e06b08d 100644 --- a/tests/documenter/complete/expected/entity_vhdlSource_html/output.html +++ b/tests/documenter/complete/expected/entity_vhdlSource_html/output.html @@ -1101,7 +1101,7 @@

Entity: test_entity_name

Diagram

integer a std_logic b std_logic_vector(1 downto 0) c std_logic_vector(1 downto 0) d std_logic e std_logic ee std_logic_vector(31 downto 0) h std_logic_vector(31 downto 0) i std_logic p std_logic r virtual_bus v_bus_1 std_logic q std_logic s virtual_bus v_bus_0 -

Description

This is an entity description 5 multiline.

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Example of description beakline

Example of Wavedrom

image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Generics

+

Description

This is an entity description 5 multiline.

Example of unordered list:

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Example of description beakline

Example of Wavedrom

image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Generics

diff --git a/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown b/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown index 5cff010c..8201d877 100644 --- a/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown +++ b/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown @@ -12,6 +12,13 @@ This is an entity description 5 multiline. +Example of unordered list: + +* Normal operation mode +* Low power mode +* Debug mode +* Test mode + Example of multiline code snipet: @@ -44,7 +51,7 @@ image: -![alt text](wavedrom_1FFZ0.svg "title") +![alt text](wavedrom_0G8l0.svg "title") @@ -52,7 +59,7 @@ Example of bitfield: -![alt text](wavedrom_Gs4Z1.svg "title") +![alt text](wavedrom_LQPE1.svg "title") diff --git a/tests/documenter/unordered_list.spec.ts b/tests/documenter/unordered_list.spec.ts new file mode 100644 index 00000000..658c38f9 --- /dev/null +++ b/tests/documenter/unordered_list.spec.ts @@ -0,0 +1,161 @@ +// Test cases for the unordered list feature +import { convert_unordered_lists_to_markdown } from "../../src/colibri/documenter/utils"; + +describe('Unordered List Conversion Tests', () => { + + test('Basic asterisk list conversion', () => { + const input = `This is a description with a list: +* First item +* Second item +* Third item +End of description.`; + + const expected = `This is a description with a list: + +* First item +* Second item +* Third item + +End of description.`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('Basic dash list conversion', () => { + const input = `Configuration options: +- Option A +- Option B +- Option C`; + + const expected = `Configuration options: + +* Option A +* Option B +* Option C`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('Mixed list markers should work', () => { + const input = `Mixed list: +* Item with asterisk +- Item with dash +* Another asterisk item`; + + const expected = `Mixed list: + +* Item with asterisk +* Item with dash +* Another asterisk item`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('Single item should not be converted to list', () => { + const input = `Single item: +* Only one item +Regular text continues.`; + + const expected = `Single item: +* Only one item +Regular text continues.`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('Multi-line list items', () => { + const input = `Complex list: +* First item with continuation + on the next line +* Second item also spans + multiple lines +* Third item`; + + const expected = `Complex list: + +* First item with continuation on the next line +* Second item also spans multiple lines +* Third item`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('Empty lines should terminate list', () => { + const input = `List with break: +* First item +* Second item + +* This starts a new list +* Fourth item`; + + const expected = `List with break: + +* First item +* Second item + + +* This starts a new list +* Fourth item`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('No conversion for non-list content', () => { + const input = `Regular description without lists. +This is just normal text. +No special formatting here.`; + + const expected = `Regular description without lists. +This is just normal text. +No special formatting here.`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('Empty or null input', () => { + expect(convert_unordered_lists_to_markdown("")).toBe(""); + expect(convert_unordered_lists_to_markdown(null as any)).toBe(""); + expect(convert_unordered_lists_to_markdown(undefined as any)).toBe(""); + }); + + test('List at beginning of text', () => { + const input = `* First item +* Second item +* Third item +Regular text follows.`; + + const expected = `* First item +* Second item +* Third item + +Regular text follows.`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); + + test('List at end of text', () => { + const input = `Description text here. +Available options: +* Option 1 +* Option 2 +* Option 3`; + + const expected = `Description text here. +Available options: + +* Option 1 +* Option 2 +* Option 3`; + + const result = convert_unordered_lists_to_markdown(input); + expect(result).toBe(expected); + }); +}); \ No newline at end of file From 15cfec2d8fb6d77bf80366be5c8caec4cdd3bbbc Mon Sep 17 00:00:00 2001 From: Gustavo Martin Date: Sun, 14 Dec 2025 20:07:48 +0100 Subject: [PATCH 2/3] conflicts --- auto_package/language.yml | 26 ++- auto_package/templates/dependencie.nj | 6 +- .../templates/language_and_snippet.nj | 7 +- package.json | 68 ++++--- resources/icon/teroshdl-icon-theme.json | 122 ------------ src/colibri/documenter/diagram.ts | 15 +- src/colibri/documenter/doxygen_parser.ts | 41 +++- src/colibri/parser/common.ts | 1 + test_keepports_diagram.ts | 54 ++++++ tests/documenter/complete/entity.vhdlSource | 27 ++- .../entity_vhdlSource_html/output.html | 180 +++++++++++++++++- .../output.markdown | 66 +++++-- 12 files changed, 420 insertions(+), 193 deletions(-) delete mode 100644 resources/icon/teroshdl-icon-theme.json create mode 100644 test_keepports_diagram.ts diff --git a/auto_package/language.yml b/auto_package/language.yml index 2488a99a..3ff1d683 100644 --- a/auto_package/language.yml +++ b/auto_package/language.yml @@ -3,18 +3,27 @@ configuration: "./configs/vhdl.configuration.json" snippets: "./snippets/vhdl/vhdl.json" aliases: ["VHDL", "vhdl"] + icon: + light: "./resources/icon/light/vhdl.svg" + dark: "./resources/icon/dark/vhdl.svg" - id: "verilog" extensions: [".v", ".vh", ".vl"] configuration: "./configs/verilog.configuration.json" snippets: "./snippets/verilog/verilog.json" aliases: ["Verilog", "verilog"] + icon: + light: "./resources/icon/light/verilog.svg" + dark: "./resources/icon/dark/verilog.svg" - id: "systemverilog" extensions: [".sv", ".svh", ".SV"] configuration: "./configs/systemverilog.configuration.json" snippets: "./snippets/systemverilog/systemverilog.json" aliases: ["System Verilog", "systemverilog"] + icon: + light: "./resources/icon/light/systemverilog.svg" + dark: "./resources/icon/dark/systemverilog.svg" - id: "vcd" extensions: [".vcd"] @@ -33,26 +42,41 @@ configuration: "./configs/tcl.configuration.json" snippets: "./snippets/tcl/tcl.json" aliases: ["TCL", "tcl"] + icon: + light: "./resources/icon/light/tcl.svg" + dark: "./resources/icon/dark/tcl.svg" - id: "ucf" extensions: [".ucf"] configuration: "./configs/ucfconstraints.configuration.json" aliases: ["vivado ucf", "ucf constraints"] + icon: + light: "./resources/icon/light/ucf.svg" + dark: "./resources/icon/dark/ucf.svg" - id: "xdc" extensions: [".xdc", ".sdc"] configuration: "./configs/xdcconstraints.configuration.json" snippets: "./snippets/xdc/xdc.json" aliases: ["vivado xdc", "xdc constraints"] + icon: + light: "./resources/icon/light/xdc.svg" + dark: "./resources/icon/dark/xdc.svg" - id: "ldc" extensions: [".ldc", ".pdc"] configuration: "./configs/xdcconstraints.configuration.json" snippets: "./snippets/xdc/xdc.json" aliases: ["lattice constraints"] + icon: + light: "./resources/icon/light/xdc.svg" + dark: "./resources/icon/dark/xdc.svg" - id: "TL-Verilog" extensions: [".tlv"] configuration: "./configs/tlverilog.configuration.json" snippets: "./snippets/tlverilog/tlverilog.json" - aliases: ["TL-Verilog", "tlv", "Transactional-Level Verilog"] \ No newline at end of file + aliases: ["TL-Verilog", "tlv", "Transactional-Level Verilog"] + icon: + light: "./resources/icon/light/verilog.svg" + dark: "./resources/icon/dark/verilog.svg" \ No newline at end of file diff --git a/auto_package/templates/dependencie.nj b/auto_package/templates/dependencie.nj index fb1fd1dc..2336c4f4 100644 --- a/auto_package/templates/dependencie.nj +++ b/auto_package/templates/dependencie.nj @@ -7,11 +7,11 @@ "netlistsvg": "https://github.com/TerosTechnology/netlistsvg.git#hierarchy", "open": "8.3.0", "proper-lockfile": "4.1.1", - "semver": "7.3.5", + "semver": "^7.6.3", "shelljs": "^0.8.3", "tmp": "0.2.1", "vscode-languageclient": "^9.0.1", - "xml2js": "0.4.23", + "xml2js": "^0.6.2", "nunjucks": "^3.2.0", "@types/nunjucks": "^3.1.4", "tree-kill": "1.2.2", @@ -29,7 +29,7 @@ "fast-xml-parser": "4.3.2", "jest-html-reporter": "^3.7.0", "js-yaml": "^4.1.0", - "json5": "2.1.3", + "json5": "^2.2.3", "lodash": "^4.17.21", "onml": "2.1.0", "pyodide": "0.24.1", diff --git a/auto_package/templates/language_and_snippet.nj b/auto_package/templates/language_and_snippet.nj index d3d34232..aa8873c1 100644 --- a/auto_package/templates/language_and_snippet.nj +++ b/auto_package/templates/language_and_snippet.nj @@ -13,7 +13,12 @@ {%- if "configuration" in language %} "configuration": "{{language["configuration"]}}", {%- endif %} - "extensions": {{language["extensions"] | replace("'", '"')}} + "extensions": {{language["extensions"] | replace("'", '"')}}{%- if "icon" in language %}, + "icon": { + "light": "{{language["icon"]["light"]}}", + "dark": "{{language["icon"]["dark"]}}" + } + {%- endif %} } {% set ns.counter = ns.counter + 1 %} {%- endfor %} diff --git a/package.json b/package.json index abc93f95..1bc3c141 100644 --- a/package.json +++ b/package.json @@ -38,13 +38,6 @@ }, "contributes": { - "iconThemes": [ - { - "id": "teroshdl-icons", - "label": "TerosHDL File Icons", - "path": "./resources/icon/teroshdl-icon-theme.json" - } - ], "viewsContainers": { "activitybar": [ { @@ -784,21 +777,33 @@ "id": "vhdl", "aliases": ["VHDL", "vhdl"], "configuration": "./configs/vhdl.configuration.json", - "extensions": [".vhd", ".vho", ".vhdl", ".vhd"] + "extensions": [".vhd", ".vho", ".vhdl", ".vhd"], + "icon": { + "light": "./resources/icon/light/vhdl.svg", + "dark": "./resources/icon/dark/vhdl.svg" + } } ,{ "id": "verilog", "aliases": ["Verilog", "verilog"], "configuration": "./configs/verilog.configuration.json", - "extensions": [".v", ".vh", ".vl"] + "extensions": [".v", ".vh", ".vl"], + "icon": { + "light": "./resources/icon/light/verilog.svg", + "dark": "./resources/icon/dark/verilog.svg" + } } ,{ "id": "systemverilog", "aliases": ["System Verilog", "systemverilog"], "configuration": "./configs/systemverilog.configuration.json", - "extensions": [".sv", ".svh", ".SV"] + "extensions": [".sv", ".svh", ".SV"], + "icon": { + "light": "./resources/icon/light/systemverilog.svg", + "dark": "./resources/icon/dark/systemverilog.svg" + } } ,{ @@ -825,35 +830,55 @@ "id": "tcl", "aliases": ["TCL", "tcl"], "configuration": "./configs/tcl.configuration.json", - "extensions": [".tcl", ".pro"] + "extensions": [".tcl", ".pro"], + "icon": { + "light": "./resources/icon/light/tcl.svg", + "dark": "./resources/icon/dark/tcl.svg" + } } ,{ "id": "ucf", "aliases": ["vivado ucf", "ucf constraints"], "configuration": "./configs/ucfconstraints.configuration.json", - "extensions": [".ucf"] + "extensions": [".ucf"], + "icon": { + "light": "./resources/icon/light/ucf.svg", + "dark": "./resources/icon/dark/ucf.svg" + } } ,{ "id": "xdc", "aliases": ["vivado xdc", "xdc constraints"], "configuration": "./configs/xdcconstraints.configuration.json", - "extensions": [".xdc", ".sdc"] + "extensions": [".xdc", ".sdc"], + "icon": { + "light": "./resources/icon/light/xdc.svg", + "dark": "./resources/icon/dark/xdc.svg" + } } ,{ "id": "ldc", "aliases": ["lattice constraints"], "configuration": "./configs/xdcconstraints.configuration.json", - "extensions": [".ldc", ".pdc"] + "extensions": [".ldc", ".pdc"], + "icon": { + "light": "./resources/icon/light/xdc.svg", + "dark": "./resources/icon/dark/xdc.svg" + } } ,{ "id": "TL-Verilog", "aliases": ["TL-Verilog", "tlv", "Transactional-Level Verilog"], "configuration": "./configs/tlverilog.configuration.json", - "extensions": [".tlv"] + "extensions": [".tlv"], + "icon": { + "light": "./resources/icon/light/verilog.svg", + "dark": "./resources/icon/dark/verilog.svg" + } } ], @@ -899,18 +924,7 @@ "mac": "command+delete", "when": "editorTextFocus" } - ], - - "configuration": { - "title": "TerosHDL", - "properties": { - "teroshdl.fileIcons.enabled": { - "type": "boolean", - "default": true, - "markdownDescription": "Enable TerosHDL file icons. To apply changes:\n1. Open Command Palette (Ctrl+Shift+P)\n2. Type 'File Icon Theme'\n3. Select 'TerosHDL File Icons' to enable or 'None' to disable" - } - } - } + ] }, "scripts": { diff --git a/resources/icon/teroshdl-icon-theme.json b/resources/icon/teroshdl-icon-theme.json deleted file mode 100644 index da7dd1e4..00000000 --- a/resources/icon/teroshdl-icon-theme.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "iconDefinitions": { - "_vhdl_file": { - "iconPath": "./light/vhdl.svg" - }, - "_vhdl_file_dark": { - "iconPath": "./dark/vhdl.svg" - }, - "_verilog_file": { - "iconPath": "./light/verilog.svg" - }, - "_verilog_file_dark": { - "iconPath": "./dark/verilog.svg" - }, - "_systemverilog_file": { - "iconPath": "./light/systemverilog.svg" - }, - "_systemverilog_file_dark": { - "iconPath": "./dark/systemverilog.svg" - }, - "_tcl_file": { - "iconPath": "./light/tcl.svg" - }, - "_tcl_file_dark": { - "iconPath": "./dark/tcl.svg" - }, - "_xdc_file": { - "iconPath": "./light/xdc.svg" - }, - "_xdc_file_dark": { - "iconPath": "./dark/xdc.svg" - }, - "_ucf_file": { - "iconPath": "./light/ucf.svg" - }, - "_ucf_file_dark": { - "iconPath": "./dark/ucf.svg" - } - }, - "fileExtensions": { - "vhd": "_vhdl_file", - "vhdl": "_vhdl_file", - "vho": "_vhdl_file", - "v": "_verilog_file", - "vh": "_verilog_file", - "vl": "_verilog_file", - "sv": "_systemverilog_file", - "svh": "_systemverilog_file", - "tcl": "_tcl_file", - "pro": "_tcl_file", - "xdc": "_xdc_file", - "sdc": "_xdc_file", - "ldc": "_xdc_file", - "pdc": "_xdc_file", - "ucf": "_ucf_file" - }, - "languageIds": { - "vhdl": "_vhdl_file", - "verilog": "_verilog_file", - "systemverilog": "_systemverilog_file", - "tcl": "_tcl_file", - "xdc": "_xdc_file", - "ldc": "_xdc_file", - "ucf": "_ucf_file" - }, - "light": { - "fileExtensions": { - "vhd": "_vhdl_file", - "vhdl": "_vhdl_file", - "vho": "_vhdl_file", - "v": "_verilog_file", - "vh": "_verilog_file", - "vl": "_verilog_file", - "sv": "_systemverilog_file", - "svh": "_systemverilog_file", - "tcl": "_tcl_file", - "pro": "_tcl_file", - "xdc": "_xdc_file", - "sdc": "_xdc_file", - "ldc": "_xdc_file", - "pdc": "_xdc_file", - "ucf": "_ucf_file" - }, - "languageIds": { - "vhdl": "_vhdl_file", - "verilog": "_verilog_file", - "systemverilog": "_systemverilog_file", - "tcl": "_tcl_file", - "xdc": "_xdc_file", - "ldc": "_xdc_file", - "ucf": "_ucf_file" - } - }, - "highContrast": { - "fileExtensions": { - "vhd": "_vhdl_file_dark", - "vhdl": "_vhdl_file_dark", - "vho": "_vhdl_file_dark", - "v": "_verilog_file_dark", - "vh": "_verilog_file_dark", - "vl": "_verilog_file_dark", - "sv": "_systemverilog_file_dark", - "svh": "_systemverilog_file_dark", - "tcl": "_tcl_file_dark", - "pro": "_tcl_file_dark", - "xdc": "_xdc_file_dark", - "sdc": "_xdc_file_dark", - "ldc": "_xdc_file_dark", - "pdc": "_xdc_file_dark", - "ucf": "_ucf_file_dark" - }, - "languageIds": { - "vhdl": "_vhdl_file_dark", - "verilog": "_verilog_file_dark", - "systemverilog": "_systemverilog_file_dark", - "tcl": "_tcl_file_dark", - "xdc": "_xdc_file_dark", - "ldc": "_xdc_file_dark", - "ucf": "_ucf_file_dark" - } - } -} diff --git a/src/colibri/documenter/diagram.ts b/src/colibri/documenter/diagram.ts index 4d3e99eb..e0510d03 100644 --- a/src/colibri/documenter/diagram.ts +++ b/src/colibri/documenter/diagram.ts @@ -164,8 +164,21 @@ function get_ports(hdl_element: common_hdl.Hdl_element) { const port_list = hdl_element.get_port_array(); const port_list_vbus = doxygen.get_virtual_bus(port_list); let complete_list: any[] = []; + + // Add normal ports (not part of virtual buses) complete_list = complete_list.concat(port_list_vbus.port_list); - complete_list = complete_list.concat(port_list_vbus.v_port_list); + + // Process virtual buses + for (const virtual_bus of port_list_vbus.v_port_list) { + if (virtual_bus.keepports) { + // If @keepports is enabled, expand virtual bus to show individual ports + complete_list = complete_list.concat(virtual_bus.port_list); + } else { + // If @keepports is not enabled, show virtual bus as single entity + complete_list.push(virtual_bus); + } + } + return complete_list; } diff --git a/src/colibri/documenter/doxygen_parser.ts b/src/colibri/documenter/doxygen_parser.ts index 478beb06..e4ff5fce 100644 --- a/src/colibri/documenter/doxygen_parser.ts +++ b/src/colibri/documenter/doxygen_parser.ts @@ -67,6 +67,7 @@ export function parse_virtualbus_init(description: string) { name: '', direction: 'in', notable: false, + keepports: false, description: '', to_delete: '' }; @@ -83,6 +84,7 @@ export function parse_virtualbus_init(description: string) { result.notable = true; corpus = notable_element.text; } + //Port name const virtual_port_element = parse_element('virtualbus', corpus); if (virtual_port_element.element_list.length === 0) { @@ -96,14 +98,21 @@ export function parse_virtualbus_init(description: string) { // Direction const direction_port_element = parse_element('dir', corpus); - if (direction_port_element.element_list.length === 0) { - result.description = corpus.trim(); - return result; + if (direction_port_element.element_list.length !== 0) { + corpus = direction_port_element.element_list[0].description.trim(); + element_0 = get_first_element(corpus); + result.direction = element_0.name; + corpus = element_0.text.trim(); + } + + // Keep ports + const keepports_element = is_keepports(corpus); + if (keepports_element.is_in === true) { + result.keepports = true; + corpus = keepports_element.text; } - corpus = direction_port_element.element_list[0].description.trim(); - element_0 = get_first_element(corpus); - result.direction = element_0.name; - result.description = element_0.text.trim(); + + result.description = corpus.trim(); if (result.is_in === true) { return result; } @@ -125,6 +134,20 @@ function is_notable(text: string) { return result; } +function is_keepports(text: string) { + const regex = /@keepports/gms; + const element_parser = text.match(regex); + const result = { + text: text, + is_in: false + }; + if (element_parser !== null) { + result.is_in = true; + result.text = text.replace(regex, '').trim(); + } + return result; +} + function get_first_element(text: string) { let corpus_split = text.split(' '); const name = corpus_split[0]; @@ -188,6 +211,7 @@ export function get_virtual_bus(port_list: common_hdl.Port_hdl[]) { }, direction: virtual_port.direction, notable: virtual_port.notable, + keepports: virtual_port.keepports, port_list: [], type: "virtual_bus" }; @@ -196,7 +220,7 @@ export function get_virtual_bus(port_list: common_hdl.Port_hdl[]) { info: { position: port_i.info.position, name: port_i.info.name, - description: port_description.replace(virtual_port.to_delete, '') + description: port_i.inline_comment.trim() }, inline_comment: "", over_comment: "", @@ -279,6 +303,7 @@ export function get_virtual_bus(port_list: common_hdl.Port_hdl[]) { }, direction: virtual_port.direction, notable: virtual_port.notable, + keepports: virtual_port.keepports, port_list: [], type: "virtual_bus" }; diff --git a/src/colibri/parser/common.ts b/src/colibri/parser/common.ts index 9787dcb8..22c329b0 100644 --- a/src/colibri/parser/common.ts +++ b/src/colibri/parser/common.ts @@ -114,6 +114,7 @@ export type Virtual_bus_hdl = { info: Common_info; direction: string; notable: boolean; + keepports: boolean; port_list: Port_hdl[]; type: string; }; diff --git a/test_keepports_diagram.ts b/test_keepports_diagram.ts new file mode 100644 index 00000000..cb52239c --- /dev/null +++ b/test_keepports_diagram.ts @@ -0,0 +1,54 @@ +import { documenter_factory } from "../src/colibri/documenter/factory"; +import { HDL_LANG } from "../src/colibri/common/general"; +import { Diagram } from "../src/colibri/documenter/diagram"; +import * as fs from "fs"; +import * as path from "path"; + +async function test_keepports_diagram() { + const test_file = path.join(__dirname, "test_keepports_diagram.vhdl"); + const vhdl_code = fs.readFileSync(test_file, "utf8"); + + console.log("Testing @keepports diagram functionality..."); + + // Create documenter + const documenter = documenter_factory(HDL_LANG.VHDL); + + // Parse the VHDL code + const result = await documenter.get_documentation(vhdl_code, { + language: HDL_LANG.VHDL, + path: test_file, + configuration: { + enable_markdown: true, + enable_comments: true + } + }); + + console.log("Parsed entities:", result.length); + + if (result.length > 0) { + const entity = result[0]; + console.log("Entity name:", entity.name); + console.log("Number of ports:", entity.port.length); + console.log("Virtual buses:", entity.virtual_bus); + + // Create diagram + const diagram = new Diagram(); + const svg_content = diagram.get_entity_as_svg(entity); + + // Save diagram to file for visual inspection + const output_file = path.join(__dirname, "keepports_diagram_test.svg"); + fs.writeFileSync(output_file, svg_content); + console.log("Diagram saved to:", output_file); + + // Check virtual bus properties + entity.virtual_bus.forEach((vbus, index) => { + console.log(`Virtual bus ${index + 1}:`); + console.log(` Name: ${vbus.name}`); + console.log(` Description: ${vbus.description}`); + console.log(` Keepports: ${vbus.keepports}`); + console.log(` Ports: ${vbus.port_list.map(p => p.name).join(", ")}`); + }); + } +} + +test_keepports_diagram().catch(console.error); \ No newline at end of file diff --git a/tests/documenter/complete/entity.vhdlSource b/tests/documenter/complete/entity.vhdlSource index daa48708..9e338372 100644 --- a/tests/documenter/complete/entity.vhdlSource +++ b/tests/documenter/complete/entity.vhdlSource @@ -83,15 +83,34 @@ entity test_entity_name is --! @virtualbus v_bus_1 @keepports Description of virtual bus 1 j : in std_logic := '1'; l : out std_logic; - m : in std_logic; + m : in std_logic; --! @end --! @virtualbus v_bus_2 Description of virtual bus 2 n : in std_logic; --! Description 3 - o : out std_logic; --! @end - p : in std_logic; + o : out std_logic; + p : in std_logic; --! @end q : out std_logic; --! Inline comment r : in std_logic; --! Over comment - s : out std_logic --! Preference inline + s : out std_logic; --! Preference inline + --! @virtualbus video_in_axi_stream @dir in a slave axi stream interface for video in + video_in_tdata : in std_logic_vector(23 downto 0); --! axis data bus + video_in_tlast : in std_logic; --! axis last + video_in_tuser : in std_logic_vector(0 downto 0); --! axis user + video_in_tvalid : in std_logic; --! axis valid handshake signal + video_in_tready : out std_logic; --! axis ready handshake signal + --! @end + --! @virtualbus video_out_axi_stream @dir out @keepports a master axi stream interface for video out + video_out_tdata : out std_logic_vector(23 downto 0); --! axis data bus video_out + video_out_tlast : out std_logic; --! axis last + video_out_tuser : out std_logic_vector(0 downto 0); --! axis user + video_out_tvalid : out std_logic; --! axis valid handshake signal + video_out_tready : in std_logic; --! axis ready handshake signal + --! @end + --! @virtualbus SPI @dir out description of SPI + cs : OUT std_logic; --! Chip select + clk_out: OUT std_logic; --! SPI clock + miso: OUT std_logic; --! Master in slave out + mosi: OUT std_logic --! Master out slave in @end ); end test_entity_name; diff --git a/tests/documenter/complete/expected/entity_vhdlSource_html/output.html b/tests/documenter/complete/expected/entity_vhdlSource_html/output.html index de2a5ef7..33917a5f 100644 --- a/tests/documenter/complete/expected/entity_vhdlSource_html/output.html +++ b/tests/documenter/complete/expected/entity_vhdlSource_html/output.html @@ -1100,8 +1100,8 @@

Entity: test_entity_name

Diagram

integer a std_logic b std_logic_vector(1 downto 0) c std_logic_vector(1 downto 0) d std_logic e std_logic ee std_logic_vector(31 downto 0) h std_logic_vector(31 downto 0) i std_logic p std_logic r virtual_bus v_bus_1 std_logic q std_logic s virtual_bus v_bus_0 -

Description

This is an entity description 5 multiline.

Example of unordered list:

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Example of description beakline

Example of Wavedrom

image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Generics

+

Diagram

integer a std_logic b std_logic_vector(1 downto 0) c std_logic_vector(1 downto 0) d std_logic e std_logic ee std_logic_vector(31 downto 0) h std_logic_vector(31 downto 0) i std_logic r std_logic j std_logic m virtual_bus v_bus_2 virtual_bus video_in_axi_stream std_logic video_out_tready std_logic q std_logic s std_logic f std_logic ff std_logic g std_logic l std_logic_vector(23 downto 0) video_out_tdata std_logic video_out_tlast std_logic_vector(0 downto 0) video_out_tuser std_logic video_out_tvalid virtual_bus SPI +

Description

This is an entity description 5 multiline.

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Example of description beakline

Example of Wavedrom

image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Generics

@@ -1174,12 +1174,6 @@

Ports

- - - - - - @@ -1201,13 +1195,37 @@

Ports

- + - + + + + + + + + + + + + + + + + + + + + + + + + +
pinstd_logic
q out std_logic v_bus_0 out Virtual bus@keepports Description of virtual bus 0Description of virtual bus 0
v_bus_1 in Virtual bus@keepports Description of virtual bus 1Description of virtual bus 1
v_bus_2inVirtual busDescription of virtual bus 2
video_in_axi_streaminVirtual busa slave axi stream interface for video in
video_out_axi_streamoutVirtual busa master axi stream interface for video out
SPIoutVirtual busdescription of SPI
@@ -1272,6 +1290,19 @@

v_bus_1

std_logic + + +

v_bus_2

+ + + + + + + + + + @@ -1284,6 +1315,135 @@

v_bus_1

+ + + + + + + +
Port nameDirectionTypeDescription
n in std_logic
pinstd_logic
+

video_in_axi_stream

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Port nameDirectionTypeDescription
video_in_tdatainstd_logic_vector(23 downto 0)axis data bus
video_in_tlastinstd_logicaxis last
video_in_tuserinstd_logic_vector(0 downto 0)axis user
video_in_tvalidinstd_logicaxis valid handshake signal
video_in_treadyoutstd_logicaxis ready handshake signal
+

video_out_axi_stream

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Port nameDirectionTypeDescription
video_out_tdataoutstd_logic_vector(23 downto 0)axis data bus video_out
video_out_tlastoutstd_logicaxis last
video_out_tuseroutstd_logic_vector(0 downto 0)axis user
video_out_tvalidoutstd_logicaxis valid handshake signal
video_out_treadyinstd_logicaxis ready handshake signal
+

SPI

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Port nameDirectionTypeDescription
csoutstd_logicChip select
clk_outoutstd_logicSPI clock
misooutstd_logicMaster in slave out
mosioutstd_logicMaster out slave in

Signals

diff --git a/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown b/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown index b057da40..03a0a44c 100644 --- a/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown +++ b/tests/documenter/complete/expected/entity_vhdlSource_markdown/output.markdown @@ -51,7 +51,7 @@ image: -![alt text](wavedrom_0G8l0.svg "title") +![alt text](wavedrom_6Vtl0.svg "title") @@ -59,7 +59,7 @@ Example of bitfield: -![alt text](wavedrom_LQPE1.svg "title") +![alt text](wavedrom_wHVW1.svg "title") @@ -76,17 +76,20 @@ Example of bitfield: ## Ports -| Port name | Direction | Type | Description | -| --------- | --------- | ----------------------------- | ---------------------------------------- | -| ee | in | std_logic | Over comment in ```port``` | -| h | in | std_logic_vector(31 downto 0) | | -| i | in | std_logic_vector(31 downto 0) | | -| p | in | std_logic | | -| q | out | std_logic | Inline comment | -| r | in | std_logic | | -| s | out | std_logic | Preference inline | -| v_bus_0 | out | Virtual bus | @keepports Description of virtual bus 0 | -| v_bus_1 | in | Virtual bus | @keepports Description of virtual bus 1 | +| Port name | Direction | Type | Description | +| -------------------- | --------- | ----------------------------- | ------------------------------------------- | +| ee | in | std_logic | Over comment in ```port``` | +| h | in | std_logic_vector(31 downto 0) | | +| i | in | std_logic_vector(31 downto 0) | | +| q | out | std_logic | Inline comment | +| r | in | std_logic | | +| s | out | std_logic | Preference inline | +| v_bus_0 | out | Virtual bus | Description of virtual bus 0 | +| v_bus_1 | in | Virtual bus | Description of virtual bus 1 | +| v_bus_2 | in | Virtual bus | Description of virtual bus 2 | +| video_in_axi_stream | in | Virtual bus | a slave axi stream interface for video in | +| video_out_axi_stream | out | Virtual bus | a master axi stream interface for video out | +| SPI | out | Virtual bus | description of SPI | ### Virtual Buses @@ -99,13 +102,44 @@ Example of bitfield: | g | inout | std_logic | | #### v_bus_1 +| Port name | Direction | Type | Description | +| --------- | --------- | --------- | ----------- | +| j | in | std_logic | | +| l | out | std_logic | | +| m | in | std_logic | | +#### v_bus_2 + | Port name | Direction | Type | Description | | --------- | --------- | --------- | ------------- | -| j | in | std_logic | | -| l | out | std_logic | | -| m | in | std_logic | | | n | in | std_logic | Description 3 | | o | out | std_logic | | +| p | in | std_logic | | +#### video_in_axi_stream + +| Port name | Direction | Type | Description | +| --------------- | --------- | ----------------------------- | --------------------------- | +| video_in_tdata | in | std_logic_vector(23 downto 0) | axis data bus | +| video_in_tlast | in | std_logic | axis last | +| video_in_tuser | in | std_logic_vector(0 downto 0) | axis user | +| video_in_tvalid | in | std_logic | axis valid handshake signal | +| video_in_tready | out | std_logic | axis ready handshake signal | +#### video_out_axi_stream + +| Port name | Direction | Type | Description | +| ---------------- | --------- | ----------------------------- | --------------------------- | +| video_out_tdata | out | std_logic_vector(23 downto 0) | axis data bus video_out | +| video_out_tlast | out | std_logic | axis last | +| video_out_tuser | out | std_logic_vector(0 downto 0) | axis user | +| video_out_tvalid | out | std_logic | axis valid handshake signal | +| video_out_tready | in | std_logic | axis ready handshake signal | +#### SPI + +| Port name | Direction | Type | Description | +| --------- | --------- | --------- | ------------------- | +| cs | out | std_logic | Chip select | +| clk_out | out | std_logic | SPI clock | +| miso | out | std_logic | Master in slave out | +| mosi | out | std_logic | Master out slave in | ## Signals From 3b8c0746e209cd6bcb7caef234e144c6794fe630 Mon Sep 17 00:00:00 2001 From: Gustavo Martin Date: Sun, 14 Dec 2025 20:44:27 +0100 Subject: [PATCH 3/3] fix --- .../complete/expected/entity_vhdlSource_html/output.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/documenter/complete/expected/entity_vhdlSource_html/output.html b/tests/documenter/complete/expected/entity_vhdlSource_html/output.html index 33917a5f..06aa5d2a 100644 --- a/tests/documenter/complete/expected/entity_vhdlSource_html/output.html +++ b/tests/documenter/complete/expected/entity_vhdlSource_html/output.html @@ -1101,7 +1101,7 @@

Entity: test_entity_name

Diagram

integer a std_logic b std_logic_vector(1 downto 0) c std_logic_vector(1 downto 0) d std_logic e std_logic ee std_logic_vector(31 downto 0) h std_logic_vector(31 downto 0) i std_logic r std_logic j std_logic m virtual_bus v_bus_2 virtual_bus video_in_axi_stream std_logic video_out_tready std_logic q std_logic s std_logic f std_logic ff std_logic g std_logic l std_logic_vector(23 downto 0) video_out_tdata std_logic video_out_tlast std_logic_vector(0 downto 0) video_out_tuser std_logic video_out_tvalid virtual_bus SPI -

Description

This is an entity description 5 multiline.

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Example of description beakline

Example of Wavedrom

image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Generics

+

Description

This is an entity description 5 multiline.

Example of unordered list:

Example of multiline code snipet:

int* versions = 0x0080000000 ;
int* major = 0x0090000000 ;
int* minor = 0x00A0000000 ;
int* patch = 0x00B0000000 ;
print_version();

Example of multiline code snipet:

function sum(a : integer := 0; b : integer := 0)
return integer is
variable result : integer;
begin
result <= a + b;
return result;
end function;

Example of description beakline

Example of Wavedrom

image:


pclkPclknclkNclkclk0clk1clk2clk3clk4

Example of bitfield:


06711121415192031opcoderdfunc3rs1imm[11:0]OP-IMMdestADDISLTISLTIUANDIORIXORIsrcI-immediate[11:0]

Generics