Thanks to visit codestin.com
Credit goes to github.com

Skip to content

copilot-emacs/copilot.el

Repository files navigation

JCS-ELPA MELPA MELPA Stable

Copilot.el

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.

Requirements

copilot.el requires Emacs 27+ and the following packages (installed automatically from MELPA):

  • editorconfig
  • jsonrpc
  • compat
  • track-changes

@github/copilot-language-server requires Node.js 22+.

Quick Start

(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!

Installation

MELPA

The simplest way to install copilot.el is from MELPA:

(use-package copilot
  :ensure t)

Or M-x package-install RET copilot RET.

Emacs 30+ (use-package :vc)

(use-package copilot
  :vc (:url "https://github.com/copilot-emacs/copilot.el"
            :rev :newest
            :branch "main"))

Emacs 27-29 (straight / quelpa)

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")))

Manual

Clone this repository, make sure the dependencies listed in Requirements are installed, then:

(add-to-list 'load-path "/path/to/copilot.el")
(require 'copilot)

Doom Emacs

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)))

Spacemacs

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).

Configuration

Completion trigger

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.

Keybindings

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)

Fish-style keybindings

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)

Zap-style partial acceptance

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)

LSP settings

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"))) ;; alternatively

You 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.

Language detection

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"))

Network proxy

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))

Server-side hooks (copilot-on-request / copilot-on-notification)

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))))

Commands

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

Customization

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 (default 0). Set to nil to disable automatic completion entirely.
  • copilot-lsp-server-version — Pin a specific @github/copilot-language-server version (nil means latest).
  • copilot-enable-predicates / copilot-disable-predicates — Control when copilot-mode requests 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.

Protocol Support

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.

Client-to-Server Requests

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

Client-to-Server Notifications

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

Server-to-Client Requests

Method Status Notes
window/showMessageRequest Supported Prompts via completing-read
window/showDocument Supported Opens URIs in browser or Emacs

Server-to-Client Notifications

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.

Known Issues

Wrong Position of Other Completion Popups

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.

Cursor Jumps to End of Line When Typing

If you are using whitespace-mode, make sure to remove newline-mark from whitespace-style.

Reporting Bugs

  • 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-error to enable stack trace).

Development

Running Tests

Unit tests (requires eask):

eask test buttercup

Linting

eask lint checkdoc
eask lint indent

Integration Testing

There'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.el

Thanks

These projects helped me a lot:

Do you want to chat with GitHub Copilot?

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!

Team

Current maintainer(s): @bbatsov, @emil-vdw, @jcs090218, @rakotomandimby.

Retired maintainer: @zerolfx.

License

copilot.el is distributed under the MIT license.

Copyright © 2022-2026 copilot-emacs maintainers and contributors.

About

An unofficial Copilot plugin for Emacs.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 68