Vibe coded by Codex with gpt-5.6-sol. Read disclamer section. Use at your own risc.
Attach arbitrary files to Neovim buffers.
file-clip.nvim complements
img-clip.nvim. While
img-clip.nvim provides a much richer, image-focused workflow, this plugin has
the narrower goal of copying and linking arbitrary existing files. It copies a
source file into a project asset directory, creates missing directories, and
inserts the appropriate Markdown or HTML link.
- Attach an explicit file path or a textual path from the system clipboard.
- Accept absolute paths and
file://URIs. - Preserve the original file name and extension.
- Create nested asset directories automatically.
- Render image-aware Markdown and HTML templates.
- Keep filesystem destinations separate from public URL paths.
- Override settings per project with
.file-clip.lua.
- Neovim 0.10 or newer
- A working Neovim clipboard provider for
:PasteFile
With lazy.nvim:
{
"lpanebr/file-clip.nvim",
opts = {},
}:AttachFile /tmp/diagram.pdf
:PasteFile:AttachFile prompts for the source path when called without an argument.
:PasteFile reads a path or file:// URI from the + clipboard register.
The same operation is available through Lua:
require("file-clip").attach_file(opts, source)
require("file-clip").paste_file(opts)By default, files are copied to assets under Neovim's working directory and
keep their original name and extension. Inserted links are made relative to the
current buffer when needed.
require("file-clip").setup({
default = {
dir_path = "assets",
url_path = nil,
use_absolute_path = false,
relative_to_current_file = false,
relative_template_path = true,
prompt_for_file_name = false,
overwrite = false,
confirm_on_overwrite = true,
},
})Set prompt_for_file_name = true to rename an attachment before copying it. The
prompt changes only the base name; the original extension is retained.
The nearest .file-clip.lua in the current file's directory or one of its
ancestors overrides the global configuration. The file must return a setup
table.
Filesystem destinations and public URLs can be configured separately. For an
Astro project with content in src/content/sinapses and public assets in
public/sinapses:
local slug = vim.fn.expand("%:t:r")
return {
default = {
dir_path = "public/sinapses/" .. slug,
url_path = "/sinapses/" .. slug,
},
}This copies diagram.pdf to public/sinapses/<slug>/diagram.pdf while inserting
[](/sinapses/<slug>/diagram.pdf) into Markdown and placing the cursor inside
the brackets.
Markdown uses image syntax for png, jpg, jpeg, gif, webp, svg, and
avif; other extensions use regular links. HTML uses <img> and <a> in the
same way.
Templates support $FILE_NAME, $FILE_NAME_NO_EXT, $FILE_PATH, $LABEL,
$CURSOR, and $EXTENSION. Per-extension templates can be supplied with
templates_by_extension.
Run the headless test suite with:
make testThe initial specification and v1 acceptance criteria are documented in
file-clip-spec.md.
Contributions are welcome through issues and pull requests. Please keep changes focused, explain the behavior being changed, and add or update tests when appropriate. Before submitting a pull request, run:
make test
stylua --check lua plugin testsThis plugin was created using Codex with the gpt-5.6-sol model for the
author's personal use. The author has performed functional tests but has not
reviewed the generated code in detail. The plugin is provided as-is, without
warranty; review the code and assess the risks before using it in your own
environment.