nvim-cmp source for Solidity imports in Foundry projects.
Import path completion through forge's remappings, segment by segment:
import "▊ → @openzeppelin, forge-std, src, lib, …
import "@openzeppelin/▊ → contracts
import "@openzeppelin/contracts/token/▊ → ERC20, ERC721, ERC777, ERC1155
import "src/▊ → your contracts
Symbol auto-import — type import { and pick any contract, interface
or library declared in your project or its dependencies; accepting the
entry completes the whole statement with the remapped path:
import {ERC72▊ ⇒ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";- Remappings come from
forge remappings(so the ones forge infers fromlib/are included), with aremappings.txtfallback whenforgeisn't on$PATH.src/andlib/are offered as root-relative imports too. - Everything runs asynchronously and is cached per project root; caches refresh in the background, so a new remapping or contract shows up on the next completion without ever blocking a keystroke.
- Plays nice with autopairs: if your pairs plugin already inserted the
closing
}, the completed} from "...";replaces it instead of duplicating it. - Selecting an entry shows the full import statement in cmp's documentation window.
- Neovim ≥ 0.10 (
vim.system,vim.fs.root) - Foundry's
forgeon$PATH(optional but recommended; falls back to parsingremappings.txt) - ripgrep (optional; falls back to
grepfor the symbol index)
{
"hrsh7th/nvim-cmp",
dependencies = {
"valenyala/cmp-forge-remappings",
},
}use { "valenyala/cmp-forge-remappings", after = "nvim-cmp" }Then add the source to your cmp setup:
require("cmp").setup({
sources = {
{ name = "forge_remappings" },
-- your other sources …
},
})Options are passed per source, the nvim-cmp way. Defaults shown:
sources = {
{
name = "forge_remappings",
option = {
-- Project dirs offered as root-relative imports (only when they
-- exist in the project root).
project_dirs = { "src/", "lib/" },
-- Set to false to disable symbol completion inside `import {`.
symbols = true,
},
},
}The source only activates in solidity buffers that live under a project
root containing foundry.toml or remappings.txt, and only on import
lines — it stays silent everywhere else.
- The symbol index greps for declarations at the start of a line, which is
what
forge fmt(and every sane formatter) produces. Free-standing top-levelstruct/enum/errordefinitions are not indexed. - Files under a remapped target are offered under their remapped path
(
@openzeppelin/...), never the rawlib/...path; anything unmapped falls back to a root-relative path, which foundry also resolves.
MIT