Note
Credits: this is started a fork from context-yank.nvim as it provided the baseline for clipboard handling and similar features to what I wanted to achieve. The goal of this repository is diverging from context-yank.nvim though. It strives to provide different formatting options for various target systems.
This is essentially a quality of life wrapper around yank operations. It can have many use cases. Some of the ones that I like the most:
- Quickly copy a filepath and line number reference so I can paste them into an agent prompt to reference some code.
- Copy a code block and wrap it in Markdown fences to send via a messaging app to someone.
- Copy a code block and wrap it in Norg fences to paste in my Neorg setup.
Here is a short example copying a file reference including line range to the clipboard, and an example copying a code block into markdown format:
Currently, the following formatters are directly supported by this plugin:
- Markdown,
- AsciiDoc,
- reStructuredText,
- filepath,
- filepath with a line range,
- Jira,
- Confluence Wiki,
- Org Mode,
- LaTex (listings),
- LaTex (minted),
- Norg,
- plain text
See docs/formatters.md for a sample output of the formatters.
I will add more in the future. You you want another one, simply open an issue or a PR.
This plugin is best used when combined with a yank ring such as
yanky.nvim. Additionally, I recommend using
gitlinker.nvim if you want to share links to code
hosted somewhere.
Install via your favourite package manager:
Plug 'f4z3r/yankcraft.nvim'use 'f4z3r/yankcraft.nvim'{
'f4z3r/yankcraft.nvim',
name = 'yankcraft',
opts = {},
},Nix package with home-manager
programs.neovim = {
enable = true;
# ...
plugins = with pkgs.vimPlugins; [
# ...
{
type = "lua";
plugin = yankcraft-nvim;
config = ''require('yankcraft').setup()'';
}
];
};The plugin does not create any keymaps automatically. It essentially exposes a single function:
copy(opts?: YankCraft.Config)You can pass various formatters using the options. These are identical to the configuration used
in the setup function. Typically you might want to provide a formatter. For instance, to get
the filepath with a line range:
local fmtr = require("yankcraft.formatters.filepath_with_line_range")
require("yankcraft").copy({formatter = fmtr})This will result in the following:
`lua/yankcraft/init.lua` (L39-L42)
By default, the configured default formatter from the setup function will be used, which formats
the code block as Markdown:
```lua
function M.content(opts)
opts = vim.tbl_deep_extend("force", vim.deepcopy(config.options), opts or {})
local ctx = context.from_selection()
local text = opts.formatter(ctx)
vim.fn.setreg(opts.register, text)
if config.options.notify then
local range = get_range(ctx)
vim.notify(string.format("Yanked %s%s", ctx.path, range), vim.log.levels.INFO)
end
return text
end
```
See docs/formatters.md for the sample output of all supported formatters.
local yankcraft = require("yankcraft")
vim.keymap.set({ "n", "x" }, "<leader>yf", function()
local formatter = require("yankcraft.formatters.filepath")
require("yankcraft").copy({formatter = formatter})
vim.cmd("normal! \27") -- return to normal mode, if you desire
end, {
desc = "Yank filepath"
})
vim.keymap.set({ "n", "x" }, "<leader>yl", function()
local formatter = require("yankcraft.formatters.filepath_with_line_range")
require("yankcraft").copy({formatter = formatter})
end, {
desc = "Yank filepath and line number(s)"
})
-- this assumes you have not setup another default formatter via `setup`
vim.keymap.set({ "n", "x" }, "<leader>ym", require("yankcraft").copy, {
desc = "Yank content in Markdown format"
})
vim.keymap.set({ "n", "x" }, "<leader>yn", function()
local norg = require("yankcraft.formatters.norg")
require("yankcraft").copy({formatter = norg})
end, {
desc = "Yank content in Norg format"
})
-- You can also define your custom formatter and pass it directly here.
-- See "Building a Custom Formatter"Defaults:
require("yankcraft").setup({
register = "+", -- register to yank into (system clipboard)
path_style = "auto", -- "auto" (git root -> cwd) | "relative" (cwd) | "absolute"
notify = true, -- notify on successful yank
dedent = true, -- whether to remove the common indents on code content copies
formatter = nil, -- your custom default formatter function, markdown by default
})You can build a custom formatter if it is not provided by the project (or feel free to open a PR). This can be done by providing a function with the following format:
fun(ctx: YankCraft.Context): stringWhere YankCraft.Context provides the following fields:
---@class YankCraft.Context
---@field path string Display path of the buffer.
---@field filetype string Filetype used as the code-fence language ("" when unknown).
---@field selection_type YankCraft.SelectionType The type of selection that was performed.
---@field start_line integer 1-based start line.
---@field end_line integer 1-based end line.
---@field start_col integer? 1-based start column (if visual selection).
---@field end_col integer? 1-based end column (if visual selection).
---@field lines string[] Collected buffer lines.
---@alias YankCraft.SelectionType
---| "char"
---| "line"
---| "block"See lua/yankcraft/formatters/norg.lua for an example implementation
of a formatter that dedents the lines of text and adds a @code <lang> start and an @end end fence.
Such a function can then be given either as a default formatter in the configuration, or when calling
the copy function directly (see Suggested Keymaps for an example).
Use the following command to run the tests:
make testMIT

