Keep Copilot in harmony with company-mode in Emacs

I use company-mode and Copilot (package copilot.el) for auto completion in Emacs. By default, copilot.el and company can show up at the same time. Because Copilot's suggestions can be long, company's popup is all over the places. And I don't like this behavior. So we need to adjust things to make Copilot and company-mode be nice to each other.

Wanted behavior:

  • Copilot and company-mode are still enabled.
  • If Copilot shows up, cancel company popup, except when company popup is manually started.
  • When Copilot popup is on, I can use:

    • M-t to complete a word.
    • M-T to complete a whole line.
    • Tab to accept current suggestion as a whole.
    • M-n and M-p to see next / previous suggestion by Copilot.
    • Escape to cancel.
    • M-c to switch to auto completion using company-mode.

Others:

  • If you use evil-mode, you might want to remove line 49 and 50.
  • I use GhostText with AtomicChrome to do LeetCode on Emacs too. And I don't want Copilot to be enabled in that case, so there is a mechanism in place.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
(use-package copilot
  :vc (:fetcher github :repo "copilot-emacs/copilot.el")
  :bind (:map copilot-completion-map
              ("<escape>" . ngoc/copilot-clear-no-notify)
              ; don't bind "TAB" so i can use C-i when i need to adjust indent
              ("<tab>"    . copilot-accept-completion)
              ("M-t"      . copilot-accept-completion-by-word)
              ("M-T"      . copilot-accept-completion-by-line)
              ("M-n"      . copilot-next-completion)
              ("M-p"      . copilot-previous-completion)
              ("M-c"      . ngoc/abort-copilot-start-company))

  :hook
  (prog-mode . copilot-mode)

  :config
  (setq copilot-install-dir (expand-file-name
                             (locate-user-emacs-file (file-name-concat "cache"
                                                                       "copilot"))))

  (defun ngoc/copilot-clear-no-notify ()
    (interactive)
    (copilot-clear-overlay t))

  (defun ngoc/god-mode-not-enabled ()
    (not (bound-and-true-p god-local-mode)))

  (defun ngoc/not-in-leetcode ()
    (not (and (bound-and-true-p atomic-chrome-edit-mode)
              (string-match-p " - LeetCode$" (buffer-name)))))

  (defun ngoc/company-not-manually-started ()
    (if (company-explicit-action-p)
        nil
      (company-abort)
      t))

  (defun ngoc/abort-company-when-copilot-overlay-visible (manually-started)
    (when (and (copilot--overlay-visible)
               (not manually-started))
      (company-abort)))

  (defun ngoc/abort-copilot-start-company ()
    (interactive)
    ; pass t to clear overlay so that it doesn't notify server about rejection
    (copilot-clear-overlay t)
    (company-manual-begin))

  (remove-hook  'copilot-enable-predicates          'evil-insert-state-p) ; i don't use evil mode
  (add-hook     'copilot-enable-predicates          'ngoc/god-mode-not-enabled)
  (add-hook     'copilot-enable-predicates          'ngoc/not-in-leetcode)
  (add-hook     'copilot-enable-display-predicates  'ngoc/company-not-manually-started)
  (add-hook     'company-completion-started-hook    'ngoc/abort-company-when-copilot-overlay-visible)

  ;; disable not useful warnings
  (add-to-list 'warning-suppress-types '(copilot copilot-exceeds-max-char))
  (setq copilot-indent-offset-warning-disable t)

  )

Last modified on 2024-05-26