Repository: terrortylor/nvim-comment
Branch: main
Commit: e9ac16ab0566
Files: 10
Total size: 35.3 KB
Directory structure:
gitextract_xu0egi6i/
├── .github/
│ └── workflows/
│ └── make.yml
├── .luacheckrc
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── lua/
│ ├── nvim_comment.lua
│ └── spec/
│ └── nvim_comment_spec.lua
└── tests/
├── comment_spec.lua
└── minimal_init.vim
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/make.yml
================================================
name: Run Tests
on: [push, pull_request]
jobs:
Tests:
name: Linting and Test
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Prepare
run: |
# sudo apt-get update
sudo apt-get install lua-check
mkdir -p ~/.local/share/nvim/site/pack/vendor/start
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
mkdir -p build
wget https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage
chmod +x nvim.appimage
mv nvim.appimage ./build/nvim
- name: Run Lint
run: make lint
- name: Run unit tests
run: |
export PATH="${PWD}/build/:${PATH}"
make unit
echo "exit code $?"
- name: Run tests
run: |
export PATH="${PWD}/build/:${PATH}"
make gh-integration
echo "exit code $?"
================================================
FILE: .luacheckrc
================================================
globals = {"Snake", "Cell", "vim", "_TEST", "LOG_LEVEL"}
read_globals = {
print = {
fields = {
revert = {}
}
},
os = {
fields = {
execute = {
fields = {
revert = {}
}
},
}
}
}
================================================
FILE: CHANGELOG.md
================================================
Changelog
======================
2021-07-10 - Add hook function to be called before reading `commentstring`
2021-02-16 - Fix issue with `gv` causing entire buffer range to be acted on
2021-02-12 - Fix issue with motion acting on last visual selection
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Alex Tylor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
all: lint test
lint:
@echo "Linting..."
luacheck --no-color .
@echo
unit:
@echo "Run unit tests..."
nvim --headless --noplugin -c 'packadd plenary.nvim' -c "PlenaryBustedDirectory lua/spec"
@echo
gh-integration:
@echo "Run integration tests..."
nvim --headless --noplugin -u tests/minimal_init.vim -c "PlenaryBustedDirectory tests { minimal_init = './tests/minimal_init.vim' }"
@echo
integration:
@echo "Run integration tests..."
nvim --headless --noplugin -c 'packadd plenary.nvim' -c "PlenaryBustedDirectory tests"
@echo
test: unit integration
================================================
FILE: README.md
================================================
# nvim-comment
Toggle comments in Neovim, using built in `commentstring` filetype option;
written in Lua. Without a doubt this plugin **is not required** and is a rip off
of [TPope's Commentary](https://github.com/tpope/vim-commentary) with less
features! What makes this plugin stand out over the numerous other comment
plugins written in Lua are:
- Comments each line, rather than adds block comments; making it easier to
toggle code when debugging
- Uses the built in `commentstring` buffer option to define comment markers
- Where a marker doesn't have a **space** character as padding this is added,
configurable (this can be disabled in the options, see below but is useful
when working with numerous linters)
- Supports motions
- Dot `.` repeatable
When the plugin is called it works out the range to comment/uncomment; if all
lines in the given range are commented then it uncomments, otherwise it comments
the range. This is useful when commenting a block out for testing with a real
like comment in it; as for the plugin a comment is a comment.
## Usage
Either use the command `CommentToggle`, e.g.:
- `CommentToggle` comment/uncomment current line
- `67,69CommentToggle` comment/uncomment a range
- `'<,'>CommentToggle` comment/uncomment a visual selection
Or use the default mappings:
- `gcc` comment/uncomment current line, this does not take a count, if you want
a count use the `gc{count}{motion}`
- `gc{motion}` comment/uncomment selection defined by a motion (as lines are
commented, any comment toggling actions will default to a linewise):
- `gcip` comment/uncomment a paragraph
- `gc4w` comment/uncomment current line
- `gc4j` comment/uncomment 4 lines below the current line
- `dic` delete comment block
- `gcic` uncomment commented block
### Configure
The comment plugin needs to be initialised using:
```lua
require('nvim_comment').setup()
```
However you can pass in some config options, the defaults are:
```lua
{
-- Linters prefer comment and line to have a space in between markers
marker_padding = true,
-- should comment out empty or whitespace only lines
comment_empty = true,
-- trim empty comment whitespace
comment_empty_trim_whitespace = true,
-- Should key mappings be created
create_mappings = true,
-- Normal mode mapping left hand side
line_mapping = "gcc",
-- Visual/Operator mapping left hand side
operator_mapping = "gc",
-- text object mapping, comment chunk,,
comment_chunk_text_object = "ic",
-- Hook function to call before commenting takes place
hook = nil
}
```
- Ignore Empty Lines
```lua
require('nvim_comment').setup({comment_empty = false})
```
- Don't trim trailing comment whitespace when commenting empty line
```lua
require('nvim_comment').setup({comment_empty_trim_whitespace = false})
```
The default for this is `true`, meaning that a commented empty line will not
contain any whitespace. Most `commentstring` comment prefixes have some
whitespace padding, disable this to keep that padding on empty lines.
- Disable mappings
```lua
require('nvim_comment').setup({create_mappings = false})
```
- Custom mappings
```lua
require('nvim_comment').setup({line_mapping = "<leader>cl", operator_mapping = "<leader>c", comment_chunk_text_object = "ic"})
```
- Disable marker padding
```lua
require('nvim_comment').setup({marker_padding = false})
```
- Hook function called before reading `commentstring`
You can run arbitrary function which will be called before plugin reads value of
`commentstring`. This can be used to integrate with
[JoosepAlviste/nvim-ts-context-commentstring](https://github.com/JoosepAlviste/nvim-ts-context-commentstring):
```lua
require('nvim_comment').setup({
hook = function()
if vim.api.nvim_buf_get_option(0, "filetype") == "vue" then
require("ts_context_commentstring.internal").update_commentstring()
end
end
})
```
- Changing/Setting `commentstring`
If you want to override the comment markers or add a new filetype just set the
`commentstring` options:
```lua
-- Assumes this is being run in the context of the filetype...
vim.api.nvim_buf_set_option(0, "commentstring", "# %s")
```
You can also use an autocommand to automatically load your `commentstring` for
certain file types:
```vim
" when you enter a (new) buffer
augroup set-commentstring-ag
autocmd!
autocmd BufEnter *.cpp,*.h :lua vim.api.nvim_buf_set_option(0, "commentstring", "// %s")
" when you've changed the name of a file opened in a buffer, the file type may have changed
autocmd BufFilePost *.cpp,*.h :lua vim.api.nvim_buf_set_option(0, "commentstring", "// %s")
augroup END
```
Or add the comment string option in the relevant `filetype` file:
```vim
let commentstring="# %s"
```
```lua
vim.api.nvim_buf_set_option(0, "commentstring", "# %s")
```
## Installation
Install just as you would a normal plugin, here are some options:
### Built in package manager
```bash
mkdir -p ~/.local/share/nvim/site/pack/plugins/start
cd ~/.local/share/nvim/site/pack/plugins/start
git clone https://github.com/terrortylor/nvim-comment
```
### Via a plugin manager
Using [packer.nvim](https://github.com/wbthomason/packer.nvim):
```lua
use "terrortylor/nvim-comment"
require('nvim_comment').setup()
```
================================================
FILE: lua/nvim_comment.lua
================================================
local api = vim.api
local M = {}
M.config = {
-- Linters prefer comment and line to have a space in between
marker_padding = true,
-- should comment out empty or whitespace only lines
comment_empty = true,
-- trim empty comment whitespace
comment_empty_trim_whitespace = true,
-- Should key mappings be created
create_mappings = true,
-- Normal mode mapping left hand side
line_mapping = "gcc",
-- Visual/Operator mapping left hand side
operator_mapping = "gc",
-- text object mapping, comment chunk
comment_chunk_text_object = "ic",
-- Hook function to call before commenting takes place
hook = nil,
}
function M.get_comment_wrapper()
local cs = api.nvim_buf_get_option(0, "commentstring")
-- make sure comment string is understood
if cs:find("%%s") then
local left, right = cs:match("^(.*)%%s(.*)")
if right == "" then
right = nil
end
-- left comment markers should have padding as linterers preffer
if M.config.marker_padding then
if not left:match("%s$") then
left = left .. " "
end
if right and not right:match("^%s") then
right = " " .. right
end
end
return left, right
else
api.nvim_command('echom "Commentstring not understood: ' .. cs .. '"')
end
end
function M.comment_line(l, indent, left, right, comment_empty, comment_empty_trim_whitespace)
if not comment_empty and l:match("^%s*$") then
return l
end
local line_empty = l:match("^%s*$")
-- standarise indentation before adding
local line = l:gsub("^" .. indent, "")
if right then
line = line .. right
end
local ret_line = indent .. left .. line
if comment_empty_trim_whitespace and line_empty then
return ret_line:gsub("^(.*)%s+$", "%1")
end
return ret_line
end
function M.uncomment_line(l, left, right, comment_empty_trim_whitespace)
local line = l
if right and right ~= "" then
line = line:gsub(vim.pesc(right) .. "$", "")
return line:gsub(vim.pesc(left), "", 1)
end
if comment_empty_trim_whitespace and left:match("%s+$") then
local left_nw = left:match("^(%S+)%s+$")
if line:match("^%s*" .. left_nw .. "$") then
return line:gsub(vim.pesc(left_nw), "", 1)
end
end
return line:gsub(vim.pesc(left), "", 1)
end
function M.operator(mode)
local line1, line2
if not mode then
line1 = api.nvim_win_get_cursor(0)[1]
line2 = line1
elseif mode:match("[vV]") then
line1 = api.nvim_buf_get_mark(0, "<")[1]
line2 = api.nvim_buf_get_mark(0, ">")[1]
else
line1 = api.nvim_buf_get_mark(0, "[")[1]
line2 = api.nvim_buf_get_mark(0, "]")[1]
end
M.comment_toggle(line1, line2)
end
function M.comment_toggle(line_start, line_end)
if type(M.config.hook) == "function" then
M.config.hook()
end
local left, right = M.get_comment_wrapper()
if not left or (not left and not right) then
return
end
local lines = api.nvim_buf_get_lines(0, line_start - 1, line_end, false)
if not lines then
return
end
-- check if any lines commented, capture indent
local esc_left = vim.pesc(left)
if M.config.comment_empty_trim_whitespace and left:match("%s+$") then
local left_nw = left:match("^(%S+)%s+$")
esc_left = vim.pesc(left_nw) .. "%s*"
end
local commented_lines_counter = 0
local empty_counter = 0
local indent
for _, v in pairs(lines) do
if v:find("^%s*" .. esc_left) then
commented_lines_counter = commented_lines_counter + 1
elseif v:match("^%s*$") then
empty_counter = empty_counter + 1
end
if not v:match("^%s*$") then
local line_indent = v:match("^%s+") or ""
if not indent or string.len(line_indent) < string.len(indent) then
indent = line_indent
end
end
end
for i, v in pairs(lines) do
if commented_lines_counter ~= (#lines - empty_counter) then
lines[i] = M.comment_line(
v,
indent,
left,
right,
M.config.comment_empty,
M.config.comment_empty_trim_whitespace
)
else
lines[i] = M.uncomment_line(v, left, right, M.config.comment_empty_trim_whitespace)
end
end
-- the LUA API doesn't seem to keep marks for lines that are changes with
-- nvim_buf_set_lines
api.nvim_call_function("setline", {line_start, lines})
-- The lua call seems to clear the visual selection so reset it
-- 2147483647 is vimL built in
api.nvim_call_function("setpos", { "'<", { 0, line_start, 1, 0 } })
api.nvim_call_function("setpos", { "'>", { 0, line_end, 2147483647, 0 } })
end
function M.select_comment_chunk()
vim.cmd([[execute "normal! \<esc>"]])
local up = vim.fn.search("\\v^(\\s*--)@!", "Wbcn")
up = up + 1
local down = vim.fn.search("\\v^(\\s*--)@!", "Wzn")
if down ~= 0 then
down = down - 1
end
local lines = vim.api.nvim_buf_line_count(0)
local pos = vim.api.nvim_win_get_cursor(0)[1]
if down == 0 then
down = lines
end
if up <= down and up <= pos and down >= pos then
vim.api.nvim_buf_set_mark(0, "<", up, 1, {})
vim.api.nvim_buf_set_mark(0, ">", down, 1, {})
vim.cmd("normal! `<V`>")
end
end
function M.setup(user_opts)
M.config = vim.tbl_extend("force", M.config, user_opts or {})
-- Messy, change with nvim_exec once merged
local vim_func = [[
function! CommentOperator(type) abort
let reg_save = @@
execute "lua require('nvim_comment').operator('" . a:type. "')"
let @@ = reg_save
endfunction
]]
vim.api.nvim_call_function("execute", { vim_func })
vim.api.nvim_command("command! -range CommentToggle lua require('nvim_comment').comment_toggle(<line1>, <line2>)")
if M.config.create_mappings then
local opts = { noremap = true }
api.nvim_set_keymap("n", M.config.line_mapping, "<Cmd>set operatorfunc=CommentOperator<CR>g@l", opts)
api.nvim_set_keymap("n", M.config.operator_mapping, "<Cmd>set operatorfunc=CommentOperator<CR>g@", opts)
api.nvim_set_keymap("x", M.config.operator_mapping, ":<C-u>call CommentOperator(visualmode())<CR>", opts)
api.nvim_set_keymap(
"x",
M.config.comment_chunk_text_object,
"<Cmd>lua require('nvim_comment').select_comment_chunk()<CR>",
opts
)
api.nvim_set_keymap(
"o",
M.config.comment_chunk_text_object,
"<Cmd>lua require('nvim_comment').select_comment_chunk()<CR>",
opts
)
end
end
return M
================================================
FILE: lua/spec/nvim_comment_spec.lua
================================================
local testModule
local mock = require('luassert.mock')
local api_mock
describe('nvim-comment', function()
before_each(function()
testModule = require('nvim_comment')
api_mock = mock(vim.api, true)
-- reset config to default
testModule.config = {
marker_padding = true
}
end)
after_each(function()
mock.revert(api_mock)
end)
describe('get_comment_wrapper', function()
local commentstrings = {
['COMMENT %s'] = {'COMMENT ', nil},
['{% comment %}%s{% endcomment %}'] = {'{% comment %}', '{% endcomment %}'},
['c# %s'] = {'c# ', nil},
['dnl %s'] = {'dnl ', nil},
['NB. %s'] = {'NB. ', nil},
['! %s'] = {'! ', nil},
['#%s'] = {'#', nil},
['# %s'] = {'# ', nil},
['%%s'] = {'%', nil},
['% %s'] = {'% ', nil},
['(*%s*)'] = {'(*', '*)'},
['(;%s;)'] = {'(;', ';)'},
['**%s'] = {'**', nil},
['-# %s'] = {'-# ', nil},
['-- %s'] = {'-- ', nil},
['-- %s'] = {'-- ', nil},
['.. %s'] = {'.. ', nil},
['.\\"%s'] = {'.\\"', nil},
['/*%s*/'] = {'/*', '*/'},
['/* %s */'] = {'/* ', ' */'},
['//%s'] = {'//', nil},
['// %s'] = {'// ', nil},
[':: %s'] = {':: ', nil},
[';%s'] = {';', nil},
['; %s'] = {'; ', nil},
['; // %s'] = {'; // ', nil},
['<!--%s-->'] = {'<!--', '-->'},
['<%#%s%>'] = {'<%#', '%>'},
['> %s'] = {'> ', nil},
[' *%s'] = {' *', nil},
['"%s'] = {'"', nil},
}
for string,expected in pairs(commentstrings) do
it('Should return comment wrapper(s) for: ' .. string .. ' no left marker padding', function()
testModule.config.marker_padding = false
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns(string)
local left, right = testModule.get_comment_wrapper(string)
assert.equals(left, expected[1])
assert.equals(right, expected[2])
end)
end
local padded_commentstrings = {
['COMMENT %s'] = {'COMMENT ', nil},
['{% comment %}%s{% endcomment %}'] = {'{% comment %} ', ' {% endcomment %}'},
['c# %s'] = {'c# ', nil},
['dnl %s'] = {'dnl ', nil},
['NB. %s'] = {'NB. ', nil},
['! %s'] = {'! ', nil},
['#%s'] = {'# ', nil},
['# %s'] = {'# ', nil},
['%%s'] = {'% ', nil},
['% %s'] = {'% ', nil},
['(*%s*)'] = {'(* ', ' *)'},
['(;%s;)'] = {'(; ', ' ;)'},
['**%s'] = {'** ', nil},
['-# %s'] = {'-# ', nil},
['-- %s'] = {'-- ', nil},
['-- %s'] = {'-- ', nil},
['.. %s'] = {'.. ', nil},
['.\\"%s'] = {'.\\" ', nil},
['/*%s*/'] = {'/* ', ' */'},
['/* %s */'] = {'/* ', ' */'},
['//%s'] = {'// ', nil},
['// %s'] = {'// ', nil},
[':: %s'] = {':: ', nil},
[';%s'] = {'; ', nil},
['; %s'] = {'; ', nil},
['; // %s'] = {'; // ', nil},
['<!--%s-->'] = {'<!-- ', ' -->'},
['<%#%s%>'] = {'<%# ', ' %>'},
['> %s'] = {'> ', nil},
[' *%s'] = {' * ', nil},
['"%s'] = {'" ', nil},
}
for string,expected in pairs(padded_commentstrings) do
it('Should return comment wrapper(s) for: ' .. string .. ' with marker padding', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns(string)
local left, right = testModule.get_comment_wrapper(string)
assert.equals(left, expected[1])
assert.equals(right, expected[2])
end)
end
it('Should return nil,nil if unsuported commentstring', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('something here')
local left, right = testModule.get_comment_wrapper('something here')
assert.equals(left, nil)
assert.equals(right, nil)
end)
end)
describe('comment_line', function()
local commentstrings = {
['COMMENT line'] = {'COMMENT ', ''},
['{% comment %}line{% endcomment %}'] = {'{% comment %}', '{% endcomment %}'},
['c# line'] = {'c# ', ''},
['dnl line'] = {'dnl ', ''},
['NB. line'] = {'NB. ', ''},
['! line'] = {'! ', ''},
['#line'] = {'#', ''},
['# line'] = {'# ', ''},
['%line'] = {'%', ''},
['% line'] = {'% ', ''},
['(*line*)'] = {'(*', '*)'},
['(;line;)'] = {'(;', ';)'},
['**line'] = {'**', ''},
['-# line'] = {'-# ', ''},
['-- line'] = {'-- ', ''},
['-- line'] = {'-- ', ''},
['.. line'] = {'.. ', ''},
['.\\"line'] = {'.\\"', ''},
['/*line*/'] = {'/*', '*/'},
['/* line */'] = {'/* ', ' */'},
['//line'] = {'//', ''},
['// line'] = {'// ', ''},
[':: line'] = {':: ', ''},
[';line'] = {';', ''},
['; line'] = {'; ', ''},
['; // line'] = {'; // ', ''},
['<!--line-->'] = {'<!--', '-->'},
['<%#line%>'] = {'<%#', '%>'},
['> line'] = {'> ', ''},
[' *line'] = {' *', ''},
['"line'] = {'"', ''},
}
for expected,comment_parts in pairs(commentstrings) do
it('Should comment line as expected, with no padding: ' .. expected, function()
local actual = testModule.comment_line('line', "", comment_parts[1], comment_parts[2], true, true)
assert.equals(expected, actual)
end)
end
it("Should add comment after any whitespace, with padding", function()
local actual = testModule.comment_line(" line", " ", "-- ", "", true, true)
assert.equals(" -- line", actual)
end)
it("Should add comment after any whitespace, with extra padding", function()
local actual = testModule.comment_line(" line", " ", "-- ", "", true, true)
assert.equals(" -- line", actual)
end)
it("Should trim whitespace", function()
local actual = testModule.comment_line("", " ", "-- ", "", true, true)
assert.equals(" --", actual)
end)
it("Should not trim whitespace", function()
local actual = testModule.comment_line("", " ", "-- ", "", true, false)
assert.equals(" -- ", actual)
end)
it("Should ignore line if empty or just whitespace", function()
local actual = testModule.comment_line(" line", " ", "-- ", "", false, true)
assert.equals(" -- line", actual)
actual = testModule.comment_line("", " ", "-- ", "", false, true)
assert.equals("", actual)
-- spaces
actual = testModule.comment_line(" ", " ", "-- ", "", false, true)
assert.equals(" ", actual)
-- Tabs
actual = testModule.comment_line(" ", " ", "-- ", "", false, true)
assert.equals(" ", actual)
end)
end)
describe('uncomment_line', function()
local commentstrings = {
['COMMENT line'] = {'COMMENT ', ''},
['{% comment %}line{% endcomment %}'] = {'{% comment %}', '{% endcomment %}'},
['c# line'] = {'c# ', ''},
['dnl line'] = {'dnl ', ''},
['NB. line'] = {'NB. ', ''},
['! line'] = {'! ', ''},
['#line'] = {'#', ''},
['# line'] = {'# ', ''},
['%line'] = {'%', ''},
['% line'] = {'% ', ''},
['(*line*)'] = {'(*', '*)'},
['(;line;)'] = {'(;', ';)'},
['**line'] = {'**', ''},
['-# line'] = {'-# ', ''},
['-- line'] = {'-- ', ''},
['-- line'] = {'-- ', ''},
['.. line'] = {'.. ', ''},
['.\\"line'] = {'.\\"', ''},
['/*line*/'] = {'/*', '*/'},
['/* line */'] = {'/* ', ' */'},
['//line'] = {'//', ''},
['// line'] = {'// ', ''},
[':: line'] = {':: ', ''},
[';line'] = {';', ''},
['; line'] = {'; ', ''},
['; // line'] = {'; // ', ''},
['<!--line-->'] = {'<!--', '-->'},
['<%#line%>'] = {'<%#', '%>'},
['> line'] = {'> ', ''},
-- [' *line'] = {' *', ''},
['"line'] = {'"', ''},
}
for input,comment_parts in pairs(commentstrings) do
it('Should uncomment line as expected: ' .. input, function()
local actual = testModule.uncomment_line(input, comment_parts[1], comment_parts[2], false)
assert.equals('line', actual)
end)
end
it('Should uncomment if trailing whitespace in left hand side removed, no right hand side', function()
local actual = testModule.uncomment_line("--", "-- ", "", true)
assert.equals('', actual)
end)
it('Should uncomment and not leave padding when comment_empty_trim_whitespace false, no right hand side', function()
local actual = testModule.uncomment_line("-- test", "-- ", "", true)
assert.equals('test', actual)
end)
end)
describe('comment_toggle', function()
it('Should add left hand side comments only on entire range', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('-- %s')
api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({
"line1",
"line2",
"line3",
})
testModule.comment_toggle(1, 3)
assert.stub(api_mock.nvim_call_function).was_called_with("setline", {1, {
"-- line1",
"-- line2",
"-- line3",
}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'<", {0, 1, 1, 0}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'>", {0, 3, 2147483647, 0}})
end)
it('Should remove left hand side comments only on entire range', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('-- %s')
api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({
"-- line1",
"-- line2",
"-- line3",
})
testModule.comment_toggle(1, 3)
assert.stub(api_mock.nvim_call_function).was_called_with("setline", {1, {
"line1",
"line2",
"line3",
}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'<", {0, 1, 1, 0}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'>", {0, 3, 2147483647, 0}})
end)
it('Should add comments on uncommented lines to entire range', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('-- %s')
api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({
"line1",
"-- line2",
"line3",
})
testModule.comment_toggle(1, 3)
assert.stub(api_mock.nvim_call_function).was_called_with("setline", {1, {
"-- line1",
"-- -- line2",
"-- line3",
}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'<", {0, 1, 1, 0}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'>", {0, 3, 2147483647, 0}})
end)
it('Should add left and right hand side comments to entire range', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('(*%s*)')
api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({
"line1",
"line2",
"line3",
})
testModule.comment_toggle(1, 3)
assert.stub(api_mock.nvim_call_function).was_called_with("setline", {1, {
"(* line1 *)",
"(* line2 *)",
"(* line3 *)",
}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'<", {0, 1, 1, 0}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'>", {0, 3, 2147483647, 0}})
end)
it('Should remove left and right hand side comments to entire range', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('(*%s*)')
api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({
"(* line1 *)",
"(* line2 *)",
"(* line3 *)",
})
testModule.comment_toggle(1, 3)
assert.stub(api_mock.nvim_call_function).was_called_with("setline", {1, {
"line1",
"line2",
"line3",
}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'<", {0, 1, 1, 0}})
assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {"'>", {0, 3, 2147483647, 0}})
end)
it('Should not do anything if commentstring not supported', function()
api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('whatwhat')
testModule.comment_toggle(1, 3)
assert.stub(api_mock.nvim_buf_get_lines).was_not_called()
assert.stub(api_mock.nvim_buf_set_lines).was_not_called()
end)
end)
end)
================================================
FILE: tests/comment_spec.lua
================================================
local function setUpBuffer(input, filetype)
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(buf, "filetype", filetype)
vim.api.nvim_command("buffer " .. buf)
vim.api.nvim_buf_set_lines(0, 0, -1, true, vim.split(input, "\n"))
end
local function goToLineRunKeys(line, feedkeys)
vim.api.nvim_win_set_cursor(0, { line, 0 })
local keys = vim.api.nvim_replace_termcodes(feedkeys, true, false, true)
vim.api.nvim_feedkeys(keys, "x", false)
end
local function getBufLines()
local result = vim.api.nvim_buf_get_lines(0, 0, vim.api.nvim_buf_line_count(0), false)
return result
end
local function runCommandAndAssert(line, feedkeys, expected)
goToLineRunKeys(line, feedkeys)
local result = getBufLines()
assert.are.same(vim.split(expected, "\n"), result)
end
describe("comment/uncomment", function()
local input = [[
local function dummy_func()
print("This is a dummy func")
end
local function another_dummy_func()
print("This is a another dummy func")
end
]]
before_each(function()
local testModule = require("nvim_comment")
testModule.setup({
marker_padding = true,
})
end)
after_each(function() end)
it("Should comment/uncomment line with dot repeatable", function()
local expected = [[
-- local function dummy_func()
print("This is a dummy func")
end
local function another_dummy_func()
print("This is a another dummy func")
end
]]
setUpBuffer(input, "lua")
-- comment
runCommandAndAssert(1, "gcc", expected)
-- uncomment
runCommandAndAssert(1, "gcc", input)
-- comment, via dot
runCommandAndAssert(1, ".", expected)
end)
it("Should comment/uncomment via motion and dot", function()
local expected = [[
-- local function dummy_func()
print("This is a dummy func")
end
local function another_dummy_func()
print("This is a another dummy func")
end
]]
setUpBuffer(input, "lua")
-- comment
runCommandAndAssert(1, "gcl", expected)
-- uncomment
runCommandAndAssert(1, "gcl", input)
-- comment, via dot
runCommandAndAssert(1, ".", expected)
end)
it("Should comment/uncomment motion with count and dot", function()
local expected = [[
-- local function dummy_func()
-- print("This is a dummy func")
-- end
local function another_dummy_func()
print("This is a another dummy func")
end
]]
setUpBuffer(input, "lua")
-- comment
runCommandAndAssert(1, "gc2j", expected)
-- uncomment
runCommandAndAssert(1, "gc2j", input)
-- comment, via dot
runCommandAndAssert(1, ".", expected)
end)
it("Should comment out another pararaph via dot", function()
local first_expected = [[
-- local function dummy_func()
-- print("This is a dummy func")
-- end
local function another_dummy_func()
print("This is a another dummy func")
end
]]
local second_expected = [[
-- local function dummy_func()
-- print("This is a dummy func")
-- end
-- local function another_dummy_func()
-- print("This is a another dummy func")
-- end
]]
setUpBuffer(input, "lua")
-- comment
runCommandAndAssert(2, "gcip", first_expected)
-- comment, via dot
runCommandAndAssert(7, ".", second_expected)
-- uncomment, via dot
runCommandAndAssert(7, "gcip", first_expected)
end)
end)
describe("padding flag", function()
local input = [[
<note>
<to>Tove</to>
<from>Jani</from>
</note>
]]
it("Should add padding", function()
require("nvim_comment").setup({ marker_padding = true })
local expected = [[
<note>
<to>Tove</to>
<!-- <from>Jani</from> -->
</note>
]]
setUpBuffer(input, "xml")
-- comment
runCommandAndAssert(3, "gcl", expected)
end)
it("Should not add padding", function()
require("nvim_comment").setup({ marker_padding = false })
local expected = [[
<note>
<to>Tove</to>
<!--<from>Jani</from>-->
</note>
]]
setUpBuffer(input, "xml")
-- comment
runCommandAndAssert(3, "gcl", expected)
end)
end)
describe("mapping with leader as space", function()
local input = [[
<note>
<to>Tove</to>
<from>Jani</from>
</note>
]]
it("Should toggle comment with line mapping", function()
require("nvim_comment").setup({
line_mapping = " cc",
operator_mapping = " c",
})
local expected = [[
<note>
<to>Tove</to>
<!--<from>Jani</from>-->
</note>
]]
setUpBuffer(input, "xml")
-- comment
runCommandAndAssert(3, " cc", expected)
end)
it("Should toggle comment with operator mapping", function()
require("nvim_comment").setup({
line_mapping = " cc",
operator_mapping = " c",
})
local expected = [[
<note>
<to>Tove</to>
<!-- <from>Jani</from>-->
<!--</note>-->
]]
setUpBuffer(input, "xml")
-- comment
runCommandAndAssert(3, " c1j", expected)
end)
end)
describe("comment empty flag", function()
local input = [[
local function dummy_func()
print("This is a dummy func")
end
local function another_dummy_func()
print("This is a another dummy func")
end]]
it("Should comment empty lines", function()
require("nvim_comment").setup({
marker_padding = true,
comment_empty = true,
})
-- luacheck: ignore
local expected = [[
-- local function dummy_func()
-- print("This is a dummy func")
-- end
--
-- local function another_dummy_func()
-- print("This is a another dummy func")
-- end]]
setUpBuffer(input, "lua")
-- comment
runCommandAndAssert(1, "vGgc", expected)
-- uncomment
runCommandAndAssert(1, "vGgc", input)
end)
it("Should not comment empty lines", function()
require("nvim_comment").setup({
marker_padding = true,
comment_empty = false,
})
local expected = [[
-- local function dummy_func()
-- print("This is a dummy func")
-- end
-- local function another_dummy_func()
-- print("This is a another dummy func")
-- end]]
setUpBuffer(input, "lua")
-- comment
runCommandAndAssert(1, "vGgc", expected)
-- comment, via dot
runCommandAndAssert(1, "vGgc", input)
end)
end)
describe("comment chunks", function()
before_each(function()
local testModule = require("nvim_comment")
testModule.setup({
marker_padding = true,
comment_empty = true,
})
end)
local input = [[
-- local foo = 'foo'
-- local bar = 'bar'
-- local baz = 'baz'
local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
-- local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
-- local foo = 'foo'
-- local bar = 'bar']]
it("Should handle text obeject at top of buffer", function()
local expected = [[
local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
-- local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
-- local foo = 'foo'
-- local bar = 'bar']]
setUpBuffer(input, "lua")
runCommandAndAssert(1, "gcic", expected)
end)
it("Should handle text obeject at botton of buffer", function()
local expected = [[
-- local foo = 'foo'
-- local bar = 'bar'
-- local baz = 'baz'
local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
-- local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
local foo = 'foo'
local bar = 'bar']]
setUpBuffer(input, "lua")
runCommandAndAssert(1, "Ggcic", expected)
end)
it("Should handle single line text object", function()
local expected = [[
-- local foo = 'foo'
-- local bar = 'bar'
-- local baz = 'baz'
local foo = 'foo'
local baz = 'baz'
local baz = 'baz'
-- local foo = 'foo'
-- local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
-- local foo = 'foo'
-- local bar = 'bar']]
setUpBuffer(input, "lua")
runCommandAndAssert(6, "dic", expected)
end)
it("Should handle multi line text object", function()
local expected = [[
-- local foo = 'foo'
-- local bar = 'bar'
-- local baz = 'baz'
local foo = 'foo'
local baz = 'baz'
-- local foo = 'foo'
local baz = 'baz'
local bar = 'bar'
local baz = 'baz'
-- local foo = 'foo'
-- local bar = 'bar']]
setUpBuffer(input, "lua")
runCommandAndAssert(9, "dic", expected)
end)
end)
describe("issues", function()
before_each(function()
local testModule = require("nvim_comment")
testModule.setup({
marker_padding = true,
comment_empty = true,
})
end)
it("issue 22", function()
local input = [[
local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
]]
local expected = [[
-- local foo = 'foo'
-- local bar = 'bar'
-- local baz = 'baz'
local foo = 'foo'
local bar = 'bar'
local baz = 'baz'
]]
setUpBuffer(input, "lua")
runCommandAndAssert(1, "gg0<c-v>jjgc", expected)
end)
it("issue 31", function()
local input = [[
this is some block
of idented text
with empty lines
yeah]]
local expected = [[
this is some block
-- of idented text
--
with empty lines
yeah]]
setUpBuffer(input, "lua")
runCommandAndAssert(1, "ggjVjgc", expected)
end)
end)
================================================
FILE: tests/minimal_init.vim
================================================
set rtp+=.
set rtp+=../plenary.nvim/
set hidden
runtime! plugin/plenary.vim
gitextract_xu0egi6i/
├── .github/
│ └── workflows/
│ └── make.yml
├── .luacheckrc
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── lua/
│ ├── nvim_comment.lua
│ └── spec/
│ └── nvim_comment_spec.lua
└── tests/
├── comment_spec.lua
└── minimal_init.vim
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (39K chars).
[
{
"path": ".github/workflows/make.yml",
"chars": 1041,
"preview": "name: Run Tests\n\non: [push, pull_request]\n\njobs:\n Tests:\n name: Linting and Test\n runs-on: ubuntu-20.04\n steps"
},
{
"path": ".luacheckrc",
"chars": 244,
"preview": "globals = {\"Snake\", \"Cell\", \"vim\", \"_TEST\", \"LOG_LEVEL\"}\nread_globals = {\n print = {\n fields = {\n revert = {}\n "
},
{
"path": "CHANGELOG.md",
"chars": 252,
"preview": "Changelog\n======================\n\n2021-07-10 - Add hook function to be called before reading `commentstring`\n2021-02-16 "
},
{
"path": "LICENSE",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2021 Alex Tylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "Makefile",
"chars": 565,
"preview": "all: lint test\n\nlint:\n\t@echo \"Linting...\"\n\tluacheck --no-color .\n\t@echo\n\nunit:\n\t@echo \"Run unit tests...\"\n\tnvim --headle"
},
{
"path": "README.md",
"chars": 5252,
"preview": "# nvim-comment\n\nToggle comments in Neovim, using built in `commentstring` filetype option;\nwritten in Lua. Without a dou"
},
{
"path": "lua/nvim_comment.lua",
"chars": 6376,
"preview": "local api = vim.api\n\nlocal M = {}\n\nM.config = {\n -- Linters prefer comment and line to have a space in between\n marker"
},
{
"path": "lua/spec/nvim_comment_spec.lua",
"chars": 12518,
"preview": "local testModule\nlocal mock = require('luassert.mock')\nlocal api_mock\n\ndescribe('nvim-comment', function()\n\n before_eac"
},
{
"path": "tests/comment_spec.lua",
"chars": 8772,
"preview": "local function setUpBuffer(input, filetype)\n\tlocal buf = vim.api.nvim_create_buf(false, true)\n\tvim.api.nvim_buf_set_opti"
},
{
"path": "tests/minimal_init.vim",
"chars": 78,
"preview": "\nset rtp+=.\nset rtp+=../plenary.nvim/\nset hidden\n\nruntime! plugin/plenary.vim\n"
}
]
About this extraction
This page contains the full source code of the terrortylor/nvim-comment GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (35.3 KB), approximately 10.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.