I'm a big believer in
using clang-format to
automatically format C and C++ code. Typically this is done by adding a file
called .clang-format
to the root directory of a project. This file tells
clang-format about how code in the project should be formatted. When
clang-format is run, it will reformat source code according to the style you've
specified. This is conceptually similar to gofmt
in Go, or yapf
in Python. I
include a .clang-format
file in all of my C/C++ projects.
I edit code using GNU Emacs, via Spacemacs. The C/C++
layer for Spacemacs has an option to automatically run clang-format on buffers
when saving them. If you enable clang-format-on-save
in the c-c++
layer,
then whenever you save a C or C++ file Emacs will reformat the buffer using
clang-format before actually persisting the buffer to disk.
I absolutely want this behavior for all of my personal projects, since it
ensures that I don't check in improperly formatted code. But I typically don't
want this behavior when hacking on third party C or C++ libraries. There are a
lot of different styles for C/C++ coding, and most projects don't include
.clang-format
files. If you try to use clang-format on a file from one of
these projects, you'll end up reformatting the entire file you're editing. Thus
I find the default behavior in Spacemacs to be kind of braindead, since it's all
or nothing. I suppose it would be OK if you were only hacking on personal (or
work) projects, and never had to touch open source code. But what's the fun in
that?
I finally sat down this weekend and fixed this. The idea is to enable
clang-format on buffers only when the project has a top level .clang-format
file. That way my personal projects will all get formatted nicely, but I can
also edit third party open source projects and not reformat the entire file when
I'm making a small change.
This works using a function that reformats the current buffer, but only if a
.clang-format
file exists in the
current Projectile project root:
(defun clang-format-buffer-smart ()
"Reformat buffer if .clang-format exists in the projectile root."
(when (f-exists? (expand-file-name ".clang-format" (projectile-project-root)))
(clang-format-buffer)))
I add this as a before-save-hook
for C and C++ buffers:
(defun clang-format-buffer-smart-on-save ()
"Add auto-save hook for clang-format-buffer-smart."
(add-hook 'before-save-hook 'clang-format-buffer-smart nil t))
(spacemacs/add-to-hooks 'clang-format-buffer-smart-on-save
'(c-mode-hook c++-mode-hook))
In my actual Emacs configuration, this is implemented as a custom Spacemacs
layer that extends the existing c-c++
layer. I'd like to contribute this code
upstream, but there's a valid use case for the existing behavior, and the
existing layer already has two different configuration options related to
clang-format, so adding a third doesn't seem productive. Let me know if you find
this useful.