9

I'm trying to create a function that places/aligns/indents curly brackets according to Allman-style formatting (for coding in C).

Generally speaking, I am fan of Smartparens' interface available to users for customizing functionality. I've written a bunch of other functions using the Smartparens interface so would have a strong preference not to switch packages at this point. That said, I'm open to package-agnostic implementations (e.g., could defadvice be relevant here?).

On to the problem at hand. What's the end goal? Let's suppose we're coding and we reach the state represented below. The pipe symbol represents the cursor; I've typed the function header and the opening brace {, and Smartparens has automatically added the closing brace }. At this point, I'd like it so that pressing RET...

int main {|}

...leads to the following:

int main
{
    |
}

I've been able to write the function that results in this behavior but it only works for the first level of indentation (e.g., for our main function in the example above). I can't get it to work for subsequent levels of indentation (see gif):

enter image description here

The relevant code is below. The function isn't pretty but I think it should work... The very last line is the interface to Smartparens.

Any suggestions?


(defun my-create-newline-and-enter-sexp (&rest _ignored)
  "Open a new brace or bracket expression, with relevant newlines and indent. "
  (interactive)
  (progn
    (backward-char 2) (newline) (forward-char) (newline)     
    (indent-according-to-mode)                               
    (previous-line 2) (indent-according-to-mode)         
    (next-line) (next-line) (indent-according-to-mode)))      

(sp-local-pair 'c-mode "{" nil :post-handlers '((my-create-newline-and-enter-sexp "RET")))
iceman
  • 1,078
  • 1
  • 9
  • 19

4 Answers4

12

Emacs-24.4's electric-pair-mode does one part of what you want already (this mode is very similar to autopair.el, not sure how it compares to smartparens). And c-toggle-auto-newline does the other part.

But sadly they don't work right together. Please M-x report-emacs-bug so we can fix that.

Instead of c-toggle-auto-newline, you can also use electric-layout-mode with a setting like (setq electric-layout-rules '((?\{ . around) (?\} . around))).

Stefan
  • 26,154
  • 3
  • 46
  • 84
7

Solved. The sequence of cursor movements from my first version (in the original post) was funky.

As reference for future readers, the following code should work. It obv needs the Smartparens package (which you can get from git-hub). I'm running Emacs 24.4. It works with electric-indent mode enabled or disabled.

(defun my-create-newline-and-allman-format (&rest _ignored)
"Allman-style formatting for C."
  (interactive)
  (progn
    (newline-and-indent)
    (previous-line) (previous-line) (search-forward "{") (backward-char) (newline-and-indent)
    (next-line) (indent-according-to-mode)))

And you'll need to include the following in your init file as well, somewhere after loading the Smartparens package:

(sp-local-pair '(c-mode) "{" nil :post-handlers '((my-create-newline-and-allman-format "RET")))
Stepan S
  • 65
  • 11
iceman
  • 1,078
  • 1
  • 9
  • 19
  • 2
    Rather than use things like `previous-line` with `search-forward` to try and re-discover where you were, you're much better off remembering your position in a variable and then just use `goto-char`. – Stefan Oct 30 '14 at 12:48
  • That's probably the more robust route. Are there any functions/variables/etc that you think might be helpful in this context? I can only think of `save-excursion` but am sure there are others I don't know. – iceman Oct 30 '14 at 16:49
4

Another, terser, way is to use a smartparens insertion spec.

(sp-local-pair 'c-mode "{" nil :post-handlers '(:add ("||\n[i]" "RET")))
PythonNut
  • 10,243
  • 2
  • 29
  • 75
0

Here's the code that I use, just to give you more ideas to amend your code:

(defun ins-c++-curly ()
  "Insert {}.
Threat is as function body when from endline before )"
  (interactive)
  (cond ((eq major-mode 'term-mode)
         (term-send-raw-string "{}")
         (term-send-raw-string "^B"))
        ((looking-back "\\()\\|try\\|else\\|const\\|:\\)$")
         (insert " {\n\n}")
         (indent-according-to-mode)
         (forward-line -1)
         (indent-according-to-mode))
        ((region-active-p)
         (let ((beg (region-beginning))
               (end (region-end)))
           (deactivate-mark)
           (goto-char beg)
           (insert "{")
           (goto-char (1+ end))
           (insert "}")))
        (t
         (insert "{}")
         (indent-according-to-mode)
         (backward-char))))

I do prefer the hanging brace style, as it saves space.

abo-abo
  • 13,943
  • 1
  • 29
  • 43