I work on many different C projects with unique indentation styles. How do I get Emacs to do per-project indentation settings without polluting the upstream project trees with .dir-locals.el
files? I want to match the projects using their paths on disk.
5 Answers
.dir-locals.el
is my preferred method when it's possible, especially as it applies to all Emacs users working on that project.
For projects that are hostile to Emacs users or otherwise don't want .dir-locals.el
files, or if you want to have personal preferences (presumably not indentation) that shouldn't apply to other people, if you use a distributed version control system, an option is to always work on personal branches that have your .dir-locals.el
. I don't know of a way to use a DCVS that makes this painless, however.
Another option is to not check in the .dir-locals.el
files. For example, with git, add it to .git/info/exclude
. That's painful when you have many checkouts of a project, of course.
What I've done where .dir-locals.el
wasn't an option is to add a hook that looks up buffer-file-name
against known values.
(defvar guessed-project nil)
(make-variable-buffer-local 'guessed-project)
(defun guess-project-from-file-name ()
(save-match-data
(setq guessed-project
(cond
((string-match "/linux-kernel/" buffer-file-name)
'linux-kernel)
((string-match "/gnu/" buffer-file-name)
'gnu)
; etc.
))))
(defun c-my-project-hook ()
(guess-project-from-file-name)
(case guessed-project
((gnu) (c-sty-style 'gnu))
((linux-kernel) (c-sty-style 'linux))
))
(add-hook 'c-mode-hook 'c-my-project-hook)
(Warning: code typed directly into my browser as I don't have access to my real code right now.)
-
2Adding
.dir-locals.el
to.git/info/exclude
seems like a good option.– shostiCommented Sep 23, 2014 at 21:51 -
What's wrong with adding it to
.gitignore
in the root of a project?– SquidlyCommented Oct 27, 2014 at 12:52 -
@Nothing, but some projects don't want editor-specific files there. Commented Oct 27, 2014 at 14:13
-
For git users, you can add
.dir-locals.el
to your global gitignore at~/.config/git/ignore
.– JeremyCommented Jun 16, 2015 at 17:56
You can't convince upstream projects like llvm and linux to check in a .dir-locals.el
.
An elaborate solution to the problem:
(defmacro define-new-c-style (name derived-from style-alist match-path)
`(progn
(c-add-style ,name
'(,derived-from ,@style-alist))
(add-hook 'c-mode-hook
(lambda ()
(let ((filename (buffer-file-name)))
(when (and filename
(string-match (expand-file-name ,match-path) filename))
(c-set-style ,name)))))))
Used as follows:
(define-new-c-style "llvm" "gnu" ((fill-column . 80)
(c++-indent-level . 2)
(c-offsets-alist . ((innamespace 0))))
"~/src/llvm")
(define-new-c-style "emacs" "gnu" nil "~/src/emacs")
I have a similar macro for other language modes as well.
distributing .dir-locals.el in different projects is hard to maintain.
My way is pretty simple, put all the code in one ~/.custom.el.
Basically, the code will be run in prog-mode-hook (or your-whatever-major-mode-hook) and do the following things:
- analyze whether the full path of file does contain the specific project name
- if true then do the setup for that project (including tweaking the indent).
I've been successfully using this method for one year.
Here is the code (call my-setup-develop-environment in prog-mode-hook):
(defun my-project-name-contains-substring (REGEX)
(let ((dir (if (buffer-file-name)
(file-name-directory (buffer-file-name))
"")))
(string-match-p REGEX dir)))
(defun my-office-code-style ()
(interactive)
(message "Office code style!")
;; web development
(setq coffee-tab-width 4)
(setq javascript-indent-level 4)
(setq js-indent-level 4)
(setq js2-basic-offset 4)
(setq web-mode-indent-style 4)
(setq web-mode-markup-indent-offset 4)
(setq web-mode-css-indent-offset 4)
(setq web-mode-code-indent-offset 4)
)
(defun my-personal-code-style ()
(interactive)
(message "My personal code style!")
(setq coffee-tab-width 4)
(setq javascript-indent-level 2)
(setq js-indent-level 2)
(setq js2-basic-offset 2)
(setq web-mode-indent-style 2)
(setq web-mode-markup-indent-offset 2)
(setq web-mode-css-indent-offset 2)
(setq web-mode-code-indent-offset 2)
)
(defun my-setup-develop-environment ()
(interactive)
(cond
;; company's project for ttag
((my-project-name-contains-substring "commerical-proj1")
(my-office-code-style))
((my-project-name-contains-substring "hobby-proj1")
(my-personal-code-style))
)
)
(add-hook 'prog-mode-hook 'my-setup-develop-environment)
The normal way to do it is using .dir-locals
. This is why they were introduced in the first place. Why not? To my knowledge, most of project-related tools tend to keep their configs in the root project directory: git, svn, Eclipse and many others…
Just don't forget to add the file to .gitignore
or similar.
I use guess-style.el.
It does pretty well at guessing a file's preferred indentation style.
.dir-locals.el
. May be coworkers would like this idea?