(comment-use-syntax): Change `maybe' to `undecided'.
[emacs.git] / lisp / newcomment.el
blob0507048ab4fb504c746e43922d0762776ded011b
1 ;;; newcomment.el --- (un)comment regions of buffers
3 ;; Copyright (C) 1999-2000 Free Software Foundation Inc.
5 ;; Author: FSF??
6 ;; Maintainer: Stefan Monnier <monnier@cs.yale.edu>
7 ;; Keywords: comment uncomment
8 ;; Version: $Name: $
9 ;; Revision: $Id: newcomment.el,v 1.6 1999/12/08 00:19:51 monnier Exp $
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.
25 ;;; Commentary:
27 ;; A replacement for simple.el's comment-related functions.
29 ;;; Bugs:
31 ;; - single-char nestable comment-start can only do the "\\s<+" stuff
32 ;; if the corresponding closing marker happens to be right.
33 ;; - comment-box in TeXinfo generate bogus comments @ccccc@
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.
37 ;; - comment-box in C with a numeric argument generates wrong end-of-line
38 ;; continuation markers.
40 ;;; Todo:
42 ;; - try to align tail comments
43 ;; - check what c-comment-line-break-function has to say
44 ;; - spill auto-fill of comments onto the end of the next line
45 ;; - uncomment-region with a consp (for blocks) or somehow make the
46 ;; deletion of continuation markers less dangerous
47 ;; - drop block-comment-<foo> unless it's really used
48 ;; - uncomment-region on a part of a comment
49 ;; - obey the numarg of uncomment-region for continuation markers
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.
63 ;;; Code:
65 (eval-when-compile (require 'cl))
67 (defgroup comment nil
68 "Indenting and filling of comments."
69 :prefix "comment-"
70 :group 'fill)
72 (defvar comment-use-syntax 'undecided
73 "Non-nil if syntax-tables can be used instead of regexps.
74 Can also be `undecided' which means that a somewhat expensive test will
75 be used to try to determine whether syntax-tables should be trusted
76 to understand comments or not.")
78 (defcustom comment-column 32
79 "*Column to indent right-margin comments to.
80 Setting this variable automatically makes it local to the current buffer.
81 Each mode establishes a different default value for this variable; you
82 can set the value for a particular mode using that mode's hook."
83 :type 'integer
84 :group 'comment)
85 (make-variable-buffer-local 'comment-column)
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)
91 :group 'comment)
93 (defcustom comment-start-skip nil
94 "*Regexp to match the start of a comment plus everything up to its body.
95 If there are any \\(...\\) pairs, the comment delimiter text is held to begin
96 at the place matched by the close of the first pair."
97 :type '(choice (const :tag "None" nil)
98 regexp)
99 :group 'comment)
101 (defvar comment-end-skip nil
102 "Regexp to match the end of a comment plus everything up to its body.")
104 (defcustom comment-end ""
105 "*String to insert to end a new comment.
106 Should be an empty string if comments are terminated by end-of-line."
107 :type 'string
108 :group 'comment)
110 (defvar comment-indent-hook nil
111 "Obsolete variable for function to compute desired indentation for a comment.
112 This function is called with no args with point at the beginning of
113 the comment's starting delimiter.")
115 (defvar comment-indent-function
116 (lambda () comment-column)
117 "Function to compute desired indentation for a comment.
118 This function is called with no args with point at the beginning of
119 the comment's starting delimiter.")
121 (defvar block-comment-start nil)
122 (defvar block-comment-end nil)
124 (defvar comment-quote-nested t
125 "Non-nil if nested comments should be quoted.
126 This should be locally set by each major mode if needed.")
128 (defvar comment-continue nil
129 "Pair of strings to insert for multiline comments.")
130 (defvar comment-add 0
131 "How many more chars should be inserted by default.")
133 (defcustom comment-style 'plain
134 "*Style to be used for inserting comments."
135 :group 'comment
136 :type '(choice (const plain)
137 (const indent)
138 (const aligned)
139 (const multi-line)
140 (const extra-line)
141 (const box)))
142 (defconst comment-styles
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).")
152 (defcustom comment-padding 1
153 "Number of spaces `comment-region' puts between comment chars and text.
154 Can also be a string instead.
156 Extra spacing between the comment characters and the comment text
157 makes the comment easier to read. Default is 1. nil means 0.")
159 (defcustom comment-multi-line nil
160 "*Non-nil means \\[indent-new-comment-line] should continue same comment
161 on new line, with no new terminator or starter.
162 This is obsolete because you might as well use \\[newline-and-indent]."
163 :type 'boolean
164 :group 'comment)
166 ;;;;
167 ;;;; Helpers
168 ;;;;
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))
176 (defun comment-string-reverse (s)
177 (comment-string-strip (concat (nreverse (string-to-list s))) nil t))
179 (defun comment-normalize-vars (&optional noerror)
180 (if (not comment-start) (or noerror (error "No comment syntax is defined"))
181 ;; comment-use-syntax
182 (when (eq comment-use-syntax 'undecided)
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)))
187 ;; Try to skip over a comment using forward-comment
188 ;; to see if the syntax tables properly recognize it.
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))))))
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)
217 (concat "\\s-*\\(\\s>" (if comment-quote-nested "" "+")
218 "\\|" (regexp-quote (substring ce 0 1))
219 (if (and comment-quote-nested (<= (length ce) 1)) "" "+")
220 (regexp-quote (substring ce 1))
221 "\\)"))))))
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)
230 (defun comment-end-quote-re (str &optional re)
231 "Make a regexp that matches the (potentially quoted) STR comment-end.
232 The regexp has one group in it which matches RE right after the
233 potential quoting."
234 (setq str (comment-string-strip str t t))
235 (when (and comment-quote-nested (> (length str) 1))
236 (concat (regexp-quote (substring str 0 1))
237 "\\\\*\\(" re "\\)"
238 (regexp-quote (substring str 1)))))
240 ;;;;
241 ;;;; Navigation
242 ;;;;
244 (defun comment-search-forward (&optional limit noerror)
245 "Find a comment start between point and LIMIT.
246 Moves point to inside the comment and returns the position of the
247 comment-starter. If no comment is found, moves point to LIMIT
248 and raises an error or returns nil of NOERROR is non-nil."
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)
260 (setq end (min (or limit (point-max)) (match-end 0)))
261 (backward-char)))
262 (goto-char end)
263 start)
264 (unless noerror (error "No comment"))))))
266 (defun comment-search-backward (&optional limit noerror)
267 "Find a comment start between LIMIT and point.
268 Moves point to inside the comment and returns the position of the
269 comment-starter. If no comment is found, moves point to LIMIT
270 and 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))))
288 (defun comment-beginning ()
289 "Find the beginning of the enclosing comment.
290 Returns nil if not inside a comment, else moves point and returns
291 the 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))))
299 (defun comment-forward (&optional n)
300 "Skip forward over N comments.
301 Just like `forward-comment' but only for positive N
302 and 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))))
314 (defun comment-enter-backward ()
315 "Move from the end of a comment to the end of its content.
316 Point is assumed to be just at the end of a comment."
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))))))
327 ;;;;
328 ;;;; Commands
329 ;;;;
331 (defalias 'indent-for-comment 'comment-indent)
332 (defun comment-indent (&optional continue)
333 "Indent this line's comment to comment column, or insert an empty comment.
334 If CONTINUE is non-nil, use the `comment-continuation' markers if any."
335 (interactive "*")
336 (let* ((empty (save-excursion (beginning-of-line)
337 (looking-at "[ \t]*$")))
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)))
342 (cond
343 ((null starter)
344 (error "No comment syntax defined"))
345 (t (let* ((eolpos (save-excursion (end-of-line) (point)))
346 cpos indent begpos)
347 (beginning-of-line)
348 (when (setq begpos (comment-search-forward eolpos t))
349 (setq cpos (point-marker))
350 (goto-char begpos))
351 (setq begpos (point))
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))))))))
371 (defalias 'set-comment-column 'comment-set-column)
372 (defun comment-set-column (arg)
373 "Set the comment column based on point.
374 With no ARG, set the comment column to the current column.
375 With just minus as arg, kill any comment on this line.
376 With 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")
379 (cond
380 ((eq arg '-) (kill-comment nil))
381 (arg
382 (save-excursion
383 (beginning-of-line)
384 (comment-search-backward)
385 (beginning-of-line)
386 (goto-char (comment-search-forward))
387 (setq comment-column (current-column))
388 (message "Comment column set to %d" comment-column))
389 (indent-for-comment))
390 (t (setq comment-column (current-column))
391 (message "Comment column set to %d" comment-column))))
393 (defalias 'kill-comment 'comment-kill)
394 (defun comment-kill (arg)
395 "Kill the comment on this line, if any.
396 With 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)
404 (let ((cs (comment-search-forward endc t)))
405 (when cs
406 (goto-char cs)
407 (skip-syntax-backward " ")
408 (setq cs (point))
409 (comment-forward)
410 (kill-region cs (if (bolp) (1- (point)) (point)))
411 (indent-according-to-mode))))
412 (if arg (forward-line 1)))))
414 (defun comment-padright (str &optional n)
415 "Construct a string composed of STR plus `comment-padding'.
416 It contains N copies of the last non-whitespace chars of STR.
417 If STR already contains padding, the corresponding amount is
418 ignored from `comment-padding'.
419 N defaults to 1.
420 If N is `re', a regexp is returned instead, that would match
421 the string for any N."
422 (setq n (or n 0))
423 (when (and (stringp str) (not (string= "" str)))
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))))))
431 (if (symbolp n)
432 (concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
433 lpad "")
434 (regexp-quote s) "+"
435 (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
436 rpad ""))
437 (concat lpad s (make-string n (aref str (1- (match-end 1)))) rpad)))))
439 (defun comment-padleft (str &optional n)
440 "Construct a string composed of `comment-padding' plus STR.
441 It contains N copies of the last non-whitespace chars of STR.
442 If STR already contains padding, the corresponding amount is
443 ignored from `comment-padding'.
444 N defaults to 1.
445 If N is `re', a regexp is returned instead, that would match
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)))
456 (multi (or (not comment-quote-nested) (string= comment-end "")
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)))))
464 (defun uncomment-region (beg end &optional arg)
465 "Uncomment each line in the BEG..END region.
466 ARG is currently ignored."
467 (interactive "*r\nP")
468 (comment-normalize-vars)
469 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
470 (save-excursion
471 (goto-char beg)
472 (unless (markerp end) (setq end (copy-marker end)))
473 (let ((numarg (prefix-numeric-value arg))
474 spt)
475 (while (and (< (point) end)
476 (setq spt (comment-search-forward end t)))
477 (let* ((ipt (point))
478 ;; find the end of the comment
479 (ept (progn
480 (goto-char spt)
481 (unless (comment-forward)
482 (error "Can't find the comment end"))
483 (point-marker)))
484 (block nil)
485 (end-quote-re (comment-end-quote-re comment-end "\\\\"))
486 (ccs (car comment-continue))
487 (srei (comment-padright ccs 're))
488 (sre (and srei (concat "^\\s-*?\\(" srei "\\)"))))
489 (save-restriction
490 (narrow-to-region spt ept)
491 ;; remove the comment-start
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)))
500 (if (null arg) (delete-region (point-min) (point))
501 (skip-syntax-backward " ")
502 (delete-char (- numarg)))
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))
509 (if (null arg) (delete-region (point) ept)
510 (skip-syntax-forward " ")
511 (delete-char numarg)))
513 ;; unquote any nested end-comment
514 (when end-quote-re
515 (goto-char (point-min))
516 (while (re-search-forward end-quote-re nil t)
517 (delete-region (match-beginning 1) (match-end 1))))
519 ;; eliminate continuation markers as well
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))))
525 (when re
526 (goto-char (point-min))
527 ;; there can't be a real SRE on the first line.
528 (when (and sre (looking-at sre)) (goto-char (match-end 0)))
529 (while (re-search-forward re nil t)
530 (replace-match "" t t nil (if (match-end 2) 2 1)))))
531 ;; go the the end for the next comment
532 (goto-char (point-max))))))))
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)))
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))))
561 (cons cs ce))
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.
567 Space is added (and then removed) at the beginning for the text's
568 indentation 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)))))))))
592 (defun comment-region-internal (beg end cs ce
593 &optional ccs cce block lines indent)
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))
601 ;; make sure we have end-markers for BLOCK mode
602 (when block (unless ce (setq ce (comment-string-reverse cs))))
603 ;; continuation defaults to the same
604 (if ccs (unless block (setq cce nil))
605 (setq ccs cs cce ce))
607 (save-excursion
608 (goto-char end)
609 (unless (or ce (eolp)) (insert "\n") (indent-according-to-mode))
610 (comment-with-narrowing beg end
611 (let ((ce-quote-re (comment-end-quote-re comment-end))
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
620 (let ((eol (line-end-position)))
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)))
628 ;; inserting ccs can change max-indent by (1- tab-width)
629 (incf max-indent (+ (max (length cs) (length ccs)) -1 tab-width))
630 (unless indent (setq min-indent 0))
632 ;; make the leading and trailing lines if requested
633 (when lines
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))))
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))))))))
654 (defun comment-region (beg end &optional arg)
655 "Comment or uncomment each line in the region.
656 With just \\[universal-prefix] prefix arg, uncomment each line in region BEG..END.
657 Numeric prefix arg ARG means use ARG comment characters.
658 If ARG is negative, delete that many comment characters instead.
659 Comments are terminated on each line, even for syntax in which newline does
660 not end the comment. Blank lines do not get comments.
662 The 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)))
667 (let* ((numarg (prefix-numeric-value arg))
668 (add comment-add)
669 (style (cdr (assoc comment-style comment-styles)))
670 (lines (nth 2 style))
671 (block (nth 1 style))
672 (multi (nth 0 style)))
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"))
683 ;; sanitize LINES
684 (setq lines
685 (and
686 lines multi
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))
692 (or (not (string= "" comment-end)) block)
693 (progn (goto-char beg) (search-forward "\n" end t)))))
695 ;; don't add end-markers just because the user asked for `block'
696 (unless (or lines (string= "" comment-end)) (setq block nil))
698 (cond
699 ((consp arg) (uncomment-region beg end))
700 ((< numarg 0) (uncomment-region beg end (- numarg)))
702 (if (and (null arg) (= (length comment-start) 1))
703 (setq numarg add) (decf numarg))
704 (comment-region-internal
705 beg end
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))))
712 (if multi (comment-padright (car comment-continue) numarg))
713 (if multi (comment-padleft (cdr comment-continue) numarg))
714 block
715 lines
716 (nth 3 style))))))
718 (defun comment-box (beg end &optional arg)
719 "Comment out the BEG..END region, putting it inside a box.
720 The numeric prefix ARG specifies how many characters to add to begin- and
721 end- 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))))
726 (defun comment-dwim (arg)
727 "Call the comment command you want.
728 If the region is active and `transient-mark-mode' is on,
729 calls `comment-region' (unless it only consists
730 in comments, in which case it calls `uncomment-region').
731 Else, if the current line is empty, insert a comment and indent it.
732 Else call `indent-for-comment' or `kill-comment' if a prefix ARG is specified."
733 (interactive "*P")
734 (comment-normalize-vars)
735 (if (and mark-active transient-mark-mode)
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)
747 (if (= (length comment-start) 1) comment-add 0))))
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))))))
754 (defcustom comment-spill t
757 (defun indent-new-comment-line (&optional soft)
758 "Break line at point and indent, continuing comment if within one.
759 This indents the body of the continued comment
760 under the previous comment line.
762 This command is intended for styles where you write a comment per line,
763 starting a new comment (and terminating it if necessary) on each line.
764 If you want to continue one comment across several lines, use \\[newline-and-indent].
766 If a fill column is specified, it overrides the use of the comment column
767 or comment indentation.
769 The inserted newline is marked hard if variable `use-hard-newlines' is true,
770 unless 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.
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
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)))))
828 (provide 'newcomment)
830 ;;; Change Log:
831 ;; $Log: newcomment.el,v $
832 ;; Revision 1.6 1999/12/08 00:19:51 monnier
833 ;; various fixes and gratuitous movements.
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 ;; ...
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.
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'.
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.
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
870 ;; - comment-region will try to quote comment-end markers inside the region
871 ;; - comment-start markers are placed at the indentation level
874 ;;; newcomment.el ends here