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