Compare commits
3 Commits
6f6e4ef29b
...
dd3ce4977e
| Author | SHA1 | Date | |
|---|---|---|---|
| dd3ce4977e | |||
| 508ebdc8e6 | |||
| 2102adc1da |
20
README.md
20
README.md
@@ -1,18 +1,20 @@
|
||||
# verilog-align-ports
|
||||
# verilog-align
|
||||
|
||||
Align SystemVerilog ANSI-style port declarations in a contiguous block around point.
|
||||
Align SystemVerilog port declarations, instantiations, and signal declarations around point.
|
||||
|
||||
## Features
|
||||
|
||||
- Aligns direction, type, range, name, and trailing `//` comments.
|
||||
- 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-instantiation`.
|
||||
- Aligns signal declarations via `verilog-align-declarations`.
|
||||
|
||||
## Installation
|
||||
|
||||
```elisp
|
||||
(add-to-list 'load-path "/path/to/verilog-align-ports")
|
||||
(require 'verilog-align-ports)
|
||||
(add-to-list 'load-path "/path/to/verilog-align")
|
||||
(require 'verilog-align)
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -20,6 +22,16 @@ Align SystemVerilog ANSI-style port declarations in a contiguous block around po
|
||||
1. Place point on a line that declares a port (input/output/inout).
|
||||
2. Run `M-x verilog-align-ports`.
|
||||
|
||||
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-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-declarations`.
|
||||
|
||||
Example input:
|
||||
|
||||
```systemverilog
|
||||
|
||||
@@ -3,5 +3,5 @@ set -euo pipefail
|
||||
|
||||
emacs -batch \
|
||||
-l ert \
|
||||
-l verilog-align-ports-test.el \
|
||||
-l verilog-align-test.el \
|
||||
-f ert-run-tests-batch-and-exit
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
;;; verilog-align-ports-test.el --- Tests for verilog-align-ports -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; ERT tests for verilog-align-ports.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
|
||||
(defconst verilog-align-ports-test--dir
|
||||
(file-name-directory (or load-file-name buffer-file-name)))
|
||||
|
||||
(load-file (expand-file-name "verilog-align-ports.el"
|
||||
verilog-align-ports-test--dir))
|
||||
|
||||
(ert-deftest verilog-align-ports-aligns-block ()
|
||||
(let* ((input (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
'("module fifo_sync #("
|
||||
" parameter integer FifoWriteDepth = 2048,"
|
||||
" parameter integer DataWidth = 32,"
|
||||
" parameter integer WrDataCountWidth = $clog2(FifoWriteDepth)+1,"
|
||||
" parameter integer RdDataCountWidth = 4"
|
||||
") ("
|
||||
" input wire rst_i, // rst,"
|
||||
" input wire clk_i, // wr_clk,"
|
||||
" input wire wr_en_i, // wr_en,"
|
||||
" input wire [DataWidth-1:0] d_i, // din,"
|
||||
" output logic full_o, // full,"
|
||||
" output logic [WrDataCountWidth-1:0] wr_data_count_o, // wr_data_count,"
|
||||
" input wire rd_en_i, // rd_en,"
|
||||
" output [DataWidth-1:0] d_o, // dout,"
|
||||
" output empty_o,// empty,"
|
||||
" output logic valid_o // data_valid"
|
||||
");")
|
||||
"\n")
|
||||
"\n"))
|
||||
(expected (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
'("module fifo_sync #("
|
||||
" parameter integer FifoWriteDepth = 2048,"
|
||||
" parameter integer DataWidth = 32,"
|
||||
" parameter integer WrDataCountWidth = $clog2(FifoWriteDepth)+1,"
|
||||
" parameter integer RdDataCountWidth = 4"
|
||||
") ("
|
||||
" input wire rst_i, // rst,"
|
||||
" input wire clk_i, // wr_clk,"
|
||||
" input wire wr_en_i, // wr_en,"
|
||||
" input wire [DataWidth-1:0] d_i, // din,"
|
||||
" output logic full_o, // full,"
|
||||
" output logic [WrDataCountWidth-1:0] wr_data_count_o, // wr_data_count,"
|
||||
" input wire rd_en_i, // rd_en,"
|
||||
" output [DataWidth-1:0] d_o, // dout,"
|
||||
" output empty_o, // empty,"
|
||||
" output logic valid_o // data_valid"
|
||||
");")
|
||||
"\n")
|
||||
"\n")))
|
||||
(with-temp-buffer
|
||||
(insert input)
|
||||
(goto-char (point-min))
|
||||
(search-forward "input wire clk_i")
|
||||
(beginning-of-line)
|
||||
(verilog-align-ports)
|
||||
(should (string= (buffer-string) expected)))))
|
||||
|
||||
(provide 'verilog-align-ports-test)
|
||||
|
||||
;;; verilog-align-ports-test.el ends here
|
||||
@@ -1,183 +0,0 @@
|
||||
;;; verilog-align-ports.el --- Align SystemVerilog port declarations -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; Align SystemVerilog ANSI-style port declarations in a contiguous block.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'subr-x)
|
||||
|
||||
(defun verilog-align-ports--line-port-p (pos)
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(let ((line (buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))))
|
||||
(string-match-p "^\\s-*\\(input\\|output\\|inout\\)\\b" line))))
|
||||
|
||||
(defun verilog-align-ports--split-comment (line)
|
||||
(let ((pos (string-match "//" line)))
|
||||
(if pos
|
||||
(cons (substring line 0 pos) (substring line pos))
|
||||
(cons line nil))))
|
||||
|
||||
(defun verilog-align-ports--parse-line (line)
|
||||
(let* ((split (verilog-align-ports--split-comment line))
|
||||
(code (string-trim-right (car split)))
|
||||
(comment (cdr split)))
|
||||
(when (string-match
|
||||
"^\\(\\s-*\\)\\(input\\|output\\|inout\\)\\b\\s-*\\(.*\\)$"
|
||||
code)
|
||||
(let* ((indent (match-string 1 code))
|
||||
(dir (match-string 2 code))
|
||||
(rest (string-trim (match-string 3 code)))
|
||||
(name nil)
|
||||
(comma nil)
|
||||
(type "")
|
||||
(range "")
|
||||
(before ""))
|
||||
(if (string-match
|
||||
"\\(\\\\[^[:space:]]+\\|[A-Za-z_][A-Za-z0-9_$]*\\)\\(\\s-*,\\)?\\s-*$"
|
||||
rest)
|
||||
(progn
|
||||
(setq name (match-string 1 rest))
|
||||
(setq comma (when (match-string 2 rest) ","))
|
||||
(setq before
|
||||
(string-trim-right (substring rest 0 (match-beginning 1)))))
|
||||
(setq name rest)
|
||||
(setq comma nil)
|
||||
(setq before ""))
|
||||
(setq before (string-trim-right before))
|
||||
(if (and (not (string-empty-p before))
|
||||
(string-match "\\(\\[[^][]+\\]\\)\\s-*$" before))
|
||||
(progn
|
||||
(setq range (match-string 1 before))
|
||||
(setq type
|
||||
(string-trim-right (substring before 0 (match-beginning 1)))))
|
||||
(setq type (string-trim before))
|
||||
(setq range ""))
|
||||
(list :indent indent
|
||||
:dir dir
|
||||
:type type
|
||||
:range range
|
||||
:name name
|
||||
:comma comma
|
||||
:comment comment)))))
|
||||
|
||||
(defun verilog-align-ports--bounds ()
|
||||
(save-excursion
|
||||
(and (verilog-align-ports--line-port-p (line-beginning-position))
|
||||
(let ((start (line-beginning-position))
|
||||
(end nil))
|
||||
(while (and (not (bobp))
|
||||
(save-excursion
|
||||
(forward-line -1)
|
||||
(verilog-align-ports--line-port-p
|
||||
(line-beginning-position))))
|
||||
(forward-line -1)
|
||||
(setq start (line-beginning-position)))
|
||||
(goto-char start)
|
||||
(while (and (not (eobp))
|
||||
(verilog-align-ports--line-port-p
|
||||
(line-beginning-position)))
|
||||
(forward-line 1))
|
||||
(setq end (line-beginning-position))
|
||||
(cons start end)))))
|
||||
|
||||
(defun verilog-align-ports--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-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)
|
||||
(max-range 0)
|
||||
(max-name 0)
|
||||
(has-comment nil))
|
||||
(dolist (entry entries)
|
||||
(setq max-dir (max max-dir (length (plist-get entry :dir))))
|
||||
(setq max-type (max max-type (length (plist-get entry :type))))
|
||||
(setq max-range (max max-range (length (plist-get entry :range))))
|
||||
(let ((name (concat (plist-get entry :name)
|
||||
(or (plist-get entry :comma) ""))))
|
||||
(setq max-name (max max-name (length name))))
|
||||
(when (plist-get entry :comment)
|
||||
(setq has-comment t)))
|
||||
(list max-dir max-type max-range max-name has-comment)))
|
||||
|
||||
(defun verilog-align-ports--pad (max-len len)
|
||||
(make-string (+ 1 (- max-len len)) ?\s))
|
||||
|
||||
(defun verilog-align-ports--format-lines (entries)
|
||||
(let* ((base-indent (plist-get (car entries) :indent))
|
||||
(maxes (verilog-align-ports--max-lengths entries))
|
||||
(max-dir (nth 0 maxes))
|
||||
(max-type (nth 1 maxes))
|
||||
(max-range (nth 2 maxes))
|
||||
(max-name (nth 3 maxes))
|
||||
(has-comment (nth 4 maxes))
|
||||
(has-type (> max-type 0))
|
||||
(has-range (> max-range 0)))
|
||||
(mapcar
|
||||
(lambda (entry)
|
||||
(let* ((dir (plist-get entry :dir))
|
||||
(type (plist-get entry :type))
|
||||
(range (plist-get entry :range))
|
||||
(name (concat (plist-get entry :name)
|
||||
(or (plist-get entry :comma) "")))
|
||||
(comment (plist-get entry :comment))
|
||||
(dir-pad (verilog-align-ports--pad max-dir (length dir)))
|
||||
(type-pad (when has-type
|
||||
(verilog-align-ports--pad max-type (length type))))
|
||||
(range-pad (when has-range
|
||||
(verilog-align-ports--pad max-range (length range))))
|
||||
(name-pad (when (and has-comment comment)
|
||||
(verilog-align-ports--pad max-name (length name)))))
|
||||
(concat base-indent
|
||||
dir
|
||||
dir-pad
|
||||
(when has-type type)
|
||||
(when has-type type-pad)
|
||||
(when has-range range)
|
||||
(when has-range range-pad)
|
||||
name
|
||||
(when (and has-comment comment) name-pad)
|
||||
(or comment ""))))
|
||||
entries)))
|
||||
|
||||
(defun verilog-align-ports--apply (start lines)
|
||||
(save-excursion
|
||||
(goto-char start)
|
||||
(dolist (line lines)
|
||||
(delete-region (line-beginning-position) (line-end-position))
|
||||
(insert line)
|
||||
(forward-line 1))))
|
||||
|
||||
;;;###autoload
|
||||
(defun verilog-align-ports ()
|
||||
"Align SystemVerilog port declarations around point."
|
||||
(interactive)
|
||||
(let ((bounds (verilog-align-ports--bounds)))
|
||||
(and bounds
|
||||
(let* ((start (car bounds))
|
||||
(end (cdr bounds))
|
||||
(entries (verilog-align-ports--collect start end)))
|
||||
(and entries
|
||||
(let ((lines (verilog-align-ports--format-lines entries)))
|
||||
(verilog-align-ports--apply start lines)
|
||||
t))))))
|
||||
|
||||
(provide 'verilog-align-ports)
|
||||
|
||||
;;; verilog-align-ports.el ends here
|
||||
207
verilog-align-test.el
Normal file
207
verilog-align-test.el
Normal file
@@ -0,0 +1,207 @@
|
||||
;;; verilog-align-test.el --- Tests for verilog-align -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; ERT tests for verilog-align.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
|
||||
(defconst verilog-align-test--dir
|
||||
(file-name-directory (or load-file-name buffer-file-name)))
|
||||
|
||||
(load-file (expand-file-name "verilog-align.el"
|
||||
verilog-align-test--dir))
|
||||
|
||||
(ert-deftest verilog-align-ports-aligns-block ()
|
||||
(let* ((input (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
'("module fifo_sync #("
|
||||
" parameter integer FifoWriteDepth = 2048,"
|
||||
" parameter integer DataWidth = 32,"
|
||||
" parameter integer WrDataCountWidth = $clog2(FifoWriteDepth)+1,"
|
||||
" parameter integer RdDataCountWidth = 4"
|
||||
") ("
|
||||
" input wire rst_i, // rst,"
|
||||
" input wire clk_i, // wr_clk,"
|
||||
" input wire wr_en_i, // wr_en,"
|
||||
" input wire [DataWidth-1:0] d_i, // din,"
|
||||
" output logic full_o, // full,"
|
||||
" output logic [WrDataCountWidth-1:0] wr_data_count_o, // wr_data_count,"
|
||||
" input wire rd_en_i, // rd_en,"
|
||||
" output [DataWidth-1:0] d_o, // dout,"
|
||||
" output empty_o,// empty,"
|
||||
" output logic valid_o // data_valid"
|
||||
");")
|
||||
"\n")
|
||||
"\n"))
|
||||
(expected (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
'("module fifo_sync #("
|
||||
" parameter integer FifoWriteDepth = 2048,"
|
||||
" parameter integer DataWidth = 32,"
|
||||
" parameter integer WrDataCountWidth = $clog2(FifoWriteDepth)+1,"
|
||||
" parameter integer RdDataCountWidth = 4"
|
||||
") ("
|
||||
" input wire rst_i, // rst,"
|
||||
" input wire clk_i, // wr_clk,"
|
||||
" input wire wr_en_i, // wr_en,"
|
||||
" input wire [DataWidth-1:0] d_i, // din,"
|
||||
" output logic full_o, // full,"
|
||||
" output logic [WrDataCountWidth-1:0] wr_data_count_o, // wr_data_count,"
|
||||
" input wire rd_en_i, // rd_en,"
|
||||
" output [DataWidth-1:0] d_o, // dout,"
|
||||
" output empty_o, // empty,"
|
||||
" output logic valid_o // data_valid"
|
||||
");")
|
||||
"\n")
|
||||
"\n")))
|
||||
(with-temp-buffer
|
||||
(insert input)
|
||||
(goto-char (point-min))
|
||||
(search-forward "input wire clk_i")
|
||||
(beginning-of-line)
|
||||
(verilog-align-ports)
|
||||
(should (string= (buffer-string) expected)))))
|
||||
|
||||
(ert-deftest verilog-align-instantiation-aligns-block ()
|
||||
(let* ((input (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
'("fifo_sync #("
|
||||
" .FifoWriteDepth(FifoWriteDepth),"
|
||||
" .DataWidth(DataWidth),"
|
||||
" .DataCountWidth(WrDataCountW),"
|
||||
" .RdDataCountWidth(RdDataCountW)"
|
||||
") dut ("
|
||||
" .rst_i (rst), //rst"
|
||||
" .clk_i (clk),"
|
||||
" .wr_en_i (wr_en_i),"
|
||||
" .d_i (d_i), // di"
|
||||
" .full_o, // full"
|
||||
" .data_count_o (wr_data_count_o),"
|
||||
" .rd_en_i (rd_en_i),"
|
||||
" .d_o () , // do"
|
||||
" .empty_o , // empty"
|
||||
" .valid_o (valid_o)"
|
||||
");")
|
||||
"\n")
|
||||
"\n"))
|
||||
(expected (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
(list
|
||||
"fifo_sync #("
|
||||
" .FifoWriteDepth(FifoWriteDepth),"
|
||||
" .DataWidth(DataWidth),"
|
||||
" .DataCountWidth(WrDataCountW),"
|
||||
" .RdDataCountWidth(RdDataCountW)"
|
||||
") dut ("
|
||||
(concat " .rst_i" (make-string 8 ?\s) "(rst),"
|
||||
(make-string 13 ?\s) "//rst")
|
||||
(concat " .clk_i" (make-string 8 ?\s) "(clk),")
|
||||
(concat " .wr_en_i" (make-string 6 ?\s) "(wr_en_i),")
|
||||
(concat " .d_i" (make-string 10 ?\s) "(d_i),"
|
||||
(make-string 13 ?\s) "// di")
|
||||
(concat " .full_o," (make-string 25 ?\s) "// full")
|
||||
(concat " .data_count_o" (make-string 1 ?\s)
|
||||
"(wr_data_count_o),")
|
||||
(concat " .rd_en_i" (make-string 6 ?\s) "(rd_en_i),")
|
||||
(concat " .d_o" (make-string 10 ?\s) "(),"
|
||||
(make-string 16 ?\s) "// do")
|
||||
(concat " .empty_o," (make-string 24 ?\s) "// empty")
|
||||
(concat " .valid_o" (make-string 6 ?\s) "(valid_o)")
|
||||
");")
|
||||
"\n")
|
||||
"\n")))
|
||||
(with-temp-buffer
|
||||
(insert input)
|
||||
(goto-char (point-min))
|
||||
(search-forward ".clk_i")
|
||||
(beginning-of-line)
|
||||
(should (verilog-align-instantiation))
|
||||
(should (string= (buffer-string) expected)))))
|
||||
|
||||
(ert-deftest verilog-align-instantiation-returns-nil-outside ()
|
||||
(with-temp-buffer
|
||||
(insert (concat
|
||||
(mapconcat
|
||||
#'identity
|
||||
'("fifo_sync #() dut ("
|
||||
" .rst_i (rst),"
|
||||
" .clk_i (clk)"
|
||||
");")
|
||||
"\n")
|
||||
"\n"))
|
||||
(goto-char (point-min))
|
||||
(search-forward "dut (")
|
||||
(beginning-of-line)
|
||||
(should (not (verilog-align-instantiation)))))
|
||||
|
||||
(ert-deftest verilog-align-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-declarations))
|
||||
(should (string= (buffer-string) expected)))))
|
||||
|
||||
(ert-deftest verilog-align-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-declarations)))))
|
||||
|
||||
(provide 'verilog-align-test)
|
||||
|
||||
;;; verilog-align-test.el ends here
|
||||
481
verilog-align.el
Normal file
481
verilog-align.el
Normal file
@@ -0,0 +1,481 @@
|
||||
;;; verilog-align.el --- Align SystemVerilog port declarations -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Commentary:
|
||||
;; Align SystemVerilog port declarations, instantiations, and signal declarations.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'subr-x)
|
||||
|
||||
(defun verilog-align--line-port-p (pos)
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(let ((line (buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))))
|
||||
(string-match-p "^\\s-*\\(input\\|output\\|inout\\)\\b" line))))
|
||||
|
||||
(defun verilog-align--line-inst-port-p (pos)
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(let ((line (buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))))
|
||||
(string-match-p
|
||||
"^\\s-*\\.\\(\\\\[^[:space:]]+\\|[A-Za-z_][A-Za-z0-9_$]*\\)\\b"
|
||||
line))))
|
||||
|
||||
(defun verilog-align--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--parse-decl-line line)))))
|
||||
|
||||
(defun verilog-align--split-comment (line)
|
||||
(let ((pos (string-match "//" line)))
|
||||
(if pos
|
||||
(cons (substring line 0 pos) (substring line pos))
|
||||
(cons line nil))))
|
||||
|
||||
(defun verilog-align--parse-line (line)
|
||||
(let* ((split (verilog-align--split-comment line))
|
||||
(code (string-trim-right (car split)))
|
||||
(comment (cdr split)))
|
||||
(when (string-match
|
||||
"^\\(\\s-*\\)\\(input\\|output\\|inout\\)\\b\\s-*\\(.*\\)$"
|
||||
code)
|
||||
(let* ((indent (match-string 1 code))
|
||||
(dir (match-string 2 code))
|
||||
(rest (string-trim (match-string 3 code)))
|
||||
(name nil)
|
||||
(comma nil)
|
||||
(type "")
|
||||
(range "")
|
||||
(before ""))
|
||||
(if (string-match
|
||||
"\\(\\\\[^[:space:]]+\\|[A-Za-z_][A-Za-z0-9_$]*\\)\\(\\s-*,\\)?\\s-*$"
|
||||
rest)
|
||||
(progn
|
||||
(setq name (match-string 1 rest))
|
||||
(setq comma (when (match-string 2 rest) ","))
|
||||
(setq before
|
||||
(string-trim-right (substring rest 0 (match-beginning 1)))))
|
||||
(setq name rest)
|
||||
(setq comma nil)
|
||||
(setq before ""))
|
||||
(setq before (string-trim-right before))
|
||||
(if (and (not (string-empty-p before))
|
||||
(string-match "\\(\\[[^][]+\\]\\)\\s-*$" before))
|
||||
(progn
|
||||
(setq range (match-string 1 before))
|
||||
(setq type
|
||||
(string-trim-right (substring before 0 (match-beginning 1)))))
|
||||
(setq type (string-trim before))
|
||||
(setq range ""))
|
||||
(list :indent indent
|
||||
:dir dir
|
||||
:type type
|
||||
:range range
|
||||
:name name
|
||||
:comma comma
|
||||
:comment comment)))))
|
||||
|
||||
(defun verilog-align--bounds ()
|
||||
(save-excursion
|
||||
(and (verilog-align--line-port-p (line-beginning-position))
|
||||
(let ((start (line-beginning-position))
|
||||
(end nil))
|
||||
(while (and (not (bobp))
|
||||
(save-excursion
|
||||
(forward-line -1)
|
||||
(verilog-align--line-port-p
|
||||
(line-beginning-position))))
|
||||
(forward-line -1)
|
||||
(setq start (line-beginning-position)))
|
||||
(goto-char start)
|
||||
(while (and (not (eobp))
|
||||
(verilog-align--line-port-p
|
||||
(line-beginning-position)))
|
||||
(forward-line 1))
|
||||
(setq end (line-beginning-position))
|
||||
(cons start end)))))
|
||||
|
||||
(defun verilog-align--inst-bounds ()
|
||||
(save-excursion
|
||||
(and (verilog-align--line-inst-port-p (line-beginning-position))
|
||||
(let ((start (line-beginning-position))
|
||||
(end nil))
|
||||
(while (and (not (bobp))
|
||||
(save-excursion
|
||||
(forward-line -1)
|
||||
(verilog-align--line-inst-port-p
|
||||
(line-beginning-position))))
|
||||
(forward-line -1)
|
||||
(setq start (line-beginning-position)))
|
||||
(goto-char start)
|
||||
(while (and (not (eobp))
|
||||
(verilog-align--line-inst-port-p
|
||||
(line-beginning-position)))
|
||||
(forward-line 1))
|
||||
(setq end (line-beginning-position))
|
||||
(cons start end)))))
|
||||
|
||||
(defun verilog-align--decl-bounds ()
|
||||
(save-excursion
|
||||
(and (verilog-align--line-decl-p (line-beginning-position))
|
||||
(let ((start (line-beginning-position))
|
||||
(end nil))
|
||||
(while (and (not (bobp))
|
||||
(save-excursion
|
||||
(forward-line -1)
|
||||
(verilog-align--line-decl-p
|
||||
(line-beginning-position))))
|
||||
(forward-line -1)
|
||||
(setq start (line-beginning-position)))
|
||||
(goto-char start)
|
||||
(while (and (not (eobp))
|
||||
(verilog-align--line-decl-p
|
||||
(line-beginning-position)))
|
||||
(forward-line 1))
|
||||
(setq end (line-beginning-position))
|
||||
(cons start end)))))
|
||||
|
||||
(defun verilog-align--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--parse-line line)))
|
||||
(when entry
|
||||
(push entry entries)))
|
||||
(forward-line 1)))
|
||||
(nreverse entries)))
|
||||
|
||||
(defun verilog-align--find-paren-end (text start)
|
||||
(let ((depth 0)
|
||||
(idx start)
|
||||
(len (length text))
|
||||
(end nil))
|
||||
(while (and (< idx len) (not end))
|
||||
(let ((ch (aref text idx)))
|
||||
(cond
|
||||
((eq ch ?\() (setq depth (1+ depth)))
|
||||
((eq ch ?\))
|
||||
(setq depth (1- depth))
|
||||
(when (eq depth 0)
|
||||
(setq end (1+ idx))))))
|
||||
(setq idx (1+ idx)))
|
||||
end))
|
||||
|
||||
(defun verilog-align--parse-inst-line (line)
|
||||
(let* ((split (verilog-align--split-comment line))
|
||||
(code (string-trim-right (car split)))
|
||||
(comment (cdr split)))
|
||||
(when (string-match
|
||||
"^\\(\\s-*\\)\\.\\(\\\\[^[:space:]]+\\|[A-Za-z_][A-Za-z0-9_$]*\\)\\(.*\\)$"
|
||||
code)
|
||||
(let* ((indent (match-string 1 code))
|
||||
(name (match-string 2 code))
|
||||
(rest (match-string 3 code))
|
||||
(rest (if rest (string-trim-right rest) ""))
|
||||
(pos (string-match "\\S-" rest))
|
||||
(has-conn nil)
|
||||
(conn "")
|
||||
(comma nil))
|
||||
(if (and pos (< pos (length rest)) (eq (aref rest pos) ?\())
|
||||
(let* ((end (verilog-align--find-paren-end rest pos)))
|
||||
(if end
|
||||
(progn
|
||||
(setq has-conn t)
|
||||
(setq conn (string-trim (substring rest (1+ pos) (1- end))))
|
||||
(let ((after (string-trim (substring rest end))))
|
||||
(when (and (> (length after) 0)
|
||||
(eq (aref after 0) ?,))
|
||||
(setq comma ","))))
|
||||
(let ((after (string-trim rest)))
|
||||
(when (and (> (length after) 0)
|
||||
(string-match-p "," after))
|
||||
(setq comma ",")))))
|
||||
(let ((after (string-trim rest)))
|
||||
(when (and (> (length after) 0)
|
||||
(string-match-p "," after))
|
||||
(setq comma ","))))
|
||||
(list :indent indent
|
||||
:name name
|
||||
:has-conn has-conn
|
||||
:conn conn
|
||||
:comma comma
|
||||
:comment comment)))))
|
||||
|
||||
(defun verilog-align--parse-decl-line (line)
|
||||
(let* ((split (verilog-align--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--inst-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--parse-inst-line line)))
|
||||
(when entry
|
||||
(push entry entries)))
|
||||
(forward-line 1)))
|
||||
(nreverse entries)))
|
||||
|
||||
(defun verilog-align--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--parse-decl-line line)))
|
||||
(when entry
|
||||
(push entry entries)))
|
||||
(forward-line 1)))
|
||||
(nreverse entries)))
|
||||
|
||||
(defun verilog-align--max-lengths (entries)
|
||||
(let ((max-dir 0)
|
||||
(max-type 0)
|
||||
(max-range 0)
|
||||
(max-name 0)
|
||||
(has-comment nil))
|
||||
(dolist (entry entries)
|
||||
(setq max-dir (max max-dir (length (plist-get entry :dir))))
|
||||
(setq max-type (max max-type (length (plist-get entry :type))))
|
||||
(setq max-range (max max-range (length (plist-get entry :range))))
|
||||
(let ((name (concat (plist-get entry :name)
|
||||
(or (plist-get entry :comma) ""))))
|
||||
(setq max-name (max max-name (length name))))
|
||||
(when (plist-get entry :comment)
|
||||
(setq has-comment t)))
|
||||
(list max-dir max-type max-range max-name has-comment)))
|
||||
|
||||
(defun verilog-align--pad (max-len len)
|
||||
(make-string (+ 1 (- max-len len)) ?\s))
|
||||
|
||||
(defun verilog-align--format-lines (entries)
|
||||
(let* ((base-indent (plist-get (car entries) :indent))
|
||||
(maxes (verilog-align--max-lengths entries))
|
||||
(max-dir (nth 0 maxes))
|
||||
(max-type (nth 1 maxes))
|
||||
(max-range (nth 2 maxes))
|
||||
(max-name (nth 3 maxes))
|
||||
(has-comment (nth 4 maxes))
|
||||
(has-type (> max-type 0))
|
||||
(has-range (> max-range 0)))
|
||||
(mapcar
|
||||
(lambda (entry)
|
||||
(let* ((dir (plist-get entry :dir))
|
||||
(type (plist-get entry :type))
|
||||
(range (plist-get entry :range))
|
||||
(name (concat (plist-get entry :name)
|
||||
(or (plist-get entry :comma) "")))
|
||||
(comment (plist-get entry :comment))
|
||||
(dir-pad (verilog-align--pad max-dir (length dir)))
|
||||
(type-pad (when has-type
|
||||
(verilog-align--pad max-type (length type))))
|
||||
(range-pad (when has-range
|
||||
(verilog-align--pad max-range (length range))))
|
||||
(name-pad (when (and has-comment comment)
|
||||
(verilog-align--pad max-name (length name)))))
|
||||
(concat base-indent
|
||||
dir
|
||||
dir-pad
|
||||
(when has-type type)
|
||||
(when has-type type-pad)
|
||||
(when has-range range)
|
||||
(when has-range range-pad)
|
||||
name
|
||||
(when (and has-comment comment) name-pad)
|
||||
(or comment ""))))
|
||||
entries)))
|
||||
|
||||
(defun verilog-align--inst-format-lines (entries)
|
||||
(let* ((base-indent (plist-get (car entries) :indent))
|
||||
(max-name 0)
|
||||
(has-comment nil)
|
||||
left-parts
|
||||
(max-left 0))
|
||||
(dolist (entry entries)
|
||||
(let ((name-len (+ 1 (length (plist-get entry :name)))))
|
||||
(setq max-name (max max-name name-len)))
|
||||
(when (plist-get entry :comment)
|
||||
(setq has-comment t)))
|
||||
(dolist (entry entries)
|
||||
(let* ((name (plist-get entry :name))
|
||||
(name-field (concat "." name))
|
||||
(has-conn (plist-get entry :has-conn))
|
||||
(conn (plist-get entry :conn))
|
||||
(comma (plist-get entry :comma))
|
||||
(pad (when has-conn
|
||||
(verilog-align--pad max-name (length name-field))))
|
||||
(left (concat base-indent
|
||||
name-field
|
||||
(when has-conn pad)
|
||||
(when has-conn "(")
|
||||
(when has-conn conn)
|
||||
(when has-conn ")")
|
||||
(when comma ","))))
|
||||
(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--pad max-left (length left))
|
||||
comment)
|
||||
left)))
|
||||
entries left-parts)))
|
||||
|
||||
(defun verilog-align--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--pad max-type (length type)))
|
||||
(range-pad (when has-range
|
||||
(verilog-align--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--pad max-left (length left))
|
||||
comment)
|
||||
left)))
|
||||
entries left-parts))))
|
||||
|
||||
(defun verilog-align--apply (start lines)
|
||||
(save-excursion
|
||||
(goto-char start)
|
||||
(dolist (line lines)
|
||||
(delete-region (line-beginning-position) (line-end-position))
|
||||
(insert line)
|
||||
(forward-line 1))))
|
||||
|
||||
;;;###autoload
|
||||
(defun verilog-align-ports ()
|
||||
"Align SystemVerilog port declarations around point."
|
||||
(interactive)
|
||||
(let ((bounds (verilog-align--bounds)))
|
||||
(and bounds
|
||||
(let* ((start (car bounds))
|
||||
(end (cdr bounds))
|
||||
(entries (verilog-align--collect start end)))
|
||||
(and entries
|
||||
(let ((lines (verilog-align--format-lines entries)))
|
||||
(verilog-align--apply start lines)
|
||||
t))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun verilog-align-instantiation ()
|
||||
"Align SystemVerilog named port connections around point."
|
||||
(interactive)
|
||||
(let ((bounds (verilog-align--inst-bounds)))
|
||||
(and bounds
|
||||
(let* ((start (car bounds))
|
||||
(end (cdr bounds))
|
||||
(entries (verilog-align--inst-collect start end)))
|
||||
(and entries
|
||||
(let ((lines (verilog-align--inst-format-lines entries)))
|
||||
(verilog-align--apply start lines)
|
||||
t))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun verilog-align-declarations ()
|
||||
"Align SystemVerilog signal declarations around point."
|
||||
(interactive)
|
||||
(let ((bounds (verilog-align--decl-bounds)))
|
||||
(and bounds
|
||||
(let* ((start (car bounds))
|
||||
(end (cdr bounds))
|
||||
(entries (verilog-align--decl-collect start end)))
|
||||
(and entries
|
||||
(let ((lines (verilog-align--decl-format-lines entries)))
|
||||
(verilog-align--apply start lines)
|
||||
t))))))
|
||||
|
||||
(provide 'verilog-align)
|
||||
|
||||
;;; verilog-align.el ends here
|
||||
Reference in New Issue
Block a user