diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index a6fd1c5ec..82ed2af22 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -1,7 +1,7 @@ ;;; lang/cc/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(defun +cc*lineup-arglist (orig-fun &rest args) +(defun +cc*align-lambda-arglist (orig-fun &rest args) "Improve indentation of continued C++11 lambda function opened as argument." (if (and (eq major-mode 'c++-mode) (ignore-errors @@ -21,59 +21,61 @@ (backward-char) (looking-at-p "[^ \t]>")) (forward-char) - (call-interactively 'self-insert-command))) - -(defun +cc--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)) - -;;;###autoload -(defun +cc|extra-fontify-c++ () - ;; We could place some regexes into `c-mode-common-hook', but - ;; note that their evaluation order matters. - ;; NOTE modern-cpp-font-lock will eventually supercede some of these rules - (font-lock-add-keywords - nil '(;; c++11 string literals - ;; L"wide string" - ;; L"wide string with UNICODE codepoint: \u2018" - ;; u8"UTF-8 string", u"UTF-16 string", U"UTF-32 string" - ("\\<\\([LuU8]+\\)\".*?\"" 1 font-lock-keyword-face) - ;; R"(user-defined literal)" - ;; R"( a "quot'd" string )" - ;; R"delimiter(The String Data" )delimiter" - ;; R"delimiter((a-z))delimiter" is equivalent to "(a-z)" - ("\\(\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(\\)" 1 font-lock-keyword-face t) ; start delimiter - ( "\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(\\(.*?\\))[^\\s-\\\\()]\\{0,16\\}\"" 1 font-lock-string-face t) ; actual string - ( "\\<[uU8]*R\"[^\\s-\\\\()]\\{0,16\\}(.*?\\()[^\\s-\\\\()]\\{0,16\\}\"\\)" 1 font-lock-keyword-face t) ; end delimiter - ) t)) - -;;;###autoload -(defun +cc|extra-fontify-c/c++ () - (font-lock-add-keywords - nil '(;; PREPROCESSOR_CONSTANT, PREPROCESSORCONSTANT - ("\\<[A-Z]*_[A-Z_]+\\>" . font-lock-constant-face) - ("\\<[A-Z]\\{3,\\}\\>" . font-lock-constant-face) - ;; integer/float/scientific numbers - ("\\<\\([\\-+]*[0-9\\.]+\\)\\>" 1 font-lock-constant-face t) - ("\\<\\([\\-+]*[0-9\\.]+\\)\\(f\\)\\>" - (1 font-lock-constant-face t) - (2 font-lock-keyword-face t)) - ("\\<\\([\\-+]*[0-9\\.]+\\)\\([eE]\\)\\([\\-+]?[0-9]+\\)\\>" - (1 font-lock-constant-face t) - (2 font-lock-keyword-face t) - (3 font-lock-constant-face t)) - ) t)) + (call-interactively #'self-insert-command))) ;;;###autoload (defun +cc-sp-point-is-template-p (id action context) + "Return t if point is in the right place for C++ angle-brackets." (and (sp-in-code-p id action context) (sp-point-after-word-p id action context))) ;;;###autoload (defun +cc-sp-point-after-include-p (id action context) + "Return t if point is in an #include." (and (sp-in-code-p id action context) (save-excursion (goto-char (line-beginning-position)) (looking-at-p "[ ]*#include[^<]+")))) + +;;;###autoload +(defun +cc-c-lineup-inclass (_langelem) + "Indent privacy keywords at same level as class properties." + (if (memq major-mode '(c-mode c++-mode)) + (let ((inclass (assq 'inclass c-syntactic-context))) + (save-excursion + (goto-char (c-langelem-pos inclass)) + (if (or (looking-at "struct") + (looking-at "typedef struct")) + '+ + '++))) + '+)) + + +;; +;; Hooks +;; + +;;;###autoload +(defun +cc|fontify-constants () + "Better fontification for preprocessor constants" + (font-lock-add-keywords + nil '(("\\<[A-Z]*_[A-Z_]+\\>" . font-lock-constant-face) + ("\\<[A-Z]\\{3,\\}\\>" . font-lock-constant-face)) + t)) + +;;;###autoload +(defun +cc|irony-add-include-paths () + "Seek out and add the nearest include/ folders to clang's options." + (when-let (dir (locate-dominating-file buffer-file-name "include/")) + (cl-pushnew (concat "-I" (expand-file-name "include/" dir)) + irony-additional-clang-options :test #'equal))) + +;;;###autoload +(defun +cc|use-c++11 () + "Enable C++11 support with clang (via irony)." + (cl-pushnew "-std=c++11" irony-additional-clang-options :test #'equal) + (when IS-MAC + ;; NOTE beware: you'll get abi-inconsistencies when passing std-objects to + ;; libraries linked with libstdc++ (e.g. if you use boost which wasn't + ;; compiled with libc++) + (cl-pushnew "-stdlib=libc++" irony-additional-clang-options :test #'equal))) diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index 2b2e3fec4..635af50aa 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -27,30 +27,13 @@ (push (cons #'+cc--objc-header-file-p 'objc-mode) magic-mode-alist) :config - (setq c-tab-always-indent nil - c-electric-flag nil) - (set! :electric '(c-mode c++-mode objc-mode java-mode) :chars '(?\n ?\})) - (set! :company-backend '(c-mode c++-mode objc-mode) '(company-irony-c-headers company-irony)) - (add-hook 'c-mode-common-hook #'rainbow-delimiters-mode) - (add-hook 'c-mode-hook #'highlight-numbers-mode) ; fontify numbers in C - (add-hook 'c++-mode-hook #'+cc|extra-fontify-c++) ; fontify C++11 string literals - - (sp-with-modes '(c-mode c++-mode objc-mode java-mode) - (sp-local-pair "<" ">" :when '(+cc-sp-point-is-template-p +cc-sp-point-after-include-p)) - (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))) - ;; Doxygen blocks - (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET") ("||\n[i]" "SPC"))) - (sp-local-pair "/*!" "*/" :post-handlers '(("||\n[i]" "RET") ("[d-1]< | " "SPC")))) - - ;; Improve indentation of inline lambdas in C++11 - (advice-add #'c-lineup-arglist :around #'+cc*lineup-arglist) - + ;;; Style/formatting ;; C/C++ style settings (c-toggle-electric-state -1) (c-toggle-auto-newline -1) @@ -62,44 +45,38 @@ (c-set-offset 'access-label '-) (c-set-offset 'arglist-intro '+) (c-set-offset 'arglist-close '0) + ;; Indent privacy keywords at same level as class properties + ;; (c-set-offset 'inclass #'+cc-c-lineup-inclass) - (defun +cc--c-lineup-inclass (_langelem) - (if (memq major-mode '(c-mode c++-mode)) - (let ((inclass (assq 'inclass c-syntactic-context))) - (save-excursion - (goto-char (c-langelem-pos inclass)) - (if (or (looking-at "struct") - (looking-at "typedef struct")) - '+ - '++))) - '+)) - (c-set-offset 'inclass #'+cc--c-lineup-inclass) + ;;; Better fontification (also see `modern-cpp-font-lock') + (add-hook 'c-mode-common-hook #'rainbow-delimiters-mode) + (add-hook! (c-mode c++-mode) #'highlight-numbers-mode) + (add-hook! (c-mode c++-mode) #'+cc|fontify-constants) + ;; Improve indentation of inline lambdas in C++11 + (advice-add #'c-lineup-arglist :around #'+cc*align-lambda-arglist) - ;; Certain electric mappings interfere with smartparens and custom bindings, - ;; so unbind them - (map! :map c-mode-map - "DEL" nil - "#" #'self-insert-command - "{" #'self-insert-command - "}" #'self-insert-command - "/" #'self-insert-command - "*" #'self-insert-command - ";" #'self-insert-command - "," #'self-insert-command - ":" #'self-insert-command - "(" #'self-insert-command - ")" #'self-insert-command - - :map c++-mode-map - "}" nil - - ;; Smartparens and cc-mode both try to autoclose angle-brackets - ;; intelligently. The result isn't very intelligent (causes redundant - ;; characters), so just do it ourselves. + ;;; Keybindings + ;; Completely disable electric keys because it interferes with smartparens and + ;; custom bindings. We'll do this ourselves. + (setq c-tab-always-indent nil + c-electric-flag nil) + (dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")")) + (define-key c-mode-base-map key nil)) + ;; Smartparens and cc-mode both try to autoclose angle-brackets intelligently. + ;; The result isn't very intelligent (causes redundant characters), so just do + ;; it ourselves. + (map! :map c++-mode-map "<" nil - :map (c-mode-base-map c++-mode-map) - :i ">" #'+cc/autoclose->-maybe)) + :i ">" #'+cc/autoclose->-maybe) + + ;; ...and leave it to smartparens + (sp-with-modes '(c-mode c++-mode objc-mode java-mode) + (sp-local-pair "<" ">" :when '(+cc-sp-point-is-template-p +cc-sp-point-after-include-p)) + (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))) + ;; Doxygen blocks + (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET") ("||\n[i]" "SPC"))) + (sp-local-pair "/*!" "*/" :post-handlers '(("||\n[i]" "RET") ("[d-1]< | " "SPC"))))) (def-package! modern-cpp-font-lock @@ -114,28 +91,29 @@ (setq irony-server-install-prefix (concat doom-etc-dir "irony-server/")) :init (defun +cc|init-irony-mode () + ;; The major-mode check is necessary because some modes derive themselves + ;; from a c base mode, like java-mode or php-mode. (when (and (memq major-mode '(c-mode c++-mode objc-mode)) (file-directory-p irony-server-install-prefix)) (irony-mode +1))) (add-hook 'c-mode-common-hook #'+cc|init-irony-mode) :config - (add-hook! 'irony-mode-hook #'(irony-eldoc flycheck-mode)) + (make-variable-buffer-local 'irony-additional-clang-options) + ;; Add nearest include/ path to compiler options + (add-hook! '(c-mode-hook c++-mode-hook) #'+cc|irony-add-include-paths) + ;; Use C++11/14 by default + (add-hook 'c++-mode-hook #'+cc|use-c++11)) - (defun +cc|init-c++11-clang-options () - (make-local-variable 'irony-additional-clang-options) - (cl-pushnew "-std=c++11" irony-additional-clang-options :test 'equal)) - (add-hook 'c++-mode-hook #'+cc|init-c++11-clang-options) - - (map! :map irony-mode-map - [remap completion-at-point] #'counsel-irony - [remap complete-symbol] #'counsel-irony)) - -(def-package! irony-eldoc :after irony) +(def-package! irony-eldoc + :after irony + :config (add-hook 'irony-mode-hook #'irony-eldoc)) (def-package! flycheck-irony :when (featurep! :feature syntax-checker) :after irony - :config (flycheck-irony-setup)) + :config + (add-hook 'irony-mode-hook #'flycheck-mode) + (flycheck-irony-setup)) ;;