4.4.1 Custom Indentation Functions

One of the most common ways to customize VHDL Mode is by writing custom indentation functions and associating them with specific syntactic symbols (see Syntactic Symbols). VHDL Mode itself uses custom indentation functions to provide more sophisticated indentation, for example when lining up selected signal assignments:


%%% TBD %%%

In this example, the statement-cont syntactic symbol has an offset of +, and vhdl-basic-offset is 2, so lines 4 through 6 are simply indented two spaces to the right of line 3. But perhaps we’d like VHDL Mode to be a little more intelligent so that it offsets the waveform descriptions relative to the signal assignment operator in line 3. To do this, we have to write a custom indentation function which finds the column of signal assignment operator on the first line of the statement. Here is the lisp code (from the vhdl-mode.el source file) that implements this:


(defun vhdl-lineup-statement-cont (langelem)
  ;; line up statement-cont after the assignment operator
  (save-excursion
    (let* ((relpos (cdr langelem))
	   (assignp (save-excursion
		     (goto-char (vhdl-point 'boi))
		     (and (re-search-forward "\\(<\\|:\\)="
					     (vhdl-point 'eol) t)
			  (- (point) (vhdl-point 'boi)))))
	   (curcol (progn
		     (goto-char relpos)
		     (current-column)))
	   foundp)
      (while (and (not foundp)
		  (< (point) (vhdl-point 'eol)))
	(re-search-forward "\\(<\\|:\\)=\\|(" (vhdl-point 'eol) 'move)
	(if (vhdl-in-literal (cdr langelem))
	    (forward-char)
	  (if (= (preceding-char) ?\()
	      ;; skip over any parenthesized expressions
	      (goto-char (min (vhdl-point 'eol)
			      (scan-lists (point) 1 1)))
	    ;; found an assignment operator (not at eol)
	    (setq foundp (not (looking-at "\\s-*$"))))))
      (if (not foundp)
	  ;; there's no assignment operator on the line
	  vhdl-basic-offset
	;; calculate indentation column after assign and ws, unless
	;; our line contains an assignment operator
	(if (not assignp)
	    (progn
	      (forward-char)
	      (skip-chars-forward " \t")
	      (setq assignp 0)))
	(- (current-column) assignp curcol))
      )))

Custom indent functions take a single argument, which is a syntactic component cons cell (see Syntactic Analysis). The function returns an integer offset value that will be added to the running total indentation for the line. Note that what actually gets returned is the difference between the column that the signal assignment operator is on, and the column of the buffer relative position passed in the function’s argument. Remember that VHDL Mode automatically adds in the column of the component’s relative buffer position and we don’t want that value added into the final total twice.

Now, to associate the function vhdl-lineup-statement-cont with the statement-cont syntactic symbol, we can add something like the following to our vhdl-mode-hook:


(vhdl-set-offset 'statement-cont 'vhdl-lineup-statement-cont)

Now the function looks like this after re-indenting (using M-x vhdl-indent-defun):


%%% TBD %%%

Custom indentation functions can be as simple or as complex as you like, and any syntactic symbol that appears in vhdl-offsets-alist can have a custom indentation function associated with it. Note however that using many custom indentation functions may have a performance impact on VHDL Mode.