2026-08-21
- Support neovim OSC 8 hyperlinks. Hold Command on macOS or Ctrl on Linux/Windows to open the link. In mouse-capturing TUI applications, also hold Shift.
2026-08-19
- Add a provider-neutral Next Edit API with versioned candidates, cancellation,
preview, navigation, acceptance, Vim/Neovim actions, and extension-owned
handleDidShowNextEditcallbacks.
2026-08-15
- Upgrade extension loader:
- No longer modify Module.prototype._compile
- Plugins use an enhanced console, including consoles imported via require
- Support loading ESM-format plugins; requires the node flag --experimental-vm-modules at startup, added by default
- Plugins use an encapsulated standalone runtime API, and extensionId is included when registering callback functions for easy tracking
- Extension loader and
loadExtensionacceptsourceCodewith anextensionRootto load a plugin from its complete CommonJS bundle (generated by coc-test) instead of resolving the entry through the package.jsonmainfield.
2026-08-14
- Break Change: minimal node version changed from 20.19.0 to 22.15.0.
2026-08-15
- Add a per-extension coc.nvim API facade: every extension runtime receives
its own frozen top-level API object. Mutable core singletons (workspace,
window, commands, languages, sources, services, extensions,
diagnosticManager, listManager, snippetManager, events, mcp) are wrapped so
extensions never receive raw manager objects; immutable value exports are
shared directly and
nvimstays a live getter. Mutating one extension's facade cannot affect another extension or coc core. - Attribute extension callback errors to the owning plugin: command handlers,
event listeners and language providers registered through the facade are
tagged with the extension id, so command errors are prefixed with
[extension: <id>], event handler errors and slow-handler warnings include the extension id, and provider__extensionNamenow comes from the registration owner instead of parsing stack traces. Extension-owned registrations are disposed with the runtime. - Add ESM extension support to the per-extension VM loader: ESM entries and
imports execute through
vm.SourceTextModuleinside the owning extension context, andcoc.nvim, Node builtins, CommonJS modules and JSON are bridged withvm.SyntheticModule. CJSrequire()of ESM is rejected withERR_REQUIRE_ESM, CJS dynamicimport()of ESM works, and ESM reload creates a fresh context, caches and namespace. coc.nvim now starts Node with--experimental-vm-modulesautomatically. - Package
exportsresolution prefers acoc.nvimcondition overrequireandimport(Node matches exports keys in object order, so the coc-specific build wins only when checked first); nested conditions prefernodeandrequire. - Migrate extension loading to a per-extension VM loader: every extension now
owns its own
vm.Contextand module cache, CommonJS modules execute throughvm.compileFunctionwith an extension-local synchronousrequire, and coc.nvim no longer patchesModule.prototype._compile, wraps module sources withModule.wrap, or executes extension code withvm.runInContext(). - Extension reload creates a fresh context and module cache: old global state disappears and entry/dependencies re-execute, while other extensions are untouched. Failed modules are removed from the cache and module graph and are re-executed on the next load.
require("coc.nvim")resolves to a stable per-extension API object;require("process")andrequire("node:process")return the sandbox process facade (the same object as the globalprocess) with exit/kill/etc. stubbed, and mutablerequire.cache/require.extensionsare no longer exposed to extensions.- Each extension now owns an independent enhanced
console: timers (time/timeLog/timeEnd), counters (count/countReset), groups,dir,table,trace,assert, andclearare isolated per extension and routed through the extension logger.require("console")andrequire("node:console")return a per-extension facade whose top-level methods route to the extension console while the nativeConsoleclass is preserved. Reload discards console state and identity; the process-global console is never modified.
2026-08-13
- Migrate the test suite from Vitest to the Node native test runner
(
node:test) on Node 24. Tests no longer import Vitest globals; native test globals are injected per file, unit tests live undersrc/__tests__/unitwith shared test utilities, and ESM compatibility shims stay local to the test bundle. - Rewrite the test runner as
scripts/test/cli.mjs: unit tests are compiled with esbuild and run from their real.test.tssource paths, while editor tests run in per-file nvim/Neovim worker processes. The runner overlaps lanes, caches bundles and compilation, supports-tfilters under isolation mode, and kills hung editor workers after a graceful timeout. - Run client integration tests against in-process fake servers instead of forked child processes, removing several child-process spawns and LSP handshakes (error-handler, initialize-failure, dynamic and pure RPC client tests).
- Report test coverage from raw V8 files, including type-only coverage, and run coverage only on the stable Neovim job in CI; editor test concurrency is bounded locally and pinned on CI to keep runs deterministic.
- Stabilize flaky editor tests: wait for echoed messages through a new
Messageevent instead of screen scraping, attach the document before completion navigation, guard stale linked-editing provider results, and stabilize runner prompts plus completion/workspace CI tests. - Add the
Messageevent toevents.on(), fired whenNotificationsechoes a message, so tests and extensions can observe echoed output reliably. - Fix MCP glob variants to resolve symlink tails for directory globs such as
link/**, so a glob through a symlink also matches the target path.
2026-08-10
- Add
workspace.registerInsertKeymap()for dynamic insert mappings that return ordered literal text and special keys directly, with current Vim state supplied through evaluatedarglistexpressions.
2026-08-07
- Add
languageserver.<name>.languageIdMapconfiguration which maps a filename (or absolute path) to the languageId sent intextDocument/didOpenfor that server, e.g.{"application.yml": "spring-boot-properties-yaml"}. This allows a server to receive a languageId different from the buffer filetype without affecting other servers.
2026-08-05
- MCP: the
coc-mcpbridge connects to the first coc.nvim instance whose workspace contains the bridge working directory and exits when no matching instance is found.--match-firstconnects to the first available instance regardless of the working directory. - MCP: the
coc-mcpbridge fails immediately with a "coc.nvim MCP service not found" error when no usable connection exists at startup. Start vim/nvim with"mcp.autoStart": truebefore launching Codex. - MCP: the socket server keeps running across
:CocRestart— its started state is kept in a vim variable and restored on startup, the per-instance discovery file is keyed by the vim pid, and thecoc-mcpbridge reconnects to the new endpoint/token when the file is rewritten. - MCP:
mcp.autoStartcontrols whether the socket server starts automatically with coc.nvim (default off);:CocCommand mcp.startstarts it on demand for the current session regardless of the setting. - MCP: the
coc-mcpbridge reads the private key fromCOC_MCP_AUTH_KEY_FILE(path to a PEM file). - MCP: new
editor/statetool returns a snapshot of the active editor — workspace root, active document (uri, language, version), cursor, visual selection, visible line range, surrounding lines, innermost document symbol under the cursor and current diagnostics. - MCP: cache idempotent LSP queries (
lsp/hover,lsp/definition,lsp/referencesand the other read-only batch queries) keyed by document uri, position, method and document version with a short TTL, and invalidate the cache when the document changes, cutting latency for repeated agent queries on the same symbol. - MCP: LSP queries abandoned by a tool timeout or
notifications/cancelledare dropped from the per-server request queue, and requests the language server never answered are tracked as stuck; once all request slots are stuck, new queries to that server fail fast with a restart hint. The same bound applies withmcp.maxConcurrentRequests: 0(unlimited concurrency, 16 stuck requests per server). - MCP:
workspace/apply_editsaves all modified buffers with:waafter applying, so edits are on disk for subsequent tools; the result reportssavedand asaveErrorwhen the save fails. - MCP: new
mcp.allowedToolswhitelist controls which tools are exposed to agents —tools/listonly returns whitelisted names andtools/callrejects the rest. Default is empty (no tools exposed) so tool access is opt-in; the configuration documents the full built-in tool list. - MCP: the
mcp.logLevelconfiguration is not supported. - MCP: protocol version negotiation supports
2024-11-05in addition to2025-06-18and2025-11-25: sessions on2024-11-05gettools/listentries limited toname/description/inputSchemaandtools/callresults withoutstructuredContent, matching the2024-11-05schema.
2026-08-04
- Add configuration
suggest.pumAlignto align the popup menu with a field ("abbr","menu","kind"or"shortcut") instead of the first text. - Advertise LSP 3.18 client capabilities for code lens and signature help:
textDocument.codeLens.resolveSupportnow reports the properties the client can resolve lazily (the code lenscommand).textDocument.signatureHelp.noActiveParameterSupportnow reports thatactiveParametercan benullto indicate no active parameter; the signature float no longer highlights the first parameter in that case.
- Code lens action picker and code action menus now show
Command#tooltipas atitle - tooltipsuffix when the server provides one. - Add multi-range formatting API for LSP 3.18
textDocument/rangesFormatting:DocumentRangeFormattingEditProvidergains an optionalprovideDocumentRangesFormattingEditsmethod andlanguagesexposesprovideDocumentRangesFormattingEdits, falling back to per-range formatting when the provider has no ranges support. The client capabilitytextDocument.rangeFormatting.rangesSupportis advertised to servers. Existing visual block selection formatting behavior is unchanged. - Support LSP 3.18
CompletionList.applyKind: the client now advertisescompletionList.applyKindSupportanddatainitemDefaults, and honorsapplyKindmerge/replace rules forcommitCharacters(union) anddata(shallow merge, falling back to the default value when the item has none). - Add a built-in MCP (Model Context Protocol) server so agents like Codex can
interact with the running editor through tools, notifications and resources:
:CocCommand mcp.startstarts a loopback socket server (TCP or Unix) tools to read editor buffers (including unsaved changes), search the workspace, apply workspace edits, and query the language servers;bin/coc-mcp.js(commandcoc-mcp) is a stdio bridge for Codex.- New public API
mcp.registerTool()lets extensions register custom MCP tools. - New configuration section
mcp.*(enabled,host,port,transport,authRequired,authClientPublicKey,allowedPaths,deniedPaths,maxClients,frameMaxBytes,timeout,readTimeout,idleTimeout,maxRequestsPerSecond,maxConcurrentRequests,languageServiceMap) and commands:CocCommand mcp.start,mcp.stopandmcp.status. - Protocol: MCP
2025-06-18with2025-11-25version negotiation,coc/*notifications (document saved/changed, diagnostics, workspace folders, editor state, service state) viacoc/subscribe, andcoc://resources. Interface specification and roadmap live in the MCP design document, usage guide indoc/coc-mcp.txt. :CocInfoincludes an## MCP serversection (transport, address, pid, cwd and connected clients with pid and connect/last-activity time); the MCP service closes its discovery/socket files on SIGTERM/SIGINT and on Vim 8VimLeavePre, and clients (the stdio bridge) exit cleanly when the service shuts down.- MCP discovery is unified under
~/.coc/mcp: every coc.nvim instance writescoc-<pid>.json(and its unix socket) there, stale files are cleaned by the stdio bridge on every scan, and the bridge reads the same directory without any environment variable (COC_MCP_DIRis an optional override). - LSP list tools cap their results via an optional
maxResultsargument (defaults: 200 for location tools, 500 for symbol tools, 100 for diagnostics and code actions; hard maximum 1000). Truncated responses addreturnedandtruncatedto the structured output next to the totalcount, and location tools truncate before enrichinglineText.
Snapshot refreshed