Initial commit
This commit is contained in:
commit
a1107b911a
53
README.md
Normal file
53
README.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# verilog-align-ports
|
||||||
|
|
||||||
|
Align SystemVerilog ANSI-style port declarations in a contiguous block 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).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```elisp
|
||||||
|
(add-to-list 'load-path "/path/to/verilog-align-ports")
|
||||||
|
(require 'verilog-align-ports)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Place point on a line that declares a port (input/output/inout).
|
||||||
|
2. Run `M-x verilog-align-ports`.
|
||||||
|
|
||||||
|
Example input:
|
||||||
|
|
||||||
|
```systemverilog
|
||||||
|
input wire rst_i, // rst,
|
||||||
|
input wire clk_i, // wr_clk,
|
||||||
|
input wire [DataWidth-1:0] d_i, // din,
|
||||||
|
output logic full_o, // full,
|
||||||
|
output [DataWidth-1:0] d_o, // dout,
|
||||||
|
output empty_o,// empty,
|
||||||
|
output logic valid_o // data_valid
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```systemverilog
|
||||||
|
input wire rst_i, // rst,
|
||||||
|
input wire clk_i, // wr_clk,
|
||||||
|
input wire [DataWidth-1:0] d_i, // din,
|
||||||
|
output logic full_o, // full,
|
||||||
|
output [DataWidth-1:0] d_o, // dout,
|
||||||
|
output empty_o, // empty,
|
||||||
|
output logic valid_o // data_valid
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-tests.sh
|
||||||
|
```
|
||||||
7
run-tests.sh
Executable file
7
run-tests.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
emacs -batch \
|
||||||
|
-l ert \
|
||||||
|
-l verilog-align-ports-test.el \
|
||||||
|
-f ert-run-tests-batch-and-exit
|
||||||
71
verilog-align-ports-test.el
Normal file
71
verilog-align-ports-test.el
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
;;; 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
|
||||||
183
verilog-align-ports.el
Normal file
183
verilog-align-ports.el
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
;;; 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
|
||||||
|
(unless (verilog-align-ports--line-port-p (line-beginning-position))
|
||||||
|
(user-error "Point is not on a port declaration line"))
|
||||||
|
(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))
|
||||||
|
(start (car bounds))
|
||||||
|
(end (cdr bounds))
|
||||||
|
(entries (verilog-align-ports--collect start end)))
|
||||||
|
(when (null entries)
|
||||||
|
(user-error "No port declarations found"))
|
||||||
|
(let ((lines (verilog-align-ports--format-lines entries)))
|
||||||
|
(verilog-align-ports--apply start lines))))
|
||||||
|
|
||||||
|
(provide 'verilog-align-ports)
|
||||||
|
|
||||||
|
;;; verilog-align-ports.el ends here
|
||||||
Loading…
x
Reference in New Issue
Block a user