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