8

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

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

  • 1
    reported! it would indeed be nice if the two could work together. – iceman Oct 30 '14 at 16:53
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")))
  • 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
3

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

(sp-local-pair 'c-mode "{" nil :post-handlers '(:add ("||\n[i]" "RET")))
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.