;;; completion/company/config.el -*- lexical-binding: t; -*- (use-package! company :commands (company-complete-common company-complete-common-or-cycle company-manual-begin company-grab-line) :hook (doom-first-input . global-company-mode) :init (setq company-minimum-prefix-length 2 company-tooltip-limit 14 company-tooltip-align-annotations t company-require-match 'never company-global-modes '(not erc-mode circe-mode message-mode help-mode gud-mode vterm-mode) company-frontends '(company-pseudo-tooltip-frontend ; always show candidates in overlay tooltip company-echo-metadata-frontend) ; show selected candidate docs in echo area ;; Buffer-local backends will be computed when loading a major mode, so ;; only specify a global default here. company-backends '(company-capf) ;; These auto-complete the current selection when ;; `company-auto-commit-chars' is typed. This is too magical. We ;; already have the much more explicit RET and TAB. company-auto-commit nil ;; Only search the current buffer for `company-dabbrev' (a backend that ;; suggests text your open buffers). This prevents Company from causing ;; lag once you have a lot of buffers open. company-dabbrev-other-buffers nil ;; Make `company-dabbrev' fully case-sensitive, to improve UX with ;; domain-specific words with particular casing. company-dabbrev-ignore-case nil company-dabbrev-downcase nil) (when (modulep! +tng) (add-hook 'global-company-mode-hook #'company-tng-mode)) :config (when (modulep! :editor evil) (add-hook 'company-mode-hook #'evil-normalize-keymaps) (unless (modulep! +childframe) ;; Don't persist company popups when switching back to normal mode. ;; `company-box' aborts on mode switch so it doesn't need this. (add-hook! 'evil-normal-state-entry-hook (defun +company-abort-h () ;; HACK `company-abort' doesn't no-op if company isn't active; causing ;; unwanted side-effects, like the suppression of messages in the ;; echo-area. ;; REVIEW Revisit this to refactor; shouldn't be necessary! (when company-candidates (company-abort))))) ;; Allow users to switch between backends on the fly. E.g. C-x C-s followed ;; by C-x C-n, will switch from `company-yasnippet' to ;; `company-dabbrev-code'. (defadvice! +company--abort-previous-a (&rest _) :before #'company-begin-backend (company-abort))) (add-hook 'after-change-major-mode-hook #'+company-init-backends-h 'append) ;; NOTE Fix #1335: ensure `company-emulation-alist' is the first item of ;; `emulation-mode-map-alists', thus higher priority than keymaps of ;; evil-mode. We raise the priority of company-mode keymaps ;; unconditionally even when completion is not activated. This should not ;; cause problems, because when completion is activated, the value of ;; `company-emulation-alist' is ((t . company-my-keymap)), when ;; completion is not activated, the value is ((t . nil)). (add-hook! 'evil-local-mode-hook (when (memq 'company-emulation-alist emulation-mode-map-alists) (company-ensure-emulation-alist))) ;; Fix #4355: allow eldoc to trigger after completions. (after! eldoc (eldoc-add-command 'company-complete-selection 'company-complete-common 'company-capf 'company-abort))) ;; ;;; Packages (after! company-files ;; Fix `company-files' completion for org file:* links (add-to-list 'company-files--regexps "file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)")) (use-package! company-box :when (modulep! +childframe) :hook (company-mode . company-box-mode) :config (setq company-box-show-single-candidate t company-box-backends-colors nil company-box-max-candidates 50 company-box-icons-alist 'company-box-icons-all-the-icons ;; Move company-box-icons--elisp to the end, because it has a catch-all ;; clause that ruins icons from other backends in elisp buffers. company-box-icons-functions (cons #'+company-box-icons--elisp-fn (delq 'company-box-icons--elisp company-box-icons-functions)) company-box-icons-all-the-icons (let ((all-the-icons-scale-factor 0.8)) `((Unknown . ,(all-the-icons-material "find_in_page" :face 'all-the-icons-purple)) (Text . ,(all-the-icons-material "text_fields" :face 'all-the-icons-green)) (Method . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) (Function . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) (Constructor . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) (Field . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) (Variable . ,(all-the-icons-material "adjust" :face 'all-the-icons-blue)) (Class . ,(all-the-icons-material "class" :face 'all-the-icons-red)) (Interface . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red)) (Module . ,(all-the-icons-material "view_module" :face 'all-the-icons-red)) (Property . ,(all-the-icons-material "settings" :face 'all-the-icons-red)) (Unit . ,(all-the-icons-material "straighten" :face 'all-the-icons-red)) (Value . ,(all-the-icons-material "filter_1" :face 'all-the-icons-red)) (Enum . ,(all-the-icons-material "plus_one" :face 'all-the-icons-red)) (Keyword . ,(all-the-icons-material "filter_center_focus" :face 'all-the-icons-red)) (Snippet . ,(all-the-icons-material "short_text" :face 'all-the-icons-red)) (Color . ,(all-the-icons-material "color_lens" :face 'all-the-icons-red)) (File . ,(all-the-icons-material "insert_drive_file" :face 'all-the-icons-red)) (Reference . ,(all-the-icons-material "collections_bookmark" :face 'all-the-icons-red)) (Folder . ,(all-the-icons-material "folder" :face 'all-the-icons-red)) (EnumMember . ,(all-the-icons-material "people" :face 'all-the-icons-red)) (Constant . ,(all-the-icons-material "pause_circle_filled" :face 'all-the-icons-red)) (Struct . ,(all-the-icons-material "streetview" :face 'all-the-icons-red)) (Event . ,(all-the-icons-material "event" :face 'all-the-icons-red)) (Operator . ,(all-the-icons-material "control_point" :face 'all-the-icons-red)) (TypeParameter . ,(all-the-icons-material "class" :face 'all-the-icons-red)) (Template . ,(all-the-icons-material "short_text" :face 'all-the-icons-green)) (ElispFunction . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) (ElispVariable . ,(all-the-icons-material "check_circle" :face 'all-the-icons-blue)) (ElispFeature . ,(all-the-icons-material "stars" :face 'all-the-icons-orange)) (ElispFace . ,(all-the-icons-material "format_paint" :face 'all-the-icons-pink))))) ;; HACK Fix oversized scrollbar in some odd cases ;; REVIEW `resize-mode' is deprecated and may stop working in the future. ;; TODO PR me upstream? (setq x-gtk-resize-child-frames 'resize-mode) ;; Disable tab-bar in company-box child frames ;; TODO PR me upstream! (add-to-list 'company-box-frame-parameters '(tab-bar-lines . 0)) ;; Don't show documentation in echo area, because company-box displays its own ;; in a child frame. (delq! 'company-echo-metadata-frontend company-frontends) (defun +company-box-icons--elisp-fn (candidate) (when (derived-mode-p 'emacs-lisp-mode) (let ((sym (intern candidate))) (cond ((fboundp sym) 'ElispFunction) ((boundp sym) 'ElispVariable) ((featurep sym) 'ElispFeature) ((facep sym) 'ElispFace))))) ;; `company-box' performs insufficient frame-live-p checks. Any command that ;; "cleans up the session" will break company-box. ;; TODO Fix this upstream. (defadvice! +company-box-detect-deleted-frame-a (frame) :filter-return #'company-box--get-frame (if (frame-live-p frame) frame)) (defadvice! +company-box-detect-deleted-doc-frame-a (_selection frame) :before #'company-box-doc (and company-box-doc-enable (frame-local-getq company-box-doc-frame frame) (not (frame-live-p (frame-local-getq company-box-doc-frame frame))) (frame-local-setq company-box-doc-frame nil frame)))) (use-package! company-dict :defer t :config (setq company-dict-dir (expand-file-name "dicts" doom-private-dir)) (add-hook! 'doom-project-hook (defun +company-enable-project-dicts-h (mode &rest _) "Enable per-project dictionaries." (if (symbol-value mode) (add-to-list 'company-dict-minor-mode-list mode nil #'eq) (setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list))))))