[gnu.gdb.bug] nroff-mode bug and proposed solution

andyo@sargasso.masscomp.com (04/20/89)

Real Name: Andrew Oram
Digital Communications Route: andyo@westford.ccur.com.{harvard,uunet,petsd}
				{harvard,uunet,petsd}!masscomp!andyo
Analog Communications Route: (508) 392-2865
Date: Wed, 19 Apr 89 11:50:39 -0400
From: andyo@sargasso

I would like to report a bug that occurs when indenting for a comment
in nroff mode.  It is easy and justifiable to dismiss nroff/troff as
an archaic tool unworthy of serious support effort, but what the hell,
I have to use it every day.  So do a lot of prominent people, judging
from articles I find in computer journals about troff.

This message lists the PROBLEM, CAUSE, proposed SOLUTION, and
JUSTIFICATION.

I would really appreciate a response.  First, masscomp-now-absorbed-into- 
but-undigested-by-concurrent is not connected to the Internet, so I'd
like to be sure my message got to you at all!  Second, I'm pretty new
to all these Emacs tools and have a very rudimentary concept of
software reliability (I'm a tech writer) so I'd like to know how my
solution rates.

PROBLEM (user-view):

In nroff mode, when I invoke indent-for-comment on a blank line, the
function inserts a space followed by the comment-start string.
However, a full-line comment in nroff/troff has to start with a
period.  A line that starts with spaces or tabs, followed by the
comment-start string, is interpreted by nroff as a blank line.  So I
have to correct the improperly created comment by hand, or end up
finding various undesired effects like line breaks or new paragraphs
where I want filled text.  The problem is particularly bad when
auto-fill is on, because do-auto-fill creates these blank lines
casually during routine text entry.


CAUSE

The blame should lie ultimately on nroff itself, which requires one
string to start end-of-line comments and a different string to start
full-line comments.  In Emacs, nroff is the only major mode to make
this distinction.  The functions in simple.el, indent.el, and fill.el
make no provision for such caprice.

Within Emacs, nroff-mode's comment-indent-hook function (which is
nroff-comment-indent) tries to compensate for the need to handle
different comment strings by inserting a period when the point is at
the beginning of the line.  This violates the mandate of
comment-indent-hook, which is to return a column position without side
effects.

In fact, this little infraction by nroff-comment-indent doesn't work
anyway, because the indent-for-comment function that calls it
deliberately deletes any text added by it.  (This part of
indent-for-comment doesn't have any effect on other modes as far as I
can tell, and thus appears to be included just to thwart
nroff-comment-indent).

SOLUTION

I suggest rewriting both indent-for-comment and nroff-comment-indent.
Three steps will fix the bug and provide a few other neat features as
well:

	
1. In the file nroff-mode.el, replace nroff-comment-indent with the
   following defun.  The old version works adequately, but does not
   perform the useful feature that comment-indent-hook functions offer
   in other modes:  that of indenting new comments under previous
   comments.

(defun nroff-comment-indent ()
  "Compute indent for an nroff/troff comment.
Puts a full-stop before comments on a line by themselves.
If new line is blank and previous line contained a non-empty comment,
the new line is indented to same column."
  (let ((pt (point)))
    (unwind-protect
	(progn
	  (skip-chars-backward " \t")
	  (if (bolp)
	      (progn
		(insert ?.)
		(setq pt (1+ pt))
		(save-excursion
		  (if
		      (progn
			(forward-line -1)
			(and
			 (not (looking-at nroff-empty-comment))
			 (re-search-forward
			  comment-start
			  (save-excursion
			    (end-of-line)
			    (point)) t)))
		      (1-
		       (progn
			 (goto-char
			  (match-beginning 0))
			 (current-column)))
		    1)))
	    (if (save-excursion
		  (backward-char 1)
		  (looking-at "^\\."))
		1
	      (max comment-column
		   (* 8 (/ (+ (current-column)
			      9) 8)))))) ; add 9 to ensure at least two blanks
      (goto-char pt))))


2. Also in the file nroff-mode.el, add the following definitions.  The
   variables could be made local to nroff-comment-indent, but I have
   found them enough useful for other purposes to make them global.

(defconst nroff-comment-line-start-skip  "^\\.[ \t]*\\\\\"[ \t*]*"
  "String that starts a line containing a full-line comment."
    )
(defconst nroff-empty-comment  (concat nroff-comment-line-start-skip "$")
  "String corresponding to a line that is marked as a comment but
contains no text." 
    )


3. In the file simple.el, replace indent-for-comment with the
   following defun:


(defun indent-for-comment ()
  "Indent this line's comment to comment column, or insert an empty comment."
  (interactive "*")
  (beginning-of-line 1)
  (if (null comment-start)
      (error "No comment syntax defined")
    (let* ((eolpos (save-excursion (end-of-line) (point)))
	   cpos indent)
      (if (re-search-forward comment-start-skip eolpos 'move)
	  (progn (setq cpos (point-marker))
		 (goto-char (match-beginning 0))))
      (setq indent (funcall comment-indent-hook))
      (skip-chars-backward " \t")
      (indent-to indent)
      (if cpos 
	  (progn (goto-char cpos)
		 (set-marker cpos nil))
	(insert comment-start)
	(save-excursion
	  (insert comment-end))))))


JUSTIFICATION

As I said under CAUSE, the part of indent-for-comment that deletes
text seems to exist just to undo side-effects of comment-indent-hook.
If my analysis is right, it really should be up to comment-indent-hook
to ensure that no text is added as a side effect.  To deal with a
tricky outside-the-pale syntax such as nroff comments,
comment-indent-hook should be free to insert text and update the
point.

The only other ways I can think of to handle the bug are:


	1. Ignore the general problem with indent-for-comment and
	   nroff-indent-comment, since they occur most often with
	   auto-fill.  Instead, manipulate other parts of the
	   indent-new-comment-line function to do what we want during
	   auto-fill.  Either use the fill-prefix feature or write a
	   assign an nroff-local function to the indent-according-to-mode
	   variable.

	   The only virtue of this solution is that it leaves all the
	   current defuns unchanged.  In all other ways it inelegant
	   and half-assed.

	2. Defeat the delete part of indent-for-comment by
	   incrementing its begpos variable within
	   nroff-indent-comment.

	   This is just too bizarre, though it's consistent with the
	   current sneaky pattern of interaction between these two
	   functions.