Emacs vs Vim C++ syntax highlighting

I don’t like the way Emacs highlights C++. Not the colours themselves but how it decides to assign them.

Emacs is on the left with the badger theme, Vim on the right with sorcerer:

Classes and functions get different colours, variables get different colours where they’re defined and used, numbers don’t stand out, etc. It’s a jumble of different colours, distracting and difficult to read.

I want it to look like Vim, and also want separate colours for C++, independent of the theme affecting the rest of Emacs.

This stack overflow answer describes how to add support for C++11 keywords. It copies the faces from the theme, then adds new regexes with font-lock-add-keywords, so I just expanded it and added some make-local-variable calls to vary the string and comment colours in the buffer.

(require 'font-lock)

(load-theme 'badger)

; http://stackoverflow.com/a/12934513
(defun --copy-face (new-face face)
  "Define NEW-FACE from existing FACE."
  (copy-face face new-face)
  (eval `(defvar ,new-face nil))
  (set new-face new-face))

(--copy-face 'font-lock-mykeyword-face 'font-lock-keyword-face)
(--copy-face 'font-lock-mytype-face 'font-lock-type-face)
(--copy-face 'font-lock-myidentifier-face 'font-lock-builtin-face)
(--copy-face 'font-lock-mydefault-face 'font-lock-builtin-face)
(--copy-face 'font-lock-myconstant-face 'font-lock-constant-face)
(--copy-face 'font-lock-mypreprocessor-face 'font-lock-preprocessor-face)
(--copy-face 'font-lock-mynewline-face 'font-lock-string-face)
(--copy-face 'font-lock-mystring-face 'font-lock-string-face)
(--copy-face 'font-lock-mycomment-face 'font-lock-comment-face)
(--copy-face 'font-lock-mywarning-face 'font-lock-warning-face)

(global-font-lock-mode t)
(setq font-lock-maximum-decoration t)

(defun my-cpp-colours ()
  (set (make-local-variable 'font-lock-string-face) 'font-lock-mystring-face)
  (set (make-local-variable 'font-lock-comment-face) 'font-lock-mycomment-face)
  (font-lock-add-keywords
   nil '(

	 ;; preprocessor
	 ("#.*\\(<.*>\\)" 1 font-lock-mystring-face)
	 ("#\\(include\\) *\\\".*\\\"" 1 font-lock-mypreprocessor-face)
	 ("#\\(include\\) *<.*>" 1 font-lock-mypreprocessor-face)
	 ("#\\<\\(define\\|include\\|undef\\|ifdef\\|ifndef\\|if\\|endif\\|pragma\\)\\>.*"
	  . font-lock-mypreprocessor-face)
	 ("#" . font-lock-mypreprocessor-face)

	 ;; keywords and types
	 ("\\<\\(void\\|unsigned\\|signed\\|char\\|short\\|bool\\|int\\|long\\|float\\|double\\)\\>"
	  . font-lock-mytype-face)
	 ("\\<\\(typename\\|template\\|inline\\|namespace\\|public\\|protected\\|private\\)\\>"
	  . font-lock-mytype-face)
	 ("\\<\\(auto\\|volatile\\|static\\|const\\)\\>"
	  . font-lock-mytype-face)
	 ("\\<\\(alignof\\|alignas\\|constexpr\\|decltype\\|noexcept\\|nullptr\\|static_assert\\|thread_local\\|override\\|final\\)\\>"
	  . font-lock-mykeyword-face)
	 ("\\<\\(char[0-9]+_t\\|u?int[0-9]+_t\\)\\>"
	  . font-lock-mykeyword-face)
	 ("\\<\\(for\\|while\\|do\\|if\\|else\\|return\\|continue\\|break\\|switch\\|case\\|default\\)\\>"
	  . font-lock-mykeyword-face)
	 ("\\<\\(new\\|delete\\|this\\)\\>"
	  . font-lock-mykeyword-face)
	 ("\\<\\(using\\|class\\|struct\\|virtual\\)\\>"
	  . font-lock-mykeyword-face)

	 ;; hexadecimal numbers
	 ("\\<0[xX][0-9A-Fa-f]+\\>"
	  . font-lock-myconstant-face)
	 ;; booleans
	 ("\\<\\(false\\|true\\)\\>"
	  . font-lock-myconstant-face)
	 ;; integer/float/scientific numbers
	 ("\\<[\\-+]*[0-9]*\\.?[0-9]+\\([ulULf]+\\|[eE][\\-+]?[0-9]+\\)?\\>"
	  . font-lock-myconstant-face)

	 ;; quote/newline/tab
	 ("\\(\\\\\"\\|\\\\n\\|\\\\t\\)"
	  1 font-lock-mynewline-face t)

	 ("\\<\\(FIXME\\|TODO\\)\\>"
	  1 font-lock-mywarning-face t)

	 ("\\<[A-Za-z_]+[A-Za-z_0-9]*\\>"
	  . font-lock-myidentifier-face)
	
	 ("." . font-lock-mydefault-face)

	 ) 'set)
  )

I also want the text between #if 0 and #endif greyed out. I tried a few methods, this stack overflow answer worked best:

; https://stackoverflow.com/a/7215951
(defun cpp-highlight-if-0/1 ()
  "Modify the face of text in between #if 0 ... #endif."
  (interactive)
  (setq cpp-known-face '(foreground-color . "#686858"))
  (setq cpp-unknown-face 'default)
  (setq cpp-face-type 'dark)
  (setq cpp-known-writable 't)
  (setq cpp-unknown-writable 't)
  (setq cpp-edit-list
        '((#("1" 0 1
             (fontified nil))
           nil
           (foreground-color . "#686858")
           both nil)
          (#("0" 0 1
             (fontified nil))
           (foreground-color . "#686858")
           nil
           both nil)))
  (cpp-highlight-buffer t))

Call them from the mode hook:

(defun my-cpp-mode-hook ()
  ;;(evil-local-set-key 'normal (kbd "M-m") 'projectile-compile-project)
  (auto-fill-mode -1)
  (cpp-highlight-if-0/1)
  (add-hook 'after-save-hook 'cpp-highlight-if-0/1 'append 'local)
  (my-cpp-colours)
  )

(add-hook 'c++-mode-hook 'my-cpp-mode-hook t)
(add-hook 'c-mode-hook 'my-cpp-mode-hook t)

Add entries to custom-set-faces to make the colours match sorcerer:

(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 ;; ...
 '(font-lock-mycomment-face ((t (:foreground "#686858" :slant italic))) t)
 '(font-lock-myconstant-face ((t (:foreground "#ff9800"))) t)
 '(font-lock-mydefault-face ((t (:foreground "#c2c2b0"))) t)
 '(font-lock-myidentifier-face ((t (:foreground "#c2c2b0"))) t)
 '(font-lock-mykeyword-face ((t (:foreground "#90b0d1"))) t)
 '(font-lock-mynewline-face ((t (:foreground "#119611"))) t)
 '(font-lock-mypreprocessor-face ((t (:foreground "#528b8b"))) t)
 '(font-lock-mystring-face ((t (:foreground "#779b70"))) t)
 '(font-lock-mytype-face ((t (:foreground "#7e8aa2"))) t)
 '(font-lock-mywarning-face ((t (
	:foreground "#cf7fcf" :background "#202020"
	:slant italic :underline t :bold t))) t)
 ;; ...
 )

Now Emacs renders C++ closer to the style in Vim:

Advertisements

2 thoughts on “Emacs vs Vim C++ syntax highlighting

  1. In the list for font-lock-add-keywords, try adding this between the FIXME|TODO expression and the myidentifier one:

    (I would have pasted that in as text, but this wordpress comment field keeps destroying the code and collapsing it to nothing, even with code tags around it.)

    Define font-lock-myfuncall-face in the same way as the others. That should get function calls, but it will also get function definitions. I don’t know a straightforward way to isolate only the calls. I’m sure it’s possible, but more work.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s