Reputation: 192657
In various IDEs, typing an open-curly results in the appearance of a matched pair of brace characters. Typically the braces are inserted in some context-sensitive way. Within a string literal, there is no intervening newline inserted between the braces. Outside a string literal, there is a newline, and things are indented immediately.
{
_cursor_
}
As I continue to type at the cursor, all new code is properly indented.
In Emacs, though, by default in my cc-mode (csharp-mode, java-mode, etc.), an open-curly runs self-insert-command which just inserts the open brace with no indent. The close-curly runs c-electric-brace, which indents the close curly only, not the full scope. The result is while I am keying within the curly scope, the indentation is all wrong, and I have to manually re-indent the curly scope when it closes.
Is there an easy way to get Emacs to behave like the popular IDEs I have used? I have written some Emacs Lisp to do, it but it is not very general and I want to know if I am mising something.
I know about the skeleton-pair-insert-maybe function. It inserts matched pairs of whatever: braces, parens, quotes, angle brackets, square brackets. But that function doesn't do any context-sensitive indenting and doesn't give me the blank newline. Is there a way to get it to indent or ... is there another function I should bind to open-curly to get what I want?
PS: my Emacs Lisp looks like this:
; The default binding for "open curly" was skeleton-pair-insert-maybe. It
; inserts a pair of braces and then does not insert a newline, and does not
; indent. I want the braces to get newlines and appropriate indenting. I
; think there is a 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. The
; key thing is to determine if the point is within a string. In cc-mode, this
; is at least sometimes done by looking at the font face. Then, if not in a
; literal string, do the appropriate magic. This seems to work.
(defun cheeso-insert-open-brace ()
"if point is not within a quoted string literal, insert an open brace, two newlines, and a close brace; indent everything and leave point on the empty line. If point is within a string literal, just insert a pair or braces, and leave point between them."
(interactive)
(if
; are we inside a string?
(c-got-face-at (point) c-literal-faces)
; if so, then just insert a pair of braces and put the point between them
(progn
(self-insert-command 1)
(insert "}")
(backward-char)
)
; not inside a literal string.
; therefore, insert paired braces with an intervening newline, and indent everything appropriately.
(progn
(self-insert-command 1)
(c-indent-command)
(newline)
(insert "}")
(c-indent-command)
(previous-line)
(newline-and-indent)
; point ends up on an empty line, within the braces, properly indented
)
)
)
Upvotes: 2
Views: 1525
Reputation: 192657
I accepted the answer from kastauyra, but still went with my own custom elisp to do what I wanted, which was insert a matched set of braces as appropriate. The original elisp code I posted failed for a couple edge cases. This is the updated elisp code I use now.
(defun cheeso-looking-back-at-regexp (regexp)
"calls backward-sexp and then checks for the regexp. Returns t if it is found, else nil"
(interactive "s")
(save-excursion
(backward-sexp)
(looking-at regexp)
)
)
(defun cheeso-looking-back-at-equals-or-array-init ()
"returns t if an equals or [] is immediate preceding. else nil."
(interactive)
(cheeso-looking-back-at-regexp "\\(\\w+\\b *=\\|[[]]+\\)")
)
(defun cheeso-prior-sexp-same-statement-same-line ()
"returns t if the prior sexp is on the same line. else nil"
(interactive)
(save-excursion
(let ((curline (line-number-at-pos))
(curpoint (point))
(aftline (progn
(backward-sexp)
(line-number-at-pos))) )
(= curline aftline)
)
)
)
(defun cheeso-insert-open-brace ()
"if point is not within a quoted string literal, insert an open brace, two newlines, and a close brace; indent everything and leave point on the empty line. If point is within a string literal, just insert a pair or braces, and leave point between them."
(interactive)
(cond
;; are we inside a string literan?
((c-got-face-at (point) c-literal-faces)
;; if so, then just insert a pair of braces and put the point between them
(self-insert-command 1)
(insert "}")
(backward-char)
)
;; was the last non-space an equals sign? or square brackets? Then it's an initializer.
((cheeso-looking-back-at-equals-or-array-init)
(self-insert-command 1)
;; all on the same line
(insert " };")
(backward-char 3)
)
;; else, it's a new scope.
;; therefore, insert paired braces with an intervening newline, and indent everything appropriately.
(t
(if (cheeso-prior-sexp-same-statement-same-line)
(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
)
)
)
Within my c-mode hook function, I bind the open-curly '{' to cheeso-insert-open-brace, like so:
(local-set-key (kbd "{") 'cheeso-insert-open-brace)
The result is, when I have something like this:
for(;;)
and then key in a open-curly, I get this:
for(;;)
{
_cursor_
}
If I have an initializer like this:
byte[] x =
and I key in an open curly, I get this:
byte[] x = { _cursor_ };
Upvotes: 2
Reputation: 10856
This .emacs excerpt (I must have found it on EmacsWiki some time ago) will do what you need:
;; Enter indents the new line
(defun my-make-CR-do-indent ()
(define-key c-mode-base-map "\C-m" 'c-context-line-break))
(add-hook 'c-initialization-hook 'my-make-CR-do-indent)
Works like this:
int main(void)
{
{
{
{
Nowhere I had to press space or tab to enter it.
Upvotes: 1