diff options
author | Gerd Moellmann <gerd@gnu.org> | 1999-12-12 18:24:19 +0000 |
---|---|---|
committer | Gerd Moellmann <gerd@gnu.org> | 1999-12-12 18:24:19 +0000 |
commit | 51f606dea8d768c59b6f78ba278c04978d712fd1 (patch) | |
tree | 6149934ab99d60f0d9fb8c15c3a236884125bc1a /lisp/progmodes/cc-cmds.el | |
parent | 3fc558970037e71143dcf92b8f5b2876a1386d57 (diff) | |
download | emacs-51f606dea8d768c59b6f78ba278c04978d712fd1.tar.gz |
Installed version 5.26
Diffstat (limited to 'lisp/progmodes/cc-cmds.el')
-rw-r--r-- | lisp/progmodes/cc-cmds.el | 1526 |
1 files changed, 1028 insertions, 498 deletions
diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el index 9877832804a..05d2599d957 100644 --- a/lisp/progmodes/cc-cmds.el +++ b/lisp/progmodes/cc-cmds.el @@ -1,8 +1,8 @@ ;;; cc-cmds.el --- user level commands for CC Mode -;; Copyright (C) 1985,87,92,93,94,95,96,97,98 Free Softare Foundation, Inc. +;; Copyright (C) 1985,1987,1992-1999 Free Software Foundation, Inc. -;; Authors: 1998 Barry A. Warsaw and Martin Stjernholm +;; Authors: 1998-1999 Barry A. Warsaw and Martin Stjernholm ;; 1992-1997 Barry A. Warsaw ;; 1987 Dave Detlefs and Stewart Clamen ;; 1985 Richard M. Stallman @@ -28,11 +28,17 @@ ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. - - (eval-when-compile - (require 'cc-defs)) + (let ((load-path + (if (and (boundp 'byte-compile-current-file) + (stringp byte-compile-current-file)) + (cons (file-name-directory byte-compile-current-file) + load-path) + load-path))) + (load "cc-defs" nil t))) +(require 'cc-engine) + (defun c-calculate-state (arg prevstate) ;; Calculate the new state of PREVSTATE, t or nil, based on arg. If ;; arg is nil or zero, toggle the state. If arg is negative, turn @@ -331,7 +337,7 @@ This function does various newline cleanups based on the value of ;; Do all appropriate clean ups (let ((here (point)) (pos (- (point-max) (point))) - mbeg mend) + mbeg mend tmp) ;; clean up empty defun braces (if (and c-auto-newline (memq 'empty-defun-braces c-cleanup-list) @@ -345,19 +351,36 @@ This function does various newline cleanups based on the value of ;; make sure matching open brace isn't in a comment (not (c-in-literal))) (delete-region (point) (1- here))) - ;; clean up brace-else-brace - (if (and c-auto-newline - (memq 'brace-else-brace c-cleanup-list) - (eq last-command-char ?\{) + ;; clean up brace-else-brace and brace-elseif-brace + (when (and c-auto-newline + (eq last-command-char ?\{) + (not (c-in-literal))) + (cond + ((and (memq 'brace-else-brace c-cleanup-list) (re-search-backward "}[ \t\n]*else[ \t\n]*{" nil t) (progn (setq mbeg (match-beginning 0) mend (match-end 0)) - (= mend here)) - (not (c-in-literal))) - (progn - (delete-region mbeg mend) - (insert "} else {"))) + (eq (match-end 0) here))) + (delete-region mbeg mend) + (insert "} else {")) + ((and (memq 'brace-elseif-brace c-cleanup-list) + (progn + (goto-char (1- here)) + (setq mend (point)) + (skip-chars-backward " \t\n") + (setq mbeg (point)) + (eq (char-before) ?\))) + (= (c-backward-token-1 1 t) 0) + (eq (char-after) ?\() + (progn + (setq tmp (point)) + (re-search-backward "}[ \t\n]*else[ \t\n]+if[ \t\n]*" + nil t)) + (eq (match-end 0) tmp)) + (delete-region mbeg mend) + (goto-char mbeg) + (insert " ")))) (goto-char (- (point-max) pos)) ) ;; does a newline go after the brace? @@ -598,8 +621,8 @@ or \"/ah\" string on the mode line, some newline cleanups are done if appropriate; see the variable `c-cleanup-list'. Also, the line is re-indented unless a numeric ARG is supplied, there -are non-whitespace characters present on the line after the colon, or -the colon is inserted inside a literal." +are non-whitespace characters present on the line after the +parenthesis, or the parenthesis is inserted inside a literal." (interactive "*P") (let (;; shut this up (c-echo-syntactic-information-p nil)) @@ -647,7 +670,8 @@ the colon is inserted inside a literal." (insert "} catch ("))) (goto-char (- (point-max) pos)) )) - (funcall old-blink-paren))))) + (if old-blink-paren + (funcall old-blink-paren)))))) @@ -692,6 +716,7 @@ Unlike the built-in `beginning-of-defun' this tries to be smarter about finding the char with open-parenthesis syntax that starts the defun." (interactive "p") + (unless arg (setq arg 1)) (if (< arg 0) (c-end-of-defun (- arg)) (while (> arg 0) @@ -706,13 +731,16 @@ defun." (cond (bod (goto-char bod)) (prevbod (goto-char prevbod)) - (t (goto-char (c-point 'bod))))) - (setq arg (1- arg)))) - (c-keep-region-active)) + (t (goto-char (point-min)) + (setq arg 0))) + (setq arg (1- arg)))) + (c-keep-region-active) + (= arg 0))) (defun c-end-of-defun (&optional arg) "Move forward to next end of defun. With argument, do it that many times. Negative argument -N means move back to Nth preceding end of defun. +Returns t unless search stops due to beginning or end of buffer. An end of a defun occurs right after the close-parenthesis that matches the open-parenthesis that starts a defun; see `beginning-of-defun'." @@ -722,16 +750,25 @@ the open-parenthesis that starts a defun; see `beginning-of-defun'." (if (< arg 0) (c-beginning-of-defun (- arg)) (while (> arg 0) - ;; skip down into the next defun-block - (while (and (c-safe (down-list 1) t) - (not (eq (char-before) ?{))) - (forward-char -1) - (c-forward-sexp)) - (c-beginning-of-defun 1) - (c-forward-sexp 1) - (setq arg (1- arg))) - (forward-line 1)) - (c-keep-region-active)) + (let ((pos (point)) + eol) + (while (and (c-safe (down-list 1) t) + (not (eq (char-before) ?{))) + ;; skip down into the next defun-block + (forward-char -1) + (c-forward-sexp)) + (c-beginning-of-defun 1) + (setq eol (c-point 'eol)) + (c-forward-sexp) + (if (< eol (point)) + ;; Don't move to next line for one line defuns. + (forward-line 1)) + (when (<= (point) pos) + (goto-char (point-max)) + (setq arg 0)) + (setq arg (1- arg)))) + (c-keep-region-active) + (= arg 0))) (defun c-beginning-of-statement (&optional count lim sentence-flag) @@ -759,18 +796,12 @@ comment." (save-excursion ;; Find the comment next to point if we're not in one. (if (> count 0) - ;; Finding a comment backwards is a bit cumbersome - ;; because `forward-comment' regards every newline as - ;; a comment when searching backwards (Emacs 19.34). - (while (and (progn (skip-chars-backward " \t") - (setq range (point)) - (setq range (if (forward-comment -1) - (cons (point) range) - nil))) - (= (char-after) ?\n))) + (setq range (if (c-forward-comment -1) + (cons (point) + (progn (c-forward-comment 1) (point))))) (skip-chars-forward " \t\n") (setq range (point)) - (setq range (if (forward-comment 1) + (setq range (if (c-forward-comment 1) (cons range (point)) nil))) (setq range (c-collect-line-comments range)))) @@ -780,23 +811,72 @@ comment." (if range (if (and sentence-flag (/= (char-syntax (char-after (car range))) ?\")) - (progn + (let* ((lit-type (c-literal-type range)) + (beg (save-excursion + (goto-char (car range)) + (looking-at (if (eq lit-type 'c) + comment-start-skip + (concat "\\(" + c-comment-prefix-regexp + "\\)[ \t]*"))) + (goto-char (match-end 0)) + (point))) + (end (save-excursion + (goto-char (- (cdr range) + (if (eq lit-type 'c) 2 1))) + (point)))) ;; move by sentence, but not past the limit of the literal (save-restriction - (narrow-to-region (save-excursion - (goto-char (car range)) - (looking-at comment-start-skip) - (goto-char (match-end 0)) - (point)) - (save-excursion - (goto-char (cdr range)) - (if (save-excursion - (goto-char (car range)) - (looking-at "/\\*")) - (backward-char 2)) - (skip-chars-backward " \t\n") - (point))) - (c-safe (forward-sentence (if (> count 0) -1 1)))) + (narrow-to-region beg end) + (c-safe (forward-sentence (if (< count 0) 1 -1))) + (if (and (memq lit-type '(c c++)) + ;; Check if we stopped due to a comment + ;; prefix and not a sentence end. + (/= (point) beg) + (save-excursion + (beginning-of-line) + (looking-at (concat "[ \t]*\\(" + c-comment-prefix-regexp + "\\)[ \t]*"))) + (>= (point) (match-beginning 0)) + (/= (match-beginning 1) (match-end 1)) + (or (< (point) (match-end 0)) + (and + (= (point) (match-end 0)) + ;; The comment prefix may contain + ;; characters that is regarded as end + ;; of sentence. + (or (eolp) + (and + (save-excursion + (forward-paragraph -1) + (< (point) (match-beginning 0))) + (save-excursion + (beginning-of-line) + (or (not (re-search-backward + sentence-end + (c-point 'bopl) + t)) + (< (match-end 0) + (c-point 'eol))))))))) + (setq count (+ count (if (< count 0) -1 1))) + (if (< count 0) + (progn + ;; In block comments, if there's only + ;; horizontal ws between the text and the + ;; comment ender, stop before it. Stop after + ;; the ender if there's either nothing or + ;; newlines between. + (when (and (eq lit-type 'c) (eq (point) end)) + (widen) + (skip-chars-backward " \t") + (when (or (eq (point) end) (bolp)) + (goto-char (cdr range))))) + (when (and (eq (point) beg) (looking-at "[ \t]*$")) + ;; Stop before instead of after the comment + ;; starter if nothing follows it. + (widen) + (goto-char (car range)))))) ;; See if we should escape the literal. (if (> count 0) (if (< (point) here) @@ -810,89 +890,143 @@ comment." (goto-char (if (> count 0) (car range) (cdr range))) (setq range nil)) ;; Below we do approximately the same as - ;; c-beginning-of-statement-1 and c-end-of-statement-1 and + ;; c-beginning-of-statement-1 and c-end-of-statement-1, and ;; perhaps they should be changed, but that'd likely break a ;; lot in cc-engine. (goto-char here) (if (> count 0) - (if (condition-case nil - ;; Stop before `{' and after `;', `{', `}' and - ;; `};' when not followed by `}' or `)', but on - ;; the other side of the syntactic ws. Also stop - ;; before `}', but only to catch comments. Move - ;; by sexps and move into parens. - (catch 'done - (let (last) - (while t - (setq last (point)) - (if (and (looking-at "[{}]") - (/= here last)) - (throw 'done (= (char-after) ?{))) - (c-backward-syntactic-ws) - (cond ((bobp) ; Must handle bob specially. - (if (= here last) - (if (= last (point-min)) - (throw 'done t) - (goto-char last) - (throw 'done nil)) - (goto-char last) - (throw 'done t))) - ((progn (backward-char) - (looking-at "[;{}]")) - (if (or (= here last) - (memq (char-after last) '(?\) ?}))) - (if (and (= (char-before) ?}) - (= (char-after) ?\;)) - (backward-char)) - (goto-char last) - (throw 'done t))) - ((= (char-syntax (char-after)) ?\") - (forward-char) - (c-backward-sexp)) - )))) - (error - (goto-char (point-min)) - t)) - (setq count (1- count))) - (if (condition-case nil - ;; Stop before `{' and `}', but on the other side of - ;; the syntactic ws, and after `;', `}' and `};'. - ;; Only stop before `{' if at top level or inside - ;; braces, though. Also stop after `{', but only to - ;; catch comments. Move by sexps and move into - ;; parens. + (condition-case nil + ;; Stop before `{' and after `;', `{', `}' and `};' + ;; when not followed by `}' or `)', but on the other + ;; side of the syntactic ws. Move by sexps and move + ;; into parens. Also stop before `#' when it's first + ;; on a line. + (let ((comment-pos (not sentence-flag)) + (large-enough (- (point-max))) + last last-below-line) (catch 'done - (let (last) - (while t - (setq last (point)) - (c-forward-syntactic-ws) - (cond ((= (char-after) ?{) + (while t + (setq last (point)) + (when (and (looking-at "{\\|^#") (/= here last)) + (unless (and c-special-brace-lists + (eq (char-after) ?{) + (c-looking-at-special-brace-list)) + (if (and (eq (char-after) ?#) + (numberp last-below-line) + (not (eq last-below-line here))) + (goto-char last-below-line)) + (throw 'done t))) + (if comment-pos + (c-forward-comment large-enough) + (when (c-forward-comment -1) + ;; Record position of first comment. + (save-excursion + (c-forward-comment 1) + (setq comment-pos (point))) + (c-forward-comment large-enough))) + (unless last-below-line + (if (save-excursion + (re-search-forward "\\(^\\|[^\\]\\)$" last t)) + (setq last-below-line last))) + (cond ((bobp) ; Must handle bob specially. + (if (= here last) + (throw 'done t) + (goto-char last) + (throw 'done t))) + ((progn (backward-char) + (looking-at "[;{}]")) + (if (and c-special-brace-lists + (eq (char-after) ?{) + (c-looking-at-special-brace-list)) + (skip-syntax-backward "w_") ; Speedup only. (if (or (= here last) - (save-excursion - (and (c-safe (progn (up-list -1) t)) - (/= (char-after) ?{)))) - (progn (forward-char) - (throw 'done nil)) + (memq (char-after last) '(?\) ?}))) + (if (and (eq (char-before) ?}) + (eq (char-after) ?\;)) + (backward-char)) (goto-char last) - (throw 'done t))) - ((and (= (char-after) ?}) - (/= here last)) - (goto-char last) - (throw 'done t)) - ((looking-at ";\\|};?") - (goto-char (match-end 0)) - (throw 'done t)) - ((= (char-syntax (char-after)) ?\") - (c-forward-sexp)) - (t - (forward-char)) - )))) - (error - (goto-char (point-max)) - t)) - (setq count (1+ count))))) + (throw 'done t)))) + ((= (char-syntax (char-after)) ?\") + (forward-char) + (c-backward-sexp)) + (t (skip-syntax-backward "w_")) ; Speedup only. + ))) + (if (and (numberp comment-pos) + (< (point) comment-pos)) + ;; We jumped over a comment that should be investigated. + (goto-char comment-pos) + (setq count (1- count)))) + (error + (goto-char (point-min)) + (setq count 0))) + (condition-case nil + ;; Stop before `{', `}', and `#' when it's first on a + ;; line, but on the other side of the syntactic ws, and + ;; after `;', `}' and `};'. Only stop before `{' if at + ;; top level or inside braces, though. Move by sexps + ;; and move into parens. Also stop at eol of lines + ;; starting with `#'. + (let ((comment-pos (not sentence-flag)) + (large-enough (point-max)) + last) + (catch 'done + (while t + (setq last (point)) + (if comment-pos + (c-forward-comment large-enough) + (if (progn + (skip-chars-forward " \t\n\r\f") + ;; Record position of first comment. + (setq comment-pos (point)) + (c-forward-comment 1)) + (c-forward-comment large-enough) + (setq comment-pos nil))) + (cond ((and (eq (char-after) ?{) + (not (and c-special-brace-lists + (c-looking-at-special-brace-list))) + (/= here last) + (save-excursion + (or (not (c-safe (up-list -1) t)) + (= (char-after) ?{)))) + (goto-char last) + (throw 'done t)) + ((and c-special-brace-lists + (eq (char-after) ?}) + (save-excursion + (and (c-safe (up-list -1) t) + (c-looking-at-special-brace-list)))) + (forward-char 1) + (skip-syntax-forward "w_")) ; Speedup only. + ((and (eq (char-after) ?}) + (/= here last)) + (goto-char last) + (throw 'done t)) + ((looking-at "^#") + (if (= here last) + (or (re-search-forward "\\(^\\|[^\\]\\)$" nil t) + (goto-char (point-max))) + (goto-char last)) + (throw 'done t)) + ((looking-at ";\\|};?") + (goto-char (match-end 0)) + (throw 'done t)) + ((= (char-syntax (char-after)) ?\") + (c-forward-sexp)) + (t + (forward-char 1) + (skip-syntax-forward "w_")) ; Speedup only. + ))) + (if (and (numberp comment-pos) + (> (point) comment-pos)) + ;; We jumped over a comment that should be investigated. + (goto-char comment-pos) + (setq count (1+ count)))) + (error + (goto-char (point-max)) + (setq count 0))) + )) ;; If we haven't moved we're near a buffer limit. - (when (= (point) here) + (when (and (not (zerop count)) (= (point) here)) (goto-char (if (> count 0) (point-min) (point-max))) (setq count 0))) ;; its possible we've been left up-buf of lim @@ -987,8 +1121,10 @@ comment." (cons c-comment-only-line-offset c-comment-only-line-offset)))) (apply '+ (mapcar 'c-get-offset syntax))))) - ;; CASE 4: use comment-column if previous line is a - ;; comment-only line indented to the left of comment-column + ;; CASE 4: If previous line is a comment-only line, use its + ;; indentation if it's greater than comment-column. Leave at + ;; least one space between the comment and the last nonblank + ;; character in any case. ((save-excursion (beginning-of-line) (and (not (bobp)) @@ -996,11 +1132,12 @@ comment." (skip-chars-forward " \t") (prog1 (looking-at c-comment-start-regexp) - (setq placeholder (point)))) - (goto-char placeholder) - (if (< (current-column) comment-column) - comment-column - (current-column))) + (setq placeholder (current-column)))) + (goto-char opoint) + (skip-chars-backward " \t") + (max (if (bolp) 0 (1+ (current-column))) + placeholder + comment-column)) ;; CASE 5: If comment-column is 0, and nothing but space ;; before the comment, align it at 0 rather than 1. ((progn @@ -1015,93 +1152,89 @@ comment." ))))) -;; for proposed new variable comment-line-break-function -(defun c-comment-line-break-function (&optional soft) - ;; we currently don't do anything with soft line breaks - (let ((literal (c-in-literal)) - at-comment-col) - (cond - ((eq literal 'string) - (insert ?\n)) - ((or (not c-comment-continuation-stars) - (not literal)) - (indent-new-comment-line soft)) - (t (let ((here (point)) - (leader c-comment-continuation-stars)) - (back-to-indentation) - ;; comment could be hanging - (if (not (c-in-literal)) - (progn - (forward-line 1) - (forward-comment -1) - (setq at-comment-col (= (current-column) comment-column)))) - ;; are we looking at a block or lines style comment? - (if (and (looking-at (concat "\\(" c-comment-start-regexp - "\\)[ \t]+")) - (string-equal (match-string 1) "//")) - ;; line style - (setq leader (match-string 0))) - (goto-char here) - (delete-region (progn (skip-chars-backward " \t") (point)) - (progn (skip-chars-forward " \t") (point))) - (newline) - ;; to avoid having an anchored comment that c-indent-line will - ;; trip up on - (insert " " leader) - (if at-comment-col - (indent-for-comment)) - (c-indent-line)))))) - -;; advice for indent-new-comment-line for older Emacsen -(or (boundp 'comment-line-break-function) - (defadvice indent-new-comment-line (around c-line-break-advice - activate preactivate) - "Calls c-comment-line-break-function if in a comment in CC Mode." - (if (or (not c-buffer-is-cc-mode) - (not (c-in-literal)) - (not c-comment-continuation-stars)) - ad-do-it - (c-comment-line-break-function (ad-get-arg 0))))) - ;; used by outline-minor-mode (defun c-outline-level () - ;; This so that `current-column' DTRT in otherwise-hidden text. - (let (buffer-invisibility-spec) - (save-excursion - (skip-chars-forward "\t ") - (current-column)))) + (save-excursion + (skip-chars-forward "\t ") + (current-column))) (defun c-up-conditional (count) "Move back to the containing preprocessor conditional, leaving mark behind. A prefix argument acts as a repeat count. With a negative argument, move forward to the end of the containing preprocessor conditional. -When going backwards, `#elif' is treated like `#else' followed by -`#if'. When going forwards, `#elif' is ignored." + +`#elif' is treated like `#else' followed by `#if', so the function +stops at them when going backward, but not when going forward." + (interactive "p") + (c-forward-conditional (- count) -1) + (c-keep-region-active)) + +(defun c-up-conditional-with-else (count) + "Move back to the containing preprocessor conditional, including `#else'. +Just like `c-up-conditional', except it also stops at `#else' +directives." + (interactive "p") + (c-forward-conditional (- count) -1 t) + (c-keep-region-active)) + +(defun c-down-conditional (count) + "Move forward into the next preprocessor conditional, leaving mark behind. +A prefix argument acts as a repeat count. With a negative argument, +move backward into the previous preprocessor conditional. + +`#elif' is treated like `#else' followed by `#if', so the function +stops at them when going forward, but not when going backward." (interactive "p") - (c-forward-conditional (- count) t) + (c-forward-conditional count 1) (c-keep-region-active)) -(defun c-backward-conditional (count &optional up-flag) +(defun c-down-conditional-with-else (count) + "Move forward into the next preprocessor conditional, including `#else'. +Just like `c-down-conditional', except it also stops at `#else' +directives." + (interactive "p") + (c-forward-conditional count 1 t) + (c-keep-region-active)) + +(defun c-backward-conditional (count &optional target-depth with-else) "Move back across a preprocessor conditional, leaving mark behind. A prefix argument acts as a repeat count. With a negative argument, move forward across a preprocessor conditional." (interactive "p") - (c-forward-conditional (- count) up-flag) + (c-forward-conditional (- count) target-depth with-else) (c-keep-region-active)) -(defun c-forward-conditional (count &optional up-flag) +(defun c-forward-conditional (count &optional target-depth with-else) "Move forward across a preprocessor conditional, leaving mark behind. A prefix argument acts as a repeat count. With a negative argument, -move backward across a preprocessor conditional." +move backward across a preprocessor conditional. + +`#elif' is treated like `#else' followed by `#if', except that the +nesting level isn't changed when tracking subconditionals. + +The optional argument TARGET-DEPTH specifies the wanted nesting depth +after each scan. I.e. if TARGET-DEPTH is -1, the function will move +out of the enclosing conditional. A non-integer non-nil TARGET-DEPTH +counts as -1. + +If the optional argument WITH-ELSE is non-nil, `#else' directives are +treated as conditional clause limits. Normally they are ignored." (interactive "p") (let* ((forward (> count 0)) (increment (if forward -1 1)) (search-function (if forward 're-search-forward 're-search-backward)) (new)) + (unless (integerp target-depth) + (setq target-depth (if target-depth -1 0))) (save-excursion (while (/= count 0) - (let ((depth (if up-flag 0 -1)) found) + (let ((depth 0) + ;; subdepth is the depth in "uninteresting" subtrees, + ;; i.e. those that takes us farther from the target + ;; depth instead of closer. + (subdepth 0) + found) (save-excursion ;; Find the "next" significant line in the proper direction. (while (and (not found) @@ -1112,36 +1245,50 @@ move backward across a preprocessor conditional." ;; precedes it. This is faster on account of ;; the fastmap feature of the regexp matcher. (funcall search-function - "#[ \t]*\\(if\\|elif\\|endif\\)" + "#[ \t]*\\(if\\|elif\\|endif\\|else\\)" nil t)) (beginning-of-line) ;; Now verify it is really a preproc line. - (if (looking-at "^[ \t]*#[ \t]*\\(if\\|elif\\|endif\\)") - (let ((prev depth)) - ;; Update depth according to what we found. - (beginning-of-line) - (cond ((looking-at "[ \t]*#[ \t]*endif") - (setq depth (+ depth increment))) - ((looking-at "[ \t]*#[ \t]*elif") - (if (and forward (= depth 0)) - (setq found (point)))) - (t (setq depth (- depth increment)))) - ;; If we are trying to move across, and we find an - ;; end before we find a beginning, get an error. - (if (and (< prev 0) (< depth prev)) - (error (if forward - "No following conditional at this level" - "No previous conditional at this level"))) + (if (looking-at "^[ \t]*#[ \t]*\\(if\\|elif\\|endif\\|else\\)") + (let (dchange (directive (match-string 1))) + (cond ((string= directive "if") + (setq dchange (- increment))) + ((string= directive "endif") + (setq dchange increment)) + ((= subdepth 0) + ;; When we're not in an "uninteresting" + ;; subtree, we might want to act on "elif" + ;; and "else" too. + (if (cond (with-else + ;; Always move toward the target depth. + (setq dchange + (if (> target-depth 0) 1 -1))) + ((string= directive "elif") + (setq dchange (- increment)))) + ;; Ignore the change if it'd take us + ;; into an "uninteresting" subtree. + (if (eq (> dchange 0) (<= target-depth 0)) + (setq dchange nil))))) + (when dchange + (when (or (/= subdepth 0) + (eq (> dchange 0) (<= target-depth 0))) + (setq subdepth (+ subdepth dchange))) + (setq depth (+ depth dchange)) + ;; If we are trying to move across, and we find an + ;; end before we find a beginning, get an error. + (if (and (< depth target-depth) (< dchange 0)) + (error (if forward + "No following conditional at this level" + "No previous conditional at this level")))) ;; When searching forward, start from next line so ;; that we don't find the same line again. (if forward (forward-line 1)) - ;; If this line exits a level of conditional, exit - ;; inner loop. - (if (< depth 0) + ;; We found something if we've arrived at the + ;; target depth. + (if (and dchange (= depth target-depth)) (setq found (point)))) ;; else - (if forward (forward-line 1)) - ))) + (if forward (forward-line 1))))) (or found (error "No containing preprocessor conditional")) (goto-char (setq new found))) @@ -1217,8 +1364,9 @@ of the expression is preserved. "Indent each line in balanced expression following point. Optional SHUTUP-P if non-nil, inhibits message printing and error checking." (interactive "*P") - (let ((here (point)) + (let ((here (point-marker)) end progress-p) + (set-marker-insertion-type here t) (unwind-protect (let ((c-echo-syntactic-information-p nil) ;keep quiet for speed (start (progn @@ -1259,7 +1407,8 @@ Optional SHUTUP-P if non-nil, inhibits message printing and error checking." (set-marker end nil)) (and progress-p (c-progress-fini 'c-indent-exp)) - (goto-char here)))) + (goto-char here) + (set-marker here nil)))) (defun c-indent-defun () "Re-indents the current top-level function def, struct or class declaration." @@ -1342,13 +1491,16 @@ Optional SHUTUP-P if non-nil, inhibits message printing and error checking." (setq sexpbeg (point)))) (if (and sexpbeg (< sexpbeg fence)) (setq sexpbeg fence))) - ;; check to see if the next line starts a - ;; comment-only line - (save-excursion - (forward-line 1) - (skip-chars-forward " \t") - (if (looking-at c-comment-start-regexp) - (setq sexpbeg (c-point 'bol)))) + ;; Since we move by sexps we might have missed + ;; comment-only lines. + (if sexpbeg + (save-excursion + (while (progn + (forward-line 1) + (skip-chars-forward " \t") + (< (point) sexpbeg)) + (if (looking-at c-comment-start-regexp) + (setq sexpbeg (c-point 'bol)))))) ;; If that sexp ends within the region, indent it all at ;; once, fast. (condition-case nil @@ -1560,12 +1712,288 @@ command to conveniently insert and align the necessary backslashes." (delete-region (1+ (point)) (progn (skip-chars-backward " \t") (point))))))) + +;;; Line breaking and paragraph filling. + +;; The filling code is based on a simple theory; leave the intricacies +;; of the text handling to the currently active mode for that +;; (e.g. adaptive-fill-mode or filladapt-mode) and do as little as +;; possible to make them work correctly wrt the comment and string +;; separators, one-line paragraphs etc. Unfortunately, when it comes +;; to it, there's quite a lot of special cases to handle which makes +;; the code anything but simple. The intention is that it will work +;; with any well-written text filling package that preserves a fill +;; prefix. +;; +;; We temporarily mask comment starters and enders as necessary for +;; the filling code to do its job on a seemingly normal text block. +;; We do _not_ mask the fill prefix, so it's up to the filling code to +;; preserve it correctly (especially important when filling C++ style +;; line comments). By default, we set up and use adaptive-fill-mode, +;; which is standard in all supported Emacs flavors. + +(defun c-guess-fill-prefix (lit-limits lit-type) + ;; Determine the appropriate comment fill prefix for a block or line + ;; comment. Return a cons of the prefix string and the column where + ;; it ends. If fill-prefix is set, it'll override. Note that this + ;; function also uses the value of point in some heuristics. + (let* ((here (point)) + (prefix-regexp (concat "[ \t]*\\(" + c-comment-prefix-regexp + "\\)[ \t]*")) + (comment-start-regexp (if (eq lit-type 'c++) + prefix-regexp + comment-start-skip)) + prefix-line comment-prefix res) + (cond + (fill-prefix + (setq res (cons fill-prefix + ;; Ugly way of getting the column after the fill + ;; prefix; it'd be nice with a current-column + ;; that works on strings.. + (let ((buffer-modified (buffer-modified-p)) + (buffer-undo-list t) + (start (point))) + (unwind-protect + (progn + (insert ?\n fill-prefix) + (current-column)) + (delete-region start (point)) + (set-buffer-modified-p buffer-modified)))))) + ((eq lit-type 'c++) + (save-excursion + ;; Set fallback for comment-prefix if none is found. + (setq comment-prefix "// ") + (beginning-of-line) + (if (> (point) (car lit-limits)) + ;; The current line is not the comment starter, so the + ;; comment has more than one line, and it can therefore be + ;; used to find the comment fill prefix. + (setq prefix-line (point)) + (goto-char (car lit-limits)) + (if (and (= (forward-line 1) 0) + (< (point) (cdr lit-limits))) + ;; The line after the comment starter is inside the + ;; comment, so we can use it. + (setq prefix-line (point)) + ;; The comment is only one line. Take the comment prefix + ;; from it and keep the indentation. + (goto-char (car lit-limits)) + (if (looking-at prefix-regexp) + (goto-char (match-end 0)) + (forward-char 2) + (skip-chars-forward " \t")) + (setq res + (if (eq (c-point 'boi) (car lit-limits)) + ;; There is only whitespace before the comment + ;; starter; take the prefix straight from this + ;; line. + (cons (buffer-substring-no-properties + (c-point 'bol) (point)) + (current-column)) + ;; There is code before the comment starter, so we + ;; have to temporarily insert and indent a new + ;; line to get the right space/tab mix in the + ;; indentation. + (let ((buffer-modified (buffer-modified-p)) + (buffer-undo-list t) + (prefix-len (- (point) (car lit-limits))) + tmp) + (unwind-protect + (progn + (goto-char (car lit-limits)) + (indent-to (prog1 (current-column) + (insert ?\n))) + (setq tmp (point)) + (forward-char prefix-len) + (cons (buffer-substring-no-properties + (c-point 'bol) (point)) + (current-column))) + (delete-region (car lit-limits) tmp) + (set-buffer-modified-p buffer-modified)))) + ))))) + (t + (save-excursion + (beginning-of-line) + (if (and (> (point) (car lit-limits)) + (not (and (looking-at "[ \t]*\\*/") + (eq (cdr lit-limits) (match-end 0))))) + ;; The current line is not the comment starter and + ;; contains more than just the ender, so it's good enough + ;; to be used for the comment fill prefix. + (setq prefix-line (point)) + (goto-char (car lit-limits)) + (if (or (/= (forward-line 1) 0) + (>= (point) (cdr lit-limits)) + (and (looking-at "[ \t]*\\*/") + (eq (cdr lit-limits) (match-end 0))) + (and (looking-at prefix-regexp) + (<= (1- (cdr lit-limits)) (match-end 0))) + (and (< here (point)) + (or (not (match-beginning 0)) + (looking-at "[ \t]*$")))) + ;; The comment is either one line or the next line + ;; contains just the comment ender. Also, if point is + ;; on the comment opener line and the following line is + ;; empty or doesn't match c-comment-prefix-regexp we + ;; assume that this is in fact a not yet closed one line + ;; comment, so we shouldn't look for the comment prefix + ;; on the next line. In these cases we have no + ;; information about a suitable comment prefix, so we + ;; resort to c-block-comment-prefix. + (setq comment-prefix (or c-block-comment-prefix "") + res (let ((buffer-modified (buffer-modified-p)) + (buffer-undo-list t) + tmp-pre tmp-post) + ;; The comment doesn't give any information + ;; about the indentation column. We'll have to + ;; temporarily insert a new comment line and + ;; indent it to find the correct column. + (unwind-protect + (progn + (goto-char (car lit-limits)) + (if (looking-at comment-start-regexp) + (goto-char (match-end 0)) + (forward-char 2) + (skip-chars-forward " \t")) + (when (eq (char-syntax (char-before)) ?\ ) + ;; If there's ws on the current + ;; line, we'll use it instead of + ;; what's ending comment-prefix. + (setq comment-prefix + (concat (substring comment-prefix + 0 (string-match + "\\s *\\'" + comment-prefix)) + (buffer-substring-no-properties + (save-excursion + (skip-chars-backward " \t") + (point)) + (point))))) + (setq tmp-pre (point-marker)) + ;; We insert an extra non-whitespace + ;; character before the line break and + ;; after comment-prefix in case it's + ;; "" or ends with whitespace. + (insert "x\n" comment-prefix ?x) + (setq tmp-post (point-marker)) + (c-indent-line) + (goto-char (1- tmp-post)) + (cons (buffer-substring-no-properties + (c-point 'bol) (point)) + (current-column))) + (when tmp-post + (delete-region tmp-pre tmp-post) + (set-marker tmp-pre nil) + (set-marker tmp-post nil)) + (set-buffer-modified-p buffer-modified)))) + ;; Otherwise the line after the comment starter is good + ;; enough to find the prefix in. + (setq prefix-line (point))))))) + (or res + (save-excursion + ;; prefix-line is the bol of a line on which we should try + ;; to find the prefix. + (let* (fb-string fb-endpos ; Contains any fallback prefix found. + (test-line + (lambda () + (when (and (looking-at prefix-regexp) + (< (match-end 0) (1- (cdr lit-limits)))) + (unless fb-string + (setq fb-string (buffer-substring-no-properties + (match-beginning 0) (match-end 0)) + fb-endpos (match-end 0))) + (unless (eq (match-end 0) (c-point 'eol)) + (throw 'found t)) + t)))) + (if (catch 'found + ;; Search for a line which has text after the prefix + ;; so that we get the proper amount of whitespace + ;; after it. We start with the current line, then + ;; search backwards, then forwards. + (goto-char prefix-line) + (when (and (funcall test-line) + (/= (match-end 1) (match-end 0))) + ;; If the current line doesn't have text but do + ;; have whitespace after the prefix, we'll use it. + (throw 'found t)) + (while (and (zerop (forward-line -1)) + (> (point) (car lit-limits))) + (funcall test-line)) + (goto-char prefix-line) + (while (and (zerop (forward-line 1)) + (< (point) (cdr lit-limits))) + (funcall test-line)) + nil) + ;; A good line with text after the prefix was found. + (cons (buffer-substring-no-properties (point) (match-end 0)) + (progn (goto-char (match-end 0)) (current-column))) + (if fb-string + ;; A good line wasn't found, but at least we have a + ;; fallback that matches the comment prefix regexp. + (cond ((string-match "\\s \\'" fb-string) + ;; There are ws after the prefix, so let's use it. + (cons fb-string + (progn (goto-char fb-endpos) (current-column)))) + ((progn + ;; Check if there's any whitespace padding + ;; on the comment start line that we can + ;; use after the prefix. + (goto-char (car lit-limits)) + (if (looking-at comment-start-regexp) + (goto-char (match-end 0)) + (forward-char 2) + (skip-chars-forward " \t")) + (eq (char-syntax (char-before)) ?\ )) + (setq fb-string (buffer-substring-no-properties + (save-excursion + (skip-chars-backward " \t") + (point)) + (point))) + (goto-char fb-endpos) + (skip-chars-backward " \t") + (let ((buffer-modified (buffer-modified-p)) + (buffer-undo-list t) + (tmp (point))) + ;; Got to mess in the buffer once again to + ;; ensure the column gets correct. :P + (unwind-protect + (progn + (insert fb-string) + (cons (buffer-substring-no-properties + (c-point 'bol) + (point)) + (current-column))) + (delete-region tmp (point))))) + (t + ;; Last resort: Just add a single space after + ;; the prefix. + (cons (concat fb-string " ") + (progn (goto-char fb-endpos) + (1+ (current-column)))))) + ;; The line doesn't match the comment prefix regexp. + (if comment-prefix + ;; We have a fallback for line comments that we must use. + (cons (concat (buffer-substring-no-properties + prefix-line (c-point 'boi)) + comment-prefix) + (progn (back-to-indentation) + (+ (current-column) (length comment-prefix)))) + ;; Assume we are dealing with a "free text" block + ;; comment where the lines doesn't have any comment + ;; prefix at all and we should just fill it as + ;; normal text. + '("" . 0))))))) + )) + (defun c-fill-paragraph (&optional arg) "Like \\[fill-paragraph] but handles C and C++ style comments. -If any of the current line is a comment or within a comment, -fill the comment or the paragraph of it that point is in, -preserving the comment indentation or line-starting decorations. +If any of the current line is a comment or within a comment, fill the +comment or the paragraph of it that point is in, preserving the +comment indentation or line-starting decorations (see the +`c-comment-prefix-regexp' and `c-block-comment-prefix' variables for +details). If point is inside multiline string literal, fill it. This currently does not respect escaped newlines, except for the special case when it @@ -1580,274 +2008,376 @@ If point is in any other situation, i.e. in normal code, do nothing. Optional prefix ARG means justify paragraph as well." (interactive "*P") - (let* ((point-save (point-marker)) - limits - comment-start-place - (first-line - ;; Check for obvious entry to comment. - (save-excursion - (beginning-of-line) - (skip-chars-forward " \t") - (and (looking-at comment-start-skip) - (setq comment-start-place (point))))) - (re1 "\\|\\([ \t]*/\\*[ \t]*\\|[ \t]*\\*/[ \t]*\\|[ \t/*]*\\)")) - (if (save-excursion - (beginning-of-line) - (looking-at ".*//")) - (let ((fill-prefix fill-prefix) - ;; Lines containing just a comment start or just an end - ;; should not be filled into paragraphs they are next - ;; to. - (paragraph-start (concat paragraph-start re1 "$")) - (paragraph-separate (concat paragraph-separate re1 "$"))) - (save-excursion - (beginning-of-line) - ;; Move up to first line of this comment. - (while (and (not (bobp)) - (looking-at "[ \t]*//[ \t]*[^ \t\n]")) - (forward-line -1)) - (if (not (looking-at ".*//[ \t]*[^ \t\n]")) - (forward-line 1)) - ;; Find the comment start in this line. - (re-search-forward "[ \t]*//[ \t]*") - ;; Set the fill-prefix to be what all lines except the first - ;; should start with. But do not alter a user set fill-prefix. - (if (null fill-prefix) - (setq fill-prefix (buffer-substring (match-beginning 0) - (match-end 0)))) - (save-restriction - ;; Narrow down to just the lines of this comment. - (narrow-to-region (c-point 'bol) - (save-excursion - (forward-line 1) - (while - (looking-at (regexp-quote fill-prefix)) - (forward-line 1)) - (point))) - (or (c-safe - ;; fill-paragraph sometimes fails to detect when we - ;; are between paragraphs. - (beginning-of-line) - (search-forward fill-prefix (c-point 'eol)) - (looking-at paragraph-separate)) - ;; Avoids recursion - (let (fill-paragraph-function) - (fill-paragraph arg)))))) - ;; else C style comments - (if (or first-line - ;; t if we enter a comment between start of function and - ;; this line. + (let (lit-limits lit-type fill + ;; beg and end limits the region to be filled. end is a marker. + beg end + ;; tmp-pre and tmp-post marks strings that are temporarily + ;; inserted at the start and end of the region. tmp-pre is a + ;; cons of the positions of the prepended string. tmp-post is + ;; a marker pointing to the single character of the appended + ;; string. + tmp-pre tmp-post + hang-ender-stuck) + ;; Restore point on undo. It's necessary since we do a lot of + ;; hidden inserts and deletes below that should be as transparent + ;; as possible. + (if (and buffer-undo-list (not (eq buffer-undo-list t))) + (setq buffer-undo-list (cons (point) buffer-undo-list))) + (save-excursion + (save-restriction + ;; Widen to catch comment limits correctly. + (widen) + (setq lit-limits (c-collect-line-comments (c-literal-limits nil t)) + lit-type (c-literal-type lit-limits))) + (forward-paragraph) + (setq end (point-marker)) + (backward-paragraph) + (setq beg (point))) + (when (and (>= (point) beg) (<= (point) end)) + (unwind-protect + (progn + (cond + ((eq lit-type 'c++) ; Line comment. (save-excursion - (setq limits (c-literal-limits)) - (and (consp limits) - (save-excursion - (goto-char (car limits)) - (looking-at c-comment-start-regexp)))) - ;; t if this line contains a comment starter. - (setq first-line - (save-excursion - (beginning-of-line) - (prog1 - (re-search-forward comment-start-skip - (save-excursion (end-of-line) - (point)) - t) - (setq comment-start-place (point))))) - ;; t if we're in the whitespace after a comment ender - ;; which ends its line. - (and (not limits) - (when (and (looking-at "[ \t]*$") - (save-excursion - (beginning-of-line) - (looking-at ".*\\*/[ \t]*$"))) - (save-excursion - (forward-comment -1) - (setq comment-start-place (point))) - t))) - ;; Inside a comment: fill one comment paragraph. - (let ((fill-prefix - (or - ;; Keep user set fill prefix if any. - fill-prefix - ;; The prefix for each line of this paragraph - ;; is the appropriate part of the start of this line, - ;; up to the column at which text should be indented. - (save-excursion - (beginning-of-line) - (if (looking-at ".*/\\*.*\\*/") - (progn (re-search-forward comment-start-skip) - (make-string (current-column) ?\ )) - (if first-line - (forward-line 1) - (if (and (looking-at "[ \t]*\\*/") - (not (save-excursion - (forward-line -1) - (looking-at ".*/\\*")))) - (forward-line -1))) - - (let ((line-width (progn (end-of-line) - (current-column)))) - (beginning-of-line) - (prog1 - (buffer-substring - (point) - - ;; How shall we decide where the end of the - ;; fill-prefix is? - (progn - (skip-chars-forward " \t*" (c-point 'eol)) - ;; kludge alert, watch out for */, in - ;; which case fill-prefix should *not* - ;; be "*"! - (if (and (eq (char-after) ?/) - (eq (char-before) ?*)) - (forward-char -1)) - (point))) - - ;; If the comment is only one line followed - ;; by a blank line, calling move-to-column - ;; above may have added some spaces and tabs - ;; to the end of the line; the fill-paragraph - ;; function will then delete it and the - ;; newline following it, so we'll lose a - ;; blank line when we shouldn't. So delete - ;; anything move-to-column added to the end - ;; of the line. We record the line width - ;; instead of the position of the old line - ;; end because move-to-column might break a - ;; tab into spaces, and the new characters - ;; introduced there shouldn't be deleted. - - ;; If you can see a better way to do this, - ;; please make the change. This seems very - ;; messy to me. - (delete-region (progn (move-to-column line-width) - (point)) - (progn (end-of-line) (point))))))))) - - ;; Lines containing just a comment start or just an end - ;; should not be filled into paragraphs they are next - ;; to. - (paragraph-start (if (c-major-mode-is 'java-mode) - (concat paragraph-start - re1 "\\(" - c-Java-javadoc-paragraph-start - "\\|$\\)") - (concat paragraph-start re1 "$"))) - (paragraph-separate (concat paragraph-separate re1 "$")) - (chars-to-delete 0) - ) - (save-restriction - ;; Don't fill the comment together with the code - ;; following it. So temporarily exclude everything - ;; before the comment start, and everything after the - ;; line where the comment ends. If comment-start-place - ;; is non-nil, the comment starter is there. Otherwise, - ;; point is inside the comment. - (narrow-to-region (save-excursion - (if comment-start-place - (goto-char comment-start-place) - (search-backward "/*")) - (if (and (not c-hanging-comment-starter-p) - (looking-at - (concat c-comment-start-regexp - "[ \t]*$"))) - (forward-line 1)) - ;; Protect text before the comment - ;; start by excluding it. Add - ;; spaces to bring back proper - ;; indentation of that point. - (let ((column (current-column))) - (prog1 (point) - (setq chars-to-delete column) - (insert-char ?\ column)))) - (save-excursion - (if comment-start-place - (goto-char (+ comment-start-place 2))) - (search-forward "*/" nil 'move) - (if (and (not c-hanging-comment-ender-p) - (save-excursion - (beginning-of-line) - (looking-at "[ \t]*\\*/"))) - (beginning-of-line) - (forward-line 1)) - (point))) - (or (c-safe - ;; fill-paragraph sometimes fails to detect when we - ;; are between paragraphs. - (beginning-of-line) - (search-forward fill-prefix (c-point 'eol)) - (looking-at paragraph-separate)) - ;; Avoids recursion - (let (fill-paragraph-function) - (fill-paragraph arg))) + ;; Fill to the comment or paragraph end, whichever + ;; comes first. + (set-marker end (min end (cdr lit-limits))) + (when (<= beg (car lit-limits)) + ;; The region to be filled includes the comment + ;; starter, so we must check it. + (goto-char (car lit-limits)) + (back-to-indentation) + (if (eq (point) (car lit-limits)) + ;; Include the first line in the fill. + (setq beg (c-point 'bol)) + ;; The first line contains code before the + ;; comment. We must fake a line that doesn't. + (setq tmp-pre t))) + )) + ((eq lit-type 'c) ; Block comment. (save-excursion - ;; Delete the chars we inserted to avoid clobbering - ;; the stuff before the comment start. - (goto-char (point-min)) - (if (> chars-to-delete 0) - (delete-region (point) (+ (point) chars-to-delete))) - ;; Find the comment ender (should be on last line of - ;; buffer, given the narrowing) and don't leave it on - ;; its own line, unless that's the style that's desired. - (goto-char (point-max)) - (forward-line -1) - (search-forward "*/" nil 'move) - (beginning-of-line) - (if (and c-hanging-comment-ender-p - (looking-at "[ \t]*\\*/")) - ;(delete-indentation))))) - (let ((fill-column (+ fill-column 9999))) + (when (>= end (cdr lit-limits)) + ;; The region to be filled includes the comment ender. + (goto-char (cdr lit-limits)) + (beginning-of-line) + (if (and (looking-at (concat "[ \t]*\\(" + c-comment-prefix-regexp + "\\)\\*/")) + (eq (cdr lit-limits) (match-end 0))) + ;; Leave the comment ender on its own line. + (set-marker end (point)) + ;; The comment ender should hang. Replace all + ;; cruft between it and the last word with a 'x' + ;; and include it in the fill. We'll change it + ;; back to a space afterwards. + (let ((ender-start (progn + (goto-char (cdr lit-limits)) + (skip-syntax-backward "^w ") + (point)))) + (goto-char (cdr lit-limits)) + (setq tmp-post (point-marker)) + (insert ?\n) + (set-marker end (point)) (forward-line -1) - (fill-region-as-paragraph (point) (point-max)) - ;; If fill-prefix ended with a `*', it may be - ;; taken away from the comment ender. We got to - ;; check this and put it back if that is the - ;; case. - (goto-char (- (point-max) 2)) - (if (not (= (char-before) ?*)) - (insert ?*)) - ))))) - ;; Else maybe a string. Fill it if it's a multiline string. - ;; FIXME: This currently doesn't handle escaped newlines. - ;; Doing that correctly is a bit tricky. - (if (and limits - (eq (char-syntax (char-after (car limits))) ?\") - (save-excursion - (goto-char (car limits)) - (end-of-line) - (< (point) (cdr limits)))) - (let (fill-paragraph-function) - (save-restriction - (narrow-to-region (save-excursion - (goto-char (1+ (car limits))) - (if (looking-at "\\\\$") - ;; Some DWIM: Leave the start - ;; line if it's nothing but an - ;; escaped newline. - (1+ (match-end 0)) - (point))) - (save-excursion - (goto-char (1- (cdr limits))) - ;; Inserting a newline and - ;; removing it again after - ;; fill-paragraph makes it more - ;; predictable. - (insert ?\n) - (point))) - ;; Do not compensate for the narrowed column. This - ;; way the literal will always be filled at the same - ;; column internally. - (fill-paragraph arg) - (goto-char (1- (point-max))) - (delete-char 1))) - ))) - (goto-char (marker-position point-save)) - (set-marker point-save nil) - ;; Always return t. This has the effect that if filling isn't - ;; done above, it isn't done at all, and it's therefore - ;; effectively disabled in normal code. - t)) + (if (and (looking-at (concat "[ \t]*\\(" + c-comment-prefix-regexp + "\\)[ \t]*")) + (eq ender-start (match-end 0))) + ;; The comment ender is prefixed by nothing + ;; but a comment line prefix. Remove it + ;; along with surrounding ws. + nil + (goto-char ender-start)) + (skip-chars-backward " \t\r\n") + (when (/= (point) ender-start) + (insert ?x) ; Insert first to keep marks right. + (delete-region (point) (1+ ender-start)) + (setq hang-ender-stuck t))))) + (when (<= beg (car lit-limits)) + ;; The region to be filled includes the comment starter. + (goto-char (car lit-limits)) + (if (looking-at (concat "\\(" comment-start-skip "\\)$")) + ;; Begin filling with the next line. + (setq beg (c-point 'bonl)) + ;; Fake the fill prefix in the first line. + (setq tmp-pre t))) + )) + ((eq lit-type 'string) ; String. + (save-excursion + (when (>= end (cdr lit-limits)) + (goto-char (1- (cdr lit-limits))) + (setq tmp-post (point-marker)) + (insert ?\n) + (set-marker end (point))) + (when (<= beg (car lit-limits)) + (goto-char (1+ (car lit-limits))) + (setq beg (if (looking-at "\\\\$") + ;; Leave the start line if it's + ;; nothing but an escaped newline. + (1+ (match-end 0)) + (point)))))) + (t (setq beg nil))) + (when tmp-pre + ;; Temporarily insert the fill prefix after the comment + ;; starter so that the first line looks like any other + ;; comment line in the narrowed region. + (setq fill (c-guess-fill-prefix lit-limits lit-type)) + (unless (string-match (concat "\\`[ \t]*\\(" + c-comment-prefix-regexp + "\\)[ \t]*\\'") + (car fill)) + ;; Oops, the prefix doesn't match the comment prefix + ;; regexp. This could produce very confusing + ;; results with adaptive fill packages together with + ;; the insert prefix magic below, since the prefix + ;; often doesn't appear at all. So let's warn about + ;; it. + (message "\ +Warning: `c-comment-prefix-regexp' doesn't match the comment prefix %S" + (car fill))) + ;; Find the right spot on the line, break it, insert + ;; the fill prefix and make sure we're back in the + ;; same column by temporarily prefixing the first word + ;; with a number of 'x'. + (save-excursion + (goto-char (car lit-limits)) + (if (looking-at (if (eq lit-type 'c++) + c-comment-prefix-regexp + comment-start-skip)) + (goto-char (match-end 0)) + (forward-char 2) + (skip-chars-forward " \t")) + (while (< (current-column) (cdr fill)) (forward-char 1)) + (let ((col (current-column))) + (setq beg (1+ (point)) + tmp-pre (list (point))) + (unwind-protect + (progn + (insert ?\n (car fill)) + (insert (make-string (- col (current-column)) ?x))) + (setcdr tmp-pre (point)))))) + (when beg + (let ((fill-paragraph-function + ;; Avoid infinite recursion. + (if (not (eq fill-paragraph-function 'c-fill-paragraph)) + fill-paragraph-function)) + (fill-prefix + (or fill-prefix + (when (and (eq lit-type 'c++) + (not (string-match + "\\`[ \t]*//" + (or (fill-context-prefix beg end) + "")))) + ;; Kludge: If the function that adapts the + ;; fill prefix doesn't produce the required + ;; comment starter for line comments, then + ;; force it by setting fill-prefix. + (car (or fill (c-guess-fill-prefix + lit-limits lit-type))))))) + ;; Preparations finally done! Now we can call the + ;; real fill function. + (save-restriction + (narrow-to-region beg end) + (fill-paragraph arg))))) + (when (consp tmp-pre) + (delete-region (car tmp-pre) (cdr tmp-pre))) + (when tmp-post + (save-excursion + (goto-char tmp-post) + (delete-char 1) + (when hang-ender-stuck + (skip-syntax-backward "^w ") + (forward-char -1) + (insert ?\ ) + (delete-char 1)) + (set-marker tmp-post nil))))) + (set-marker end nil)) + ;; Always return t. This has the effect that if filling isn't done + ;; above, it isn't done at all, and it's therefore effectively + ;; disabled in normal code. + t) + +(defun c-do-auto-fill () + ;; Do automatic filling if not inside a context where it should be + ;; ignored. + (let ((c-auto-fill-prefix + ;; The decision whether the line should be broken is actually + ;; done in c-indent-new-comment-line, which do-auto-fill + ;; calls to break lines. We just set this special variable + ;; so that we'll know when we're called from there. It's + ;; also used to detect whether fill-prefix is user set or + ;; generated automatically by do-auto-fill. + fill-prefix)) + (do-auto-fill))) + +(defun c-indent-new-comment-line (&optional soft) + "Break line at point and indent, continuing comment if within one. +If inside a comment and `comment-multi-line' is non-nil, the +indentation and line prefix are preserved (see the +`c-comment-prefix-regexp' and `c-block-comment-prefix' variables for +details). If inside a comment and `comment-multi-line' is nil, a new +comment of the same type is started on the next line and indented as +appropriate for comments. + +If a fill prefix is specified, it overrides all the above." + (interactive) + (let ((fill-prefix fill-prefix) + (do-line-break + (lambda () + (delete-region (progn (skip-chars-backward " \t") (point)) + (progn (skip-chars-forward " \t") (point))) + (if soft (insert-and-inherit ?\n) (newline 1)))) + ;; Already know the literal type and limits when called from + ;; c-context-line-break. + (c-lit-limits (if (boundp 'c-lit-limits) c-lit-limits)) + (c-lit-type (if (boundp 'c-lit-type) c-lit-type))) + (when (boundp 'c-auto-fill-prefix) + ;; Called from do-auto-fill. + (unless c-lit-limits + (setq c-lit-limits (c-literal-limits nil nil t))) + (unless c-lit-type + (setq c-lit-type (c-literal-type c-lit-limits))) + (if (memq (cond ((eq c-lit-type 'pound) + ;; Come to think about it, "pound" is a bit + ;; of a misnomer, so call it "cpp" instead + ;; in user interaction. + 'cpp) + ((null c-lit-type) 'code) + (t c-lit-type)) + c-ignore-auto-fill) + (setq fill-prefix t) ; Used as flag in the cond. + (if (null c-auto-fill-prefix) + (setq fill-prefix nil)))) + (cond ((eq fill-prefix t) + ;; A call from do-auto-fill which should be ignored. + ) + (fill-prefix + ;; A fill-prefix overrides anything. + (funcall do-line-break) + (insert-and-inherit fill-prefix)) + ((progn + (unless c-lit-limits + (setq c-lit-limits (c-literal-limits nil nil t))) + (unless c-lit-type + (setq c-lit-type (c-literal-type c-lit-limits))) + (memq c-lit-type '(c c++))) + (if comment-multi-line + ;; Inside a comment that should be continued. + (let ((fill (c-guess-fill-prefix + (setq c-lit-limits + (c-collect-line-comments c-lit-limits)) + c-lit-type)) + (pos (point))) + (if (save-excursion + (back-to-indentation) + (> (point) (car c-lit-limits)) + (looking-at c-comment-prefix-regexp)) + (progn + ;; Skip forward past the fill prefix in case + ;; we're standing in it. + (while (and (< (current-column) (cdr fill)) + (not (eolp))) + (forward-char 1)) + (if (> (point) (if (and (eq c-lit-type 'c) + (save-excursion + (forward-char -2) + (looking-at "\\*/"))) + (- (cdr c-lit-limits) 2) + (cdr c-lit-limits))) + (progn + ;; The skip takes us out of the comment; + ;; insert the fill prefix at bol instead + ;; and keep the position. + (setq pos (copy-marker pos t)) + (beginning-of-line) + (insert-and-inherit (car fill)) + (if soft (insert-and-inherit ?\n) (newline 1)) + (goto-char pos) + (set-marker pos nil)) + (funcall do-line-break) + (insert-and-inherit (car fill)))) + (funcall do-line-break) + (insert-and-inherit (car fill)))) + ;; Inside a comment that should be broken. + (let ((comment-start comment-start) + (comment-end comment-end) + col) + (if (eq c-lit-type 'c) + (unless (string-match "[ \t]*/\\*" comment-start) + (setq comment-start "/* " comment-end " */")) + (unless (string-match "[ \t]*//" comment-start) + (setq comment-start "// " comment-end ""))) + (setq col (save-excursion + (back-to-indentation) + (current-column))) + (funcall do-line-break) + (when (and comment-end (not (equal comment-end ""))) + (forward-char -1) + (insert-and-inherit comment-end) + (forward-char 1)) + ;; c-comment-indent may look at the current + ;; indentation, so let's start out with the same + ;; indentation as the previous one. + (indent-to col) + (insert-and-inherit comment-start) + (indent-for-comment)))) + (t + ;; Somewhere else in the code. + (let ((col (save-excursion + (while (progn (back-to-indentation) + (and (looking-at "^\\s *$") + (= (forward-line -1) 0)))) + (current-column)))) + (funcall do-line-break) + (indent-to col)))))) + +(defalias 'c-comment-line-break-function 'c-indent-new-comment-line) +(make-obsolete 'c-comment-line-break-function 'c-indent-new-comment-line) + +;; advice for indent-new-comment-line for older Emacsen +(unless (boundp 'comment-line-break-function) + (defadvice indent-new-comment-line (around c-line-break-advice + activate preactivate) + "Call `c-indent-new-comment-line' if in CC Mode." + (if (or (boundp 'c-inside-line-break-advice) + (not c-buffer-is-cc-mode)) + ad-do-it + (let (c-inside-line-break-advice) + (c-indent-new-comment-line (ad-get-arg 0)))))) + +(defun c-context-line-break () + "Do a line break suitable to the context. + +When point is outside a comment, insert a newline and indent according +to the syntactic context. + +When point is inside a comment, continue it with the appropriate +comment prefix (see the `c-comment-prefix-regexp' and +`c-block-comment-prefix' variables for details). The end of a +C++-style line comment doesn't count as inside the comment, though." + (interactive "*") + (let* ((c-lit-limits (c-literal-limits nil nil t)) + (c-lit-type (c-literal-type c-lit-limits))) + (if (or (eq c-lit-type 'c) + (and (eq c-lit-type 'c++) + (< (point) + (1- (cdr (setq c-lit-limits + (c-collect-line-comments c-lit-limits))))))) + (let ((comment-multi-line t) + (fill-prefix nil)) + (c-indent-new-comment-line)) + (delete-region (point) (progn (skip-chars-backward " \t") (point))) + (newline) + ;; c-indent-line may look at the current indentation, so let's + ;; start out with the same indentation as the previous line. + (let ((col (save-excursion + (forward-line -1) + (while (progn (back-to-indentation) + (and (looking-at "^\\s *$") + (= (forward-line -1) 0)))) + (current-column)))) + (indent-to col)) + (c-indent-line)))) (provide 'cc-cmds) |