(indent-c++-exp): Use calculate-c-indent-after-brace.
[bpt/emacs.git] / lisp / progmodes / cplus-md.el
index 3c08cf3..555f5a4 100644 (file)
@@ -1,5 +1,9 @@
 ;;; cplus-md.el --- C++ code editing commands for Emacs
-;;; Copyright (C) 1985, 1992 Free Software Foundation, Inc.
+
+;; Copyright (C) 1985, 1992, 1994, 1995 Free Software Foundation, Inc.
+
+;; Maintainer: Dave Detlefs <dld@cs.cmu.edu>
+;; Keywords: c
 
 ;; This file is part of GNU Emacs.
 
 ;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.
-;; Maintainer: Dave Detlefs <dld@cs.cmu.edu>
-;; Keywords: c
+;; 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.
 
 ;;; Commentary:
 
@@ -47,7 +50,7 @@
 ;;   Fixed handling of "default:", where ":" was the last character in the
 ;;   buffer.  Fixed indentation of comments starting in column 0, and when
 ;;   previous line contained more than one comment start string.  Fixed
-;;   handling of "friend class".
+;;   handling of "friend".
 ;;
 ;; Aug 7, 1989; John Hagerman (hagerman@ece.cmu.edu):
 ;;   Changed calculate-c++-indent to handle member initializations
 
 (if c++-mode-syntax-table
     ()
-  (setq c++-mode-syntax-table (copy-syntax-table c-mode-syntax-table))
+  (setq c++-mode-syntax-table (make-syntax-table))
+  (modify-syntax-entry ?\\ "\\" c++-mode-syntax-table)
+  (modify-syntax-entry ?/ ". 14" c++-mode-syntax-table)
+  (modify-syntax-entry ?* ". 23" c++-mode-syntax-table)
+  (modify-syntax-entry ?+ "." c++-mode-syntax-table)
+  (modify-syntax-entry ?- "." c++-mode-syntax-table)
+  (modify-syntax-entry ?= "." c++-mode-syntax-table)
+  (modify-syntax-entry ?% "." c++-mode-syntax-table)
+  (modify-syntax-entry ?< "." c++-mode-syntax-table)
+  (modify-syntax-entry ?> "." c++-mode-syntax-table)
+  (modify-syntax-entry ?& "." c++-mode-syntax-table)
+  (modify-syntax-entry ?| "." c++-mode-syntax-table)
+  (modify-syntax-entry ?\' "\"" c++-mode-syntax-table)
   (modify-syntax-entry ?* ". 23b" c++-mode-syntax-table)
   (modify-syntax-entry ?/ ". 124" c++-mode-syntax-table)
-  (modify-syntax-entry ?\n ">" c++-mode-syntax-table))
+  (modify-syntax-entry ?\n ">" c++-mode-syntax-table)
+  (modify-syntax-entry ?\^m ">" c++-mode-syntax-table))
 
 (defvar c++-continued-member-init-offset nil
   "*Extra indent for continuation lines of member inits;
@@ -134,15 +150,109 @@ with the colon on the first line.")
 (defvar c++-member-init-indent 0
   "*Indentation level of member initializations in function declarations.")
 (defvar c++-friend-offset -4
-  "*Offset of C++ friend class declarations relative to member declarations.")
+  "*Offset of C++ friend declarations relative to member declarations.")
 (defvar c++-electric-colon t
   "*If t, colon is an electric terminator.")
 (defvar c++-empty-arglist-indent nil
   "*Indicates how far to indent an line following an empty argument
 list.  Nil indicates to just after the paren.")
 
+(defvar c++-imenu-generic-expression
+  (` 
+   ((nil
+     (, 
+      (concat
+       "^"                               ; beginning of line is required
+       "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
+       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"     ; type specs; there can be no
+       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"     ; more than 3 tokens, right?
+       
+       "\\("                             ; last type spec including */&
+       "[a-zA-Z0-9_:]+"
+       "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)"        ; either pointer/ref sign or whitespace
+       "\\)?"                            ; if there is a last type spec
+       "\\("                         ; name; take that into the imenu entry
+       "[a-zA-Z0-9_:~]+"                     ; member function, ctor or dtor...
+                                       ; (may not contain * because then 
+                                       ; "a::operator char*" would become "char*"!)
+       "\\|"
+       "\\([a-zA-Z0-9_:~]*::\\)?operator"
+       "[^a-zA-Z1-9_][^(]*"          ; ...or operator
+       " \\)"
+       "[ \t]*([^)]*)[ \t\n]*[^              ;]" ; require something other than a ; after
+                                       ; the (...) to avoid prototypes.  Can't
+                                       ; catch cases with () inside the parentheses
+                                       ; surrounding the parameters
+                                       ; (like "int foo(int a=bar()) {...}"
+       
+       )) 6)    
+    ("Class" 
+     (, (concat 
+        "^"                               ; beginning of line is required
+        "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
+        "class[ \t]+"
+        "\\([a-zA-Z0-9_]+\\)"                ; this is the string we want to get
+        "[ \t]*[:{]"
+        )) 2)
+;; Example of generic expression for finding prototypes, structs, unions, enums.
+;; Uncomment if you want to find these too.  It will be a bit slower gathering
+;; the indexes.
+;    ("Prototypes"
+;     (, 
+;      (concat
+;       "^"                              ; beginning of line is required
+;       "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
+;       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"    ; type specs; there can be no
+;       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"    ; more than 3 tokens, right?
+       
+;       "\\("                            ; last type spec including */&
+;       "[a-zA-Z0-9_:]+"
+;       "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)"       ; either pointer/ref sign or whitespace
+;       "\\)?"                           ; if there is a last type spec
+;       "\\("                        ; name; take that into the imenu entry
+;       "[a-zA-Z0-9_:~]+"                    ; member function, ctor or dtor...
+;                                      ; (may not contain * because then 
+;                                      ; "a::operator char*" would become "char*"!)
+;       "\\|"
+;       "\\([a-zA-Z0-9_:~]*::\\)?operator"
+;       "[^a-zA-Z1-9_][^(]*"         ; ...or operator
+;       " \\)"
+;       "[ \t]*([^)]*)[ \t\n]*;"       ; require ';' after
+;                                      ; the (...) Can't
+;                                      ; catch cases with () inside the parentheses
+;                                      ; surrounding the parameters
+;                                      ; (like "int foo(int a=bar());"       
+;       )) 6)    
+;    ("Struct"
+;     (, (concat
+;       "^"                            ; beginning of line is required
+;       "\\(static[ \t]+\\)?"          ; there may be static or const.
+;       "\\(const[ \t]+\\)?"
+;       "struct[ \t]+"
+;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
+;       "[ \t]*[{]"
+;       )) 3)
+;    ("Enum"
+;     (, (concat
+;       "^"                            ; beginning of line is required
+;       "\\(static[ \t]+\\)?"          ; there may be static or const.
+;       "\\(const[ \t]+\\)?"
+;       "enum[ \t]+"
+;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
+;       "[ \t]*[{]"
+;       )) 3)
+;    ("Union"
+;     (, (concat
+;       "^"                            ; beginning of line is required
+;       "\\(static[ \t]+\\)?"          ; there may be static or const.
+;       "\\(const[ \t]+\\)?"
+;       "union[ \t]+"
+;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
+;       "[ \t]*[{]"
+;       )) 3)
+    ))
+  "Imenu generic expression for C++ mode.  See `imenu-generic-expression'.")
 
-;;;###autoload
 (defun c++-mode ()
   "Major mode for editing C++ code.  Very much like editing C code.
 Expression and list commands understand all C++ brackets.
@@ -180,14 +290,14 @@ Variables controlling indentation style:
     Extra indentation for line that is a label, or case or ``default:'', or
     ``public:'' or ``private:'', or ``protected:''.
  c++-electric-colon
-    If non-nil at invocation of c++-mode (t is the default) colon electricly
+    If non-nil at invocation of c++-mode (t is the default) colon electrically
     indents.
  c++-empty-arglist-indent
     If non-nil, a function declaration or invocation which ends a line with a
     left paren is indented this many extra spaces, instead of flush with the
     left paren.
  c++-friend-offset
-    Offset of C++ friend class declarations relative to member declarations.
+    Offset of C++ friend declarations relative to member declarations.
  c++-member-init-indent
     Indentation level of member initializations in function declarations,
     if they are on a separate line beginning with a colon.
@@ -210,6 +320,8 @@ Turning on C++ mode calls the value of the variable `c++-mode-hook' with
 no args if that value is non-nil."
   (interactive)
   (kill-all-local-variables)
+  ;; This code depends on the old C mode.
+  (require 'c-mode)
   (use-local-map c++-mode-map)
   (set-syntax-table c++-mode-syntax-table)
   (setq major-mode 'c++-mode
@@ -221,11 +333,13 @@ no args if that value is non-nil."
   (set (make-local-variable 'comment-end) "")
   (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|// *")
   (set (make-local-variable 'comment-indent-function) 'c++-comment-indent)
-  (set (make-local-variable 'paragraph-start) (concat "^$\\|" page-delimiter))
+  (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter))
   (set (make-local-variable 'paragraph-separate) paragraph-start)
   (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
   (set (make-local-variable 'require-final-newline) t)
-  (set (make-local-variable 'parse-sexp-ignore-comments) nil)
+  (set (make-local-variable 'parse-sexp-ignore-comments) t)
+  (make-local-variable 'imenu-generic-expression)
+  (setq imenu-generic-expression c++-imenu-generic-expression)
   (run-hooks 'c++-mode-hook)
   (if c++-electric-colon
       (define-key c++-mode-map ":" 'electric-c++-terminator)))
@@ -299,15 +413,15 @@ no args if that value is non-nil."
                        ;; So quickly rule out most other uses of colon
                        ;; and do no indentation for them.
                        (and (eq last-command-char ?:)
-                            (not (looking-at "case[ \t]"))
-                            (save-excursion
-                              (forward-word 1)
-                              (skip-chars-forward " \t")
-                              (< (point) end))
-                            ;; Do re-indent double colons
-                            (save-excursion
-                              (end-of-line 1)
-                              (looking-at ":")))
+                            (or (not (or (looking-at "case[ \t]")
+                                         (save-excursion
+                                           (forward-word 1)
+                                           (skip-chars-forward " \t")
+                                           (>= (point) end))))
+                                ;; Do re-indent double colons
+                                (save-excursion
+                                  (end-of-line 1)
+                                  (looking-at ":"))))
                        (progn
                          (beginning-of-defun)
                          (let ((pps (parse-partial-sexp (point) end)))
@@ -399,7 +513,7 @@ Return the amount the indentation changed by."
                  (setq indent (save-excursion
                                 (c-backward-to-start-of-if)
                                 (current-indentation))))
-                ((looking-at "friend\[ \t]class[ \t]")
+                ((looking-at "friend\[ \t]")
                  (setq indent (+ indent c++-friend-offset)))
                 ((= (following-char) ?})
                  (setq indent (- indent c-indent-level)))
@@ -464,6 +578,8 @@ Returns nil if line starts inside a string, t if in a comment."
                     (backward-char 1))
                 (if (= (preceding-char) ?})
                     0
+                  (if (= (preceding-char) ?\))
+                      (forward-list -1))
                   (beginning-of-line)  ; continued arg decls or member inits
                   (skip-chars-forward " \t")
                   (if (= (following-char) ?:)
@@ -509,7 +625,14 @@ Returns nil if line starts inside a string, t if in a comment."
             ;; Statement.  Find previous non-comment character.
             (goto-char indent-point)
             (c++-backward-to-noncomment containing-sexp)
-            (if (not (memq (preceding-char) '(nil ?\, ?\; ?} ?: ?\{)))
+            (if (and (not (memq (preceding-char) '(0 ?\, ?\; ?\} ?\{)))
+                     ;; But don't treat a line with a close-brace
+                     ;; as a continuation.  It is probably the
+                     ;; end of an enum type declaration.
+                     (save-excursion
+                       (goto-char indent-point)
+                       (skip-chars-forward " \t")
+                       (not (= (following-char) ?}))))
                 ;; This line is continuation of preceding line's statement;
                 ;; indent  c-continued-statement-offset  more than the
                 ;; previous line of the statement.
@@ -528,26 +651,37 @@ Returns nil if line starts inside a string, t if in a comment."
                 ;; If no, find that first statement and indent like it.
                 (save-excursion
                   (forward-char 1)
-                  (while (progn (skip-chars-forward " \t\n")
-                                (looking-at
-                                 (concat
-                                  "#\\|/\\*\\|//"
-                                  "\\|case[ \t]"
-                                  "\\|[a-zA-Z0-9_$]*:[^:]"
-                                  "\\|friend[ \t]class[ \t]")))
-                    ;; Skip over comments and labels following openbrace.
-                    (cond ((= (following-char) ?\#)
-                           (forward-line 1))
-                          ((looking-at "/\\*")
-                           (search-forward "*/" nil 'move))
-                          ((looking-at "//\\|friend[ \t]class[ \t]")
-                           (forward-line 1))
-                          (t
-                           (re-search-forward ":[^:]" nil 'move))))
-                     ;; The first following code counts
-                     ;; if it is before the line we want to indent.
-                     (and (< (point) indent-point)
-                          (current-column)))
+                  (let ((colon-line-end 0))
+                    (while (progn (skip-chars-forward " \t\n")
+                                  (looking-at
+                                   (concat
+                                    "#\\|/\\*\\|//"
+                                    "\\|case[ \t]"
+                                    "\\|[a-zA-Z0-9_$]*:[^:]"
+                                    "\\|friend[ \t]")))
+                      ;; Skip over comments and labels following openbrace.
+                      (cond ((= (following-char) ?\#)
+                             (forward-line 1))
+                            ((looking-at "/\\*")
+                             (search-forward "*/" nil 'move))
+                            ((looking-at "//\\|friend[ \t]")
+                             (forward-line 1))
+                            (t
+                             (save-excursion (end-of-line)
+                                             (setq colon-line-end (point)))
+                             (search-forward ":"))))
+                    ;; The first following code counts
+                    ;; if it is before the line we want to indent.
+                    (and (< (point) indent-point)
+                         (- 
+                          (if (> colon-line-end (point))
+                              (- (current-indentation) c-label-offset)
+                            (current-column))
+                          ;; If prev stmt starts with open-brace, that
+                          ;; open brace was offset by c-brace-offset.
+                          ;; Compensate to get the column where
+                          ;; an ordinary statement would start.
+                          (if (= (following-char) ?\{) c-brace-offset 0)))))
                 ;; If no previous statement,
                 ;; indent it relative to line brace is on.
                 ;; For open brace in column zero, don't let statement
@@ -585,12 +719,16 @@ Returns nil if line starts inside a string, t if in a comment."
            ((and
              (search-backward "//" (max (c++-point-bol) lim) 'move)
              (not (c++-within-string-p (point) opoint))))
-         (t (beginning-of-line)
-            (skip-chars-forward " \t")
-            (if (looking-at "#")
-                (setq stop (<= (point) lim))
-              (setq stop t)
-              (goto-char opoint)))))))
+           ;; No comment to be found.
+           ;; If there's a # command on this line,
+           ;; move back to it.
+           (t (beginning-of-line)
+              (skip-chars-forward " \t")
+              ;; But don't get fooled if we are already before the #.
+              (if (and (looking-at "#") (< (point) opoint))
+                  (setq stop (<= (point) lim))
+                (setq stop t)
+                (goto-char opoint)))))))
 
 (defun indent-c++-exp ()
   "Indent each line of the C++ grouping following point."
@@ -658,49 +796,55 @@ Returns nil if line starts inside a string, t if in a comment."
                     (>= (car indent-stack) 0))
                ;; Line is on an existing nesting level.
                ;; Lines inside parens are handled specially.
-               (if (/= (char-after (car contain-stack)) ?\{)
-                   (setq this-indent (car indent-stack))
-                 ;; Line is at statement level.
-                 ;; Is it a new statement?  Is it an else?
-                 ;; Find last non-comment character before this line
-                 (save-excursion
-                   (setq at-else (looking-at "else\\W"))
-                   (setq at-brace (= (following-char) ?\{))
-                   (c++-backward-to-noncomment opoint)
-                   (if (not (memq (preceding-char) '(nil ?\, ?\; ?\} ?: ?\{)))
-                       ;; Preceding line did not end in comma or semi;
-                       ;; indent this line  c-continued-statement-offset
-                       ;; more than previous.
-                       (progn
-                         (c-backward-to-start-of-continued-exp
-                          (car contain-stack))
-                         (setq this-indent
-                               (+ c-continued-statement-offset
-                                  (current-column)
-                                  (if at-brace c-continued-brace-offset 0))))
-                     ;; Preceding line ended in comma or semi;
-                     ;; use the standard indent for this level.
-                     (if at-else
-                         (progn (c-backward-to-start-of-if opoint)
-                                (setq this-indent (current-indentation)))
-                       (setq this-indent (car indent-stack))))))
+               nil
              ;; Just started a new nesting level.
              ;; Compute the standard indent for this level.
-             (let ((val (calculate-c++-indent
-                         (if (car indent-stack)
-                             (- (car indent-stack))))))
-               (setcar indent-stack
-                       (setq this-indent val))))
+             (let (val)
+               (if (= (char-after (car contain-stack)) ?{)
+                   (save-excursion
+                     (goto-char (car contain-stack))
+                     (setq val (calculate-c-indent-after-brace)))
+                 (setq val (calculate-c++-indent
+                            (if (car indent-stack)
+                                (- (car indent-stack))))))
+               (setcar indent-stack val)))
+           ;; Adjust line indentation according to its predecessor.
+           (if (/= (char-after (car contain-stack)) ?\{)
+               (setq this-indent (car indent-stack))
+             ;; Line is at statement level.
+             ;; Is it a new statement?  Is it an else?
+             ;; Find last non-comment character before this line
+             (save-excursion
+               (setq at-else (looking-at "else\\W"))
+               (setq at-brace (= (following-char) ?\{))
+               (c++-backward-to-noncomment opoint)
+               (if (not (memq (preceding-char) '(nil ?\, ?\; ?\} ?: ?\{)))
+                   ;; Preceding line did not end in comma or semi;
+                   ;; indent this line  c-continued-statement-offset
+                   ;; more than previous.
+                   (progn
+                     (c-backward-to-start-of-continued-exp
+                      (car contain-stack))
+                     (setq this-indent
+                           (+ c-continued-statement-offset
+                              (current-column)
+                              (if at-brace c-continued-brace-offset 0))))
+                 ;; Preceding line ended in comma or semi;
+                 ;; use the standard indent for this level.
+                 (if at-else
+                     (progn (c-backward-to-start-of-if opoint)
+                            (setq this-indent (current-indentation)))
+                   (setq this-indent (car indent-stack))))))
            ;; Adjust line indentation according to its contents
            (if (looking-at "\\(public\\|private\\|protected\\):")
-               (setq this-indent (- this-indent c-indent-level)))
-           (if (or (looking-at "case[ \t]")
-                   (and (looking-at "[A-Za-z]")
-                        (save-excursion
-                          (forward-sexp 1)
-                          (looking-at ":[^:]"))))
-               (setq this-indent (max 1 (+ this-indent c-label-offset))))
-           (if (looking-at "friend[ \t]class[ \t]")
+               (setq this-indent (- this-indent c-indent-level))
+             (if (or (looking-at "case[ \t]")
+                     (and (looking-at "[A-Za-z]")
+                          (save-excursion
+                            (forward-sexp 1)
+                            (looking-at ":[^:]"))))
+                 (setq this-indent (max 1 (+ this-indent c-label-offset)))))
+           (if (looking-at "friend[ \t]")
                (setq this-indent (+ this-indent c++-friend-offset)))
            (if (= (following-char) ?\})
                (setq this-indent (- this-indent c-indent-level)))
@@ -797,7 +941,7 @@ The fill lines remain a comment."
 ;; 
 ;;        ; overloadable operators
 ;;        (op-sym1
-;;      "[---+*/%^&|~!=<>]\\|[---+*/%^&|<>=!]=\\|<<=?\\|>>=?")
+;;      "[-+*/%^&|~!=<>]\\|[-+*/%^&|<>=!]=\\|<<=?\\|>>=?")
 ;;        (op-sym2
 ;;      "&&\\|||\\|\\+\\+\\|--\\|()\\|\\[\\]")  
 ;;        (op-sym (concat "\\(" op-sym1 "\\|" op-sym2 "\\)"))