Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract emacs c style options from clang-format style

A colleague provided me with a clang-format style file for a C++ project we are working on. I installed clang-format.el in order to be able to format a buffer from emacs. Reformatting works as expected. Emacs default c-mode indentation is still completely different, though.

I find it quite disturbing to destroy the source code formatting when editing and restore it later. Is there any way to read a clang-format file and apply the corresponding cc-mode options?

like image 947
choeger Avatar asked Oct 06 '16 11:10

choeger


People also ask

Does clang-format work for C?

clang-format is located in clang/tools/clang-format and can be used to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.

How do you use ClangFormat?

You can install clang-format and git-clang-format via npm install -g clang-format . To automatically format a file according to Electron C++ code style, run clang-format -i path/to/electron/file.cc . It should work on macOS/Linux/Windows.

Where is clang-format config file?

Configuring Style with clang-format clang-format file located in the closest parent directory of the input file.

Is clang-format good?

Like most tools, it is not perfect nor covers every single case, but it is good enough to be helpful. clang-format can be used for several purposes: Quickly reformat a block of code to the kernel style. Specially useful when moving code around and aligning/sorting.


2 Answers

I am not aware if there is any direct conversion tool. However, you can try to use following trick:

  1. Concatenate decent amount of C++ files from your project into single one (for instance cat *.cpp > single.cpp)

  2. Apply clang-format to that single.cpp

  3. Open single.cpp within Emacs

  4. Use guess feature of CC-mode: M-x c-guess-no-install and then M-x c-guess-view

like image 122
Evgeny Panasyuk Avatar answered Sep 19 '22 05:09

Evgeny Panasyuk


I'm a bit late on this, but hopefully someone will still find this useful. I've posted the relevant portion of my clang-format config below, which provides the functionality you want.

(use-package clang-format
  :after (s)
  :init
  (defun get-clang-format-option (config-str field is-num)
    "Retrieve a config option from a clang-format config.

CONFIG-STR is a string containing the entire clang-format config.
FIELD is specific option, e.g. `IndentWidth'.  IS-NUM is a
boolean that should be set to 1 if the option is numeric,
otherwise assumed alphabetic."
    (if is-num
        (let ((primary-match (s-match (concat "^" field ":[ \t]*[0-9]+") config-str)))
          (if primary-match
              (string-to-number (car (s-match "[0-9]+" (car primary-match))))
            0))
      (let ((primary-match (s-match (concat "^" field ":[ \t]*[A-Za-z]+") config-str)))
        (if primary-match
            (car (s-match "[A-Za-z]+$" (car primary-match)))
          ""))))
  :hook (c-mode-common . (lambda ()
                           (let* ((clang-format-config
                                   (shell-command-to-string "clang-format -dump-config"))
                                  (c-offset (get-clang-format-option clang-format-config "IndentWidth" t))
                                  (tabs-str (get-clang-format-option clang-format-config "UseTab" nil))
                                  (base-style
                                   (get-clang-format-option clang-format-config "BasedOnStyle" nil)))
                             (progn
                               (if (> c-offset 0)
                                   (setq-local c-basic-offset c-offset)
                                 (if (not (equal "" base-style))
                                     (cond ((or (equal "LLVM" base-style)
                                                (equal "Google" base-style)
                                                (equal "Chromium" base-style)
                                                (equal "Mozilla" base-style))
                                            (setq-local c-basic-offset 2))
                                           ((equal "WebKit" base-style)
                                            (setq-local c-basic-offset 4)))))
                               (if (not (equal "" tabs-str))
                                   (if (not (string-equal "Never" tabs-str))
                                       (setq-local indent-tabs-mode t)
                                     (setq-local indent-tabs-mode nil))
                                 (if (not (equal "" base-style))
                                     (cond ((or (equal "LLVM" base-style)
                                                (equal "Google" base-style)
                                                (equal "Chromium" base-style)
                                                (equal "Mozilla" base-style)
                                                (equal "WebKit" base-style))
                                            (setq-local indent-tabs-mode nil))))))))))

I use use-package. If you don't, you will have to make some minor adjustments. Additionally, note that I use s.el for string manipulation.

The code looks for "IndentWidth" and "UseTab". If it finds "UseTab" and it isn't set to "Never", I set indent-tabs-mode to t. Otherwise I set it to nil. The value for "IndentWidth" goes to c-basic-offset. If these fields are not found, but "BasedOnStyle" is found, the appropriate values are set based on that style. I have not included the Microsoft style since my version of clang-format won't give me a config dump for it (presumably it's in a later version). "IndentWidth" and "UseTab" override the behavior of "BasedOnStyle" which accords with clang-format's behavior. Finally, note that c-basic-offset and indent-tabs-mode are set as buffer local variables so this setup works as expected when working on several files with different configs.

like image 42
MattHusz Avatar answered Sep 20 '22 05:09

MattHusz