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.