;; SYNOPSIS: Move by paragraphs.
;; AUTHOR: GPL(C) Mohsin Ahmed, http://www.cs.albany.edu/~mosh
;; HOW:
;;   Move by paragraphs, if destination is in sight.
;;   Else first to window border, then scroll up Or recenter.
;;   
;;   If you can see the whole para, you can also
;;   format it while moving over it.
;;   
;;   Use second arg to format(fill) as well.
;;   Notes:
;;   o Count should be 1 or -1 during filling.
;;   o If para is too big, format it with C-u M-x C-q.
;;     Since you don't want to fill an unseen para.
;;

(defvar mosh-move-para-show nil "Whether to show para or move.")

(defun mosh-move-para-show (count fill)
  "Move by paras when destination is visible.
   If para too large move in stages:
   (a) recenter (b) move to window end.
  "
  (interactive)
  (let (dest)
    (save-excursion
      (forward-paragraph count)
      (setq dest (point)))
    (cond
     ;; Para end is visible? move and/or format.
     ((pos-visible-in-window-p dest)
      (if (not fill)
          (goto-char dest)  ;; Just move.
        ;; We must format while moving.
        (cond
         ;; We can't use goto-char as para has changed.
         ((> count 0) (fill-paragraph fill) (forward-paragraph 1))
         (t (goto-char dest) (fill-paragraph fill))
        )
      )
     )
     ;; Para too big, do any one of:
     (t
      (move-to-window-line count)  ;; to loop over visible paras.
      ;; (scroll-up count)         ;; move by screenfuls
      ;; (recenter count)          ;; move by windows.
     )
)))

(defun mosh-format-this-para (&optional go)
  "Format a para, optionally goto end of para."
  ;; (setq eop 100000)

  (interactive)
  (let (eop (case-fold-search nil))

    (if (looking-at "\n")
        (skip-chars-forward "\n"))

    ;; See how far this para extends.

    (save-excursion
      (forward-paragraph)
      (setq eop (point))
    )

    ;; We must not fill with lines before us.

    (save-excursion
      (beginning-of-line) (backward-char 2)
      (if (looking-at "\n") nil
        (forward-char 2)
        (insert "\n"))
    )

    ;; Assume Capital letters at beginning of line mean new para.
    ;; So separate that line.

    (save-excursion
      (forward-char)
      (if (re-search-forward "^ *[A-Z]" eop t)
          (progn (goto-char (match-beginning 0)) (insert "\n")))
    )
  )

  ;; Now do it.

  (fill-paragraph 1)
  (if go (forward-paragraph))
)

(mosh-map-local-keys
     [C-up]     '(mosh-move-para-show -1 nil)
     [C-down]   '(mosh-move-para-show  1 nil)
     [M-C-up]   '(mosh-move-para-show -1 t)
     [M-C-down] '(mosh-move-para-show  1 t)
     [S-down]   '(mosh-format-this-para t)
)

(provide 'mpara)

;;; EOF


