[comp.emacs] Seeking Truly Electric-C mode for Gnu Emacs

mlandau@bbn.com (Matt Landau) (01/19/90)

Having recently been forced by circumstances to switch from using
Unipress Emacs -- which I really liked -- to Gnu Emacs -- which I'm
really coming to appreciate, except for the lack of a good SunView
bitmap-based terminal driver -- I find myself looking for a new C 
programming mode.

Can anyone supply a pointer to a *truly* electric C-mode?  Something
along the lines of Ray Dunn's C+ mode for MockLisp would be nice; in 
particular, something that understands that you really *do* want 
indentation after "if", "while", and "for" statements that aren't 
followed by braces.  Try as I might, I cannot get Gnu's c-mode to 
figure that one out.  (On the other hand, writing the helper functions 
to enable c-fill.el's c-comment mode on /* and disable is on */ was
pleasantly trivial :-)

I've already tried running C+ mode through translate-to-mocklisp, but
the things it does with MockLisp are too gross for the automated
translator to handle.  They're also too gross for me to translate by
hand, at least without more time than I have and a copy of the Elisp
Reference Manual!

So, that leaves me looking for a more powerful C mode.  Will the power
of the net come through again?
--
 Matt Landau			Waiting for a flash of enlightenment
 mlandau@bbn.com			  in all this blood and thunder

rberlin@birdland.sun.com (Rich Berlin) (01/20/90)

> except for the lack of a good SunView bitmap-based terminal driver

Have you checked out emacstool?  It's in the gnuemacs etc directory.
Gives you mouse support, configurable menu on the right button, and
some other goodies.

> particular, something that understands that you really *do* want 
> indentation after "if", "while", and "for" statements that aren't 
> followed by braces.  Try as I might, I cannot get Gnu's c-mode to 
> figure that one out.

Here are the settings I use.  I think they'll do what you want.

(c-indent-level		      . 4)
(c-continued-statement-offset . 4)
(c-brace-offset		      . 0)
(c-continued-brace-offset     . -4)
(c-brace-imaginary-offset     . 0)
(c-argdecl-indent	      . 4)
(c-label-offset		      . -2)
(c-auto-newline		      . nil)
(fill-column		      . 75)

-- Rich

mlandau@bbn.com (Matt Landau) (01/20/90)

rberlin@birdland.sun.com (Rich Berlin) writes:
>> Try as I might, I cannot get Gnu's c-mode to figure that one out.
>(c-continued-statement-offset . 4)
>(c-continued-brace-offset     . -4)

This turned out to be the combination of things I was looking for.  No
need for 10 more people to follow up and tell me so :-)
--
 Matt Landau			Waiting for a flash of enlightenment
 mlandau@bbn.com			  in all this blood and thunder

msb@hosmsb.ATT.COM (Mike Balenger) (01/20/90)

>>>>> On 19 Jan 90 09:01:14 GMT, mlandau@bbn.com (Matt Landau) said:

mlandau> Having recently been forced by circumstances to switch from
mlandau> using Unipress Emacs -- which I really liked -- to Gnu Emacs
mlandau> -- which I'm really coming to appreciate, except for the lack
mlandau> of a good SunView bitmap-based terminal driver -- I find
mlandau> myself looking for a new C programming mode.

I had asked the same question back in October.  I haven't gotten
around to playing with this code, but if you get a chance to, please
post a review.

Mike Balenger

================================================================

>>>>On Fri, 13 Oct 89, 1989, wpkege%snll-arpagw.llnl.gov@lll-lcc.llnl.gov (kegelmeyer william p) said:

wpkege> Greetings:
wpkege> 
wpkege> This message is in response to your request to hear what I
wpkege> learned when I requested information on a "full-featured
wpkege> C-mode" for GNU emacs from the net.  There was only one
wpkege> substantive response, but it was entirely adequate.  Dana
wpkege> Chee, dana@bellcore.com, pointed me to a package written by
wpkege> Mark Davies, mark@comp.vuw.ac.nz, that was intenend to look
wpkege> like Gosling elec-c mode, which is exactly what I was looking
wpkege> for.  Mr. Davies posted this in NOvember of 1988; I wrote to
wpkege> ask if he had a more recent version, and he indicated that it
wpkege> hasn't changed substantially since he posted it.
wpkege> 
wpkege> The mode file follows in this message.
wpkege> 
wpkege> 						Philip Kegelmeyer
wpkege> 
wpkege> Sandia National Laboratories	    voice:        (415) 294-3016
wpkege> Imaging Technologies Division	    ARPANET:	  wpkege@sandia.llnl.gov
wpkege> P.O. Box 969, ORG 8435		    alternative:  wpkege@snll-arpagw.llnl.gov
wpkege> Livermore, CA, 94551-0969	    
wpkege> 

--------------------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))))


--
----------------------------------------------------------------------
<cute quote>            Michael S. Balenger             (201) 949-8789
<cute disclaimer>       AT&T Bell Labs
                        Room 1L-405
msb@ho5cad.att.com      Crawfords Corner Road
att!ho5cad!msb          Holmdel, NJ   07733

mcdaniel@amara.UUCP (Tim McDaniel) (01/23/90)

>;;   Mark Davies <mark@comp.vuw.ac.nz> - Dec 1985, revised Jun 1987
>;; 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)

This didn't work for me.  It would load my text .emacs file, but not
the byte-compiled "equivalent" of it.  I'm not sure why: my version of
emacs? the way I autoloaded elec-c?  Anyway, I did
    (autoload 'elec-c-mode "elec-c" "" t)
    ...
    (setq c-mode-hook 'elec-c-mode)
    (setq elec-c-mode-hook
          '(lambda ()
             (setq c-auto-newline t)
             ...
           )
          )

instead, and that works both ways.
-- 
Tim McDaniel
Applied Dynamics International, Ann Arbor, MI
Internet: mcdaniel%amara.uucp@mailgw.cc.umich.edu
UUCP: {uunet,sharkey}!amara!mcdaniel