[comp.emacs] out-indent: outline mode enhancement

bard@THEORY.LCS.MIT.EDU (06/14/89)

I write heavily-indented outlines that look like this:

* Wombats
 ** From Hell
 ** From the Moons of Neptune
   *** Highly Insulated
   *** Methane-breathing
 ** From Australia
   *** Reported in science fiction stories
   *** Real

and so I wrote a few commands for inserting the heading points more
automatically: e.g., c-c c-c gives you another heading point at the current
depth, c-c c-e gives you one further in, and so on.

-- Bard the emacs gargoyle

------------------------ cut here ------------------------

;; out-indent.el -- indented outline-mode 
;; Copyright (C) Bard Bloom, June 1989
;; bard@theory.lcs.mit.edu

;; This file is not yet part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;; WHAT THIS DOES:
;; This lets you write outlines which are textually as well as logically
;; indented.   It is best suited to writing outlines and things like that,
;; which is one of my major uses of outline-mode.
;;
;; For example, you can write:
;;
;; * Heading
;;  ** Subheading-1
;;  ** Subheading-2
;;   *** Sub-subheading
;;  ** Subheading-3
;;
;; OK. You could do this before, too, but you had to put in all those 
;; spaces and stars by hand.  Now you can put them in by emacs commands,
;; and it even gets the spaces right.
;;
;;
;; Here's what you do.  
;; Have your outline-mode-hook call set-outline-levels 
;;      (or use the one I have provided).
;;      The outline-levels is a list of the headings you want.
;;      It is actually an association-list, with elements of the form:
;;         (level  outline-string)
;;      For example,
;;         ((1  "*")
;;          (2  " **")
;;          (3  "  ***")
;;          (1  ">"))
;;      This will use 1-3 stars for depth 1-3 points; it will recognize a ">" 
;;      but not insert it.  (Note: outline-regexp is built automatically from 
;;      this alist; you shouldn't set it yourself.  The things are
;;      regexp-quoted, so don't worry about using *'s.)
;;      
;;
;; The commands for making new outline points:
;; c-c c-c: start a new outline point on the next line, 
;;          which is (by default) at the same level as this one.
;;          If you're at a **, this will make a new **.
;;          c-u c-c c-c means make a top-level heading.
;;          c-u c-u c-c c-c means make a second-level heading.
;;          numeric argument makes a heading of that level.
;; c-c c-d: Start a heading which is one (or ARG) levels further out.
;;          Go from "**" to "*"
;; c-c c-e: Start a heading which is one (or ARG) levels deeper.
;;          Go from "**" to "***"
;;
;; This works quite well with Martin Neitzel's gin-mode, a gadget which guesses
;; the proper indentation for the current line.  The variable 
;; gin-left-hang-indent-re should have have something like
;;     \\*+\\s +     
;; (with double-backslashes for use with strings)
;; so that each point fills in a block nicely.
;;
;; I do not consider this perfected yet.  Comments and improvements 
;; to bard@theory.lcs.mit.edu.

(provide 'out-indent)

(defun set-outline-levels (level-list)
  "Sets outline-levels. The argument LEVEL-LIST should be a list looking
like:
  (
   (1 \"*\")
   (2 \" **\")
   (3 \"  ***\"))
The numbers tell what level the outline entry is at.  The string is the 
string which will start entries at that level: things at level two (here) 
will start with a space and two stars."
  (setq outline-levels level-list)
  (setq outline-level-regexp (regexp-quote "*"))
  (dolist (i outline-levels)
    (setq outline-level-regexp
          (concat outline-level-regexp "\\|" (regexp-quote (cadr i))))))

(set-outline-levels
 '((1 "*")
   (2 "  **")
   (3 "    ***")
   (4 "      ****")
   (5 "        *****")
   (6 "          ******")
   (7 "            *******")))


(defun outline-new-heading (arg)
  "Start a new outline point on the next line.
Default level is the same as the last one.  
numeric ARG sets the level of the heading.
c-u means level one,
c-u c-u means level two."
  (interactive "P")
  (outline-maybe-newline)
  (let ((depth
         (cond
          ((null arg)
           (outline-previous-depth))
          ((equal arg '(4)) 1)
          ((equal arg '(16)) 2)
          ((integerp arg)
           arg)
          (t
           (error "huh?")))))
    (insert (cadr (assq depth outline-levels)) " ")))

(defun outline-previous-depth ()
  (catch 'outline-depth-found
    (save-excursion
      (while (not (bobp))
        (previous-line 1)
        (beginning-of-line 1)
        (when (looking-at outline-level-regexp)
          (dolist (l outline-levels)
            (when (looking-at (regexp-quote (cadr l)))
              (throw 'outline-depth-found (car l)))))
      ))
    ;; At this point, we're lost -- assume depth 1.
    (throw 'outline-depth-found 1)
    ))

(define-key outline-mode-map "\C-c\C-c" 'outline-new-heading)
(define-key outline-mode-map "\C-c\C-d" 'outline-new-outer-heading)
(define-key outline-mode-map "\C-c\C-e" 'outline-new-inner-heading)

(defun outline-new-outer-heading (arg)
  "Start a new outline point on the next line, ARG levels farther out."
  (interactive "p")
  (outline-maybe-newline)
  (insert (cadr (assq (- (outline-previous-depth) arg)
                      outline-levels)) " "))

(defun outline-new-inner-heading (arg)
  "Start a new outline point on the next line, ARG levels deeper."
  (interactive "p")
  (outline-maybe-newline)
  (insert (cadr (assq (+ (outline-previous-depth) arg)
                      outline-levels)) " "))

(defun outline-maybe-newline ()
  (let ((shouldnt-newline
         (save-excursion
           (beginning-of-line 1)
           (looking-at "^[ \t]*$"))))
    (cond
     (shouldnt-newline
      (delete-region
       (save-excursion (beginning-of-line 1) (point))
       (save-excursion (end-of-line 1) (point))))
     (t (newline)))))