From 508ebdc8e641dbf8b1897fbb968ea75ed3579917 Mon Sep 17 00:00:00 2001 From: Nikolay Puzanov Date: Sun, 8 Feb 2026 13:47:42 +0300 Subject: [PATCH] Add function for alignment of signal declarations. --- README.md | 6 ++ verilog-align-ports-test.el | 62 +++++++++++++++ verilog-align-ports.el | 145 ++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) diff --git a/README.md b/README.md index a7626ef..c8e2222 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Align SystemVerilog ANSI-style port declarations in a contiguous block around po - Works on the contiguous block of port declarations above and below point. - Stops at the first non-port line (including blank lines). - Aligns named port connections in module instantiations via `verilog-align-ports-instantiation`. +- Aligns signal declarations via `verilog-align-ports-declarations`. ## Installation @@ -26,6 +27,11 @@ For module instantiations: 1. Place point on a named port connection line (e.g., `.clk_i (clk)` or `.full_o,`). 2. Run `M-x verilog-align-ports-instantiation`. +For signal declarations: + +1. Place point on a declaration line (e.g., `logic foo;` or `wire [3:0] bar;`). +2. Run `M-x verilog-align-ports-declarations`. + Example input: ```systemverilog diff --git a/verilog-align-ports-test.el b/verilog-align-ports-test.el index c204dfc..097f136 100644 --- a/verilog-align-ports-test.el +++ b/verilog-align-ports-test.el @@ -140,6 +140,68 @@ (beginning-of-line) (should (not (verilog-align-ports-instantiation))))) +(ert-deftest verilog-align-ports-declarations-aligns-block () + (let* ((input (concat + (mapconcat + #'identity + '("logic wr_en_i; // write enable" + "logic [DataWidth-1:0] d_i; // data input" + "logic full_o;" + "logic [WrDataCountW-1:0] wr_data_count_o; // data count" + "wire rd_en_i; // read enable" + "logic [DataWidth-1:0] d_o;" + "reg empty_o;// empty flag" + "logic valid_o;") + "\n") + "\n")) + (expected (concat + (mapconcat + #'identity + (list + (concat "logic" (make-string 20 ?\s) + "wr_en_i;" (make-string 9 ?\s) + "// write enable") + (concat "logic" " " "[DataWidth-1:0]" (make-string 4 ?\s) + "d_i;" (make-string 13 ?\s) + "// data input") + (concat "logic" (make-string 20 ?\s) "full_o;") + (concat "logic" " " "[WrDataCountW-1:0]" " " + "wr_data_count_o;" " " "// data count") + (concat "wire" (make-string 21 ?\s) + "rd_en_i;" (make-string 9 ?\s) + "// read enable") + (concat "logic" " " "[DataWidth-1:0]" (make-string 4 ?\s) + "d_o;") + (concat "reg" (make-string 22 ?\s) + "empty_o;" (make-string 9 ?\s) + "// empty flag") + (concat "logic" (make-string 20 ?\s) "valid_o;") + ) + "\n") + "\n"))) + (with-temp-buffer + (insert input) + (goto-char (point-min)) + (search-forward "logic full_o") + (beginning-of-line) + (should (verilog-align-ports-declarations)) + (should (string= (buffer-string) expected))))) + +(ert-deftest verilog-align-ports-declarations-returns-nil-outside () + (with-temp-buffer + (insert (concat + (mapconcat + #'identity + '("module foo;" + "logic a;" + "endmodule") + "\n") + "\n")) + (goto-char (point-min)) + (search-forward "module foo") + (beginning-of-line) + (should (not (verilog-align-ports-declarations))))) + (provide 'verilog-align-ports-test) ;;; verilog-align-ports-test.el ends here diff --git a/verilog-align-ports.el b/verilog-align-ports.el index a3186d5..f634859 100644 --- a/verilog-align-ports.el +++ b/verilog-align-ports.el @@ -26,6 +26,17 @@ "^\\s-*\\.\\(\\\\[^[:space:]]+\\|[A-Za-z_][A-Za-z0-9_$]*\\)\\b" line)))) +(defun verilog-align-ports--line-decl-p (pos) + (save-excursion + (goto-char pos) + (let ((line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)))) + (and (string-match-p + "^\\s-*\\(logic\\|wire\\|reg\\)\\b" + line) + (verilog-align-ports--parse-decl-line line))))) + (defun verilog-align-ports--split-comment (line) (let ((pos (string-match "//" line))) (if pos @@ -112,6 +123,26 @@ (verilog-align-ports--line-inst-port-p (line-beginning-position))) (forward-line 1)) + (setq end (line-beginning-position)) + (cons start end))))) + +(defun verilog-align-ports--decl-bounds () + (save-excursion + (and (verilog-align-ports--line-decl-p (line-beginning-position)) + (let ((start (line-beginning-position)) + (end nil)) + (while (and (not (bobp)) + (save-excursion + (forward-line -1) + (verilog-align-ports--line-decl-p + (line-beginning-position)))) + (forward-line -1) + (setq start (line-beginning-position))) + (goto-char start) + (while (and (not (eobp)) + (verilog-align-ports--line-decl-p + (line-beginning-position))) + (forward-line 1)) (setq end (line-beginning-position)) (cons start end))))) @@ -185,6 +216,50 @@ :comma comma :comment comment))))) +(defun verilog-align-ports--parse-decl-line (line) + (let* ((split (verilog-align-ports--split-comment line)) + (code (string-trim-right (car split))) + (comment (cdr split))) + (when (string-match + "^\\(\\s-*\\)\\(logic\\|wire\\|reg\\)\\b\\s-*\\(.*\\)$" + code) + (let* ((indent (match-string 1 code)) + (kind (match-string 2 code)) + (rest (string-trim (match-string 3 code))) + (name nil) + (tail nil) + (type-rest "") + (range "") + (before "")) + (when (string-match + "\\(\\\\[^[:space:]]+\\|[A-Za-z_][A-Za-z0-9_$]*\\)\\s-*\\(?:\\(=[^;]*\\)\\)?;\\s-*$" + rest) + (setq name (match-string 1 rest)) + (setq tail (when (match-string 2 rest) + (string-trim (match-string 2 rest)))) + (setq before + (string-trim-right (substring rest 0 (match-beginning 1)))) + (setq before (string-trim before)) + (unless (string-match-p "," before) + (if (and (not (string-empty-p before)) + (string-match "\\(\\[[^][]+\\]\\)\\s-*$" before)) + (progn + (setq range (match-string 1 before)) + (setq type-rest + (string-trim-right + (substring before 0 (match-beginning 1))))) + (setq type-rest before) + (setq range "")) + (let ((type (if (string-empty-p type-rest) + kind + (concat kind " " type-rest)))) + (list :indent indent + :type type + :range range + :name name + :tail tail + :comment comment)))))))) + (defun verilog-align-ports--inst-collect (start end) (let (entries) (save-excursion @@ -199,6 +274,20 @@ (forward-line 1))) (nreverse entries))) +(defun verilog-align-ports--decl-collect (start end) + (let (entries) + (save-excursion + (goto-char start) + (while (< (point) end) + (let* ((line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + (entry (verilog-align-ports--parse-decl-line line))) + (when entry + (push entry entries))) + (forward-line 1))) + (nreverse entries))) + (defun verilog-align-ports--max-lengths (entries) (let ((max-dir 0) (max-type 0) @@ -295,6 +384,48 @@ left))) entries left-parts))) +(defun verilog-align-ports--decl-format-lines (entries) + (let* ((base-indent (plist-get (car entries) :indent)) + (max-type 0) + (max-range 0) + (has-comment nil) + left-parts + (max-left 0)) + (dolist (entry entries) + (setq max-type (max max-type (length (plist-get entry :type)))) + (setq max-range (max max-range (length (plist-get entry :range)))) + (when (plist-get entry :comment) + (setq has-comment t))) + (let ((has-range (> max-range 0))) + (dolist (entry entries) + (let* ((type (plist-get entry :type)) + (range (plist-get entry :range)) + (name (plist-get entry :name)) + (tail (plist-get entry :tail)) + (type-pad (verilog-align-ports--pad max-type (length type))) + (range-pad (when has-range + (verilog-align-ports--pad max-range (length range)))) + (left (concat base-indent + type + type-pad + (when has-range range) + (when has-range range-pad) + name + (when tail (concat " " tail)) + ";"))) + (push left left-parts) + (setq max-left (max max-left (length left))))) + (setq left-parts (nreverse left-parts)) + (cl-mapcar + (lambda (entry left) + (let ((comment (plist-get entry :comment))) + (if (and has-comment comment) + (concat left + (verilog-align-ports--pad max-left (length left)) + comment) + left))) + entries left-parts)))) + (defun verilog-align-ports--apply (start lines) (save-excursion (goto-char start) @@ -331,6 +462,20 @@ (verilog-align-ports--apply start lines) t)))))) +;;;###autoload +(defun verilog-align-ports-declarations () + "Align SystemVerilog signal declarations around point." + (interactive) + (let ((bounds (verilog-align-ports--decl-bounds))) + (and bounds + (let* ((start (car bounds)) + (end (cdr bounds)) + (entries (verilog-align-ports--decl-collect start end))) + (and entries + (let ((lines (verilog-align-ports--decl-format-lines entries))) + (verilog-align-ports--apply start lines) + t)))))) + (provide 'verilog-align-ports) ;;; verilog-align-ports.el ends here