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.
This commit is contained in:
Evgeni Chasnovski 2024-05-23 23:30:53 +03:00 committed by GitHub
parent c614969570
commit 0a2218f965
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 25 deletions

View File

@ -579,6 +579,10 @@ Acting on multiple lines behaves as follows:
transformed to empty comments (e.g. `/**/`). Comment markers are aligned to
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
|treesitter| parser is installed, then |vim.filetype.get_option()| is called
to look up the value of 'commentstring' corresponding to the cursor position.

View File

@ -77,14 +77,11 @@ local function make_comment_check(parts)
local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
-- Commented line has the following structure:
-- <possible whitespace> <left> <anything> <right> <possible whitespace>
local nonblank_regex = '^%s-' .. l_esc .. '.*' .. 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-$'
-- <whitespace> <trimmed left> <anything> <trimmed right> <whitespace>
local regex = '^%s-' .. vim.trim(l_esc) .. '.*' .. vim.trim(r_esc) .. '%s-$'
return function(line)
return line:find(nonblank_regex) ~= nil or line:find(blank_regex) ~= nil
return line:find(regex) ~= nil
end
end
@ -153,14 +150,14 @@ end
---@return fun(line: string): string
local function make_uncomment_function(parts)
local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
local nonblank_regex = '^(%s*)' .. l_esc .. '(.*)' .. r_esc .. '(%s-)$'
local blank_regex = '^(%s*)' .. vim.trim(l_esc) .. '(%s*)' .. vim.trim(r_esc) .. '(%s-)$'
local regex = '^(%s*)' .. l_esc .. '(.*)' .. r_esc .. '(%s-)$'
local regex_trimmed = '^(%s*)' .. vim.trim(l_esc) .. '(.*)' .. vim.trim(r_esc) .. '(%s-)$'
return function(line)
-- Try both non-blank and blank regexes
local indent, new_line, trail = line:match(nonblank_regex)
-- Try regex with exact comment parts first, fall back to trimmed parts
local indent, new_line, trail = line:match(regex)
if new_line == nil then
indent, new_line, trail = line:match(blank_regex)
indent, new_line, trail = line:match(regex_trimmed)
end
-- Return original if line is not commented

View File

@ -301,27 +301,34 @@ describe('commenting', function()
eq(get_lines(), { 'aa', '', ' ', '\t', 'aa' })
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)
set_lines({ '#aa', '# aa', '# aa' })
set_lines({ '/*aa*/', '/* aa */', '/* aa */' })
toggle_lines(from, to)
eq(get_lines(), ref_lines)
end
set_commentstring('#%s')
validate(1, 3, { 'aa', ' aa', ' aa' })
validate(2, 3, { '#aa', ' aa', ' aa' })
validate(3, 3, { '#aa', '# aa', ' aa' })
-- 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' })
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()