summaryrefslogtreecommitdiffstats
path: root/content/posts/WIP-emacs-align-columns/index.org
blob: eb140f8ce852bc1ee98f474b01db201a8f4a0595 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#+TITLE: Aligning columns in Emacs
#+DATE: 2021-03-26T11:27:34-04:00
#+DRAFT: false
#+DESCRIPTION: How to align text separated by whitespace using Emacs
#+TAGS[]: emacs elisp
#+KEYWORDS[]: emacs elisp
#+SLUG:
#+SUMMARY:

#+ATTR_HTML: :alt Before and after columns aligned
#+ATTR_HTML: :title Before and after aligned columns
[[file:cover.png]]

I've been writing a lot of GraphQL resolvers in ruby at work recently,
and frequently run into a situation where I have to align many columns
of text by hand. I figured this isn't a very unique problem, and that
there was probably already some code within Emacs to handle it.

As it turns out I was correct in my assumption. There's an excellent
built-in package called =align= that takes care of just that.

Many of the features of the =align= package are mode-specific, but I
just wanted a function that can align columns regardless of which mode
they're used in. There's a function that's part of the package that
can be used for just that fortunately, =align-regexp=, which takes a
regular expression as input and uses it to guide the alignment.

I was able to use it to make a generic function.

#+begin_src elisp
(defun align-non-space (BEG END)
  "Align non-space columns in region BEG END."
  (interactive "r")
  (align-regexp BEG END "\\(\\s-*\\)\\S-+" 1 1 t))
#+end_src

Let's walk through what's going on here.

- =BEG= and =END= are the beginning and end positions of the area to be aligned
- =(interactive "r")= tells Emacs that the function is interactive,
  meaning that it can be called from the =M-x= menu
  - ="r"= tells Emacs that when the function is called interactively, it expects a
    region (beginning and end points) as arguments
- =align-regexp= where the work is happening. This function has the
  following signature.
  #+begin_src elisp
  (align-regexp BEG END REGEXP &optional GROUP SPACING REPEAT)
  #+end_src

  - =BEG= and =END= is the region that it expects as the first arguments
  - ="\\(\\s-*\\)\\S-+"= is an Emacs regular expression. Backslashes
    are doubled because they need to be escaped in a string literal
    - =\( \)= is a capture group
      - =\s-= is a [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Backslash.html][regular expression construct]] specific to Emacs which
        specifies a type of /syntax/ which is to be matched.
        - =-= refers to the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Class-Table.html][whitespace characters]]
      - =*= means match zero or more
    - =\S-= is similar to the previous construct, but instead means to
      match anything /other/ than whitespace
    - =+= means one or more
  - =1= here refers to the group within the regex that will be
    modified to align the fields
  - =1= is the number of spaces between fields once aligned
  - =t= indicates to repeat the rule multiple times on the same line

To use this function, simply highlight a region you want to align and
run =M-x align-non-space=.