mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	fix(comment): fall back to using trimmed comment markers (#28950)
fix(comment): fall back to using trimmed comment markers (#28938)
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.
(cherry picked from commit 0a2218f965)
Co-authored-by: Evgeni Chasnovski <evgeni.chasnovski@gmail.com>
			
			
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							bdd5871dc5
						
					
				
				
					commit
					21b21b94e6
				
			@@ -579,6 +579,10 @@ Acting on multiple lines behaves as follows:
 | 
				
			|||||||
  transformed to empty comments (e.g. `/**/`). Comment markers are aligned to
 | 
					  transformed to empty comments (e.g. `/**/`). Comment markers are aligned to
 | 
				
			||||||
  the least indented line.
 | 
					  the least indented line.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Matching 'commentstring' does not account for whitespace in comment markers.
 | 
				
			||||||
 | 
					Removing comment markers is first attempted exactly, with fallback to using
 | 
				
			||||||
 | 
					markers trimmed from whitespace.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If the filetype of the buffer is associated with a language for which a
 | 
					If the filetype of the buffer is associated with a language for which a
 | 
				
			||||||
|treesitter| parser is installed, then |vim.filetype.get_option()| is called
 | 
					|treesitter| parser is installed, then |vim.filetype.get_option()| is called
 | 
				
			||||||
to look up the value of 'commentstring' corresponding to the cursor position.
 | 
					to look up the value of 'commentstring' corresponding to the cursor position.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,14 +77,11 @@ local function make_comment_check(parts)
 | 
				
			|||||||
  local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
 | 
					  local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  -- Commented line has the following structure:
 | 
					  -- Commented line has the following structure:
 | 
				
			||||||
  -- <possible whitespace> <left> <anything> <right> <possible whitespace>
 | 
					  -- <whitespace> <trimmed left> <anything> <trimmed right> <whitespace>
 | 
				
			||||||
  local nonblank_regex = '^%s-' .. l_esc .. '.*' .. r_esc .. '%s-$'
 | 
					  local regex = '^%s-' .. vim.trim(l_esc) .. '.*' .. vim.trim(r_esc) .. '%s-$'
 | 
				
			||||||
 | 
					 | 
				
			||||||
  -- Commented blank line can have any amount of whitespace around parts
 | 
					 | 
				
			||||||
  local blank_regex = '^%s-' .. vim.trim(l_esc) .. '%s*' .. vim.trim(r_esc) .. '%s-$'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return function(line)
 | 
					  return function(line)
 | 
				
			||||||
    return line:find(nonblank_regex) ~= nil or line:find(blank_regex) ~= nil
 | 
					    return line:find(regex) ~= nil
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -153,14 +150,14 @@ end
 | 
				
			|||||||
---@return fun(line: string): string
 | 
					---@return fun(line: string): string
 | 
				
			||||||
local function make_uncomment_function(parts)
 | 
					local function make_uncomment_function(parts)
 | 
				
			||||||
  local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
 | 
					  local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
 | 
				
			||||||
  local nonblank_regex = '^(%s*)' .. l_esc .. '(.*)' .. r_esc .. '(%s-)$'
 | 
					  local regex = '^(%s*)' .. l_esc .. '(.*)' .. r_esc .. '(%s-)$'
 | 
				
			||||||
  local blank_regex = '^(%s*)' .. vim.trim(l_esc) .. '(%s*)' .. vim.trim(r_esc) .. '(%s-)$'
 | 
					  local regex_trimmed = '^(%s*)' .. vim.trim(l_esc) .. '(.*)' .. vim.trim(r_esc) .. '(%s-)$'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return function(line)
 | 
					  return function(line)
 | 
				
			||||||
    -- Try both non-blank and blank regexes
 | 
					    -- Try regex with exact comment parts first, fall back to trimmed parts
 | 
				
			||||||
    local indent, new_line, trail = line:match(nonblank_regex)
 | 
					    local indent, new_line, trail = line:match(regex)
 | 
				
			||||||
    if new_line == nil then
 | 
					    if new_line == nil then
 | 
				
			||||||
      indent, new_line, trail = line:match(blank_regex)
 | 
					      indent, new_line, trail = line:match(regex_trimmed)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    -- Return original if line is not commented
 | 
					    -- Return original if line is not commented
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -301,27 +301,34 @@ describe('commenting', function()
 | 
				
			|||||||
      eq(get_lines(), { 'aa', '', '  ', '\t', 'aa' })
 | 
					      eq(get_lines(), { 'aa', '', '  ', '\t', 'aa' })
 | 
				
			||||||
    end)
 | 
					    end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('matches comment parts strictly when detecting comment/uncomment', function()
 | 
					    it('correctly matches comment parts during checking and uncommenting', function()
 | 
				
			||||||
      local validate = function(from, to, ref_lines)
 | 
					      local validate = function(from, to, ref_lines)
 | 
				
			||||||
        set_lines({ '#aa', '# aa', '#  aa' })
 | 
					        set_lines({ '/*aa*/', '/* aa */', '/*  aa  */' })
 | 
				
			||||||
        toggle_lines(from, to)
 | 
					        toggle_lines(from, to)
 | 
				
			||||||
        eq(get_lines(), ref_lines)
 | 
					        eq(get_lines(), ref_lines)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      set_commentstring('#%s')
 | 
					      -- Should first try to match 'commentstring' parts exactly with their
 | 
				
			||||||
      validate(1, 3, { 'aa', ' aa', '  aa' })
 | 
					      -- whitespace, with fallback on trimmed parts
 | 
				
			||||||
      validate(2, 3, { '#aa', ' aa', '  aa' })
 | 
					      set_commentstring('/*%s*/')
 | 
				
			||||||
      validate(3, 3, { '#aa', '# aa', '  aa' })
 | 
					      validate(1, 3, { 'aa', ' aa ', '  aa  ' })
 | 
				
			||||||
 | 
					      validate(2, 3, { '/*aa*/', ' aa ', '  aa  ' })
 | 
				
			||||||
 | 
					      validate(3, 3, { '/*aa*/', '/* aa */', '  aa  ' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      set_commentstring('# %s')
 | 
					      set_commentstring('/* %s */')
 | 
				
			||||||
      validate(1, 3, { '# #aa', '# # aa', '# #  aa' })
 | 
					      validate(1, 3, { 'aa', 'aa', ' aa ' })
 | 
				
			||||||
      validate(2, 3, { '#aa', 'aa', ' aa' })
 | 
					      validate(2, 3, { '/*aa*/', 'aa', ' aa ' })
 | 
				
			||||||
      validate(3, 3, { '#aa', '# aa', ' aa' })
 | 
					      validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      set_commentstring('#  %s')
 | 
					      set_commentstring('/*  %s  */')
 | 
				
			||||||
      validate(1, 3, { '#  #aa', '#  # aa', '#  #  aa' })
 | 
					      validate(1, 3, { 'aa', ' aa ', 'aa' })
 | 
				
			||||||
      validate(2, 3, { '#aa', '#  # aa', '#  #  aa' })
 | 
					      validate(2, 3, { '/*aa*/', ' aa ', 'aa' })
 | 
				
			||||||
      validate(3, 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)
 | 
					    end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('uncomments on inconsistent indent levels', function()
 | 
					    it('uncomments on inconsistent indent levels', function()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user