0
votes

When c-auto-newline is set to non-nil, it re-indents the current line and inserts a carriage return and then indents the new line. However. I'm using 1TBS indent-style (with 4 space indents), which means if/else statements are made like this:

if (n == 1) {
    exit(EXIT_SUCCESS);
} else {
    perror("n");
}

Also, I write do/while write loops like this:

do {
    printf("%d\n", n++);
} while (n < 64);

As such, while I do want a newline automatically inserted after every opening brace and semicolon, I don't want newlines to be automatically inserted after an if statement or do loop is concluded with a closing brace.

How can I have GNU Emacs (23.2.1, *nix; or the latest CVS) selectively insert newlines like that? Along the same lines, can I have Emacs insert an opening brace, a newline, and a closing brace on another newline, while putting the cursor in the middle of the two braces after closing parentheses following an if statement, function declaration, and the like?

1

1 Answers

2
votes

This page in the cc-mode manual describes how to make the cc-mode indentation and auto-newline format a do-while loop the way you want.

You'd need to adjust that to handle the if/else case also.

As for the latter - I couldn't figure out how to do that intelligently with cc-mode so I wrote a new function to do it.

(defun csharp-insert-open-brace ()
  "Intelligently insert a pair of curly braces. This fn is most
often bound to the open-curly brace, with

    (local-set-key (kbd \"{\") 'csharp-insert-open-brace)

The default binding for an open curly brace in cc-modes is often
`c-electric-brace' or `skeleton-pair-insert-maybe'.  The former
can be configured to insert newlines around braces in various
syntactic positions.  The latter inserts a pair of braces and
then does not insert a newline, and does not indent.

This fn provides another option, with some additional
intelligence for csharp-mode.  When you type an open curly, the
appropriate pair of braces appears, with spacing and indent set
in a context-sensitive manner.

Within a string literal, you just get a pair of braces, and point
is set between them. Following an equals sign, you get a pair of
braces, with a semincolon appended. Otherwise, you
get the open brace on a new line, with the closing brace on the
line following, and point on the line between.

There may be another way to get this to happen appropriately just
within emacs, but I could not figure out how to do it.  So I
wrote this alternative.

"
  (interactive)
  (let
      (tpoint
       (in-string (string= (csharp-in-literal) "string"))
       (preceding3
        (save-excursion
          (and
           (skip-chars-backward " \t")
           (> (- (point) 2) (point-min))
           (buffer-substring-no-properties (point) (- (point) 3)))))
       (one-word-back
        (save-excursion
          (backward-word 2)
          (thing-at-point 'word))))

    (cond

     ;; Case 1: inside a string literal?
     ;; --------------------------------------------
     ;; If so, then just insert a pair of braces and put the point
     ;; between them.  The most common case is a format string for
     ;; String.Format() or Console.WriteLine().
     (in-string
      (self-insert-command 1)
      (insert "}")
      (backward-char))

     ;; Case 2: the open brace starts an array initializer.
     ;; --------------------------------------------
     ;; When the last non-space was an equals sign or square brackets,
     ;; then it's an initializer.
     ((save-excursion
        (and (c-safe (backward-sexp) t)
             (looking-at "\\(\\w+\\b *=\\|[[]]+\\)")))
      (self-insert-command 1)
      (insert "  };")
      (backward-char 3))

     ;; Case 3: the open brace starts an instance initializer
     ;; --------------------------------------------
     ;; If one-word-back was "new", then it's an object initializer.
     ((string= one-word-back "new")
      (save-excursion
        (message "object initializer")
        (setq tpoint (point)) ;; prepare to indent-region later
        (newline)
        (self-insert-command 1)
        (newline-and-indent)
        (newline)
        (insert "};")
        (c-indent-region tpoint (point))
        (previous-line)
        (indent-according-to-mode)
        (end-of-line)
        (setq tpoint (point)))
      (goto-char tpoint))

     ;; Case 4: a lambda initialier.
     ;; --------------------------------------------
     ;; If the open curly follows =>, then it's a lambda initializer.
     ((string= (substring preceding3 -2) "=>")
      (message "lambda init")
      (self-insert-command 1)
      (insert "  }")
      (backward-char 2))

     ;; else, it's a new scope. (if, while, class, etc)
     (t
      (save-excursion
        (message "new scope")
        (set-mark (point)) ;; prepare to indent-region later
        ;; check if the prior sexp is on the same line
        (if (save-excursion
              (let ((curline (line-number-at-pos))
                    (aftline (progn
                               (if (c-safe (backward-sexp) t)
                                   (line-number-at-pos)
                                 -1))))
                (= curline aftline)))
            (newline-and-indent))
        (self-insert-command 1)
        (c-indent-line-or-region)
        (end-of-line)
        (newline)
        (insert "}")
        ;;(c-indent-command) ;; not sure of the difference here
        (c-indent-line-or-region)
        (previous-line)
        (end-of-line)
        (newline-and-indent)
        ;; point ends up on an empty line, within the braces, properly indented
        (setq tpoint (point)))

      (goto-char tpoint)))))

You'd have to replace csharp-in-literal above with c-in-literal to use that in any cc-mode. If you did that, you also need to modify some of the cases in that fn. You'd want to change the array initializer and the object initializer, as I think those are novel to c#.