(fill) <defgroup>: Move to fill.el.
[bpt/emacs.git] / lisp / newcomment.el
CommitLineData
83b96b22
SM
1;;; newcomment.el --- (un)comment regions of buffers
2
2720770c 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
140(defvar comment-region-function nil
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
148(defvar uncomment-region-function nil
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
ac87b3a9 218 "*Non-nil means \\[comment-indent-new-line] continues comments, with no new terminator or starter.
3e569d22 219This is obsolete because you might as well use \\[newline-and-indent]."
0603917d 220 :type 'boolean)
3e569d22 221
6976bf99
SM
222(defcustom comment-empty-lines nil
223 "If nil, `comment-region' does not comment out empty lines.
224If t, it always comments out empty lines.
225if `eol' it only comments out empty lines if comments are
226terminated by the end of line (i.e. `comment-end' is empty)."
227 :type '(choice (const :tag "Never" nil)
228 (const :tag "Always" t)
229 (const :tag "EOl-terminated" 'eol)))
230
2ab98065
SM
231;;;;
232;;;; Helpers
233;;;;
234
f5215400
SM
235(defun comment-string-strip (str beforep afterp)
236 "Strip STR of any leading (if BEFOREP) and/or trailing (if AFTERP) space."
237 (string-match (concat "\\`" (if beforep "\\s-*")
ad679e45 238 "\\(.*?\\)" (if afterp "\\s-*\n?")
2ab98065
SM
239 "\\'") str)
240 (match-string 1 str))
241
242(defun comment-string-reverse (s)
f5215400 243 "Return the mirror image of string S, without any trailing space."
771c9b97 244 (comment-string-strip (concat (nreverse (string-to-list s))) nil t))
2ab98065 245
7164ef13 246;;;###autoload
2ab98065 247(defun comment-normalize-vars (&optional noerror)
0f21c5a0
SM
248 "Check and setup the variables needed by other commenting functions.
249Functions autoloaded from newcomment.el, being entry points, should call
250this function before any other, so the rest of the code can assume that
251the variables are properly set."
0d6a08f6
SM
252 (unless (and (not comment-start) noerror)
253 (unless comment-start
31df2900
SM
254 (let ((cs (read-string "No comment syntax is defined. Use: ")))
255 (if (zerop (length cs))
256 (error "No comment syntax defined")
257 (set (make-local-variable 'comment-start) cs))))
2ab98065 258 ;; comment-use-syntax
771c9b97 259 (when (eq comment-use-syntax 'undecided)
2ab98065
SM
260 (set (make-local-variable 'comment-use-syntax)
261 (let ((st (syntax-table))
262 (cs comment-start)
263 (ce (if (string= "" comment-end) "\n" comment-end)))
771c9b97
SM
264 ;; Try to skip over a comment using forward-comment
265 ;; to see if the syntax tables properly recognize it.
2ab98065
SM
266 (with-temp-buffer
267 (set-syntax-table st)
268 (insert cs " hello " ce)
269 (goto-char (point-min))
270 (and (forward-comment 1) (eobp))))))
2ab98065 271 ;; comment-padding
9145f1c2 272 (unless comment-padding (setq comment-padding 0))
2ab98065
SM
273 (when (integerp comment-padding)
274 (setq comment-padding (make-string comment-padding ? )))
275 ;; comment markers
276 ;;(setq comment-start (comment-string-strip comment-start t nil))
277 ;;(setq comment-end (comment-string-strip comment-end nil t))
278 ;; comment-continue
f5215400 279 (unless (or comment-continue (string= comment-end ""))
2ab98065 280 (set (make-local-variable 'comment-continue)
9b0d1d6e 281 (concat (if (string-match "\\S-\\S-" comment-start) " " "|")
7164ef13
SM
282 (substring comment-start 1)))
283 ;; Hasn't been necessary yet.
284 ;; (unless (string-match comment-start-skip comment-continue)
285 ;; (kill-local-variable 'comment-continue))
286 )
2ab98065 287 ;; comment-skip regexps
f23a0f3a
SM
288 (unless (and comment-start-skip
289 ;; In case comment-start has changed since last time.
290 (string-match comment-start-skip comment-start))
2ab98065 291 (set (make-local-variable 'comment-start-skip)
f0a478be 292 (concat "\\(\\(^\\|[^\\\n]\\)\\(\\\\\\\\\\)*\\)\\(\\s<+\\|"
2ab98065 293 (regexp-quote (comment-string-strip comment-start t t))
75ed43a6
SM
294 ;; Let's not allow any \s- but only [ \t] since \n
295 ;; might be both a comment-end marker and \s-.
296 "+\\)[ \t]*")))
f23a0f3a
SM
297 (unless (and comment-end-skip
298 ;; In case comment-end has changed since last time.
299 (string-match comment-end-skip comment-end))
2ab98065
SM
300 (let ((ce (if (string= "" comment-end) "\n"
301 (comment-string-strip comment-end t t))))
302 (set (make-local-variable 'comment-end-skip)
37dbd369
SM
303 ;; We use [ \t] rather than \s- because we don't want to
304 ;; remove ^L in C mode when uncommenting.
305 (concat "[ \t]*\\(\\s>" (if comment-quote-nested "" "+")
2ab98065 306 "\\|" (regexp-quote (substring ce 0 1))
771c9b97 307 (if (and comment-quote-nested (<= (length ce) 1)) "" "+")
2ab98065
SM
308 (regexp-quote (substring ce 1))
309 "\\)"))))))
f1180544 310
f5215400
SM
311(defun comment-quote-re (str unp)
312 (concat (regexp-quote (substring str 0 1))
313 "\\\\" (if unp "+" "*")
314 (regexp-quote (substring str 1))))
315
316(defun comment-quote-nested (cs ce unp)
317 "Quote or unquote nested comments.
318If UNP is non-nil, unquote nested comment markers."
319 (setq cs (comment-string-strip cs t t))
320 (setq ce (comment-string-strip ce t t))
321 (when (and comment-quote-nested (> (length ce) 0))
322 (let ((re (concat (comment-quote-re ce unp)
323 "\\|" (comment-quote-re cs unp))))
324 (goto-char (point-min))
325 (while (re-search-forward re nil t)
326 (goto-char (match-beginning 0))
327 (forward-char 1)
328 (if unp (delete-char 1) (insert "\\"))
329 (when (= (length ce) 1)
330 ;; If the comment-end is a single char, adding a \ after that
331 ;; "first" char won't deactivate it, so we turn such a CE
332 ;; into !CS. I.e. for pascal, we turn } into !{
333 (if (not unp)
334 (when (string= (match-string 0) ce)
335 (replace-match (concat "!" cs) t t))
336 (when (and (< (point-min) (match-beginning 0))
337 (string= (buffer-substring (1- (match-beginning 0))
338 (1- (match-end 0)))
339 (concat "!" cs)))
340 (backward-char 2)
341 (delete-char (- (match-end 0) (match-beginning 0)))
342 (insert ce))))))))
2ab98065
SM
343
344;;;;
345;;;; Navigation
346;;;;
347
ad679e45 348(defun comment-search-forward (limit &optional noerror)
771c9b97
SM
349 "Find a comment start between point and LIMIT.
350Moves point to inside the comment and returns the position of the
351comment-starter. If no comment is found, moves point to LIMIT
e8fe7d39 352and raises an error or returns nil of NOERROR is non-nil."
2ab98065 353 (if (not comment-use-syntax)
9b0d1d6e
SM
354 (if (re-search-forward comment-start-skip limit noerror)
355 (or (match-end 1) (match-beginning 0))
356 (goto-char limit)
357 (unless noerror (error "No comment")))
ad679e45
SM
358 (let* ((pt (point))
359 ;; Assume (at first) that pt is outside of any string.
360 (s (parse-partial-sexp pt (or limit (point-max)) nil nil nil t)))
361 (when (and (nth 8 s) (nth 3 s))
362 ;; The search ended inside a string. Try to see if it
363 ;; works better when we assume that pt is inside a string.
364 (setq s (parse-partial-sexp
365 pt (or limit (point-max)) nil nil
366 (list nil nil nil (nth 3 s) nil nil nil nil)
367 t)))
368 (if (not (and (nth 8 s) (not (nth 3 s))))
369 (unless noerror (error "No comment"))
370 ;; We found the comment.
9b0d1d6e 371 (let ((pos (point))
ad679e45 372 (start (nth 8 s))
9b0d1d6e 373 (bol (line-beginning-position))
ad679e45
SM
374 (end nil))
375 (while (and (null end) (>= (point) bol))
376 (if (looking-at comment-start-skip)
377 (setq end (min (or limit (point-max)) (match-end 0)))
378 (backward-char)))
9b0d1d6e 379 (goto-char (or end pos))
ad679e45 380 start)))))
2ab98065
SM
381
382(defun comment-search-backward (&optional limit noerror)
383 "Find a comment start between LIMIT and point.
771c9b97
SM
384Moves point to inside the comment and returns the position of the
385comment-starter. If no comment is found, moves point to LIMIT
2ab98065 386and raises an error or returns nil of NOERROR is non-nil."
ad679e45
SM
387 ;; FIXME: If a comment-start appears inside a comment, we may erroneously
388 ;; stop there. This can be rather bad in general, but since
389 ;; comment-search-backward is only used to find the comment-column (in
390 ;; comment-set-column) and to find the comment-start string (via
391 ;; comment-beginning) in indent-new-comment-line, it should be harmless.
2ab98065
SM
392 (if (not (re-search-backward comment-start-skip limit t))
393 (unless noerror (error "No comment"))
394 (beginning-of-line)
395 (let* ((end (match-end 0))
396 (cs (comment-search-forward end t))
397 (pt (point)))
398 (if (not cs)
399 (progn (beginning-of-line)
400 (comment-search-backward limit noerror))
401 (while (progn (goto-char cs)
402 (comment-forward)
403 (and (< (point) end)
404 (setq cs (comment-search-forward end t))))
405 (setq pt (point)))
406 (goto-char pt)
407 cs))))
408
409(defun comment-beginning ()
771c9b97
SM
410 "Find the beginning of the enclosing comment.
411Returns nil if not inside a comment, else moves point and returns
2ab98065 412the same as `comment-search-forward'."
75ed43a6
SM
413 ;; HACK ATTACK!
414 ;; We should really test `in-string-p' but that can be expensive.
415 (unless (eq (get-text-property (point) 'face) 'font-lock-string-face)
416 (let ((pt (point))
417 (cs (comment-search-backward nil t)))
418 (when cs
419 (if (save-excursion
420 (goto-char cs)
dde6824c
SM
421 (and
422 ;; For modes where comment-start and comment-end are the same,
423 ;; the search above may have found a `ce' rather than a `cs'.
424 (or (not (looking-at comment-end-skip))
425 ;; Maybe font-lock knows that it's a `cs'?
426 (eq (get-text-property (match-end 0) 'face)
427 'font-lock-comment-face)
428 (unless (eq (get-text-property (point) 'face)
429 'font-lock-comment-face)
430 ;; Let's assume it's a `cs' if we're on the same line.
431 (>= (line-end-position) pt)))
432 ;; Make sure that PT is not past the end of the comment.
433 (if (comment-forward 1) (> (point) pt) (eobp))))
75ed43a6
SM
434 cs
435 (goto-char pt)
436 nil)))))
2ab98065
SM
437
438(defun comment-forward (&optional n)
439 "Skip forward over N comments.
440Just like `forward-comment' but only for positive N
441and can use regexps instead of syntax."
442 (setq n (or n 1))
443 (if (< n 0) (error "No comment-backward")
444 (if comment-use-syntax (forward-comment n)
445 (while (> n 0)
59a1ce8d 446 (setq n
0f38a885
SM
447 (if (or (forward-comment 1)
448 (and (looking-at comment-start-skip)
449 (goto-char (match-end 0))
450 (re-search-forward comment-end-skip nil 'move)))
59a1ce8d 451 (1- n) -1)))
2ab98065
SM
452 (= n 0))))
453
454(defun comment-enter-backward ()
455 "Move from the end of a comment to the end of its content.
771c9b97 456Point is assumed to be just at the end of a comment."
2ab98065
SM
457 (if (bolp)
458 ;; comment-end = ""
459 (progn (backward-char) (skip-syntax-backward " "))
460 (let ((end (point)))
461 (beginning-of-line)
462 (save-restriction
463 (narrow-to-region (point) end)
ad679e45
SM
464 (if (re-search-forward (concat comment-end-skip "\\'") nil t)
465 (goto-char (match-beginning 0))
466 ;; comment-end-skip not found probably because it was not set right.
467 ;; Since \\s> should catch the single-char case, we'll blindly
468 ;; assume we're at the end of a two-char comment-end.
469 (goto-char (point-max))
470 (backward-char 2)
471 (skip-chars-backward (string (char-after)))
472 (skip-syntax-backward " "))))))
2ab98065
SM
473
474;;;;
475;;;; Commands
476;;;;
477
fae99944 478;;;###autoload
87cf8421
SM
479(defun comment-indent-default ()
480 "Default for `comment-indent-function'."
d3fcda22
SM
481 (if (and (looking-at "\\s<\\s<\\(\\s<\\)?")
482 (or (match-end 1) (/= (current-column) (current-indentation))))
483 0
87cf8421
SM
484 (when (or (/= (current-column) (current-indentation))
485 (and (> comment-add 0) (looking-at "\\s<\\S<")))
486 comment-column)))
487
be83ecb2 488;;;###autoload
3e569d22 489(defun comment-indent (&optional continue)
a71b3805 490 "Indent this line's comment to `comment-column', or insert an empty comment.
dde6824c 491If CONTINUE is non-nil, use the `comment-continue' markers if any."
83b96b22 492 (interactive "*")
ad679e45 493 (comment-normalize-vars)
83b96b22
SM
494 (let* ((empty (save-excursion (beginning-of-line)
495 (looking-at "[ \t]*$")))
f5215400 496 (starter (or (and continue comment-continue)
2ab98065 497 (and empty block-comment-start) comment-start))
f5215400 498 (ender (or (and continue comment-continue "")
2ab98065 499 (and empty block-comment-end) comment-end)))
2e1fbba4
SM
500 (unless starter (error "No comment syntax defined"))
501 (beginning-of-line)
502 (let* ((eolpos (line-end-position))
503 (begpos (comment-search-forward eolpos t))
504 cpos indent)
505 ;; An existing comment?
7a06b250
SM
506 (if begpos
507 (progn
508 (if (and (not (looking-at "[\t\n ]"))
509 (looking-at comment-end-skip))
510 ;; The comment is empty and we have skipped all its space
511 ;; and landed right before the comment-ender:
512 ;; Go back to the middle of the space.
513 (forward-char (/ (skip-chars-backward " \t") -2)))
514 (setq cpos (point-marker)))
2e1fbba4 515 ;; If none, insert one.
a71b3805
EZ
516 (if comment-insert-comment-function
517 (funcall comment-insert-comment-function)
2e1fbba4 518 (save-excursion
a71b3805
EZ
519 ;; Some `comment-indent-function's insist on not moving
520 ;; comments that are in column 0, so we first go to the
521 ;; likely target column.
567e961e 522 (indent-to comment-column)
e6bba95e
DL
523 ;; Ensure there's a space before the comment for things
524 ;; like sh where it matters (as well as being neater).
07f10bab 525 (unless (memq (char-before) '(nil ?\n ?\t ?\ ))
e6bba95e 526 (insert ?\ ))
07f10bab 527 (setq begpos (point))
bb304a7a 528 (insert starter)
2e1fbba4 529 (setq cpos (point-marker))
a71b3805 530 (insert ender))))
2e1fbba4
SM
531 (goto-char begpos)
532 ;; Compute desired indent.
053b8d35 533 (setq indent (save-excursion (funcall comment-indent-function)))
8cc313a3
SM
534 ;; If `indent' is nil and there's code before the comment, we can't
535 ;; use `indent-according-to-mode', so we default to comment-column.
536 (unless (or indent (save-excursion (skip-chars-backward " \t") (bolp)))
537 (setq indent comment-column))
2e1fbba4 538 (if (not indent)
bfe6e13f 539 ;; comment-indent-function refuses: delegate to line-indent.
2e1fbba4 540 (indent-according-to-mode)
8cc313a3 541 ;; If the comment is at the left of code, adjust the indentation.
a764440a 542 (unless (save-excursion (skip-chars-backward " \t") (bolp))
8cc313a3 543 ;; Avoid moving comments past the fill-column.
bfe6e13f 544 (let ((max (+ (current-column)
88fe06af 545 (- (or comment-fill-column fill-column)
bfe6e13f
SM
546 (save-excursion (end-of-line) (current-column))))))
547 (if (<= max indent)
548 (setq indent max) ;Don't move past the fill column.
549 ;; We can choose anywhere between indent..max.
550 ;; Let's try to align to a comment on the previous line.
8cc313a3
SM
551 (let ((other nil)
552 (min (max indent
553 (save-excursion (skip-chars-backward " \t")
554 (1+ (current-column))))))
bfe6e13f
SM
555 (save-excursion
556 (when (and (zerop (forward-line -1))
557 (setq other (comment-search-forward
558 (line-end-position) t)))
559 (goto-char other) (setq other (current-column))))
8cc313a3 560 (if (and other (<= other max) (>= other min))
bfe6e13f
SM
561 ;; There is a comment and it's in the range: bingo.
562 (setq indent other)
563 ;; Let's try to align to a comment on the next line, then.
564 (let ((other nil))
565 (save-excursion
566 (when (and (zerop (forward-line 1))
567 (setq other (comment-search-forward
568 (line-end-position) t)))
569 (goto-char other) (setq other (current-column))))
8cc313a3 570 (if (and other (<= other max) (> other min))
bfe6e13f
SM
571 ;; There is a comment and it's in the range: bingo.
572 (setq indent other))))))))
7164ef13 573 (unless (= (current-column) indent)
2e1fbba4 574 ;; If that's different from current, change it.
7164ef13 575 (delete-region (point) (progn (skip-chars-backward " \t") (point)))
2e1fbba4
SM
576 (indent-to (if (bolp) indent
577 (max indent (1+ (current-column)))))))
578 (goto-char cpos)
579 (set-marker cpos nil))))
83b96b22 580
be83ecb2 581;;;###autoload
3e569d22 582(defun comment-set-column (arg)
83b96b22 583 "Set the comment column based on point.
2ab98065 584With no ARG, set the comment column to the current column.
83b96b22
SM
585With just minus as arg, kill any comment on this line.
586With any other arg, set comment column to indentation of the previous comment
587 and then align or create a comment on this line at that column."
588 (interactive "P")
e8fe7d39 589 (cond
ad679e45 590 ((eq arg '-) (comment-kill nil))
e8fe7d39 591 (arg
2abd0306 592 (comment-normalize-vars)
e8fe7d39
SM
593 (save-excursion
594 (beginning-of-line)
2ab98065 595 (comment-search-backward)
e8fe7d39 596 (beginning-of-line)
ad679e45 597 (goto-char (comment-search-forward (line-end-position)))
83b96b22 598 (setq comment-column (current-column))
e8fe7d39 599 (message "Comment column set to %d" comment-column))
ad679e45 600 (comment-indent))
e8fe7d39 601 (t (setq comment-column (current-column))
83b96b22
SM
602 (message "Comment column set to %d" comment-column))))
603
be83ecb2 604;;;###autoload
3e569d22 605(defun comment-kill (arg)
7a0a180a
SM
606 "Kill the comment on this line, if any.
607With prefix ARG, kill comments on that many lines starting with this one."
608 (interactive "P")
2abd0306 609 (comment-normalize-vars)
ad679e45
SM
610 (dotimes (_ (prefix-numeric-value arg))
611 (save-excursion
612 (beginning-of-line)
613 (let ((cs (comment-search-forward (line-end-position) t)))
614 (when cs
615 (goto-char cs)
616 (skip-syntax-backward " ")
617 (setq cs (point))
618 (comment-forward)
619 (kill-region cs (if (bolp) (1- (point)) (point)))
620 (indent-according-to-mode))))
621 (if arg (forward-line 1))))
7a0a180a 622
2ab98065
SM
623(defun comment-padright (str &optional n)
624 "Construct a string composed of STR plus `comment-padding'.
f5215400 625It also adds N copies of the last non-whitespace chars of STR.
2ab98065 626If STR already contains padding, the corresponding amount is
f5215400
SM
627ignored from `comment-padding'.
628N defaults to 0.
771c9b97 629If N is `re', a regexp is returned instead, that would match
f5215400 630the string for any N."
2ab98065
SM
631 (setq n (or n 0))
632 (when (and (stringp str) (not (string= "" str)))
f5215400 633 ;; Separate the actual string from any leading/trailing padding
3e569d22 634 (string-match "\\`\\s-*\\(.*?\\)\\s-*\\'" str)
f5215400
SM
635 (let ((s (match-string 1 str)) ;actual string
636 (lpad (substring str 0 (match-beginning 1))) ;left padding
637 (rpad (concat (substring str (match-end 1)) ;original right padding
638 (substring comment-padding ;additional right padding
3e569d22 639 (min (- (match-end 0) (match-end 1))
9b0d1d6e
SM
640 (length comment-padding)))))
641 ;; We can only duplicate C if the comment-end has multiple chars
642 ;; or if comments can be nested, else the comment-end `}' would
643 ;; be turned into `}}}' where only the first ends the comment
644 ;; and the rest becomes bogus junk.
645 (multi (not (and comment-quote-nested
646 ;; comment-end is a single char
647 (string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
f5215400 648 (if (not (symbolp n))
9b0d1d6e 649 (concat lpad s (when multi (make-string n (aref str (1- (match-end 1))))) rpad)
f5215400
SM
650 ;; construct a regexp that would match anything from just S
651 ;; to any possible output of this function for any N.
652 (concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
653 lpad "") ;padding is not required
9b0d1d6e
SM
654 (regexp-quote s)
655 (when multi "+") ;the last char of S might be repeated
f5215400
SM
656 (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
657 rpad "")))))) ;padding is not required
2ab98065
SM
658
659(defun comment-padleft (str &optional n)
660 "Construct a string composed of `comment-padding' plus STR.
f5215400 661It also adds N copies of the first non-whitespace chars of STR.
2ab98065 662If STR already contains padding, the corresponding amount is
f5215400
SM
663ignored from `comment-padding'.
664N defaults to 0.
771c9b97 665If N is `re', a regexp is returned instead, that would match
2ab98065
SM
666 the string for any N."
667 (setq n (or n 0))
668 (when (and (stringp str) (not (string= "" str)))
f5215400 669 ;; Only separate the left pad because we assume there is no right pad.
2ab98065
SM
670 (string-match "\\`\\s-*" str)
671 (let ((s (substring str (match-end 0)))
672 (pad (concat (substring comment-padding
673 (min (- (match-end 0) (match-beginning 0))
674 (length comment-padding)))
675 (match-string 0 str)))
f5215400
SM
676 (c (aref str (match-end 0))) ;the first non-space char of STR
677 ;; We can only duplicate C if the comment-end has multiple chars
678 ;; or if comments can be nested, else the comment-end `}' would
679 ;; be turned into `}}}' where only the first ends the comment
680 ;; and the rest becomes bogus junk.
681 (multi (not (and comment-quote-nested
682 ;; comment-end is a single char
683 (string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
684 (if (not (symbolp n))
685 (concat pad (when multi (make-string n c)) s)
686 ;; Construct a regexp that would match anything from just S
687 ;; to any possible output of this function for any N.
688 ;; We match any number of leading spaces because this regexp will
689 ;; be used for uncommenting where we might want to remove
690 ;; uncomment markers with arbitrary leading space (because
691 ;; they were aligned).
692 (concat "\\s-*"
693 (if multi (concat (regexp-quote (string c)) "*"))
694 (regexp-quote s))))))
83b96b22 695
be83ecb2 696;;;###autoload
7a0a180a 697(defun uncomment-region (beg end &optional arg)
eb0b51f8 698 "Uncomment each line in the BEG .. END region.
f5215400
SM
699The numeric prefix ARG can specify a number of chars to remove from the
700comment markers."
83b96b22
SM
701 (interactive "*r\nP")
702 (comment-normalize-vars)
065b7364 703 (when (> beg end) (setq beg (prog1 end (setq end beg))))
83b96b22 704 (save-excursion
a71b3805
EZ
705 (if uncomment-region-function
706 (funcall uncomment-region-function beg end arg)
707 (goto-char beg)
708 (setq end (copy-marker end))
709 (let* ((numarg (prefix-numeric-value arg))
710 (ccs comment-continue)
711 (srei (comment-padright ccs 're))
712 (csre (comment-padright comment-start 're))
713 (sre (and srei (concat "^\\s-*?\\(" srei "\\)")))
714 spt)
715 (while (and (< (point) end)
716 (setq spt (comment-search-forward end t)))
717 (let ((ipt (point))
718 ;; Find the end of the comment.
719 (ept (progn
720 (goto-char spt)
2720770c
SM
721 (unless (or (comment-forward)
722 ;; Allow non-terminated comments.
723 (eobp))
a71b3805
EZ
724 (error "Can't find the comment end"))
725 (point)))
726 (box nil)
727 (box-equal nil)) ;Whether we might be using `=' for boxes.
728 (save-restriction
729 (narrow-to-region spt ept)
730
731 ;; Remove the comment-start.
732 (goto-char ipt)
2ab98065 733 (skip-syntax-backward " ")
f5215400 734 ;; A box-comment starts with a looong comment-start marker.
eb0b51f8
SM
735 (when (and (or (and (= (- (point) (point-min)) 1)
736 (setq box-equal t)
737 (looking-at "=\\{7\\}")
738 (not (eq (char-before (point-max)) ?\n))
739 (skip-chars-forward "="))
740 (> (- (point) (point-min) (length comment-start)) 7))
741 (> (count-lines (point-min) (point-max)) 2))
f5215400 742 (setq box t))
79ca2b11
SM
743 ;; Skip the padding. Padding can come from comment-padding and/or
744 ;; from comment-start, so we first check comment-start.
745 (if (or (save-excursion (goto-char (point-min)) (looking-at csre))
746 (looking-at (regexp-quote comment-padding)))
747 (goto-char (match-end 0)))
2ab98065
SM
748 (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei)))
749 (goto-char (match-end 0)))
3e569d22
SM
750 (if (null arg) (delete-region (point-min) (point))
751 (skip-syntax-backward " ")
a71b3805
EZ
752 (delete-char (- numarg))
753 (unless (or (bobp)
754 (save-excursion (goto-char (point-min))
755 (looking-at comment-start-skip)))
756 ;; If there's something left but it doesn't look like
757 ;; a comment-start any more, just remove it.
758 (delete-region (point-min) (point))))
759
760 ;; Remove the end-comment (and leading padding and such).
761 (goto-char (point-max)) (comment-enter-backward)
762 ;; Check for special `=' used sometimes in comment-box.
763 (when (and box-equal (not (eq (char-before (point-max)) ?\n)))
764 (let ((pos (point)))
765 ;; skip `=' but only if there are at least 7.
766 (when (> (skip-chars-backward "=") -7) (goto-char pos))))
767 (unless (looking-at "\\(\n\\|\\s-\\)*\\'")
768 (when (and (bolp) (not (bobp))) (backward-char))
769 (if (null arg) (delete-region (point) (point-max))
770 (skip-syntax-forward " ")
771 (delete-char numarg)
772 (unless (or (eobp) (looking-at comment-end-skip))
773 ;; If there's something left but it doesn't look like
774 ;; a comment-end any more, just remove it.
775 (delete-region (point) (point-max)))))
776
777 ;; Unquote any nested end-comment.
778 (comment-quote-nested comment-start comment-end t)
779
780 ;; Eliminate continuation markers as well.
781 (when sre
782 (let* ((cce (comment-string-reverse (or comment-continue
783 comment-start)))
784 (erei (and box (comment-padleft cce 're)))
785 (ere (and erei (concat "\\(" erei "\\)\\s-*$"))))
786 (goto-char (point-min))
787 (while (progn
788 (if (and ere (re-search-forward
789 ere (line-end-position) t))
790 (replace-match "" t t nil (if (match-end 2) 2 1))
791 (setq ere nil))
792 (forward-line 1)
793 (re-search-forward sre (line-end-position) t))
794 (replace-match "" t t nil (if (match-end 2) 2 1)))))
795 ;; Go to the end for the next comment.
796 (goto-char (point-max)))))))
797 (set-marker end nil)))
83b96b22 798
aac88001 799(defun comment-make-extra-lines (cs ce ccs cce min-indent max-indent &optional block)
ad679e45
SM
800 "Make the leading and trailing extra lines.
801This is used for `extra-line' style (or `box' style if BLOCK is specified)."
9b0d1d6e
SM
802 (let ((eindent 0))
803 (if (not block)
804 ;; Try to match CS and CE's content so they align aesthetically.
805 (progn
806 (setq ce (comment-string-strip ce t t))
807 (when (string-match "\\(.+\\).*\n\\(.*?\\)\\1" (concat ce "\n" cs))
808 (setq eindent
809 (max (- (match-end 2) (match-beginning 2) (match-beginning 0))
810 0))))
811 ;; box comment
812 (let* ((width (- max-indent min-indent))
813 (s (concat cs "a=m" cce))
814 (e (concat ccs "a=m" ce))
815 (c (if (string-match ".*\\S-\\S-" cs)
3674a4a9
SM
816 (aref cs (1- (match-end 0)))
817 (if (and (equal comment-end "") (string-match ".*\\S-" cs))
818 (aref cs (1- (match-end 0))) ?=)))
819 (re "\\s-*a=m\\s-*")
820 (_ (string-match re s))
821 (lcs (length cs))
9b0d1d6e
SM
822 (fill
823 (make-string (+ width (- (match-end 0)
3674a4a9 824 (match-beginning 0) lcs 3)) c)))
aac88001 825 (setq cs (replace-match fill t t s))
3674a4a9
SM
826 (when (and (not (string-match comment-start-skip cs))
827 (string-match "a=m" s))
828 ;; The whitespace around CS cannot be ignored: put it back.
829 (setq re "a=m")
830 (setq fill (make-string (- width lcs) c))
831 (setq cs (replace-match fill t t s)))
832 (string-match re e)
9b0d1d6e
SM
833 (setq ce (replace-match fill t t e))))
834 (cons (concat cs "\n" (make-string min-indent ? ) ccs)
835 (concat cce "\n" (make-string (+ min-indent eindent) ? ) ce))))
aac88001 836
aac88001
SM
837(defmacro comment-with-narrowing (beg end &rest body)
838 "Execute BODY with BEG..END narrowing.
839Space is added (and then removed) at the beginning for the text's
840indentation to be kept as it was before narrowing."
4745e738 841 (declare (debug t) (indent 2))
59a1ce8d 842 (let ((bindent (make-symbol "bindent")))
bfe6e13f 843 `(let ((,bindent (save-excursion (goto-char ,beg) (current-column))))
59a1ce8d 844 (save-restriction
bfe6e13f 845 (narrow-to-region ,beg ,end)
59a1ce8d
SM
846 (goto-char (point-min))
847 (insert (make-string ,bindent ? ))
848 (prog1
849 (progn ,@body)
850 ;; remove the bindent
851 (save-excursion
852 (goto-char (point-min))
853 (when (looking-at " *")
854 (let ((n (min (- (match-end 0) (match-beginning 0)) ,bindent)))
aac88001 855 (delete-char n)
59a1ce8d
SM
856 (setq ,bindent (- ,bindent n))))
857 (end-of-line)
858 (let ((e (point)))
859 (beginning-of-line)
860 (while (and (> ,bindent 0) (re-search-forward " *" e t))
861 (let ((n (min ,bindent (- (match-end 0) (match-beginning 0) 1))))
862 (goto-char (match-beginning 0))
863 (delete-char n)
864 (setq ,bindent (- ,bindent n)))))))))))
aac88001 865
771c9b97
SM
866(defun comment-region-internal (beg end cs ce
867 &optional ccs cce block lines indent)
3674a4a9 868 "Comment region BEG .. END.
4125ec7e
SM
869CS and CE are the comment start resp end string.
870CCS and CCE are the comment continuation strings for the start resp end
f5215400
SM
871of lines (default to CS and CE).
872BLOCK indicates that end of lines should be marked with either CCE, CE or CS
873\(if CE is empty) and that those markers should be aligned.
874LINES indicates that an extra lines will be used at the beginning and end
875of the region for CE and CS.
876INDENT indicates to put CS and CCS at the current indentation of the region
877rather than at left margin."
59a1ce8d 878 ;;(assert (< beg end))
6976bf99
SM
879 (let ((no-empty (not (or (eq comment-empty-lines t)
880 (and comment-empty-lines (zerop (length ce)))))))
f5215400 881 ;; Sanitize CE and CCE.
83b96b22
SM
882 (if (and (stringp ce) (string= "" ce)) (setq ce nil))
883 (if (and (stringp cce) (string= "" cce)) (setq cce nil))
f5215400
SM
884 ;; If CE is empty, multiline cannot be used.
885 (unless ce (setq ccs nil cce nil))
886 ;; Should we mark empty lines as well ?
83b96b22 887 (if (or ccs block lines) (setq no-empty nil))
f5215400 888 ;; Make sure we have end-markers for BLOCK mode.
2ab98065 889 (when block (unless ce (setq ce (comment-string-reverse cs))))
f5215400
SM
890 ;; If BLOCK is not requested, we don't need CCE.
891 (unless block (setq cce nil))
892 ;; Continuation defaults to the same as CS and CE.
893 (unless ccs (setq ccs cs cce ce))
f1180544 894
83b96b22 895 (save-excursion
aac88001 896 (goto-char end)
f5215400
SM
897 ;; If the end is not at the end of a line and the comment-end
898 ;; is implicit (i.e. a newline), explicitly insert a newline.
aac88001
SM
899 (unless (or ce (eolp)) (insert "\n") (indent-according-to-mode))
900 (comment-with-narrowing beg end
f5215400 901 (let ((min-indent (point-max))
9d5240d2 902 (max-indent 0))
83b96b22 903 (goto-char (point-min))
f5215400
SM
904 ;; Quote any nested comment marker
905 (comment-quote-nested comment-start comment-end nil)
906
907 ;; Loop over all lines to find the needed indentations.
4125ec7e 908 (goto-char (point-min))
f5215400
SM
909 (while
910 (progn
911 (unless (looking-at "[ \t]*$")
912 (setq min-indent (min min-indent (current-indentation))))
913 (end-of-line)
914 (setq max-indent (max max-indent (current-column)))
915 (not (or (eobp) (progn (forward-line) nil)))))
f1180544 916
f5215400 917 ;; Inserting ccs can change max-indent by (1- tab-width).
59a1ce8d
SM
918 (setq max-indent
919 (+ max-indent (max (length cs) (length ccs)) tab-width -1))
771c9b97 920 (unless indent (setq min-indent 0))
83b96b22 921
aac88001 922 ;; make the leading and trailing lines if requested
83b96b22 923 (when lines
771c9b97
SM
924 (let ((csce
925 (comment-make-extra-lines
926 cs ce ccs cce min-indent max-indent block)))
927 (setq cs (car csce))
928 (setq ce (cdr csce))))
f1180544 929
83b96b22
SM
930 (goto-char (point-min))
931 ;; Loop over all lines from BEG to END.
f5215400
SM
932 (while
933 (progn
934 (unless (and no-empty (looking-at "[ \t]*$"))
935 (move-to-column min-indent t)
936 (insert cs) (setq cs ccs) ;switch to CCS after the first line
937 (end-of-line)
938 (if (eobp) (setq cce ce))
939 (when cce
940 (when block (move-to-column max-indent t))
941 (insert cce)))
942 (end-of-line)
943 (not (or (eobp) (progn (forward-line) nil))))))))))
83b96b22 944
be83ecb2 945;;;###autoload
83b96b22
SM
946(defun comment-region (beg end &optional arg)
947 "Comment or uncomment each line in the region.
3674a4a9 948With just \\[universal-argument] prefix arg, uncomment each line in region BEG .. END.
83b96b22
SM
949Numeric prefix arg ARG means use ARG comment characters.
950If ARG is negative, delete that many comment characters instead.
be83ecb2
SM
951By default, comments start at the left margin, are terminated on each line,
952even for syntax in which newline does not end the comment and blank lines
953do not get comments. This can be changed with `comment-style'.
83b96b22
SM
954
955The strings used as comment starts are built from
956`comment-start' without trailing spaces and `comment-padding'."
957 (interactive "*r\nP")
958 (comment-normalize-vars)
959 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
2ab98065 960 (let* ((numarg (prefix-numeric-value arg))
771c9b97 961 (add comment-add)
2ab98065
SM
962 (style (cdr (assoc comment-style comment-styles)))
963 (lines (nth 2 style))
964 (block (nth 1 style))
965 (multi (nth 0 style)))
83b96b22 966 (save-excursion
a71b3805
EZ
967 (if comment-region-function
968 (funcall comment-region-function beg end arg)
969 ;; we use `chars' instead of `syntax' because `\n' might be
970 ;; of end-comment syntax rather than of whitespace syntax.
971 ;; sanitize BEG and END
972 (goto-char beg) (skip-chars-forward " \t\n\r") (beginning-of-line)
973 (setq beg (max beg (point)))
974 (goto-char end) (skip-chars-backward " \t\n\r") (end-of-line)
975 (setq end (min end (point)))
976 (if (>= beg end) (error "Nothing to comment"))
977
978 ;; sanitize LINES
979 (setq lines
980 (and
981 lines ;; multi
982 (progn (goto-char beg) (beginning-of-line)
983 (skip-syntax-forward " ")
984 (>= (point) beg))
985 (progn (goto-char end) (end-of-line) (skip-syntax-backward " ")
986 (<= (point) end))
987 (or block (not (string= "" comment-end)))
988 (or block (progn (goto-char beg) (search-forward "\n" end t))))))
989
990 ;; don't add end-markers just because the user asked for `block'
991 (unless (or lines (string= "" comment-end)) (setq block nil))
992
993 (cond
994 ((consp arg) (uncomment-region beg end))
995 ((< numarg 0) (uncomment-region beg end (- numarg)))
996 (t
997 (setq numarg (if (and (null arg) (= (length comment-start) 1))
998 add (1- numarg)))
999 (comment-region-internal
1000 beg end
1001 (let ((s (comment-padright comment-start numarg)))
1002 (if (string-match comment-start-skip s) s
1003 (comment-padright comment-start)))
1004 (let ((s (comment-padleft comment-end numarg)))
1005 (and s (if (string-match comment-end-skip s) s
1006 (comment-padright comment-end))))
1007 (if multi (comment-padright comment-continue numarg))
1008 (if multi
1009 (comment-padleft (comment-string-reverse comment-continue) numarg))
1010 block
1011 lines
1012 (nth 3 style)))))))
771c9b97
SM
1013
1014(defun comment-box (beg end &optional arg)
3674a4a9 1015 "Comment out the BEG .. END region, putting it inside a box.
771c9b97
SM
1016The numeric prefix ARG specifies how many characters to add to begin- and
1017end- comment markers additionally to what `comment-add' already specifies."
1018 (interactive "*r\np")
9b0d1d6e
SM
1019 (let ((comment-style (if (cadr (assoc comment-style comment-styles))
1020 'box-multi 'box)))
771c9b97 1021 (comment-region beg end (+ comment-add arg))))
83b96b22 1022
88fe06af
SM
1023
1024;;;###autoload
1025(defun comment-or-uncomment-region (beg end &optional arg)
1026 "Call `comment-region', unless the region only consists of comments,
1027in which case call `uncomment-region'. If a prefix arg is given, it
1028is passed on to the respective function."
1029 (interactive "*r\nP")
2abd0306 1030 (comment-normalize-vars)
88fe06af
SM
1031 (funcall (if (save-excursion ;; check for already commented region
1032 (goto-char beg)
1033 (comment-forward (point-max))
1034 (<= end (point)))
1035 'uncomment-region 'comment-region)
1036 beg end arg))
1037
be83ecb2 1038;;;###autoload
2ab98065 1039(defun comment-dwim (arg)
ad679e45
SM
1040 "Call the comment command you want (Do What I Mean).
1041If the region is active and `transient-mark-mode' is on, call
39cb51e7 1042 `comment-region' (unless it only consists of comments, in which
ad679e45 1043 case it calls `uncomment-region').
2ab98065 1044Else, if the current line is empty, insert a comment and indent it.
ad679e45
SM
1045Else if a prefix ARG is specified, call `comment-kill'.
1046Else, call `comment-indent'."
2ab98065
SM
1047 (interactive "*P")
1048 (comment-normalize-vars)
771c9b97 1049 (if (and mark-active transient-mark-mode)
88fe06af 1050 (comment-or-uncomment-region (region-beginning) (region-end) arg)
2ab98065 1051 (if (save-excursion (beginning-of-line) (not (looking-at "\\s-*$")))
ad679e45
SM
1052 ;; FIXME: If there's no comment to kill on this line and ARG is
1053 ;; specified, calling comment-kill is not very clever.
1054 (if arg (comment-kill (and (integerp arg) arg)) (comment-indent))
2ab98065 1055 (let ((add (if arg (prefix-numeric-value arg)
771c9b97 1056 (if (= (length comment-start) 1) comment-add 0))))
2e1fbba4
SM
1057 ;; Some modes insist on keeping column 0 comment in column 0
1058 ;; so we need to move away from it before inserting the comment.
1059 (indent-according-to-mode)
2ab98065
SM
1060 (insert (comment-padright comment-start add))
1061 (save-excursion
1062 (unless (string= "" comment-end)
1063 (insert (comment-padleft comment-end add)))
1064 (indent-according-to-mode))))))
1065
392f1ef5
SM
1066(defcustom comment-auto-fill-only-comments nil
1067 "Non-nil means to only auto-fill inside comments.
1068This has no effect in modes that do not define a comment syntax."
0603917d 1069 :type 'boolean)
392f1ef5 1070
bfe6e13f 1071(defun comment-valid-prefix-p (prefix compos)
7164ef13
SM
1072 (or
1073 ;; Accept any prefix if the current comment is not EOL-terminated.
1074 (save-excursion (goto-char compos) (comment-forward) (not (bolp)))
1075 ;; Accept any prefix that starts with a comment-start marker.
1076 (string-match (concat "\\`[ \t]*\\(?:" comment-start-skip "\\)")
bfe6e13f 1077 prefix)))
7164ef13 1078
be83ecb2 1079;;;###autoload
ad679e45 1080(defun comment-indent-new-line (&optional soft)
2ab98065
SM
1081 "Break line at point and indent, continuing comment if within one.
1082This indents the body of the continued comment
1083under the previous comment line.
1084
1085This command is intended for styles where you write a comment per line,
1086starting a new comment (and terminating it if necessary) on each line.
1087If you want to continue one comment across several lines, use \\[newline-and-indent].
1088
1089If a fill column is specified, it overrides the use of the comment column
1090or comment indentation.
1091
1092The inserted newline is marked hard if variable `use-hard-newlines' is true,
1093unless optional argument SOFT is non-nil."
1094 (interactive)
1095 (comment-normalize-vars t)
59a1ce8d
SM
1096 (let (compos comin)
1097 ;; If we are not inside a comment and we only auto-fill comments,
1098 ;; don't do anything (unless no comment syntax is defined).
392f1ef5
SM
1099 (unless (and comment-start
1100 comment-auto-fill-only-comments
0603917d 1101 (not (interactive-p))
392f1ef5 1102 (not (save-excursion
59a1ce8d 1103 (prog1 (setq compos (comment-beginning))
392f1ef5 1104 (setq comin (point))))))
59a1ce8d
SM
1105
1106 ;; Now we know we should auto-fill.
88fe06af
SM
1107 ;; Insert the newline before removing empty space so that markers
1108 ;; get preserved better.
392f1ef5 1109 (if soft (insert-and-inherit ?\n) (newline 1))
88fe06af
SM
1110 (save-excursion (forward-char -1) (delete-horizontal-space))
1111 (delete-horizontal-space)
1112
7164ef13
SM
1113 (if (and fill-prefix (not adaptive-fill-mode))
1114 ;; Blindly trust a non-adaptive fill-prefix.
392f1ef5
SM
1115 (progn
1116 (indent-to-left-margin)
88fe06af 1117 (insert-before-markers-and-inherit fill-prefix))
59a1ce8d
SM
1118
1119 ;; If necessary check whether we're inside a comment.
a764440a 1120 (unless (or compos (null comment-start))
392f1ef5
SM
1121 (save-excursion
1122 (backward-char)
59a1ce8d
SM
1123 (setq compos (comment-beginning))
1124 (setq comin (point))))
1125
7164ef13
SM
1126 (cond
1127 ;; If there's an adaptive prefix, use it unless we're inside
1128 ;; a comment and the prefix is not a comment starter.
1129 ((and fill-prefix
1130 (or (not compos)
bfe6e13f 1131 (comment-valid-prefix-p fill-prefix compos)))
7164ef13
SM
1132 (indent-to-left-margin)
1133 (insert-and-inherit fill-prefix))
1134 ;; If we're not inside a comment, just try to indent.
1135 ((not compos) (indent-according-to-mode))
1136 (t
59a1ce8d
SM
1137 (let* ((comment-column
1138 ;; The continuation indentation should be somewhere between
1139 ;; the current line's indentation (plus 2 for good measure)
1140 ;; and the current comment's indentation, with a preference
1141 ;; for comment-column.
1142 (save-excursion
a764440a 1143 ;; FIXME: use prev line's info rather than first line's.
59a1ce8d
SM
1144 (goto-char compos)
1145 (min (current-column) (max comment-column
1146 (+ 2 (current-indentation))))))
1147 (comstart (buffer-substring compos comin))
1148 (normalp
1149 (string-match (regexp-quote (comment-string-strip
1150 comment-start t t))
1151 comstart))
1152 (comment-end
1153 (if normalp comment-end
1154 ;; The comment starter is not the normal comment-start
1155 ;; so we can't just use comment-end.
1156 (save-excursion
1157 (goto-char compos)
1158 (if (not (comment-forward)) comment-end
1159 (comment-string-strip
1160 (buffer-substring
1161 (save-excursion (comment-enter-backward) (point))
1162 (point))
1163 nil t)))))
1164 (comment-start comstart)
a764440a
SM
1165 (continuep (or comment-multi-line
1166 (cadr (assoc comment-style comment-styles))))
59a1ce8d 1167 ;; Force comment-continue to be recreated from comment-start.
dde6824c 1168 ;; FIXME: wrong if comment-continue was set explicitly!
a764440a 1169 ;; FIXME: use prev line's continuation if available.
59a1ce8d 1170 (comment-continue nil))
a764440a
SM
1171 (if (and comment-multi-line (> (length comment-end) 0))
1172 (indent-according-to-mode)
1173 (insert-and-inherit ?\n)
1174 (forward-char -1)
1175 (comment-indent continuep)
1176 (save-excursion
1177 (let ((pt (point)))
1178 (end-of-line)
1179 (let ((comend (buffer-substring pt (point))))
1180 ;; The 1+ is to make sure we delete the \n inserted above.
1181 (delete-region pt (1+ (point)))
1182 (end-of-line 0)
1183 (insert comend))))))))))))
2ab98065 1184
83b96b22
SM
1185(provide 'newcomment)
1186
ab5796a9 1187;;; arch-tag: 01e3320a-00c8-44ea-a696-8f8e7354c858
83b96b22 1188;;; newcomment.el ends here