-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
feat(comment): add built-in commenting #28176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
dundargoc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works flawlessly for me! You knocked it out of the park with this.
f5c9330 to
fbb2f50
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicely done. Testability is a strong reason for including this impl.
supports tree-sitter by computing 'commentstring' option under cursor, which can matter in presence of tree-sitter injected languages.
...
comments and uncomments blank lines while 'tpope/vim-commentary' does not. ... more intuitive experience
These are worth mentioning briefly in the help, IMO.
P.S. Please include the PR description in the commit message.
|
(Links in commit messages can get very spammy, though.) |
| --- Get 'commentstring' at cursor | ||
| ---@param ref_position integer[] | ||
| ---@return string | ||
| local function get_commentstring(ref_position) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible followup: this could be more broadly useful refactored as a vim.treesitter.get_local_option('commentstring', position).
|
This is now resolved by using |
Thanks 🙏!
The first one is already mentioned with "... 'commentstring' at cursor". I'd leave it at that.
Will add a stripped down version during later squash. |
|
I'm very excited to finally see this feature included. Thanks @echasnovski for once again providing an excellent contribution. However, I have one concern about this:
I disagree that this is more intuitive, and in fact I would argue it's the opposite, in particular when un-commenting. Consider this example: // int x = foo();
// int y = bar(x);The 2nd line is a blank line: there are no characters in it all. If I use the "toggle comment" operation on these 3 lines, what I get is the following: // // int x = foo();
//
// // int y = bar(x);But what I would expect to see is int x = foo();
int y = bar(x);This is rather annoying because when this happens I have to toggle the comments on the region again to get back to the original state, and then manually use the "toggle comment" operator on each individual line. For 2 lines that may not be a big deal, but for a less contrived, real world use case where there many such lines/individual regions, it becomes much more annoying. I really feel strongly that this should not be the default behavior. |
This situation, assuming continuous usage of suggested commenting, can happen if user toggled comment for those lines separately. In this case it is reasonable to expect for them to be also uncommented separately. I argue that commenting whole regions at once is more common than doing it line by line. So middle blank line would have been commented to begin with.
Unfortunately, I feel strongly that this should be the default behavior. Its only downside is that toggling twice a blank non-empty line converts it to empty, which I argue is even a good side-effect. The benefits of commenting blank lines are:
|
|
For the record, I'm in @gpanders camp here, personally. Although I don't care enough to block the PR on this, it's good to have this discussion explicitly and no matter what the end result is, document it. (I found "carefully thought through decision for a more intuitive experience" too vague myself but knew from previous discussions that it was about the text object.) |
|
One possible compromise would be to break symmetry:
One thing to keep in mind is that
is a fallacy, since you often have to work with already commented code from other people. Second alternative -- if we absolutely can't get consensus -- is to add an option for this, just like mini.comment already has. I'd prefer if we could do without, but if that's the price of adding this feature, it'd be worth it. |
|
I wasn't aware of this and I also strongly agree with @gpanders . I thought the goal here was to include a Lua version of vim-commentary, which is what a large percentage of the vim/nvim user base has grown accustomed too. |
The goal here was to include a 'mini.comment' port, which is explicitly stated very close to top of the initial comment. With an explicit mention of this issue in the later comparison because I knew it might be a big deal. I would also say (in the same spirit) that a large percentage of Neovim user base has grown accustomed to have blank lines commented as both 'mini.comment' and 'NumToStr/Comment.nvim' have this enabled by default.
I don't think breaking a symmetry is a good idea as it would break commutativity: "comment-uncomment" will return to initial lines (if empty lines included) while "uncomment-comment" will not.
It is not as big of a statement to be called fallacy. Mostly because many more issues can arise if previous editing by other people are taken into account. For example, different comment structure could have been used and uncommenting will not work as expected (if at all).
From my side I'll say the following. I would like to not be associated with a contribution which does not include possibility of commenting blank lines. Having it configurable is fine by me, but it will open another can of worms about how configuration is done. One thing to add here is that forcing ignoring blank lines by default (with alternative behind an option) will disrupt synchronization of tests in this PR and 'mini.comment'. Which is a bit worth for long term maintainability. |
I'd be fine with that. Again, the use case in mind here (which needs to be kept in mind) is working with pre-commented code; converting it to your format (blank line commented) is a feature, not a problem. |
|
Experience report. Thanks for the feature, the injected languages recognition is pretty great (despite what @barrett-ruth observed, which I haven't run into yet when editing lua-in-viml). I noticed the existence of this feature due to the default of Also I was surprised that the following kept on working (in the sense that it maintains the space between commentstring and first text), I was happy to find out about that: " Rewrap the current comment block. Necessary because paragraphs don't work
" properly inside of comment blocks, so we re-use tpope's comment block text
" object. nnoremap doesn't work. I have _no_ idea why, maybe because gc isn't
" builtin?. It appears to set a cursor to the end of the block, which I fix with
" marks. Overrides mark `q`, which I hope I wasn't using. Normally I'd use the
" `'` mark but that gets overriden during the `gc` movement, probably because of
" a jump.
nmap <leader>e mjgqgc`j(The comment about vim-commentary is now a bit outdated, of course.) Also, this seems to override |
|
That seems to be a bug in vim-commentary. The following change will fix it: diff --git a/plugin/commentary.vim b/plugin/commentary.vim
index 5370106..ed056a4 100644
--- a/plugin/commentary.vim
+++ b/plugin/commentary.vim
@@ -108,7 +108,6 @@ nnoremap <expr> <Plug>Commentary <SID>go()
nnoremap <expr> <Plug>CommentaryLine <SID>go() . '_'
onoremap <silent> <Plug>Commentary :<C-U>call <SID>textobject(get(v:, 'operator', '') ==# 'c')<CR>
nnoremap <silent> <Plug>ChangeCommentary c:<C-U>call <SID>textobject(1)<CR>
-nmap <silent> <Plug>CommentaryUndo :echoerr "Change your <Plug>CommentaryUndo map to <Plug>Commentary<Plug>Commentary"<CR>
if !hasmapto('<Plug>Commentary') || maparg('gc','n') ==# ''
xmap gc <Plug>Commentary
@@ -118,4 +117,6 @@ if !hasmapto('<Plug>Commentary') || maparg('gc','n') ==# ''
nmap gcu <Plug>Commentary<Plug>Commentary
endif
+nmap <silent> <Plug>CommentaryUndo :echoerr "Change your <Plug>CommentaryUndo map to <Plug>Commentary<Plug>Commentary"<CR>
+
" vim:set et sw=2: |
What specifically are you referring to? The fact that vim-commentary does not override builtin commenting? |
Yes. I've created tpope/vim-commentary#173 to fix it. |
The reason for this is to allow any 'commentstring' value (including no padding) in the presence of no other way to override it (opposed to |
As these are set by the bundled |
|
You're right, changing the commentstring to add a space seems to work in some sense, but it does mean that |
|
Also, perhaps |
|
Already in the works. |
|
I've edited the initial comment with some details, but will duplicate here for more visibility.
|
|
Awesome! Thanks for this! I have nvim-ts-context-commentstring installed, is this still needed / helpful? |
For most use cases - probably no. For some complicated ones (like JSX) - probably yes. The best way to know for sure is to try without it and see if anything does not work as you'd expect. |
…im nightly version, see neovim/neovim#28176
…m nightly version, see neovim/neovim#28176
This is [built-in to neovim now][1] on HEAD, so use it instead. Behaves differently (doesn't ignore blank lines), but would rather use the built-in thing than quibble with that. [1]: neovim/neovim#28176
I think this now should behave as expected after #28262. |
Until then I just created a simple autocommand to enforce the space(s): vim.api.nvim_create_autocmd({ 'FileType' }, {
desc = 'Force commentstring to include spaces',
-- group = ...,
callback = function()
local cs = vim.bo.commentstring
if not cs or not cs:match('%%s') then
return
end
vim.bo.commentstring = cs:gsub('%s*%%s%s*', ' %%s '):gsub('%s*$', '')
end,
}) |
Here is a slightly more concise and precise version: vim.api.nvim_create_autocmd({ 'FileType' }, {
desc = 'Force commentstring to include spaces',
-- group = ...,
callback = function(event)
local cs = vim.bo[event.buf].commentstring
vim.bo[event.buf].commentstring = cs:gsub('(%S)%%s', '%1 %%s'):gsub('%%s(%S)', '%%s %1')
end,
}) |
|
I have my own little comment function but I wanted to try out the builtin one, but there's one behaviour I'm missing which is that the builtin comment doesn't always toggle comments, instead it's on a per-block basis. Is there any solution for that? would it be something that could be added or are most people against it? So currently this: // commented code
uncommented codebecomes this: // // commented code
// uncommented codeWhile I would it like to become this: commented code
// uncommented code |
I don't think this is intuitive or expected behavior at all, because the You can replicate the behavior you want using |
This PR adds built-in support for commenting.
Design
Enable commenting support only through
gcmappings for simplicity. No ability to configure, no Lua module, no user commands. Yet.Overall implementation is a simplified version of mini.comment adapted to be a better suit for core. It basically means reducing code paths which use only specific fixed set of plugin config.
All used options are default except
pad_comment_parts = false. This means that 'commentstring' option is used as is without forcing single space inner padding. This choice is deliberate because without user configuration this is the only approach which allows users to have any comment format. The downside of this is that some (usually "older") values of 'commentstring' currently have no padding in them (like Vimscript's"%s"and C's/*%s*/). I think (more like, hope) those can be changed without breaking much.The best approach for users to customize 'commentstring' is to create a filetype plugin (see
:h ftplugin) orFileTypeautocommand inside which set the option locally (with:setlocalorlua vim.bo). Changing it interactively will not always work (for example, in presence of active tree-sitter parser).Comparison with 'tpope/vim-commentary'
As tpope/vim-commentary was considered for inclusion in #27999, here is a quick summary of how this PR differs from it:
User-facing features. Both implement similar user-facing mappings. This PR does not include
gcuwhich is essentially agcgc. There are no commands, events, or configuration in this PR.Size. Both have reasonably comparable number of lines of code, while this PR has more comments in tricky areas. Here is a summary table (both are compared without code which makes mappings):
Maintainability. This PR has (purely subjectively) better readability, tests, and Lua types.
Configurability. This PR has no user configuration, while 'vim-commentary' has some (partially as a counter-measure to possibly modifying 'commentstring' option).
Extra features:
This PR supports tree-sitter by computing
'commentstring'option under cursor, which can matter in presence of tree-sitter injected languages. Note: presence of tree-sitter parser in buffer is enough for this to take effect; runningvim.treesitter.start()/vim.treesitter.stop()affects only highlighting.This PR comments blank lines while 'tpope/vim-commentary' does not.
It is a carefully thought through decision for a more intuitive experience at a cost of extra rules for how blank lines should be commented and later uncommented.
Edit: after a feedback and discussion, blank lines are not taken into account when deciding whether a text range should be commented or uncommented. This allows uncommenting commented blocks which are separated only by blank lines. See this comment for an example.
This PR has much better speed on larger chunks of lines (like >1000). This is thanks to using
nvim_buf_set_lines()to set all new lines at once, and not withvim.fn.setline(). This comes at a price of somelockmarkshackery and affecting extmarks inside a range. See more details above singlenvim_buf_set_lines()call in this PR or a full PhD thesis in 'mini.comment'.