*** empty log message ***
[bpt/emacs.git] / lisp / newcomment.el
index aa53f50..9194350 100644 (file)
@@ -1,6 +1,6 @@
 ;;; newcomment.el --- (un)comment regions of buffers
 
-;; Copyright (C) 1999, 2000  Free Software Foundation Inc.
+;; Copyright (C) 1999,2000,2003  Free Software Foundation Inc.
 
 ;; Author: code extracted from Emacs-20's simple.el
 ;; Maintainer: Stefan Monnier <monnier@cs.yale.edu>
@@ -129,6 +129,31 @@ the comment's starting delimiter and should return either the desired
 column indentation or nil.
 If nil is returned, indentation is delegated to `indent-according-to-mode'.")
 
+;;;###autoload
+(defvar comment-insert-comment-function nil
+  "Function to insert a comment when a line doesn't contain one.
+The function has no args.
+
+Applicable at least in modes for languages like fixed-format Fortran where
+comments always start in column zero.")
+
+(defvar comment-region-function nil
+  "Function to comment a region.
+Its args are the same as those of `comment-region', but BEG and END are
+guaranteed to be correctly ordered.  It is called within `save-excursion'.
+
+Applicable at least in modes for languages like fixed-format Fortran where
+comments always start in column zero.")
+
+(defvar uncomment-region-function nil
+  "Function to uncomment a region.
+Its args are the same as those of `uncomment-region', but BEG and END are
+guaranteed to be correctly ordered.  It is called within `save-excursion'.
+
+Applicable at least in modes for languages like fixed-format Fortran where
+comments always start in column zero.")
+
+;; ?? never set
 (defvar block-comment-start nil)
 (defvar block-comment-end nil)
 
@@ -194,6 +219,15 @@ makes the comment easier to read.  Default is 1.  nil means 0."
 This is obsolete because you might as well use \\[newline-and-indent]."
   :type 'boolean)
 
+(defcustom comment-empty-lines nil
+  "If nil, `comment-region' does not comment out empty lines.
+If t, it always comments out empty lines.
+if `eol' it only comments out empty lines if comments are
+terminated by the end of line (i.e. `comment-end' is empty)."
+  :type '(choice (const :tag "Never" nil)
+         (const :tag "Always" t)
+         (const :tag "EOl-terminated" 'eol)))
+
 ;;;;
 ;;;; Helpers
 ;;;;
@@ -211,10 +245,14 @@ This is obsolete because you might as well use \\[newline-and-indent]."
 
 ;;;###autoload
 (defun comment-normalize-vars (&optional noerror)
-  (if (not comment-start)
-      (unless noerror
-       (set (make-local-variable 'comment-start)
-            (read-string "No comment syntax is defined.  Use: ")))
+  "Check and setup the variables needed by other commenting functions.
+Functions autoloaded from newcomment.el, being entry points, should call
+this function before any other, so the rest of the code can assume that
+the variables are properly set."
+  (unless (and (not comment-start) noerror)
+    (unless comment-start
+      (set (make-local-variable 'comment-start)
+          (read-string "No comment syntax is defined.  Use: ")))
     ;; comment-use-syntax
     (when (eq comment-use-syntax 'undecided)
       (set (make-local-variable 'comment-use-syntax)
@@ -447,7 +485,7 @@ Point is assumed to be just at the end of a comment."
 
 ;;;###autoload
 (defun comment-indent (&optional continue)
-  "Indent this line's comment to comment column, or insert an empty comment.
+  "Indent this line's comment to `comment-column', or insert an empty comment.
 If CONTINUE is non-nil, use the `comment-continue' markers if any."
   (interactive "*")
   (comment-normalize-vars)
@@ -473,31 +511,63 @@ If CONTINUE is non-nil, use the `comment-continue' markers if any."
                (forward-char (/ (skip-chars-backward " \t") -2)))
            (setq cpos (point-marker)))
          ;; If none, insert one.
+       (if comment-insert-comment-function
+           (funcall comment-insert-comment-function)
          (save-excursion
-           ;; Some comment-indent-function insist on not moving comments that
-           ;; are in column 0, so we first go to the likely target column.
+           ;; Some `comment-indent-function's insist on not moving
+           ;; comments that are in column 0, so we first go to the
+           ;; likely target column.
            (indent-to comment-column)
-           (setq begpos (point))
            ;; Ensure there's a space before the comment for things
            ;; like sh where it matters (as well as being neater).
-           (unless (eq ?\  (char-syntax (char-before)))
+           (unless (memq (char-before) '(nil ?\n ?\t ?\ ))
              (insert ?\ ))
+           (setq begpos (point))
            (insert starter)
            (setq cpos (point-marker))
-           (insert ender)))
+           (insert ender))))
       (goto-char begpos)
       ;; Compute desired indent.
       (setq indent (save-excursion (funcall comment-indent-function)))
+      ;; If `indent' is nil and there's code before the comment, we can't
+      ;; use `indent-according-to-mode', so we default to comment-column.
+      (unless (or indent (save-excursion (skip-chars-backward " \t") (bolp)))
+       (setq indent comment-column))
       (if (not indent)
-         ;; comment-indent-function refuses: delegate to indent.
+         ;; comment-indent-function refuses: delegate to line-indent.
          (indent-according-to-mode)
-       ;; Avoid moving comments past the fill-column.
+       ;; If the comment is at the left of code, adjust the indentation.
        (unless (save-excursion (skip-chars-backward " \t") (bolp))
-         (setq indent
-               (min indent
-                    (+ (current-column)
+         ;; Avoid moving comments past the fill-column.
+         (let ((max (+ (current-column)
                        (- (or comment-fill-column fill-column)
-                          (save-excursion (end-of-line) (current-column)))))))
+                          (save-excursion (end-of-line) (current-column))))))
+           (if (<= max indent)
+               (setq indent max)       ;Don't move past the fill column.
+             ;; We can choose anywhere between indent..max.
+             ;; Let's try to align to a comment on the previous line.
+             (let ((other nil)
+                   (min (max indent
+                             (save-excursion (skip-chars-backward " \t")
+                                             (1+ (current-column))))))
+               (save-excursion
+                 (when (and (zerop (forward-line -1))
+                            (setq other (comment-search-forward
+                                        (line-end-position) t)))
+                   (goto-char other) (setq other (current-column))))
+               (if (and other (<= other max) (>= other min))
+                   ;; There is a comment and it's in the range: bingo.
+                   (setq indent other)
+                 ;; Let's try to align to a comment on the next line, then.
+                 (let ((other nil))
+                   (save-excursion
+                     (when (and (zerop (forward-line 1))
+                                (setq other (comment-search-forward
+                                            (line-end-position) t)))
+                       (goto-char other) (setq other (current-column))))
+                   (if (and other (<= other max) (> other min))
+                       ;; There is a comment and it's in the range: bingo.
+                       (setq indent other))))))))
        (unless (= (current-column) indent)
          ;; If that's different from current, change it.
          (delete-region (point) (progn (skip-chars-backward " \t") (point)))
@@ -517,6 +587,7 @@ With any other arg, set comment column to indentation of the previous comment
   (cond
    ((eq arg '-) (comment-kill nil))
    (arg
+    (comment-normalize-vars)
     (save-excursion
       (beginning-of-line)
       (comment-search-backward)
@@ -533,6 +604,7 @@ With any other arg, set comment column to indentation of the previous comment
   "Kill the comment on this line, if any.
 With prefix ARG, kill comments on that many lines starting with this one."
   (interactive "P")
+  (comment-normalize-vars)
   (dotimes (_ (prefix-numeric-value arg))
     (save-excursion
       (beginning-of-line)
@@ -626,31 +698,62 @@ The numeric prefix ARG can specify a number of chars to remove from the
 comment markers."
   (interactive "*r\nP")
   (comment-normalize-vars)
-  (if (> beg end) (let (mid) (setq mid beg beg end end mid)))
+  (when (> beg end) (setq beg (prog1 end (setq end beg))))
   (save-excursion
-    (goto-char beg)
-    (setq end (copy-marker end))
-    (let* ((numarg (prefix-numeric-value arg))
-           (ccs comment-continue)
-           (srei (comment-padright ccs 're))
-           (sre (and srei (concat "^\\s-*?\\(" srei "\\)")))
-           spt)
-      (while (and (< (point) end)
-                 (setq spt (comment-search-forward end t)))
-       (let ((ipt (point))
-              ;; Find the end of the comment.
-              (ept (progn
-                     (goto-char spt)
-                     (unless (comment-forward)
-                       (error "Can't find the comment end"))
-                     (point)))
-              (box nil)
-              (box-equal nil))     ;Whether we might be using `=' for boxes.
-         (save-restriction
-           (narrow-to-region spt ept)
-
-           ;; Remove the comment-start.
-           (goto-char ipt)
+    (if uncomment-region-function
+       (funcall uncomment-region-function beg end arg)
+      (goto-char beg)
+      (setq end (copy-marker end))
+      (let* ((numarg (prefix-numeric-value arg))
+            (ccs comment-continue)
+            (srei (comment-padright ccs 're))
+            (csre (comment-padright comment-start 're))
+            (sre (and srei (concat "^\\s-*?\\(" srei "\\)")))
+            spt)
+       (while (and (< (point) end)
+                   (setq spt (comment-search-forward end t)))
+         (let ((ipt (point))
+               ;; Find the end of the comment.
+               (ept (progn
+                      (goto-char spt)
+                      (unless
+                          (or
+                           (comment-forward)
+                           ;; Allow eob as comment-end instead of \n.
+                           (and
+                            (eobp)
+                            (let ((s1 (aref (syntax-table) (char-after spt)))
+                                  (s2 (aref (syntax-table)
+                                            (or (char-after (1+ spt)) 0)))
+                                  (sn (aref (syntax-table) ?\n))
+                                  (flag->b (car (string-to-syntax "> b")))
+                                  (flag-1b (car (string-to-syntax "  1b")))
+                                  (flag-2b (car (string-to-syntax "  2b"))))
+                              (cond
+                               ;; One-character comment-start terminated by
+                               ;; \n.
+                               ((and
+                                 (equal sn (string-to-syntax ">"))
+                                 (equal s1 (string-to-syntax "<")))
+                                (insert-char ?\n 1)
+                                t)
+                               ;; Two-character type b comment-start
+                               ;; terminated by \n.
+                               ((and
+                                 (= (logand (car sn) flag->b) flag->b)
+                                 (= (logand (car s1) flag-1b) flag-1b)
+                                 (= (logand (car s2) flag-2b) flag-2b))
+                                (insert-char ?\n 1)
+                                t)))))
+                        (error "Can't find the comment end"))
+                      (point)))
+               (box nil)
+               (box-equal nil)) ;Whether we might be using `=' for boxes.
+           (save-restriction
+             (narrow-to-region spt ept)
+
+             ;; Remove the comment-start.
+             (goto-char ipt)
            (skip-syntax-backward " ")
            ;; A box-comment starts with a looong comment-start marker.
            (when (and (or (and (= (- (point) (point-min)) 1)
@@ -661,58 +764,61 @@ comment markers."
                           (> (- (point) (point-min) (length comment-start)) 7))
                       (> (count-lines (point-min) (point-max)) 2))
              (setq box t))
-           (when (looking-at (regexp-quote comment-padding))
-             (goto-char (match-end 0)))
+           ;; Skip the padding.  Padding can come from comment-padding and/or
+           ;; from comment-start, so we first check comment-start.
+           (if (or (save-excursion (goto-char (point-min)) (looking-at csre))
+                   (looking-at (regexp-quote comment-padding)))
+               (goto-char (match-end 0)))
            (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei)))
              (goto-char (match-end 0)))
            (if (null arg) (delete-region (point-min) (point))
              (skip-syntax-backward " ")
-             (delete-char (- numarg))
-             (unless (or (bobp)
-                         (save-excursion (goto-char (point-min))
-                                         (looking-at comment-start-skip)))
-               ;; If there's something left but it doesn't look like
-               ;; a comment-start any more, just remove it.
-               (delete-region (point-min) (point))))
-
-           ;; Remove the end-comment (and leading padding and such).
-           (goto-char (point-max)) (comment-enter-backward)
-           ;; Check for special `=' used sometimes in comment-box.
-           (when (and box-equal (not (eq (char-before (point-max)) ?\n)))
-             (let ((pos (point)))
-               ;; skip `=' but only if there are at least 7.
-               (when (> (skip-chars-backward "=") -7) (goto-char pos))))
-           (unless (looking-at "\\(\n\\|\\s-\\)*\\'")
-             (when (and (bolp) (not (bobp))) (backward-char))
-             (if (null arg) (delete-region (point) (point-max))
-               (skip-syntax-forward " ")
-               (delete-char numarg)
-               (unless (or (eobp) (looking-at comment-end-skip))
-                 ;; If there's something left but it doesn't look like
-                 ;; a comment-end any more, just remove it.
-                 (delete-region (point) (point-max)))))
-
-           ;; Unquote any nested end-comment.
-           (comment-quote-nested comment-start comment-end t)
-
-           ;; Eliminate continuation markers as well.
-           (when sre
-             (let* ((cce (comment-string-reverse (or comment-continue
-                                                     comment-start)))
-                    (erei (and box (comment-padleft cce 're)))
-                    (ere (and erei (concat "\\(" erei "\\)\\s-*$"))))
-               (goto-char (point-min))
-               (while (progn
-                        (if (and ere (re-search-forward
-                                      ere (line-end-position) t))
-                            (replace-match "" t t nil (if (match-end 2) 2 1))
-                          (setq ere nil))
-                        (forward-line 1)
-                        (re-search-forward sre (line-end-position) t))
-                 (replace-match "" t t nil (if (match-end 2) 2 1)))))
-           ;; Go to the end for the next comment.
-           (goto-char (point-max)))))
-      (set-marker end nil))))
+                 (delete-char (- numarg))
+                 (unless (or (bobp)
+                             (save-excursion (goto-char (point-min))
+                                             (looking-at comment-start-skip)))
+                   ;; If there's something left but it doesn't look like
+                   ;; a comment-start any more, just remove it.
+                   (delete-region (point-min) (point))))
+
+               ;; Remove the end-comment (and leading padding and such).
+               (goto-char (point-max)) (comment-enter-backward)
+               ;; Check for special `=' used sometimes in comment-box.
+               (when (and box-equal (not (eq (char-before (point-max)) ?\n)))
+                 (let ((pos (point)))
+                   ;; skip `=' but only if there are at least 7.
+                   (when (> (skip-chars-backward "=") -7) (goto-char pos))))
+               (unless (looking-at "\\(\n\\|\\s-\\)*\\'")
+                 (when (and (bolp) (not (bobp))) (backward-char))
+                 (if (null arg) (delete-region (point) (point-max))
+                   (skip-syntax-forward " ")
+                   (delete-char numarg)
+                   (unless (or (eobp) (looking-at comment-end-skip))
+                     ;; If there's something left but it doesn't look like
+                     ;; a comment-end any more, just remove it.
+                     (delete-region (point) (point-max)))))
+
+               ;; Unquote any nested end-comment.
+               (comment-quote-nested comment-start comment-end t)
+
+               ;; Eliminate continuation markers as well.
+               (when sre
+                 (let* ((cce (comment-string-reverse (or comment-continue
+                                                         comment-start)))
+                        (erei (and box (comment-padleft cce 're)))
+                        (ere (and erei (concat "\\(" erei "\\)\\s-*$"))))
+                   (goto-char (point-min))
+                   (while (progn
+                            (if (and ere (re-search-forward
+                                          ere (line-end-position) t))
+                                (replace-match "" t t nil (if (match-end 2) 2 1))
+                              (setq ere nil))
+                            (forward-line 1)
+                            (re-search-forward sre (line-end-position) t))
+                     (replace-match "" t t nil (if (match-end 2) 2 1)))))
+               ;; Go to the end for the next comment.
+               (goto-char (point-max)))))))
+      (set-marker end nil)))
 
 (defun comment-make-extra-lines (cs ce ccs cce min-indent max-indent &optional block)
   "Make the leading and trailing extra lines.
@@ -758,9 +864,9 @@ Space is added (and then removed) at the beginning for the text's
 indentation to be kept as it was before narrowing."
   (declare (debug t) (indent 2))
   (let ((bindent (make-symbol "bindent")))
-    `(let ((,bindent (save-excursion (goto-char beg) (current-column))))
+    `(let ((,bindent (save-excursion (goto-char ,beg) (current-column))))
        (save-restriction
-        (narrow-to-region beg end)
+        (narrow-to-region ,beg ,end)
         (goto-char (point-min))
         (insert (make-string ,bindent ? ))
         (prog1
@@ -794,7 +900,8 @@ of the region for CE and CS.
 INDENT indicates to put CS and CCS at the current indentation of the region
 rather than at left margin."
   ;;(assert (< beg end))
-  (let ((no-empty t))
+  (let ((no-empty (not (or (eq comment-empty-lines t)
+                          (and comment-empty-lines (zerop (length ce)))))))
     ;; Sanitize CE and CCE.
     (if (and (stringp ce) (string= "" ce)) (setq ce nil))
     (if (and (stringp cce) (string= "" cce)) (setq cce nil))
@@ -881,49 +988,52 @@ The strings used as comment starts are built from
         (block (nth 1 style))
         (multi (nth 0 style)))
     (save-excursion
-      ;; we use `chars' instead of `syntax' because `\n' might be
-      ;; of end-comment syntax rather than of whitespace syntax.
-      ;; sanitize BEG and END
-      (goto-char beg) (skip-chars-forward " \t\n\r") (beginning-of-line)
-      (setq beg (max beg (point)))
-      (goto-char end) (skip-chars-backward " \t\n\r") (end-of-line)
-      (setq end (min end (point)))
-      (if (>= beg end) (error "Nothing to comment"))
-
-      ;; sanitize LINES
-      (setq lines
-           (and
-            lines ;; multi
-            (progn (goto-char beg) (beginning-of-line)
-                   (skip-syntax-forward " ")
-                   (>= (point) beg))
-            (progn (goto-char end) (end-of-line) (skip-syntax-backward " ")
-                   (<= (point) end))
-            (or block (not (string= "" comment-end)))
-            (or block (progn (goto-char beg) (search-forward "\n" end t))))))
-
-    ;; don't add end-markers just because the user asked for `block'
-    (unless (or lines (string= "" comment-end)) (setq block nil))
-
-    (cond
-     ((consp arg) (uncomment-region beg end))
-     ((< numarg 0) (uncomment-region beg end (- numarg)))
-     (t
-      (setq numarg (if (and (null arg) (= (length comment-start) 1))
-                      add (1- numarg)))
-      (comment-region-internal
-       beg end
-       (let ((s (comment-padright comment-start numarg)))
-        (if (string-match comment-start-skip s) s
-          (comment-padright comment-start)))
-       (let ((s (comment-padleft comment-end numarg)))
-        (and s (if (string-match comment-end-skip s) s
-                 (comment-padright comment-end))))
-       (if multi (comment-padright comment-continue numarg))
-       (if multi (comment-padleft (comment-string-reverse comment-continue) numarg))
-       block
-       lines
-       (nth 3 style))))))
+      (if comment-region-function
+         (funcall comment-region-function beg end arg)
+       ;; we use `chars' instead of `syntax' because `\n' might be
+       ;; of end-comment syntax rather than of whitespace syntax.
+       ;; sanitize BEG and END
+       (goto-char beg) (skip-chars-forward " \t\n\r") (beginning-of-line)
+       (setq beg (max beg (point)))
+       (goto-char end) (skip-chars-backward " \t\n\r") (end-of-line)
+       (setq end (min end (point)))
+       (if (>= beg end) (error "Nothing to comment"))
+
+       ;; sanitize LINES
+       (setq lines
+             (and
+              lines ;; multi
+              (progn (goto-char beg) (beginning-of-line)
+                     (skip-syntax-forward " ")
+                     (>= (point) beg))
+              (progn (goto-char end) (end-of-line) (skip-syntax-backward " ")
+                     (<= (point) end))
+              (or block (not (string= "" comment-end)))
+              (or block (progn (goto-char beg) (search-forward "\n" end t))))))
+
+      ;; don't add end-markers just because the user asked for `block'
+      (unless (or lines (string= "" comment-end)) (setq block nil))
+
+      (cond
+       ((consp arg) (uncomment-region beg end))
+       ((< numarg 0) (uncomment-region beg end (- numarg)))
+       (t
+       (setq numarg (if (and (null arg) (= (length comment-start) 1))
+                        add (1- numarg)))
+       (comment-region-internal
+        beg end
+        (let ((s (comment-padright comment-start numarg)))
+          (if (string-match comment-start-skip s) s
+            (comment-padright comment-start)))
+        (let ((s (comment-padleft comment-end numarg)))
+          (and s (if (string-match comment-end-skip s) s
+                   (comment-padright comment-end))))
+        (if multi (comment-padright comment-continue numarg))
+        (if multi
+            (comment-padleft (comment-string-reverse comment-continue) numarg))
+        block
+        lines
+        (nth 3 style)))))))
 
 (defun comment-box (beg end &optional arg)
   "Comment out the BEG .. END region, putting it inside a box.
@@ -941,6 +1051,7 @@ end- comment markers additionally to what `comment-add' already specifies."
 in which case call `uncomment-region'.  If a prefix arg is given, it
 is passed on to the respective function."
   (interactive "*r\nP")
+  (comment-normalize-vars)
   (funcall (if (save-excursion ;; check for already commented region
                 (goto-char beg)
                 (comment-forward (point-max))
@@ -981,13 +1092,13 @@ Else, call `comment-indent'."
 This has no effect in modes that do not define a comment syntax."
   :type 'boolean)
 
-(defun comment-valid-prefix (prefix compos)
+(defun comment-valid-prefix-p (prefix compos)
   (or
    ;; Accept any prefix if the current comment is not EOL-terminated.
    (save-excursion (goto-char compos) (comment-forward) (not (bolp)))
    ;; Accept any prefix that starts with a comment-start marker.
    (string-match (concat "\\`[ \t]*\\(?:" comment-start-skip "\\)")
-                fill-prefix)))
+                prefix)))
 
 ;;;###autoload
 (defun comment-indent-new-line (&optional soft)
@@ -1041,7 +1152,7 @@ unless optional argument SOFT is non-nil."
         ;; a comment and the prefix is not a comment starter.
         ((and fill-prefix
               (or (not compos)
-                  (comment-valid-prefix fill-prefix compos)))
+                  (comment-valid-prefix-p fill-prefix compos)))
          (indent-to-left-margin)
          (insert-and-inherit fill-prefix))
         ;; If we're not inside a comment, just try to indent.
@@ -1097,4 +1208,5 @@ unless optional argument SOFT is non-nil."
 
 (provide 'newcomment)
 
+;;; arch-tag: 01e3320a-00c8-44ea-a696-8f8e7354c858
 ;;; newcomment.el ends here