wrs@k.cs.cmu.edu (Walter Smith) (05/01/87)
I got several requests for this, so I'm posting it. Thanks to Dave
Detlefs and Stewart Clamen.
- Walt
------------------------------cut here------------------------------
;; C++ code editing commands for Emacs
;; Dave Detlefs and Stewart Clamen 1987
;; Derived from c-mode.el, Copyright (C) 1985 Richard M. Stallman.
;; Copyright (C) 1987 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY. No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing. Refer to the GNU Emacs General Public
;; License for full details.
;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License. A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities. It should be in a
;; file named COPYING. Among other things, the copyright notice
;; and this notice must be preserved on all copies.
(defvar c++-mode-syntax-table nil
"Syntax table in use in C++-mode buffers.")
(defvar c++-mode-abbrev-table nil
"Abbrev table in use in C++-mode buffers.")
(defvar c++-mode-map (make-sparse-keymap)
"Keymap used in C++ mode.")
(define-key c++-mode-map "\C-j" 'reindent-then-newline-and-indent)
(define-key c++-mode-map "{" 'electric-c++-brace)
(define-key c++-mode-map "}" 'electric-c++-brace)
(define-key c++-mode-map ";" 'electric-c++-semi)
(define-key c++-mode-map ":" 'electric-c++-terminator)
(define-key c++-mode-map "\e\C-h" 'mark-c-function)
(define-key c++-mode-map "\e\C-q" 'indent-c++-exp)
(define-key c++-mode-map "\177" 'backward-delete-char-untabify)
(define-key c++-mode-map "\t" 'c++-indent-line)
;(define-funkey c++-mode-map 'r14 'fill-C-comment)
;; Indentation parameters are defined in C-mode, and reused here.
;(defconst c-indent-level 2
; "*Indentation of C statements with respect to containing block.")
;(defconst c-brace-imaginary-offset 0
; "*Imagined indentation of a C open brace that actually follows a statement.")
;(defconst c-brace-offset 0
; "*Extra indentation for braces, compared with other text in same context.")
;(defconst c-argdecl-indent 5
; "*Indentation level of declarations of C function arguments.")
;(defconst c-label-offset -2
; "*Offset of C label lines and case statements relative to usual indentation.")
;(defconst c-continued-statement-offset 2
; "*Extra indent for lines not starting new statements.")
;(defconst c-auto-newline nil
; "*Non-nil means automatically newline before and after braces,
;and after colons and semicolons, inserted in C code.")
(defun c++-mode ()
"Major mode for editing C++ code. Very much like editing C code.
Expression and list commands understand all C++ brackets.
Tab indents for C++ code.
Comments are delimited with /* ... */ {or with // ... <newline>}
Paragraphs are separated by blank lines only.
Delete converts tabs to spaces as it moves back.
\\{c++-mode-map}
Variables controlling indentation style:
c-auto-newline
Non-nil means automatically newline before and after braces,
and after colons and semicolons, inserted in C code.
c-indent-level
Indentation of C statements within surrounding block.
The surrounding block's indentation is the indentation
of the line on which the open-brace appears.
c-continued-statement-offset
Extra indentation given to a substatement, such as the
then-clause of an if or body of a while.
c-brace-offset
Extra indentation for line if it starts with an open brace.
c-brace-imaginary-offset
An open brace following other text is treated as if it were
this far to the right of the start of its line.
c-argdecl-indent
Indentation level of declarations of C function arguments.
c-label-offset
Extra indentation for line that is a label, or case or default.
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)
(use-local-map c++-mode-map)
(setq major-mode 'c++-mode)
(setq mode-name "C++")
(define-abbrev-table 'c++-mode-abbrev-table ())
(setq local-abbrev-table c++-mode-abbrev-table)
(if (not c++-mode-syntax-table)
(let ((i 0))
(setq c++-mode-syntax-table (make-syntax-table))
(set-syntax-table c++-mode-syntax-table)
(modify-syntax-entry ?\\ "\\")
(modify-syntax-entry ?/ ". 12")
(modify-syntax-entry ?\n ">")
(modify-syntax-entry ?+ ".")
(modify-syntax-entry ?- ".")
(modify-syntax-entry ?= ".")
(modify-syntax-entry ?% ".")
(modify-syntax-entry ?< ".")
(modify-syntax-entry ?> ".")
(modify-syntax-entry ?\' "\""))
(set-syntax-table c++-mode-syntax-table))
(make-local-variable 'paragraph-start)
(setq paragraph-start (concat "^$\\|" page-delimiter))
(make-local-variable 'paragraph-separate)
(setq paragraph-separate paragraph-start)
(make-local-variable 'indent-line-function)
(setq indent-line-function 'c++-indent-line)
(make-local-variable 'require-final-newline)
(setq require-final-newline t)
(make-local-variable 'comment-start)
(setq comment-start "// ")
(make-local-variable 'comment-end)
(setq comment-end "")
(make-local-variable 'comment-column)
(setq comment-column 32)
(make-local-variable 'comment-start-skip)
(setq comment-start-skip "/\\*+ *\\|// *")
(make-local-variable 'comment-indent-hook)
(setq comment-indent-hook 'c++-comment-indent)
(make-local-variable 'parse-sexp-ignore-comments)
(setq parse-sexp-ignore-comments nil)
(run-hooks 'c++-mode-hook))
;; 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 ()
(let ((cur-col (current-column)))
(save-excursion
(forward-line -1)
(end-of-line 1)
(if (re-search-backward comment-start-skip
(save-excursion (beginning-of-line 1) (point))
t)
(progn (match-beginning 0)
(if (= (current-column) 0) 0
(max (1+ cur-col) (current-column))))
; Else indent at comment column, except leave at least one space.
(forward-line 1)
(end-of-line 1)
(skip-chars-backward " \t")
(max (1+ cur-col) comment-column)))))
(defun electric-c++-brace (arg)
"Insert character and correct line's indentation."
(interactive "P")
(let (insertpos)
(if (and (not arg)
(eolp)
(or (save-excursion
(skip-chars-backward " \t")
(bolp))
(if c-auto-newline
(progn (c++-indent-line) (newline) t)
nil)))
(progn
(insert last-command-char)
(c++-indent-line nil)
(if c-auto-newline
(progn
(setq insertpos (1- (point)))
(newline)
(c++-indent-line nil)))
(save-excursion
(if insertpos (goto-char (1+ insertpos)))
(delete-char -1))))
(if insertpos
(save-excursion
(goto-char insertpos)
(self-insert-command (prefix-numeric-value arg)))
(self-insert-command (prefix-numeric-value arg)))))
(defun electric-c++-semi (arg)
"Insert character and correct line's indentation."
(interactive "P")
(if c-auto-newline
(electric-c++-terminator arg)
(self-insert-command (prefix-numeric-value arg))))
(defun electric-c++-terminator (arg)
"Insert character and correct line's indentation."
(interactive "P")
(let (insertpos (end (point)))
(if (and (not arg) (eolp)
(not (save-excursion
(beginning-of-line)
(skip-chars-forward " \t")
(or (= (following-char) ?#)
(let ((pps (parse-partial-sexp (point) end)))
(or (nth 3 pps) (nth 4 pps) (nth 5 pps)))))))
(progn
(insert last-command-char)
(c++-indent-line nil)
(and c-auto-newline
(not (c-inside-parens-p))
(progn
(setq insertpos (1- (point)))
(newline)
(c++-indent-line nil)))
(save-excursion
(if insertpos (goto-char (1+ insertpos)))
(delete-char -1))))
(if insertpos
(save-excursion
(goto-char insertpos)
(self-insert-command (prefix-numeric-value arg)))
(self-insert-command (prefix-numeric-value arg)))))
(defun c-inside-parens-p ()
(condition-case ()
(save-excursion
(save-restriction
(narrow-to-region (point)
(progn (beginning-of-defun) (point)))
(goto-char (point-max))
(= (char-after (or (scan-lists (point) -1 1) (point-min))) ?\()))
(error nil)))
(defun c++-indent-line (&optional whole-exp)
"Indent current line as C++ code.
Argument means shift any additional lines of grouping
rigidly with thls line."
(interactive "P")
(let ((indent (calculate-c++-indent nil))
beg end shift-amt
(case-fold-search nil)
(pos (- (point-max) (point))))
(beginning-of-line)
(setq beg (point))
(cond ((eq indent nil)
(setq indent (current-indentation)))
((eq indent t)
(setq indent (calculate-c-indent-within-comment)))
((looking-at "[ \t]*#")
(setq indent 0))
(t
(skip-chars-forward " \t")
(if (listp indent) (setq indent (car indent)))
(cond ((looking-at "\\(public\\|private\\|protected\\):")
(setq indent (- indent c-indent-level)))
((and (looking-at "[A-Za-z]")
(save-excursion
(forward-sexp 1)
(looking-at ":[^:]")))
(setq indent (max 1 (+ indent c-label-offset))))
((looking-at "case\\b")
(setq indent (- indent c-indent-level)))
((looking-at "else\\b")
(setq indent (save-excursion
(c-backward-to-start-of-if)
(current-indentation))))
((looking-at "friend\[ \t]class[ \t]")
(setq indent (- indent c-indent-level)))
((= (following-char) ?})
(setq indent (- indent c-indent-level)))
((= (following-char) ?{)
(setq indent (+ indent c-brace-offset))))))
(skip-chars-forward " \t")
(setq shift-amt (- indent (current-column)))
(if (zerop shift-amt)
(if (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos)))
(delete-region beg (point))
(indent-to indent)
;; If initial point was within line's indentation,
;; position after the indentation. Else stay at same point in text.
(if (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos)))
;; If desired, shift remaining lines of expression the same amount.
(and whole-exp
(save-excursion
(goto-char beg)
(forward-sexp 1)
(setq end (point))
(goto-char beg)
(forward-line 1)
(setq beg (point))
(> end beg))
(indent-code-rigidly beg end shift-amt "#")))))
(defun calculate-c++-indent (&optional parse-start)
"Return appropriate indentation for current line as C++ code.
In usual case returns an integer: the column to indent to.
Returns nil if line starts inside a string, t if in a comment."
(save-excursion
(beginning-of-line)
(let ((indent-point (point))
(case-fold-search nil)
state
containing-sexp)
(if parse-start
(goto-char parse-start)
(beginning-of-defun))
(while (< (point) indent-point)
(setq parse-start (point))
(setq state (parse-partial-sexp (point) indent-point 0))
(setq containing-sexp (car (cdr state))))
(cond ((or (nth 3 state) (nth 4 state))
;; return nil or t if should not change this line
(nth 4 state))
((null containing-sexp)
;; Line is at top level. May be data or function definition,
;; or may be function argument declaration.
;; Indent like the previous top level line
;; unless that ends in a closeparen without semicolon,
;; in which case this line is the first argument decl.
(goto-char indent-point)
(skip-chars-forward " \t")
(if (= (following-char) ?{)
0 ; Unless it starts a function body
(c++-backward-to-noncomment (or parse-start (point-min)))
(if (= (preceding-char) ?\))
c-argdecl-indent
(current-indentation))))
((/= (char-after containing-sexp) ?{)
;; line is expression, not statement:
;; indent to just after the surrounding open.
(goto-char (1+ containing-sexp))
(current-column))
(t
;; Statement. Find previous non-comment character.
;;; (message "statement..")
(goto-char indent-point)
(c++-backward-to-noncomment containing-sexp)
(if (not (memq (preceding-char) '(nil ?\, ?\; ?} ?: ?\{)))
;; This line is continuation of preceding line's statement;
;; indent c-continued-statement-offset more than the
;; previous line of the statement.
(progn
(c-backward-to-start-of-continued-exp containing-sexp)
(+ c-continued-statement-offset (current-column)))
;; This line starts a new statement.
;; Position following last unclosed open.
(goto-char containing-sexp)
;; Is line first statement after an open-brace?
(or
;; 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 ":[^:]"))))
;;; (message "P=%d IP=%d CC=%d" (point)
;;; indent-point (current-column))
;; The first following code counts
;; if it is before the line we want to indent.
(and (< (point) indent-point)
(current-column)))
;; If no previous statement,
;; indent it relative to line brace is on.
;; For open brace in column zero, don't let statement
;; start there too. If c-indent-offset is zero,
;; use c-brace-offset + c-continued-statement-offset instead.
;; For open-braces not the first thing in a line,
;; add in c-brace-imaginary-offset.
;;; (and (message "1st statement...") nil)
(+ (if (and (bolp) (zerop c-indent-level))
(+ c-brace-offset c-continued-statement-offset)
c-indent-level)
(if (save-excursion (skip-chars-backward " \t")
(bolp))
0 c-brace-imaginary-offset)
(current-indentation)))))))))
(defun calculate-c-indent-within-comment ()
"Return the indentation amount for line, assuming that
the current line is to be regarded as part of a block comment."
(let (end star-start)
(save-excursion
(beginning-of-line)
(skip-chars-forward " \t")
(setq star-start (= (following-char) ?\*))
(skip-chars-backward " \t\n")
(setq end (point))
(beginning-of-line)
(skip-chars-forward " \t")
(and (re-search-forward "/\\*[ \t]*" end t)
star-start
(goto-char (1+ (match-beginning 0))))
(current-column))))
(defun c++-backward-to-noncomment (lim)
(let (opoint stop)
(while (not stop)
(skip-chars-backward " \t\n\r\f" lim)
(setq opoint (point))
(if (and (>= (point) (+ 2 lim))
(save-excursion
(forward-char -2)
(looking-at "\\*/")))
(search-backward "/*" lim 'move)
(or (search-backward "//" (max (point-bol) lim) 'move)
(progn
(beginning-of-line)
(skip-chars-forward " \t")
(if (looking-at "#\\|//")
(setq stop (<= (point) lim))
(setq stop t)
(goto-char opoint))))))))
(defun c-backward-to-start-of-continued-exp (lim)
(if (= (preceding-char) ?\))
(forward-sexp -1))
(beginning-of-line)
(if (<= (point) lim)
(goto-char (1+ lim)))
(skip-chars-forward " \t"))
(defun c-backward-to-start-of-if (&optional limit)
"Move to the start of the last ``unbalanced'' if."
(or limit (setq limit (save-excursion (beginning-of-defun) (point))))
(let ((if-level 1)
(case-fold-search nil))
(while (not (zerop if-level))
(backward-sexp 1)
(cond ((looking-at "else\\b")
(setq if-level (1+ if-level)))
((looking-at "if\\b")
(setq if-level (1- if-level)))
((< (point) limit)
(setq if-level 0)
(goto-char limit))))))
(defun indent-c++-exp ()
"Indent each line of the C++ grouping following point."
(interactive)
(let ((indent-stack (list nil))
(contain-stack (list (point)))
(case-fold-search nil)
restart outer-loop-done inner-loop-done state ostate
this-indent last-sexp
at-else
(opoint (point))
(next-depth 0))
(save-excursion
(forward-sexp 1))
(save-excursion
(setq outer-loop-done nil)
(while (and (not (eobp)) (not outer-loop-done))
(setq last-depth next-depth)
;; Compute how depth changes over this line
;; plus enough other lines to get to one that
;; does not end inside a comment or string.
;; Meanwhile, do appropriate indentation on comment lines.
(setq innerloop-done nil)
(while (and (not innerloop-done)
(not (and (eobp) (setq outer-loop-done t))))
(setq ostate state)
(setq state (parse-partial-sexp (point) (progn (end-of-line) (point))
nil nil state))
(setq next-depth (car state))
(if (and (car (cdr (cdr state)))
(>= (car (cdr (cdr state))) 0))
(setq last-sexp (car (cdr (cdr state)))))
(if (or (nth 4 ostate))
(c++-indent-line))
(if (or (nth 3 state))
(forward-line 1)
(setq innerloop-done t)))
(if (<= next-depth 0)
(setq outer-loop-done t))
(if outer-loop-done
nil
(if (/= last-depth next-depth)
(setq last-sexp nil))
(while (> last-depth next-depth)
(setq indent-stack (cdr indent-stack)
contain-stack (cdr contain-stack)
last-depth (1- last-depth)))
(while (< last-depth next-depth)
(setq indent-stack (cons nil indent-stack)
contain-stack (cons nil contain-stack)
last-depth (1+ last-depth)))
(if (null (car contain-stack))
(setcar contain-stack (or (car (cdr state))
(save-excursion (forward-sexp -1)
(point)))))
(forward-line 1)
(skip-chars-forward " \t")
(if (eolp)
nil
(if (and (car indent-stack)
(>= (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"))
(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))))
;; 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))))))
;; 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))))
;; Adjust line indentation according to its contents
(if (looking-at "\\(public\\|private\\|protected\\):")
(setq this-indent (- this-indent c-indent-level)))
(if (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 "case[ \t]")
(setq this-indent (- this-indent c-indent-level)))
(if (looking-at "friend[ \t]class[ \t]")
(setq this-indent (- this-indent c-indent-level)))
(if (= (following-char) ?})
(setq this-indent (- this-indent c-indent-level)))
(if (= (following-char) ?{)
(setq this-indent (+ this-indent c-brace-offset)))
;; Put chosen indentation into effect.
(or (= (current-column) this-indent)
(= (following-char) ?\#)
(progn
(delete-region (point) (progn (beginning-of-line) (point)))
(indent-to this-indent)))
;; Indent any comment following the text.
(or (looking-at comment-start-skip)
(if (re-search-forward comment-start-skip (save-excursion (end-of-line) (point)) t)
(progn (indent-for-comment) (beginning-of-line))))))))))
(defun fill-C-comment ()
(interactive)
(save-excursion
(let ((save fill-prefix))
(beginning-of-line 1)
(save-excursion
(re-search-forward comment-start-skip
(save-excursion (end-of-line) (point))
t)
(goto-char (match-end 0))
(set-fill-prefix))
(while (looking-at fill-prefix)
(previous-line 1))
(next-line 1)
(insert-string "\n")
(fill-paragraph nil)
(delete-char -1)
(setq fill-prefix save))))
(defun point-bol ()
"Returns the value of the point at the beginning of the current
line."
(save-excursion
(beginning-of-line)
(point)))
(defun previous-line-starts-with (string)
"Returns t if previous nonblank line begins with STRING"
(save-excursion
(beginning-of-line 0)
(skip-chars-forward " \t")
(if (eolp)
(previous-line-starts-with string)
(looking-at string))))
;;; This section defines some functions that are useful in defining
;;; parameterized functions and classes using macros.
(defun c++-macroize-region (start end)
(interactive "r")
(save-excursion
(let ((last-line (progn (goto-char end)
(count-lines (point-min) (point)))))
(goto-char start)
(let ((i (count-lines (point-min) (point))))
(while (<= i last-line)
(end-of-line 1)
(let ((target (max (+ (current-column) 1) 78)))
(while (< (current-column) target) (insert "\t"))
(delete-char -1)
(while (< (current-column) target) (insert " "))
(insert "\\")
(next-line 1)
(setq i (1+ i))))))))
(define-key c++-mode-map "\C-c\\" 'c++-macroize-region)
------------------------------cut here------------------------------
--
Walter Smith, Math/CS undergraduate, Carnegie-Mellon University
CS graduate student starting August!
uucp: ...!seismo!cmu-cs-k!wrs ARPA: wrs@k.cs.cmu.edu
usps: 5139 Forbes Ave.; Pittsburgh, PA 15213