README.md
August 2, 2026 · View on GitHub
celeste_comment.nvim
Batteries-included commenting plugin — line/block comment, textobjects, real sticky cursor, and more!
- Features
- Comparison
- Showcase
- Requirements
- Installation
- Default Configuration
- What it doesn't do
- Limitations
- Future work
- Acknowledgments
Features
- Line/block comment toggle -- fully dot-repeatable with count support
- Truly accurate keep cursor -- precise row/column tracking across
TextEdits, adjusted per edit - Truly accurate keep selection -- selection range tracks each
TextEditprecisely when toggling comments in visual mode - VSCode-style indent algorithm -- handles mixed tabs and spaces
- Invert/Force add/Force remove comment -- per-line comment action control
- Textobjects -- line, block, and auto textobjects, works without Tree-sitter
- Insert mode line comment toggle -- with cursor sticky support
- Insert comment above / below / at end of line
- Case insensitive comment detection -- e.g.
@REMvs@remvs@rEm - Context-aware comment string resolution via Tree-sitter -- comment string adapts to context via Tree-sitter, no extra plugins required. e.g. supports
JSX/TSXout of the box - Multi-variant comment string detection — recognizes all comment prefix variants when uncommenting (e.g. Rust
//,///,//!) TextEdits-- unlike Neovim's built-in or other plugins, edits are modeled asTextEdits, making it more hackable and composable
Comparison
| Feature | celeste_comment.nvim | Neovim built-in | Comment.nvim | mini.comment | vim-commentary |
|---|---|---|---|---|---|
| Edit model | TextEdits — edits as range+text objects • commit changes via nvim_buf_set_text or nvim_buf_set_lines (lockmarks) | Direct line replacement • nvim_buf_set_lines (lockmarks) | Direct line replacement • nvim_buf_set_lines (lockmarks) | Direct line replacement • nvim_buf_set_lines (lockmarks) | Direct line replacement • Vim setline() |
| Line comment | ✅ | ✅ | ✅ | ✅ | ✅ |
| Block comment | ✅ | ❌ | ✅ | ❌ | ❌ |
| Force add comment | ✅ | ❌ | ❌ | ❌ | ❌ |
| Force remove comment | ✅ | ❌ | ❌ | ❌ | ❌ |
| Dot-repeat | ✅ | ✅ | ✅ | ✅ | ✅ |
| Count | ✅ | ✅ | ✅ | ✅ | ✅ |
| Indent algorithm | VSCode-style — min visible col • handle mixed tab/space | Simple — min whitespace prefix • does not handle mixed tab/space | Standard — shiftwidth/tabstop | Simple — min whitespace prefix • does not handle mixed tab/space | Minimal — ^\s*\zs• optional startofline |
| Keep cursor | Precise tracking — cursor adjusts per TextEdit | ❌ | Imprecise restore — save/restore • no edit adjustment | ❌ | ❌ |
| Keep selection | Precise tracking — selection adjusts per TextEdit | ❌ | ❌ | ❌ | ❌ |
| Invert per line | ✅ | ❌ | ❌ | ❌ | ❌ |
| Line textobject | ✅ | ✅ | ❌ | ✅ | ✅ |
| Block textobject | ✅ | ❌ | ❌ | ❌ | ❌ |
| Textobject auto | ✅ | ❌ | ❌ | ❌ | ❌ |
| Uncomment auto | ✅ | ❌ | ❌ | ❌ | ✅ |
Showcase
Line/Block comment toggle, textobjects, gcu
Commenting in insert mode with keep cursor
Context-aware comment string resolution via Tree-sitter
Keep selection when toggle comments in visual mode
Force add/remove comment and dot-repeat
Invert comment status per-line
Cursor sticky + Dot-repeat
With multicursor.nvim
Requirements
- Neovim >= 0.12
- Tree-sitter parsers (Optional) -- for context-aware comment string resolution
Installation
Important
- Breaking changes may occur in MINOR version bumps (e.g.
0.1.0→0.2.0). - PATCH bumps (e.g.
0.1.0→0.1.1) are backward compatible. Pinning to a specific version or commit is recommended.
vim.pack (Neovim 0.12+)
vim.pack.add({
{
src = "https://github.com/celeste3z/celeste_comment.nvim",
name = "celeste_comment",
version = vim.version.range("*"),
}
})
require("celeste_comment").setup({})
lazy.nvim
{ "celeste3z/celeste_comment.nvim", lazy = false, opts = {} }
Default Configuration
{
-- Restore cursor position after commenting.
keep_cursor = true,
-- Restore selection after commenting.
-- See `:help celeste_comment-config-keep_selection`
-- Possible values: "never" | "accurate" | "expand_block"
keep_selection = "never",
-- Insert space between comment marker and text.
insert_space = true,
-- Place comment at start of line, skip indent alignment
line_comment_no_indent = false,
-- Match comment markers case-insensitively (e.g. `@REM` vs `@rem` vs `@rEm`)
case_insensitive = false,
-- Trim whitespace before detecting block tokens.
block_relaxed_detect = true,
-- Max lines to search for block comment pairs.
block_textobj_nlines = 200,
-- How to handle empty lines during comment toggle.
-- See `:help celeste_comment-config-ignore_empty_lines` for more details
-- Possible values: "never" | "mixed" | "always"
ignore_empty_lines = "always",
-- Fallback to block comment when line comment wraps.
-- See `:help celeste_comment-config-fallback_to_block` for more details
-- Possible values: "never" | "if_line_cms_wrapped"
fallback_to_block = "if_line_cms_wrapped",
-- Log level (nvim-0.13+). Ignored on older versions.
log_level = vim.log.levels.OFF,
-- Comment string configuration.
cms_confs = nil,
mappings = {
-- Line comment by motion (n)
line_toggle = "gc",
-- Line comment current line (n)
line_toggle_cur = "gcc",
-- Line comment visual selection (x)
line_toggle_visual = "gc",
-- Insert mode line toggle (i), example `{"<M-/>", "<M-_>"}`
line_toggle_insert = "",
-- Block comment by motion (n, x)
block_toggle = "gb",
-- Block comment current line (n)
block_toggle_cur = "gbc",
-- Block comment visual selection (x)
block_toggle_visual = "gb",
-- Linewise textobject (o)
line_textobject = "gc",
-- Blockwise textobject (o)
block_textobject = "gb",
-- Auto textobject (o, x), example 'ga'
auto_textobject = "",
-- Auto uncomment (n), example `gcu`
uncomment_auto = "",
-- Insert comment below (n), example `gco`
line_add_below = "",
-- Insert comment above (n), example `gcO`
line_add_above = "",
-- Insert comment at end of line (n), example `gcA`
line_add_eol = "",
-- Invert comment per line (n, x), example `gcI`
line_invert = "",
-- Force add line comment (n, x), example `gCC`
line_force_add = "",
-- Force remove line comment (n, x), example `gCU`
line_force_remove = "",
-- Cursor sticky dot-repeat
dot_repeat = ".",
},
hooks = {
-- Called before commit edits, receives context
pre_commit_edits = nil,
-- Called after commit edits, receives context
post_commit_edits = nil,
-- Custom comment string resolver function
cms_conf_resolver = nil,
},
}
See :help celeste_comment-configuration for details.
Tip
If a language has just one comment style (e.g. vim, asm) and Neovim already
can sets its commentstring natively, you don't have to define anything here, we can
fully fall back to Neovim's built-in commentstring resolution.
This plugin already has built-in block comment support for most common languages.
If your filetype isn't included, the fastest and simplest way to add it is:
- Put the code below in your
ftplugin/<filetype>.lua(see:help filetype-plugin) - Use
FileTypeautocommand (:help FileType)
-- for example
vim.b.celeste_comment_block_commentstring = "{-%s-}"
Or, you can use cms_confs:
-- for example, `xxx` should be the Tree-sitter parser name or filetype
require("celeste_comment").setup({
cms_confs = {
"xxx" = {"//%s", "/*%s*/"}
}
})
Line comments are configured via vim.bo.commentstring (the standard Neovim option).
For advanced comment string resolution, see :help celeste_comment.
If you like this plugin, give it a ⭐!
What it doesn't do
- Cover all cases — This plugin's aim is to handle the vast majority of common scenarios, not every possible case. Known textobject edge cases and unusual comment patterns are acknowledged but not planned to fix.
- Doc comment
- Header comment
Limitations
-
Auto-detect textobject accuracy —
textobject_auto()first checks whether the current line contains a line comment. In languages like Lua where--is used for both line comments (--) and block comments (--[[ ]]), a line starting with--may be misidentified as a line comment, leading to incorrect textobject selection. -
Regex-based textobject range — Pattern matching can produce false positives in certain scenarios. For example, comment-like tokens inside strings may be mistakenly treated as actual comments. Additionally, the scan range is capped by
block_textobj_nlines(default 200), so textobject detection may not work beyond that limit. -
Visual block mode (
<C-v>) — Selection is treated as linewise; the entire selected lines are block-commented rather than inserting comment markers per column. For column-wise comment operations, consider using a plugin like multicursor.nvim.
Future work
- Integrated with Neovim's builtin multicursor.
Acknowledgments
-
VSCode — The indent algorithm is ported from VSCode's comment implementation. Most of its test cases have also been ported to this plugin's test suite. This plugin is highly inspired by it.
-
Zed — The capture-based overrides paradigm for comment scope resolution.
-
mini.comment — Its code style and linewise textobjects implementation served as a reference for this plugin's development.
-
Comment.nvim — Part of the built-in language comment string table was adapted from Comment.nvim.