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+ and the following packages (installed automatically from MELPA):
editorconfigjsonrpccompattrack-changes
@github/copilot-language-server requires Node.js 22+.
(use-package copilot
:ensure t
: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)))Then run M-x copilot-install-server and M-x copilot-login. That's it!
The simplest way to install copilot.el is from MELPA:
(use-package copilot
:ensure t)Or M-x package-install RET copilot RET.
(use-package copilot
:vc (:url "https://github.com/copilot-emacs/copilot.el"
:rev :newest
:branch "main"))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")))Clone this repository, make sure the dependencies listed in Requirements are installed, then:
(add-to-list 'load-path "/path/to/copilot.el")
(require 'copilot)Details
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)))Details
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.
After installing the package, run M-x copilot-install-server to install the language server, then M-x copilot-login to authenticate. You can verify everything works with M-x copilot-diagnose (NotAuthorized means you don't have a valid subscription).
Use copilot-mode to automatically provide completions in a buffer:
(add-hook 'prog-mode-hook 'copilot-mode)Or enable it globally with global-copilot-mode:
(global-copilot-mode)To customize when completions trigger, see copilot-enable-predicates and copilot-disable-predicates. To customize when completions are displayed, see copilot-enable-display-predicates and copilot-disable-display-predicates.
Alternatively, you can call copilot-complete manually and use copilot-clear-overlay in post-command-hook to dismiss completions.
copilot-mode does not set any keybindings by default. Use copilot-completion-map (active while a completion overlay is visible) to bind keys:
(keymap-set copilot-completion-map "<tab>" #'copilot-accept-completion)
(keymap-set copilot-completion-map "TAB" #'copilot-accept-completion)
(keymap-set copilot-completion-map "C-<tab>" #'copilot-accept-completion-by-word)
(keymap-set copilot-completion-map "C-TAB" #'copilot-accept-completion-by-word)
(keymap-set copilot-completion-map "M-n" #'copilot-next-completion)
(keymap-set copilot-completion-map "M-p" #'copilot-previous-completion)If you use company-mode or corfu, TAB is already taken. An alternative inspired by Fish shell avoids the conflict entirely — right-arrow accepts, and forward-word/end-of-line accept partially:
(keymap-set copilot-completion-map "<right>" #'copilot-accept-completion)
(keymap-set copilot-completion-map "C-f" #'copilot-accept-completion)
(keymap-set copilot-completion-map "M-<right>" #'copilot-accept-completion-by-word)
(keymap-set copilot-completion-map "M-f" #'copilot-accept-completion-by-word)
(keymap-set copilot-completion-map "C-e" #'copilot-accept-completion-by-line)
(keymap-set copilot-completion-map "<end>" #'copilot-accept-completion-by-line)
(keymap-set copilot-completion-map "M-n" #'copilot-next-completion)
(keymap-set copilot-completion-map "M-p" #'copilot-previous-completion)To remap the built-in zap commands automatically whenever the overlay is visible:
(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 configure the underlying LSP settings via 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. The resulting languageId should match the 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"))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))copilot-on-request registers a handler for incoming LSP requests. Return a JSON-serializable value as the result, or call jsonrpc-error for errors. Read more.
;; 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))))copilot-on-notification registers a listener for incoming LSP notifications.
(copilot-on-notification
'window/logMessage
(lambda (msg) (message (plist-get msg :message))))Tip
You don't need to memorize the list — just type M-x copilot- followed by TAB, or use the Copilot menu in the menubar.
| Command | Description |
|---|---|
| Setup | |
copilot-install-server |
Install the language server |
copilot-uninstall-server |
Remove the installed language server |
copilot-reinstall-server |
Re-install the language server |
copilot-login |
Log in to GitHub Copilot |
copilot-logout |
Log out from GitHub Copilot |
copilot-diagnose |
Restart the server and show diagnostic info |
copilot-select-completion-model |
Choose which model to use for completions |
| Completion | |
copilot-mode |
Toggle automatic completions in the current buffer |
copilot-complete |
Trigger a completion at point |
copilot-panel-complete |
Show a panel buffer with multiple completion suggestions |
copilot-accept-completion |
Accept the current completion |
copilot-accept-completion-by-word |
Accept the next N words (prefix arg) |
copilot-accept-completion-by-line |
Accept the next N lines (prefix arg) |
copilot-accept-completion-by-sentence |
Accept the next N sentences (prefix arg) |
copilot-accept-completion-by-paragraph |
Accept the next N paragraphs (prefix arg) |
copilot-accept-completion-to-char |
Accept through a character (inclusive, like zap-to-char) |
copilot-accept-completion-up-to-char |
Accept up to a character (exclusive, like zap-up-to-char) |
copilot-clear-overlay |
Dismiss the completion overlay |
| Navigation | |
copilot-next-completion |
Cycle to the next completion |
copilot-previous-completion |
Cycle to the previous completion |
Tip
Use M-x customize-group RET copilot to browse all available
configuration options and their documentation.
A few commonly tweaked variables:
copilot-idle-delay— Seconds to wait before triggering completion (default0). Set tonilto disable automatic completion entirely.copilot-lsp-server-version— Pin a specific @github/copilot-language-server version (nilmeans latest).copilot-enable-predicates/copilot-disable-predicates— Control whencopilot-moderequests completions.copilot-enable-display-predicates/copilot-disable-display-predicates— Control when completions are shown.copilot-clear-overlay-ignore-commands— Commands that won't dismiss the overlay.copilot-indentation-alist— Override indentation width per major mode.
copilot.el communicates with @github/copilot-language-server over JSON-RPC using the LSP protocol plus Copilot-specific extensions. The table below shows the current coverage.
| Method | Status | Notes |
|---|---|---|
initialize |
Supported | Sends rootUri, workspaceFolders, and editor info |
shutdown |
Supported | Clean shutdown before exit |
textDocument/inlineCompletion |
Supported | Core completion mechanism |
workspace/executeCommand |
Supported | Executes server-side commands on accept |
signInInitiate / signInConfirm / checkStatus / signOut |
Supported | Authentication flow |
copilot/models |
Supported | Lists available completion models |
getPanelCompletions |
Supported | Multiple suggestions in a panel buffer |
| Method | Status | Notes |
|---|---|---|
initialized |
Supported | |
exit |
Supported | Sent after shutdown |
textDocument/didOpen |
Supported | |
textDocument/didClose |
Supported | |
textDocument/didChange |
Supported | Incremental sync |
textDocument/didFocus |
Supported | |
textDocument/didShowCompletion |
Supported | Telemetry when overlay is displayed |
textDocument/didPartiallyAcceptCompletion |
Supported | Telemetry for partial acceptance |
workspace/didChangeConfiguration |
Supported | Sent on settings change |
workspace/didChangeWorkspaceFolders |
Supported | Dynamic workspace roots |
$/cancelRequest |
Supported | Cancels stale completion requests |
textDocument/didSave |
Not yet | |
notebookDocument/* |
Not yet |
| Method | Status | Notes |
|---|---|---|
window/showMessageRequest |
Supported | Prompts via completing-read |
window/showDocument |
Supported | Opens URIs in browser or Emacs |
| Method | Status | Notes |
|---|---|---|
window/logMessage |
Supported | Logged to *copilot-language-server-log* |
didChangeStatus |
Supported | Shown in mode-line |
$/progress |
Supported | Progress shown in mode-line |
PanelSolution / PanelSolutionsDone |
Supported | Panel completion results |
Extensible via copilot-on-request and copilot-on-notification for any messages not handled above.
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).
Unit tests (requires eask):
eask test buttercupeask lint checkdoc
eask lint indentThere's a manual integration test in dev/integration-smoke.el that connects to the real Copilot language server and verifies the textDocument/inlineCompletion round-trip. It requires the server to be installed and authenticated.
emacs --batch -L . -l dev/integration-smoke.elThese 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-2026 copilot-emacs maintainers and contributors.