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