[
  {
    "path": ".github/workflows/make.yml",
    "content": "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:\n      - uses: actions/checkout@v2\n\n      - name: Prepare\n        run: |\n          # sudo apt-get update\n          sudo apt-get install lua-check\n          mkdir -p ~/.local/share/nvim/site/pack/vendor/start\n          git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim\n          ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start\n          mkdir -p build\n          wget https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage\n          chmod +x nvim.appimage\n          mv nvim.appimage ./build/nvim\n      - name: Run Lint\n        run: make lint\n\n      - name: Run unit tests\n        run: |\n          export PATH=\"${PWD}/build/:${PATH}\"\n          make unit\n          echo \"exit code $?\"\n\n      - name: Run tests\n        run: |\n          export PATH=\"${PWD}/build/:${PATH}\"\n          make gh-integration\n          echo \"exit code $?\"\n"
  },
  {
    "path": ".luacheckrc",
    "content": "globals = {\"Snake\", \"Cell\", \"vim\", \"_TEST\", \"LOG_LEVEL\"}\nread_globals = {\n  print = {\n    fields = {\n      revert = {}\n    }\n  },\n  os = {\n    fields = {\n      execute = {\n        fields = {\n          revert = {}\n        }\n      },\n    }\n  }\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Changelog\n======================\n\n2021-07-10 - Add hook function to be called before reading `commentstring`\n2021-02-16 - Fix issue with `gv` causing entire buffer range to be acted on\n2021-02-12 - Fix issue with motion acting on last visual selection\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Alex Tylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "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 --headless --noplugin -c 'packadd plenary.nvim' -c \"PlenaryBustedDirectory lua/spec\"\n\t@echo\n\ngh-integration:\n\t@echo \"Run integration tests...\"\n\tnvim --headless --noplugin -u tests/minimal_init.vim  -c \"PlenaryBustedDirectory tests  { minimal_init = './tests/minimal_init.vim' }\"\n\t@echo\n\nintegration:\n\t@echo \"Run integration tests...\"\n\tnvim --headless --noplugin -c 'packadd plenary.nvim' -c \"PlenaryBustedDirectory tests\"\n\t@echo\n\ntest: unit integration\n"
  },
  {
    "path": "README.md",
    "content": "# nvim-comment\n\nToggle comments in Neovim, using built in `commentstring` filetype option;\nwritten in Lua. Without a doubt this plugin **is not required** and is a rip off\nof [TPope's Commentary](https://github.com/tpope/vim-commentary) with less\nfeatures! What makes this plugin stand out over the numerous other comment\nplugins written in Lua are:\n\n- Comments each line, rather than adds block comments; making it easier to\n  toggle code when debugging\n- Uses the built in `commentstring` buffer option to define comment markers\n- Where a marker doesn't have a **space** character as padding this is added,\n  configurable (this can be disabled in the options, see below but is useful\n  when working with numerous linters)\n- Supports motions\n- Dot `.` repeatable\n\nWhen the plugin is called it works out the range to comment/uncomment; if all\nlines in the given range are commented then it uncomments, otherwise it comments\nthe range. This is useful when commenting a block out for testing with a real\nlike comment in it; as for the plugin a comment is a comment.\n\n## Usage\n\nEither use the command `CommentToggle`, e.g.:\n\n- `CommentToggle` comment/uncomment current line\n- `67,69CommentToggle` comment/uncomment a range\n- `'<,'>CommentToggle` comment/uncomment a visual selection\n\nOr use the default mappings:\n\n- `gcc` comment/uncomment current line, this does not take a count, if you want\n  a count use the `gc{count}{motion}`\n- `gc{motion}` comment/uncomment selection defined by a motion (as lines are\n  commented, any comment toggling actions will default to a linewise):\n  - `gcip` comment/uncomment a paragraph\n  - `gc4w` comment/uncomment current line\n  - `gc4j` comment/uncomment 4 lines below the current line\n  - `dic` delete comment block\n  - `gcic` uncomment commented block\n\n### Configure\n\nThe comment plugin needs to be initialised using:\n\n```lua\nrequire('nvim_comment').setup()\n```\n\nHowever you can pass in some config options, the defaults are:\n\n```lua\n{\n  -- Linters prefer comment and line to have a space in between markers\n  marker_padding = true,\n  -- should comment out empty or whitespace only lines\n  comment_empty = true,\n  -- trim empty comment whitespace\n  comment_empty_trim_whitespace = true,\n  -- Should key mappings be created\n  create_mappings = true,\n  -- Normal mode mapping left hand side\n  line_mapping = \"gcc\",\n  -- Visual/Operator mapping left hand side\n  operator_mapping = \"gc\",\n  -- text object mapping, comment chunk,,\n  comment_chunk_text_object = \"ic\",\n  -- Hook function to call before commenting takes place\n  hook = nil\n}\n```\n\n- Ignore Empty Lines\n\n```lua\nrequire('nvim_comment').setup({comment_empty = false})\n```\n\n- Don't trim trailing comment whitespace when commenting empty line\n```lua\nrequire('nvim_comment').setup({comment_empty_trim_whitespace = false})\n```\n\nThe default for this is `true`, meaning that a commented empty line will not\ncontain any whitespace. Most `commentstring` comment prefixes have some\nwhitespace padding, disable this to keep that padding on empty lines.\n\n- Disable mappings\n\n```lua\nrequire('nvim_comment').setup({create_mappings = false})\n```\n\n- Custom mappings\n\n```lua\nrequire('nvim_comment').setup({line_mapping = \"<leader>cl\", operator_mapping = \"<leader>c\", comment_chunk_text_object = \"ic\"})\n```\n\n- Disable marker padding\n\n```lua\nrequire('nvim_comment').setup({marker_padding = false})\n```\n\n- Hook function called before reading `commentstring`\n\nYou can run arbitrary function which will be called before plugin reads value of\n`commentstring`. This can be used to integrate with\n[JoosepAlviste/nvim-ts-context-commentstring](https://github.com/JoosepAlviste/nvim-ts-context-commentstring):\n\n```lua\nrequire('nvim_comment').setup({\n  hook = function()\n    if vim.api.nvim_buf_get_option(0, \"filetype\") == \"vue\" then\n      require(\"ts_context_commentstring.internal\").update_commentstring()\n    end\n  end\n})\n```\n\n- Changing/Setting `commentstring`\n\nIf you want to override the comment markers or add a new filetype just set the\n`commentstring` options:\n\n```lua\n-- Assumes this is being run in the context of the filetype...\nvim.api.nvim_buf_set_option(0, \"commentstring\", \"# %s\")\n```\n\nYou can also use an autocommand to automatically load your `commentstring` for\ncertain file types:\n\n```vim\n\" when you enter a (new) buffer\naugroup set-commentstring-ag\nautocmd!\nautocmd BufEnter *.cpp,*.h :lua vim.api.nvim_buf_set_option(0, \"commentstring\", \"// %s\")\n\" when you've changed the name of a file opened in a buffer, the file type may have changed\nautocmd BufFilePost *.cpp,*.h :lua vim.api.nvim_buf_set_option(0, \"commentstring\", \"// %s\")\naugroup END\n```\n\nOr add the comment string option in the relevant `filetype` file:\n\n```vim\nlet commentstring=\"# %s\"\n```\n\n```lua\nvim.api.nvim_buf_set_option(0, \"commentstring\", \"# %s\")\n```\n\n## Installation\n\nInstall just as you would a normal plugin, here are some options:\n\n### Built in package manager\n\n```bash\nmkdir -p ~/.local/share/nvim/site/pack/plugins/start\ncd ~/.local/share/nvim/site/pack/plugins/start\ngit clone https://github.com/terrortylor/nvim-comment\n```\n\n### Via a plugin manager\n\nUsing [packer.nvim](https://github.com/wbthomason/packer.nvim):\n\n```lua\nuse \"terrortylor/nvim-comment\"\nrequire('nvim_comment').setup()\n```\n"
  },
  {
    "path": "lua/nvim_comment.lua",
    "content": "local api = vim.api\n\nlocal M = {}\n\nM.config = {\n  -- Linters prefer comment and line to have a space in between\n  marker_padding = true,\n  -- should comment out empty or whitespace only lines\n  comment_empty = true,\n  -- trim empty comment whitespace\n  comment_empty_trim_whitespace = true,\n  -- Should key mappings be created\n  create_mappings = true,\n  -- Normal mode mapping left hand side\n  line_mapping = \"gcc\",\n  -- Visual/Operator mapping left hand side\n  operator_mapping = \"gc\",\n  -- text object mapping, comment chunk\n  comment_chunk_text_object = \"ic\",\n  -- Hook function to call before commenting takes place\n  hook = nil,\n}\n\nfunction M.get_comment_wrapper()\n  local cs = api.nvim_buf_get_option(0, \"commentstring\")\n\n  -- make sure comment string is understood\n  if cs:find(\"%%s\") then\n    local left, right = cs:match(\"^(.*)%%s(.*)\")\n    if right == \"\" then\n      right = nil\n    end\n\n    -- left comment markers should have padding as linterers preffer\n    if M.config.marker_padding then\n      if not left:match(\"%s$\") then\n        left = left .. \" \"\n      end\n      if right and not right:match(\"^%s\") then\n        right = \" \" .. right\n      end\n    end\n\n    return left, right\n  else\n    api.nvim_command('echom \"Commentstring not understood: ' .. cs .. '\"')\n  end\nend\n\nfunction M.comment_line(l, indent, left, right, comment_empty, comment_empty_trim_whitespace)\n  if not comment_empty and l:match(\"^%s*$\") then\n    return l\n  end\n\n  local line_empty = l:match(\"^%s*$\")\n\n  -- standarise indentation before adding\n  local line = l:gsub(\"^\" .. indent, \"\")\n  if right then\n    line = line .. right\n  end\n\n  local ret_line = indent .. left .. line\n  if comment_empty_trim_whitespace and line_empty then\n    return ret_line:gsub(\"^(.*)%s+$\", \"%1\")\n  end\n\n  return ret_line\nend\n\nfunction M.uncomment_line(l, left, right, comment_empty_trim_whitespace)\n  local line = l\n  if right and right ~= \"\" then\n    line = line:gsub(vim.pesc(right) .. \"$\", \"\")\n    return line:gsub(vim.pesc(left), \"\", 1)\n  end\n\n  if comment_empty_trim_whitespace and left:match(\"%s+$\") then\n    local left_nw = left:match(\"^(%S+)%s+$\")\n    if line:match(\"^%s*\" .. left_nw .. \"$\") then\n      return line:gsub(vim.pesc(left_nw), \"\", 1)\n    end\n  end\n\n  return line:gsub(vim.pesc(left), \"\", 1)\nend\n\nfunction M.operator(mode)\n  local line1, line2\n  if not mode then\n    line1 = api.nvim_win_get_cursor(0)[1]\n    line2 = line1\n  elseif mode:match(\"[vV\u0016]\") then\n    line1 = api.nvim_buf_get_mark(0, \"<\")[1]\n    line2 = api.nvim_buf_get_mark(0, \">\")[1]\n  else\n    line1 = api.nvim_buf_get_mark(0, \"[\")[1]\n    line2 = api.nvim_buf_get_mark(0, \"]\")[1]\n  end\n\n  M.comment_toggle(line1, line2)\nend\n\nfunction M.comment_toggle(line_start, line_end)\n  if type(M.config.hook) == \"function\" then\n    M.config.hook()\n  end\n\n  local left, right = M.get_comment_wrapper()\n  if not left or (not left and not right) then\n    return\n  end\n\n  local lines = api.nvim_buf_get_lines(0, line_start - 1, line_end, false)\n  if not lines then\n    return\n  end\n\n  -- check if any lines commented, capture indent\n  local esc_left = vim.pesc(left)\n  if M.config.comment_empty_trim_whitespace and left:match(\"%s+$\") then\n    local left_nw = left:match(\"^(%S+)%s+$\")\n    esc_left = vim.pesc(left_nw) .. \"%s*\"\n  end\n  local commented_lines_counter = 0\n  local empty_counter = 0\n  local indent\n\n  for _, v in pairs(lines) do\n    if v:find(\"^%s*\" .. esc_left) then\n      commented_lines_counter = commented_lines_counter + 1\n    elseif v:match(\"^%s*$\") then\n      empty_counter = empty_counter + 1\n    end\n\n    if not v:match(\"^%s*$\") then\n      local line_indent = v:match(\"^%s+\") or \"\"\n      if not indent or string.len(line_indent) < string.len(indent) then\n        indent = line_indent\n      end\n    end\n  end\n\n  for i, v in pairs(lines) do\n    if commented_lines_counter ~= (#lines - empty_counter) then\n      lines[i] = M.comment_line(\n        v,\n        indent,\n        left,\n        right,\n        M.config.comment_empty,\n        M.config.comment_empty_trim_whitespace\n      )\n    else\n      lines[i] = M.uncomment_line(v, left, right, M.config.comment_empty_trim_whitespace)\n    end\n  end\n  -- the LUA API doesn't seem to keep marks for lines that are changes with\n  -- nvim_buf_set_lines\n  api.nvim_call_function(\"setline\", {line_start, lines})\n\n  -- The lua call seems to clear the visual selection so reset it\n  -- 2147483647 is vimL built in\n  api.nvim_call_function(\"setpos\", { \"'<\", { 0, line_start, 1, 0 } })\n  api.nvim_call_function(\"setpos\", { \"'>\", { 0, line_end, 2147483647, 0 } })\nend\n\nfunction M.select_comment_chunk()\n  vim.cmd([[execute \"normal! \\<esc>\"]])\n  local up = vim.fn.search(\"\\\\v^(\\\\s*--)@!\", \"Wbcn\")\n  up = up + 1\n  local down = vim.fn.search(\"\\\\v^(\\\\s*--)@!\", \"Wzn\")\n  if down ~= 0 then\n    down = down - 1\n  end\n\n  local lines = vim.api.nvim_buf_line_count(0)\n  local pos = vim.api.nvim_win_get_cursor(0)[1]\n\n  if down == 0 then\n    down = lines\n  end\n\n  if up <= down and up <= pos and down >= pos then\n    vim.api.nvim_buf_set_mark(0, \"<\", up, 1, {})\n    vim.api.nvim_buf_set_mark(0, \">\", down, 1, {})\n    vim.cmd(\"normal! `<V`>\")\n  end\nend\n\nfunction M.setup(user_opts)\n  M.config = vim.tbl_extend(\"force\", M.config, user_opts or {})\n\n  -- Messy, change with nvim_exec once merged\n  local vim_func = [[\n  function! CommentOperator(type) abort\n    let reg_save = @@\n    execute \"lua require('nvim_comment').operator('\" . a:type. \"')\"\n    let @@ = reg_save\n  endfunction\n  ]]\n\n  vim.api.nvim_call_function(\"execute\", { vim_func })\n  vim.api.nvim_command(\"command! -range CommentToggle lua require('nvim_comment').comment_toggle(<line1>, <line2>)\")\n\n  if M.config.create_mappings then\n    local opts = { noremap = true }\n    api.nvim_set_keymap(\"n\", M.config.line_mapping, \"<Cmd>set operatorfunc=CommentOperator<CR>g@l\", opts)\n    api.nvim_set_keymap(\"n\", M.config.operator_mapping, \"<Cmd>set operatorfunc=CommentOperator<CR>g@\", opts)\n    api.nvim_set_keymap(\"x\", M.config.operator_mapping, \":<C-u>call CommentOperator(visualmode())<CR>\", opts)\n    api.nvim_set_keymap(\n      \"x\",\n      M.config.comment_chunk_text_object,\n      \"<Cmd>lua require('nvim_comment').select_comment_chunk()<CR>\",\n      opts\n    )\n    api.nvim_set_keymap(\n      \"o\",\n      M.config.comment_chunk_text_object,\n      \"<Cmd>lua require('nvim_comment').select_comment_chunk()<CR>\",\n      opts\n    )\n  end\nend\n\nreturn M\n"
  },
  {
    "path": "lua/spec/nvim_comment_spec.lua",
    "content": "local testModule\nlocal mock = require('luassert.mock')\nlocal api_mock\n\ndescribe('nvim-comment', function()\n\n  before_each(function()\n    testModule = require('nvim_comment')\n    api_mock = mock(vim.api, true)\n    -- reset config to default\n    testModule.config = {\n      marker_padding = true\n    }\n  end)\n\n  after_each(function()\n    mock.revert(api_mock)\n  end)\n\n  describe('get_comment_wrapper', function()\n    local commentstrings = {\n      ['COMMENT %s'] = {'COMMENT ', nil},\n      ['{% comment %}%s{% endcomment %}'] = {'{% comment %}', '{% endcomment %}'},\n      ['c# %s'] = {'c# ', nil},\n      ['dnl %s'] = {'dnl ', nil},\n      ['NB. %s'] = {'NB. ', nil},\n      ['! %s'] = {'! ', nil},\n      ['#%s'] = {'#', nil},\n      ['# %s'] = {'# ', nil},\n      ['%%s'] = {'%', nil},\n      ['% %s'] = {'% ', nil},\n      ['(*%s*)'] = {'(*', '*)'},\n      ['(;%s;)'] = {'(;', ';)'},\n      ['**%s'] = {'**', nil},\n      ['-# %s'] = {'-# ', nil},\n      ['-- %s'] = {'-- ', nil},\n      ['--  %s'] = {'--  ', nil},\n      ['.. %s'] = {'.. ', nil},\n      ['.\\\\\"%s'] = {'.\\\\\"', nil},\n      ['/*%s*/'] = {'/*', '*/'},\n      ['/* %s */'] = {'/* ', ' */'},\n      ['//%s'] = {'//', nil},\n      ['// %s'] = {'// ', nil},\n      [':: %s'] = {':: ', nil},\n      [';%s'] = {';', nil},\n      ['; %s'] = {'; ', nil},\n      ['; // %s'] = {'; // ', nil},\n      ['<!--%s-->'] = {'<!--', '-->'},\n      ['<%#%s%>'] = {'<%#', '%>'},\n      ['> %s'] = {'> ', nil},\n      ['      *%s'] = {'      *', nil},\n      ['\"%s'] = {'\"', nil},\n    }\n\n    for string,expected in pairs(commentstrings) do\n      it('Should return comment wrapper(s) for: ' .. string .. '  no left marker padding', function()\n        testModule.config.marker_padding = false\n        api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns(string)\n        local left, right = testModule.get_comment_wrapper(string)\n\n        assert.equals(left, expected[1])\n        assert.equals(right, expected[2])\n      end)\n    end\n\n    local padded_commentstrings = {\n      ['COMMENT %s'] = {'COMMENT ', nil},\n      ['{% comment %}%s{% endcomment %}'] = {'{% comment %} ', ' {% endcomment %}'},\n      ['c# %s'] = {'c# ', nil},\n      ['dnl %s'] = {'dnl ', nil},\n      ['NB. %s'] = {'NB. ', nil},\n      ['! %s'] = {'! ', nil},\n      ['#%s'] = {'# ', nil},\n      ['# %s'] = {'# ', nil},\n      ['%%s'] = {'% ', nil},\n      ['% %s'] = {'% ', nil},\n      ['(*%s*)'] = {'(* ', ' *)'},\n      ['(;%s;)'] = {'(; ', ' ;)'},\n      ['**%s'] = {'** ', nil},\n      ['-# %s'] = {'-# ', nil},\n      ['-- %s'] = {'-- ', nil},\n      ['--  %s'] = {'--  ', nil},\n      ['.. %s'] = {'.. ', nil},\n      ['.\\\\\"%s'] = {'.\\\\\" ', nil},\n      ['/*%s*/'] = {'/* ', ' */'},\n      ['/* %s */'] = {'/* ', ' */'},\n      ['//%s'] = {'// ', nil},\n      ['// %s'] = {'// ', nil},\n      [':: %s'] = {':: ', nil},\n      [';%s'] = {'; ', nil},\n      ['; %s'] = {'; ', nil},\n      ['; // %s'] = {'; // ', nil},\n      ['<!--%s-->'] = {'<!-- ', ' -->'},\n      ['<%#%s%>'] = {'<%# ', ' %>'},\n      ['> %s'] = {'> ', nil},\n      ['      *%s'] = {'      * ', nil},\n      ['\"%s'] = {'\" ', nil},\n    }\n\n    for string,expected in pairs(padded_commentstrings) do\n      it('Should return comment wrapper(s) for: ' .. string .. ' with marker padding', function()\n        api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns(string)\n        local left, right = testModule.get_comment_wrapper(string)\n\n        assert.equals(left, expected[1])\n        assert.equals(right, expected[2])\n      end)\n    end\n\n    it('Should return nil,nil if unsuported commentstring', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('something here')\n      local left, right = testModule.get_comment_wrapper('something here')\n\n      assert.equals(left, nil)\n      assert.equals(right, nil)\n    end)\n  end)\n\n  describe('comment_line', function()\n    local commentstrings = {\n      ['COMMENT line'] = {'COMMENT ', ''},\n      ['{% comment %}line{% endcomment %}'] = {'{% comment %}', '{% endcomment %}'},\n      ['c# line'] = {'c# ', ''},\n      ['dnl line'] = {'dnl ', ''},\n      ['NB. line'] = {'NB. ', ''},\n      ['! line'] = {'! ', ''},\n      ['#line'] = {'#', ''},\n      ['# line'] = {'# ', ''},\n      ['%line'] = {'%', ''},\n      ['% line'] = {'% ', ''},\n      ['(*line*)'] = {'(*', '*)'},\n      ['(;line;)'] = {'(;', ';)'},\n      ['**line'] = {'**', ''},\n      ['-# line'] = {'-# ', ''},\n      ['-- line'] = {'-- ', ''},\n      ['--  line'] = {'--  ', ''},\n      ['.. line'] = {'.. ', ''},\n      ['.\\\\\"line'] = {'.\\\\\"', ''},\n      ['/*line*/'] = {'/*', '*/'},\n      ['/* line */'] = {'/* ', ' */'},\n      ['//line'] = {'//', ''},\n      ['// line'] = {'// ', ''},\n      [':: line'] = {':: ', ''},\n      [';line'] = {';', ''},\n      ['; line'] = {'; ', ''},\n      ['; // line'] = {'; // ', ''},\n      ['<!--line-->'] = {'<!--', '-->'},\n      ['<%#line%>'] = {'<%#', '%>'},\n      ['> line'] = {'> ', ''},\n      ['      *line'] = {'      *', ''},\n      ['\"line'] = {'\"', ''},\n    }\n\n    for expected,comment_parts in pairs(commentstrings) do\n      it('Should comment line as expected, with no padding: ' .. expected, function()\n        local actual = testModule.comment_line('line', \"\", comment_parts[1], comment_parts[2], true, true)\n\n        assert.equals(expected, actual)\n      end)\n    end\n\n    it(\"Should add comment after any whitespace, with padding\", function()\n      local actual = testModule.comment_line(\"  line\", \"  \",  \"-- \", \"\", true, true)\n\n      assert.equals(\"  -- line\", actual)\n    end)\n\n    it(\"Should add comment after any whitespace, with extra padding\", function()\n      local actual = testModule.comment_line(\"    line\", \"  \",  \"-- \", \"\", true, true)\n\n      assert.equals(\"  --   line\", actual)\n    end)\n\n    it(\"Should trim whitespace\", function()\n      local actual = testModule.comment_line(\"\", \"  \",  \"-- \", \"\", true, true)\n\n      assert.equals(\"  --\", actual)\n    end)\n\n    it(\"Should not trim whitespace\", function()\n      local actual = testModule.comment_line(\"\", \"  \",  \"-- \", \"\", true, false)\n\n      assert.equals(\"  -- \", actual)\n    end)\n\n    it(\"Should ignore line if empty or just whitespace\", function()\n      local actual = testModule.comment_line(\"    line\", \"  \",  \"-- \", \"\", false, true)\n\n      assert.equals(\"  --   line\", actual)\n\n      actual = testModule.comment_line(\"\", \"  \",  \"-- \", \"\", false, true)\n\n      assert.equals(\"\", actual)\n\n      -- spaces\n      actual = testModule.comment_line(\"  \", \"  \",  \"-- \", \"\", false, true)\n\n      assert.equals(\"  \", actual)\n\n      -- Tabs\n      actual = testModule.comment_line(\"   \", \"  \",  \"-- \", \"\", false, true)\n\n      assert.equals(\"   \", actual)\n    end)\n  end)\n\n  describe('uncomment_line', function()\n    local commentstrings = {\n      ['COMMENT line'] = {'COMMENT ', ''},\n      ['{% comment %}line{% endcomment %}'] = {'{% comment %}', '{% endcomment %}'},\n      ['c# line'] = {'c# ', ''},\n      ['dnl line'] = {'dnl ', ''},\n      ['NB. line'] = {'NB. ', ''},\n      ['! line'] = {'! ', ''},\n      ['#line'] = {'#', ''},\n      ['# line'] = {'# ', ''},\n      ['%line'] = {'%', ''},\n      ['% line'] = {'% ', ''},\n      ['(*line*)'] = {'(*', '*)'},\n      ['(;line;)'] = {'(;', ';)'},\n      ['**line'] = {'**', ''},\n      ['-# line'] = {'-# ', ''},\n      ['-- line'] = {'-- ', ''},\n      ['--  line'] = {'--  ', ''},\n      ['.. line'] = {'.. ', ''},\n      ['.\\\\\"line'] = {'.\\\\\"', ''},\n      ['/*line*/'] = {'/*', '*/'},\n      ['/* line */'] = {'/* ', ' */'},\n      ['//line'] = {'//', ''},\n      ['// line'] = {'// ', ''},\n      [':: line'] = {':: ', ''},\n      [';line'] = {';', ''},\n      ['; line'] = {'; ', ''},\n      ['; // line'] = {'; // ', ''},\n      ['<!--line-->'] = {'<!--', '-->'},\n      ['<%#line%>'] = {'<%#', '%>'},\n      ['> line'] = {'> ', ''},\n      -- ['      *line'] = {'      *', ''},\n      ['\"line'] = {'\"', ''},\n    }\n\n    for input,comment_parts in pairs(commentstrings) do\n      it('Should uncomment line as expected: ' .. input, function()\n        local actual = testModule.uncomment_line(input, comment_parts[1], comment_parts[2], false)\n\n        assert.equals('line', actual)\n      end)\n    end\n\n    it('Should uncomment if trailing whitespace in left hand side removed, no right hand side', function()\n        local actual = testModule.uncomment_line(\"--\", \"-- \", \"\", true)\n\n        assert.equals('', actual)\n    end)\n\n    it('Should uncomment and not leave padding when comment_empty_trim_whitespace false, no right hand side', function()\n        local actual = testModule.uncomment_line(\"-- test\", \"-- \", \"\", true)\n\n        assert.equals('test', actual)\n    end)\n  end)\n\n  describe('comment_toggle', function()\n    it('Should add left hand side comments only on entire range', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('-- %s')\n      api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({\n        \"line1\",\n        \"line2\",\n        \"line3\",\n      })\n\n      testModule.comment_toggle(1, 3)\n\n      assert.stub(api_mock.nvim_call_function).was_called_with(\"setline\", {1, {\n        \"-- line1\",\n        \"-- line2\",\n        \"-- line3\",\n      }})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'<\", {0, 1, 1, 0}})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'>\", {0, 3, 2147483647, 0}})\n\n    end)\n\n    it('Should remove left hand side comments only on entire range', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('-- %s')\n      api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({\n        \"-- line1\",\n        \"-- line2\",\n        \"-- line3\",\n      })\n\n      testModule.comment_toggle(1, 3)\n\n      assert.stub(api_mock.nvim_call_function).was_called_with(\"setline\", {1, {\n        \"line1\",\n        \"line2\",\n        \"line3\",\n      }})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'<\", {0, 1, 1, 0}})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'>\", {0, 3, 2147483647, 0}})\n    end)\n\n    it('Should add comments on uncommented lines to entire range', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('-- %s')\n      api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({\n        \"line1\",\n        \"-- line2\",\n        \"line3\",\n      })\n\n      testModule.comment_toggle(1, 3)\n\n      assert.stub(api_mock.nvim_call_function).was_called_with(\"setline\", {1, {\n        \"-- line1\",\n        \"-- -- line2\",\n        \"-- line3\",\n      }})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'<\", {0, 1, 1, 0}})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'>\", {0, 3, 2147483647, 0}})\n    end)\n\n    it('Should add left and right hand side comments to entire range', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('(*%s*)')\n      api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({\n        \"line1\",\n        \"line2\",\n        \"line3\",\n      })\n\n      testModule.comment_toggle(1, 3)\n\n      assert.stub(api_mock.nvim_call_function).was_called_with(\"setline\", {1, {\n        \"(* line1 *)\",\n        \"(* line2 *)\",\n        \"(* line3 *)\",\n      }})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'<\", {0, 1, 1, 0}})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'>\", {0, 3, 2147483647, 0}})\n    end)\n\n    it('Should remove left and right hand side comments to entire range', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('(*%s*)')\n      api_mock.nvim_buf_get_lines.on_call_with(0, 0, 3, false).returns({\n        \"(* line1 *)\",\n        \"(* line2 *)\",\n        \"(* line3 *)\",\n      })\n\n      testModule.comment_toggle(1, 3)\n\n      assert.stub(api_mock.nvim_call_function).was_called_with(\"setline\", {1, {\n        \"line1\",\n        \"line2\",\n        \"line3\",\n      }})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'<\", {0, 1, 1, 0}})\n      assert.stub(api_mock.nvim_call_function).was_called_with('setpos', {\"'>\", {0, 3, 2147483647, 0}})\n    end)\n\n    it('Should not do anything if commentstring not supported', function()\n      api_mock.nvim_buf_get_option.on_call_with(0, 'commentstring').returns('whatwhat')\n\n      testModule.comment_toggle(1, 3)\n\n      assert.stub(api_mock.nvim_buf_get_lines).was_not_called()\n      assert.stub(api_mock.nvim_buf_set_lines).was_not_called()\n    end)\n  end)\nend)\n"
  },
  {
    "path": "tests/comment_spec.lua",
    "content": "local function setUpBuffer(input, filetype)\n\tlocal buf = vim.api.nvim_create_buf(false, true)\n\tvim.api.nvim_buf_set_option(buf, \"filetype\", filetype)\n\tvim.api.nvim_command(\"buffer \" .. buf)\n\n\tvim.api.nvim_buf_set_lines(0, 0, -1, true, vim.split(input, \"\\n\"))\nend\n\nlocal function goToLineRunKeys(line, feedkeys)\n\tvim.api.nvim_win_set_cursor(0, { line, 0 })\n\tlocal keys = vim.api.nvim_replace_termcodes(feedkeys, true, false, true)\n\tvim.api.nvim_feedkeys(keys, \"x\", false)\nend\nlocal function getBufLines()\n\tlocal result = vim.api.nvim_buf_get_lines(0, 0, vim.api.nvim_buf_line_count(0), false)\n\treturn result\nend\n\nlocal function runCommandAndAssert(line, feedkeys, expected)\n\tgoToLineRunKeys(line, feedkeys)\n\tlocal result = getBufLines()\n\tassert.are.same(vim.split(expected, \"\\n\"), result)\nend\n\ndescribe(\"comment/uncomment\", function()\n\tlocal input = [[\nlocal function dummy_func()\n  print(\"This is a dummy func\")\nend\n\nlocal function another_dummy_func()\n  print(\"This is a another dummy func\")\nend\n]]\n\n\tbefore_each(function()\n\t\tlocal testModule = require(\"nvim_comment\")\n\t\ttestModule.setup({\n\t\t\tmarker_padding = true,\n\t\t})\n\tend)\n\n\tafter_each(function() end)\n\n\tit(\"Should comment/uncomment line with dot repeatable\", function()\n\t\tlocal expected = [[\n-- local function dummy_func()\n  print(\"This is a dummy func\")\nend\n\nlocal function another_dummy_func()\n  print(\"This is a another dummy func\")\nend\n]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\t-- comment\n\t\trunCommandAndAssert(1, \"gcc\", expected)\n\t\t-- uncomment\n\t\trunCommandAndAssert(1, \"gcc\", input)\n\t\t-- comment, via dot\n\t\trunCommandAndAssert(1, \".\", expected)\n\tend)\n\n\tit(\"Should comment/uncomment via motion and dot\", function()\n\t\tlocal expected = [[\n-- local function dummy_func()\n  print(\"This is a dummy func\")\nend\n\nlocal function another_dummy_func()\n  print(\"This is a another dummy func\")\nend\n]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\t-- comment\n\t\trunCommandAndAssert(1, \"gcl\", expected)\n\t\t-- uncomment\n\t\trunCommandAndAssert(1, \"gcl\", input)\n\t\t-- comment, via dot\n\t\trunCommandAndAssert(1, \".\", expected)\n\tend)\n\n\tit(\"Should comment/uncomment motion with count and dot\", function()\n\t\tlocal expected = [[\n-- local function dummy_func()\n--   print(\"This is a dummy func\")\n-- end\n\nlocal function another_dummy_func()\n  print(\"This is a another dummy func\")\nend\n]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\t-- comment\n\t\trunCommandAndAssert(1, \"gc2j\", expected)\n\t\t-- uncomment\n\t\trunCommandAndAssert(1, \"gc2j\", input)\n\t\t-- comment, via dot\n\t\trunCommandAndAssert(1, \".\", expected)\n\tend)\n\n\tit(\"Should comment out another pararaph via dot\", function()\n\t\tlocal first_expected = [[\n-- local function dummy_func()\n--   print(\"This is a dummy func\")\n-- end\n\nlocal function another_dummy_func()\n  print(\"This is a another dummy func\")\nend\n]]\n\n\t\tlocal second_expected = [[\n-- local function dummy_func()\n--   print(\"This is a dummy func\")\n-- end\n\n-- local function another_dummy_func()\n--   print(\"This is a another dummy func\")\n-- end\n]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\t-- comment\n\t\trunCommandAndAssert(2, \"gcip\", first_expected)\n\t\t-- comment, via dot\n\t\trunCommandAndAssert(7, \".\", second_expected)\n\t\t-- uncomment, via dot\n\t\trunCommandAndAssert(7, \"gcip\", first_expected)\n\tend)\nend)\n\ndescribe(\"padding flag\", function()\n\tlocal input = [[\n<note>\n  <to>Tove</to>\n  <from>Jani</from>\n</note>\n]]\n\n\tit(\"Should add padding\", function()\n\t\trequire(\"nvim_comment\").setup({ marker_padding = true })\n\n\t\tlocal expected = [[\n<note>\n  <to>Tove</to>\n  <!-- <from>Jani</from> -->\n</note>\n]]\n\n\t\tsetUpBuffer(input, \"xml\")\n\t\t-- comment\n\t\trunCommandAndAssert(3, \"gcl\", expected)\n\tend)\n\tit(\"Should not add padding\", function()\n\t\trequire(\"nvim_comment\").setup({ marker_padding = false })\n\n\t\tlocal expected = [[\n<note>\n  <to>Tove</to>\n  <!--<from>Jani</from>-->\n</note>\n]]\n\n\t\tsetUpBuffer(input, \"xml\")\n\t\t-- comment\n\t\trunCommandAndAssert(3, \"gcl\", expected)\n\tend)\nend)\n\ndescribe(\"mapping with leader as space\", function()\n\tlocal input = [[\n<note>\n  <to>Tove</to>\n  <from>Jani</from>\n</note>\n]]\n\n\tit(\"Should toggle comment with line mapping\", function()\n\t\trequire(\"nvim_comment\").setup({\n\t\t\tline_mapping = \" cc\",\n\t\t\toperator_mapping = \" c\",\n\t\t})\n\n\t\tlocal expected = [[\n<note>\n  <to>Tove</to>\n  <!--<from>Jani</from>-->\n</note>\n]]\n\n\t\tsetUpBuffer(input, \"xml\")\n\t\t-- comment\n\t\trunCommandAndAssert(3, \" cc\", expected)\n\tend)\n\n\tit(\"Should toggle comment with operator mapping\", function()\n\t\trequire(\"nvim_comment\").setup({\n\t\t\tline_mapping = \" cc\",\n\t\t\toperator_mapping = \" c\",\n\t\t})\n\n\t\tlocal expected = [[\n<note>\n  <to>Tove</to>\n<!--  <from>Jani</from>-->\n<!--</note>-->\n]]\n\n\t\tsetUpBuffer(input, \"xml\")\n\t\t-- comment\n\t\trunCommandAndAssert(3, \" c1j\", expected)\n\tend)\nend)\n\ndescribe(\"comment empty flag\", function()\n\tlocal input = [[\nlocal function dummy_func()\n  print(\"This is a dummy func\")\nend\n\nlocal function another_dummy_func()\n  print(\"This is a another dummy func\")\nend]]\n\n\tit(\"Should comment empty lines\", function()\n\t\trequire(\"nvim_comment\").setup({\n\t\t\tmarker_padding = true,\n\t\t\tcomment_empty = true,\n\t\t})\n\n\t\t-- luacheck: ignore\n\t\tlocal expected = [[\n-- local function dummy_func()\n--   print(\"This is a dummy func\")\n-- end\n--\n-- local function another_dummy_func()\n--   print(\"This is a another dummy func\")\n-- end]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\t-- comment\n\t\trunCommandAndAssert(1, \"vGgc\", expected)\n\t\t-- uncomment\n\t\trunCommandAndAssert(1, \"vGgc\", input)\n\tend)\n\n\tit(\"Should not comment empty lines\", function()\n\t\trequire(\"nvim_comment\").setup({\n\t\t\tmarker_padding = true,\n\t\t\tcomment_empty = false,\n\t\t})\n\n\t\tlocal expected = [[\n-- local function dummy_func()\n--   print(\"This is a dummy func\")\n-- end\n\n-- local function another_dummy_func()\n--   print(\"This is a another dummy func\")\n-- end]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\t-- comment\n\t\trunCommandAndAssert(1, \"vGgc\", expected)\n\t\t-- comment, via dot\n\t\trunCommandAndAssert(1, \"vGgc\", input)\n\tend)\nend)\n\ndescribe(\"comment chunks\", function()\n\tbefore_each(function()\n\t\tlocal testModule = require(\"nvim_comment\")\n\t\ttestModule.setup({\n\t\t\tmarker_padding = true,\n\t\t\tcomment_empty = true,\n\t\t})\n\tend)\n\n\tlocal input = [[\n-- local foo = 'foo'\n-- local bar = 'bar'\n-- local baz = 'baz'\nlocal foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local bar = 'bar']]\n\n\tit(\"Should handle text obeject at top of buffer\", function()\n\t\tlocal expected = [[\nlocal foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\nlocal foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local bar = 'bar']]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\trunCommandAndAssert(1, \"gcic\", expected)\n\tend)\n\n\tit(\"Should handle text obeject at botton of buffer\", function()\n\t\tlocal expected = [[\n-- local foo = 'foo'\n-- local bar = 'bar'\n-- local baz = 'baz'\nlocal foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\nlocal foo = 'foo'\nlocal bar = 'bar']]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\trunCommandAndAssert(1, \"Ggcic\", expected)\n\tend)\n\n\tit(\"Should handle single line text object\", function()\n\t\tlocal expected = [[\n-- local foo = 'foo'\n-- local bar = 'bar'\n-- local baz = 'baz'\nlocal foo = 'foo'\nlocal baz = 'baz'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local bar = 'bar']]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\trunCommandAndAssert(6, \"dic\", expected)\n\tend)\n\n\tit(\"Should handle multi line text object\", function()\n\t\tlocal expected = [[\n-- local foo = 'foo'\n-- local bar = 'bar'\n-- local baz = 'baz'\nlocal foo = 'foo'\nlocal baz = 'baz'\n-- local foo = 'foo'\nlocal baz = 'baz'\nlocal bar = 'bar'\nlocal baz = 'baz'\n-- local foo = 'foo'\n-- local bar = 'bar']]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\trunCommandAndAssert(9, \"dic\", expected)\n\tend)\nend)\n\ndescribe(\"issues\", function()\n\tbefore_each(function()\n\t\tlocal testModule = require(\"nvim_comment\")\n\t\ttestModule.setup({\n\t\t\tmarker_padding = true,\n\t\t\tcomment_empty = true,\n\t\t})\n\tend)\n\n\tit(\"issue 22\", function()\n\t\tlocal input = [[\nlocal foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\nlocal foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\n]]\n\t\tlocal expected = [[\n-- local foo = 'foo'\n-- local bar = 'bar'\n-- local baz = 'baz'\nlocal foo = 'foo'\nlocal bar = 'bar'\nlocal baz = 'baz'\n]]\n\n\t\tsetUpBuffer(input, \"lua\")\n\t\trunCommandAndAssert(1, \"gg0<c-v>jjgc\", expected)\n\tend)\n\n\tit(\"issue 31\", function()\n\t\tlocal input = [[\nthis is some block\n  of idented text\n\n  with empty lines\nyeah]]\n\n\t\tlocal expected = [[\nthis is some block\n  -- of idented text\n  --\n  with empty lines\nyeah]]\n\t\tsetUpBuffer(input, \"lua\")\n\t\trunCommandAndAssert(1, \"ggjVjgc\", expected)\n\tend)\nend)\n"
  },
  {
    "path": "tests/minimal_init.vim",
    "content": "\nset rtp+=.\nset rtp+=../plenary.nvim/\nset hidden\n\nruntime! plugin/plenary.vim\n"
  }
]