*** empty log message ***
[bpt/emacs.git] / lisp / newcomment.el
CommitLineData
83b96b22
SM
1;;; newcomment.el --- (un)comment regions of buffers
2
771c9b97 3;; Copyright (C) 1999-2000 Free Software Foundation Inc.
83b96b22 4
771c9b97
SM
5;; Author: FSF??
6;; Maintainer: Stefan Monnier <monnier@cs.yale.edu>
83b96b22
SM
7;; Keywords: comment uncomment
8;; Version: $Name: $
771c9b97 9;; Revision: $Id: newcomment.el,v 1.6 1999/12/08 00:19:51 monnier Exp $
83b96b22
SM
10
11;; This program is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2 of the License, or
14;; (at your option) any later version.
15;;
16;; This program is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20;;
21;; You should have received a copy of the GNU General Public License
22;; along with this program; if not, write to the Free Software
23;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25;;; Commentary:
26
771c9b97 27;; A replacement for simple.el's comment-related functions.
83b96b22
SM
28
29;;; Bugs:
30
2ab98065
SM
31;; - single-char nestable comment-start can only do the "\\s<+" stuff
32;; if the corresponding closing marker happens to be right.
771c9b97 33;; - comment-box in TeXinfo generate bogus comments @ccccc@
3e569d22
SM
34;; - the code assumes that bol is outside of any comment/string.
35;; - uncomment-region with a numeric argument can render multichar
36;; comment markers invalid.
771c9b97
SM
37;; - comment-box in C with a numeric argument generates wrong end-of-line
38;; continuation markers.
83b96b22
SM
39
40;;; Todo:
41
771c9b97
SM
42;; - try to align tail comments
43;; - check what c-comment-line-break-function has to say
3e569d22 44;; - spill auto-fill of comments onto the end of the next line
7a0a180a
SM
45;; - uncomment-region with a consp (for blocks) or somehow make the
46;; deletion of continuation markers less dangerous
2ab98065 47;; - drop block-comment-<foo> unless it's really used
3e569d22
SM
48;; - uncomment-region on a part of a comment
49;; - obey the numarg of uncomment-region for continuation markers
771c9b97
SM
50;; - support gnu-style "multi-line with space in continue"
51;; - document string-strip
52;; - better document comment-continue (and probably turn it into a
53;; simple string).
54;; - don't overwrite comment-padding.
55;; - better document comment-end-quote. Probably make it better
56;; self-sufficient so that other quoting can be used.
57;; - comment the padright/padleft.
58;; - somehow allow comment-dwim to use the region even if transient-mark-mode
59;; is not turned on.
60;; - document comment-region-internal
61;; - comment-quote-nested should quote both the start and end of comment.
83b96b22
SM
62
63;;; Code:
64
65(eval-when-compile (require 'cl))
66
3e569d22
SM
67(defgroup comment nil
68 "Indenting and filling of comments."
69 :prefix "comment-"
70 :group 'fill)
71
771c9b97
SM
72(defvar comment-use-syntax 'undecided
73 "Non-nil if syntax-tables can be used instead of regexps.
74Can also be `undecided' which means that a somewhat expensive test will
75be used to try to determine whether syntax-tables should be trusted
76to understand comments or not.")
2ab98065 77
83b96b22
SM
78(defcustom comment-column 32
79 "*Column to indent right-margin comments to.
80Setting this variable automatically makes it local to the current buffer.
81Each mode establishes a different default value for this variable; you
82can set the value for a particular mode using that mode's hook."
83 :type 'integer
3e569d22 84 :group 'comment)
83b96b22
SM
85(make-variable-buffer-local 'comment-column)
86
87(defcustom comment-start nil
88 "*String to insert to start a new comment, or nil if no comment syntax."
89 :type '(choice (const :tag "None" nil)
90 string)
3e569d22 91 :group 'comment)
83b96b22
SM
92
93(defcustom comment-start-skip nil
94 "*Regexp to match the start of a comment plus everything up to its body.
95If there are any \\(...\\) pairs, the comment delimiter text is held to begin
96at the place matched by the close of the first pair."
97 :type '(choice (const :tag "None" nil)
98 regexp)
3e569d22 99 :group 'comment)
771c9b97 100
3e569d22
SM
101(defvar comment-end-skip nil
102 "Regexp to match the end of a comment plus everything up to its body.")
83b96b22
SM
103
104(defcustom comment-end ""
105 "*String to insert to end a new comment.
106Should be an empty string if comments are terminated by end-of-line."
107 :type 'string
3e569d22 108 :group 'comment)
83b96b22
SM
109
110(defvar comment-indent-hook nil
111 "Obsolete variable for function to compute desired indentation for a comment.
112This function is called with no args with point at the beginning of
113the comment's starting delimiter.")
114
115(defvar comment-indent-function
771c9b97 116 (lambda () comment-column)
83b96b22
SM
117 "Function to compute desired indentation for a comment.
118This function is called with no args with point at the beginning of
119the comment's starting delimiter.")
120
3e569d22
SM
121(defvar block-comment-start nil)
122(defvar block-comment-end nil)
83b96b22 123
771c9b97
SM
124(defvar comment-quote-nested t
125 "Non-nil if nested comments should be quoted.
126This should be locally set by each major mode if needed.")
127
128(defvar comment-continue nil
2ab98065 129 "Pair of strings to insert for multiline comments.")
771c9b97 130(defvar comment-add 0
2ab98065
SM
131 "How many more chars should be inserted by default.")
132
771c9b97 133(defcustom comment-style 'plain
2ab98065
SM
134 "*Style to be used for inserting comments."
135 :group 'comment
136 :type '(choice (const plain)
771c9b97 137 (const indent)
2ab98065
SM
138 (const aligned)
139 (const multi-line)
771c9b97
SM
140 (const extra-line)
141 (const box)))
2ab98065 142(defconst comment-styles
771c9b97
SM
143 '((plain . (nil nil nil nil))
144 (indent . (nil nil nil t))
145 (aligned . (nil t nil t))
146 (multi-line . (t nil nil t))
147 (extra-line . (t nil t t))
148 (box . (t t t t)))
149 "Possible styles.
150(STYLE . (MULTI ALIGN EXTRA INDENT).")
2ab98065 151
3e569d22 152(defcustom comment-padding 1
2ab98065
SM
153 "Number of spaces `comment-region' puts between comment chars and text.
154Can also be a string instead.
155
156Extra spacing between the comment characters and the comment text
771c9b97 157makes the comment easier to read. Default is 1. nil means 0.")
2ab98065 158
3e569d22
SM
159(defcustom comment-multi-line nil
160 "*Non-nil means \\[indent-new-comment-line] should continue same comment
161on new line, with no new terminator or starter.
162This is obsolete because you might as well use \\[newline-and-indent]."
163 :type 'boolean
164 :group 'comment)
165
2ab98065
SM
166;;;;
167;;;; Helpers
168;;;;
169
170(defun comment-string-strip (str before after)
171 (string-match (concat "\\`" (if before "\\s-*")
172 "\\(.*?\\)" (if after "\\s-*")
173 "\\'") str)
174 (match-string 1 str))
175
176(defun comment-string-reverse (s)
771c9b97 177 (comment-string-strip (concat (nreverse (string-to-list s))) nil t))
2ab98065
SM
178
179(defun comment-normalize-vars (&optional noerror)
180 (if (not comment-start) (or noerror (error "No comment syntax is defined"))
181 ;; comment-use-syntax
771c9b97 182 (when (eq comment-use-syntax 'undecided)
2ab98065
SM
183 (set (make-local-variable 'comment-use-syntax)
184 (let ((st (syntax-table))
185 (cs comment-start)
186 (ce (if (string= "" comment-end) "\n" comment-end)))
771c9b97
SM
187 ;; Try to skip over a comment using forward-comment
188 ;; to see if the syntax tables properly recognize it.
2ab98065
SM
189 (with-temp-buffer
190 (set-syntax-table st)
191 (insert cs " hello " ce)
192 (goto-char (point-min))
193 (and (forward-comment 1) (eobp))))))
2ab98065
SM
194 ;; comment-padding
195 (when (integerp comment-padding)
196 (setq comment-padding (make-string comment-padding ? )))
197 ;; comment markers
198 ;;(setq comment-start (comment-string-strip comment-start t nil))
199 ;;(setq comment-end (comment-string-strip comment-end nil t))
200 ;; comment-continue
201 (unless (or (car comment-continue) (string= comment-end ""))
202 (set (make-local-variable 'comment-continue)
203 (cons (concat " " (substring comment-start 1))
204 nil)))
205 (when (and (car comment-continue) (null (cdr comment-continue)))
206 (setcdr comment-continue (comment-string-reverse (car comment-continue))))
207 ;; comment-skip regexps
208 (unless comment-start-skip
209 (set (make-local-variable 'comment-start-skip)
210 (concat "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(\\s<+\\|"
211 (regexp-quote (comment-string-strip comment-start t t))
212 "+\\)\\s-*")))
213 (unless comment-end-skip
214 (let ((ce (if (string= "" comment-end) "\n"
215 (comment-string-strip comment-end t t))))
216 (set (make-local-variable 'comment-end-skip)
771c9b97 217 (concat "\\s-*\\(\\s>" (if comment-quote-nested "" "+")
2ab98065 218 "\\|" (regexp-quote (substring ce 0 1))
771c9b97 219 (if (and comment-quote-nested (<= (length ce) 1)) "" "+")
2ab98065
SM
220 (regexp-quote (substring ce 1))
221 "\\)"))))))
222
223(defmacro until (&rest body)
224 (let ((retsym (make-symbol "ret")))
225 `(let (,retsym)
226 (while (not (setq ,retsym (progn ,@body))))
227 ,retsym)))
228(def-edebug-spec until t)
229
230(defun comment-end-quote-re (str &optional re)
231 "Make a regexp that matches the (potentially quoted) STR comment-end.
232The regexp has one group in it which matches RE right after the
233potential quoting."
234 (setq str (comment-string-strip str t t))
771c9b97 235 (when (and comment-quote-nested (> (length str) 1))
2ab98065
SM
236 (concat (regexp-quote (substring str 0 1))
237 "\\\\*\\(" re "\\)"
238 (regexp-quote (substring str 1)))))
239
240;;;;
241;;;; Navigation
242;;;;
243
244(defun comment-search-forward (&optional limit noerror)
771c9b97
SM
245 "Find a comment start between point and LIMIT.
246Moves point to inside the comment and returns the position of the
247comment-starter. If no comment is found, moves point to LIMIT
e8fe7d39 248and raises an error or returns nil of NOERROR is non-nil."
2ab98065
SM
249 (if (not comment-use-syntax)
250 (when (re-search-forward comment-start-skip limit noerror)
251 (or (match-end 1) (match-beginning 0)))
252 (let ((s (parse-partial-sexp (point) (or limit (point-max)) nil nil nil t)))
253 (if (and (nth 8 s) (not (nth 3 s)))
254 (let ((pt (point))
255 (start (nth 8 s))
256 (bol (save-excursion (beginning-of-line) (point)))
257 (end nil))
258 (while (and (null end) (>= (point) bol))
259 (if (looking-at comment-start-skip)
771c9b97 260 (setq end (min (or limit (point-max)) (match-end 0)))
2ab98065
SM
261 (backward-char)))
262 (goto-char end)
263 start)
264 (unless noerror (error "No comment"))))))
265
266(defun comment-search-backward (&optional limit noerror)
267 "Find a comment start between LIMIT and point.
771c9b97
SM
268Moves point to inside the comment and returns the position of the
269comment-starter. If no comment is found, moves point to LIMIT
2ab98065
SM
270and raises an error or returns nil of NOERROR is non-nil."
271 (if (not (re-search-backward comment-start-skip limit t))
272 (unless noerror (error "No comment"))
273 (beginning-of-line)
274 (let* ((end (match-end 0))
275 (cs (comment-search-forward end t))
276 (pt (point)))
277 (if (not cs)
278 (progn (beginning-of-line)
279 (comment-search-backward limit noerror))
280 (while (progn (goto-char cs)
281 (comment-forward)
282 (and (< (point) end)
283 (setq cs (comment-search-forward end t))))
284 (setq pt (point)))
285 (goto-char pt)
286 cs))))
287
288(defun comment-beginning ()
771c9b97
SM
289 "Find the beginning of the enclosing comment.
290Returns nil if not inside a comment, else moves point and returns
2ab98065
SM
291the same as `comment-search-forward'."
292 (let ((pt (point))
293 (cs (comment-search-backward nil t)))
294 (save-excursion
295 (and cs
296 (progn (goto-char cs) (forward-comment 1) (> (point) pt))
297 cs))))
298
299(defun comment-forward (&optional n)
300 "Skip forward over N comments.
301Just like `forward-comment' but only for positive N
302and can use regexps instead of syntax."
303 (setq n (or n 1))
304 (if (< n 0) (error "No comment-backward")
305 (if comment-use-syntax (forward-comment n)
306 (while (> n 0)
307 (skip-syntax-forward " ")
308 (if (and (looking-at comment-start-skip)
309 (re-search-forward comment-end-skip nil 'move))
310 (decf n)
311 (setq n -1)))
312 (= n 0))))
313
314(defun comment-enter-backward ()
315 "Move from the end of a comment to the end of its content.
771c9b97 316Point is assumed to be just at the end of a comment."
2ab98065
SM
317 (if (bolp)
318 ;; comment-end = ""
319 (progn (backward-char) (skip-syntax-backward " "))
320 (let ((end (point)))
321 (beginning-of-line)
322 (save-restriction
323 (narrow-to-region (point) end)
324 (re-search-forward (concat comment-end-skip "\\'"))
325 (goto-char (match-beginning 0))))))
326
327;;;;
328;;;; Commands
329;;;;
330
3e569d22
SM
331(defalias 'indent-for-comment 'comment-indent)
332(defun comment-indent (&optional continue)
2ab98065
SM
333 "Indent this line's comment to comment column, or insert an empty comment.
334If CONTINUE is non-nil, use the `comment-continuation' markers if any."
83b96b22
SM
335 (interactive "*")
336 (let* ((empty (save-excursion (beginning-of-line)
337 (looking-at "[ \t]*$")))
2ab98065
SM
338 (starter (or (and continue (car comment-continue))
339 (and empty block-comment-start) comment-start))
340 (ender (or (and continue (car comment-continue) "")
341 (and empty block-comment-end) comment-end)))
83b96b22
SM
342 (cond
343 ((null starter)
344 (error "No comment syntax defined"))
83b96b22
SM
345 (t (let* ((eolpos (save-excursion (end-of-line) (point)))
346 cpos indent begpos)
347 (beginning-of-line)
2ab98065 348 (when (setq begpos (comment-search-forward eolpos t))
e8fe7d39
SM
349 (setq cpos (point-marker))
350 (goto-char begpos))
351 (setq begpos (point))
83b96b22
SM
352 ;; Compute desired indent.
353 (if (= (current-column)
354 (setq indent (if comment-indent-hook
355 (funcall comment-indent-hook)
356 (funcall comment-indent-function))))
357 (goto-char begpos)
358 ;; If that's different from current, change it.
359 (skip-chars-backward " \t")
360 (delete-region (point) begpos)
361 (indent-to indent))
362 ;; An existing comment?
363 (if cpos
364 (progn (goto-char cpos)
365 (set-marker cpos nil))
366 ;; No, insert one.
367 (insert starter)
368 (save-excursion
369 (insert ender))))))))
370
3e569d22
SM
371(defalias 'set-comment-column 'comment-set-column)
372(defun comment-set-column (arg)
83b96b22 373 "Set the comment column based on point.
2ab98065 374With no ARG, set the comment column to the current column.
83b96b22
SM
375With just minus as arg, kill any comment on this line.
376With any other arg, set comment column to indentation of the previous comment
377 and then align or create a comment on this line at that column."
378 (interactive "P")
e8fe7d39
SM
379 (cond
380 ((eq arg '-) (kill-comment nil))
381 (arg
382 (save-excursion
383 (beginning-of-line)
2ab98065 384 (comment-search-backward)
e8fe7d39 385 (beginning-of-line)
2ab98065 386 (goto-char (comment-search-forward))
83b96b22 387 (setq comment-column (current-column))
e8fe7d39
SM
388 (message "Comment column set to %d" comment-column))
389 (indent-for-comment))
390 (t (setq comment-column (current-column))
83b96b22
SM
391 (message "Comment column set to %d" comment-column))))
392
3e569d22
SM
393(defalias 'kill-comment 'comment-kill)
394(defun comment-kill (arg)
7a0a180a
SM
395 "Kill the comment on this line, if any.
396With prefix ARG, kill comments on that many lines starting with this one."
397 (interactive "P")
398 (let (endc)
399 (dotimes (_ (prefix-numeric-value arg))
400 (save-excursion
401 (end-of-line)
402 (setq endc (point))
403 (beginning-of-line)
2ab98065 404 (let ((cs (comment-search-forward endc t)))
7a0a180a
SM
405 (when cs
406 (goto-char cs)
407 (skip-syntax-backward " ")
408 (setq cs (point))
2ab98065 409 (comment-forward)
7a0a180a
SM
410 (kill-region cs (if (bolp) (1- (point)) (point)))
411 (indent-according-to-mode))))
412 (if arg (forward-line 1)))))
413
2ab98065
SM
414(defun comment-padright (str &optional n)
415 "Construct a string composed of STR plus `comment-padding'.
416It contains N copies of the last non-whitespace chars of STR.
417If STR already contains padding, the corresponding amount is
418 ignored from `comment-padding'.
419N defaults to 1.
771c9b97 420If N is `re', a regexp is returned instead, that would match
2ab98065
SM
421 the string for any N."
422 (setq n (or n 0))
423 (when (and (stringp str) (not (string= "" str)))
3e569d22
SM
424 (string-match "\\`\\s-*\\(.*?\\)\\s-*\\'" str)
425 (let ((s (match-string 1 str))
426 (lpad (substring str 0 (match-beginning 1)))
427 (rpad (concat (substring str (match-end 1))
428 (substring comment-padding
429 (min (- (match-end 0) (match-end 1))
430 (length comment-padding))))))
2ab98065 431 (if (symbolp n)
3e569d22
SM
432 (concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
433 lpad "")
434 (regexp-quote s) "+"
2ab98065 435 (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
3e569d22
SM
436 rpad ""))
437 (concat lpad s (make-string n (aref str (1- (match-end 1)))) rpad)))))
2ab98065
SM
438
439(defun comment-padleft (str &optional n)
440 "Construct a string composed of `comment-padding' plus STR.
441It contains N copies of the last non-whitespace chars of STR.
442If STR already contains padding, the corresponding amount is
443 ignored from `comment-padding'.
444N defaults to 1.
771c9b97 445If N is `re', a regexp is returned instead, that would match
2ab98065
SM
446 the string for any N."
447 (setq n (or n 0))
448 (when (and (stringp str) (not (string= "" str)))
449 (string-match "\\`\\s-*" str)
450 (let ((s (substring str (match-end 0)))
451 (pad (concat (substring comment-padding
452 (min (- (match-end 0) (match-beginning 0))
453 (length comment-padding)))
454 (match-string 0 str)))
455 (c (aref str (match-end 0)))
771c9b97 456 (multi (or (not comment-quote-nested) (string= comment-end "")
2ab98065
SM
457 (> (length str) (1+ (match-end 0))))))
458 (if (symbolp n)
459 (concat "\\s-*"
460 (if multi (concat (regexp-quote (string c)) "*"))
461 (regexp-quote s))
462 (concat pad (when multi (make-string n c)) s)))))
83b96b22 463
7a0a180a
SM
464(defun uncomment-region (beg end &optional arg)
465 "Uncomment each line in the BEG..END region.
466ARG is currently ignored."
83b96b22
SM
467 (interactive "*r\nP")
468 (comment-normalize-vars)
469 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
470 (save-excursion
7a0a180a
SM
471 (goto-char beg)
472 (unless (markerp end) (setq end (copy-marker end)))
473 (let ((numarg (prefix-numeric-value arg))
2ab98065 474 spt)
7a0a180a 475 (while (and (< (point) end)
2ab98065
SM
476 (setq spt (comment-search-forward end t)))
477 (let* ((ipt (point))
e8fe7d39
SM
478 ;; find the end of the comment
479 (ept (progn
2ab98065
SM
480 (goto-char spt)
481 (unless (comment-forward)
e8fe7d39
SM
482 (error "Can't find the comment end"))
483 (point-marker)))
2ab98065
SM
484 (block nil)
485 (end-quote-re (comment-end-quote-re comment-end "\\\\"))
486 (ccs (car comment-continue))
487 (srei (comment-padright ccs 're))
3e569d22 488 (sre (and srei (concat "^\\s-*?\\(" srei "\\)"))))
e8fe7d39
SM
489 (save-restriction
490 (narrow-to-region spt ept)
e8fe7d39 491 ;; remove the comment-start
2ab98065
SM
492 (goto-char ipt)
493 (skip-syntax-backward " ")
494 (when (> (- (point) (point-min) (length comment-start)) 7)
495 (setq block t))
496 (when (looking-at (regexp-quote comment-padding))
497 (goto-char (match-end 0)))
498 (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei)))
499 (goto-char (match-end 0)))
3e569d22
SM
500 (if (null arg) (delete-region (point-min) (point))
501 (skip-syntax-backward " ")
502 (delete-char (- numarg)))
2ab98065
SM
503
504 ;; remove the end-comment (and leading padding and such)
505 (goto-char (point-max)) (comment-enter-backward)
506 (unless (string-match "\\`\\(\n\\|\\s-\\)*\\'"
507 (buffer-substring (point) ept))
508 (when (and (bolp) (not (bobp))) (backward-char))
3e569d22
SM
509 (if (null arg) (delete-region (point) ept)
510 (skip-syntax-forward " ")
511 (delete-char numarg)))
e8fe7d39
SM
512
513 ;; unquote any nested end-comment
514 (when end-quote-re
7a0a180a 515 (goto-char (point-min))
e8fe7d39
SM
516 (while (re-search-forward end-quote-re nil t)
517 (delete-region (match-beginning 1) (match-end 1))))
518
519 ;; eliminate continuation markers as well
2ab98065
SM
520 (let* ((cce (or (cdr comment-continue)
521 (comment-string-reverse comment-start)))
522 (erei (and block (comment-padleft cce 're)))
523 (ere (and erei (concat "\\(" erei "\\)\\s-*$")))
524 (re (if (and sre ere) (concat sre "\\|" ere) (or sre ere))))
e8fe7d39 525 (when re
7a0a180a 526 (goto-char (point-min))
2ab98065
SM
527 ;; there can't be a real SRE on the first line.
528 (when (and sre (looking-at sre)) (goto-char (match-end 0)))
e8fe7d39 529 (while (re-search-forward re nil t)
2ab98065
SM
530 (replace-match "" t t nil (if (match-end 2) 2 1)))))
531 ;; go the the end for the next comment
e8fe7d39 532 (goto-char (point-max))))))))
83b96b22 533
aac88001
SM
534(defun comment-make-extra-lines (cs ce ccs cce min-indent max-indent &optional block)
535 (if block
536 (let* ((s (concat cs "a=m" cce "\n"
537 (make-string min-indent ? ) ccs))
538 (e (concat cce "\n" (make-string min-indent ? )
539 ccs "a=m" ce))
540 (_ (assert (string-match "\\s-*\\(a=m\\)\\s-*" s)))
541 (fill (make-string (+ (- max-indent
542 min-indent
543 (match-beginning 0))
544 (- (match-end 0)
545 (match-end 1)))
546 (aref s (match-end 0)))))
547 (setq cs (replace-match fill t t s))
548 (assert (string-match "\\s-*\\(a=m\\)\\s-*" e))
549 (setq ce (replace-match fill t t e)))
550 (when (and ce (string-match "\\`\\s-*\\(.*\\S-\\)\\s-*\\'" ce))
551 (setq ce (match-string 1 ce)))
552 (let* ((c (concat ce "a=m" cs))
553 (indent (if (string-match "\\(.+\\).*a=m\\(.*\\)\\1" c)
554 (max (+ min-indent
555 (- (match-end 2) (match-beginning 2))
556 (- (match-beginning 0)))
557 0)
558 min-indent)))
559 (setq ce (concat cce "\n" (make-string indent ? ) (or ce cs)))
560 (setq cs (concat cs "\n" (make-string min-indent ? ) ccs))))
771c9b97 561 (cons cs ce))
aac88001
SM
562
563(def-edebug-spec comment-with-narrowing t)
564(put 'comment-with-narrowing 'lisp-indent-function 2)
565(defmacro comment-with-narrowing (beg end &rest body)
566 "Execute BODY with BEG..END narrowing.
567Space is added (and then removed) at the beginning for the text's
568indentation to be kept as it was before narrowing."
569 `(let ((-bindent (save-excursion (goto-char beg) (current-column))))
570 (save-restriction
571 (narrow-to-region beg end)
572 (goto-char (point-min))
573 (insert (make-string -bindent ? ))
574 (prog1
575 (progn ,@body)
576 ;; remove the -bindent
577 (save-excursion
578 (goto-char (point-min))
579 (when (looking-at " *")
580 (let ((n (min (- (match-end 0) (match-beginning 0)) -bindent)))
581 (delete-char n)
582 (decf -bindent n)))
583 (end-of-line)
584 (let ((e (point)))
585 (beginning-of-line)
586 (while (and (> -bindent 0) (re-search-forward " +" e t))
587 (let ((n (min -bindent (- (match-end 0) (match-beginning 0) 1))))
588 (goto-char (match-beginning 0))
589 (delete-char n)
590 (decf -bindent n)))))))))
591
771c9b97
SM
592(defun comment-region-internal (beg end cs ce
593 &optional ccs cce block lines indent)
83b96b22
SM
594 (assert (< beg end))
595 (let ((no-empty t))
596 ;; sanitize ce and cce
597 (if (and (stringp ce) (string= "" ce)) (setq ce nil))
598 (if (and (stringp cce) (string= "" cce)) (setq cce nil))
599 ;; should we mark empty lines as well ?
600 (if (or ccs block lines) (setq no-empty nil))
7a0a180a 601 ;; make sure we have end-markers for BLOCK mode
2ab98065 602 (when block (unless ce (setq ce (comment-string-reverse cs))))
83b96b22
SM
603 ;; continuation defaults to the same
604 (if ccs (unless block (setq cce nil))
605 (setq ccs cs cce ce))
7a0a180a 606
83b96b22 607 (save-excursion
aac88001
SM
608 (goto-char end)
609 (unless (or ce (eolp)) (insert "\n") (indent-according-to-mode))
610 (comment-with-narrowing beg end
7a0a180a 611 (let ((ce-quote-re (comment-end-quote-re comment-end))
83b96b22
SM
612 (min-indent (point-max))
613 (max-indent 0))
614 (goto-char (point-min))
615 ;; loop over all lines to find the needed indentations
616 (until
617 (unless (looking-at "[ \t]*$")
618 (setq min-indent (min min-indent (current-indentation))))
619 (when ce-quote-re
771c9b97 620 (let ((eol (line-end-position)))
83b96b22
SM
621 (while (re-search-forward ce-quote-re eol 'move)
622 (incf eol)
623 (replace-match "\\" t t nil 1))))
624 (end-of-line)
625 (setq max-indent (max max-indent (current-column)))
626 (or (eobp) (progn (forward-line) nil)))
627
628 ;; inserting ccs can change max-indent by (1- tab-width)
629 (incf max-indent (+ (max (length cs) (length ccs)) -1 tab-width))
771c9b97 630 (unless indent (setq min-indent 0))
83b96b22 631
aac88001 632 ;; make the leading and trailing lines if requested
83b96b22 633 (when lines
771c9b97
SM
634 (let ((csce
635 (comment-make-extra-lines
636 cs ce ccs cce min-indent max-indent block)))
637 (setq cs (car csce))
638 (setq ce (cdr csce))))
83b96b22
SM
639
640 (goto-char (point-min))
641 ;; Loop over all lines from BEG to END.
642 (until
643 (unless (and no-empty (looking-at "[ \t]*$"))
644 (move-to-column min-indent t)
645 (insert cs) (setq cs ccs)
646 (end-of-line)
647 (if (eobp) (setq cce ce))
648 (when cce
649 (when block (move-to-column max-indent t))
650 (insert cce)))
651 (end-of-line)
652 (or (eobp) (progn (forward-line) nil))))))))
653
83b96b22
SM
654(defun comment-region (beg end &optional arg)
655 "Comment or uncomment each line in the region.
656With just \\[universal-prefix] prefix arg, uncomment each line in region BEG..END.
657Numeric prefix arg ARG means use ARG comment characters.
658If ARG is negative, delete that many comment characters instead.
659Comments are terminated on each line, even for syntax in which newline does
660not end the comment. Blank lines do not get comments.
661
662The strings used as comment starts are built from
663`comment-start' without trailing spaces and `comment-padding'."
664 (interactive "*r\nP")
665 (comment-normalize-vars)
666 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
2ab98065 667 (let* ((numarg (prefix-numeric-value arg))
771c9b97 668 (add comment-add)
2ab98065
SM
669 (style (cdr (assoc comment-style comment-styles)))
670 (lines (nth 2 style))
671 (block (nth 1 style))
672 (multi (nth 0 style)))
83b96b22
SM
673 (save-excursion
674 ;; we use `chars' instead of `syntax' because `\n' might be
675 ;; of end-comment syntax rather than of whitespace syntax.
676 ;; sanitize BEG and END
677 (goto-char beg) (skip-chars-forward " \t\n\r") (beginning-of-line)
678 (setq beg (max beg (point)))
679 (goto-char end) (skip-chars-backward " \t\n\r") (end-of-line)
680 (setq end (min end (point)))
681 (if (>= beg end) (error "Nothing to comment"))
682
83b96b22
SM
683 ;; sanitize LINES
684 (setq lines
685 (and
2ab98065 686 lines multi
83b96b22
SM
687 (progn (goto-char beg) (beginning-of-line)
688 (skip-syntax-forward " ")
689 (>= (point) beg))
690 (progn (goto-char end) (end-of-line) (skip-syntax-backward " ")
691 (<= (point) end))
771c9b97 692 (or (not (string= "" comment-end)) block)
2ab98065 693 (progn (goto-char beg) (search-forward "\n" end t)))))
83b96b22 694
2ab98065
SM
695 ;; don't add end-markers just because the user asked for `block'
696 (unless (or lines (string= "" comment-end)) (setq block nil))
697
83b96b22
SM
698 (cond
699 ((consp arg) (uncomment-region beg end))
700 ((< numarg 0) (uncomment-region beg end (- numarg)))
701 (t
702 (if (and (null arg) (= (length comment-start) 1))
703 (setq numarg add) (decf numarg))
704 (comment-region-internal
705 beg end
3e569d22
SM
706 (let ((s (comment-padright comment-start numarg)))
707 (if (string-match comment-start-skip s) s
708 (comment-padright comment-start)))
709 (let ((s (comment-padleft comment-end numarg)))
710 (and s (if (string-match comment-end-skip s) s
711 (comment-padright comment-end))))
2ab98065
SM
712 (if multi (comment-padright (car comment-continue) numarg))
713 (if multi (comment-padleft (cdr comment-continue) numarg))
83b96b22 714 block
771c9b97
SM
715 lines
716 (nth 3 style))))))
717
718(defun comment-box (beg end &optional arg)
719 "Comment out the BEG..END region, putting it inside a box.
720The numeric prefix ARG specifies how many characters to add to begin- and
721end- comment markers additionally to what `comment-add' already specifies."
722 (interactive "*r\np")
723 (let ((comment-style 'box))
724 (comment-region beg end (+ comment-add arg))))
83b96b22 725
2ab98065
SM
726(defun comment-dwim (arg)
727 "Call the comment command you want.
771c9b97
SM
728If the region is active and `transient-mark-mode' is on,
729calls `comment-region' (unless it only consists
2ab98065
SM
730in comments, in which case it calls `uncomment-region').
731Else, if the current line is empty, insert a comment and indent it.
732Else call `indent-for-comment' or `kill-comment' if a prefix ARG is specified."
733 (interactive "*P")
734 (comment-normalize-vars)
771c9b97 735 (if (and mark-active transient-mark-mode)
2ab98065
SM
736 (let ((beg (min (point) (mark)))
737 (end (max (point) (mark))))
738 (if (save-excursion ;; check for already commented region
739 (goto-char beg)
740 (comment-forward (point-max))
741 (<= end (point)))
742 (uncomment-region beg end arg)
743 (comment-region beg end arg)))
744 (if (save-excursion (beginning-of-line) (not (looking-at "\\s-*$")))
745 (if arg (kill-comment (and (integerp arg) arg)) (indent-for-comment))
746 (let ((add (if arg (prefix-numeric-value arg)
771c9b97 747 (if (= (length comment-start) 1) comment-add 0))))
2ab98065
SM
748 (insert (comment-padright comment-start add))
749 (save-excursion
750 (unless (string= "" comment-end)
751 (insert (comment-padleft comment-end add)))
752 (indent-according-to-mode))))))
753
3e569d22
SM
754(defcustom comment-spill t
755 "")
2ab98065
SM
756
757(defun indent-new-comment-line (&optional soft)
758 "Break line at point and indent, continuing comment if within one.
759This indents the body of the continued comment
760under the previous comment line.
761
762This command is intended for styles where you write a comment per line,
763starting a new comment (and terminating it if necessary) on each line.
764If you want to continue one comment across several lines, use \\[newline-and-indent].
765
766If a fill column is specified, it overrides the use of the comment column
767or comment indentation.
768
769The inserted newline is marked hard if variable `use-hard-newlines' is true,
770unless optional argument SOFT is non-nil."
771 (interactive)
772 (comment-normalize-vars t)
773 (let (comcol comstart)
774 (skip-chars-backward " \t")
775 (delete-region (point)
776 (progn (skip-chars-forward " \t")
777 (point)))
778 (if soft (insert-and-inherit ?\n) (newline 1))
779 (if fill-prefix
780 (progn
781 (indent-to-left-margin)
782 (insert-and-inherit fill-prefix))
783 (unless comment-multi-line
784 (save-excursion
785 (backward-char)
786 (if (and comment-start
787 (setq comcol (comment-beginning)))
788 ;; The old line has a comment and point was inside the comment.
789 ;; Set WIN to the pos of the comment-start.
790
791 ;; If comment-start-skip contains a \(...\) pair,
792 ;; the real comment delimiter starts at the end of that pair.
793 (let ((win comcol))
794 ;; But if the comment is empty, look at preceding lines
795 ;; to find one that has a nonempty comment.
796 ;; (while (and (eolp) (not (bobp))
797 ;; (let (opoint)
798 ;; (beginning-of-line)
799 ;; (setq opoint (point))
800 ;; (forward-line -1)
801 ;; (setq win (comment-search-forward opoint t)))))
802 ;; Why do we do that ? -sm
803
804 ;; Indent this line like what we found.
805 (setq comstart (buffer-substring win (point)))
806 (goto-char win)
807 (setq comcol (current-column))
808 ))))
809 (if comcol
810 (let ((comment-column comcol)
811 (comment-start comstart))
812 ;;(if (not (eolp)) (setq comment-end ""))
813 (insert-and-inherit ?\n)
814 (forward-char -1)
815 (indent-for-comment (cadr (assoc comment-style comment-styles)))
816 (save-excursion
817 (let ((pt (point)))
818 (end-of-line)
819 (let ((comend (buffer-substring pt (point))))
820 ;; The 1+ is to make sure we delete the \n inserted above.
821 (delete-region pt (1+ (point)))
822 (beginning-of-line)
823 (backward-char)
824 (insert comend)
825 (forward-char)))))
826 (indent-according-to-mode)))))
827
83b96b22
SM
828(provide 'newcomment)
829
830;;; Change Log:
aac88001 831;; $Log: newcomment.el,v $
771c9b97
SM
832;; Revision 1.6 1999/12/08 00:19:51 monnier
833;; various fixes and gratuitous movements.
834;;
3e569d22
SM
835;; Revision 1.5 1999/11/30 16:20:55 monnier
836;; (comment-style(s)): Replaces comment-extra-lines (and comment-multi-line).
837;; (comment-use-syntax): Whether to use the syntax-table or just the regexps.
838;; (comment-end-skip): To find the end of the text.
839;; ...
840;;
2ab98065
SM
841;; Revision 1.4 1999/11/29 01:31:47 monnier
842;; (comment-find): New function.
843;; (indent-for-comment, set-comment-column, kill-comment): use it.
844;;
e8fe7d39
SM
845;; Revision 1.3 1999/11/29 00:49:18 monnier
846;; (kill-comment): Fixed by rewriting it with syntax-tables rather than regexps
847;; (comment-normalize-vars): Set default (cdr comment-continue)
848;; (comment-end-quote-re): new function taken out of `comment-region-internal'
849;; (uncomment-region): Rewritten using syntax-tables. Also unquotes
850;; nested comment-ends and eliminates continuation markers.
851;; (comment-region-internal): Don't create a default for cce.
852;; Use `comment-end-quote-re'.
853;;
7a0a180a
SM
854;; Revision 1.2 1999/11/28 21:33:55 monnier
855;; (comment-make-extra-lines): Moved out of comment-region-internal.
856;; (comment-with-narrowing): New macro. Provides a way to preserve
857;; indentation inside narrowing.
858;; (comment-region-internal): Add "\n" to close the comment if necessary.
859;; Correctly handle commenting-out when BEG is not bolp.
860;;
aac88001
SM
861;; Revision 1.1 1999/11/28 18:51:06 monnier
862;; First "working" version:
863;; - uncomment-region doesn't work for some unknown reason
864;; - comment-multi-line allows the use of multi line comments
865;; - comment-extra-lines allows yet another style choice
866;; - comment-add allows to default to `;;'
867;; - comment-region on a comment calls uncomment-region
868;; - C-u C-u comment-region aligns comment end markers
869;; - C-u C-u C-u comment-region puts the comment inside a rectangle
3e569d22 870;; - comment-region will try to quote comment-end markers inside the region
aac88001
SM
871;; - comment-start markers are placed at the indentation level
872;;
83b96b22
SM
873
874;;; newcomment.el ends here