I find myself using M-x align
a lot in Emacs to line up the identifiers in blocks of C declarations or statements like the following:
struct foo { something x; int hello; long *blodgett; }; int *q; bazbazbaz *y; static char fnork; spoonful = sugar; spork = 1;
After marking the region containing the declarations a quick M-x align
transforms it into:
struct foo { something x; int hello; long *blodgett; }; int *q; bazbazbaz *y; static char fnork; spoonful = sugar; spork = 1;
Which looks much neater to my eyes. However when the declarations have initialisers the result is not at all pleasing:
int x = 5; struct baz baz = { 1, 2 }; const int *this_is_a_pointer = NULL;
Becomes
int x = 5; struct baz baz = { 1, 2 }; const int *this_is_a_pointer = NULL;
Really I only want it to align the identifiers and leave the =
where they were. I’m sure I can’t be the only one with this problem but I’ve no idea what to type into Google or Stack Overflow to find the solution, and the manual doesn’t help either.
My current solution is to patch the list of alignment rules in align-rules-list
with this bit of hackery:
;;; Define this in a file with `lexical-binding' set or else change the ;;; `let' below to `lexical-let'. (defun nick-fix-c-align () (let ((var-decl-regexp (alist-get 'regexp (alist-get 'c-variable-declaration align-rules-list)))) (push `(valid . ,(lambda () (not (save-excursion (end-of-line) (looking-back var-decl-regexp))))) (alist-get 'c-assignment align-rules-list))))
The variable align-rules-list
holds a list of rules for patterns like “C assignment”, “C variable declaration”, and so on. Each rule is an alist with a regex trigger, a list of modes to enable it in, an optional predicate valid
to further restrict when it is run, and some other irrelevant options. The align
function loops over this list and aligns any text where the regex matches and valid
returns true.
The problem is C declarations with intialisers trigger both the c-variable-declaration
and the c-assignment
rules. There’s no way to tell it “stop processing rules if this matches” so the function above modifies the rules list to add an extra predicate to the c-assignment
rule which says “do not apply this rule when the line also matches the regex for C variable declarations”.
align-load-hook
is called after align.el
has been loaded, which is a convienent time to patch the rules list. Afterwards M-x align
lines up only the identifiers in declarations:
int x = 5; struct baz baz = { 1, 2 }; const int *this_is_a_pointer = NULL;
Not really related to this post but as comments are closed for your Phosh on S7 efforts, I wanted to draw your attention to https://gitlab.com/postmarketOS/pmaports/-/issues/948.
January 24, 2021 @ 11:19 am