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)

Reply via email to