Like everything else posted to kragen-hacks without any notice to the
contrary, this program is in the public domain; I abandon any
copyright in it.
;;; Toggling of argument lists between horizontal and vertical.
;; For example, turn this: memset(bigstring, '\xe3', bigstringsize-1);
;; into this: memset(bigstring,
;; '\xe3',
;; bigstringsize-1);
;; or vice versa.
;; This was really useful at Airwave back in 2004, but I never
;; understood how it worked. I miss it, so I reimplemented it, which
;; took me a couple of hours. The idea is that when you're writing
;; out an argument list becomes too long to write on one line, you
;; have a single key to put each item on its own line; and that same
;; key does the inverse operation, if the argument list is already on
;; multiple lines.
;; Works on things other than argument lists, too, like {}-enclosed
;; blocks of statements, or list and dict displays in Python.
;; Bugs:
;;
;; - doesn't escape from comments the way it escapes from strings
;; - doesn't drop the trailing separator when doing
;; vertical-to-horizontal
;; - doesn't add a trailing separator when going
;; horizontal-to-vertical
;; - removes trailing whitespace going horizontal-to-vertical, even
;; before a close delimiter, even if there's leading whitespace
;; after the open delimiter
;; - doesn't understand comments-to-the-end-of-the-line and how they
;; screw up the transformation
;; - always puts the first argument on the same line as the open
;; delimiter; it would be better to have a third format in which
;; that first item indented on the next line instead.
;; - the functions should probably get a package prefix on their names
(defun inside-string-p ()
"Returns true if we're inside a string."
(cadddr (syntax-ppss)))
(defun backward-up-list-escaping-strings ()
"Like backward-up-list, but works if we're inside a string."
;; probably should take comments into account too
(while (inside-string-p) (backward-char))
(backward-up-list))
(defun start-of-list ()
"Go to inside the start of the currently enclosing list --- e.g. arg list."
(interactive)
(backward-up-list-escaping-strings)
(down-list))
(defun end-of-list-p ()
"Can we move no further forward without going up a list?"
(looking-at "\\(\\s.\\|\n\\|\\s-\\)*\\s)"))
(defun horizontal-to-vertical-list ()
"Turn a horizontal argument list into a vertical argument list.
This is written so that it only breaks at commas and semicolons;
too bad for Lisps."
(interactive)
(save-excursion
(start-of-list)
(while (not (end-of-list-p))
(while (not (or (end-of-list-p) (looking-at "\\s-*[;,]"))) ; skip over arg
(forward-sexp))
(while (looking-at "\\s-*[;,]") (forward-char)) ; skip over comma
;; now delete whitespace after comma
(while (and (not (looking-at "\n")) (looking-at "\\s-")) (delete-char 1))
(when (not (end-of-list-p)) ; insert newline if needed and indent
(if (looking-at "\n") (forward-char) (insert "\n"))
(indent-for-tab-command)))
(if (current-list-horizontal-p)
(message "Couldn't find any commas or semicolons. Are you editing Lisp?"))))
(defun vertical-to-horizontal-list ()
(interactive)
(save-excursion
(backward-up-list-escaping-strings)
(forward-list)
(backward-char)
(while (not (current-list-horizontal-p))
(save-excursion (delete-indentation)))))
(defun current-list-horizontal-p ()
"Returns nil unless the list around point is all on one line."
(save-excursion
(backward-up-list-escaping-strings)
(let ((start (point)))
(forward-list)
(= 1 (count-lines start (point))))))
(defun toggle-list-orientation ()
"Turn a horizontal list into a vertical one, or vice versa."
(interactive)
(if (current-list-horizontal-p)
(horizontal-to-vertical-list)
(vertical-to-horizontal-list)))
(global-set-key [f7] 'toggle-list-orientation)