mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	Problem: Currently comment detection, addition, and removal are done
  by matching 'commentstring' exactly. This has the downside when users
  want to add comment markers with space (like with `-- %s`
  commentstring) but also be able to uncomment lines that do not contain
  space (like `--aaa`).
Solution: Use the following approach:
  - Line is commented if it matches 'commentstring' with trimmed parts.
  - Adding comment is 100% relying on 'commentstring' parts (as is now).
  - Removing comment is first trying exact 'commentstring' parts with
    fallback on trying its trimmed parts.
		
	
		
			
				
	
	
		
			652 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			652 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local t = require('test.testutil')
 | 
						|
local n = require('test.functional.testnvim')()
 | 
						|
 | 
						|
local api = n.api
 | 
						|
local clear = n.clear
 | 
						|
local eq = t.eq
 | 
						|
local exec_capture = n.exec_capture
 | 
						|
local exec_lua = n.exec_lua
 | 
						|
local feed = n.feed
 | 
						|
 | 
						|
-- Reference text
 | 
						|
-- aa
 | 
						|
--  aa
 | 
						|
--   aa
 | 
						|
--
 | 
						|
--   aa
 | 
						|
--  aa
 | 
						|
-- aa
 | 
						|
local example_lines = { 'aa', ' aa', '  aa', '', '  aa', ' aa', 'aa' }
 | 
						|
 | 
						|
local set_commentstring = function(commentstring)
 | 
						|
  api.nvim_set_option_value('commentstring', commentstring, { buf = 0 })
 | 
						|
end
 | 
						|
 | 
						|
local get_lines = function(from, to)
 | 
						|
  from, to = from or 0, to or -1
 | 
						|
  return api.nvim_buf_get_lines(0, from, to, false)
 | 
						|
end
 | 
						|
 | 
						|
local set_lines = function(lines, from, to)
 | 
						|
  from, to = from or 0, to or -1
 | 
						|
  api.nvim_buf_set_lines(0, from, to, false, lines)
 | 
						|
end
 | 
						|
 | 
						|
local set_cursor = function(row, col)
 | 
						|
  api.nvim_win_set_cursor(0, { row, col })
 | 
						|
end
 | 
						|
 | 
						|
local get_cursor = function()
 | 
						|
  return api.nvim_win_get_cursor(0)
 | 
						|
end
 | 
						|
 | 
						|
local setup_treesitter = function()
 | 
						|
  -- NOTE: This leverages bundled Vimscript and Lua tree-sitter parsers
 | 
						|
  api.nvim_set_option_value('filetype', 'vim', { buf = 0 })
 | 
						|
  exec_lua('vim.treesitter.start()')
 | 
						|
end
 | 
						|
 | 
						|
before_each(function()
 | 
						|
  clear({ args_rm = { '--cmd' }, args = { '--clean' } })
 | 
						|
end)
 | 
						|
 | 
						|
describe('commenting', function()
 | 
						|
  before_each(function()
 | 
						|
    set_lines(example_lines)
 | 
						|
    set_commentstring('# %s')
 | 
						|
  end)
 | 
						|
 | 
						|
  describe('toggle_lines()', function()
 | 
						|
    local toggle_lines = function(...)
 | 
						|
      exec_lua('require("vim._comment").toggle_lines(...)', ...)
 | 
						|
    end
 | 
						|
 | 
						|
    it('works', function()
 | 
						|
      toggle_lines(3, 5)
 | 
						|
      eq(get_lines(2, 5), { '  # aa', '  #', '  # aa' })
 | 
						|
 | 
						|
      toggle_lines(3, 5)
 | 
						|
      eq(get_lines(2, 5), { '  aa', '', '  aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it("works with different 'commentstring' options", function()
 | 
						|
      local validate = function(lines_before, lines_after, lines_again)
 | 
						|
        set_lines(lines_before)
 | 
						|
        toggle_lines(1, #lines_before)
 | 
						|
        eq(get_lines(), lines_after)
 | 
						|
        toggle_lines(1, #lines_before)
 | 
						|
        eq(get_lines(), lines_again or lines_before)
 | 
						|
      end
 | 
						|
 | 
						|
      -- Single whitespace inside comment parts (main case)
 | 
						|
      set_commentstring('# %s #')
 | 
						|
      -- - General case
 | 
						|
      validate(
 | 
						|
        { 'aa', '  aa', 'aa  ', '  aa  ' },
 | 
						|
        { '# aa #', '#   aa #', '# aa   #', '#   aa   #' }
 | 
						|
      )
 | 
						|
      -- - Tabs
 | 
						|
      validate(
 | 
						|
        { 'aa', '\taa', 'aa\t', '\taa\t' },
 | 
						|
        { '# aa #', '# \taa #', '# aa\t #', '# \taa\t #' }
 | 
						|
      )
 | 
						|
      -- - With indent
 | 
						|
      validate({ ' aa', '  aa' }, { ' # aa #', ' #  aa #' })
 | 
						|
      -- - With blank/empty lines
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  # aa #', '  ##', '  ##', '  ##' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring('# %s')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { '# aa', '#   aa', '# aa  ', '#   aa  ' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '# aa', '# \taa', '# aa\t', '# \taa\t' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' # aa', ' #  aa' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  # aa', '  #', '  #', '  #' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring('%s #')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { 'aa #', '  aa #', 'aa   #', '  aa   #' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa #', '\taa #', 'aa\t #', '\taa\t #' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' aa #', '  aa #' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  aa #', '  #', '  #', '  #' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      -- No whitespace in parts
 | 
						|
      set_commentstring('#%s#')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { '#aa#', '#  aa#', '#aa  #', '#  aa  #' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#aa#', '#\taa#', '#aa\t#', '#\taa\t#' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' #aa#', ' # aa#' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  #aa#', '  ##', '  ##', '  ##' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring('#%s')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { '#aa', '#  aa', '#aa  ', '#  aa  ' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#aa', '#\taa', '#aa\t', '#\taa\t' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' #aa', ' # aa' })
 | 
						|
      validate({ '  aa', '', '  ', '\t' }, { '  #aa', '  #', '  #', '  #' }, { '  aa', '', '', '' })
 | 
						|
 | 
						|
      set_commentstring('%s#')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { 'aa#', '  aa#', 'aa  #', '  aa  #' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa#', '\taa#', 'aa\t#', '\taa\t#' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' aa#', '  aa#' })
 | 
						|
      validate({ '  aa', '', '  ', '\t' }, { '  aa#', '  #', '  #', '  #' }, { '  aa', '', '', '' })
 | 
						|
 | 
						|
      -- Extra whitespace inside comment parts
 | 
						|
      set_commentstring('#  %s  #')
 | 
						|
      validate(
 | 
						|
        { 'aa', '  aa', 'aa  ', '  aa  ' },
 | 
						|
        { '#  aa  #', '#    aa  #', '#  aa    #', '#    aa    #' }
 | 
						|
      )
 | 
						|
      validate(
 | 
						|
        { 'aa', '\taa', 'aa\t', '\taa\t' },
 | 
						|
        { '#  aa  #', '#  \taa  #', '#  aa\t  #', '#  \taa\t  #' }
 | 
						|
      )
 | 
						|
      validate({ ' aa', '  aa' }, { ' #  aa  #', ' #   aa  #' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  #  aa  #', '  ##', '  ##', '  ##' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring('#  %s')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { '#  aa', '#    aa', '#  aa  ', '#    aa  ' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#  aa', '#  \taa', '#  aa\t', '#  \taa\t' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' #  aa', ' #   aa' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  #  aa', '  #', '  #', '  #' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring('%s  #')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { 'aa  #', '  aa  #', 'aa    #', '  aa    #' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa  #', '\taa  #', 'aa\t  #', '\taa\t  #' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' aa  #', '  aa  #' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  aa  #', '  #', '  #', '  #' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      -- Whitespace outside of comment parts
 | 
						|
      set_commentstring(' # %s # ')
 | 
						|
      validate(
 | 
						|
        { 'aa', '  aa', 'aa  ', '  aa  ' },
 | 
						|
        { ' # aa # ', ' #   aa # ', ' # aa   # ', ' #   aa   # ' }
 | 
						|
      )
 | 
						|
      validate(
 | 
						|
        { 'aa', '\taa', 'aa\t', '\taa\t' },
 | 
						|
        { ' # aa # ', ' # \taa # ', ' # aa\t # ', ' # \taa\t # ' }
 | 
						|
      )
 | 
						|
      validate({ ' aa', '  aa' }, { '  # aa # ', '  #  aa # ' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '   # aa # ', '  ##', '  ##', '  ##' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring(' # %s ')
 | 
						|
      validate(
 | 
						|
        { 'aa', '  aa', 'aa  ', '  aa  ' },
 | 
						|
        { ' # aa ', ' #   aa ', ' # aa   ', ' #   aa   ' }
 | 
						|
      )
 | 
						|
      validate(
 | 
						|
        { 'aa', '\taa', 'aa\t', '\taa\t' },
 | 
						|
        { ' # aa ', ' # \taa ', ' # aa\t ', ' # \taa\t ' }
 | 
						|
      )
 | 
						|
      validate({ ' aa', '  aa' }, { '  # aa ', '  #  aa ' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '   # aa ', '  #', '  #', '  #' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      set_commentstring(' %s # ')
 | 
						|
      validate(
 | 
						|
        { 'aa', '  aa', 'aa  ', '  aa  ' },
 | 
						|
        { ' aa # ', '   aa # ', ' aa   # ', '   aa   # ' }
 | 
						|
      )
 | 
						|
      validate(
 | 
						|
        { 'aa', '\taa', 'aa\t', '\taa\t' },
 | 
						|
        { ' aa # ', ' \taa # ', ' aa\t # ', ' \taa\t # ' }
 | 
						|
      )
 | 
						|
      validate({ ' aa', '  aa' }, { '  aa # ', '   aa # ' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '   aa # ', '  #', '  #', '  #' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
 | 
						|
      -- LaTeX
 | 
						|
      set_commentstring('% %s')
 | 
						|
      validate({ 'aa', '  aa', 'aa  ', '  aa  ' }, { '% aa', '%   aa', '% aa  ', '%   aa  ' })
 | 
						|
      validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '% aa', '% \taa', '% aa\t', '% \taa\t' })
 | 
						|
      validate({ ' aa', '  aa' }, { ' % aa', ' %  aa' })
 | 
						|
      validate(
 | 
						|
        { '  aa', '', '  ', '\t' },
 | 
						|
        { '  % aa', '  %', '  %', '  %' },
 | 
						|
        { '  aa', '', '', '' }
 | 
						|
      )
 | 
						|
    end)
 | 
						|
 | 
						|
    it('respects tree-sitter injections', function()
 | 
						|
      setup_treesitter()
 | 
						|
 | 
						|
      local lines = {
 | 
						|
        'set background=dark',
 | 
						|
        'lua << EOF',
 | 
						|
        'print(1)',
 | 
						|
        'vim.api.nvim_exec2([[',
 | 
						|
        '    set background=light',
 | 
						|
        ']])',
 | 
						|
        'EOF',
 | 
						|
      }
 | 
						|
 | 
						|
      -- Single line comments
 | 
						|
      local validate = function(line, ref_output)
 | 
						|
        set_lines(lines)
 | 
						|
        toggle_lines(line, line)
 | 
						|
        eq(get_lines(line - 1, line)[1], ref_output)
 | 
						|
      end
 | 
						|
 | 
						|
      validate(1, '"set background=dark')
 | 
						|
      validate(2, '"lua << EOF')
 | 
						|
      validate(3, '-- print(1)')
 | 
						|
      validate(4, '-- vim.api.nvim_exec2([[')
 | 
						|
      validate(5, '    "set background=light')
 | 
						|
      validate(6, '-- ]])')
 | 
						|
      validate(7, '"EOF')
 | 
						|
 | 
						|
      -- Multiline comments should be computed based on first line 'commentstring'
 | 
						|
      set_lines(lines)
 | 
						|
      toggle_lines(1, 3)
 | 
						|
      local out_lines = get_lines()
 | 
						|
      eq(out_lines[1], '"set background=dark')
 | 
						|
      eq(out_lines[2], '"lua << EOF')
 | 
						|
      eq(out_lines[3], '"print(1)')
 | 
						|
    end)
 | 
						|
 | 
						|
    it('correctly computes indent', function()
 | 
						|
      toggle_lines(2, 4)
 | 
						|
      eq(get_lines(1, 4), { ' # aa', ' #  aa', ' #' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('correctly detects comment/uncomment', function()
 | 
						|
      local validate = function(from, to, ref_lines)
 | 
						|
        set_lines({ '', 'aa', '# aa', '# aa', 'aa', '' })
 | 
						|
        toggle_lines(from, to)
 | 
						|
        eq(get_lines(), ref_lines)
 | 
						|
      end
 | 
						|
 | 
						|
      -- It should uncomment only if all non-blank lines are comments
 | 
						|
      validate(3, 4, { '', 'aa', 'aa', 'aa', 'aa', '' })
 | 
						|
      validate(2, 4, { '', '# aa', '# # aa', '# # aa', 'aa', '' })
 | 
						|
      validate(3, 5, { '', 'aa', '# # aa', '# # aa', '# aa', '' })
 | 
						|
      validate(1, 6, { '#', '# aa', '# # aa', '# # aa', '# aa', '#' })
 | 
						|
 | 
						|
      -- Blank lines should be ignored when making a decision
 | 
						|
      set_lines({ '# aa', '', '  ', '\t', '# aa' })
 | 
						|
      toggle_lines(1, 5)
 | 
						|
      eq(get_lines(), { 'aa', '', '  ', '\t', 'aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('correctly matches comment parts during checking and uncommenting', function()
 | 
						|
      local validate = function(from, to, ref_lines)
 | 
						|
        set_lines({ '/*aa*/', '/* aa */', '/*  aa  */' })
 | 
						|
        toggle_lines(from, to)
 | 
						|
        eq(get_lines(), ref_lines)
 | 
						|
      end
 | 
						|
 | 
						|
      -- Should first try to match 'commentstring' parts exactly with their
 | 
						|
      -- whitespace, with fallback on trimmed parts
 | 
						|
      set_commentstring('/*%s*/')
 | 
						|
      validate(1, 3, { 'aa', ' aa ', '  aa  ' })
 | 
						|
      validate(2, 3, { '/*aa*/', ' aa ', '  aa  ' })
 | 
						|
      validate(3, 3, { '/*aa*/', '/* aa */', '  aa  ' })
 | 
						|
 | 
						|
      set_commentstring('/* %s */')
 | 
						|
      validate(1, 3, { 'aa', 'aa', ' aa ' })
 | 
						|
      validate(2, 3, { '/*aa*/', 'aa', ' aa ' })
 | 
						|
      validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' })
 | 
						|
 | 
						|
      set_commentstring('/*  %s  */')
 | 
						|
      validate(1, 3, { 'aa', ' aa ', 'aa' })
 | 
						|
      validate(2, 3, { '/*aa*/', ' aa ', 'aa' })
 | 
						|
      validate(3, 3, { '/*aa*/', '/* aa */', 'aa' })
 | 
						|
 | 
						|
      set_commentstring(' /*%s*/ ')
 | 
						|
      validate(1, 3, { 'aa', ' aa ', '  aa  ' })
 | 
						|
      validate(2, 3, { '/*aa*/', ' aa ', '  aa  ' })
 | 
						|
      validate(3, 3, { '/*aa*/', '/* aa */', '  aa  ' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('uncomments on inconsistent indent levels', function()
 | 
						|
      set_lines({ '# aa', ' # aa', '  # aa' })
 | 
						|
      toggle_lines(1, 3)
 | 
						|
      eq(get_lines(), { 'aa', ' aa', '  aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('respects tabs', function()
 | 
						|
      api.nvim_set_option_value('expandtab', false, { buf = 0 })
 | 
						|
      set_lines({ '\t\taa', '\t\taa' })
 | 
						|
 | 
						|
      toggle_lines(1, 2)
 | 
						|
      eq(get_lines(), { '\t\t# aa', '\t\t# aa' })
 | 
						|
 | 
						|
      toggle_lines(1, 2)
 | 
						|
      eq(get_lines(), { '\t\taa', '\t\taa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('works with trailing whitespace', function()
 | 
						|
      -- Without right-hand side
 | 
						|
      set_commentstring('# %s')
 | 
						|
      set_lines({ ' aa', ' aa  ', '  ' })
 | 
						|
      toggle_lines(1, 3)
 | 
						|
      eq(get_lines(), { ' # aa', ' # aa  ', ' #' })
 | 
						|
      toggle_lines(1, 3)
 | 
						|
      eq(get_lines(), { ' aa', ' aa  ', '' })
 | 
						|
 | 
						|
      -- With right-hand side
 | 
						|
      set_commentstring('%s #')
 | 
						|
      set_lines({ ' aa', ' aa  ', '  ' })
 | 
						|
      toggle_lines(1, 3)
 | 
						|
      eq(get_lines(), { ' aa #', ' aa   #', ' #' })
 | 
						|
      toggle_lines(1, 3)
 | 
						|
      eq(get_lines(), { ' aa', ' aa  ', '' })
 | 
						|
 | 
						|
      -- Trailing whitespace after right side should be preserved for non-blanks
 | 
						|
      set_commentstring('%s #')
 | 
						|
      set_lines({ ' aa #  ', ' aa #\t', ' #  ', ' #\t' })
 | 
						|
      toggle_lines(1, 4)
 | 
						|
      eq(get_lines(), { ' aa  ', ' aa\t', '', '' })
 | 
						|
    end)
 | 
						|
  end)
 | 
						|
 | 
						|
  describe('Operator', function()
 | 
						|
    it('works in Normal mode', function()
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('gc', 'ap')
 | 
						|
      eq(get_lines(), { '# aa', '#  aa', '#   aa', '#', '  aa', ' aa', 'aa' })
 | 
						|
      -- Cursor moves to start line
 | 
						|
      eq(get_cursor(), { 1, 0 })
 | 
						|
 | 
						|
      -- Supports `v:count`
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(2, 0)
 | 
						|
      feed('2gc', 'ap')
 | 
						|
      eq(get_lines(), { '# aa', '#  aa', '#   aa', '#', '#   aa', '#  aa', '# aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('allows dot-repeat in Normal mode', function()
 | 
						|
      local doubly_commented = { '# # aa', '# #  aa', '# #   aa', '# #', '#   aa', '#  aa', '# aa' }
 | 
						|
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('gc', 'ap')
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), doubly_commented)
 | 
						|
 | 
						|
      -- Not immediate dot-repeat
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('gc', 'ap')
 | 
						|
      set_cursor(7, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), doubly_commented)
 | 
						|
    end)
 | 
						|
 | 
						|
    it('works in Visual mode', function()
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('v', 'ap', 'gc')
 | 
						|
      eq(get_lines(), { '# aa', '#  aa', '#   aa', '#', '  aa', ' aa', 'aa' })
 | 
						|
 | 
						|
      -- Cursor moves to start line
 | 
						|
      eq(get_cursor(), { 1, 0 })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('allows dot-repeat after initial Visual mode', function()
 | 
						|
      -- local example_lines = { 'aa', ' aa', '  aa', '', '  aa', ' aa', 'aa' }
 | 
						|
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('vip', 'gc')
 | 
						|
      eq(get_lines(), { '# aa', '#  aa', '#   aa', '', '  aa', ' aa', 'aa' })
 | 
						|
      eq(get_cursor(), { 1, 0 })
 | 
						|
 | 
						|
      -- Dot-repeat after first application in Visual mode should apply to the same
 | 
						|
      -- relative region
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), example_lines)
 | 
						|
 | 
						|
      set_cursor(3, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), { 'aa', ' aa', '  # aa', '  #', '  # aa', ' aa', 'aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it("respects 'commentstring'", function()
 | 
						|
      set_commentstring('/*%s*/')
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('gc', 'ap')
 | 
						|
      eq(get_lines(), { '/*aa*/', '/* aa*/', '/*  aa*/', '/**/', '  aa', ' aa', 'aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it("works with empty 'commentstring'", function()
 | 
						|
      set_commentstring('')
 | 
						|
      set_cursor(2, 2)
 | 
						|
      feed('gc', 'ap')
 | 
						|
      eq(get_lines(), example_lines)
 | 
						|
      eq(exec_capture('1messages'), [[Option 'commentstring' is empty.]])
 | 
						|
    end)
 | 
						|
 | 
						|
    it('respects tree-sitter injections', function()
 | 
						|
      setup_treesitter()
 | 
						|
 | 
						|
      local lines = {
 | 
						|
        'set background=dark',
 | 
						|
        'lua << EOF',
 | 
						|
        'print(1)',
 | 
						|
        'vim.api.nvim_exec2([[',
 | 
						|
        '    set background=light',
 | 
						|
        ']])',
 | 
						|
        'EOF',
 | 
						|
      }
 | 
						|
 | 
						|
      -- Single line comments
 | 
						|
      local validate = function(line, ref_output)
 | 
						|
        set_lines(lines)
 | 
						|
        set_cursor(line, 0)
 | 
						|
        feed('gc_')
 | 
						|
        eq(get_lines(line - 1, line)[1], ref_output)
 | 
						|
      end
 | 
						|
 | 
						|
      validate(1, '"set background=dark')
 | 
						|
      validate(2, '"lua << EOF')
 | 
						|
      validate(3, '-- print(1)')
 | 
						|
      validate(4, '-- vim.api.nvim_exec2([[')
 | 
						|
      validate(5, '    "set background=light')
 | 
						|
      validate(6, '-- ]])')
 | 
						|
      validate(7, '"EOF')
 | 
						|
 | 
						|
      -- Has proper dot-repeat which recomputes 'commentstring'
 | 
						|
      set_lines(lines)
 | 
						|
 | 
						|
      set_cursor(1, 0)
 | 
						|
      feed('gc_')
 | 
						|
      eq(get_lines()[1], '"set background=dark')
 | 
						|
 | 
						|
      set_cursor(3, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines()[3], '-- print(1)')
 | 
						|
 | 
						|
      -- Multiline comments should be computed based on cursor position
 | 
						|
      -- which in case of Visual selection means its left part
 | 
						|
      set_lines(lines)
 | 
						|
      set_cursor(1, 0)
 | 
						|
      feed('v2j', 'gc')
 | 
						|
      local out_lines = get_lines()
 | 
						|
      eq(out_lines[1], '"set background=dark')
 | 
						|
      eq(out_lines[2], '"lua << EOF')
 | 
						|
      eq(out_lines[3], '"print(1)')
 | 
						|
    end)
 | 
						|
 | 
						|
    it("recomputes local 'commentstring' based on cursor position", function()
 | 
						|
      setup_treesitter()
 | 
						|
      local lines = {
 | 
						|
        '  print(1)',
 | 
						|
        'lua << EOF',
 | 
						|
        '  print(1)',
 | 
						|
        'EOF',
 | 
						|
      }
 | 
						|
      set_lines(lines)
 | 
						|
 | 
						|
      set_cursor(1, 1)
 | 
						|
      feed('gc_')
 | 
						|
      eq(get_lines()[1], '  "print(1)')
 | 
						|
 | 
						|
      set_lines(lines)
 | 
						|
      set_cursor(3, 2)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines()[3], '  -- print(1)')
 | 
						|
    end)
 | 
						|
 | 
						|
    it('preserves marks', function()
 | 
						|
      set_cursor(2, 0)
 | 
						|
      -- Set '`<' and '`>' marks
 | 
						|
      feed('VV')
 | 
						|
      feed('gc', 'ip')
 | 
						|
      eq(api.nvim_buf_get_mark(0, '<'), { 2, 0 })
 | 
						|
      eq(api.nvim_buf_get_mark(0, '>'), { 2, 2147483647 })
 | 
						|
    end)
 | 
						|
  end)
 | 
						|
 | 
						|
  describe('Current line', function()
 | 
						|
    it('works', function()
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(1, 1)
 | 
						|
      feed('gcc')
 | 
						|
      eq(get_lines(0, 2), { '# aa', ' aa' })
 | 
						|
 | 
						|
      -- Does not comment empty line
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(4, 0)
 | 
						|
      feed('gcc')
 | 
						|
      eq(get_lines(2, 5), { '  aa', '', '  aa' })
 | 
						|
 | 
						|
      -- Supports `v:count`
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(2, 0)
 | 
						|
      feed('2gcc')
 | 
						|
      eq(get_lines(0, 3), { 'aa', ' # aa', ' #  aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('allows dot-repeat', function()
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(1, 1)
 | 
						|
      feed('gcc')
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), example_lines)
 | 
						|
 | 
						|
      -- Not immediate dot-repeat
 | 
						|
      set_lines(example_lines)
 | 
						|
      set_cursor(1, 1)
 | 
						|
      feed('gcc')
 | 
						|
      set_cursor(7, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(6, 7), { '# aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('respects tree-sitter injections', function()
 | 
						|
      setup_treesitter()
 | 
						|
 | 
						|
      local lines = {
 | 
						|
        'set background=dark',
 | 
						|
        'lua << EOF',
 | 
						|
        'print(1)',
 | 
						|
        'EOF',
 | 
						|
      }
 | 
						|
      set_lines(lines)
 | 
						|
 | 
						|
      set_cursor(1, 0)
 | 
						|
      feed('gcc')
 | 
						|
      eq(get_lines(), { '"set background=dark', 'lua << EOF', 'print(1)', 'EOF' })
 | 
						|
 | 
						|
      -- Should work with dot-repeat
 | 
						|
      set_cursor(3, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), { '"set background=dark', 'lua << EOF', '-- print(1)', 'EOF' })
 | 
						|
    end)
 | 
						|
  end)
 | 
						|
 | 
						|
  describe('Textobject', function()
 | 
						|
    it('works', function()
 | 
						|
      set_lines({ 'aa', '# aa', '# aa', 'aa' })
 | 
						|
      set_cursor(2, 0)
 | 
						|
      feed('d', 'gc')
 | 
						|
      eq(get_lines(), { 'aa', 'aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('allows dot-repeat', function()
 | 
						|
      set_lines({ 'aa', '# aa', '# aa', 'aa', '# aa' })
 | 
						|
      set_cursor(2, 0)
 | 
						|
      feed('d', 'gc')
 | 
						|
      set_cursor(3, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), { 'aa', 'aa' })
 | 
						|
    end)
 | 
						|
 | 
						|
    it('does nothing when not inside textobject', function()
 | 
						|
      -- Builtin operators
 | 
						|
      feed('d', 'gc')
 | 
						|
      eq(get_lines(), example_lines)
 | 
						|
 | 
						|
      -- Comment operator
 | 
						|
      local validate_no_action = function(line, col)
 | 
						|
        set_lines(example_lines)
 | 
						|
        set_cursor(line, col)
 | 
						|
        feed('gc', 'gc')
 | 
						|
        eq(get_lines(), example_lines)
 | 
						|
      end
 | 
						|
 | 
						|
      validate_no_action(1, 1)
 | 
						|
      validate_no_action(2, 2)
 | 
						|
 | 
						|
      -- Doesn't work (but should) because both `[` and `]` are set to (1, 0)
 | 
						|
      -- (instead of more reasonable (1, -1) or (0, 2147483647)).
 | 
						|
      -- validate_no_action(1, 0)
 | 
						|
    end)
 | 
						|
 | 
						|
    it('respects tree-sitter injections', function()
 | 
						|
      setup_treesitter()
 | 
						|
      local lines = {
 | 
						|
        '"set background=dark',
 | 
						|
        '"set termguicolors',
 | 
						|
        'lua << EOF',
 | 
						|
        '-- print(1)',
 | 
						|
        '-- print(2)',
 | 
						|
        'EOF',
 | 
						|
      }
 | 
						|
      set_lines(lines)
 | 
						|
 | 
						|
      set_cursor(1, 0)
 | 
						|
      feed('dgc')
 | 
						|
      eq(get_lines(), { 'lua << EOF', '-- print(1)', '-- print(2)', 'EOF' })
 | 
						|
 | 
						|
      -- Should work with dot-repeat
 | 
						|
      set_cursor(2, 0)
 | 
						|
      feed('.')
 | 
						|
      eq(get_lines(), { 'lua << EOF', 'EOF' })
 | 
						|
    end)
 | 
						|
  end)
 | 
						|
end)
 |