Completion

This file configures company-mode, which provides tab-completion for most of my programming environments and REPLs.

(use-package company
  :ensure t
  :bind
  <<do.completion/keys>>
  :init
  (global-company-mode)
  :config
  <<do.completion/settings>>
  <<do.completion/dabbrev>>
  <<do.completion/backends>>
  <<do.completion/tabkey>>
  <<do.completion/theme>>)

General Settings

(setq company-tooltip-align-annotations t)
(setq company-selection-wrap-around t)
(setq company-tooltip-flip-when-above t)
(setq company-idle-delay 0.0)

company-dabbrev

TODO: move the references to company-dabbrev-code-modes to their respective programming environment layers.

Some settings to fix capitalization in company-dabbrev-code.

(require 'company-dabbrev)
(require 'company-dabbrev-code)
(setq company-dabbrev-code-everywhere t)
(setq company-dabbrev-code-ignore-case nil)
(setq company-dabbrev-ignore-case nil)

Backends

AUCTeX

(use-package company-auctex
  :ensure t)

Math symbols

(use-package company-math
  :ensure t)

TODO Backend list

TODO: check settings.org and add the elpy section to elpy configuration.

(defvar do.completion/backend-list
  '(company-elisp
    company-nxml
    company-css
    company-cmake
    company-capf
    company-files
    company-keywords
    company-math-symbols-unicode
    company-ispell
    company-yasnippet)
  "A list of my default company backends")

;; set the correct variable
(setq company-backends do.completion/backend-list)

Completion on <tab>

The following functions define the global behavior of the <tab> key. Modified from here. We want to

  1. Check if in minibuffer, if yes, use minibuffer-complete.
  2. Check if there are snippets, if yes, execute the snippet
  3. Check for company-completions, if yes, enter company.
  4. If there are no snippets or completions, indent the line.
(defun do.completion/check-expansion ()
  (save-excursion
    (if (looking-at "\\_>") t
      (backward-char 1)
      (if (looking-at "\\.") t
        (backward-char 1)
        (if (looking-at "->") t nil)))))

(defun do.completion/do-yas-expand ()
  (let ((yas-fallback-behavior 'return-nil))
    (yas-expand)))

(defun do.completion/tab-indent-or-complete ()
  (interactive)
  (if (minibufferp)
      (minibuffer-complete)
    (if (or (not yas-minor-mode) ;; xxx change this to point to right var
            (null (when (looking-at "\\_>") (do.completion/do-yas-expand))))
        (if (do.completion/check-expansion)
            (company-complete-common)
          (indent-for-tab-command)))))

Keybindings

We want to enable company when tab is pressed and cycle through suggestions on subsequent pressings of tab.

(:map company-active-map
      ("<tab>" . company-complete-common-or-cycle)
      :map prog-mode-map
      ("<tab>" . do.completion/tab-indent-or-complete))

Theme

This section ensures that our completion popup aligns with the chosen color theme

(do.theme/set-face-attr-from-list
 'company-tooltip
 do.theme.popup/popup-face-attrs)

(do.theme/set-face-attr-from-list
 'company-tooltip-selection
 do.theme.popup/selected-face-attrs)

(do.theme/set-face-attr-from-list
 'company-tooltip-common
 do.theme.popup/summary-face-attrs)

(do.theme/set-face-attr-from-list
 'company-tooltip-common-selection
 do.theme.popup/summary-selected-face-attrs)

(do.theme/set-face-attr-from-list
 'company-tooltip-annotation
 do.theme.popup/summary-face-attrs)

(do.theme/set-face-attr-from-list
 'company-tooltip-annotation-selection
 do.theme.popup/summary-selected-face-attrs)

(do.theme/set-face-attr-from-list
 'company-scrollbar-fg
 do.theme.popup/scrollbar-fg-attrs)

(do.theme/set-face-attr-from-list
 'company-scrollbar-bg
 do.theme.popup/scrollbar-bg-attrs)

(do.theme/set-face-attr-from-list
 'company-preview-common
 do.theme.popup/preview-attrs)

(do.theme/set-face-attr-from-list
 'company-preview
 do.theme.popup/preview-attrs)

(do.theme/set-face-attr-from-list
 'company-preview-search
 do.theme.popup/preview-search-attrs)

company-posframe

(use-package company-posframe
  :ensure t
  :diminish company-posframe-mode
  :custom
  (company-posframe-show-indicator nil)
  (company-posframe-show-metadata nil)
  :hook
  (company-mode . company-posframe-mode))

company-posframe has been acting up lately, an alternative is company-box:

(use-package company-box
  :ensure t
  :diminish company-box-mode
  :hook
  (company-mode . company-box-mode))