;;; SYNOPSIS: Align tables /columns etc.
;;; AUTHOR: GPL(C) Mohsin Ahmed, http://www.cs.albany.edu/~mosh
;;; Other alignment functions in align.el - eg. mosh-justify-right.
;;; Also checkout (indent-to) or (indent-to-column)

(defvar mosh-align-column 0
  "Used to mark and then indent to specific columns, see f8-tab f9-tab."
)

(defun mosh-note-align-column ()
  "Note current column"
  (interactive)
  (setq mosh-align-column (current-column))
  (message "mosh-set-align-column: %d" mosh-align-column)
)

;; Sample usage:
;;        [f8 ?1]     'mosh-note-align-column
;;        [f9 ?1]     '(mosh-tab-to-column mosh-align-column))

(defun mosh-tab-to-column (i)
  "Insert spaces to reach column i or fill-column."
  (interactive "p")                     ; user can supply column.
  (delete-horizontal-space)             ; remove excess space.
  (if (eq i 1)(setq i fill-column))     ; use default column?
  (insert-char 32 (- i (current-column))) ; to reach column i.
)


(defun mosh-line-length (&optional line)
  "Return length of line, optional next line."
  (save-excursion
    (and line (forward-line line))
    (end-of-line)
    (current-column)
))

(defun mosh-justify-right ()
   "Justify, Push cursor right until end is at fill-column."
   (interactive)
   (let (i cur-column)
     (setq cur-column (current-column))
     (if (eolp) nil
         (end-of-line) (mosh-eat-whitespace-backward))
     (setq i (- fill-column (current-column)))
     (move-to-column cur-column)
     (insert-char 32 i) ; space
))

(defun mosh-align-rectangle (start end)
  "In a rectangle, remove white spaces from column n [current-column].
   Both end points should be at the same column n."
  (interactive "r")
  (operate-on-rectangle
     '(lambda (a b c) (while (looking-at "[ \t]") (delete-char 1)))
     start end nil)
)

(defun mosh-align-table (regexp mincol alcol)
  "What you do:
       Goto TEXT,       note column number-1, this is mincol to ignore.
       Push text right, note column number-2, this is alcol to align.
       Goto end of text and put mark,
       Goto start, and press \\[mosh-align-table] to begin.

   You supply regexp that matches TEXT on each line of the region
   after mincol, this text will be pushed to alcol.

   For each line, you say yes/no/quit.
  "
  (interactive "sregexp to align:\nnColumns to ignore:\nnColum to align on:")

  (beginning-of-line)
  (let (achar)
    (while
        (and
         (re-search-forward regexp (point-max) t)
         (goto-char (match-beginning 0))
         (message "align y/n/q? ")
         (setq achar (read-char))
         (not (eq achar ?q)))
      (if (eq achar ?y)
          (progn
            (mosh-tab-to-column alcol)
            (mosh-color-region (point) (match-end 0) 'bold)))
      (forward-line)
      (if mincol
          (move-to-column mincol))
)))

;;; ====================================================================
;;;   Algorithm (mosh-convex-align arg 1 1)
;;;   {
;;;     c = current column;
;;;     repeat
;;;          previous line,
;;;          end of line, ignore spaces at line end.
;;;          d = current column,
;;;          until d > c + delta or no more lines;
;;;     forward word.
;;;     e = current column.
;;;     if e <= d
;;;         return as forward word must have moved past a newline.
;;;     come back to original position.
;;;         move from c to e
;;;         remove spaces ahead of us.
;;;   }
;;;
;;; Eg. Try aligning word back and forth.
;;; xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx
;;; xxxxxxxxxxxx xxxx
;;; xxxxx xxxx
;;;  word
;;; xxxxx xxxx
;;; xxxxxxxxxxxx xxxxxxx
;;; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx


(defun mosh-convex-align (&optional worddir linedir)
  "Align with the line above (or below if linedir is -1),
   Align with the next (or previous word if worddir is -1),
   Default for both is 1 for next word, previous line.

   It finds a line long enough that the user is possibly trying to
   align with. On that line, it looks for possible tab marks and
   moves the current line accordingly.
   Keys:
          M-right is (mosh-convex-align  1 1)
          M-left  is (mosh-convex-align -1 1)"

  (setq worddir (or worddir 1))              ; Defaults
  (setq linedir (or linedir 1))

  (if (and (bolp) (eq worddir -1))           ; Nothing to do?
      (error "Beginning of line."))
  (let (thiscol thatcol minlen)
    (setq thiscol (current-column))
    (if (equal worddir 1)
        (setq minlen         (+ thiscol 2))
        (setq minlen  (max 0 (- thiscol 2)))
        )
    (save-excursion
      (while
          (progn
            (previous-line linedir)
            (end-of-line)
            (skip-chars-backward "\t ")
            (< (current-column) minlen)
            ))
      (move-to-column thiscol)
      (mosh-move-word worddir)
      ; (forward-word  worddir)

      (setq thatcol (current-column)))

    (if (eq worddir 1)
        (if (> thatcol thiscol)
            (insert-char 32 (- thatcol thiscol)) ; move forward
            ; (insert-char 32 4)           won't work as of now.
            )
        (if (< thatcol thiscol)
           (move-to-column thatcol)              ; go back
           (move-to-column minlen)               ; else move back anyways.
           )
        )
    ) ; endlet.
  ; remove excess space.
  (delete-region (point) (progn (skip-chars-forward " \t")(point)))
)

(provide 'align)
;; EOF

