28

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.

artagnon
  • 2,237
  • 1
  • 15
  • 17
  • 2
    How about using [EditorConfig](http://editorconfig.org/) instead of `.dir-locals.el`. May be coworkers would like this idea? – welldan97 Sep 24 '14 at 02:17

5 Answers5

21

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

7

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.

artagnon
  • 2,237
  • 1
  • 15
  • 17
6

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)
chen bin
  • 4,781
  • 18
  • 36
2

I use guess-style.el.

It does pretty well at guessing a file's preferred indentation style.

NateEag
  • 586
  • 3
  • 9
2

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.

Vlad
  • 121
  • 2