;;; cosmo-format.el --- Cosmopolitan Clang-Format Integration ;; Author: Justine Tunney ;; Version: 0.1.0 ;; License: Public Domain ;; Keywords: c c++ clang ;; To the extent possible under law, Justine Tunney has waived all ;; copyright and related or neighboring rights to this file, as it is ;; written in the following disclaimers: and ;; ;;; Commentary: ;; ;; This module automates indentation, whitespace, and other stylistic ;; concerns while editing C/C++ source files. The clang-format program, ;; if present on the system, is run each time a buffer is saved. ;;; Installation: ;; ;; Put the following in your .emacs.d/init.el file: ;; ;; (require 'cosmo-format) ;; ;; Put this file in the root of your project: ;; ;; printf '---\nBasedOnStyle: Google\n...\n' >.clang-format ;; ;; Any buffer whose pathname matches `cosmo-format-path-regex' will ;; be formatted automatically on save if: ;; ;; 1. It's able to find the clang-format program, or ;; `cosmo-format-bin' is customized. ;; ;; 2. There's a .clang-format file up the directory tree, or ;; `cosmo-format-arg' is customized; in which case, it is ;; recommended that it be customized buffer locally. ;; ;; For all other cases, there are no latency penalties (i.e. superfluous ;; i/o syscalls) or risks to leaving this enabled globally. ;;; Code: (defcustom cosmo-format-bin nil "Explicit command or pathname of clang-format program." :type 'string :group 'cosmo-format) (defcustom cosmo-format-arg nil "Explicit argument to clang-format program." :type 'string :group 'cosmo-format) (defcustom cosmo-format-modes '(c-mode c++-mode java-mode protobuf-mode) "List of major-modes that need clang-format." :type '(repeat symbol) :group 'cosmo-format) (defcustom cosmo-format-exts '("c" "cc" "h" "inc" ;; c/c++ "hh" "cpp" "hpp" ;; ms c/c++ "rl" ;; ragel "proto") ;; protobuf "List of pathname extensions that need clang-format." :type '(repeat string) :group 'cosmo-format) (defcustom cosmo-format-blacklist '() "List of files to ignore, matched by basename." :type '(repeat string) :group 'cosmo-format) (defvar cosmo--clang-format-bin) (defmacro cosmo-memoize (var mvar form) "Return VAR or evaluate FORM memoized locally to MVAR." `(cond (,var ,var) ((fboundp (quote ,mvar)) (cond ((eq ,mvar 'null) nil) (t ,mvar))) (t (let ((res ,form)) (setq-local ,mvar (or res 'null)) res)))) (defun cosmo--find-clang-format-bin () (cosmo-memoize cosmo-format-bin cosmo--clang-format-bin (or (executable-find "clang-format-10") (executable-find "clang-format-9") (executable-find "clang-format-8") (executable-find "clang-format-7") (executable-find "clang-format")))) (defun cosmo-format () "Beautifies source code in current buffer." (interactive) (when (and (memq major-mode cosmo-format-modes) (member (file-name-extension (buffer-file-name)) cosmo-format-exts) (not (member (file-name-nondirectory (buffer-name)) cosmo-format-blacklist))) (let ((bin (cosmo--find-clang-format-bin))) (when bin (let ((p (point)) (tmp (make-temp-file "cosmo-format")) (arg (or cosmo-format-arg (and (locate-dominating-file (buffer-file-name) ".clang-format") "-style=file")))) (when arg (message arg) (write-region nil nil tmp) (let ((buf (get-buffer-create "*clang-format*")) (exe (cosmo--find-clang-format-bin))) (with-current-buffer buf (call-process exe tmp t nil arg)) (replace-buffer-contents buf) (kill-buffer buf) (delete-file tmp nil)))))))) ;; Emacs 26.3+ needed for replace-buffer-contents; so worth it!! (unless (version-list-< (version-to-list emacs-version) '(26 3)) (add-hook 'before-save-hook 'cosmo-format)) (provide 'cosmo-format) ;;; cosmo-format.el ends here