Docstring fixes.
[bpt/emacs.git] / lisp / newcomment.el
CommitLineData
83b96b22
SM
1;;; newcomment.el --- (un)comment regions of buffers
2
771c9b97 3;; Copyright (C) 1999-2000 Free Software Foundation Inc.
83b96b22 4
59a1ce8d 5;; Author: code extracted from Emacs-20's simple.el
771c9b97 6;; Maintainer: Stefan Monnier <monnier@cs.yale.edu>
83b96b22
SM
7;; Keywords: comment uncomment
8;; Version: $Name: $
bdbe3a89 9;; Revision: $Id: newcomment.el,v 1.19 2000/07/06 13:25:31 monnier Exp $
83b96b22 10
59a1ce8d
SM
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
83b96b22 14;; it under the terms of the GNU General Public License as published by
59a1ce8d
SM
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
83b96b22
SM
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
59a1ce8d 22
83b96b22 23;; You should have received a copy of the GNU General Public License
59a1ce8d
SM
24;; along with GNU Emacs; see the file COPYING. If not, write to the
25;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
83b96b22
SM
27
28;;; Commentary:
29
771c9b97 30;; A replacement for simple.el's comment-related functions.
83b96b22
SM
31
32;;; Bugs:
33
2ab98065
SM
34;; - single-char nestable comment-start can only do the "\\s<+" stuff
35;; if the corresponding closing marker happens to be right.
771c9b97 36;; - comment-box in TeXinfo generate bogus comments @ccccc@
3e569d22
SM
37;; - uncomment-region with a numeric argument can render multichar
38;; comment markers invalid.
3fc5b4e2
SM
39;; - comment-indent or comment-region when called inside a comment
40;; will happily break the surrounding comment.
41;; - comment-quote-nested will not (un)quote properly all nested comment
42;; markers if there are more than just comment-start and comment-end.
43;; For example, in Pascal where {...*) and (*...} are possible.
83b96b22
SM
44
45;;; Todo:
46
771c9b97
SM
47;; - try to align tail comments
48;; - check what c-comment-line-break-function has to say
3e569d22 49;; - spill auto-fill of comments onto the end of the next line
7a0a180a
SM
50;; - uncomment-region with a consp (for blocks) or somehow make the
51;; deletion of continuation markers less dangerous
2ab98065 52;; - drop block-comment-<foo> unless it's really used
f5215400 53;; - uncomment-region on a subpart of a comment
771c9b97 54;; - support gnu-style "multi-line with space in continue"
771c9b97
SM
55;; - somehow allow comment-dwim to use the region even if transient-mark-mode
56;; is not turned on.
83b96b22 57
bdbe3a89
SM
58;; - when auto-filling a comment, try to move the comment to the left
59;; rather than break it (if possible).
60;; - sometimes default the comment-column to the same
61;; one used on the preceding line(s).
62
83b96b22
SM
63;;; Code:
64
be83ecb2 65;;;###autoload
59a1ce8d 66(defalias 'indent-for-comment 'comment-indent)
be83ecb2 67;;;###autoload
59a1ce8d 68(defalias 'set-comment-column 'comment-set-column)
be83ecb2 69;;;###autoload
59a1ce8d 70(defalias 'kill-comment 'comment-kill)
be83ecb2 71;;;###autoload
59a1ce8d 72(defalias 'indent-new-comment-line 'comment-indent-new-line)
83b96b22 73
be83ecb2 74;;;###autoload
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
771c9b97
SM
81(defvar comment-use-syntax 'undecided
82 "Non-nil if syntax-tables can be used instead of regexps.
83Can also be `undecided' which means that a somewhat expensive test will
84be used to try to determine whether syntax-tables should be trusted
f5215400
SM
85to understand comments or not in the given buffer.
86Major modes should set this variable.")
2ab98065 87
be83ecb2 88;;;###autoload
83b96b22
SM
89(defcustom comment-column 32
90 "*Column to indent right-margin comments to.
91Setting this variable automatically makes it local to the current buffer.
92Each mode establishes a different default value for this variable; you
93can set the value for a particular mode using that mode's hook."
94 :type 'integer
3e569d22 95 :group 'comment)
83b96b22
SM
96(make-variable-buffer-local 'comment-column)
97
be83ecb2 98;;;###autoload
f5215400
SM
99(defvar comment-start nil
100 "*String to insert to start a new comment, or nil if no comment syntax.")
83b96b22 101
be83ecb2 102;;;###autoload
f5215400 103(defvar comment-start-skip nil
83b96b22
SM
104 "*Regexp to match the start of a comment plus everything up to its body.
105If there are any \\(...\\) pairs, the comment delimiter text is held to begin
f5215400 106at the place matched by the close of the first pair.")
771c9b97 107
be83ecb2 108;;;###autoload
3e569d22
SM
109(defvar comment-end-skip nil
110 "Regexp to match the end of a comment plus everything up to its body.")
83b96b22 111
be83ecb2 112;;;###autoload
f5215400 113(defvar comment-end ""
83b96b22 114 "*String to insert to end a new comment.
f5215400 115Should be an empty string if comments are terminated by end-of-line.")
83b96b22 116
be83ecb2 117;;;###autoload
83b96b22 118(defvar comment-indent-function
bdbe3a89 119 (lambda () (if (looking-at "\\s<\\s<\\s<") 0 comment-column))
83b96b22
SM
120 "Function to compute desired indentation for a comment.
121This function is called with no args with point at the beginning of
122the comment's starting delimiter.")
123
3e569d22
SM
124(defvar block-comment-start nil)
125(defvar block-comment-end nil)
83b96b22 126
771c9b97
SM
127(defvar comment-quote-nested t
128 "Non-nil if nested comments should be quoted.
129This should be locally set by each major mode if needed.")
130
131(defvar comment-continue nil
f5215400
SM
132 "Continuation string to insert for multiline comments.
133This string will be added at the beginning of each line except the very
134first one when commenting a region with a commenting style that allows
135comments to span several lines.
136It should generally have the same length as `comment-start' in order to
137preserve indentation.
138If it is nil a value will be automatically derived from `comment-start'
139by replacing its first character with a space.")
140
771c9b97 141(defvar comment-add 0
f5215400
SM
142 "How many more comment chars should be inserted by `comment-region'.
143This determines the default value of the numeric argument of `comment-region'.
144This should generally stay 0, except for a few modes like Lisp where
145it can be convenient to set it to 1 so that regions are commented with
146two semi-colons.")
2ab98065 147
2ab98065 148(defconst comment-styles
771c9b97
SM
149 '((plain . (nil nil nil nil))
150 (indent . (nil nil nil t))
151 (aligned . (nil t nil t))
152 (multi-line . (t nil nil t))
153 (extra-line . (t nil t t))
9b0d1d6e
SM
154 (box . (nil t t t))
155 (box-multi . (t t t t)))
f5215400
SM
156 "Possible comment styles of the form (STYLE . (MULTI ALIGN EXTRA INDENT)).
157STYLE should be a mnemonic symbol.
158MULTI specifies that comments are allowed to span multiple lines.
159ALIGN specifies that the `comment-end' markers should be aligned.
160EXTRA specifies that an extra line should be used before and after the
161 region to comment (to put the `comment-end' and `comment-start').
162INDENT specifies that the `comment-start' markers should not be put at the
163 left margin but at the current indentation of the region to comment.")
164
be83ecb2 165;;;###autoload
f5215400
SM
166(defcustom comment-style 'plain
167 "*Style to be used for `comment-region'.
168See `comment-styles' for a list of available styles."
169 :group 'comment
be83ecb2
SM
170 :type (if (boundp 'comment-styles)
171 `(choice ,@(mapcar (lambda (s) `(const ,(car s))) comment-styles))
172 'symbol))
2ab98065 173
be83ecb2
SM
174;;;###autoload
175(defcustom comment-padding " "
f5215400
SM
176 "Padding string that `comment-region' puts between comment chars and text.
177Can also be an integer which will be automatically turned into a string
178of the corresponding number of spaces.
2ab98065
SM
179
180Extra spacing between the comment characters and the comment text
771c9b97 181makes the comment easier to read. Default is 1. nil means 0.")
2ab98065 182
be83ecb2 183;;;###autoload
3e569d22 184(defcustom comment-multi-line nil
ac87b3a9 185 "*Non-nil means \\[comment-indent-new-line] continues comments, with no new terminator or starter.
3e569d22
SM
186This is obsolete because you might as well use \\[newline-and-indent]."
187 :type 'boolean
188 :group 'comment)
189
2ab98065
SM
190;;;;
191;;;; Helpers
192;;;;
193
f5215400
SM
194(defun comment-string-strip (str beforep afterp)
195 "Strip STR of any leading (if BEFOREP) and/or trailing (if AFTERP) space."
196 (string-match (concat "\\`" (if beforep "\\s-*")
ad679e45 197 "\\(.*?\\)" (if afterp "\\s-*\n?")
2ab98065
SM
198 "\\'") str)
199 (match-string 1 str))
200
201(defun comment-string-reverse (s)
f5215400 202 "Return the mirror image of string S, without any trailing space."
771c9b97 203 (comment-string-strip (concat (nreverse (string-to-list s))) nil t))
2ab98065
SM
204
205(defun comment-normalize-vars (&optional noerror)
206 (if (not comment-start) (or noerror (error "No comment syntax is defined"))
207 ;; comment-use-syntax
771c9b97 208 (when (eq comment-use-syntax 'undecided)
2ab98065
SM
209 (set (make-local-variable 'comment-use-syntax)
210 (let ((st (syntax-table))
211 (cs comment-start)
212 (ce (if (string= "" comment-end) "\n" comment-end)))
771c9b97
SM
213 ;; Try to skip over a comment using forward-comment
214 ;; to see if the syntax tables properly recognize it.
2ab98065
SM
215 (with-temp-buffer
216 (set-syntax-table st)
217 (insert cs " hello " ce)
218 (goto-char (point-min))
219 (and (forward-comment 1) (eobp))))))
2ab98065
SM
220 ;; comment-padding
221 (when (integerp comment-padding)
222 (setq comment-padding (make-string comment-padding ? )))
223 ;; comment markers
224 ;;(setq comment-start (comment-string-strip comment-start t nil))
225 ;;(setq comment-end (comment-string-strip comment-end nil t))
226 ;; comment-continue
f5215400 227 (unless (or comment-continue (string= comment-end ""))
2ab98065 228 (set (make-local-variable 'comment-continue)
9b0d1d6e
SM
229 (concat (if (string-match "\\S-\\S-" comment-start) " " "|")
230 (substring comment-start 1))))
2ab98065
SM
231 ;; comment-skip regexps
232 (unless comment-start-skip
233 (set (make-local-variable 'comment-start-skip)
234 (concat "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(\\s<+\\|"
235 (regexp-quote (comment-string-strip comment-start t t))
236 "+\\)\\s-*")))
237 (unless comment-end-skip
238 (let ((ce (if (string= "" comment-end) "\n"
239 (comment-string-strip comment-end t t))))
240 (set (make-local-variable 'comment-end-skip)
771c9b97 241 (concat "\\s-*\\(\\s>" (if comment-quote-nested "" "+")
2ab98065 242 "\\|" (regexp-quote (substring ce 0 1))
771c9b97 243 (if (and comment-quote-nested (<= (length ce) 1)) "" "+")
2ab98065
SM
244 (regexp-quote (substring ce 1))
245 "\\)"))))))
246
f5215400
SM
247(defun comment-quote-re (str unp)
248 (concat (regexp-quote (substring str 0 1))
249 "\\\\" (if unp "+" "*")
250 (regexp-quote (substring str 1))))
251
252(defun comment-quote-nested (cs ce unp)
253 "Quote or unquote nested comments.
254If UNP is non-nil, unquote nested comment markers."
255 (setq cs (comment-string-strip cs t t))
256 (setq ce (comment-string-strip ce t t))
257 (when (and comment-quote-nested (> (length ce) 0))
258 (let ((re (concat (comment-quote-re ce unp)
259 "\\|" (comment-quote-re cs unp))))
260 (goto-char (point-min))
261 (while (re-search-forward re nil t)
262 (goto-char (match-beginning 0))
263 (forward-char 1)
264 (if unp (delete-char 1) (insert "\\"))
265 (when (= (length ce) 1)
266 ;; If the comment-end is a single char, adding a \ after that
267 ;; "first" char won't deactivate it, so we turn such a CE
268 ;; into !CS. I.e. for pascal, we turn } into !{
269 (if (not unp)
270 (when (string= (match-string 0) ce)
271 (replace-match (concat "!" cs) t t))
272 (when (and (< (point-min) (match-beginning 0))
273 (string= (buffer-substring (1- (match-beginning 0))
274 (1- (match-end 0)))
275 (concat "!" cs)))
276 (backward-char 2)
277 (delete-char (- (match-end 0) (match-beginning 0)))
278 (insert ce))))))))
2ab98065
SM
279
280;;;;
281;;;; Navigation
282;;;;
283
ad679e45 284(defun comment-search-forward (limit &optional noerror)
771c9b97
SM
285 "Find a comment start between point and LIMIT.
286Moves point to inside the comment and returns the position of the
287comment-starter. If no comment is found, moves point to LIMIT
e8fe7d39 288and raises an error or returns nil of NOERROR is non-nil."
2ab98065 289 (if (not comment-use-syntax)
9b0d1d6e
SM
290 (if (re-search-forward comment-start-skip limit noerror)
291 (or (match-end 1) (match-beginning 0))
292 (goto-char limit)
293 (unless noerror (error "No comment")))
ad679e45
SM
294 (let* ((pt (point))
295 ;; Assume (at first) that pt is outside of any string.
296 (s (parse-partial-sexp pt (or limit (point-max)) nil nil nil t)))
297 (when (and (nth 8 s) (nth 3 s))
298 ;; The search ended inside a string. Try to see if it
299 ;; works better when we assume that pt is inside a string.
300 (setq s (parse-partial-sexp
301 pt (or limit (point-max)) nil nil
302 (list nil nil nil (nth 3 s) nil nil nil nil)
303 t)))
304 (if (not (and (nth 8 s) (not (nth 3 s))))
305 (unless noerror (error "No comment"))
306 ;; We found the comment.
9b0d1d6e 307 (let ((pos (point))
ad679e45 308 (start (nth 8 s))
9b0d1d6e 309 (bol (line-beginning-position))
ad679e45
SM
310 (end nil))
311 (while (and (null end) (>= (point) bol))
312 (if (looking-at comment-start-skip)
313 (setq end (min (or limit (point-max)) (match-end 0)))
314 (backward-char)))
9b0d1d6e 315 (goto-char (or end pos))
ad679e45 316 start)))))
2ab98065
SM
317
318(defun comment-search-backward (&optional limit noerror)
319 "Find a comment start between LIMIT and point.
771c9b97
SM
320Moves point to inside the comment and returns the position of the
321comment-starter. If no comment is found, moves point to LIMIT
2ab98065 322and raises an error or returns nil of NOERROR is non-nil."
ad679e45
SM
323 ;; FIXME: If a comment-start appears inside a comment, we may erroneously
324 ;; stop there. This can be rather bad in general, but since
325 ;; comment-search-backward is only used to find the comment-column (in
326 ;; comment-set-column) and to find the comment-start string (via
327 ;; comment-beginning) in indent-new-comment-line, it should be harmless.
2ab98065
SM
328 (if (not (re-search-backward comment-start-skip limit t))
329 (unless noerror (error "No comment"))
330 (beginning-of-line)
331 (let* ((end (match-end 0))
332 (cs (comment-search-forward end t))
333 (pt (point)))
334 (if (not cs)
335 (progn (beginning-of-line)
336 (comment-search-backward limit noerror))
337 (while (progn (goto-char cs)
338 (comment-forward)
339 (and (< (point) end)
340 (setq cs (comment-search-forward end t))))
341 (setq pt (point)))
342 (goto-char pt)
343 cs))))
344
345(defun comment-beginning ()
771c9b97
SM
346 "Find the beginning of the enclosing comment.
347Returns nil if not inside a comment, else moves point and returns
2ab98065
SM
348the same as `comment-search-forward'."
349 (let ((pt (point))
350 (cs (comment-search-backward nil t)))
ad679e45 351 (when cs
392f1ef5
SM
352 (if (save-excursion
353 (goto-char cs)
354 (if (comment-forward 1) (> (point) pt) (eobp)))
ad679e45
SM
355 cs
356 (goto-char pt)
357 nil))))
2ab98065
SM
358
359(defun comment-forward (&optional n)
360 "Skip forward over N comments.
361Just like `forward-comment' but only for positive N
362and can use regexps instead of syntax."
363 (setq n (or n 1))
364 (if (< n 0) (error "No comment-backward")
365 (if comment-use-syntax (forward-comment n)
366 (while (> n 0)
367 (skip-syntax-forward " ")
59a1ce8d
SM
368 (setq n
369 (if (and (looking-at comment-start-skip)
370 (re-search-forward comment-end-skip nil 'move))
371 (1- n) -1)))
2ab98065
SM
372 (= n 0))))
373
374(defun comment-enter-backward ()
375 "Move from the end of a comment to the end of its content.
771c9b97 376Point is assumed to be just at the end of a comment."
2ab98065
SM
377 (if (bolp)
378 ;; comment-end = ""
379 (progn (backward-char) (skip-syntax-backward " "))
380 (let ((end (point)))
381 (beginning-of-line)
382 (save-restriction
383 (narrow-to-region (point) end)
ad679e45
SM
384 (if (re-search-forward (concat comment-end-skip "\\'") nil t)
385 (goto-char (match-beginning 0))
386 ;; comment-end-skip not found probably because it was not set right.
387 ;; Since \\s> should catch the single-char case, we'll blindly
388 ;; assume we're at the end of a two-char comment-end.
389 (goto-char (point-max))
390 (backward-char 2)
391 (skip-chars-backward (string (char-after)))
392 (skip-syntax-backward " "))))))
2ab98065
SM
393
394;;;;
395;;;; Commands
396;;;;
397
be83ecb2 398;;;###autoload
3e569d22 399(defun comment-indent (&optional continue)
2ab98065
SM
400 "Indent this line's comment to comment column, or insert an empty comment.
401If CONTINUE is non-nil, use the `comment-continuation' markers if any."
83b96b22 402 (interactive "*")
ad679e45 403 (comment-normalize-vars)
83b96b22
SM
404 (let* ((empty (save-excursion (beginning-of-line)
405 (looking-at "[ \t]*$")))
f5215400 406 (starter (or (and continue comment-continue)
2ab98065 407 (and empty block-comment-start) comment-start))
f5215400 408 (ender (or (and continue comment-continue "")
2ab98065 409 (and empty block-comment-end) comment-end)))
83b96b22
SM
410 (cond
411 ((null starter)
412 (error "No comment syntax defined"))
ad679e45 413 (t (let* ((eolpos (line-end-position))
83b96b22
SM
414 cpos indent begpos)
415 (beginning-of-line)
9b0d1d6e
SM
416 (if (not (setq begpos (comment-search-forward eolpos t)))
417 (setq begpos (point))
e8fe7d39
SM
418 (setq cpos (point-marker))
419 (goto-char begpos))
83b96b22 420 ;; Compute desired indent.
bdbe3a89
SM
421 (setq indent (funcall comment-indent-function))
422 ;; Avoid moving comments past the fill-column.
423 (setq indent
424 (min indent
425 (+ (current-column)
426 (- fill-column
427 (save-excursion (end-of-line) (current-column))))))
428 (if (= (current-column) indent)
83b96b22
SM
429 (goto-char begpos)
430 ;; If that's different from current, change it.
431 (skip-chars-backward " \t")
432 (delete-region (point) begpos)
bdbe3a89 433 (indent-to (if (bolp) indent (max indent (1+ (current-column))))))
83b96b22
SM
434 ;; An existing comment?
435 (if cpos
9b0d1d6e 436 (progn (goto-char cpos) (set-marker cpos nil))
83b96b22
SM
437 ;; No, insert one.
438 (insert starter)
439 (save-excursion
440 (insert ender))))))))
441
be83ecb2 442;;;###autoload
3e569d22 443(defun comment-set-column (arg)
83b96b22 444 "Set the comment column based on point.
2ab98065 445With no ARG, set the comment column to the current column.
83b96b22
SM
446With just minus as arg, kill any comment on this line.
447With any other arg, set comment column to indentation of the previous comment
448 and then align or create a comment on this line at that column."
449 (interactive "P")
e8fe7d39 450 (cond
ad679e45 451 ((eq arg '-) (comment-kill nil))
e8fe7d39
SM
452 (arg
453 (save-excursion
454 (beginning-of-line)
2ab98065 455 (comment-search-backward)
e8fe7d39 456 (beginning-of-line)
ad679e45 457 (goto-char (comment-search-forward (line-end-position)))
83b96b22 458 (setq comment-column (current-column))
e8fe7d39 459 (message "Comment column set to %d" comment-column))
ad679e45 460 (comment-indent))
e8fe7d39 461 (t (setq comment-column (current-column))
83b96b22
SM
462 (message "Comment column set to %d" comment-column))))
463
be83ecb2 464;;;###autoload
3e569d22 465(defun comment-kill (arg)
7a0a180a
SM
466 "Kill the comment on this line, if any.
467With prefix ARG, kill comments on that many lines starting with this one."
468 (interactive "P")
ad679e45
SM
469 (dotimes (_ (prefix-numeric-value arg))
470 (save-excursion
471 (beginning-of-line)
472 (let ((cs (comment-search-forward (line-end-position) t)))
473 (when cs
474 (goto-char cs)
475 (skip-syntax-backward " ")
476 (setq cs (point))
477 (comment-forward)
478 (kill-region cs (if (bolp) (1- (point)) (point)))
479 (indent-according-to-mode))))
480 (if arg (forward-line 1))))
7a0a180a 481
2ab98065
SM
482(defun comment-padright (str &optional n)
483 "Construct a string composed of STR plus `comment-padding'.
f5215400 484It also adds N copies of the last non-whitespace chars of STR.
2ab98065 485If STR already contains padding, the corresponding amount is
f5215400
SM
486ignored from `comment-padding'.
487N defaults to 0.
771c9b97 488If N is `re', a regexp is returned instead, that would match
f5215400 489the string for any N."
2ab98065
SM
490 (setq n (or n 0))
491 (when (and (stringp str) (not (string= "" str)))
f5215400 492 ;; Separate the actual string from any leading/trailing padding
3e569d22 493 (string-match "\\`\\s-*\\(.*?\\)\\s-*\\'" str)
f5215400
SM
494 (let ((s (match-string 1 str)) ;actual string
495 (lpad (substring str 0 (match-beginning 1))) ;left padding
496 (rpad (concat (substring str (match-end 1)) ;original right padding
497 (substring comment-padding ;additional right padding
3e569d22 498 (min (- (match-end 0) (match-end 1))
9b0d1d6e
SM
499 (length comment-padding)))))
500 ;; We can only duplicate C if the comment-end has multiple chars
501 ;; or if comments can be nested, else the comment-end `}' would
502 ;; be turned into `}}}' where only the first ends the comment
503 ;; and the rest becomes bogus junk.
504 (multi (not (and comment-quote-nested
505 ;; comment-end is a single char
506 (string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
f5215400 507 (if (not (symbolp n))
9b0d1d6e 508 (concat lpad s (when multi (make-string n (aref str (1- (match-end 1))))) rpad)
f5215400
SM
509 ;; construct a regexp that would match anything from just S
510 ;; to any possible output of this function for any N.
511 (concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
512 lpad "") ;padding is not required
9b0d1d6e
SM
513 (regexp-quote s)
514 (when multi "+") ;the last char of S might be repeated
f5215400
SM
515 (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
516 rpad "")))))) ;padding is not required
2ab98065
SM
517
518(defun comment-padleft (str &optional n)
519 "Construct a string composed of `comment-padding' plus STR.
f5215400 520It also adds N copies of the first non-whitespace chars of STR.
2ab98065 521If STR already contains padding, the corresponding amount is
f5215400
SM
522ignored from `comment-padding'.
523N defaults to 0.
771c9b97 524If N is `re', a regexp is returned instead, that would match
2ab98065
SM
525 the string for any N."
526 (setq n (or n 0))
527 (when (and (stringp str) (not (string= "" str)))
f5215400 528 ;; Only separate the left pad because we assume there is no right pad.
2ab98065
SM
529 (string-match "\\`\\s-*" str)
530 (let ((s (substring str (match-end 0)))
531 (pad (concat (substring comment-padding
532 (min (- (match-end 0) (match-beginning 0))
533 (length comment-padding)))
534 (match-string 0 str)))
f5215400
SM
535 (c (aref str (match-end 0))) ;the first non-space char of STR
536 ;; We can only duplicate C if the comment-end has multiple chars
537 ;; or if comments can be nested, else the comment-end `}' would
538 ;; be turned into `}}}' where only the first ends the comment
539 ;; and the rest becomes bogus junk.
540 (multi (not (and comment-quote-nested
541 ;; comment-end is a single char
542 (string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
543 (if (not (symbolp n))
544 (concat pad (when multi (make-string n c)) s)
545 ;; Construct a regexp that would match anything from just S
546 ;; to any possible output of this function for any N.
547 ;; We match any number of leading spaces because this regexp will
548 ;; be used for uncommenting where we might want to remove
549 ;; uncomment markers with arbitrary leading space (because
550 ;; they were aligned).
551 (concat "\\s-*"
552 (if multi (concat (regexp-quote (string c)) "*"))
553 (regexp-quote s))))))
83b96b22 554
be83ecb2 555;;;###autoload
7a0a180a
SM
556(defun uncomment-region (beg end &optional arg)
557 "Uncomment each line in the BEG..END region.
f5215400
SM
558The numeric prefix ARG can specify a number of chars to remove from the
559comment markers."
83b96b22
SM
560 (interactive "*r\nP")
561 (comment-normalize-vars)
562 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
563 (save-excursion
7a0a180a 564 (goto-char beg)
f5215400 565 (setq end (copy-marker end))
7a0a180a 566 (let ((numarg (prefix-numeric-value arg))
2ab98065 567 spt)
7a0a180a 568 (while (and (< (point) end)
2ab98065
SM
569 (setq spt (comment-search-forward end t)))
570 (let* ((ipt (point))
f5215400 571 ;; Find the end of the comment.
e8fe7d39 572 (ept (progn
2ab98065
SM
573 (goto-char spt)
574 (unless (comment-forward)
e8fe7d39 575 (error "Can't find the comment end"))
f5215400
SM
576 (point)))
577 (box nil)
578 (ccs comment-continue)
2ab98065 579 (srei (comment-padright ccs 're))
3e569d22 580 (sre (and srei (concat "^\\s-*?\\(" srei "\\)"))))
e8fe7d39
SM
581 (save-restriction
582 (narrow-to-region spt ept)
f5215400 583 ;; Remove the comment-start.
2ab98065
SM
584 (goto-char ipt)
585 (skip-syntax-backward " ")
9b0d1d6e
SM
586 ;; Check for special `=' used sometimes in comment-box.
587 (when (and (= (- (point) (point-min)) 1) (looking-at "=\\{7\\}"))
588 (skip-chars-forward "="))
f5215400 589 ;; A box-comment starts with a looong comment-start marker.
2ab98065 590 (when (> (- (point) (point-min) (length comment-start)) 7)
f5215400 591 (setq box t))
2ab98065
SM
592 (when (looking-at (regexp-quote comment-padding))
593 (goto-char (match-end 0)))
594 (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei)))
595 (goto-char (match-end 0)))
3e569d22
SM
596 (if (null arg) (delete-region (point-min) (point))
597 (skip-syntax-backward " ")
598 (delete-char (- numarg)))
2ab98065 599
f5215400 600 ;; Remove the end-comment (and leading padding and such).
2ab98065 601 (goto-char (point-max)) (comment-enter-backward)
9b0d1d6e 602 ;; Check for special `=' used sometimes in comment-box.
6dcd3806
SM
603 (when (= (- (point-max) (point)) 1)
604 (let ((pos (point)))
605 ;; skip `=' but only if there are at least 7.
606 (when (> (skip-chars-backward "=") -7) (goto-char pos))))
9b0d1d6e 607 (unless (looking-at "\\(\n\\|\\s-\\)*\\'")
2ab98065 608 (when (and (bolp) (not (bobp))) (backward-char))
f5215400 609 (if (null arg) (delete-region (point) (point-max))
3e569d22
SM
610 (skip-syntax-forward " ")
611 (delete-char numarg)))
e8fe7d39 612
f5215400
SM
613 ;; Unquote any nested end-comment.
614 (comment-quote-nested comment-start comment-end t)
615
616 ;; Eliminate continuation markers as well.
617 (when sre
618 (let* ((cce (comment-string-reverse (or comment-continue
619 comment-start)))
620 (erei (and box (comment-padleft cce 're)))
621 (ere (and erei (concat "\\(" erei "\\)\\s-*$"))))
7a0a180a 622 (goto-char (point-min))
f5215400
SM
623 (while (progn
624 (if (and ere (re-search-forward
625 ere (line-end-position) t))
626 (replace-match "" t t nil (if (match-end 2) 2 1))
627 (setq ere nil))
628 (forward-line 1)
629 (re-search-forward sre (line-end-position) t))
2ab98065 630 (replace-match "" t t nil (if (match-end 2) 2 1)))))
f5215400
SM
631 ;; Go the the end for the next comment.
632 (goto-char (point-max)))))
633 (set-marker end nil))))
83b96b22 634
aac88001 635(defun comment-make-extra-lines (cs ce ccs cce min-indent max-indent &optional block)
ad679e45
SM
636 "Make the leading and trailing extra lines.
637This is used for `extra-line' style (or `box' style if BLOCK is specified)."
9b0d1d6e
SM
638 (let ((eindent 0))
639 (if (not block)
640 ;; Try to match CS and CE's content so they align aesthetically.
641 (progn
642 (setq ce (comment-string-strip ce t t))
643 (when (string-match "\\(.+\\).*\n\\(.*?\\)\\1" (concat ce "\n" cs))
644 (setq eindent
645 (max (- (match-end 2) (match-beginning 2) (match-beginning 0))
646 0))))
647 ;; box comment
648 (let* ((width (- max-indent min-indent))
649 (s (concat cs "a=m" cce))
650 (e (concat ccs "a=m" ce))
651 (c (if (string-match ".*\\S-\\S-" cs)
652 (aref cs (1- (match-end 0))) ?=))
3fc5b4e2 653 (_ (string-match "\\s-*a=m\\s-*" s))
9b0d1d6e
SM
654 (fill
655 (make-string (+ width (- (match-end 0)
656 (match-beginning 0) (length cs) 3)) c)))
aac88001 657 (setq cs (replace-match fill t t s))
3fc5b4e2 658 (string-match "\\s-*a=m\\s-*" e)
9b0d1d6e
SM
659 (setq ce (replace-match fill t t e))))
660 (cons (concat cs "\n" (make-string min-indent ? ) ccs)
661 (concat cce "\n" (make-string (+ min-indent eindent) ? ) ce))))
aac88001
SM
662
663(def-edebug-spec comment-with-narrowing t)
664(put 'comment-with-narrowing 'lisp-indent-function 2)
665(defmacro comment-with-narrowing (beg end &rest body)
666 "Execute BODY with BEG..END narrowing.
667Space is added (and then removed) at the beginning for the text's
668indentation to be kept as it was before narrowing."
59a1ce8d
SM
669 (let ((bindent (make-symbol "bindent")))
670 `(let ((,bindent (save-excursion (goto-char beg) (current-column))))
671 (save-restriction
672 (narrow-to-region beg end)
673 (goto-char (point-min))
674 (insert (make-string ,bindent ? ))
675 (prog1
676 (progn ,@body)
677 ;; remove the bindent
678 (save-excursion
679 (goto-char (point-min))
680 (when (looking-at " *")
681 (let ((n (min (- (match-end 0) (match-beginning 0)) ,bindent)))
aac88001 682 (delete-char n)
59a1ce8d
SM
683 (setq ,bindent (- ,bindent n))))
684 (end-of-line)
685 (let ((e (point)))
686 (beginning-of-line)
687 (while (and (> ,bindent 0) (re-search-forward " *" e t))
688 (let ((n (min ,bindent (- (match-end 0) (match-beginning 0) 1))))
689 (goto-char (match-beginning 0))
690 (delete-char n)
691 (setq ,bindent (- ,bindent n)))))))))))
aac88001 692
771c9b97
SM
693(defun comment-region-internal (beg end cs ce
694 &optional ccs cce block lines indent)
f5215400 695 "Comment region BEG..END.
4125ec7e
SM
696CS and CE are the comment start resp end string.
697CCS and CCE are the comment continuation strings for the start resp end
f5215400
SM
698of lines (default to CS and CE).
699BLOCK indicates that end of lines should be marked with either CCE, CE or CS
700\(if CE is empty) and that those markers should be aligned.
701LINES indicates that an extra lines will be used at the beginning and end
702of the region for CE and CS.
703INDENT indicates to put CS and CCS at the current indentation of the region
704rather than at left margin."
59a1ce8d 705 ;;(assert (< beg end))
83b96b22 706 (let ((no-empty t))
f5215400 707 ;; Sanitize CE and CCE.
83b96b22
SM
708 (if (and (stringp ce) (string= "" ce)) (setq ce nil))
709 (if (and (stringp cce) (string= "" cce)) (setq cce nil))
f5215400
SM
710 ;; If CE is empty, multiline cannot be used.
711 (unless ce (setq ccs nil cce nil))
712 ;; Should we mark empty lines as well ?
83b96b22 713 (if (or ccs block lines) (setq no-empty nil))
f5215400 714 ;; Make sure we have end-markers for BLOCK mode.
2ab98065 715 (when block (unless ce (setq ce (comment-string-reverse cs))))
f5215400
SM
716 ;; If BLOCK is not requested, we don't need CCE.
717 (unless block (setq cce nil))
718 ;; Continuation defaults to the same as CS and CE.
719 (unless ccs (setq ccs cs cce ce))
7a0a180a 720
83b96b22 721 (save-excursion
aac88001 722 (goto-char end)
f5215400
SM
723 ;; If the end is not at the end of a line and the comment-end
724 ;; is implicit (i.e. a newline), explicitly insert a newline.
aac88001
SM
725 (unless (or ce (eolp)) (insert "\n") (indent-according-to-mode))
726 (comment-with-narrowing beg end
f5215400 727 (let ((min-indent (point-max))
83b96b22
SM
728 (max-indent 0))
729 (goto-char (point-min))
f5215400
SM
730 ;; Quote any nested comment marker
731 (comment-quote-nested comment-start comment-end nil)
732
733 ;; Loop over all lines to find the needed indentations.
4125ec7e 734 (goto-char (point-min))
f5215400
SM
735 (while
736 (progn
737 (unless (looking-at "[ \t]*$")
738 (setq min-indent (min min-indent (current-indentation))))
739 (end-of-line)
740 (setq max-indent (max max-indent (current-column)))
741 (not (or (eobp) (progn (forward-line) nil)))))
742
743 ;; Inserting ccs can change max-indent by (1- tab-width).
59a1ce8d
SM
744 (setq max-indent
745 (+ max-indent (max (length cs) (length ccs)) tab-width -1))
771c9b97 746 (unless indent (setq min-indent 0))
83b96b22 747
aac88001 748 ;; make the leading and trailing lines if requested
83b96b22 749 (when lines
771c9b97
SM
750 (let ((csce
751 (comment-make-extra-lines
752 cs ce ccs cce min-indent max-indent block)))
753 (setq cs (car csce))
754 (setq ce (cdr csce))))
83b96b22
SM
755
756 (goto-char (point-min))
757 ;; Loop over all lines from BEG to END.
f5215400
SM
758 (while
759 (progn
760 (unless (and no-empty (looking-at "[ \t]*$"))
761 (move-to-column min-indent t)
762 (insert cs) (setq cs ccs) ;switch to CCS after the first line
763 (end-of-line)
764 (if (eobp) (setq cce ce))
765 (when cce
766 (when block (move-to-column max-indent t))
767 (insert cce)))
768 (end-of-line)
769 (not (or (eobp) (progn (forward-line) nil))))))))))
83b96b22 770
be83ecb2 771;;;###autoload
83b96b22
SM
772(defun comment-region (beg end &optional arg)
773 "Comment or uncomment each line in the region.
774With just \\[universal-prefix] prefix arg, uncomment each line in region BEG..END.
775Numeric prefix arg ARG means use ARG comment characters.
776If ARG is negative, delete that many comment characters instead.
be83ecb2
SM
777By default, comments start at the left margin, are terminated on each line,
778even for syntax in which newline does not end the comment and blank lines
779do not get comments. This can be changed with `comment-style'.
83b96b22
SM
780
781The strings used as comment starts are built from
782`comment-start' without trailing spaces and `comment-padding'."
783 (interactive "*r\nP")
784 (comment-normalize-vars)
785 (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
2ab98065 786 (let* ((numarg (prefix-numeric-value arg))
771c9b97 787 (add comment-add)
2ab98065
SM
788 (style (cdr (assoc comment-style comment-styles)))
789 (lines (nth 2 style))
790 (block (nth 1 style))
791 (multi (nth 0 style)))
83b96b22
SM
792 (save-excursion
793 ;; we use `chars' instead of `syntax' because `\n' might be
794 ;; of end-comment syntax rather than of whitespace syntax.
795 ;; sanitize BEG and END
796 (goto-char beg) (skip-chars-forward " \t\n\r") (beginning-of-line)
797 (setq beg (max beg (point)))
798 (goto-char end) (skip-chars-backward " \t\n\r") (end-of-line)
799 (setq end (min end (point)))
800 (if (>= beg end) (error "Nothing to comment"))
801
83b96b22
SM
802 ;; sanitize LINES
803 (setq lines
804 (and
9b0d1d6e 805 lines ;; multi
83b96b22
SM
806 (progn (goto-char beg) (beginning-of-line)
807 (skip-syntax-forward " ")
808 (>= (point) beg))
809 (progn (goto-char end) (end-of-line) (skip-syntax-backward " ")
810 (<= (point) end))
771c9b97 811 (or (not (string= "" comment-end)) block)
2ab98065 812 (progn (goto-char beg) (search-forward "\n" end t)))))
83b96b22 813
2ab98065
SM
814 ;; don't add end-markers just because the user asked for `block'
815 (unless (or lines (string= "" comment-end)) (setq block nil))
816
83b96b22
SM
817 (cond
818 ((consp arg) (uncomment-region beg end))
819 ((< numarg 0) (uncomment-region beg end (- numarg)))
820 (t
59a1ce8d
SM
821 (setq numarg (if (and (null arg) (= (length comment-start) 1))
822 add (1- numarg)))
83b96b22
SM
823 (comment-region-internal
824 beg end
3e569d22
SM
825 (let ((s (comment-padright comment-start numarg)))
826 (if (string-match comment-start-skip s) s
827 (comment-padright comment-start)))
828 (let ((s (comment-padleft comment-end numarg)))
829 (and s (if (string-match comment-end-skip s) s
830 (comment-padright comment-end))))
f5215400
SM
831 (if multi (comment-padright comment-continue numarg))
832 (if multi (comment-padleft (comment-string-reverse comment-continue) numarg))
83b96b22 833 block
771c9b97
SM
834 lines
835 (nth 3 style))))))
836
837(defun comment-box (beg end &optional arg)
838 "Comment out the BEG..END region, putting it inside a box.
839The numeric prefix ARG specifies how many characters to add to begin- and
840end- comment markers additionally to what `comment-add' already specifies."
841 (interactive "*r\np")
9b0d1d6e
SM
842 (let ((comment-style (if (cadr (assoc comment-style comment-styles))
843 'box-multi 'box)))
771c9b97 844 (comment-region beg end (+ comment-add arg))))
83b96b22 845
be83ecb2 846;;;###autoload
2ab98065 847(defun comment-dwim (arg)
ad679e45
SM
848 "Call the comment command you want (Do What I Mean).
849If the region is active and `transient-mark-mode' is on, call
850 `comment-region' (unless it only consists in comments, in which
851 case it calls `uncomment-region').
2ab98065 852Else, if the current line is empty, insert a comment and indent it.
ad679e45
SM
853Else if a prefix ARG is specified, call `comment-kill'.
854Else, call `comment-indent'."
2ab98065
SM
855 (interactive "*P")
856 (comment-normalize-vars)
771c9b97 857 (if (and mark-active transient-mark-mode)
2ab98065
SM
858 (let ((beg (min (point) (mark)))
859 (end (max (point) (mark))))
860 (if (save-excursion ;; check for already commented region
861 (goto-char beg)
862 (comment-forward (point-max))
863 (<= end (point)))
864 (uncomment-region beg end arg)
865 (comment-region beg end arg)))
866 (if (save-excursion (beginning-of-line) (not (looking-at "\\s-*$")))
ad679e45
SM
867 ;; FIXME: If there's no comment to kill on this line and ARG is
868 ;; specified, calling comment-kill is not very clever.
869 (if arg (comment-kill (and (integerp arg) arg)) (comment-indent))
2ab98065 870 (let ((add (if arg (prefix-numeric-value arg)
771c9b97 871 (if (= (length comment-start) 1) comment-add 0))))
2ab98065
SM
872 (insert (comment-padright comment-start add))
873 (save-excursion
874 (unless (string= "" comment-end)
875 (insert (comment-padleft comment-end add)))
876 (indent-according-to-mode))))))
877
392f1ef5
SM
878(defcustom comment-auto-fill-only-comments nil
879 "Non-nil means to only auto-fill inside comments.
880This has no effect in modes that do not define a comment syntax."
881 :type 'boolean
882 :group 'comment)
883
be83ecb2 884;;;###autoload
ad679e45 885(defun comment-indent-new-line (&optional soft)
2ab98065
SM
886 "Break line at point and indent, continuing comment if within one.
887This indents the body of the continued comment
888under the previous comment line.
889
890This command is intended for styles where you write a comment per line,
891starting a new comment (and terminating it if necessary) on each line.
892If you want to continue one comment across several lines, use \\[newline-and-indent].
893
894If a fill column is specified, it overrides the use of the comment column
895or comment indentation.
896
897The inserted newline is marked hard if variable `use-hard-newlines' is true,
898unless optional argument SOFT is non-nil."
899 (interactive)
900 (comment-normalize-vars t)
59a1ce8d
SM
901 (let (compos comin)
902 ;; If we are not inside a comment and we only auto-fill comments,
903 ;; don't do anything (unless no comment syntax is defined).
392f1ef5
SM
904 (unless (and comment-start
905 comment-auto-fill-only-comments
906 (not (save-excursion
59a1ce8d 907 (prog1 (setq compos (comment-beginning))
392f1ef5 908 (setq comin (point))))))
59a1ce8d
SM
909
910 ;; Now we know we should auto-fill.
392f1ef5 911 (delete-region (progn (skip-chars-backward " \t") (point))
59a1ce8d 912 (progn (skip-chars-forward " \t") (point)))
392f1ef5
SM
913 (if soft (insert-and-inherit ?\n) (newline 1))
914 (if fill-prefix
915 (progn
916 (indent-to-left-margin)
917 (insert-and-inherit fill-prefix))
59a1ce8d
SM
918
919 ;; If necessary check whether we're inside a comment.
920 (unless (or comment-multi-line compos (null comment-start))
392f1ef5
SM
921 (save-excursion
922 (backward-char)
59a1ce8d
SM
923 (setq compos (comment-beginning))
924 (setq comin (point))))
925
926 ;; If we're not inside a comment, just try to indent.
927 (if (not compos) (indent-according-to-mode)
928 (let* ((comment-column
929 ;; The continuation indentation should be somewhere between
930 ;; the current line's indentation (plus 2 for good measure)
931 ;; and the current comment's indentation, with a preference
932 ;; for comment-column.
933 (save-excursion
934 (goto-char compos)
935 (min (current-column) (max comment-column
936 (+ 2 (current-indentation))))))
937 (comstart (buffer-substring compos comin))
938 (normalp
939 (string-match (regexp-quote (comment-string-strip
940 comment-start t t))
941 comstart))
942 (comment-end
943 (if normalp comment-end
944 ;; The comment starter is not the normal comment-start
945 ;; so we can't just use comment-end.
946 (save-excursion
947 (goto-char compos)
948 (if (not (comment-forward)) comment-end
949 (comment-string-strip
950 (buffer-substring
951 (save-excursion (comment-enter-backward) (point))
952 (point))
953 nil t)))))
954 (comment-start comstart)
955 ;; Force comment-continue to be recreated from comment-start.
956 (comment-continue nil))
957 (insert-and-inherit ?\n)
958 (forward-char -1)
959 (comment-indent (cadr (assoc comment-style comment-styles)))
960 (save-excursion
961 (let ((pt (point)))
962 (end-of-line)
963 (let ((comend (buffer-substring pt (point))))
964 ;; The 1+ is to make sure we delete the \n inserted above.
965 (delete-region pt (1+ (point)))
966 (beginning-of-line)
967 (backward-char)
968 (insert comend)
969 (forward-char))))))))))
2ab98065 970
83b96b22
SM
971(provide 'newcomment)
972
83b96b22 973;;; newcomment.el ends here