(comment-start, comment-start-skip, comment-end): Made `defvar'.

(comment-style): Extract the choice out of comment-styles.
(comment-continue): Just a simple string now.
(comment-normalize-vars): Update for the new comment-continue.
(until, comment-end-quote-re): Removed.
(comment-quote-re, comment-quote-nested): New functions for quoting.
  These quote both the end and the start and also work for single-chars.
(comment-padright): Added lots of comments.
(comment-padleft): Added more comments.  Check comment-end rather than
  STR to determine whether N can be applied or not.
(uncomment-region): Rename BLOCK to BOX.
  Use the new comment-quote-nested.
  Use only one marker and properly set it back to nil.
  Be more picky when eliminating continuation markers.
This commit is contained in:
Stefan Monnier 2000-05-14 00:56:10 +00:00
parent b6389bfbf9
commit f52154007f

View file

@ -6,7 +6,7 @@
;; Maintainer: Stefan Monnier <monnier@cs.yale.edu>
;; Keywords: comment uncomment
;; Version: $Name: $
;; Revision: $Id: newcomment.el,v 1.6 1999/12/08 00:19:51 monnier Exp $
;; Revision: $Id: newcomment.el,v 1.7 2000/05/13 19:41:08 monnier Exp $
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@ -34,8 +34,6 @@
;; - the code assumes that bol is outside of any comment/string.
;; - uncomment-region with a numeric argument can render multichar
;; comment markers invalid.
;; - comment-box in C with a numeric argument generates wrong end-of-line
;; continuation markers.
;;; Todo:
@ -45,20 +43,10 @@
;; - uncomment-region with a consp (for blocks) or somehow make the
;; deletion of continuation markers less dangerous
;; - drop block-comment-<foo> unless it's really used
;; - uncomment-region on a part of a comment
;; - obey the numarg of uncomment-region for continuation markers
;; - uncomment-region on a subpart of a comment
;; - support gnu-style "multi-line with space in continue"
;; - document string-strip
;; - better document comment-continue (and probably turn it into a
;; simple string).
;; - don't overwrite comment-padding.
;; - better document comment-end-quote. Probably make it better
;; self-sufficient so that other quoting can be used.
;; - comment the padright/padleft.
;; - somehow allow comment-dwim to use the region even if transient-mark-mode
;; is not turned on.
;; - document comment-region-internal
;; - comment-quote-nested should quote both the start and end of comment.
;;; Code:
@ -73,7 +61,8 @@
"Non-nil if syntax-tables can be used instead of regexps.
Can also be `undecided' which means that a somewhat expensive test will
be used to try to determine whether syntax-tables should be trusted
to understand comments or not.")
to understand comments or not in the given buffer.
Major modes should set this variable.")
(defcustom comment-column 32
"*Column to indent right-margin comments to.
@ -84,28 +73,20 @@ can set the value for a particular mode using that mode's hook."
:group 'comment)
(make-variable-buffer-local 'comment-column)
(defcustom comment-start nil
"*String to insert to start a new comment, or nil if no comment syntax."
:type '(choice (const :tag "None" nil)
string)
:group 'comment)
(defvar comment-start nil
"*String to insert to start a new comment, or nil if no comment syntax.")
(defcustom comment-start-skip nil
(defvar comment-start-skip nil
"*Regexp to match the start of a comment plus everything up to its body.
If there are any \\(...\\) pairs, the comment delimiter text is held to begin
at the place matched by the close of the first pair."
:type '(choice (const :tag "None" nil)
regexp)
:group 'comment)
at the place matched by the close of the first pair.")
(defvar comment-end-skip nil
"Regexp to match the end of a comment plus everything up to its body.")
(defcustom comment-end ""
(defvar comment-end ""
"*String to insert to end a new comment.
Should be an empty string if comments are terminated by end-of-line."
:type 'string
:group 'comment)
Should be an empty string if comments are terminated by end-of-line.")
(defvar comment-indent-hook nil
"Obsolete variable for function to compute desired indentation for a comment.
@ -126,19 +107,22 @@ the comment's starting delimiter.")
This should be locally set by each major mode if needed.")
(defvar comment-continue nil
"Pair of strings to insert for multiline comments.")
(defvar comment-add 0
"How many more chars should be inserted by default.")
"Continuation string to insert for multiline comments.
This string will be added at the beginning of each line except the very
first one when commenting a region with a commenting style that allows
comments to span several lines.
It should generally have the same length as `comment-start' in order to
preserve indentation.
If it is nil a value will be automatically derived from `comment-start'
by replacing its first character with a space.")
(defvar comment-add 0
"How many more comment chars should be inserted by `comment-region'.
This determines the default value of the numeric argument of `comment-region'.
This should generally stay 0, except for a few modes like Lisp where
it can be convenient to set it to 1 so that regions are commented with
two semi-colons.")
(defcustom comment-style 'plain
"*Style to be used for inserting comments."
:group 'comment
:type '(choice (const plain)
(const indent)
(const aligned)
(const multi-line)
(const extra-line)
(const box)))
(defconst comment-styles
'((plain . (nil nil nil nil))
(indent . (nil nil nil t))
@ -146,12 +130,25 @@ This should be locally set by each major mode if needed.")
(multi-line . (t nil nil t))
(extra-line . (t nil t t))
(box . (t t t t)))
"Possible styles.
(STYLE . (MULTI ALIGN EXTRA INDENT).")
"Possible comment styles of the form (STYLE . (MULTI ALIGN EXTRA INDENT)).
STYLE should be a mnemonic symbol.
MULTI specifies that comments are allowed to span multiple lines.
ALIGN specifies that the `comment-end' markers should be aligned.
EXTRA specifies that an extra line should be used before and after the
region to comment (to put the `comment-end' and `comment-start').
INDENT specifies that the `comment-start' markers should not be put at the
left margin but at the current indentation of the region to comment.")
(defcustom comment-style 'plain
"*Style to be used for `comment-region'.
See `comment-styles' for a list of available styles."
:group 'comment
:type `(choice ,@(mapcar (lambda (s) `(const ,(car s))) comment-styles)))
(defcustom comment-padding 1
"Number of spaces `comment-region' puts between comment chars and text.
Can also be a string instead.
"Padding string that `comment-region' puts between comment chars and text.
Can also be an integer which will be automatically turned into a string
of the corresponding number of spaces.
Extra spacing between the comment characters and the comment text
makes the comment easier to read. Default is 1. nil means 0.")
@ -167,13 +164,15 @@ This is obsolete because you might as well use \\[newline-and-indent]."
;;;; Helpers
;;;;
(defun comment-string-strip (str before after)
(string-match (concat "\\`" (if before "\\s-*")
"\\(.*?\\)" (if after "\\s-*")
(defun comment-string-strip (str beforep afterp)
"Strip STR of any leading (if BEFOREP) and/or trailing (if AFTERP) space."
(string-match (concat "\\`" (if beforep "\\s-*")
"\\(.*?\\)" (if afterp "\\s-*")
"\\'") str)
(match-string 1 str))
(defun comment-string-reverse (s)
"Return the mirror image of string S, without any trailing space."
(comment-string-strip (concat (nreverse (string-to-list s))) nil t))
(defun comment-normalize-vars (&optional noerror)
@ -198,12 +197,9 @@ This is obsolete because you might as well use \\[newline-and-indent]."
;;(setq comment-start (comment-string-strip comment-start t nil))
;;(setq comment-end (comment-string-strip comment-end nil t))
;; comment-continue
(unless (or (car comment-continue) (string= comment-end ""))
(unless (or comment-continue (string= comment-end ""))
(set (make-local-variable 'comment-continue)
(cons (concat " " (substring comment-start 1))
nil)))
(when (and (car comment-continue) (null (cdr comment-continue)))
(setcdr comment-continue (comment-string-reverse (car comment-continue))))
(concat " " (substring comment-start 1))))
;; comment-skip regexps
(unless comment-start-skip
(set (make-local-variable 'comment-start-skip)
@ -220,22 +216,38 @@ This is obsolete because you might as well use \\[newline-and-indent]."
(regexp-quote (substring ce 1))
"\\)"))))))
(defmacro until (&rest body)
(let ((retsym (make-symbol "ret")))
`(let (,retsym)
(while (not (setq ,retsym (progn ,@body))))
,retsym)))
(def-edebug-spec until t)
(defun comment-quote-re (str unp)
(concat (regexp-quote (substring str 0 1))
"\\\\" (if unp "+" "*")
(regexp-quote (substring str 1))))
(defun comment-end-quote-re (str &optional re)
"Make a regexp that matches the (potentially quoted) STR comment-end.
The regexp has one group in it which matches RE right after the
potential quoting."
(setq str (comment-string-strip str t t))
(when (and comment-quote-nested (> (length str) 1))
(concat (regexp-quote (substring str 0 1))
"\\\\*\\(" re "\\)"
(regexp-quote (substring str 1)))))
(defun comment-quote-nested (cs ce unp)
"Quote or unquote nested comments.
If UNP is non-nil, unquote nested comment markers."
(setq cs (comment-string-strip cs t t))
(setq ce (comment-string-strip ce t t))
(when (and comment-quote-nested (> (length ce) 0))
(let ((re (concat (comment-quote-re ce unp)
"\\|" (comment-quote-re cs unp))))
(goto-char (point-min))
(while (re-search-forward re nil t)
(goto-char (match-beginning 0))
(forward-char 1)
(if unp (delete-char 1) (insert "\\"))
(when (= (length ce) 1)
;; If the comment-end is a single char, adding a \ after that
;; "first" char won't deactivate it, so we turn such a CE
;; into !CS. I.e. for pascal, we turn } into !{
(if (not unp)
(when (string= (match-string 0) ce)
(replace-match (concat "!" cs) t t))
(when (and (< (point-min) (match-beginning 0))
(string= (buffer-substring (1- (match-beginning 0))
(1- (match-end 0)))
(concat "!" cs)))
(backward-char 2)
(delete-char (- (match-end 0) (match-beginning 0)))
(insert ce))))))))
;;;;
;;;; Navigation
@ -335,9 +347,9 @@ If CONTINUE is non-nil, use the `comment-continuation' markers if any."
(interactive "*")
(let* ((empty (save-excursion (beginning-of-line)
(looking-at "[ \t]*$")))
(starter (or (and continue (car comment-continue))
(starter (or (and continue comment-continue)
(and empty block-comment-start) comment-start))
(ender (or (and continue (car comment-continue) "")
(ender (or (and continue comment-continue "")
(and empty block-comment-end) comment-end)))
(cond
((null starter)
@ -413,86 +425,102 @@ With prefix ARG, kill comments on that many lines starting with this one."
(defun comment-padright (str &optional n)
"Construct a string composed of STR plus `comment-padding'.
It contains N copies of the last non-whitespace chars of STR.
It also adds N copies of the last non-whitespace chars of STR.
If STR already contains padding, the corresponding amount is
ignored from `comment-padding'.
N defaults to 1.
ignored from `comment-padding'.
N defaults to 0.
If N is `re', a regexp is returned instead, that would match
the string for any N."
the string for any N."
(setq n (or n 0))
(when (and (stringp str) (not (string= "" str)))
;; Separate the actual string from any leading/trailing padding
(string-match "\\`\\s-*\\(.*?\\)\\s-*\\'" str)
(let ((s (match-string 1 str))
(lpad (substring str 0 (match-beginning 1)))
(rpad (concat (substring str (match-end 1))
(substring comment-padding
(let ((s (match-string 1 str)) ;actual string
(lpad (substring str 0 (match-beginning 1))) ;left padding
(rpad (concat (substring str (match-end 1)) ;original right padding
(substring comment-padding ;additional right padding
(min (- (match-end 0) (match-end 1))
(length comment-padding))))))
(if (symbolp n)
(concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
lpad "")
(regexp-quote s) "+"
(mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
rpad ""))
(concat lpad s (make-string n (aref str (1- (match-end 1)))) rpad)))))
(if (not (symbolp n))
(concat lpad s (make-string n (aref str (1- (match-end 1)))) rpad)
;; construct a regexp that would match anything from just S
;; to any possible output of this function for any N.
(concat (mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
lpad "") ;padding is not required
(regexp-quote s) "+" ;the last char of S might be repeated
(mapconcat (lambda (c) (concat (regexp-quote (string c)) "?"))
rpad "")))))) ;padding is not required
(defun comment-padleft (str &optional n)
"Construct a string composed of `comment-padding' plus STR.
It contains N copies of the last non-whitespace chars of STR.
It also adds N copies of the first non-whitespace chars of STR.
If STR already contains padding, the corresponding amount is
ignored from `comment-padding'.
N defaults to 1.
ignored from `comment-padding'.
N defaults to 0.
If N is `re', a regexp is returned instead, that would match
the string for any N."
(setq n (or n 0))
(when (and (stringp str) (not (string= "" str)))
;; Only separate the left pad because we assume there is no right pad.
(string-match "\\`\\s-*" str)
(let ((s (substring str (match-end 0)))
(pad (concat (substring comment-padding
(min (- (match-end 0) (match-beginning 0))
(length comment-padding)))
(match-string 0 str)))
(c (aref str (match-end 0)))
(multi (or (not comment-quote-nested) (string= comment-end "")
(> (length str) (1+ (match-end 0))))))
(if (symbolp n)
(concat "\\s-*"
(if multi (concat (regexp-quote (string c)) "*"))
(regexp-quote s))
(concat pad (when multi (make-string n c)) s)))))
(c (aref str (match-end 0))) ;the first non-space char of STR
;; We can only duplicate C if the comment-end has multiple chars
;; or if comments can be nested, else the comment-end `}' would
;; be turned into `}}}' where only the first ends the comment
;; and the rest becomes bogus junk.
(multi (not (and comment-quote-nested
;; comment-end is a single char
(string-match "\\`\\s-*\\S-\\s-*\\'" comment-end)))))
(if (not (symbolp n))
(concat pad (when multi (make-string n c)) s)
;; Construct a regexp that would match anything from just S
;; to any possible output of this function for any N.
;; We match any number of leading spaces because this regexp will
;; be used for uncommenting where we might want to remove
;; uncomment markers with arbitrary leading space (because
;; they were aligned).
(concat "\\s-*"
(if multi (concat (regexp-quote (string c)) "*"))
(regexp-quote s))))))
(defun uncomment-region (beg end &optional arg)
"Uncomment each line in the BEG..END region.
ARG is currently ignored."
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)))
(save-excursion
(goto-char beg)
(unless (markerp end) (setq end (copy-marker end)))
(setq end (copy-marker end))
(let ((numarg (prefix-numeric-value arg))
spt)
(while (and (< (point) end)
(setq spt (comment-search-forward end t)))
(let* ((ipt (point))
;; find the end of the comment
;; Find the end of the comment.
(ept (progn
(goto-char spt)
(unless (comment-forward)
(error "Can't find the comment end"))
(point-marker)))
(block nil)
(end-quote-re (comment-end-quote-re comment-end "\\\\"))
(ccs (car comment-continue))
(point)))
(box nil)
(ccs comment-continue)
(srei (comment-padright ccs 're))
(sre (and srei (concat "^\\s-*?\\(" srei "\\)"))))
(save-restriction
(narrow-to-region spt ept)
;; remove the comment-start
;; Remove the comment-start.
(goto-char ipt)
(skip-syntax-backward " ")
;; A box-comment starts with a looong comment-start marker.
(when (> (- (point) (point-min) (length comment-start)) 7)
(setq block t))
(setq box t))
(when (looking-at (regexp-quote comment-padding))
(goto-char (match-end 0)))
(when (and sre (looking-at (concat "\\s-*\n\\s-*" srei)))
@ -501,35 +529,36 @@ ARG is currently ignored."
(skip-syntax-backward " ")
(delete-char (- numarg)))
;; remove the end-comment (and leading padding and such)
;; Remove the end-comment (and leading padding and such).
(goto-char (point-max)) (comment-enter-backward)
(unless (string-match "\\`\\(\n\\|\\s-\\)*\\'"
(buffer-substring (point) ept))
(buffer-substring (point) (point-max)))
(when (and (bolp) (not (bobp))) (backward-char))
(if (null arg) (delete-region (point) ept)
(if (null arg) (delete-region (point) (point-max))
(skip-syntax-forward " ")
(delete-char numarg)))
;; unquote any nested end-comment
(when end-quote-re
(goto-char (point-min))
(while (re-search-forward end-quote-re nil t)
(delete-region (match-beginning 1) (match-end 1))))
;; Unquote any nested end-comment.
(comment-quote-nested comment-start comment-end t)
;; eliminate continuation markers as well
(let* ((cce (or (cdr comment-continue)
(comment-string-reverse comment-start)))
(erei (and block (comment-padleft cce 're)))
(ere (and erei (concat "\\(" erei "\\)\\s-*$")))
(re (if (and sre ere) (concat sre "\\|" ere) (or sre ere))))
(when re
;; 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))
;; there can't be a real SRE on the first line.
(when (and sre (looking-at sre)) (goto-char (match-end 0)))
(while (re-search-forward re nil t)
(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 the the end for the next comment
(goto-char (point-max))))))))
;; Go the 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)
(if block
@ -591,41 +620,54 @@ indentation to be kept as it was before narrowing."
(defun comment-region-internal (beg end cs ce
&optional ccs cce block lines indent)
"Comment region BEG..END.
CS and CE are the comment start resp. end string.
CCS and CCE are the comment continuation strings for the start resp. end
of lines (default to CS and CE).
BLOCK indicates that end of lines should be marked with either CCE, CE or CS
\(if CE is empty) and that those markers should be aligned.
LINES indicates that an extra lines will be used at the beginning and end
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))
;; sanitize ce and cce
;; Sanitize CE and CCE.
(if (and (stringp ce) (string= "" ce)) (setq ce nil))
(if (and (stringp cce) (string= "" cce)) (setq cce nil))
;; should we mark empty lines as well ?
;; If CE is empty, multiline cannot be used.
(unless ce (setq ccs nil cce nil))
;; Should we mark empty lines as well ?
(if (or ccs block lines) (setq no-empty nil))
;; make sure we have end-markers for BLOCK mode
;; Make sure we have end-markers for BLOCK mode.
(when block (unless ce (setq ce (comment-string-reverse cs))))
;; continuation defaults to the same
(if ccs (unless block (setq cce nil))
(setq ccs cs cce ce))
;; If BLOCK is not requested, we don't need CCE.
(unless block (setq cce nil))
;; Continuation defaults to the same as CS and CE.
(unless ccs (setq ccs cs cce ce))
(save-excursion
(goto-char end)
;; If the end is not at the end of a line and the comment-end
;; is implicit (i.e. a newline), explicitly insert a newline.
(unless (or ce (eolp)) (insert "\n") (indent-according-to-mode))
(comment-with-narrowing beg end
(let ((ce-quote-re (comment-end-quote-re comment-end))
(min-indent (point-max))
(let ((min-indent (point-max))
(max-indent 0))
(goto-char (point-min))
;; loop over all lines to find the needed indentations
(until
(unless (looking-at "[ \t]*$")
(setq min-indent (min min-indent (current-indentation))))
(when ce-quote-re
(let ((eol (line-end-position)))
(while (re-search-forward ce-quote-re eol 'move)
(incf eol)
(replace-match "\\" t t nil 1))))
(end-of-line)
(setq max-indent (max max-indent (current-column)))
(or (eobp) (progn (forward-line) nil)))
;; Quote any nested comment marker
(comment-quote-nested comment-start comment-end nil)
;; inserting ccs can change max-indent by (1- tab-width)
;; Loop over all lines to find the needed indentations.
(while
(progn
(unless (looking-at "[ \t]*$")
(setq min-indent (min min-indent (current-indentation))))
(end-of-line)
(setq max-indent (max max-indent (current-column)))
(not (or (eobp) (progn (forward-line) nil)))))
;; Inserting ccs can change max-indent by (1- tab-width).
(incf max-indent (+ (max (length cs) (length ccs)) -1 tab-width))
(unless indent (setq min-indent 0))
@ -639,17 +681,18 @@ indentation to be kept as it was before narrowing."
(goto-char (point-min))
;; Loop over all lines from BEG to END.
(until
(unless (and no-empty (looking-at "[ \t]*$"))
(move-to-column min-indent t)
(insert cs) (setq cs ccs)
(end-of-line)
(if (eobp) (setq cce ce))
(when cce
(when block (move-to-column max-indent t))
(insert cce)))
(end-of-line)
(or (eobp) (progn (forward-line) nil))))))))
(while
(progn
(unless (and no-empty (looking-at "[ \t]*$"))
(move-to-column min-indent t)
(insert cs) (setq cs ccs) ;switch to CCS after the first line
(end-of-line)
(if (eobp) (setq cce ce))
(when cce
(when block (move-to-column max-indent t))
(insert cce)))
(end-of-line)
(not (or (eobp) (progn (forward-line) nil))))))))))
(defun comment-region (beg end &optional arg)
"Comment or uncomment each line in the region.
@ -709,8 +752,8 @@ The strings used as comment starts are built from
(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 (car comment-continue) numarg))
(if multi (comment-padleft (cdr comment-continue) numarg))
(if multi (comment-padright comment-continue numarg))
(if multi (comment-padleft (comment-string-reverse comment-continue) numarg))
block
lines
(nth 3 style))))))
@ -829,6 +872,28 @@ unless optional argument SOFT is non-nil."
;;; Change Log:
;; $Log: newcomment.el,v $
;; Revision 1.7 2000/05/13 19:41:08 monnier
;; (comment-use-syntax): Change `maybe' to `undecided'.
;; (comment-quote-nested): New. Replaces comment-nested.
;; (comment-add): Turn into a mere defvar or a integer.
;; (comment-style): Change default to `plain'.
;; (comment-styles): Rename `plain' to `indent' and create a new plainer `plain'.
;; (comment-string-reverse): Use nreverse.
;; (comment-normalize-vars): Change `maybe' to `undecided', add comments.
;; Don't infer the setting of comment-nested anymore (the default for
;; comment-quote-nested is safe). Use comment-quote-nested.
;; (comment-end-quote-re): Use comment-quote-nested.
;; (comment-search-forward): Obey LIMIT.
;; (comment-indent): Don't skip forward further past comment-search-forward.
;; (comment-padleft): Use comment-quote-nested.
;; (comment-make-extra-lines): Use `cons' rather than `values'.
;; (comment-region-internal): New arg INDENT. Use line-end-position.
;; Avoid multiple-value-setq.
;; (comment-region): Follow the new comment-add semantics.
;; Don't do box comments any more.
;; (comment-box): New function.
;; (comment-dwim): Only do the region stuff is transient-mark-active.
;;
;; Revision 1.6 1999/12/08 00:19:51 monnier
;; various fixes and gratuitous movements.
;;