doc/emojis.txt — rendered from the plugin's own vimdoc
*emojis.txt* Emoji operations for Neovim *emojis.nvim* Author: Stefan Bartl Version: 0.3.0
CONTENTS
1. Introduction .............. |emojis-intro| 2. Requirements .............. |emojis-requirements| 3. Installation .............. |emojis-installation| 4. Configuration ............. |emojis-config| 5. The :Emojis command ....... |:Emojis| 5.1 Actions ............... |emojis-actions| 5.2 Scopes ................ |emojis-scopes| 6. Space-collapse behaviour .. |emojis-clear-spaces| 7. Tab completion ............ |emojis-completion| 8. Lua API ................... |emojis-api| 9. Health check .............. |emojis-health| 10. Bindings .................. |emojis-bindings| 11. Architecture .............. |emojis-architecture| 12. Quick-insert overlay ...... |emojis-overlay| 13. Emoji checkboxes .......... |emojis-checkboxes| 14. Unicode toolkit ........... |emojis-unicode|
1. INTRODUCTION
emojis.nvim provides a single:Emojiscommand to remove, count, list, replace, or insert emojis across several scopes — the current line, the visual selection, the whole buffer, or the entire project (via ripgrep). Cross-platform. Emoji detection uses a pure UTF-8 byte tokenizer; no external library is required. Requires lib.nvim — the:Emojiscommand is registered vialib.nvim.bindings.usercmd.composer.
2. REQUIREMENTS
- Neovim 0.9 or later - ripgrep (rg) — only for thecwdscope - telescope.nvim or fzf-lua — optional; live-search picker for `:Emojis insert(picker.engine = "auto"), falls back tovim.ui.select` - lib.nvim (StefanBartl/lib.nvim) — required; the:Emojiscommand is registered vialib.nvim.bindings.usercmd.composer, with no fallback (notify/mapspecifically still degrade to a native fallback if somehow absent at that call site, but the command layer itself does not)
3. INSTALLATION
lazy.nvim:
{
"StefanBartl/emojis.nvim",
dependencies = { "StefanBartl/lib.nvim" }, -- required
cmd = "Emojis",
opts = {},
}
packer.nvim:
use {
"StefanBartl/emojis.nvim",
requires = { "StefanBartl/lib.nvim" }, -- required
config = function()
require("emojis").setup()
end,
}
vim-plug:
Plug 'StefanBartl/lib.nvim' " required
Plug 'StefanBartl/emojis.nvim'
lua require("emojis").setup()
4. CONFIGURATION
require("emojis").setup({
default_scope = "%", -- scope used when none is given
command = "Emojis", -- user-command name
picks = { -- insert-picker entries: { glyph, label }
{ "✅", "check" }, { "⚠️", "warning" }, --[[ … ]]
},
names = { -- codepoint -> :name: for replace
[0x2705] = ":white_check_mark:",
[0x26A0] = ":warning:",
},
search = { -- cwd search (ripgrep)
cmd = "rg",
extra_args = { "--no-heading", "--line-number", "--with-filename", "--color=never" },
no_ignore = false, -- true -> --no-ignore (also search gitignored files)
},
keymaps = { -- opt-in preset keymaps
preset = false,
},
wrap = { -- marker for the wrap action
prefix = "[[",
suffix = "]]",
},
preview = { -- opt-in highlight before clear/replace
enable = false,
duration_ms = 150,
hl_group = "IncSearch",
},
picker = { -- insert-picker engine
engine = "auto", -- "auto" | "telescope" | "fzf-lua" | "select"
},
overlay = { -- quick-insert overlay (`:Emojis overlay`)
mode = "grid", -- "grid" | "grid_keys" | "list"
frecency = true, -- reorder picks by recorded usage
columns = 5,
limit = 20,
title = " Emojis ",
theme = "rounded", -- any ui.kit theme arg
-- picks = { { "✅", "white_check_mark" }, … } -- replaces the default list
},
checkbox = { -- emoji checkbox cycles (`:Emojis toggle [set]`)
default_set = "", -- "" = search every set below; or e.g. "status"
sets = {
checkbox = { "🔲", "✅" },
status = { "🔴", "🟡", "🟢" },
review = { "👍", "👎" },
},
order = { "checkbox", "status", "review" }, -- search order when default_set = ""
},
})
default_scope
Scope applied when the second argument is omitted. One ofword,line,visual,%,cwd. Default:"%".
command
Name of the registered user command. Default: "Emojis".
picks
Array of{ glyph, label }entries shown by:Emojis insert. The default 60+-entry catalog also derivesnames(one shared label per glyph, seeconfig/DEFAULTS.lua), so overridingpicksalone does not affectnames.
names
Map of Unicode codepoint to replacement text used by `:Emojis replace/unreplace. Unknown emojis fall back to:U+XXXX:`.
search.cmd
External search binary for the cwd scope. Default: "rg".
search.extra_args
Arguments passed before the pattern and path. Default: ripgrep flags for no-heading, line-number, with-filename, no-color.
search.no_ignore
Whentrue, passes--no-ignoreso the cwd scope also searches gitignored files. Default:false.
keymaps.preset
Whentrue, binds the opt-in preset keymaps (<C-e>,<leader>ec,<leader>el) and labels the<leader>egroup in which-key if installed. Default:false. See |emojis-bindings|.
wrap.prefix, wrap.suffix
Text inserted before/after each emoji by thewrapaction. Default:"[["/"]]".
preview.enable
Whentrue, briefly highlights the emojis about to be mutated beforeclear/replaceruns (extmarks,preview.hl_group), blocking forpreview.duration_ms. Default:false.
preview.duration_ms
How long the highlight is shown before the buffer is mutated. Default:
150.
preview.hl_group
Highlight group used for the preview extmarks. Default: "IncSearch".
picker.engine
Insert-picker engine:"auto"tries telescope.nvim then fzf-lua (both optional), falling back tovim.ui.select;"telescope"/"fzf-lua"force one (still falling back if not installed);"select"always usesvim.ui.select. Default:"auto".
overlay.mode
Default interaction mode for:Emojis overlay:"grid"(hjkl/arrows +<CR>),"grid_keys"(one hotkey per cell), or"list"(delegates tokit.chooser). Default:"grid". See |emojis-overlay|.
overlay.frecency
Whentrue, every insertion (overlay andinsertpicker alike) is recorded andoverlay.picksis reordered most-used-first with a 30-day recency half-life. Default:true. See |emojis-overlay|.
overlay.columns
Grid width ingrid/grid_keysmode. Default:5.
overlay.limit
Maximum number of cells shown. Default: 20.
overlay.title
Float title. Default: " Emojis ".
overlay.theme
Anyui.kittheme argument: a preset name ("minimal","rounded","solid","double","ascii") or an override table. Default:"rounded".
overlay.picks
{ glyph, label } entries for the overlay grid. Unlike most options, this
**replaces** the default list rather than merging into it.
checkbox.sets
Table of named glyph cycles used by:Emojis toggle [set]. A set you redefine replaces the default's states rather than merging into it. The defaults (checkbox,status,review) are deliberately disjoint.
checkbox.default_set
Set used when:Emojis toggleis called with nosetargument. Empty string (default) searches every set incheckbox.order.
checkbox.order
Search order across sets when checkbox.default_set = "", and the
tie-break for a glyph appearing in more than one set. Sets not listed here
are still searched, appended in name-sorted order.
5. THE :Emojis COMMAND
:Emojis [action] [scope]
:[range]Emojis [action]
With no arguments,:Emojisis equivalent to:Emojis clear %. An explicit Vim range (:'<,'>Emojis,:10,20Emojis) always overrides thescopekeyword. Built vialib.nvim.bindings.usercmd.composer: one route per action, forwarding to the same dispatch function as before (unchanged). An unrecognized action now reports composer's own usage block (every registered action, one per line) instead of the old plain-string error.
5.1 Actions
clear
Remove every emoji in scope. Collapses surrounding spaces — see |emojis-clear-spaces|. Default action.
replace
Replace each emoji with its:name:placeholder (or:U+XXXX:fallback).
unreplace
Replace:name:/:U+XXXX:placeholders back with their emoji — the inverse of |emojis-actions|replace. Unrecognized:...:tokens are left untouched.
list
Collect every emoji in scope into the quickfix list and :copen.
count
Count emojis in scope and report via a notification.
insert
Open a picker (telescope.nvim/fzf-lua if available perpicker.engine, elsevim.ui.select) and insert the chosen emoji at the cursor. Thescopeargument is ignored for this action.
first
Move the cursor to the first emoji in the buffer. The scope argument is
ignored; the buffer is not modified.
next
Move the cursor to the next emoji after the cursor, wrapping to the top of
the buffer if none is found below. The scope argument is ignored; the
buffer is not modified.
wrap
Surround each emoji withconfig.wrap.prefix/config.wrap.suffix(default[[/]]), without removing it — e.g. for downstream machine processing.
overlay
Open the quick-insert overlay (config.overlay). Thescopeargument is instead an interaction mode:grid|`grid_keys`|list. See |emojis-overlay|.
toggle
Cycle the emoji checkbox glyph found on the cursor line (or every line in a range/visual selection) one step through a configuredconfig.checkbox.setscycle. Thescopeargument is instead a set name. See |emojis-checkboxes|.
unicode
Unicode toolkit for any character, not just emoji:name|`search`|table|digraphs. Thescopeargument is instead a sub-action. See |emojis-unicode|.
5.2 Scopes
% Whole current buffer (default).
line The current cursor line.
word The whitespace-delimited run of text containing the cursor byte
column — not the whole line. Errors if the cursor sits on
whitespace. Only actions operating on a single line
(clear/replace/list/count) narrow to this sub-range.
visual The lines of the last / current visual selection.
cwd All files under the working directory, searched asynchronously with
ripgrep. list/count report results directly; clear/replace
first show the same matches, then ask for confirmation (default:
cancel) before mutating every matched file. Run :Emojis list cwd
first as a dry-run preview. Buffers with unsaved changes are skipped
rather than clobbered. Arguments after cwd are passed to ripgrep as
extra --glob filters, e.g. :Emojis count cwd *.md.
6. SPACE-COLLAPSE BEHAVIOUR
When clear removes an emoji (or a run of adjacent emojis) that had a single
space on both sides, the result keeps exactly one space instead of leaving
two:
" 🚀 " -> " "
"a 🚀 b" -> "a b"
" 🚀🔥 " -> " "
"a🚀b" -> "ab"
Emojis carrying a Variation-Selector-16 (e.g. ⚠️) are treated as a single emoji grapheme — counted once and replaced as one placeholder.
7. TAB COMPLETION
:Emojis completes the action at the first argument and the scope at the
second:
:Emojis <Tab> clear count first insert list next overlay
replace toggle unicode unreplace wrap
:Emojis clear <Tab> word line visual % cwd
:Emojis overlay <Tab> grid grid_keys list
:Emojis toggle <Tab> <configured config.checkbox.sets names>
:Emojis unicode <Tab> name search table digraphs
8. LUA API
local emojis = require("emojis")
setup({opts}) *emojis.setup()*
Configure and activate. Idempotent.
clear() *emojis.clear()*
Clear emojis from the whole current buffer.
count() *emojis.count()*
Count emojis in the whole current buffer.
insert() *emojis.insert()*
Open the insert picker at the cursor.
overlay({mode}) *emojis.overlay()*
Open the quick-insert overlay. mode is optional:
"grid"|`"grid_keys"`|"list", defaulting to config.overlay.mode.
toggle({set}, {dir}) *emojis.toggle()*
Cycle the checkbox glyph on the cursor line, or every line in the current
visual range. set is optional (defaults to config.checkbox.default_set;
an explicit empty string does not override a non-empty default_set).
checkbox_add({set}) *emojis.checkbox_add()*
Add a checkbox glyph to the cursor line / visual range if it lacks one.
checkbox_remove({set}) *emojis.checkbox_remove()*
Remove the checkbox glyph from the cursor line / visual range.
cascade_groups({set}) *emojis.cascade_groups()*
Return config.checkbox.sets in cascade.nvim's cycle.groups format, so
the same glyph vocabulary drives both plugins. Pure data function — never
require("cascade") itself, safe to call whether or not cascade.nvim is
installed.
ops() *emojis.ops()*
Return the pure operations module (clear/count/list/replace) that works on
string arrays without touching the Neovim API. Useful for scripting/tests:
local ops = require("emojis").ops()
local cleaned, removed = ops.clear({ " 🚀 done" }) -- { " done" }, 1
The |emojis-unicode| toolkit is reachable the same way, viarequire("emojis.unicode")— see |emojis-unicode| anddocs/api.md.
9. HEALTH CHECK
:checkhealth emojis
Checks: - Neovim >= 0.9 -lib.nvim.bindings.usercmd.composeravailable (required for the:Emojiscommand layer, no fallback) -vim.ui.selectavailable (insert picker) - ripgrep on PATH (cwd scope) -vim.systemavailable (async search) - plugin loaded (guard flag set) - which-key found (optional; labels the preset's<leader>egroup) -keymaps.presetenabled or not
10. BINDINGS
Full cheatsheet of every keymap, user command, and autocommand: seedocs/BINDINGS.mdin the repository. Only the:Emojiscommand is always registered; the preset keymaps (<C-e>,<leader>ee,<leader>et,<leader>ec,<leader>el) require |emojis-config|keymaps.preset = trueand are labelled under the<leader>egroup in which-key if it is installed (optional, no hard dependency). emojis.nvim defines no autocommands. <C-e> n, i Insert picker at the cursor <leader>ee n Quick-insert overlay <leader>et n, x Toggle emoji checkbox (line / visual range) <leader>ec n Count emojis in the buffer <leader>el n List emojis in the buffer -> quickfix
11. ARCHITECTURE
plugin/emojis.lua Load guard
lua/emojis/
init.lua Public API, setup()
@types.lua LuaLS type definitions
config/
DEFAULTS.lua Immutable default configuration
init.lua Merge + access to active config
util/
notify.lua Prefixed notify wrapper (via util/lib.lua)
lib.lua Soft bridge to lib.nvim (notify/map), with fallback
core/
patterns.lua Pure UTF-8 emoji tokenizer (graphemes incl. VS16)
ops.lua Pure clear/count/list/replace operations
scope.lua Scope (+ range) -> buffer line range
insert.lua Shared insert helper (picker + overlay), records frecency
checkbox.lua Pure line-scoped checkbox find/cycle
bindings/
init.lua Orchestrates usrcmds/keymaps/autocmds
usrcmds.lua Registers :Emojis (via commands.lua)
keymaps.lua Opt-in preset keymaps (keymaps.preset); also the which-key group label
autocmds.lua Empty (no autocmds by design)
overlay/
init.lua Quick-insert overlay (grid/grid_keys/list modes)
frecency.lua Usage tracking (stdpath("data")/emojis.nvim/frecency.json)
actions.lua Buffer-facing handlers (edit/list/count)
nav.lua Cursor navigation (first/next)
picker.lua Insert picker (vim.ui.select)
search.lua Async cwd search (ripgrep)
commands.lua :Emojis dispatch + tab completion
health.lua checkhealth provider
12. QUICK-INSERT OVERLAY
:Emojis overlay [grid|grid_keys|list]
A small float holding the ~20 emojis a developer actually reaches for (config.overlay.picks), ordered by how often you use them. Unlikeinsert(full catalog), the overlay is meant to be opened and dismissed in a second or two. Bound to<leader>eewhenkeymaps.preset = true. The second argument is an interaction mode, not a scope. Omit it to useconfig.overlay.mode: grid 2D grid; hjkl/arrows move, <CR> inserts (default) grid_keys Same grid, plus a direct hotkey per cell (one keypress inserts) list One glyph per row with its shortcode, via lib.nvim's kit chooser<Esc>orqcloses without inserting. Every insertion — from the overlay and theinsertpicker — is recorded tostdpath("data")/emojis.nvim/frecency.jsonand reordersconfig.overlay.picks(most-used-first, 30-day recency half-life). Sorting only ever reorders the configured set; it never adds or removes entries.overlay.frecency = falsedisables both the reordering and the file write; clear the history at any time withrequire("emojis.overlay.frecency").reset().
13. EMOJI CHECKBOXES
:Emojis toggle [set]
:[range]Emojis toggle [set]
Cycles an emoji "checkbox" glyph one step through a configuredconfig.checkbox.setscycle, e.g.🔲 1. Hallo->✅ 1. Hallo-> back again. The glyph is found anywhere on the line (line-scoped, not cursor-scoped), so the cursor can sit at the end of the text being written. Unlike the other actions, the second argument is a set name, not a scope — and the scope is always a line range (explicit Vim range, or the cursor line/visual selection), neverword,%, orcwd. Bound to<leader>et(normal and visual mode) whenkeymaps.preset = true.require("emojis").cascade_groups()returnsconfig.checkbox.setsin cascade.nvim'scycle.groupsformat, so the same glyph vocabulary drives both cascade's cursor-precise<C-y>cycling and emojis.nvim's line-scoped:Emojis toggle— see |emojis.cascade_groups()|. The pure logic in core/* is isolated from every API/UI layer and is therefore unit-testable on plain string arrays (seeTESTS/).
14. UNICODE TOOLKIT
:Emojis unicode name [reg [type]]
:Emojis unicode search[!] <query>
:Emojis unicode table
:Emojis unicode digraphs
A chrisbra/unicode.vim replacement, for any Unicode character.
name
Report the character under the cursor: codepoint (hex/dec), glyph, name, and any digraph that produces it. Withreg, also save one representation into that register;typepicks which —value|`hex`|name|`html`|digraph|regex(defaultname).reg = "="is refused: the expression register evaluates its contents as Vimscript the next time anything reads@=, and the name text can come from downloaded/cached data.
search[!]
Find characters by name substring (case-insensitive), or by an exactU+xxxx/0xNNNN/decimal value. Results open in|vim.ui.select()|; picking one reports it. With!, picking one inserts the glyph at the cursor instead.
table
Open a scratch buffer listing the whole loaded name table, one line per character.
digraphs
Open a scratch buffer listing every digraph Neovim itself knows (|digraph_getlist()|) — needs no download. The name lookup has two tiers. A glyph already inconfig.picks/config.namesresolves instantly, no network involved. Anything else is looked up in the Unicode Character Database's ownUnicodeData.txt, downloaded once per machine viacurlintostdpath("cache")/emojis/UnicodeData.txtand cached there. Withoutcurlon$PATH, or before the first successful download,name(for an uncatalogued character),search, andtablereport why and do nothing else;digraphsis unaffected. A codepoint inside a large contiguous UCD block (CJK Unified Ideographs, Hangul Syllables, Tangut, ...) gets a synthesized "PREFIX-HEX" name, e.g.CJK UNIFIED IDEOGRAPH-4E2D. The download writes to a temp file and is renamed into place only once it passes a size check; a cached file that turns out corrupt or incomplete is deleted and reported rather than trusted, so retrying the command is enough to recover.