Search code examples
emacselispvertical-alignmentsystem-verilog

How to add vertical alignment feature to a major-mode for Emacs


I am using verilg-mode for emacs everyday but the code alignment is not that good for me. So want to add something like vertical alignment.

First, I hope to align the declaration lines like these:

bit [1:0] a;
bit [3254:0] b;
bit unsigned [P_DWIDTH-1:0] c;
bit unsigned [P_DWIDTH-1:P_DWIDTH-4] d;
int e;

to:

bit          [         1:0         ] a;
bit          [      3254:0         ] b;
bit unsigned [P_DWIDTH-1:0         ] c;
bit unsigned [P_DWIDTH-1:P_DWIDTH-4] d;
int                                  e;

I don't have much Elisp experience. I am not sure something like align-regexp is the right thing to look at? Or anyone please point me a right direction to start.


Solution

  • According to @homeless reply, I did modification: Use narrow-to-region to avoid region boundary changes.

    (defun align-decl-vertically ()
      "Align verilog declarations."
      (interactive)
      (save-excursion
        (save-restriction
          (narrow-to-region (region-beginning) (region-end))
          ;; remove spaces around ":"
          (goto-char (point-min))
          (while (re-search-forward "\\s-*:\\s-*" (point-max) t)
            (replace-match ":"))
    
          ;; align "["
          (align-regexp (point-min) (point-max) "\\(\\s-*\\)\\[" -1 1 0)
          ;; align ":"
          (align-regexp (point-min) (point-max) "\\[\\(.+:\\)" -1 0 0)
          ;; align "]"
          (align-regexp (point-min) (point-max) "\\s-*\\(\\]\\)" -1 0 0)
          ;; align variable name
          (align-regexp (point-min) (point-max) "\\(\\s-+\\)\\S-+;" -1 1 0)
          (widen))))
    

    Also find another way to update rules for align to achieve this (this not work exactly as what I expect but just list here maybe some would help me to fix it):

    (add-to-list 'align-mode-rules-list                             
                 '(declaration-range-field-alignment                 
                   (regexp . "\\(\\s-*\\[\\)\\(.*:\\).*\\S-+\\(\\s-*\\]\\)\\(.*\\)")
                   (group . (1 2 3 4))                              
                   (modes . '(verilog-mode))                        
                   (tab-stop . nil)                                 
                   (spacing . (1 0 0 1))                            
                   (repeat . nil)                                   
                   (justify . t)))                                  
    (add-to-list 'align-mode-rules-list                             
                 '(declaration-variable-name-alignment              
                   (regexp . "\\(\\s-*\\S-*\\s-*;\\)")                  
                   (group . 1)                                      
                   (modes . '(verilog-mode))                        
                   (repeat . nil)                                   
                   (tab-stop . nil)                                 
                   (spacing . 1)                                    
                   (justify . t)))          
    

    But sometimes it may need to run align more than once to get the final result. I haven't figure out why yet.