mirror of
https://github.com/neovim/neovim
synced 2025-07-16 09:11:51 +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