[comp.emacs] Good c-mode for GNU?

kovar@husc4.HARVARD.EDU (11/14/88)

  I used to use UniPress emacs and was quite happy with the electric-c
modes available for it. They'd provide brace completions, for statement
outlines, and the like. The GNU Emacs c-mode is woefully inadaquate
in my eyes. Has anyone written a more intelligent version?

-David Kovar

mark@comp.vuw.ac.nz (Mark Davies) (11/23/88)

In article <591@husc6.harvard.edu> kovar@husc4.harvard.edu (David Kovar) writes:
>  I used to use UniPress emacs and was quite happy with the electric-c
>modes available for it. They'd provide brace completions, for statement
>outlines, and the like. The GNU Emacs c-mode is woefully inadaquate
>in my eyes. Has anyone written a more intelligent version?
>
>-David Kovar

This is a mode that started off many years ago looking a lot like Gosmacs
elec-c mode.  It has evolved (devolved?) a lot since then.  There is one
major philosophical difference with the gosmacs version.  I don't like
going into recursive-edit to insert parts of a template (eg the expression
in a case stmt) as I am prone to wonder off and edit something else and
then notice half an hour later that I am still in recursive edit.

I've been meaning to post this for a long time, but wanted to make a few
more additions to it.  I still want to make those additions but haven't in
the last year and probably won't for a while, so here it is.

This version definitely works on 18.51 and 18.52  (and I think worked
unchanged on 18.49)

cheers
mark
--------------------cut here--------------------------
;; elec C code editing commands for Emacs
;;   Mark Davies <mark@comp.vuw.ac.nz> - Dec 1985, revised Jun 1987

;; (C) Copyright 1987 Mark Davies
;; This file is released under the terms of the GNU Emacs General Public
;; Licence.

;; I invoke this from my .emacs file with the following incantation
;;   (defvar c-mode-hook 'elec-c-mode)
;;   (defvar elec-c-mode-hook '(lambda () (auto-fill-mode 1)))
;;   (setq c-auto-newline t)

;; Things to do:
;;  o allow option to put open braces on separate line. ie. formatting ala
;;    Stallman.
;;  o place the statement on current line in condition of a new if/while/do
;;    construct.
;;  o Add Comments

(defvar elec-c-mode-abbrev-table nil
  "Abbrev table in use in elec-C-mode buffers.")
(defvar elec-c-mode-map nil
  "Keymap used in elec C mode.")
(defvar elec-c-verbatim nil
  "Should abbrevs be expanded explicitly?")

(setq elec-c-mode-map (make-sparse-keymap))
(define-key elec-c-mode-map "{" 'elec-c-left-brace)
(define-key elec-c-mode-map "}" 'electric-c-brace)
(define-key elec-c-mode-map "(" 'elec-c-opening-brac)
(define-key elec-c-mode-map "[" 'elec-c-opening-brac)
(define-key elec-c-mode-map ";" 'elec-c-semi)
(define-key elec-c-mode-map ":" 'electric-c-terminator)
(define-key elec-c-mode-map "\e\C-h" 'mark-c-function)
(define-key elec-c-mode-map "\e\C-q" 'indent-c-exp)
(define-key elec-c-mode-map "\177" 'backward-delete-char-untabify)
(define-key elec-c-mode-map "\C-c\C-c" 'elec-c-close-block)
(define-key elec-c-mode-map "\C-cv" 'toggle-verbatim)
(define-key elec-c-mode-map "\e{" 'elec-c-remove-braces)
(define-key elec-c-mode-map "\C-j" 'elec-c-linefeed)
(define-key elec-c-mode-map "\t" 'c-indent-command)

(modify-syntax-entry ?# "w" c-mode-syntax-table)
(modify-syntax-entry ?_ "w" c-mode-syntax-table)

(defconst comment-edged nil
  "*Use comments of the form:
            /*
             * ...
             */")

(defun elec-c-mode ()
  "High powered C editing mode
Elec C mode provides expansion of the C control constructs:
   if, else, while, for, do, and switch.
The user types the keyword immediately followed by a space, which causes
the construct to be expanded, and the user positioned where (s)he is most
likely to want to be.
eg. when the user types a space following \"if\" the following appears in
the buffer:
            if () {
            }

and the cursor is between the parentheses.  The user can then type some
boolean expression within the parens.  Having done that, typing \\[elec-c-linefeed]
places you, appropriately indented on a new line between the braces.

Various characters in C almost always come in pairs: {}, (), [].
When the user types the first, he gets the second as well, with optional
special formatting done on {}.  You can always quote (with \\[quoted-insert]) the left
\"paren\" to avoid the expansion.

#de, and #in are defined as abbreviations for #define and #include 
respectively.

With auto-fill-mode on, three types of automatic formatting of comments are
possible. The default is of the form
                            /* ...   ... */
                            /* ...     ... */
If comment-multi-line is set non-nil you get comments of the form
                            /* ...   ...
                               ...     ... */
If additionally comment-edged is set non-nil you get comments of the form
                            /*
                             * ...    ...
                             */

Expression and list commands understand all C brackets.
Tab indents for C code.
Paragraphs are separated by blank lines only.
Delete converts tabs to spaces as it moves back.
\\{elec-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.
    with this on colons and semicolons want to go to the end of the line.
 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 a 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 elec C mode calls the value of the variable elec-c-mode-hook
with no args, if that value is non-nil."
  (interactive)
  (kill-all-local-variables)
  (use-local-map elec-c-mode-map)
  (setq major-mode 'elec-c-mode)
  (setq mode-name "elec C")
  (if (not elec-c-mode-abbrev-table)
      (let ((prev-a-c abbrevs-changed))
	(define-abbrev-table 'elec-c-mode-abbrev-table '(
        	("main" "main" elec-main 0)
		("argc" "argc" elec-argc 0)
		("if" "if" elec-if-while 0)
		("switch" "switch" elec-if-while 0)
		("while" "while" elec-if-while 0)
		("else" "else" elec-else 0)
		("for" "for" elec-for 0)
		("do" "do" elec-do 0)
		("#d" "#define" nil 0)
		("#de" "#define" nil 0)
		("#e" "#endif" nil 0)
		("#i" "#ifdef" nil 0)
		("#in" "#include" nil 0)))
	(setq abbrevs-changed prev-a-c)))
  (setq local-abbrev-table elec-c-mode-abbrev-table)
  (abbrev-mode 1)
  (set-syntax-table c-mode-syntax-table)
  (make-local-variable 'elec-c-verbatim)
  (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 t)
  (run-hooks 'elec-c-mode-hook))

; so lets hope noone writes *LARGE* C files.
(defun elec-c-inside-comment-p ()
  (nth 4 (parse-partial-sexp (point-min) (point))))

(defun elec-c-inside-string-p ()
  (nth 3 (parse-partial-sexp (point-min) (point))))

(defun elec-c-inside-comment-or-string-p ()
  (let ((parse-state (parse-partial-sexp (point-min) (point))))
    (or (nth 4 parse-state) (nth 3 parse-state))))

(defun elec-c-open-block ()
  (interactive)
  (search-forward "{")
  (backward-char 1)
  (forward-sexp 1)
  (backward-char 1)
  (split-line)
  (c-indent-line))

(defun elec-c-close-block ()
  (interactive)
  (while (not (looking-at "{"))
    (backward-up-list 1))
  (forward-sexp 1)
  (save-excursion
    (next-line -1)
    (delete-blank-lines)
    (beginning-of-line)
    (if (looking-at "[ \t]*$")
	(kill-line 1)))
  (end-of-line)
  (newline)
  (c-indent-line))

(defun elec-c-remove-braces ()
  "remove the surrounding pair of {}'s from the function."
  (interactive)
  (save-excursion
    (while (not (looking-at "{"))
      (backward-up-list 1))
    (let (end)
      (save-excursion			; kill tail
	(forward-sexp 1)
	(delete-char -1)
	(delete-horizontal-space)
	(and (bolp) (eolp)
	     (delete-char 1))
	(setq end (point-marker)))
      (delete-char 1)			; kill head
      (delete-horizontal-space)
      (and (bolp) (eolp)
	   (delete-char 1))
      (while (<= (point) (marker-position end))
	(c-indent-line)
	(forward-line 1)))))

(defun elec-c-linefeed ()
  "Go to end of line, open a new line and indent appropriately."
  (interactive)
  (end-of-line)
  (newline-and-indent))

(defun elec-c-semi (arg)
  "Insert character and correct line's indentation."
  (interactive "P")
  (if c-auto-newline
      (let ((end (point)))
	(if (not (save-excursion
		   (beginning-of-line)
		   (skip-chars-forward " \t")
		   (or (= (following-char) ?#)
		       (progn
			 (beginning-of-defun)
			 (let ((pps (parse-partial-sexp (point) end)))
			   (or (nth 3 pps) (nth 4 pps) (nth 5 pps)))))))
	    (end-of-line))
	(electric-c-terminator arg))
    (self-insert-command (prefix-numeric-value arg))))

(defun elec-c-left-brace ()
  "if c-auto-newline is on insert matching close brace and format appropriately."
  (interactive)
  (if (or (not c-auto-newline)
	  (c-inside-parens-p)
	  (elec-c-inside-comment-or-string-p))
      (insert ?{)
    (end-of-line)
    (delete-horizontal-space)
    (if (/= (char-syntax (preceding-char)) ? )
	(insert ? ))
    (insert ?{)
    (c-indent-line)
    (insert "\n\n}"))
    (c-indent-line)
    (next-line -1)
    (c-indent-line))
      
(defun elec-c-opening-brac ()
  "For one of (, [ insert it and its pair, and postion point in the centre"
  (interactive)
  (insert last-command-char)
  (if (not (elec-c-inside-comment-or-string-p))
      (save-excursion
	(cond
	 ((= last-command-char ?\() (insert ?\)))
	 ((= last-command-char ?[) (insert ?]))))))

(defun elec-main ()
  (if (elec-c-inside-comment-or-string-p)
      nil
    (insert-string " ()\n{\n}\n")
    (search-backward ")")
    (setq unread-command-char ?\^?)))

(defun elec-argc ()
  (if (save-excursion
	(beginning-of-line)
	(looking-at "[ \t]*main[ \t](argc"))
      (progn
	(insert-string ", argv")
	(end-of-line)
	(newline) (c-indent-line)
	(insert-string "int argc;")
	(newline) (c-indent-line)
	(insert-string "char *argv [];")
	(elec-c-open-block)
	(setq unread-command-char ?\^?))))

(defun elec-if-while ()
  (if (elec-c-inside-comment-or-string-p)
      nil
    (insert-string " () {\n}")
    (c-indent-line)
    (search-backward ")")
    (setq unread-command-char ?\^?)))

(defun elec-else ()
  (if (elec-c-inside-comment-or-string-p)
      nil
    (insert-string " {\n\n}")
    (c-indent-line)
    (next-line -1)
    (c-indent-line)
    (setq unread-command-char ?\^?)))
    
(defun elec-for ()
  (if (elec-c-inside-comment-or-string-p)
      nil
    (insert-string " (;;) {\n}")
    (c-indent-line)
    (search-backward ";;)")
    (setq unread-command-char ?\^?)))

(defun elec-do ()
  (if (elec-c-inside-comment-or-string-p)
      nil
    (insert-string " {\n\n} while ();")
    (c-indent-line)
    (next-line -1)
    (c-indent-line)
    (setq unread-command-char ?\^?)))

; this is a HACK but I can't think of a better place to do it.

(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)
    (and (eq major-mode 'elec-c-mode)
	 comment-edged
	 (/= last-command-char ?\t)
	 (save-excursion
	   (insert "* ")))
    (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 toggle-verbatim (arg)
  "Toggle elec-c verbatim mode.
Doesn't expand keywords unless explicitly
This command toggles that mode (off->on, on->off), 
with an argument, turns it on iff arg is positive, otherwise off."
  (interactive "P")
  (abbrev-mode arg)
  (or (assq 'elec-c-verbatim minor-mode-alist)
      (setq minor-mode-alist (append minor-mode-alist
				     (list '(elec-c-verbatim
					     " Verbatim")))))
  (setq elec-c-verbatim
	(if (null arg) (not elec-c-verbatim)
	  (> (prefix-numeric-value arg) 0))))
-- 
Domainised:  mark@comp.vuw.ac.nz	Bang form: ...!uunet!vuwcomp!mark