Erlang mode in Emacs

Luke Gorrie <>
Tue Dec 17 16:22:09 CET 2002


Luke Gorrie <> writes:

> I have a command for aligning the arrows in function heads that I've
> been meaning to send in for years - maybe something we can add to the
> mode and assign a key?

Here's an updated version, which works like the old one except that if
you give a prefix argument (by pressing C-u before the command), it
will indent all arrows in the region, not just the ones in function
heads. Fix courtesy of Matthias Lang, and meant for expressions like
this:

  parse([open, {name, Name}|T]) -> 
      case Name of 
          "bye"      -> {ok, #cmd_tuple{command = bye}};
          "delete"   -> delete(T);
          "install"  -> install(T);
          "new"      -> new(T);
          "nop"      -> {ok, #cmd_tuple{command = nop}};
          "query"    -> qry(T);
          "reset"    -> reset(T);
          "set"      -> set(T);
          "takeover" -> takeover(T);
          "update"   -> update(T);
          "zero"     -> zero(T)
      end.

Also, the `line-beginning-position' function doesn't exist in XEmacs,
so here is a workaround to make the erlang-align-arrows work there:

  (unless (fboundp 'line-beginning-position)
    (defalias 'line-beginning-position 'point-at-bol))

(We can't just use point-at-bol all the time, because Emacs20 lacks
that - it seems that some Emacs has to work around.)

Okay, so here's the updated command (two functions now):

(defun erlang-align-arrows (start end)
  "Align arrows (\"->\") in function clauses from START to END.
When called interactively, aligns arrows after function clauses inside
the region.

With a prefix argument, aligns all arrows, not just those in function
clauses.

Example:

sum(L) -> sum(L, 0).
sum([H|T], Sum) -> sum(T, Sum + H);
sum([], Sum) -> Sum.

becomes:

sum(L)          -> sum(L, 0).
sum([H|T], Sum) -> sum(T, Sum + H);
sum([], Sum)    -> Sum."
  (interactive "r")
  (save-excursion
    (let (;; regexp for matching arrows. without a prefix argument,
	  ;; the regexp matches function heads. With a prefix, it
	  ;; matches any arrow.
	  (re (if current-prefix-arg
		  "^.*\\(\\)->"
		(concat "^" erlang-atom-regexp ".*\\(\\)->")))
	  ;; part of regexp matching directly before the arrow
	  (arrow-match-pos (if current-prefix-arg
			       1
			     (1+ erlang-atom-regexp-matches)))
	  ;; accumulator for positions where arrows are found, ordered
	  ;; by buffer position (from greatest to smallest)
	  (arrow-positions '())
	  ;; accumulator for longest distance from start of line to arrow
	  (most-indent 0)
	  ;; marker to track the end of the region we're aligning
	  (end-marker (progn (goto-char end)
			     (point-marker))))
      ;; Pass 1: Find the arrow positions, adjust the whitespace
      ;; before each arrow to one space, and find the greatest
      ;; indentation level.
      (goto-char start)
      (while (re-search-forward re end-marker t)
	(goto-char (match-beginning arrow-match-pos))
	(just-one-space)		; adjust whitespace
	(setq arrow-positions (cons (point) arrow-positions))
	(setq most-indent (max most-indent (erlang-column-number))))
      (set-marker end-marker nil)	; free the marker
      ;; Pass 2: Insert extra padding so that all arrow indentation is
      ;; equal. This is done last-to-first by buffer position, so that
      ;; inserting spaces before one arrow doesn't change the
      ;; positions of the next ones.
      (mapcar (lambda (arrow-pos)
		(goto-char arrow-pos)
		(let* ((pad (- most-indent (erlang-column-number))))
		  (when (> pad 0)
		    (insert (make-string pad ? )))))
	      arrow-positions))))

(defun erlang-column-number ()
  "Return the column number of the current position in the buffer.
Tab characters are counted by their visual width."
  (string-width (buffer-substring (line-beginning-position) (point))))




More information about the erlang-questions mailing list