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