Mercurial > emacs
diff lisp/progmodes/cc-cmds.el @ 18720:fc6d08b9bbe2
Initial revision
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Thu, 10 Jul 1997 07:54:06 +0000 |
parents | |
children | 6e04c0670f55 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/progmodes/cc-cmds.el Thu Jul 10 07:54:06 1997 +0000 @@ -0,0 +1,1369 @@ +;;; cc-cmds.el --- user level commands for CC Mode + +;; Copyright (C) 1985,87,92,93,94,95,96,97 Free Software Foundation, Inc. + +;; Authors: 1992-1997 Barry A. Warsaw +;; 1987 Dave Detlefs and Stewart Clamen +;; 1985 Richard M. Stallman +;; Maintainer: cc-mode-help@python.org +;; Created: 22-Apr-1997 (split from cc-mode.el) +;; Version: 5.12 +;; Keywords: c languages oop + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + + +(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 + ;; the state off, and if arg is positive, turn the state on + (if (or (not arg) + (zerop (setq arg (prefix-numeric-value arg)))) + (not prevstate) + (> arg 0))) + +;; Auto-newline and hungry-delete +(defun c-toggle-auto-state (arg) + "Toggle auto-newline feature. +Optional numeric ARG, if supplied turns on auto-newline when positive, +turns it off when negative, and just toggles it when zero. + +When the auto-newline feature is enabled (as evidenced by the `/a' or +`/ah' on the modeline after the mode name) newlines are automatically +inserted after special characters such as brace, comma, semi-colon, +and colon." + (interactive "P") + (setq c-auto-newline (c-calculate-state arg c-auto-newline)) + (c-update-modeline) + (c-keep-region-active)) + +(defun c-toggle-hungry-state (arg) + "Toggle hungry-delete-key feature. +Optional numeric ARG, if supplied turns on hungry-delete when positive, +turns it off when negative, and just toggles it when zero. + +When the hungry-delete-key feature is enabled (as evidenced by the +`/h' or `/ah' on the modeline after the mode name) the delete key +gobbles all preceding whitespace in one fell swoop." + (interactive "P") + (setq c-hungry-delete-key (c-calculate-state arg c-hungry-delete-key)) + (c-update-modeline) + (c-keep-region-active)) + +(defun c-toggle-auto-hungry-state (arg) + "Toggle auto-newline and hungry-delete-key features. +Optional numeric ARG, if supplied turns on auto-newline and +hungry-delete when positive, turns them off when negative, and just +toggles them when zero. + +See `c-toggle-auto-state' and `c-toggle-hungry-state' for details." + (interactive "P") + (setq c-auto-newline (c-calculate-state arg c-auto-newline)) + (setq c-hungry-delete-key (c-calculate-state arg c-hungry-delete-key)) + (c-update-modeline) + (c-keep-region-active)) + + +;; Electric keys + +;; Note: In XEmacs 20.3 the Delete and BackSpace keysyms have been +;; separated and "\177" is no longer an alias for both keys. Also, +;; the variable delete-key-deletes-forward controls in which direction +;; the Delete keysym deletes characters. The functions +;; c-electric-delete and c-electric-backspace attempt to deal with +;; this new functionality. For Emacs 19 and XEmacs 19 backwards +;; compatibility, the old behavior has moved to c-electric-backspace +;; and c-backspace-function. + +(defun c-electric-backspace (arg) + "Deletes preceding character or whitespace. +If `c-hungry-delete-key' is non-nil, as evidenced by the \"/h\" or +\"/ah\" string on the mode line, then all preceding whitespace is +consumed. If however an ARG is supplied, or `c-hungry-delete-key' is +nil, or point is inside a literal then the function in the variable +`c-backspace-function' is called. + +See also \\[c-electric-delete]." + (interactive "P") + (if (or (not c-hungry-delete-key) + arg + (c-in-literal)) + (funcall c-backspace-function (prefix-numeric-value arg)) + (let ((here (point))) + (skip-chars-backward " \t\n") + (if (/= (point) here) + (delete-region (point) here) + (funcall c-backspace-function 1) + )))) + +(defun c-electric-delete (arg) + "Deletes preceding or following character or whitespace. + +The behavior of this function depends on the variable +`delete-key-deletes-forward'. If this variable is nil (or does not +exist, as in older Emacsen), then this function behaves identical to +\\[c-electric-backspace]. + +If `delete-key-deletes-forward' is non-nil, then deletion occurs in +the forward direction. So if `c-hungry-delete-key' is non-nil, as +evidenced by the \"/h\" or \"/ah\" string on the mode line, then all +following whitespace is consumed. If however an ARG is supplied, or +`c-hungry-delete-key' is nil, or point is inside a literal then the +function in the variable `c-delete-function' is called." + (interactive "P") + (if (and (boundp 'delete-key-deletes-forward) + delete-key-deletes-forward) + (if (or (not c-hungry-delete-key) + arg + (c-in-literal)) + (funcall c-delete-function (prefix-numeric-value arg)) + (let ((here (point))) + (skip-chars-forward " \t\n") + (if (/= (point) here) + (delete-region (point) here) + (funcall c-delete-function 1)))) + ;; act just like c-electric-backspace + (c-electric-backspace arg))) + +(defun c-electric-pound (arg) + "Electric pound (`#') insertion. +Inserts a `#' character specially depending on the variable +`c-electric-pound-behavior'. If a numeric ARG is supplied, or if +point is inside a literal, nothing special happens." + (interactive "P") + (if (or (c-in-literal) + arg + (not (memq 'alignleft c-electric-pound-behavior))) + ;; do nothing special + (self-insert-command (prefix-numeric-value arg)) + ;; place the pound character at the left edge + (let ((pos (- (point-max) (point))) + (bolp (bolp))) + (beginning-of-line) + (delete-horizontal-space) + (insert-char last-command-char 1) + (and (not bolp) + (goto-char (- (point-max) pos))) + ))) + +(defun c-electric-brace (arg) + "Insert a brace. + +If the auto-newline feature is turned on, as evidenced by the \"/a\" +or \"/ah\" string on the mode line, newlines are inserted before and +after braces based on the value of `c-hanging-braces-alist'. + +Also, the line is re-indented unless a numeric ARG is supplied, there +are non-whitespace characters present on the line after the brace, or +the brace is inserted inside a literal." + (interactive "P") + (let* ((c-state-cache (c-parse-state)) + (safepos (c-safe-position (point) c-state-cache)) + (literal (c-in-literal safepos))) + ;; if we're in a literal, or we're not at the end of the line, or + ;; a numeric arg is provided, or auto-newlining is turned off, + ;; then just insert the character. + (if (or literal arg +; (not c-auto-newline) + (not (looking-at "[ \t]*$"))) + (self-insert-command (prefix-numeric-value arg)) + (let* ((syms '(class-open class-close defun-open defun-close + inline-open inline-close brace-list-open brace-list-close + brace-list-intro brace-list-entry block-open block-close + substatement-open statement-case-open + extern-lang-open extern-lang-close)) + ;; we want to inhibit blinking the paren since this will + ;; be most disruptive. we'll blink it ourselves later on + (old-blink-paren blink-paren-function) + blink-paren-function + (insertion-point (point)) + delete-temp-newline + (preserve-p (eq 32 (char-syntax (char-before)))) + ;; shut this up too + (c-echo-syntactic-information-p nil) + (syntax (progn + ;; only insert a newline if there is + ;; non-whitespace behind us + (if (save-excursion + (skip-chars-backward " \t") + (not (bolp))) + (progn (newline) + (setq delete-temp-newline t))) + (self-insert-command (prefix-numeric-value arg)) + ;; state cache doesn't change + (c-guess-basic-syntax))) + (newlines (and + c-auto-newline + (or (c-lookup-lists syms syntax c-hanging-braces-alist) + '(ignore before after))))) + ;; If syntax is a function symbol, then call it using the + ;; defined semantics. + (if (and (not (consp (cdr newlines))) + (functionp (cdr newlines))) + (let ((c-syntactic-context syntax)) + (setq newlines + (funcall (cdr newlines) (car newlines) insertion-point)))) + ;; does a newline go before the open brace? + (if (memq 'before newlines) + ;; we leave the newline we've put in there before, + ;; but we need to re-indent the line above + (let ((pos (- (point-max) (point))) + (here (point)) + (c-state-cache c-state-cache)) + (forward-line -1) + ;; we may need to update the cache. this should still be + ;; faster than recalculating the state in many cases + (save-excursion + (save-restriction + (narrow-to-region here (point)) + (if (and (c-safe (progn (backward-up-list -1) t)) + (memq (char-before) '(?\) ?})) + (progn (widen) + (c-safe (progn (forward-sexp -1) t)))) + (setq c-state-cache + (c-hack-state (point) 'open c-state-cache)) + (if (and (car c-state-cache) + (not (consp (car c-state-cache))) + (<= (point) (car c-state-cache))) + (setq c-state-cache (cdr c-state-cache)) + )))) + (let ((here (point)) + (shift (c-indent-line))) + (setq c-state-cache (c-adjust-state (c-point 'bol) here + (- shift) c-state-cache))) + (goto-char (- (point-max) pos)) + ;; if the buffer has changed due to the indentation, we + ;; need to recalculate syntax for the current line, but + ;; we won't need to update the state cache. + (if (/= (point) here) + (setq syntax (c-guess-basic-syntax)))) + ;; must remove the newline we just stuck in (if we really did it) + (and delete-temp-newline + (save-excursion + ;; if there is whitespace before point, then preserve + ;; at least one space. + (delete-indentation) + (just-one-space) + (if (not preserve-p) + (delete-char -1)))) + ;; since we're hanging the brace, we need to recalculate + ;; syntax. Update the state to accurately reflect the + ;; beginning of the line. We punt if we cross any open or + ;; closed parens because its just too hard to modify the + ;; known state. This limitation will be fixed in v5. + (save-excursion + (let ((bol (c-point 'bol))) + (if (zerop (car (parse-partial-sexp bol (1- (point))))) + (setq c-state-cache (c-whack-state bol c-state-cache) + syntax (c-guess-basic-syntax)) + ;; gotta punt. this requires some horrible kludgery + (beginning-of-line) + (makunbound 'c-state-cache) + (setq c-state-cache (c-parse-state) + syntax nil)))) + ) + ;; now adjust the line's indentation. don't update the state + ;; cache since c-guess-basic-syntax isn't called when the + ;; syntax is passed to c-indent-line + (let ((here (point)) + (shift (c-indent-line syntax))) + (setq c-state-cache (c-adjust-state (c-point 'bol) here + (- shift) c-state-cache))) + ;; Do all appropriate clean ups + (let ((here (point)) + (pos (- (point-max) (point))) + mbeg mend) + ;; clean up empty defun braces + (if (and c-auto-newline + (memq 'empty-defun-braces c-cleanup-list) + (eq last-command-char ?\}) + (c-intersect-lists '(defun-close class-close inline-close) + syntax) + (progn + (forward-char -1) + (skip-chars-backward " \t\n") + (eq (char-before) ?\{)) + ;; 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 ?\{) + (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 {"))) + ;; clean up brace-elseif-brace + (if (and c-auto-newline + (memq 'brace-elseif-brace c-cleanup-list) + (eq last-command-char ?\{) + (re-search-backward "}[ \t\n]*else[ \t\n]+if[ \t\n]*" nil t) + (save-excursion + (goto-char (match-end 0)) + (c-safe (forward-sexp 1)) + (skip-chars-forward " \t\n") + (setq mbeg (match-beginning 0) + mend (match-end 0)) + (= here (1+ (point)))) + (not (c-in-literal))) + (progn + (delete-region mbeg mend) + (insert "} else if "))) + (goto-char (- (point-max) pos)) + ) + ;; does a newline go after the brace? + (if (memq 'after newlines) + (progn + (newline) + ;; update on c-state-cache + (let* ((bufpos (- (point) 2)) + (which (if (eq (char-after bufpos) ?{) 'open 'close)) + (c-state-cache (c-hack-state bufpos which c-state-cache))) + (c-indent-line)))) + ;; blink the paren + (and (eq last-command-char ?\}) + old-blink-paren + (save-excursion + (c-backward-syntactic-ws safepos) + (funcall old-blink-paren))) + )))) + +(defun c-electric-slash (arg) + "Insert a slash character. +If slash is second of a double-slash C++ style comment introducing +construct, and we are on a comment-only-line, indent line as comment. +If numeric ARG is supplied or point is inside a literal, indentation +is inhibited." + (interactive "P") + (let ((indentp (and (not arg) + (eq (char-before) ?/) + (eq last-command-char ?/) + (not (c-in-literal)))) + ;; shut this up + (c-echo-syntactic-information-p nil)) + (self-insert-command (prefix-numeric-value arg)) + (if indentp + (c-indent-line)))) + +(defun c-electric-star (arg) + "Insert a star character. +If the star is the second character of a C style comment introducing +construct, and we are on a comment-only-line, indent line as comment. +If numeric ARG is supplied or point is inside a literal, indentation +is inhibited." + (interactive "P") + (self-insert-command (prefix-numeric-value arg)) + ;; if we are in a literal, or if arg is given do not re-indent the + ;; current line, unless this star introduces a comment-only line. + (if (and (not arg) + (memq (c-in-literal) '(c)) + (eq (char-before) ?*) + (save-excursion + (forward-char -1) + (skip-chars-backward "*") + (if (eq (char-before) ?/) + (forward-char -1)) + (skip-chars-backward " \t") + (bolp))) + ;; shut this up + (let (c-echo-syntactic-information-p) + (c-indent-line)) + )) + +(defun c-electric-semi&comma (arg) + "Insert a comma or semicolon. +When the auto-newline feature is turned on, as evidenced by the \"/a\" +or \"/ah\" string on the mode line, a newline might be inserted. See +the variable `c-hanging-semi&comma-criteria' for how newline insertion +is determined. + +When semicolon is inserted, the line is re-indented unless a numeric +arg is supplied, point is inside a literal, or there are +non-whitespace characters on the line following the semicolon." + (interactive "P") + (let* ((lim (c-most-enclosing-brace (c-parse-state))) + (literal (c-in-literal lim)) + (here (point)) + ;; shut this up + (c-echo-syntactic-information-p nil)) + (if (or literal + arg + (not (looking-at "[ \t]*$"))) + (self-insert-command (prefix-numeric-value arg)) + ;; do some special stuff with the character + (self-insert-command (prefix-numeric-value arg)) + ;; do all cleanups, reindentations, and newline insertions, but + ;; only if c-auto-newline is turned on + (if (not c-auto-newline) nil + ;; clean ups + (let ((pos (- (point-max) (point)))) + (if (and (or (and + (eq last-command-char ?,) + (memq 'list-close-comma c-cleanup-list)) + (and + (eq last-command-char ?\;) + (memq 'defun-close-semi c-cleanup-list))) + (progn + (forward-char -1) + (skip-chars-backward " \t\n") + (eq (char-before) ?})) + ;; make sure matching open brace isn't in a comment + (not (c-in-literal lim))) + (delete-region (point) here)) + (goto-char (- (point-max) pos))) + ;; re-indent line + (c-indent-line) + ;; check to see if a newline should be added + (let ((criteria c-hanging-semi&comma-criteria) + answer add-newline-p) + (while criteria + (setq answer (funcall (car criteria))) + ;; only nil value means continue checking + (if (not answer) + (setq criteria (cdr criteria)) + (setq criteria nil) + ;; only 'stop specifically says do not add a newline + (setq add-newline-p (not (eq answer 'stop))) + )) + (if add-newline-p + (progn (newline) + (c-indent-line))) + ))))) + +(defun c-electric-colon (arg) + "Insert a colon. + +If the auto-newline feature is turned on, as evidenced by the \"/a\" +or \"/ah\" string on the mode line, newlines are inserted before and +after colons based on the value of `c-hanging-colons-alist'. + +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. + +This function cleans up double colon scope operators based on the +value of `c-cleanup-list'." + (interactive "P") + (let* ((bod (c-point 'bod)) + (literal (c-in-literal bod)) + syntax newlines + ;; shut this up + (c-echo-syntactic-information-p nil)) + (if (or literal + arg + (not (looking-at "[ \t]*$"))) + (self-insert-command (prefix-numeric-value arg)) + ;; insert the colon, then do any specified cleanups + (self-insert-command (prefix-numeric-value arg)) + (let ((pos (- (point-max) (point))) + (here (point))) + (if (and c-auto-newline + (memq 'scope-operator c-cleanup-list) + (eq (char-before) ?:) + (progn + (forward-char -1) + (skip-chars-backward " \t\n") + (eq (char-before) ?:)) + (not (c-in-literal)) + (not (eq (char-after (- (point) 2)) ?:))) + (delete-region (point) (1- here))) + (goto-char (- (point-max) pos))) + ;; lets do some special stuff with the colon character + (setq syntax (c-guess-basic-syntax) + ;; some language elements can only be determined by + ;; checking the following line. Lets first look for ones + ;; that can be found when looking on the line with the + ;; colon + newlines + (and c-auto-newline + (or (c-lookup-lists '(case-label label access-label) + syntax c-hanging-colons-alist) + (c-lookup-lists '(member-init-intro inher-intro) + (prog2 + (insert "\n") + (c-guess-basic-syntax) + (delete-char -1)) + c-hanging-colons-alist)))) + ;; indent the current line + (c-indent-line syntax) + ;; does a newline go before the colon? Watch out for already + ;; non-hung colons. However, we don't unhang them because that + ;; would be a cleanup (and anti-social). + (if (and (memq 'before newlines) + (save-excursion + (skip-chars-backward ": \t") + (not (bolp)))) + (let ((pos (- (point-max) (point)))) + (forward-char -1) + (newline) + (c-indent-line) + (goto-char (- (point-max) pos)))) + ;; does a newline go after the colon? + (if (memq 'after (cdr-safe newlines)) + (progn + (newline) + (c-indent-line))) + ))) + +(defun c-electric-lt-gt (arg) + "Insert a less-than, or greater-than character. +When the auto-newline feature is turned on, as evidenced by the \"/a\" +or \"/ah\" string on the mode line, the line will be re-indented if +the character inserted is the second of a C++ style stream operator +and the buffer is in C++ mode. + +The line will also not be re-indented if a numeric argument is +supplied, or point is inside a literal." + (interactive "P") + (let ((indentp (and (not arg) + (eq (char-before) last-command-char) + (not (c-in-literal)))) + ;; shut this up + (c-echo-syntactic-information-p nil)) + (self-insert-command (prefix-numeric-value arg)) + (if indentp + (c-indent-line)))) + + + +;; better movement routines for ThisStyleOfVariablesCommonInCPlusPlus +;; originally contributed by Terry_Glanfield.Southern@rxuk.xerox.com +(defun c-forward-into-nomenclature (&optional arg) + "Move forward to end of a nomenclature section or word. +With arg, to it arg times." + (interactive "p") + (let ((case-fold-search nil)) + (if (> arg 0) + (re-search-forward "\\W*\\([A-Z]*[a-z0-9]*\\)" (point-max) t arg) + (while (and (< arg 0) + (re-search-backward + "\\(\\(\\W\\|[a-z0-9]\\)[A-Z]+\\|\\W\\w+\\)" + (point-min) 0)) + (forward-char 1) + (setq arg (1+ arg))))) + (c-keep-region-active)) + +(defun c-backward-into-nomenclature (&optional arg) + "Move backward to beginning of a nomenclature section or word. +With optional ARG, move that many times. If ARG is negative, move +forward." + (interactive "p") + (c-forward-into-nomenclature (- arg)) + (c-keep-region-active)) + +(defun c-scope-operator () + "Insert a double colon scope operator at point. +No indentation or other \"electric\" behavior is performed." + (interactive) + (insert "::")) + + +(defun c-beginning-of-statement (&optional count lim sentence-flag) + "Go to the beginning of the innermost C statement. +With prefix arg, go back N - 1 statements. If already at the +beginning of a statement then go to the beginning of the preceding +one. If within a string or comment, or next to a comment (only +whitespace between), move by sentences instead of statements. + +When called from a program, this function takes 3 optional args: the +repetition count, a buffer position limit which is the farthest back +to search, and a flag saying whether to do sentence motion when in a +comment." + (interactive (list (prefix-numeric-value current-prefix-arg) + nil t)) + (let ((here (point)) + (count (or count 1)) + (lim (or lim (c-point 'bod))) + state) + (save-excursion + (goto-char lim) + (setq state (parse-partial-sexp (point) here nil nil))) + (if (and sentence-flag + (or (nth 3 state) + (nth 4 state) +; (looking-at (concat "[ \t]*" comment-start-skip)) + (save-excursion + (skip-chars-backward " \t") + (goto-char (- (point) 2)) + (looking-at "\\*/")))) + (forward-sentence (- count)) + (while (> count 0) + (c-beginning-of-statement-1 lim) + (setq count (1- count))) + (while (< count 0) + (c-end-of-statement-1) + (setq count (1+ count)))) + ;; its possible we've been left up-buf of lim + (goto-char (max (point) lim)) + ) + (c-keep-region-active)) + +(defun c-end-of-statement (&optional count lim sentence-flag) + "Go to the end of the innermost C statement. + +With prefix arg, go forward N - 1 statements. Move forward to end of +the next statement if already at end. If within a string or comment, +move by sentences instead of statements. + +When called from a program, this function takes 3 optional args: the +repetition count, a buffer position limit which is the farthest back +to search, and a flag saying whether to do sentence motion when in a +comment." + (interactive (list (prefix-numeric-value current-prefix-arg) + nil t)) + (c-beginning-of-statement (- (or count 1)) lim sentence-flag) + (c-keep-region-active)) + + +;; set up electric character functions to work with pending-del, +;; (a.k.a. delsel) mode. All symbols get the t value except +;; c-electric-delete which gets 'supersede. +(mapcar + (function + (lambda (sym) + (put sym 'delete-selection t) ; for delsel (Emacs) + (put sym 'pending-delete t))) ; for pending-del (XEmacs) + '(c-electric-pound + c-electric-brace + c-electric-slash + c-electric-star + c-electric-semi&comma + c-electric-lt-gt + c-electric-colon)) +(put 'c-electric-delete 'delete-selection 'supersede) ; delsel +(put 'c-electric-delete 'pending-delete 'supersede) ; pending-del + + +;; This is used by indent-for-comment to decide how much to indent a +;; comment in C code based on its context. +(defun c-comment-indent () + (if (looking-at (concat "^\\(" c-comment-start-regexp "\\)")) + 0 ;Existing comment at bol stays there. + (let ((opoint (point)) + placeholder) + (save-excursion + (beginning-of-line) + (cond + ;; CASE 1: A comment following a solitary close-brace should + ;; have only one space. + ((looking-at (concat "[ \t]*}[ \t]*\\($\\|" + c-comment-start-regexp + "\\)")) + (search-forward "}") + (1+ (current-column))) + ;; CASE 2: 2 spaces after #endif + ((or (looking-at "^#[ \t]*endif[ \t]*") + (looking-at "^#[ \t]*else[ \t]*")) + 7) + ;; CASE 3: when comment-column is nil, calculate the offset + ;; according to c-offsets-alist. E.g. identical to hitting + ;; TAB. + ((and c-indent-comments-syntactically-p + (save-excursion + (skip-chars-forward " \t") + (or (looking-at comment-start) + (eolp)))) + (let ((syntax (c-guess-basic-syntax))) + ;; BOGOSITY ALERT: if we're looking at the eol, its + ;; because indent-for-comment hasn't put the comment-start + ;; in the buffer yet. this will screw up the syntactic + ;; analysis so we kludge in the necessary info. Another + ;; kludge is that if we're at the bol, then we really want + ;; to ignore any anchoring as specified by + ;; c-comment-only-line-offset since it doesn't apply here. + (if (save-excursion + (beginning-of-line) + (skip-chars-forward " \t") + (eolp)) + (c-add-syntax 'comment-intro)) + (let ((c-comment-only-line-offset + (if (consp c-comment-only-line-offset) + c-comment-only-line-offset + (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 + ((save-excursion + (beginning-of-line) + (and (not (bobp)) + (forward-line -1)) + (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))) + ;; CASE 5: If comment-column is 0, and nothing but space + ;; before the comment, align it at 0 rather than 1. + ((progn + (goto-char opoint) + (skip-chars-backward " \t") + (and (= comment-column 0) (bolp))) + 0) + ;; CASE 6: indent at comment column except leave at least one + ;; space. + (t (max (1+ (current-column)) + comment-column)) + ))))) + +;; used by outline-minor-mode +(defun c-outline-level () + (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." + (interactive "p") + (c-forward-conditional (- count) t) + (c-keep-region-active)) + +(defun c-backward-conditional (count &optional up-flag) + "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-keep-region-active)) + +(defun c-forward-conditional (count &optional up-flag) + "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." + (interactive "p") + (let* ((forward (> count 0)) + (increment (if forward -1 1)) + (search-function (if forward 're-search-forward 're-search-backward)) + (new)) + (save-excursion + (while (/= count 0) + (let ((depth (if up-flag 0 -1)) found) + (save-excursion + ;; Find the "next" significant line in the proper direction. + (while (and (not found) + ;; Rather than searching for a # sign that + ;; comes at the beginning of a line aside from + ;; whitespace, search first for a string + ;; starting with # sign. Then verify what + ;; precedes it. This is faster on account of + ;; the fastmap feature of the regexp matcher. + (funcall search-function + "#[ \t]*\\(if\\|elif\\|endif\\)" + 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"))) + ;; 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) + (setq found (point)))) + ;; else + (if forward (forward-line 1)) + ))) + (or found + (error "No containing preprocessor conditional")) + (goto-char (setq new found))) + (setq count (+ count increment)))) + (push-mark) + (goto-char new)) + (c-keep-region-active)) + + +;; commands to indent lines, regions, defuns, and expressions +(defun c-indent-command (&optional whole-exp) + "Indent current line as C code, and/or insert some whitespace. + +If `c-tab-always-indent' is t, always just indent the current line. +If nil, indent the current line only if point is at the left margin or +in the line's indentation; otherwise insert some whitespace[*]. If +other than nil or t, then some whitespace[*] is inserted only within +literals (comments and strings) and inside preprocessor directives, +but the line is always reindented. + +A numeric argument, regardless of its value, means indent rigidly all +the lines of the expression starting after point so that this line +becomes properly indented. The relative indentation among the lines +of the expression are preserved. + + [*] The amount and kind of whitespace inserted is controlled by the + variable `c-insert-tab-function', which is called to do the actual + insertion of whitespace. Normally the function in this variable + just inserts a tab character, or the equivalent number of spaces, + depending on the variable `indent-tabs-mode'." + + (interactive "P") + (let ((bod (c-point 'bod))) + (if whole-exp + ;; If arg, always indent this line as C + ;; and shift remaining lines of expression the same amount. + (let ((shift-amt (c-indent-line)) + beg end) + (save-excursion + (if (eq c-tab-always-indent t) + (beginning-of-line)) + (setq beg (point)) + (forward-sexp 1) + (setq end (point)) + (goto-char beg) + (forward-line 1) + (setq beg (point))) + (if (> end beg) + (indent-code-rigidly beg end (- shift-amt) "#"))) + ;; No arg supplied, use c-tab-always-indent to determine + ;; behavior + (cond + ;; CASE 1: indent when at column zero or in lines indentation, + ;; otherwise insert a tab + ((not c-tab-always-indent) + (if (save-excursion + (skip-chars-backward " \t") + (not (bolp))) + (funcall c-insert-tab-function) + (c-indent-line))) + ;; CASE 2: just indent the line + ((eq c-tab-always-indent t) + (c-indent-line)) + ;; CASE 3: if in a literal, insert a tab, but always indent the + ;; line + (t + (if (c-in-literal bod) + (funcall c-insert-tab-function)) + (c-indent-line) + ))))) + +(defun c-indent-exp (&optional shutup-p) + "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)) + end progress-p) + (unwind-protect + (let ((c-echo-syntactic-information-p nil) ;keep quiet for speed + (start (progn + ;; try to be smarter about finding the range of + ;; lines to indent. skip all following + ;; whitespace. failing that, try to find any + ;; opening brace on the current line + (skip-chars-forward " \t\n") + (if (memq (char-after) '(?\( ?\[ ?\{)) + (point) + (let ((state (parse-partial-sexp (point) + (c-point 'eol)))) + (and (nth 1 state) + (goto-char (nth 1 state)) + (memq (char-after) '(?\( ?\[ ?\{)) + (point))))))) + ;; find balanced expression end + (setq end (and (c-safe (progn (forward-sexp 1) t)) + (point-marker))) + ;; sanity check + (and (not start) + (not shutup-p) + (error "Cannot find start of balanced expression to indent.")) + (and (not end) + (not shutup-p) + (error "Cannot find end of balanced expression to indent.")) + (c-progress-init start end 'c-indent-exp) + (setq progress-p t) + (goto-char start) + (beginning-of-line) + (while (< (point) end) + (if (not (looking-at "[ \t]*$")) + (c-indent-line)) + (c-progress-update) + (forward-line 1))) + ;; make sure marker is deleted + (and end + (set-marker end nil)) + (and progress-p + (c-progress-fini 'c-indent-exp)) + (goto-char here)))) + +(defun c-indent-defun () + "Re-indents the current top-level function def, struct or class declaration." + (interactive) + (let ((here (point-marker)) + (c-echo-syntactic-information-p nil) + (brace (c-least-enclosing-brace (c-parse-state)))) + (if brace + (goto-char brace) + (beginning-of-defun)) + ;; if we're sitting at b-o-b, it might be because there was no + ;; least enclosing brace and we were sitting on the defun's open + ;; brace. + (if (and (bobp) (not (eq (char-after) ?\{))) + (goto-char here)) + ;; if defun-prompt-regexp is non-nil, b-o-d might not leave us at + ;; the open brace. I consider this an Emacs bug. + (and (boundp 'defun-prompt-regexp) + defun-prompt-regexp + (looking-at defun-prompt-regexp) + (goto-char (match-end 0))) + ;; catch all errors in c-indent-exp so we can 1. give more + ;; meaningful error message, and 2. restore point + (unwind-protect + (c-indent-exp) + (goto-char here) + (set-marker here nil)))) + +(defun c-indent-region (start end) + ;; Indent every line whose first char is between START and END inclusive. + (save-excursion + (goto-char start) + ;; Advance to first nonblank line. + (skip-chars-forward " \t\n") + (beginning-of-line) + (let (endmark) + (unwind-protect + (let ((c-tab-always-indent t) + ;; shut up any echo msgs on indiv lines + (c-echo-syntactic-information-p nil) + fence) + (c-progress-init start end 'c-indent-region) + (setq endmark (copy-marker end)) + (while (and (bolp) + (not (eobp)) + (< (point) endmark)) + ;; update progress + (c-progress-update) + ;; Indent one line as with TAB. + (let (nextline sexpend sexpbeg) + ;; skip blank lines + (skip-chars-forward " \t\n") + (beginning-of-line) + ;; indent the current line + (c-indent-line) + (setq fence (point)) + (if (save-excursion + (beginning-of-line) + (looking-at "[ \t]*#")) + (forward-line 1) + (save-excursion + ;; Find beginning of following line. + (setq nextline (c-point 'bonl)) + ;; Find first beginning-of-sexp for sexp extending past + ;; this line. + (beginning-of-line) + (while (< (point) nextline) + (condition-case nil + (progn + (forward-sexp 1) + (setq sexpend (point))) + (error (setq sexpend nil) + (goto-char nextline))) + (c-forward-syntactic-ws)) + (if sexpend + (progn + ;; make sure the sexp we found really starts on the + ;; current line and extends past it + (goto-char sexpend) + (setq sexpend (point-marker)) + (c-safe (backward-sexp 1)) + (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)))) + ;; If that sexp ends within the region, indent it all at + ;; once, fast. + (condition-case nil + (if (and sexpend + (> sexpend nextline) + (<= sexpend endmark)) + (progn + (goto-char sexpbeg) + (c-indent-exp 'shutup) + (c-progress-update) + (goto-char sexpend))) + (error + (goto-char sexpbeg) + (c-indent-line))) + ;; Move to following line and try again. + (and sexpend + (markerp sexpend) + (set-marker sexpend nil)) + (forward-line 1) + (setq fence (point)))))) + (set-marker endmark nil) + (c-progress-fini 'c-indent-region) + (c-echo-parsing-error) + )))) + +(defun c-mark-function () + "Put mark at end of a C, C++, or Objective-C defun, point at beginning." + (interactive) + (let ((here (point)) + ;; there should be a c-point position for 'eod + (eod (save-excursion (end-of-defun) (point))) + (state (c-parse-state)) + brace) + (while state + (setq brace (car state)) + (if (consp brace) + (goto-char (cdr brace)) + (goto-char brace)) + (setq state (cdr state))) + (if (eq (char-after) ?{) + (progn + (forward-line -1) + (while (not (or (bobp) + (looking-at "[ \t]*$"))) + (forward-line -1))) + (forward-line 1) + (skip-chars-forward " \t\n")) + (push-mark here) + (push-mark eod nil t))) + + +;; for progress reporting +(defvar c-progress-info nil) + +(defun c-progress-init (start end context) + ;; start the progress update messages. if this emacs doesn't have a + ;; built-in timer, just be dumb about it + (if (not (fboundp 'current-time)) + (message "indenting region... (this may take a while)") + ;; if progress has already been initialized, do nothing. otherwise + ;; initialize the counter with a vector of: + ;; [start end lastsec context] + (if c-progress-info + () + (setq c-progress-info (vector start + (save-excursion + (goto-char end) + (point-marker)) + (nth 1 (current-time)) + context)) + (message "indenting region...")))) + +(defun c-progress-update () + ;; update progress + (if (not (and c-progress-info c-progress-interval)) + nil + (let ((now (nth 1 (current-time))) + (start (aref c-progress-info 0)) + (end (aref c-progress-info 1)) + (lastsecs (aref c-progress-info 2))) + ;; should we update? currently, update happens every 2 seconds, + ;; what's the right value? + (if (< c-progress-interval (- now lastsecs)) + (progn + (message "indenting region... (%d%% complete)" + (/ (* 100 (- (point) start)) (- end start))) + (aset c-progress-info 2 now))) + ))) + +(defun c-progress-fini (context) + ;; finished + (if (or (eq context (aref c-progress-info 3)) + (eq context t)) + (progn + (set-marker (aref c-progress-info 1) nil) + (setq c-progress-info nil) + (message "indenting region...done")))) + + + +;;; This page handles insertion and removal of backslashes for C macros. + +(defun c-backslash-region (from to delete-flag) + "Insert, align, or delete end-of-line backslashes on the lines in the region. +With no argument, inserts backslashes and aligns existing backslashes. +With an argument, deletes the backslashes. + +This function does not modify blank lines at the start of the region. +If the region ends at the start of a line, it always deletes the +backslash (if any) at the end of the previous line. + +You can put the region around an entire macro definition and use this +command to conveniently insert and align the necessary backslashes." + (interactive "r\nP") + (save-excursion + (goto-char from) + (let ((column c-backslash-column) + (endmark (make-marker))) + (move-marker endmark to) + ;; Compute the smallest column number past the ends of all the lines. + (if (not delete-flag) + (while (< (point) to) + (end-of-line) + (if (eq (char-before) ?\\) + (progn (forward-char -1) + (skip-chars-backward " \t"))) + (setq column (max column (1+ (current-column)))) + (forward-line 1))) + ;; Adjust upward to a tab column, if that doesn't push past the margin. + (if (> (% column tab-width) 0) + (let ((adjusted (* (/ (+ column tab-width -1) tab-width) tab-width))) + (if (< adjusted (window-width)) + (setq column adjusted)))) + ;; Don't modify blank lines at start of region. + (goto-char from) + (while (and (< (point) endmark) (eolp)) + (forward-line 1)) + ;; Add or remove backslashes on all the lines. + (while (< (point) endmark) + (if (and (not delete-flag) + ;; Un-backslashify the last line + ;; if the region ends right at the start of the next line. + (save-excursion + (forward-line 1) + (< (point) endmark))) + (c-append-backslash column) + (c-delete-backslash)) + (forward-line 1)) + (move-marker endmark nil))) + (c-keep-region-active)) + +(defun c-append-backslash (column) + (end-of-line) + (if (eq (char-before) ?\\) + (progn (forward-char -1) + (delete-horizontal-space) + (indent-to column)) + (indent-to column) + (insert "\\"))) + +(defun c-delete-backslash () + (end-of-line) + (or (bolp) + (progn + (forward-char -1) + (if (looking-at "\\\\") + (delete-region (1+ (point)) + (progn (skip-chars-backward " \t") (point))))))) + + +(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. + +Optional prefix ARG means justify paragraph as well." + (interactive "P") + (let* (comment-start-place + (first-line + ;; Check for obvious entry to comment. + (save-excursion + (beginning-of-line) + (skip-chars-forward " \t\n") + (and (looking-at comment-start-skip) + (setq comment-start-place (point))))) + (re1 "\\|[ \t]*/\\*[ \t]*$\\|[ \t]*\\*/[ \t]*$\\|[ \t/*]*$")) + (if (and c-double-slash-is-comments-p + (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 fill-prefix) + (forward-line 1)) + (point))) + (fill-paragraph arg) + t))) + ;; else C style comments + (if (or first-line + ;; t if we enter a comment between start of function and + ;; this line. + (eq (c-in-literal) 'c) + ;; 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)))))) + ;; Inside a comment: fill one comment paragraph. + (let ((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 "[ \t]*/\\*.*\\*/") + (progn (re-search-forward comment-start-skip) + (make-string (current-column) ?\ )) + (if first-line (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 + (beginning-of-line) + (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 (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) + (forward-line 1) + (point))) + (fill-paragraph arg) + (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))) + (forward-line -1) + (fill-region-as-paragraph (point) (point-max)))))) + t))))) + + +(provide 'cc-cmds) +;;; cc-cmds.el ends here