GitHub Copilot ghost-text suggestions via the official
@github/copilot-language-server JSON-RPC server.
The plugin spawns the language server as a child process. Either:
npm install -g @github/copilot-language-server— in which case the copilot-language-server shim ends up on $PATH
and the plugin uses it automatically — or install it locally inside
the project (./node_modules/@github/copilot-language-server/), which
is also auto-detected. To override, set HED_COPILOT_LSP to either a
binary or a .js file path.
:copilot login -> prints a userCode and a verification URL
:copilot login <userCode> -> confirm after entering the code in the browser
:copilot status -> show signed-in user and enable state
:copilot logout
GitHub stores the OAuth token in ~/.config/github-copilot/; future
sessions reconnect without re-logging-in.
- Once signed in, every char insert in INSERT mode at end-of-line
schedules a debounced (250 ms)
getCompletionsrequest. - The first returned completion renders as ghost text on the current
row using the
copilotvirtual-text namespace. <Tab>in INSERT mode accepts the suggestion. With no live suggestion, it falls back to inserting a literal tab (or spaces, ifexpand_tab).- Cursor move off the suggestion's anchor row, or leaving INSERT
mode, dismisses it (and notifies the server with
notifyRejected). - Suggestions only render at end-of-line in v1; mid-line ghost text needs the inline placement form of virtual_text (currently EOL-only).
:copilot pane -> open a horizontal split listing all alternatives
:copilot next -> show the next alternative as the active ghost
:copilot prev -> show the previous alternative
The [copilot] buffer in the bottom pane refreshes on every new
response. The active alternative is marked with * at the start of
its line.
:copilot enable | disable -> toggle ghost-text fetching
:copilot restart -> kill the server and respawn
- The plugin reuses the bundled
cJSONfromplugins/lsp/cjson/. Framing (Content-Length: …LSP-style headers) mirrorsplugins/lsp/lsp_impl.c. - The
copilotvirtual-text namespace opts out of edit-time auto clearing (seevtext_ns_set_auto_clear); the plugin manages its own clears so ghost text survives the keystroke that triggered the request. - Document positions are sent as byte offsets (matching the editor's cursor.x). Strict UTF-16 code-unit math is a v2 concern; ASCII files are the common case.
- v1 sends a fresh
doc.sourcewith everygetCompletionsand atextDocument/didOpenper buffer at sign-in time; full incrementaldidChangeis deferred.