(url-cookie-multiple-line): Specify group and type in defcustom.
[bpt/emacs.git] / lisp / newcomment.el
CommitLineData
83b96b22
SM
1;;; newcomment.el --- (un)comment regions of buffers
2
b70dd952 3;; Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation Inc.
83b96b22 4
59a1ce8d 5;; Author: code extracted from Emacs-20's simple.el
771c9b97 6;; Maintainer: Stefan Monnier <monnier@cs.yale.edu>
83b96b22 7;; Keywords: comment uncomment
83b96b22 8
59a1ce8d
SM
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
83b96b22 12;; it under the terms of the GNU General Public License as published by
59a1ce8d
SM
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
83b96b22
SM
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.
59a1ce8d 20
83b96b22 21;; You should have received a copy of the GNU General Public License
59a1ce8d
SM
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
83b96b22
SM
25
26;;; Commentary:
27
771c9b97 28;; A replacement for simple.el's comment-related functions.
83b96b22
SM
29
30;;; Bugs:
31
3674a4a9
SM
32;; - boxed comments in Perl are not properly uncommented because they are
33;; uncommented one-line at a time.
75e95a97 34;; - nested comments in sgml-mode are not properly quoted.
2ab98065
SM
35;; - single-char nestable comment-start can only do the "\\s<+" stuff
36;; if the corresponding closing marker happens to be right.
3e569d22
SM
37;; - uncomment-region with a numeric argument can render multichar
38;; comment markers invalid.
3fc5b4e2
SM
39;; - comment-indent or comment-region when called inside a comment
40;; will happily break the surrounding comment.
41;; - comment-quote-nested will not (un)quote properly all nested comment
42;; markers if there are more than just comment-start and comment-end.
43;; For example, in Pascal where {...*) and (*...} are possible.
83b96b22
SM
44
45;;; Todo:
46
3674a4a9
SM
47;; - rebox.el-style refill.
48;; - quantized steps in comment-alignment.
49;; - try to align tail comments.
50;; - check what c-comment-line-break-function has to say.
51;; - spill auto-fill of comments onto the end of the next line.
7a0a180a 52;; - uncomment-region with a consp (for blocks) or somehow make the
3674a4a9
SM
53;; deletion of continuation markers less dangerous.
54;; - drop block-comment-<foo> unless it's really used.
55;; - uncomment-region on a subpart of a comment.
56;; - support gnu-style "multi-line with space in continue".
771c9b97
SM
57;; - somehow allow comment-dwim to use the region even if transient-mark-mode
58;; is not turned on.
83b96b22 59
bdbe3a89
SM
60;; - when auto-filling a comment, try to move the comment to the left
61;; rather than break it (if possible).
62;; - sometimes default the comment-column to the same
63;; one used on the preceding line(s).
64
83b96b22
SM
65;;; Code:
66
be83ecb2 67;;;###autoload
59a1ce8d 68(defalias 'indent-for-comment 'comment-indent)
be83ecb2 69;;;###autoload
59a1ce8d 70(defalias 'set-comment-column 'comment-set-column)
be83ecb2 71;;;###autoload
59a1ce8d 72(defalias 'kill-comment 'comment-kill)
be83ecb2 73;;;###autoload
59a1ce8d 74(defalias 'indent-new-comment-line 'comment-indent-new-line)
83b96b22 75
3e569d22
SM
76(defgroup comment nil
77 "Indenting and filling of comments."
78 :prefix "comment-"
ac87b3a9 79 :version "21.1"
3e569d22
SM
80 :group 'fill)
81
75975685
RS
82;; Autoload this to avoid warnings, since some major modes define it.
83;;;###autoload
771c9b97
SM
84(defvar comment-use-syntax 'undecided
85 "Non-nil if syntax-tables can be used instead of regexps.
86Can also be `undecided' which means that a somewhat expensive test will
87be used to try to determine whether syntax-tables should be trusted
f5215400
SM
88to understand comments or not in the given buffer.
89Major modes should set this variable.")
2ab98065 90
88fe06af
SM
91(defcustom comment-fill-column nil
92 "Column to use for `comment-indent'. If nil, use `fill-column' instead."
41dfdd71 93 :type '(choice (const nil) integer))
88fe06af 94
be83ecb2 95;;;###autoload
83b96b22
SM
96(defcustom comment-column 32
97 "*Column to indent right-margin comments to.
83b96b22 98Each mode establishes a different default value for this variable; you
a704fec0
SM
99can set the value for a particular mode using that mode's hook.
100Comments might be indented to a value smaller than this in order
88fe06af 101not to go beyond `comment-fill-column'."
0603917d 102 :type 'integer)
83b96b22
SM
103(make-variable-buffer-local 'comment-column)
104
be83ecb2 105;;;###autoload
f5215400
SM
106(defvar comment-start nil
107 "*String to insert to start a new comment, or nil if no comment syntax.")
83b96b22 108
be83ecb2 109;;;###autoload
f5215400 110(defvar comment-start-skip nil
83b96b22
SM
111 "*Regexp to match the start of a comment plus everything up to its body.
112If there are any \\(...\\) pairs, the comment delimiter text is held to begin
f5215400 113at the place matched by the close of the first pair.")
771c9b97 114
be83ecb2 115;;;###autoload
3e569d22
SM
116(defvar comment-end-skip nil
117 "Regexp to match the end of a comment plus everything up to its body.")
83b96b22 118
be83ecb2 119;;;###autoload
f5215400 120(defvar comment-end ""
83b96b22 121 "*String to insert to end a new comment.
f5215400 122Should be an empty string if comments are terminated by end-of-line.")
83b96b22 123
be83ecb2 124;;;###autoload
87cf8421 125(defvar comment-indent-function 'comment-indent-default
83b96b22
SM
126 "Function to compute desired indentation for a comment.
127This function is called with no args with point at the beginning of
87cf8421
SM
128the comment's starting delimiter and should return either the desired
129column indentation or nil.
130If nil is returned, indentation is delegated to `indent-according-to-mode'.")
83b96b22 131
a71b3805
EZ
132;;;###autoload
133(defvar comment-insert-comment-function nil
134 "Function to insert a comment when a line doesn't contain one.
135The function has no args.
136
137Applicable at least in modes for languages like fixed-format Fortran where
138comments always start in column zero.")
139
b70dd952 140(defvar comment-region-function 'comment-region-default
a71b3805
EZ
141 "Function to comment a region.
142Its args are the same as those of `comment-region', but BEG and END are
143guaranteed to be correctly ordered. It is called within `save-excursion'.
144
145Applicable at least in modes for languages like fixed-format Fortran where
146comments always start in column zero.")
147
b70dd952 148(defvar uncomment-region-function 'uncomment-region-default
a71b3805
EZ
149 "Function to uncomment a region.
150Its args are the same as those of `uncomment-region', but BEG and END are
151guaranteed to be correctly ordered. It is called within `save-excursion'.
152
153Applicable at least in modes for languages like fixed-format Fortran where
154comments always start in column zero.")
155
156;; ?? never set
3e569d22
SM
157(defvar block-comment-start nil)
158(defvar block-comment-end nil)
83b96b22 159
771c9b97
SM
160(defvar comment-quote-nested t
161 "Non-nil if nested comments should be quoted.
162This should be locally set by each major mode if needed.")
163
164(defvar comment-continue nil
f5215400
SM
165 "Continuation string to insert for multiline comments.
166This string will be added at the beginning of each line except the very
167first one when commenting a region with a commenting style that allows
168comments to span several lines.
169It should generally have the same length as `comment-start' in order to
170preserve indentation.
171If it is nil a value will be automatically derived from `comment-start'
172by replacing its first character with a space.")
173
771c9b97 174(defvar comment-add 0
f5215400
SM
175 "How many more comment chars should be inserted by `comment-region'.
176This determines the default value of the numeric argument of `comment-region'.
177This should generally stay 0, except for a few modes like Lisp where
178it can be convenient to set it to 1 so that regions are commented with
179two semi-colons.")
2ab98065 180
2ab98065 181(defconst comment-styles
771c9b97
SM
182 '((plain . (nil nil nil nil))
183 (indent . (nil nil nil t))
184 (aligned . (nil t nil t))
185 (multi-line . (t nil nil t))
186 (extra-line . (t nil t t))
9b0d1d6e
SM
187 (box . (nil t t t))
188 (box-multi . (t t t t)))
f5215400
SM
189 "Possible comment styles of the form (STYLE . (MULTI ALIGN EXTRA INDENT)).
190STYLE should be a mnemonic symbol.
191MULTI specifies that comments are allowed to span multiple lines.
192ALIGN specifies that the `comment-end' markers should be aligned.
193EXTRA specifies that an extra line should be used before and after the
194 region to comment (to put the `comment-end' and `comment-start').
195INDENT specifies that the `comment-start' markers should not be put at the
196 left margin but at the current indentation of the region to comment.")
197
be83ecb2 198;;;###autoload
f5215400
SM
199(defcustom comment-style 'plain
200 "*Style to be used for `comment-region'.
201See `comment-styles' for a list of available styles."
be83ecb2
SM
202 :type (if (boundp 'comment-styles)
203 `(choice ,@(mapcar (lambda (s) `(const ,(car s))) comment-styles))
204 'symbol))
2ab98065 205
be83ecb2
SM
206;;;###autoload
207(defcustom comment-padding " "
f5215400
SM
208 "Padding string that `comment-region' puts between comment chars and text.
209Can also be an integer which will be automatically turned into a string
210of the corresponding number of spaces.
2ab98065
SM
211
212Extra spacing between the comment characters and the comment text
0603917d
SM
213makes the comment easier to read. Default is 1. nil means 0."
214 :type '(choice string integer (const nil)))
2ab98065 215
be83ecb2 216;;;###autoload
3e569d22 217(defcustom comment-multi-line nil
269a8f1c
LT
218 "*Non-nil means `comment-indent-new-line' continues comments.
219That is, it inserts no new terminator or starter.
220This affects `auto-fill-mode', which is the main reason to
221customize this variable.
222
223It also affects \\[indent-new-comment-line]. However, if you want this
224behavior for explicit filling, you might as well use \\[newline-and-indent]."
0603917d 225 :type 'boolean)
3e569d22 226
6976bf99
SM
227(defcustom comment-empty-lines nil
228 "If nil, `comment-region' does not comment out empty lines.
229If t, it always comments out empty lines.
230if `eol' it only comments out empty lines if comments are
231terminated by the end of line (i.e. `comment-end' is empty)."
232 :type '(choice (const :tag "Never" nil)
233 (const :tag "Always" t)
234 (const :tag "EOl-terminated" 'eol)))
235
2ab98065
SM
236;;;;
237;;;; Helpers
238;;;;
239
f5215400
SM
240(defun comment-string-strip (str beforep afterp)
241 "Strip STR of any leading (if BEFOREP) and/or trailing (if AFTERP) space."
242 (string-match (concat "\\`" (if beforep "\\s-*")
ad679e45 243 "\\(.*?\\)" (if afterp "\\s-*\n?")
2ab98065
SM
244 "\\'") str)
245 (match-string 1 str))
246
247(defun comment-string-reverse (s)
f5215400 248 "Return the mirror image of string S, without any trailing space."
771c9b97 249 (comment-string-strip (concat (nreverse (string-to-list s))) nil t))
2ab98065 250
7164ef13 251;;;###autoload
2ab98065 252(defun comment-normalize-vars (&optional noerror)
0f21c5a0
SM
253 "Check and setup the variables needed by other commenting functions.
254Functions autoloaded from newcomment.el, being entry points, should call
255this function before any other, so the rest of the code can assume that
256the variables are properly set."
0d6a08f6
SM
257 (unless (and (not comment-start) noerror)
258 (unless comment-start
31df2900
SM
259 (let ((cs (read-string "No comment syntax is defined. Use: ")))
260 (if (zerop (length cs))
261 (error "No comment syntax defined")
262 (set (make-local-variable 'comment-start) cs))))
2ab98065 263 ;; comment-use-syntax
771c9b97 264 (when (eq comment-use-syntax 'undecided)
2ab98065
SM
265 (set (make-local-variable 'comment-use-syntax)
266 (let ((st (syntax-table))
267 (cs comment-start)
268 (ce (if (string= "" comment-end) "\n" comment-end)))
771c9b97
SM
269 ;; Try to skip over a comment using forward-comment
270 ;; to see if the syntax tables properly recognize it.
2ab98065
SM
271 (with-temp-buffer
272 (set-syntax-table st)
273 (insert cs " hello " ce)
274 (goto-char (point-min))
275 (and (forward-comment 1) (eobp))))))
2ab98065 276 ;; comment-padding
9145f1c2 277 (unless comment-padding (setq comment-padding 0))
2ab98065
SM
278 (when (integerp comment-padding)
279 (setq comment-padding (make-string comment-padding ? )))
280 ;; comment markers
281 ;;(setq comment-start (comment-string-strip comment-start t nil))
282 ;;(setq comment-end (comment-string-strip comment-end nil t))
283 ;; comment-continue
f5215400 284 (unless (or comment-continue (string= comment-end ""))
2ab98065 285 (set (make-local-variable 'comment-continue)
9b0d1d6e 286 (concat (if (string-match "\\S-\\S-" comment-start) " " "|")
7164ef13
SM
287 (substring comment-start 1)))
288 ;; Hasn't been necessary yet.
289 ;; (unless (string-match comment-start-skip comment-continue)
290 ;; (kill-local-variable 'comment-continue))
291 )
2ab98065 292 ;; comment-skip regexps
f23a0f3a
SM
293 (unless (and comment-start-skip
294 ;; In case comment-start has changed since last time.
295 (string-match comment-start-skip comment-start))
2ab98065 296 (set (make-local-variable 'comment-start-skip)
f0a478be 297 (concat "\\(\\(^\\|[^\\\n]\\)\\(\\\\\\\\\\)*\\)\\(\\s<+\\|"
2ab98065 298 (regexp-quote (comment-string-strip comment-start t t))
75ed43a6
SM
299 ;; Let's not allow any \s- but only [ \t] since \n
300 ;; might be both a comment-end marker and \s-.
301 "+\\)[ \t]*")))
f23a0f3a
SM
302 (unless (and comment-end-skip
303 ;; In case comment-end has changed since last time.
304 (string-match comment-end-skip comment-end))
2ab98065
SM
305 (let ((ce (if (string= "" comment-end) "\n"
306 (comment-string-strip comment-end t t))))
307 (set (make-local-variable 'comment-end-skip)
37dbd369
SM
308 ;; We use [ \t] rather than \s- because we don't want to
309 ;; remove ^L in C mode when uncommenting.
310 (concat "[ \t]*\\(\\s>" (if comment-quote-nested "" "+")
2ab98065 311 "\\|" (regexp-quote (substring ce 0 1))
771c9b97 312 (if (and comment-quote-nested (<= (length ce) 1)) "" "+")
2ab98065
SM
313 (regexp-quote (substring ce 1))
314 "\\)"))))))
f1180544 315
f5215400
SM
316(defun comment-quote-re (str unp)
317 (concat (regexp-quote (substring str 0 1))
318 "\\\\" (if unp "+" "*")
319 (regexp-quote (substring str 1))))
320
321(defun comment-quote-nested (cs ce unp)
322 "Quote or unquote nested comments.
323If UNP is non-nil, unquote nested comment markers."
324 (setq cs (comment-string-strip cs t t))
325 (setq ce (comment-string-strip ce t t))
326 (when (and comment-quote-nested (> (length ce) 0))
327 (let ((re (concat (comment-quote-re ce unp)
328 "\\|" (comment-quote-re cs unp))))
329 (goto-char (point-min))
330 (while (re-search-forward re nil t)
331 (goto-char (match-beginning 0))
332 (forward-char 1)
333 (if unp (delete-char 1) (insert "\\"))
334 (when (= (length ce) 1)
335 ;; If the comment-end is a single char, adding a \ after that
336 ;; "first" char won't deactivate it, so we turn such a CE
337 ;; into !CS. I.e. for pascal, we turn } into !{
338 (if (not unp)
339 (when (string= (match-string 0) ce)
340 (replace-match (concat "!" cs) t t))
341 (when (and (< (point-min) (match-beginning 0))
342 (string= (buffer-substring (1- (match-beginning 0))
343 (1- (match-end 0)))
344 (concat "!" cs)))
345 (backward-char 2)
346 (delete-char (- (match-end 0) (match-beginning 0)))
347 (insert ce))))))))
2ab98065
SM
348
349;;;;
350;;;; Navigation
351;;;;
352
f5f95edf
SM
353(defvar comment-use-global-state nil
354 "Non-nil means that the global syntactic context is used.
355More specifically, it means that `syntax-ppss' is used to find out whether
356point is within a string or not. Major modes whose syntax is faithfully
357described by the syntax-tables can set this to non-nil so comment markers
358in strings will not confuse Emacs.")
359
ad679e45 360(defun comment-search-forward (limit &optional noerror)
771c9b97
SM
361 "Find a comment start between point and LIMIT.
362Moves point to inside the comment and returns the position of the
363comment-starter. If no comment is found, moves point to LIMIT
11a26e05 364and raises an error or returns nil if NOERROR is non-nil."
2ab98065 365 (if (not comment-use-syntax)
9b0d1d6e
SM
366 (if (re-search-forward comment-start-skip limit noerror)
367 (or (match-end 1) (match-beginning 0))
368 (goto-char limit)
369 (unless noerror (error "No comment")))
ad679e45
SM
370 (let* ((pt (point))
371 ;; Assume (at first) that pt is outside of any string.
f5f95edf
SM
372 (s (parse-partial-sexp pt (or limit (point-max)) nil nil
373 (if comment-use-global-state (syntax-ppss pt))
374 t)))
375 (when (and (nth 8 s) (nth 3 s) (not comment-use-global-state))
b70dd952
SM
376 ;; The search ended at eol inside a string. Try to see if it
377 ;; works better when we assume that pt is inside a string.
378 (setq s (parse-partial-sexp
379 pt (or limit (point-max)) nil nil
380 (list nil nil nil (nth 3 s) nil nil nil nil)
381 t)))
ad679e45
SM
382 (if (not (and (nth 8 s) (not (nth 3 s))))
383 (unless noerror (error "No comment"))
384 ;; We found the comment.
9b0d1d6e 385 (let ((pos (point))
ad679e45 386 (start (nth 8 s))
9b0d1d6e 387 (bol (line-beginning-position))
ad679e45
SM
388 (end nil))
389 (while (and (null end) (>= (point) bol))
390 (if (looking-at comment-start-skip)
391 (setq end (min (or limit (point-max)) (match-end 0)))
392 (backward-char)))
9b0d1d6e 393 (goto-char (or end pos))
ad679e45 394 start)))))
2ab98065
SM
395
396(defun comment-search-backward (&optional limit noerror)
397 "Find a comment start between LIMIT and point.
771c9b97
SM
398Moves point to inside the comment and returns the position of the
399comment-starter. If no comment is found, moves point to LIMIT
11a26e05 400and raises an error or returns nil if NOERROR is non-nil."
ad679e45
SM
401 ;; FIXME: If a comment-start appears inside a comment, we may erroneously
402 ;; stop there. This can be rather bad in general, but since
403 ;; comment-search-backward is only used to find the comment-column (in
404 ;; comment-set-column) and to find the comment-start string (via
405 ;; comment-beginning) in indent-new-comment-line, it should be harmless.
2ab98065
SM
406 (if (not (re-search-backward comment-start-skip limit t))
407 (unless noerror (error "No comment"))
408 (beginning-of-line)
409 (let* ((end (match-end 0))
410 (cs (comment-search-forward end t))
411 (pt (point)))
412 (if (not cs)
413 (progn (beginning-of-line)
414 (comment-search-backward limit noerror))
415 (while (progn (goto-char cs)
416 (comment-forward)
417 (and (< (point) end)
418 (setq cs (comment-search-forward end t))))
419 (setq pt (point)))
420 (goto-char pt)
421 cs))))
422
423(defun comment-beginning ()
771c9b97
SM
424 "Find the beginning of the enclosing comment.
425Returns nil if not inside a comment, else moves point and returns
2308f447 426the same as `comment-search-backward'."
75ed43a6
SM
427 ;; HACK ATTACK!
428 ;; We should really test `in-string-p' but that can be expensive.
429 (unless (eq (get-text-property (point) 'face) 'font-lock-string-face)
430 (let ((pt (point))
431 (cs (comment-search-backward nil t)))
432 (when cs
433 (if (save-excursion
434 (goto-char cs)
dde6824c
SM
435 (and
436 ;; For modes where comment-start and comment-end are the same,
437 ;; the search above may have found a `ce' rather than a `cs'.
2308f447 438 (or (if comment-end-skip (not (looking-at comment-end-skip)))
dde6824c
SM
439 ;; Maybe font-lock knows that it's a `cs'?
440 (eq (get-text-property (match-end 0) 'face)
441 'font-lock-comment-face)
442 (unless (eq (get-text-property (point) 'face)
443 'font-lock-comment-face)
444 ;; Let's assume it's a `cs' if we're on the same line.
445 (>= (line-end-position) pt)))
446 ;; Make sure that PT is not past the end of the comment.
447 (if (comment-forward 1) (> (point) pt) (eobp))))
75ed43a6
SM
448 cs
449 (goto-char pt)
450 nil)))))
2ab98065
SM
451
452(defun comment-forward (&optional n)
453 "Skip forward over N comments.
454Just like `forward-comment' but only for positive N
455and can use regexps instead of syntax."
456 (setq n (or n 1))
457 (if (< n 0) (error "No comment-backward")
458 (if comment-use-syntax (forward-comment n)
459 (while (> n 0)
59a1ce8d 460 (setq n
0f38a885
SM
461 (if (or (forward-comment 1)
462 (and (looking-at comment-start-skip)
463 (goto-char (match-end 0))
464 (re-search-forward comment-end-skip nil 'move)))
59a1ce8d 465 (1- n) -1)))
2ab98065
SM
466 (= n 0))))
467
468(defun comment-enter-backward ()
469 "Move from the end of a comment to the end of its content.
771c9b97 470Point is assumed to be just at the end of a comment."
2ab98065
SM
471 (if (bolp)
472 ;; comment-end = ""
473 (progn (backward-char) (skip-syntax-backward " "))
474 (let ((end (point)))
475 (beginning-of-line)
476 (save-restriction
477 (narrow-to-region (point) end)
ad679e45
SM
478 (if (re-search-forward (concat comment-end-skip "\\'") nil t)
479 (goto-char (match-beginning 0))
480 ;; comment-end-skip not found probably because it was not set right.
481 ;; Since \\s> should catch the single-char case, we'll blindly
482 ;; assume we're at the end of a two-char comment-end.
483 (goto-char (point-max))
484 (backward-char 2)
485 (skip-chars-backward (string (char-after)))
486 (skip-syntax-backward " "))))))
2ab98065
SM
487
488;;;;
489;;;; Commands
490;;;;
491
fae99944 492;;;###autoload
87cf8421
SM
493(defun comment-indent-default ()
494 "Default for `comment-indent-function'."
d3fcda22
SM
495 (if (and (looking-at "\\s<\\s<\\(\\s<\\)?")
496 (or (match-end 1) (/= (current-column) (current-indentation))))
497 0
87cf8421
SM
498 (when (or (/= (current-column) (current-indentation))
499 (and (> comment-add 0) (looking-at "\\s<\\S<")))
500 comment-column)))
501
be83ecb2 502;;;###autoload
3e569d22 503(defun comment-indent (&optional continue)
a71b3805 504 "Indent this line's comment to `comment-column', or insert an empty comment.
dde6824c 505If CONTINUE is non-nil, use the `comment-continue' markers if any."
83b96b22 506 (interactive "*")
ad679e45 507 (comment-normalize-vars)
83b96b22
SM
508 (let* ((empty (save-excursion (beginning-of-line)
509 (looking-at "[ \t]*$")))
f5215400 510 (starter (or (and continue comment-continue)
2ab98065 511 (and empty block-comment-start) comment-start))
f5215400 512 (ender (or (and continue comment-continue "")
2ab98065 513 (and empty block-comment-end) comment-end)))
2e1fbba4
SM
514 (unless starter (error "No comment syntax defined"))
515 (beginning-of-line)
516 (let* ((eolpos (line-end-position))
517 (begpos (comment-search-forward eolpos t))
518 cpos indent)
519 ;; An existing comment?
7a06b250
SM
520 (if begpos
521 (progn
522 (if (and (not (looking-at "[\t\n ]"))
523 (looking-at comment-end-skip))
524 ;; The comment is empty and we have skipped all its space
525 ;; and landed right before the comment-ender:
526 ;; Go back to the middle of the space.
527 (forward-char (/ (skip-chars-backward " \t") -2)))
528 (setq cpos (point-marker)))
2e1fbba4 529 ;; If none, insert one.
a71b3805
EZ
530 (if comment-insert-comment-function
531 (funcall comment-insert-comment-function)
2e1fbba4 532 (save-excursion
a71b3805
EZ
533 ;; Some `comment-indent-function's insist on not moving
534 ;; comments that are in column 0, so we first go to the
535 ;; likely target column.
567e961e 536 (indent-to comment-column)
e6bba95e
DL
537 ;; Ensure there's a space before the comment for things
538 ;; like sh where it matters (as well as being neater).
07f10bab 539 (unless (memq (char-before) '(nil ?\n ?\t ?\ ))
e6bba95e 540 (insert ?\ ))
07f10bab 541 (setq begpos (point))
bb304a7a 542 (insert starter)
2e1fbba4 543 (setq cpos (point-marker))
a71b3805 544 (insert ender))))
2e1fbba4
SM
545 (goto-char begpos)
546 ;; Compute desired indent.
053b8d35 547 (setq indent (save-excursion (funcall comment-indent-function)))
8cc313a3
SM
548 ;; If `indent' is nil and there's code before the comment, we can't
549 ;; use `indent-according-to-mode', so we default to comment-column.
550 (unless (or indent (save-excursion (skip-chars-backward " \t") (bolp)))
551 (setq indent comment-column))
2e1fbba4 552 (if (not indent)
bfe6e13f 553 ;; comment-indent-function refuses: delegate to line-indent.
2e1fbba4 554 (indent-according-to-mode)
8cc313a3 555 ;; If the comment is at the left of code, adjust the indentation.
a764440a 556 (unless (save-excursion (skip-chars-backward " \t") (bolp))
8cc313a3 557 ;; Avoid moving comments past the fill-column.
bfe6e13f 558 (let ((max (+ (current-column)
88fe06af 559 (- (or comment-fill-column fill-column)
bfe6e13f
SM
560 (save-excursion (end-of-line) (current-column))))))
561 (if (<= max indent)
562 (setq indent max) ;Don't move past the fill column.
563 ;; We can choose anywhere between indent..max.
564 ;; Let's try to align to a comment on the previous line.
8cc313a3
SM
565 (let ((other nil)
566 (min (max indent
567 (save-excursion (skip-chars-backward " \t")
568 (1+ (current-column))))))
bfe6e13f
SM
569 (save-excursion
570 (when (and (zerop (forward-line -1))
571 (setq other (comment-search-forward
572 (line-end-position) t)))
573 (goto-char other) (setq other (current-column))))
8cc313a3 574 (if (and other (<= other max) (>= other min))
bfe6e13f
SM
575 ;; There is a comment and it's in the range: bingo.
576 (setq indent other)
577 ;; Let's try to align to a comment on the next line, then.
578 (let ((other nil))
579 (save-excursion
580 (when (and (zerop (forward-line 1))
581 (setq other (comment-search-forward
582 (line-end-position) t)))
583 (goto-char other) (setq other (current-column))))
8cc313a3 584 (if (and other (<= other max) (> other min))
bfe6e13f
SM
585 ;; There is a comment and it's in the range: bingo.
586 (setq indent other))))))))
7164ef13 587 (unless (= (current-column) indent)
2e1fbba4 588 ;; If that's different from current, change it.
7164ef13 589 (delete-region (point) (progn (skip-chars-backward " \t") (point)))
2e1fbba4
SM
590 (indent-to (if (bolp) indent
591 (max indent (1+ (current-column)))))))
592 (goto-char cpos)
593 (set-marker cpos nil))))
83b96b22 594
be83ecb2 595;;;###autoload
3e569d22 596(defun comment-set-column (arg)
83b96b22 597 "Set the comment column based on point.
2ab98065 598With no ARG, set the comment column to the current column.
83b96b22
SM
599With just minus as arg, kill any comment on this line.
600With any other arg, set comment column to indentation of the previous comment
601 and then align or create a comment on this line at that column."
602 (interactive "P")
e8fe7d39 603 (cond
ad679e45 604 ((eq arg '-) (comment-kill nil))
e8fe7d39 605 (arg
2abd0306 606 (comment-normalize-vars)
e8fe7d39
SM
607 (save-excursion
608 (beginning-of-line)
2ab98065 609 (comment-search-backward)
e8fe7d39 610 (beginning-of-line)
ad679e45 611 (goto-char (comment-search-forward (line-end-position)))
83b96b22 612 (setq comment-column (current-column))
e8fe7d39 613 (message "Comment column set to %d" comment-column))
ad679e45 614 (comment-indent))
e8fe7d39 615 (t (setq comment-column (current-column))
83b96b22
SM
616 (message "Comment column set to %d" comment-column))))
617
be83ecb2 618;;;###autoload
3e569d22 619(defun comment-kill (arg)
7a0a180a
SM
620 "Kill the comment on this line, if any.
621With prefix ARG, kill comments on that many lines starting with this one."
622 (interactive "P")
2abd0306 623 (comment-normalize-vars)
ad679e45
SM
624 (dotimes (_ (prefix-numeric-value arg))
625 (save-excursion
626 (beginning-of-line)
627 (let ((cs (comment-search-forward (line-end-position) t)))
628 (when cs
629 (goto-char cs)
630 (skip-syntax-backward " ")
631 (setq cs (point))
632 (comment-forward)
633 (kill-region cs (if (bolp) (1- (point)) (point)))
634 (indent-according-to-mode))))
635 (if arg (forward-line 1))))
7a0a180a 636
2ab98065
SM
637(defun comment-padright (str &optional n)
638 "Construct a string composed of STR plus `comment-padding'.
f5215400 639It also adds N copies of the last non-whitespace chars of STR.
2ab98065 640If STR already contains padding, the corresponding amount is
f5215400
SM
641ignored from `comment-padding'.
642N defaults to 0.
771c9b97 643If N is `re', a regexp is returned instead, that would match
f5215400 644the string for any N."
2ab98065
SM
645 (setq n (or n 0))
646 (when (and (stringp str) (not (string= "" str)))
f5215400 647 ;; Separate the actual string from any leading/trailing padding
3e569d22 648 (string-match "\\`\\s-*\\(.*?\\)\\s-*\\'" str)
f5215400
SM
649 (let ((s (match-string 1 str)) ;actual string
650 (lpad (substring str 0 (match-beginning 1))) ;left padding
651 (rpad (concat (substring str (match-end 1)) ;original right padding
652 (substring comment-padding ;additional right padding
3e569d22 653 (min (- (match-end 0) (match-end 1))
9b0d1d6e
SM
654 (length comment-padding)))))
655 ;; We can only duplicate C if the comment-end has multiple chars
656 ;; or if comments can be nested, else the comment-end `}' would
657 ;; be turned into `}}}' where only the first ends the comment
658 ;; and the rest becomes bogus junk.
659 (multi (not (and comment-quote-nested
660 ;; comment-end is a single char
661 (string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
f5215400 662 (if (not (symbolp n))
9b0d1d6e 663 (concat lpad s (when multi (make-string n (aref str (1- (match-end 1))))) rpad)
f5215400
SM
664 ;; construct a regexp that would match anything from just S
665 ;; to any possible output of this function for any N.
666 (concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
667 lpad "") ;padding is not required
9b0d1d6e
SM
668 (regexp-quote s)
669 (when multi "+") ;the last char of S might be repeated
f5215400
SM
670 (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
671 rpad "")))))) ;padding is not required
2ab98065
SM
672
673(defun comment-padleft (str &optional n)
674 "Construct a string composed of `comment-padding' plus STR.
f5215400 675It also adds N copies of the first non-whitespace chars of STR.
2ab98065 676If STR already contains padding, the corresponding amount is
f5215400
SM
677ignored from `comment-padding'.
678N defaults to 0.
771c9b97 679If N is `re', a regexp is returned instead, that would match
2ab98065
SM
680 the string for any N."
681 (setq n (or n 0))
682 (when (and (stringp str) (not (string= "" str)))
f5215400 683 ;; Only separate the left pad because we assume there is no right pad.
2ab98065
SM
684 (string-match "\\`\\s-*" str)
685 (let ((s (substring str (match-end 0)))
686 (pad (concat (substring comment-padding
687 (min (- (match-end 0) (match-beginning 0))
688 (length comment-padding)))
689 (match-string 0 str)))
f5215400
SM
690 (c (aref str (match-end 0))) ;the first non-space char of STR
691 ;; We can only duplicate C if the comment-end has multiple chars
692 ;; or if comments can be nested, else the comment-end `}' would
693 ;; be turned into `}}}' where only the first ends the comment
694 ;; and the rest becomes bogus junk.
695 (multi (not (and comment-quote-nested
696 ;; comment-end is a single char
697 (string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
698 (if (not (symbolp n))
699 (concat pad (when multi (make-string n c)) s)
700 ;; Construct a regexp that would match anything from just S
701 ;; to any possible output of this function for any N.
702 ;; We match any number of leading spaces because this regexp will
703 ;; be used for uncommenting where we might want to remove
704 ;; uncomment markers with arbitrary leading space (because
705 ;; they were aligned).
706 (concat "\\s-*"
707 (if multi (concat (regexp-quote (string c)) "*"))
708 (regexp-quote s))))))
83b96b22 709
be83ecb2 710;;;###autoload
7a0a180a 711(defun uncomment-region (beg end &optional arg)
eb0b51f8 712 "Uncomment each line in the BEG .. END region.
f5215400
SM
713The numeric prefix ARG can specify a number of chars to remove from the
714comment markers."
83b96b22
SM
715 (interactive "*r\nP")
716 (comment-normalize-vars)
065b7364 717 (when (> beg end) (setq beg (prog1 end (setq end beg))))
b70dd952
SM
718 ;; Bind `comment-use-global-state' to nil. While uncommenting a region
719 ;; (which works a line at a time), a comment can appear to be
cbdad6e2
EZ
720 ;; included in a mult-line string, but it is actually not.
721 (let ((comment-use-global-state nil))
722 (save-excursion
b70dd952
SM
723 (funcall uncomment-region-function beg end arg))))
724
725(defun uncomment-region-default (beg end &optional arg)
726 "Uncomment each line in the BEG .. END region.
727The numeric prefix ARG can specify a number of chars to remove from the
728comment markers."
729 (goto-char beg)
730 (setq end (copy-marker end))
731 (let* ((numarg (prefix-numeric-value arg))
732 (ccs comment-continue)
733 (srei (comment-padright ccs 're))
734 (csre (comment-padright comment-start 're))
735 (sre (and srei (concat "^\\s-*?\\(" srei "\\)")))
736 spt)
737 (while (and (< (point) end)
738 (setq spt (comment-search-forward end t)))
739 (let ((ipt (point))
740 ;; Find the end of the comment.
741 (ept (progn
742 (goto-char spt)
743 (unless (or (comment-forward)
744 ;; Allow non-terminated comments.
745 (eobp))
746 (error "Can't find the comment end"))
747 (point)))
748 (box nil)
749 (box-equal nil)) ;Whether we might be using `=' for boxes.
750 (save-restriction
751 (narrow-to-region spt ept)
cbdad6e2 752
b70dd952
SM
753 ;; Remove the comment-start.
754 (goto-char ipt)
755 (skip-syntax-backward " ")
756 ;; A box-comment starts with a looong comment-start marker.
757 (when (and (or (and (= (- (point) (point-min)) 1)
758 (setq box-equal t)
759 (looking-at "=\\{7\\}")
760 (not (eq (char-before (point-max)) ?\n))
761 (skip-chars-forward "="))
762 (> (- (point) (point-min) (length comment-start)) 7))
763 (> (count-lines (point-min) (point-max)) 2))
764 (setq box t))
765 ;; Skip the padding. Padding can come from comment-padding and/or
766 ;; from comment-start, so we first check comment-start.
767 (if (or (save-excursion (goto-char (point-min)) (looking-at csre))
768 (looking-at (regexp-quote comment-padding)))
769 (goto-char (match-end 0)))
770 (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei)))
771 (goto-char (match-end 0)))
772 (if (null arg) (delete-region (point-min) (point))
773 (skip-syntax-backward " ")
774 (delete-char (- numarg))
775 (unless (or (bobp)
776 (save-excursion (goto-char (point-min))
777 (looking-at comment-start-skip)))
778 ;; If there's something left but it doesn't look like
779 ;; a comment-start any more, just remove it.
780 (delete-region (point-min) (point))))
cbdad6e2 781
b70dd952
SM
782 ;; Remove the end-comment (and leading padding and such).
783 (goto-char (point-max)) (comment-enter-backward)
784 ;; Check for special `=' used sometimes in comment-box.
785 (when (and box-equal (not (eq (char-before (point-max)) ?\n)))
786 (let ((pos (point)))
787 ;; skip `=' but only if there are at least 7.
788 (when (> (skip-chars-backward "=") -7) (goto-char pos))))
789 (unless (looking-at "\\(\n\\|\\s-\\)*\\'")
790 (when (and (bolp) (not (bobp))) (backward-char))
791 (if (null arg) (delete-region (point) (point-max))
792 (skip-syntax-forward " ")
793 (delete-char numarg)
794 (unless (or (eobp) (looking-at comment-end-skip))
795 ;; If there's something left but it doesn't look like
796 ;; a comment-end any more, just remove it.
797 (delete-region (point) (point-max)))))
798
799 ;; Unquote any nested end-comment.
800 (comment-quote-nested comment-start comment-end t)
801
802 ;; Eliminate continuation markers as well.
803 (when sre
804 (let* ((cce (comment-string-reverse (or comment-continue
805 comment-start)))
806 (erei (and box (comment-padleft cce 're)))
807 (ere (and erei (concat "\\(" erei "\\)\\s-*$"))))
808 (goto-char (point-min))
809 (while (progn
810 (if (and ere (re-search-forward
811 ere (line-end-position) t))
812 (replace-match "" t t nil (if (match-end 2) 2 1))
813 (setq ere nil))
814 (forward-line 1)
815 (re-search-forward sre (line-end-position) t))
816 (replace-match "" t t nil (if (match-end 2) 2 1)))))
817 ;; Go to the end for the next comment.
818 (goto-char (point-max))))))
819 (set-marker end nil))
83b96b22 820
aac88001 821(defun comment-make-extra-lines (cs ce ccs cce min-indent max-indent &optional block)
ad679e45
SM
822 "Make the leading and trailing extra lines.
823This is used for `extra-line' style (or `box' style if BLOCK is specified)."
9b0d1d6e
SM
824 (let ((eindent 0))
825 (if (not block)
826 ;; Try to match CS and CE's content so they align aesthetically.
827 (progn
828 (setq ce (comment-string-strip ce t t))
829 (when (string-match "\\(.+\\).*\n\\(.*?\\)\\1" (concat ce "\n" cs))
830 (setq eindent
831 (max (- (match-end 2) (match-beginning 2) (match-beginning 0))
832 0))))
833 ;; box comment
834 (let* ((width (- max-indent min-indent))
835 (s (concat cs "a=m" cce))
836 (e (concat ccs "a=m" ce))
837 (c (if (string-match ".*\\S-\\S-" cs)
3674a4a9
SM
838 (aref cs (1- (match-end 0)))
839 (if (and (equal comment-end "") (string-match ".*\\S-" cs))
840 (aref cs (1- (match-end 0))) ?=)))
841 (re "\\s-*a=m\\s-*")
842 (_ (string-match re s))
843 (lcs (length cs))
9b0d1d6e
SM
844 (fill
845 (make-string (+ width (- (match-end 0)
3674a4a9 846 (match-beginning 0) lcs 3)) c)))
aac88001 847 (setq cs (replace-match fill t t s))
3674a4a9
SM
848 (when (and (not (string-match comment-start-skip cs))
849 (string-match "a=m" s))
850 ;; The whitespace around CS cannot be ignored: put it back.
851 (setq re "a=m")
852 (setq fill (make-string (- width lcs) c))
853 (setq cs (replace-match fill t t s)))
854 (string-match re e)
9b0d1d6e
SM
855 (setq ce (replace-match fill t t e))))
856 (cons (concat cs "\n" (make-string min-indent ? ) ccs)
857 (concat cce "\n" (make-string (+ min-indent eindent) ? ) ce))))
aac88001 858
aac88001
SM
859(defmacro comment-with-narrowing (beg end &rest body)
860 "Execute BODY with BEG..END narrowing.
861Space is added (and then removed) at the beginning for the text's
862indentation to be kept as it was before narrowing."
4745e738 863 (declare (debug t) (indent 2))
59a1ce8d 864 (let ((bindent (make-symbol "bindent")))
bfe6e13f 865 `(let ((,bindent (save-excursion (goto-char ,beg) (current-column))))
59a1ce8d 866 (save-restriction
bfe6e13f 867 (narrow-to-region ,beg ,end)
59a1ce8d
SM
868 (goto-char (point-min))
869 (insert (make-string ,bindent ? ))
870 (prog1
871 (progn ,@body)
872 ;; remove the bindent
873 (save-excursion
874 (goto-char (point-min))
875 (when (looking-at " *")
876 (let ((n (min (- (match-end 0) (match-beginning 0)) ,bindent)))
aac88001 877 (delete-char n)
59a1ce8d
SM
878 (setq ,bindent (- ,bindent n))))
879 (end-of-line)
880 (let ((e (point)))
881 (beginning-of-line)
882 (while (and (> ,bindent 0) (re-search-forward " *" e t))
883 (let ((n (min ,bindent (- (match-end 0) (match-beginning 0) 1))))
884 (goto-char (match-beginning 0))
885 (delete-char n)
886 (setq ,bindent (- ,bindent n)))))))))))
aac88001 887
771c9b97 888(defun comment-region-internal (beg end cs ce
aa417798 889 &optional ccs cce block lines indent)
3674a4a9 890 "Comment region BEG .. END.
aa417798
JB
891CS and CE are the comment start string and comment end string,
892respectively. CCS and CCE are the comment continuation strings
893for the start and end of lines, respectively (default to CS and CE).
894BLOCK indicates that end of lines should be marked with either CCE,
895CE or CS \(if CE is empty) and that those markers should be aligned.
896LINES indicates that an extra lines will be used at the beginning
897and end of the region for CE and CS.
898INDENT indicates to put CS and CCS at the current indentation of
899the region rather than at left margin."
59a1ce8d 900 ;;(assert (< beg end))
6976bf99
SM
901 (let ((no-empty (not (or (eq comment-empty-lines t)
902 (and comment-empty-lines (zerop (length ce)))))))
f5215400 903 ;; Sanitize CE and CCE.
83b96b22
SM
904 (if (and (stringp ce) (string= "" ce)) (setq ce nil))
905 (if (and (stringp cce) (string= "" cce)) (setq cce nil))
f5215400
SM
906 ;; If CE is empty, multiline cannot be used.
907 (unless ce (setq ccs nil cce nil))
908 ;; Should we mark empty lines as well ?
83b96b22 909 (if (or ccs block lines) (setq no-empty nil))
f5215400 910 ;; Make sure we have end-markers for BLOCK mode.
2ab98065 911 (when block (unless ce (setq ce (comment-string-reverse cs))))
f5215400
SM
912 ;; If BLOCK is not requested, we don't need CCE.
913 (unless block (setq cce nil))
914 ;; Continuation defaults to the same as CS and CE.
915 (unless ccs (setq ccs cs cce ce))
f1180544 916
83b96b22 917 (save-excursion
aac88001 918 (goto-char end)
f5215400
SM
919 ;; If the end is not at the end of a line and the comment-end
920 ;; is implicit (i.e. a newline), explicitly insert a newline.
aac88001
SM
921 (unless (or ce (eolp)) (insert "\n") (indent-according-to-mode))
922 (comment-with-narrowing beg end
f5215400 923 (let ((min-indent (point-max))
9d5240d2 924 (max-indent 0))
83b96b22 925 (goto-char (point-min))
f5215400
SM
926 ;; Quote any nested comment marker
927 (comment-quote-nested comment-start comment-end nil)
928
929 ;; Loop over all lines to find the needed indentations.
4125ec7e 930 (goto-char (point-min))
f5215400
SM
931 (while
932 (progn
933 (unless (looking-at "[ \t]*$")
934 (setq min-indent (min min-indent (current-indentation))))
935 (end-of-line)
936 (setq max-indent (max max-indent (current-column)))
937 (not (or (eobp) (progn (forward-line) nil)))))
f1180544 938
f5215400 939 ;; Inserting ccs can change max-indent by (1- tab-width).
59a1ce8d
SM
940 (setq max-indent
941 (+ max-indent (max (length cs) (length ccs)) tab-width -1))
771c9b97 942 (unless indent (setq min-indent 0))
83b96b22 943
aac88001 944 ;; make the leading and trailing lines if requested
83b96b22 945 (when lines
771c9b97
SM
946 (let ((csce
947 (comment-make-extra-lines
948 cs ce ccs cce min-indent max-indent block)))
949 (setq cs (car csce))
950 (setq ce (cdr csce))))
f1180544 951
83b96b22
SM
952 (goto-char (point-min))
953 ;; Loop over all lines from BEG to END.
f5215400
SM
954 (while
955 (progn
956 (unless (and no-empty (looking-at "[ \t]*$"))
957 (move-to-column min-indent t)
958 (insert cs) (setq cs ccs) ;switch to CCS after the first line
959 (end-of-line)
960 (if (eobp) (setq cce ce))
961 (when cce
962 (when block (move-to-column max-indent t))
963 (insert cce)))
964 (end-of-line)
965 (not (or (eobp) (progn (forward-line) nil))))))))))
83b96b22 966
be83ecb2 967;;;###autoload
83b96b22
SM
968(defun comment-region (beg end &optional arg)
969 "Comment or uncomment each line in the region.
3674a4a9 970With just \\[universal-argument] prefix arg, uncomment each line in region BEG .. END.
11a26e05 971Numeric prefix ARG means use ARG comment characters.
83b96b22 972If ARG is negative, delete that many comment characters instead.
be83ecb2
SM
973By default, comments start at the left margin, are terminated on each line,
974even for syntax in which newline does not end the comment and blank lines
975do not get comments. This can be changed with `comment-style'.
83b96b22
SM
976
977The strings used as comment starts are built from
978`comment-start' without trailing spaces and `comment-padding'."
979 (interactive "*r\nP")
980 (comment-normalize-vars)
981 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
b70dd952
SM
982 (save-excursion
983 ;; FIXME: maybe we should call uncomment depending on ARG.
984 (funcall comment-region-function beg end arg)))
985
986(defun comment-region-default (beg end &optional arg)
2ab98065 987 (let* ((numarg (prefix-numeric-value arg))
771c9b97 988 (add comment-add)
2ab98065
SM
989 (style (cdr (assoc comment-style comment-styles)))
990 (lines (nth 2 style))
991 (block (nth 1 style))
992 (multi (nth 0 style)))
b70dd952
SM
993 ;; we use `chars' instead of `syntax' because `\n' might be
994 ;; of end-comment syntax rather than of whitespace syntax.
995 ;; sanitize BEG and END
996 (goto-char beg) (skip-chars-forward " \t\n\r") (beginning-of-line)
997 (setq beg (max beg (point)))
998 (goto-char end) (skip-chars-backward " \t\n\r") (end-of-line)
999 (setq end (min end (point)))
1000 (if (>= beg end) (error "Nothing to comment"))
1001
1002 ;; sanitize LINES
1003 (setq lines
1004 (and
1005 lines ;; multi
1006 (progn (goto-char beg) (beginning-of-line)
1007 (skip-syntax-forward " ")
1008 (>= (point) beg))
1009 (progn (goto-char end) (end-of-line) (skip-syntax-backward " ")
1010 (<= (point) end))
1011 (or block (not (string= "" comment-end)))
1012 (or block (progn (goto-char beg) (search-forward "\n" end t)))))
1013
1014 ;; don't add end-markers just because the user asked for `block'
1015 (unless (or lines (string= "" comment-end)) (setq block nil))
1016
1017 (cond
1018 ((consp arg) (uncomment-region beg end))
1019 ((< numarg 0) (uncomment-region beg end (- numarg)))
1020 (t
1021 (setq numarg (if (and (null arg) (= (length comment-start) 1))
1022 add (1- numarg)))
1023 (comment-region-internal
1024 beg end
1025 (let ((s (comment-padright comment-start numarg)))
1026 (if (string-match comment-start-skip s) s
1027 (comment-padright comment-start)))
1028 (let ((s (comment-padleft comment-end numarg)))
1029 (and s (if (string-match comment-end-skip s) s
1030 (comment-padright comment-end))))
1031 (if multi (comment-padright comment-continue numarg))
1032 (if multi
1033 (comment-padleft (comment-string-reverse comment-continue) numarg))
1034 block
1035 lines
1036 (nth 3 style))))))
771c9b97
SM
1037
1038(defun comment-box (beg end &optional arg)
3674a4a9 1039 "Comment out the BEG .. END region, putting it inside a box.
771c9b97
SM
1040The numeric prefix ARG specifies how many characters to add to begin- and
1041end- comment markers additionally to what `comment-add' already specifies."
1042 (interactive "*r\np")
9b0d1d6e
SM
1043 (let ((comment-style (if (cadr (assoc comment-style comment-styles))
1044 'box-multi 'box)))
771c9b97 1045 (comment-region beg end (+ comment-add arg))))
83b96b22 1046
88fe06af
SM
1047
1048;;;###autoload
1049(defun comment-or-uncomment-region (beg end &optional arg)
1050 "Call `comment-region', unless the region only consists of comments,
1051in which case call `uncomment-region'. If a prefix arg is given, it
1052is passed on to the respective function."
1053 (interactive "*r\nP")
2abd0306 1054 (comment-normalize-vars)
88fe06af
SM
1055 (funcall (if (save-excursion ;; check for already commented region
1056 (goto-char beg)
1057 (comment-forward (point-max))
1058 (<= end (point)))
1059 'uncomment-region 'comment-region)
1060 beg end arg))
1061
be83ecb2 1062;;;###autoload
2ab98065 1063(defun comment-dwim (arg)
ad679e45
SM
1064 "Call the comment command you want (Do What I Mean).
1065If the region is active and `transient-mark-mode' is on, call
39cb51e7 1066 `comment-region' (unless it only consists of comments, in which
ad679e45 1067 case it calls `uncomment-region').
2ab98065 1068Else, if the current line is empty, insert a comment and indent it.
ad679e45
SM
1069Else if a prefix ARG is specified, call `comment-kill'.
1070Else, call `comment-indent'."
2ab98065
SM
1071 (interactive "*P")
1072 (comment-normalize-vars)
771c9b97 1073 (if (and mark-active transient-mark-mode)
88fe06af 1074 (comment-or-uncomment-region (region-beginning) (region-end) arg)
2ab98065 1075 (if (save-excursion (beginning-of-line) (not (looking-at "\\s-*$")))
ad679e45
SM
1076 ;; FIXME: If there's no comment to kill on this line and ARG is
1077 ;; specified, calling comment-kill is not very clever.
1078 (if arg (comment-kill (and (integerp arg) arg)) (comment-indent))
2ab98065 1079 (let ((add (if arg (prefix-numeric-value arg)
771c9b97 1080 (if (= (length comment-start) 1) comment-add 0))))
2e1fbba4
SM
1081 ;; Some modes insist on keeping column 0 comment in column 0
1082 ;; so we need to move away from it before inserting the comment.
1083 (indent-according-to-mode)
2ab98065
SM
1084 (insert (comment-padright comment-start add))
1085 (save-excursion
1086 (unless (string= "" comment-end)
1087 (insert (comment-padleft comment-end add)))
1088 (indent-according-to-mode))))))
1089
1fc075d8 1090;;;###autoload
392f1ef5
SM
1091(defcustom comment-auto-fill-only-comments nil
1092 "Non-nil means to only auto-fill inside comments.
1093This has no effect in modes that do not define a comment syntax."
0603917d 1094 :type 'boolean)
392f1ef5 1095
bfe6e13f 1096(defun comment-valid-prefix-p (prefix compos)
7164ef13
SM
1097 (or
1098 ;; Accept any prefix if the current comment is not EOL-terminated.
1099 (save-excursion (goto-char compos) (comment-forward) (not (bolp)))
1100 ;; Accept any prefix that starts with a comment-start marker.
1101 (string-match (concat "\\`[ \t]*\\(?:" comment-start-skip "\\)")
bfe6e13f 1102 prefix)))
7164ef13 1103
be83ecb2 1104;;;###autoload
ad679e45 1105(defun comment-indent-new-line (&optional soft)
2ab98065
SM
1106 "Break line at point and indent, continuing comment if within one.
1107This indents the body of the continued comment
1108under the previous comment line.
1109
1110This command is intended for styles where you write a comment per line,
1111starting a new comment (and terminating it if necessary) on each line.
1112If you want to continue one comment across several lines, use \\[newline-and-indent].
1113
1114If a fill column is specified, it overrides the use of the comment column
1115or comment indentation.
1116
1117The inserted newline is marked hard if variable `use-hard-newlines' is true,
1118unless optional argument SOFT is non-nil."
1119 (interactive)
1120 (comment-normalize-vars t)
59a1ce8d
SM
1121 (let (compos comin)
1122 ;; If we are not inside a comment and we only auto-fill comments,
1123 ;; don't do anything (unless no comment syntax is defined).
392f1ef5
SM
1124 (unless (and comment-start
1125 comment-auto-fill-only-comments
0603917d 1126 (not (interactive-p))
392f1ef5 1127 (not (save-excursion
59a1ce8d 1128 (prog1 (setq compos (comment-beginning))
392f1ef5 1129 (setq comin (point))))))
59a1ce8d
SM
1130
1131 ;; Now we know we should auto-fill.
88fe06af
SM
1132 ;; Insert the newline before removing empty space so that markers
1133 ;; get preserved better.
392f1ef5 1134 (if soft (insert-and-inherit ?\n) (newline 1))
88fe06af
SM
1135 (save-excursion (forward-char -1) (delete-horizontal-space))
1136 (delete-horizontal-space)
1137
7164ef13
SM
1138 (if (and fill-prefix (not adaptive-fill-mode))
1139 ;; Blindly trust a non-adaptive fill-prefix.
392f1ef5
SM
1140 (progn
1141 (indent-to-left-margin)
88fe06af 1142 (insert-before-markers-and-inherit fill-prefix))
59a1ce8d
SM
1143
1144 ;; If necessary check whether we're inside a comment.
a764440a 1145 (unless (or compos (null comment-start))
392f1ef5
SM
1146 (save-excursion
1147 (backward-char)
59a1ce8d
SM
1148 (setq compos (comment-beginning))
1149 (setq comin (point))))
1150
7164ef13
SM
1151 (cond
1152 ;; If there's an adaptive prefix, use it unless we're inside
1153 ;; a comment and the prefix is not a comment starter.
1154 ((and fill-prefix
1155 (or (not compos)
bfe6e13f 1156 (comment-valid-prefix-p fill-prefix compos)))
7164ef13
SM
1157 (indent-to-left-margin)
1158 (insert-and-inherit fill-prefix))
1159 ;; If we're not inside a comment, just try to indent.
1160 ((not compos) (indent-according-to-mode))
1161 (t
59a1ce8d
SM
1162 (let* ((comment-column
1163 ;; The continuation indentation should be somewhere between
1164 ;; the current line's indentation (plus 2 for good measure)
1165 ;; and the current comment's indentation, with a preference
1166 ;; for comment-column.
1167 (save-excursion
a764440a 1168 ;; FIXME: use prev line's info rather than first line's.
59a1ce8d
SM
1169 (goto-char compos)
1170 (min (current-column) (max comment-column
1171 (+ 2 (current-indentation))))))
1172 (comstart (buffer-substring compos comin))
1173 (normalp
1174 (string-match (regexp-quote (comment-string-strip
1175 comment-start t t))
1176 comstart))
1177 (comment-end
1178 (if normalp comment-end
1179 ;; The comment starter is not the normal comment-start
1180 ;; so we can't just use comment-end.
1181 (save-excursion
1182 (goto-char compos)
1183 (if (not (comment-forward)) comment-end
1184 (comment-string-strip
1185 (buffer-substring
1186 (save-excursion (comment-enter-backward) (point))
1187 (point))
1188 nil t)))))
1189 (comment-start comstart)
a764440a
SM
1190 (continuep (or comment-multi-line
1191 (cadr (assoc comment-style comment-styles))))
59a1ce8d 1192 ;; Force comment-continue to be recreated from comment-start.
dde6824c 1193 ;; FIXME: wrong if comment-continue was set explicitly!
a764440a 1194 ;; FIXME: use prev line's continuation if available.
59a1ce8d 1195 (comment-continue nil))
a764440a
SM
1196 (if (and comment-multi-line (> (length comment-end) 0))
1197 (indent-according-to-mode)
1198 (insert-and-inherit ?\n)
1199 (forward-char -1)
1200 (comment-indent continuep)
1201 (save-excursion
1202 (let ((pt (point)))
1203 (end-of-line)
1204 (let ((comend (buffer-substring pt (point))))
1205 ;; The 1+ is to make sure we delete the \n inserted above.
1206 (delete-region pt (1+ (point)))
1207 (end-of-line 0)
1208 (insert comend))))))))))))
2ab98065 1209
83b96b22
SM
1210(provide 'newcomment)
1211
b70dd952 1212;; arch-tag: 01e3320a-00c8-44ea-a696-8f8e7354c858
83b96b22 1213;;; newcomment.el ends here