[comp.emacs] Answers to "Two EMACS Questions."

eberard@ajpo.sei.cmu.edu (Edward Berard) (09/10/89)

Earlier, I asked "two Emacs questions." I received a number of responses.
I am including the proposed answers in this message. [Since some of the 
answers include the original questions, I am not repeating the questions.]

Thanks to all who replied, both to comp.emacs, and to me.

				-- Ed Berard

===============================================

From: John Robinson <jr@BBN.COM>

I assume here you are talking about GNU emacs (you ought to have said
which one in your posting; this list goes to readers who use various
PC emacses, non-GNU unix emacses, etc.)

> 	1. Is there a way in emacs to create "hanging indents" like
> 	   this one? What I would like is to be able to create a
> 	   paragraph formatted like this one, and not have to shift
> 	   each line, re-format, etc. myself.
> 

I think the following package may have what you want.  It has a
"fill-what-I-mean" semantics.  It goes back to the beginning of a
paragraph, preserves the indentation of the first line, goes to the
second line, assumes that that line's indentation is what's intended
for the remainder of the paragraph, and fills from there to the end of
paragraph with that prefix.

With the standard stuff, the first-line treatment is more of a
problem, and you have to explicitly set the fill-prefix with ^X .
(set-fill-prefix).  Also, you may want to modify paragraph-separate
and paragraph-start.

Contact this author:

  newpara		  
       Bruce Israel, <israel@brillig.umd.edu>
       Fill paragraph properly.

There is also indented-text mode, which I don't know much about, but
you might read up on it.

> 	2. I have a large (approximately 600 lines) file. Each line in
> 	   the file looks like two strings separated by a tab. Since
> 	   the length of the first string varies, the tab is not in
> 	   the same place in each line. Is there any way to tell emacs
> 	   to take every line in the file and cause the second string
> 	   in each line to begin at the same column?

Hmmmm ... it should work to set the variable tab-width to something
larger than the length of your longest first component.  The handy
thing to do would be to jam the setting into a local modes trailer at
the end of the file (assuming you have some way to add a comment
line).  Unfortunately, this did not seem to have the intended effect
when I did it.  It sems that emacs, like Unix, insists that tab stops
always occur at columns 9, 17, 25, ...  The tab stops that it does
support are where you get to when you "indent-to-tab-stop", but it
indents you there by putting in the right number of tabs and spaces on
the assumption that tabs are every 8 spaces.

So you would have to write two functions, one which expanded the tabs
so that each line had enough to get to say column 41 (and you would
use tab-to-tab-stop to get to that column for new lines you type if
you set tab-width to 40), and another function which you use to
compress the multiple tabs out again when you save the file.  You
could make the expansions and compression automatic through the use of
local variables, auto-modes, and read- or write-file-hooks (some
subset).

To learn more about this stuff, consult the info system (^H i) or read
the manual.  The contents of the info system and the manual are the
same.

===============================================

From: bard@cs.cornell.edu (Bard Bloom):

(Assuming you're on unix)

1) Make a subdirectory ~/emacs

2) Put the stuff at the end of this message into ~/emacs/gin-mode.el

3) Make a file ~/.emacs if you don't have one already.  

4) Put the lines
      (load-file "~/emacs/gin-mode.el") 
   in your ~/.emacs file.

5) To enter gin-mode, type m-x gin-mode.  (There are other ways --- I have it
   on all the time, by the command
   (setq text-mode-hook (function (lambda () (gin-mode 1))))
   in my ~/.emacs file.)

6) Now, several kinds of things (including numbered lists like this one) will
   automatically indent themselves right.  The things are:

   \item's in LaTeX
   
   - Lines beginning with dashes, stars, or equals-signs --- one or more of
     them, and they don't all need to be the same.

   a. Single Letters and numbers followed by `.', `:', or `)'.  Just one of
      these.  This sometimes screws up.

It's not perfect, but it usually works pretty well.

-- Bard

---- put the stuff after this line into a file ~/emacs/gin-mode.el ----


;; gin-mode.el -- Set up minor mode with guess-indent stuff.
;; Copyright (C) Martin Neitzel, May 1989

;; 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.


;; SUMMARY
;;
;; Gnu Emacs supports filling of paragraphs and wrapping of lines with a
;; settable string to be used for the left margin, the variable
;; ``fill-prefix''.  Setting this variable by hand is fine, but can
;; become mildly annoying if it has to be changed often in a document.
;; However, the appropriate value for fill-prefix can be derived from
;; the layout of the current line in almost all cases.
;;
;; This is a minor mode that ``guesses'' the indentation to be used
;; for (auto-) filling.  It has proven to be very handy in all text-mode
;; variants.  It uses a simple but effective heuristic to guess a
;; fill-prefix based on the current line and two (configurable) regular
;; expressions.  I almost never have to use "^X." explicitly anymore.
;; 
;; The two regexps control
;; 
;; 	* what line beginnings have to be taken as "fill-prefix" (my
;; 	  standard setup recognizes initial white space and typical
;; 	  mail-prefixes like ">" or "name> ").
;; 
;; 	* what line beginnings are really hanging indents.  The
;; 	  standard setup recognizes the stars used right here,
;; 	  enumerations, and some more...

;; The guessing stuff

(provide 'gin-mode)

(defvar gin-left-hang-indent-re
  (concat
   "\\s *\\("
     "\\(\\\\item\\(\\[.*\\]\\)?\\s +\\)\\|"
     "\\([---*=]+\\s +\\)\\|"
     "\\([a-zA-Z0-9][.:)]\\s +\\)\\|"
     "\\(\\(\\\\>\\)+\\s +\\)"
     "\\)"
     )
;  "\\s *\\(\\\\item\\(\\[.*\\]\\)?\\|[---*=]+\\s +\\|\\([a-zA-Z0-9][.:)]\\s +\\)\\|\\(\\\\>\\)+\\)"
;  "\\s *\\(\\\\item\\|[-*]\\|([a-zA-Z0-9])\\|[a-zA-Z0-9]\\.?:]?\\)\\s +"
  "*Regexp that defines a hanging indent of a paragraph.
If it is seen by gin-guess-prefix, the next lines are indented with
white space beyond the hanging indent.  Setting this variable makes
it buffer-local.")

(defvar gin-set-hang-re-last-actions "dh"
  "state saver for gin-set-hang-re's default/here/both argument.")

(defun gin-set-hang-re (new-re default-here-or-both)
  "Sets gin's left-hang regexp to NEW-RE.  DEFAULT-HERE-OR-BOTH is a string.  
If it contains a `d', this does a setq-default to try to set the regexp 
everywhere.  If it contains an `h', it sets it in this buffer.  You may do
both; in fact, that is the default. "
  (interactive
   (list
    (read-string "New Gin hang regexp: " gin-left-hang-indent-re)
    (read-string "Default and/or Here ([DH]*): ")))
  (if (string-match "[dD]" default-here-or-both)
      (setq-default gin-left-hang-indent-re new-re))
  (if (string-match "[hH]" default-here-or-both)
      (setq gin-left-hang-indent-re new-re)))

(defvar gin-retain-indent-re
  "[a-zA-Z]*>+[ \t]*\\|[ \t]+"
  "*Regexp that defines how a fill-prefix can look like.
If such a string is seen by gin-guess-prefix in the current line,
the next line will be indented with it, too.  Setting this variable
makes it buffer-local.")

(defun gin-guess-prefix ()
  "Try to figure out the prefix for the next line."
  (save-excursion
    (beginning-of-line)
    (cond ((looking-at gin-left-hang-indent-re)
	   (let ((beg (point))
		 indent-size
		 (indent-prefix ""))
	     (re-search-forward gin-left-hang-indent-re)
	     (setq indent-size (current-column))
	     ;; First gather tabs as needed ...
	     (if indent-tabs-mode
		 (while (>= indent-size tab-width)
		   (setq indent-prefix (concat indent-prefix "\t"))
		   (setq indent-size (- indent-size tab-width))))
	     ;; ... then append the rest as spaces:
	     (while (> indent-size 0)
	       (setq indent-prefix (concat indent-prefix " "))
	       (setq indent-size (1- indent-size)))
	     indent-prefix))

	  ((looking-at gin-retain-indent-re)
	   (buffer-substring (match-beginning 0) (match-end 0)))

	  (t ""))))



;; Replacements for old functions dealing with the fill-prefix.
;; Their function values are stuffed into the original symbols.

(defun gin-fill-paragraph (arg)
  "fill-paragraph in Gin mode, tries to guess the appropriate fill-prefix.
With arg, also justify."
  (interactive "P")
  (if gin-mode
      (let ((fill-prefix (gin-guess-prefix)))
	(funcall 'gin-old-fill-paragraph arg))
    (funcall 'gin-old-fill-paragraph arg)))

(defun gin-do-auto-fill()
  (if gin-mode
      (let ((fill-prefix (gin-guess-prefix)))
	(funcall 'gin-old-do-auto-fill))
    (funcall 'gin-old-do-auto-fill)))



;; When loaded for the first time, install our minor mode indicator

(defconst gin-old-fill-paragraph nil
  "Keeps the true fill-paragraph function during Gin mode.")

(defconst gin-old-do-auto-fill nil
  "Keeps the true do-auto-fill function during Gin mode.")

(defun gin-overlay-functions()
  "Undermine emacs with Gin stuff."
  (fset 'fill-paragraph (symbol-function 'gin-fill-paragraph))
  (fset 'do-auto-fill (symbol-function 'gin-do-auto-fill)))

(defun gin-restore-originals ()
  "Throw gin-mode functions out everywhere."
  (fset 'fill-paragraph (symbol-function 'gin-old-fill-paragraph))
  (fset 'do-auto-fill (symbol-function 'gin-old-do-auto-fill)))

(if (boundp 'gin-mode)
    nil
  (setq minor-mode-alist (cons '(gin-mode " Gin") 
			       minor-mode-alist))
  (make-variable-buffer-local 'gin-mode)
  (set-default 'gin-mode nil)
  (make-variable-buffer-local 'gin-left-hang-indent-re)
  (make-variable-buffer-local 'gin-retain-indent-re)
  (fset 'gin-old-fill-paragraph (symbol-function 'fill-paragraph))
  (fset 'gin-old-do-auto-fill (symbol-function 'do-auto-fill))
  (gin-overlay-functions))

  

(defun gin-mode (arg) 
  "Minor mode to guess indentations.
Toggle gin-mode, or turn it on iff optional ARG is positiv.

Gin mode adds the capability to \"guess\" a suitable indent for
filling based on the current line.  The line is matched against the
two regexps 'gin-left-hang-indent-re' and 'gin-retain-indent-re', see
their documentation.

When Gin mode is active, auto-filling and fill-paragraph will both use
a \"guessed\" value as fill-prefix."

  (interactive "P")
  (setq gin-mode
	(if (null arg) (not gin-mode)
	  (> (prefix-numeric-value arg) 0)))
  (set-buffer-modified-p (buffer-modified-p))
  (local-set-key "\eq" 'gin-alt-fill-paragraph)
  (local-set-key "\eo" 'gin-unindent-line)
  )

(defun gin-unindent-line ()
  "Gin-mode seems to be too enthusiastic sometimes.  This unindents the 
current line."
  (interactive)
  (save-excursion
    (beginning-of-line 1)
    (delete-horizontal-space)))

(defun gin-alt-fill-paragraph (arg)
  "Another fill-paragraph in Gin mode"
  (interactive "P")
  (undo-boundary)
  (save-excursion
    (let* ((a (point-after (bards-backward-paragraph 1)
                           (beginning-of-line 1)))
           (b (point-after (goto-char a) (forward-paragraph 1)))
           (fill-prefix (progn (goto-char a)
                               (gin-guess-prefix)))
           (nlines (count-lines a b) )
           )
      (fill-region-as-paragraph a b arg)
      (message "Fill prefix was `%s'." fill-prefix)
)))
        
===============================================

Bard also sent me the following functions which he said to place
near the beginning of my .emacs file:

(defmacro point-after (&rest commands)
  "Returns the value of point after executing the COMMANDS.  Doesn't move
point.  (Expands to (save-excursion COMMANDS (point)))."
  (` (save-excursion
       (,@ commands)
       point)))

;; these also go somewhere at the beginning of your .emacs file.

(defun bards-backward-paragraph (n)
  (interactive "p")
  (cond
    ((memq last-command '(bards-backward-paragraph))
     (backward-paragraph 1)))
  (backward-paragraph n)
  (forward-char 1))

(defun bards-forward-paragraph (n)
   (interactive "p")
   (if (eq last-command 'bards-forward-paragraph) (forward-paragraph 1))
   (forward-paragraph n)
   (backward-char 1))

===============================================

From: goodenough@SEI.CMU.EDU:

Hi Ed,

There was recently posted a macro that solves the hanging indent problem.
I've included it at the end of this message, together with info about how to
modify your .emacs file so it is always loaded.

As for removing the tabs, you can use M-X untabify, which replaces tabs with
spaces so that the text looks the same.  If the tabs don't go to the right
column, you can M-X edit-tab-stops to reset the tabs, and I think untabify
will work with the new tab positions.

-- gin-mode.el text here ---------------

;; gin-mode.el -- Set up minor mode with guess-indent stuff.
;; Copyright (C) Martin Neitzel, May 1989

;; 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.


;; SUMMARY
;;
;; Gnu Emacs supports filling of paragraphs and wrapping of lines with a
;; settable string to be used for the left margin, the variable
;; ``fill-prefix''.  Setting this variable by hand is fine, but can
;; become mildly annoying if it has to be changed often in a document.
;; However, the appropriate value for fill-prefix can be derived from
;; the layout of the current line in almost all cases.
;;
;; This is a minor mode that ``guesses'' the indentation to be used
;; for (auto-) filling.  It has proven to be very handy in all text-mode
;; variants.  It uses a simple but effective heuristic to guess a
;; fill-prefix based on the current line and two (configurable) regular
;; expressions.  I almost never have to use "^X." explicitly anymore.
;; 
;; The two regexps control
;; 
;; 	* what line beginnings have to be taken as "fill-prefix" (my
;; 	  standard setup recognizes initial white space and typical
;; 	  mail-prefixes like ">" or "name> ").
;; 
;; 	* what line beginnings are really hanging indents.  The
;; 	  standard setup recognizes the stars used right here,
;; 	  enumerations, and some more...

;; The guessing stuff

(provide 'gin-mode)

(defvar gin-left-hang-indent-re
  "\\s *\\([-*.]\\|([a-zA-Z0-9]+)\\|[a-zA-Z][.)]\\|[0-9]+[.):]\\)\\s +"
  "*Regexp that defines a hanging indent of a paragraph.
If it is seen by gin-guess-prefix, the next lines are indented with
white space beyond the hanging indent.  Setting this variable makes
it buffer-local.")

(defvar gin-retain-indent-re
  "[a-zA-Z]*>+[ \t]*\\|[ \t]+"
  "*Regexp that defines how a fill-prefix can look like.
If such a string is seen by gin-guess-prefix in the current line,
the next line will be indented with it, too.  Setting this variable
makes it buffer-local.")

(defun gin-guess-prefix ()
  "Try to figure out the prefix for the next line."
  (save-excursion
    (beginning-of-line)
    (cond ((looking-at gin-left-hang-indent-re)
	   (let ((beg (point))
		 indent-size
		 (indent-prefix ""))
	     (re-search-forward gin-left-hang-indent-re)
	     (setq indent-size (current-column))
	     ;; First gather tabs as needed ...
	     (if indent-tabs-mode
		 (while (>= indent-size tab-width)
		   (setq indent-prefix (concat indent-prefix "\t"))
		   (setq indent-size (- indent-size tab-width))))
	     ;; ... then append the rest as spaces:
	     (while (> indent-size 0)
	       (setq indent-prefix (concat indent-prefix " "))
	       (setq indent-size (1- indent-size)))
	     indent-prefix))

	  ((looking-at gin-retain-indent-re)
	   (buffer-substring (match-beginning 0) (match-end 0)))

	  (t ""))))



;; Replacements for old functions dealing with the fill-prefix.
;; Their function values are stuffed into the original symbols.

(defun gin-fill-paragraph (arg)
  "fill-paragraph in Gin mode, tries to guess the appropriate fill-prefix.
With arg, also justify."
  (interactive "P")
  (if gin-mode
      (let ((fill-prefix (gin-guess-prefix)))
	(funcall 'gin-old-fill-paragraph arg))
    (funcall 'gin-old-fill-paragraph arg)))

(defun gin-do-auto-fill()
  (if gin-mode
      (let ((fill-prefix (gin-guess-prefix)))
	(funcall 'gin-old-do-auto-fill))
    (funcall 'gin-old-do-auto-fill)))



;; When loaded for the first time, install our minor mode indicator

(defconst gin-old-fill-paragraph nil
  "Keeps the true fill-paragraph function during Gin mode.")

(defconst gin-old-do-auto-fill nil
  "Keeps the true do-auto-fill function during Gin mode.")

(defun gin-overlay-functions()
  "Undermine emacs with Gin stuff."
  (fset 'fill-paragraph (symbol-function 'gin-fill-paragraph))
  (fset 'do-auto-fill (symbol-function 'gin-do-auto-fill)))

(defun gin-restore-originals ()
  "Throw gin-mode functions out everywhere."
  (fset 'fill-paragraph (symbol-function 'gin-old-fill-paragraph))
  (fset 'do-auto-fill (symbol-function 'gin-old-do-auto-fill)))

(if (boundp 'gin-mode)
    nil
  (setq minor-mode-alist (cons '(gin-mode " Gin") 
			       minor-mode-alist))
  (make-variable-buffer-local 'gin-mode)
  (set-default 'gin-mode nil)
  (make-variable-buffer-local 'gin-left-hang-indent-re)
  (make-variable-buffer-local 'gin-retain-indent-re)
  (fset 'gin-old-fill-paragraph (symbol-function 'fill-paragraph))
  (fset 'gin-old-do-auto-fill (symbol-function 'do-auto-fill))
  (gin-overlay-functions))

  

(defun gin-mode (arg) 
  "Minor mode to guess indentations.
Toggle gin-mode, or turn it on iff optional ARG is positiv.

Gin mode adds the capability to \"guess\" a suitable indent for
filling based on the current line.  The line is matched against the
two regexps 'gin-left-hang-indent-re' and 'gin-retain-indent-re', see
their documentation.

When Gin mode is active, auto-filling and fill-paragraph will both use
a \"guessed\" value as fill-prefix."

  (interactive "P")
  (setq gin-mode
	(if (null arg) (not gin-mode)
	  (> (prefix-numeric-value arg) 0)))
  (set-buffer-modified-p (buffer-modified-p)))


------------cut here and add the following to your .emacs file  -------

(load-file "~/gin-mode.el")

(setq mail-mode-hook '(lambda ()
     (gin-mode 1)
     (auto-fill-mode 1)
))

(setq mail-setup-hook '(lambda ()
))

(setq text-mode-hook
   '(lambda ()
       (auto-fill-mode 1)
       (setq fill-column 78)
       (gin-mode 1)
     (setq paragraph-start '"^[\n\f.]\\|^@[a-zA-Z][^({[]")
     (setq paragraph-separate '"^[\f.]\\|^@[a-zA-Z][^({[]")
))

===============================================

From: novavax!weiner@ssd.harris.com (Bob Weiner):

   I am a person who uses emacs only occasionally, and when I do use it,
   I use it strictly as a word processor. I have two novice questions:

You neglected the first rule of comp.emacs.  Specify which emacs you are
talking about.  I'll assume GNU Emacs to anser your questions.

	   1. Is there a way in emacs to create "hanging indents" like
	      this one? What I would like is to be able to create a
	      paragraph formatted like this one, and not have to shift
	      each line, re-format, etc. myself.

Vanilla GNU Emacs allows you to create hanging indents if you turn on
auto-fill-mode and then do a set-fill-prefix {C-x .} at the column you
want every line except the first to fill to.  Then as you type, each
line is indented properly.  Refilling should probably work also.

It does not let you arbitrarily change the indent in a paragraph and
then have all lines but the first reindented to the proper place.  For
this, you need my par-align.el package which I am sending to you separately.

	   2. I have a large (approximately 600 lines) file. Each line in
	      the file looks like two strings separated by a tab. Since
	      the length of the first string varies, the tab is not in
	      the same place in each line. Is there any way to tell emacs
	      to take every line in the file and cause the second string
	      in each line to begin at the same column?

By coding a little function using the elisp function 'indent-to-column'
you can do this.  You would compute the max length of any item in the
first column and then indent past this value to start the second.

===============================================

From: novavax!weiner@ssd.harris.com (Bob Weiner):
Subject: par-align.el source

;;!emacs
;;
;; FILE:         par-align.el
;; SUMMARY:      Improved paragraph fill and align functions
;; USAGE:        GNU Emacs Lisp Library
;;
;; AUTHOR:       Bob Weiner, Applied Research, Motorola, Inc.
;; E-MAIL:       USENET:  weiner@novavax.UUCP
;; ORIG-DATE:    14-Apr-89
;; LAST-MOD:     18-Aug-89 at 23:26:17 by Bob Weiner
;;
;; Copyright (C) 1989 Bob Weiner and Free Software Foundation, Inc.
;; Available for use and distribution under the same terms as GNU Emacs.
;;
;; This file is not yet part of GNU Emacs.
;;
;; DESCRIPTION:  
;;
;;   This package allows GNU Emacs paragraph commands to deal more
;;   reasonably with indented text and embedded comments.  It lets you
;;   realign and set the fill prefix on one line of a paragraph, e.g. the
;;   first, and then have all the other lines match up when you fill the
;;   paragraph.
;;   
;;   I think the 'fill-paragraph-and-align' command eliminates the need for
;;   Kyle Jones, 'C Comment Edit' package.  Additionally, there is no need
;;   to edit in a special buffer.
;;   
;;   If you set the fill-prefix to some value, then set it to nil (set it at
;;   the beginning of a line) and then fill the paragraph, the old
;;   fill-prefix will be stripped off and the paragraph will be filled
;;   properly with no prefix.  Then just add the new fill-prefix, if any,
;;   that you want and refill again.
;;   
;;   If you have ever tried to fill Emacs Lisp documentation strings or
;;   embedded comments, you know that all you get is an ugly mess.  You end
;;   up having to fill after putting blank lines at the beginning and end.
;;   Now you can fill properly without making any inline changes.  The fill
;;   routines do not fill the first line of documentation strings since it is
;;   used by the apropos commands and may intentionally be short.
;;   
;;   The fill and movement commands also recognize multiple paragraphs
;;   within a single comment stretch, just like the ones you see in this
;;   description text.  Just remember to set the fill prefix properly, just
;;   as you normally would for the fill-paragraph command.
;;   
;;   When filling a region, fill-prefix-prev (old fill prefix) can be
;;   replaced by fill-prefix throughout the entire region before each
;;   paragraph is filled.  Simply call the 'fill-region-and-align-all'
;;   command.
;;   
;;   Always fill with point before some of the text to be filled.  If you
;;   try to fill when point is on a line following all of the text to be
;;   filled, the next paragraph may be filled by mistake, e.g. when filling
;;   with point near the end of a comment string.
;;
;;   C comments of the form (with any number of '*' chars):
;;   /*
;;    * <text>
;;    * <text>
;;    */
;;   are filled well.
;;
;;   Multiple line C comments of the form:
;;   /*        */
;;   /*        */
;;   are not filled well, so don't use them.  If you have some already, you
;;   can convert them to the kind above with the
;;   'c-comment-make-prefix-only' command found in this package.
;;
;;   Comments in UNIX shell scripts of the form (with any number of '#' chars):
;;   #
;;   # <text>
;;   # <text>
;;   #
;;   are filled well if the first line of the file begins with a pattern that
;;   matches the regular expression "^#!/bin/[ck]?sh" as shell scripts should.
;;
;;   Fortran comments of the form:
;;   C
;;   C <text>
;;   C <text>
;;   C
;;   are filled well.
;;
;;   The commands bound immediately below do all of this work.
;;
;; DESCRIP-END.


;; Suggested key bindings
;;
(global-set-key "\M-j" 'fill-paragraph-and-align)
(global-set-key "\C-x\C-j" 'fill-region-and-align-all)
(define-key text-mode-map "\C-x\C-j" 'fill-region-and-align)
(define-key indented-text-mode-map "\C-x\C-j" 'fill-region-and-align)
(define-key outline-mode-map "\C-x\C-j" 'fill-region-and-align)

;; The next two functions are suitable as replacements for the standard
;; paragraph movement commands if you want them to recognize paragraph
;; boundaries the way the fill functions below do.
;;
;; Suggested key bindings
;;
;;(global-set-key "\M-n" 'forward-para)
;;(global-set-key "\M-p" 'backward-para)

(defun forward-para (&optional arg)
  "Move forward to end of paragraph.  With ARG, do it arg times.
A line which  paragraph-start  matches either separates paragraphs,
if  paragraph-separate  matches it also, or is the first line of a paragraph.
A paragraph end is the beginning of a line which is not part of the paragraph
to which the end of the previous line belongs, or the end of the buffer."
  (interactive "p")
  (paragraph-filter 'forward-paragraph arg))

(defun backward-para (&optional arg)
  "Move backward to start of paragraph.  With ARG, do it arg times.
A paragraph start is the beginning of a line which is a first-line-of-paragraph
or which is ordinary text and follows a paragraph-separating line, except
if the first real line of a paragraph is preceded by a blank line,
the paragraph starts at that blank line.
See forward-paragraph for more information."
  (interactive "p")
  (or arg (setq arg 1))
  (paragraph-filter 'forward-paragraph (- arg)))


(defconst fill-prefix-prev nil
  "Previous string inserted at front of new line during filling, or nil for none.
Setting this variable automatically makes it local to the current buffer.")
(make-variable-buffer-local 'fill-prefix-prev)

;; Redefine this function so that it sets 'fill-prefix-prev' also.
(defun set-fill-prefix ()
  "Set the fill-prefix to the current line up to point.
Also sets fill-prefix-prev to previous value of fill-prefix.
Filling expects lines to start with the fill prefix and reinserts the fill
prefix in each resulting line."
  (interactive)
  (setq fill-prefix-prev fill-prefix
	fill-prefix (buffer-substring
		      (save-excursion (beginning-of-line) (point))
		      (point)))
  (if (equal fill-prefix-prev "")
      (setq fill-prefix-prev nil))
  (if (equal fill-prefix "")
      (setq fill-prefix nil))
  (if fill-prefix
      (message "fill-prefix: \"%s\"" fill-prefix)
    (message "fill-prefix cancelled")))

(defun fill-paragraph-and-align (justify-flag)
  "Fill current paragraph.  Prefix arg JUSTIFY-FLAG means justify as well.
Does not alter fill prefix on first line of paragraph.  Any whitespace
separated version of the fill-prefix, or fill-prefix-prev when fill-prefix is
nil, at the beginning of lines is deleted before the fill is performed.  This
aligns all lines in a paragraph properly after the fill-prefix is changed.
Works well within text, singly delimited C comments, Lisp comments and
documentation strings, and Fortran comments."
  (interactive "P")
  (paragraph-filter 'fill-para-align justify-flag))

(defun fill-region-and-align-all (justify-flag)
  "Fill each line in region.  Prefix arg JUSTIFY-FLAG means justify as well.
Replace any whitespace separated version of fill-prefix-prev with fill-prefix
in all lines of region.  This aligns all lines throughout the region properly
after the fill-prefix is changed.  Works well within text, singly delimited C
comments, UNIX shell scripts, Lisp comments and documentation strings, and
Fortran comments.
See also documentation for fill-region-and-align."
  (interactive "P")
  (paragraph-filter 'fill-region-align justify-flag t))

(defun fill-region-and-align (justify-flag &optional align-all)
  "Fill each line in region.  Prefix arg JUSTIFY-FLAG means justify as well.
Optional ALIGN-ALL non-nil means replace fill-prefix-prev with fill-prefix in
all lines of region, otherwise, does not alter prefix in paragraph separator
lines and first lines of paragraphs.
Any whitespace separated version of fill-prefix, or fill-prefix-prev when
fill-prefix is nil, at the beginning of appropriate lines is removed before the
fill is performed.  This aligns all lines in a paragraph properly after the
fill-prefix is changed.  Works well within text, singly delimited C comments,
UNIX shell scripts, Lisp comments and documentation strings, and Fortran comments."
  (interactive "P")
  (paragraph-filter 'fill-region-align justify-flag align-all))

(defun paragraph-filter (func arg1 &optional arg2)
;;;                         LISP          C            Fortran
;;;                         ----------------------------------------
;;; comment-start           ";"           "/* "        nil (^[Cc*])        
;;; comment-start-skip      ";+ *"        "/\\*+ *"    "![ \t]*"
;;; comment-end             "" ([^J^L])   " */"        "" (^J)
;;;
  (let* ((*lsp* (or (eq major-mode 'emacs-lisp-mode)
		 (eq major-mode 'lisp-interaction-mode)
		 (eq major-mode 'scheme-mode)
		 (eq major-mode 'lisp-mode)))
	 (*txt* (or (eq major-mode 'text-mode)
		    (eq major-mode 'indented-text-mode)
		    (eq major-mode 'outline-mode)
		    (eq major-mode 'picture-mode)))
	 (extra-para-sep
	   (concat "^"
		   ;; Don't change the '?' in the following lines to a '+', it
		   ;; will break certain fill boundary conditions.
		   (if (eq major-mode 'fortran-mode) "[cC*]?")
		   "[ \t]*\\("
		   (cond ((string-match "^#!/bin/[ck]?sh"
				       (buffer-substring 1 11))
			  "#*")
			 ;; Don't change the '?' in the following lines to a '+', it
			 ;; will break certain fill boundary conditions.
			 (comment-start-skip
			   (concat "\\(" comment-start-skip "\\)?")))
		   (if (eq major-mode 'c-mode) "\\|\*+/?")
		   "\\)[ \t]*$"
		   (if *lsp* "\\|^[ \t]*\\([[(]\\|\"\\)")
		   ;; Mainly for Interleaf TPS markup documents
		   (if *txt* "\\|^[@<]")
		   ))
	 (paragraph-separate
	   (concat paragraph-separate "\\|" extra-para-sep))
	 (paragraph-start
	   (concat paragraph-start "\\|" extra-para-sep)))
    (if arg2
	(funcall func arg1 arg2)
      (funcall func arg1))))

(defun fill-region-align (justify-flag &optional align-all)
  (if align-all
      (replace-fill-str fill-prefix-prev fill-prefix))
  (save-excursion
    (let ((end (max (point) (mark))))
      (goto-char (min (point) (mark)))
      (while (and (not (eobp)) (< (point) end))
	(fill-para-align justify-flag align-all)
	(forward-paragraph)
	;; Forward to real paragraph end if not in lisp mode
	(or *lsp* (re-search-forward (concat "\\'\\|" paragraph-separate)))))))

(defun fill-para-align (justify-flag &optional leave-prefix)
  (save-excursion
    (end-of-line)
    ;; Backward to para begin
    (re-search-backward (concat "\\`\\|" paragraph-separate))
    (forward-line (if (looking-at extra-para-sep) 2 1))
    (let ((region-start (point)))
      (forward-line -1)
      (let ((from (point)))
	(forward-paragraph)
	;; Forward to real paragraph end if not in lisp mode
	(or *lsp* (re-search-forward (concat "\\'\\|" paragraph-separate)))
	(beginning-of-line)
	(or leave-prefix
	    (replace-fill-str
	      (or fill-prefix fill-prefix-prev)
	      "" nil region-start (point)))
	(fill-region-as-paragraph from (point) justify-flag)))))

(defun replace-fill-str (fill-str-prev fill-str &optional suffix start end)
  "Replace whitespace separated FILL-STR-PREV with FILL-STR.
Optional SUFFIX non-nil means replace at ends of lines, default is beginnings.
Optional arguments START and END specify the replace region, default is the
current region."
  (if fill-str-prev
      (progn (if (not start) (setq start (min (point) (mark))))
	     (if (not end)   (setq end   (max (point) (mark))))
	     (if (not fill-str) (setq fill-str ""))
	     (save-excursion
	       (save-restriction
		 (narrow-to-region start end)
		 (goto-char (point-min))
		 (perform-replace
		   (concat
		     (if suffix nil "^")
		     "[ \t]*"
		     (regexp-quote
		       ;; Get non-whitespace separated fill-str-prev
		       (substring
			 fill-str-prev
			 (or (string-match "[^ \t]" fill-str-prev) 0)
			 (if (string-match
			       "[ \t]*\\(.*[^ \t]\\)[ \t]*$" fill-str-prev)
			     (match-end 1))))
		       "[ \t]*"
		       (if suffix "$"))
		   fill-str nil t nil))))))

(defun c-comment-make-prefix-only ()
  "Make multiply-delimited C comments in region singly delimited.
Converts comments of form:
/*  <text>  */      to    /*
/*  <text>  */             * <text>
                           * <text>
                           */"
  (interactive)
  (replace-fill-str "/*" " * ")
  (replace-fill-str "*/" "" t)
  (save-excursion
    (goto-char (min (point) (mark)))
    (insert "/*\n"))
  (save-excursion
    (goto-char (max (point) (mark)))
    (insert (if (bolp) " */\n" "\n */"))))

(provide 'par-align)

===============================================

Again, thanks to all who sent help.

				-- Ed Berard
				   Phone: (301) 353-9652
				   FAX  : (301) 353-9272