Copilot.el is an Emacs plugin for GitHub Copilot.
This plugin is unofficial, however it makes use of the official @github/copilot-language-server provided by Microsoft.
Note
You need access to GitHub Copilot to use this plugin. The service introduced a free layer in early 2025.
copilot.el requires Emacs 27+.
@github/copilot-language-server requires Node.js 22+.
-
Setup
copilot.elas described in the next section. -
Install the copilot server by
M-x copilot-install-server. -
Login to Copilot by
M-x copilot-login. You can also check the status by runningM-x copilot-diagnose(NotAuthorizedmeans you don't have a valid subscription). -
Enjoy!
Add package definition to ~/.doom.d/packages.el:
(package! copilot
:recipe (:host github :repo "copilot-emacs/copilot.el" :files ("*.el")))Configure copilot in ~/.doom.d/config.el:
;; accept completion from copilot and fallback to company
(use-package! copilot
:hook (prog-mode . copilot-mode)
:bind (:map copilot-completion-map
("<tab>" . 'copilot-accept-completion)
("TAB" . 'copilot-accept-completion)
("C-TAB" . 'copilot-accept-completion-by-word)
("C-<tab>" . 'copilot-accept-completion-by-word)))Strongly recommend to enable childframe option in company module ((company +childframe)) to prevent overlay conflict.
If pressing tab to complete sometimes doesn't work you might want to bind completion to another key or try:
(after! (evil copilot)
;; Define the custom function that either accepts the completion or does the default behavior
(defun my/copilot-tab-or-default ()
(interactive)
(if (and (bound-and-true-p copilot-mode)
;; Add any other conditions to check for active copilot suggestions if necessary
)
(copilot-accept-completion)
(evil-insert 1))) ; Default action to insert a tab. Adjust as needed.
;; Bind the custom function to <tab> in Evil's insert state
(evil-define-key 'insert 'global (kbd "<tab>") 'my/copilot-tab-or-default))If you would love to configure indentation here, this is an example config that may work for you:
(use-package! copilot
:hook (prog-mode . copilot-mode)
:bind (:map copilot-completion-map
("<tab>" . 'copilot-accept-completion)
("TAB" . 'copilot-accept-completion)
("C-TAB" . 'copilot-accept-completion-by-word)
("C-<tab>" . 'copilot-accept-completion-by-word)
("C-n" . 'copilot-next-completion)
("C-p" . 'copilot-previous-completion))
:config
(add-to-list 'copilot-indentation-alist '(prog-mode 2))
(add-to-list 'copilot-indentation-alist '(org-mode 2))
(add-to-list 'copilot-indentation-alist '(text-mode 2))
(add-to-list 'copilot-indentation-alist '(clojure-mode 2))
(add-to-list 'copilot-indentation-alist '(emacs-lisp-mode 2)))Edit your ~/.spacemacs to include the GitHub Copilot layer this will setup everything for you:
;; ===================
;; dotspacemacs/layers
;; ===================
;; add or uncomment the auto-completion layer
;; add the GitHub Copilot layer
dotspacemacs-configuration-layers
'(
...
auto-completion
github-copilot
...
)For details about the default bindings please refer to the Spacemacs documentation for the github-copilot layer.
straight.el:
(use-package copilot
:straight (:host github :repo "copilot-emacs/copilot.el" :files ("*.el"))
:ensure t)quelpa + quelpa-use-package:
(use-package copilot
:quelpa (copilot :fetcher github
:repo "copilot-emacs/copilot.el"
:branch "main"
:files ("*.el")))(use-package copilot
:vc (:url "https://github.com/copilot-emacs/copilot.el"
:rev :newest
:branch "main"))Use :map, :hook, and :config to customize copilot.el via use-package.
Please make sure you have these dependencies installed (available in ELPA/MELPA):
editorconfigf
After installing those, clone this repository then insert the below snippet into your config file.
(add-to-list 'load-path "/path/to/copilot.el")
(require 'copilot)(add-hook 'prog-mode-hook 'copilot-mode)To customize the behavior of copilot-mode, please check copilot-enable-predicates and copilot-disable-predicates.
You need to bind copilot-complete to some key and call copilot-clear-overlay inside post-command-hook.
Use tab to accept completions (you may also want to bind copilot-accept-completion-by-word to some key):
(define-key copilot-completion-map (kbd "<tab>") 'copilot-accept-completion)
(define-key copilot-completion-map (kbd "TAB") 'copilot-accept-completion)You can configure the underlying LSP settings by changing
copilot-lsp-settings. The complete list of available options can be found
here.
Here we set the GitHub Enterprise server to https://example2.ghe.com, exchange the URL with your own server.
(setopt copilot-lsp-settings '(:github-enterprise (:uri "https://example2.ghe.com"))) ;; allows changing the value without restarting the LSP
(setq copilot-lsp-settings '(:github-enterprise (:uri "https://example2.ghe.com"))) ;; alternativelyYou have to restart the LSP (M-x copilot-diagnose) when using setq to change the value. When logging in, the URL for the authentication flow should be the same as the one set in copilot-lsp-settings.
Copilot.el detects the programming language of a buffer based on the major-mode
name, stripping the -mode part. Resulting languageId should match table
here.
You can add unusual major-mode mappings to copilot-major-mode-alist. Without
the proper language set suggestions may be of poorer quality.
(add-to-list 'copilot-major-mode-alist '("enh-ruby" . "ruby"))In this section you'll find a listing of all the essential interactive
commands provided by copilot.el. It's important to note that by default
copilot-mode doesn't setup any keybindings for its completion commands,
so you'll have to do this yourself - as shown in the "Configurations" section.
Tip
You don't need to memorize the list as you can always do M-x copilot- followed
TAB or refer to the mode's menu in Emacs's menubar.
Check the current status of the plugin. Also you can check logs in the *copilot events* buffer and stderr output in the *copilot stderr* buffer.
Login to GitHub, required for using the plugin.
Enable/disable copilot-mode.
Try to complete at the current point.
Accept the current completion.
Clear copilot overlay in the current buffer.
Similar to copilot-accept-completion, but accept the completion by line or
word. You can use prefix argument to specify the number of lines or words to
accept. Some further variants are included:
copilot-accept-completion-by-sentencecopilot-accept-completion-by-paragraph
Partially accept the completion until a delimiter, similar to Emacs's
zap-up-to-char (exclusive) and zap-to-char (inclusive). To remap
the built-in zap commands automatically whenever the overlay is
visible, use:
(keymap-set copilot-completion-map "<remap> <zap-to-char>" #'copilot-accept-completion-to-char)
(keymap-set copilot-completion-map "<remap> <zap-up-to-char>" #'copilot-accept-completion-up-to-char)You can use prefix arguments. For example, with default bindings,
C-2 M-z , accepts through the second comma.
Cycle through the completion list.
Log out from GitHub.
Tip
Use M-x customize-group RET copilot to see all available
configuration options.
The version of the @github/copilot-language-server to use. If set to nil (default),
the latest version will be installed.
Time in seconds to wait before starting completion (default to 0). Note that
Copilot itself has a ~100ms delay because of network communication. You can
disable it completely by setting it to nil:
(setq copilot-idle-delay nil)A list of predicate functions with no argument to enable/disable triggering
Copilot in copilot-mode.
A list of predicate functions with no argument to enable/disable showing
Copilot's completions in copilot-mode.
A list of commands that won't cause the overlay to be cleared.
Format: '(:host "127.0.0.1" :port 7890 :username: "user" :password: "password"), where :username and :password are optional.
For example:
(setq copilot-network-proxy '(:host "127.0.0.1" :port 7890))Register a handler to be called when a request of type method is
received. Return JSON serializable as result or calling jsonrpc-error for
errors. readmore
For example:
; Display desktop notification if emacs is built with d-bus
(copilot-on-request
'window/showMessageRequest
(lambda (msg) (notifications-notify :title "Emacs Copilot" :body (plist-get msg :message))))Register a listener for copilot notifications.
For example:
(copilot-on-notification
'window/logMessage
(lambda (msg) (message (plist-get msg :message))This is an example of using together with default frontend of
company-mode. Because both company-mode and copilot.el use overlay to show
completion, so the conflict is inevitable. To solve the problem, I recommend
you to use company-box (only available on GUI), which is based on child frame
rather than overlay.
After using company-box, you have:
In other editors (e.g. VS Code, PyCharm), completions from copilot and other sources can not show at the same time.
But I decided to allow them to coexist, allowing you to choose a better one at any time.
If you are using whitespace-mode, make sure to remove newline-mark from whitespace-style.
- Make sure you have restarted your Emacs (and rebuild the plugin if necessary) after updating the plugin.
- Please enable event logging by customize
copilot-log-max(to e.g. 1000) and enable debug log(setq copilot-server-args '("--stdio" "--debug")), then paste related logs in the*copilot events*,*copilot stderr*and*copilot-language-server-log*buffer. - If an exception is thrown, please also paste the stack trace (use
M-x toggle-debug-on-errorto enable stack trace).
These projects helped me a lot:
- https://github.com/TommyX12/company-tabnine/
- https://github.com/cryptobadger/flight-attendant.el
- https://github.com/github/copilot.vim
- @github/copilot-language-server
Just like the copilot plugin for Intellij or VS Code?
Please take a look at copilot-chat.el
Note
It's possible that chat functionality will be added to copilot.el as well down the road. PRs welcome!
Current maintainer(s): @bbatsov, @emil-vdw, @jcs090218, @rakotomandimby.
Retired maintainer: @zerolfx.
copilot.el is distributed under the MIT license.
Copyright © 2022-2025 copilot-emacs maintainers and contributors.


