1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,33 @@
name: ci
on:
push:
paths:
- "**.lua"
branches:
- master
env:
PLUGIN_NAME: Comment
jobs:
docs:
runs-on: ubuntu-latest
name: emmylua to help doc
steps:
- uses: actions/checkout@v2
- name: Generating help
shell: bash
run: |
curl -Lq https://github.com/numToStr/lemmy-help/releases/latest/download/lemmy-help-x86_64-unknown-linux-gnu.tar.gz | tar xz
./lemmy-help -fact \
lua/Comment/{init.lua,config.lua} plugin/Comment.lua \
lua/Comment/{api.lua,ft.lua,utils.lua,opfunc.lua,extra.lua} > doc/${{env.PLUGIN_NAME}}.txt
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: ${{ github.head_ref }}
commit_message: "chore(docs): auto-generate `:help` doc"
file_pattern: doc/*.txt

View File

@ -0,0 +1,46 @@
# Compiled Lua sources
luac.out
# luarocks build files
*.src.rock
*.zip
*.tar.gz
# Object files
*.o
*.os
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
*.def
*.exp
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
tmp
scratch
# ignore generated doc tags
doc/tags

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Vikas Raj
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,360 @@
<h1 align="center">// Comment.nvim </h1>
<p align="center"><sup>⚡ Smart and Powerful commenting plugin for neovim ⚡</sup></p>
![Comment.nvim](https://user-images.githubusercontent.com/42532967/136532939-926a8350-84b7-4e78-b045-fe21b5947388.gif "Commenting go brrrr")
### ✨ Features
- Supports treesitter. [Read more](#treesitter)
- Supports `commentstring`. Read `:h comment.commentstring`
- Supports line (`//`) and block (`/* */`) comments
- Dot (`.`) repeat support for `gcc`, `gbc` and friends
- Count support for `[count]gcc` and `[count]gbc`
- Left-right (`gcw` `gc$`) and Up-Down (`gc2j` `gc4k`) motions
- Use with text-objects (`gci{` `gbat`)
- Supports pre and post hooks
- Ignore certain lines, powered by Lua regex
### 🚀 Installation
- With [lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
-- add this to your lua/plugins.lua, lua/plugins/init.lua, or the file you keep your other plugins:
{
'numToStr/Comment.nvim',
opts = {
-- add any options here
},
lazy = false,
}
```
- With [packer.nvim](https://github.com/wbthomason/packer.nvim)
```lua
use {
'numToStr/Comment.nvim',
config = function()
require('Comment').setup()
end
}
```
- With [vim-plug](https://github.com/junegunn/vim-plug)
```vim
Plug 'numToStr/Comment.nvim'
" Somewhere after plug#end()
lua require('Comment').setup()
```
### 📖 Getting Help
`Comment.nvim` provides help docs which can be accessed by running `:help comment-nvim`
<a id="setup"></a>
### ⚒️ Setup
First you need to call the `setup()` method to create the default mappings.
> **Note** - If you are facing **Keybindings are mapped but they are not working** issue then please try [this](https://github.com/numToStr/Comment.nvim/issues/115#issuecomment-1032290098)
- Lua
```lua
require('Comment').setup()
```
- VimL
```vim
lua << EOF
require('Comment').setup()
EOF
```
<a id="config"></a>
#### Configuration (optional)
Following are the **default** config for the [`setup()`](#setup). If you want to override, just modify the option that you want then it will be merged with the default config. Read `:h comment.config` for more info.
```lua
{
---Add a space b/w comment and the line
padding = true,
---Whether the cursor should stay at its position
sticky = true,
---Lines to be ignored while (un)comment
ignore = nil,
---LHS of toggle mappings in NORMAL mode
toggler = {
---Line-comment toggle keymap
line = 'gcc',
---Block-comment toggle keymap
block = 'gbc',
},
---LHS of operator-pending mappings in NORMAL and VISUAL mode
opleader = {
---Line-comment keymap
line = 'gc',
---Block-comment keymap
block = 'gb',
},
---LHS of extra mappings
extra = {
---Add comment on the line above
above = 'gcO',
---Add comment on the line below
below = 'gco',
---Add comment at the end of line
eol = 'gcA',
},
---Enable keybindings
---NOTE: If given `false` then the plugin won't create any mappings
mappings = {
---Operator-pending mapping; `gcc` `gbc` `gc[count]{motion}` `gb[count]{motion}`
basic = true,
---Extra mapping; `gco`, `gcO`, `gcA`
extra = true,
},
---Function to call before (un)comment
pre_hook = nil,
---Function to call after (un)comment
post_hook = nil,
}
```
### 🔥 Usage
When you call [`setup()`](#setup) method, `Comment.nvim` sets up some basic mapping which can be used in NORMAL and VISUAL mode to get you started with the pleasure of commenting stuff out.
<a id="basic-mappings"></a>
#### Basic mappings
These mappings are enabled by default. (config: `mappings.basic`)
- NORMAL mode
```help
`gcc` - Toggles the current line using linewise comment
`gbc` - Toggles the current line using blockwise comment
`[count]gcc` - Toggles the number of line given as a prefix-count using linewise
`[count]gbc` - Toggles the number of line given as a prefix-count using blockwise
`gc[count]{motion}` - (Op-pending) Toggles the region using linewise comment
`gb[count]{motion}` - (Op-pending) Toggles the region using blockwise comment
```
- VISUAL mode
```help
`gc` - Toggles the region using linewise comment
`gb` - Toggles the region using blockwise comment
```
<a id="extra-mappings"></a>
#### Extra mappings
These mappings are enabled by default. (config: `mappings.extra`)
- NORMAL mode
```help
`gco` - Insert comment to the next line and enters INSERT mode
`gcO` - Insert comment to the previous line and enters INSERT mode
`gcA` - Insert comment to end of the current line and enters INSERT mode
```
##### Examples
```help
# Linewise
`gcw` - Toggle from the current cursor position to the next word
`gc$` - Toggle from the current cursor position to the end of line
`gc}` - Toggle until the next blank line
`gc5j` - Toggle 5 lines after the current cursor position
`gc8k` - Toggle 8 lines before the current cursor position
`gcip` - Toggle inside of paragraph
`gca}` - Toggle around curly brackets
# Blockwise
`gb2}` - Toggle until the 2 next blank line
`gbaf` - Toggle comment around a function (w/ LSP/treesitter support)
`gbac` - Toggle comment around a class (w/ LSP/treesitter support)
```
<a id="treesitter"></a>
### 🌳 Treesitter
This plugin has native **treesitter** support for calculating `commentstring` which works for multiple (injected/embedded) languages like Vue or Markdown. But due to the nature of the parsed tree, this implementation has some known limitations.
1. No `jsx/tsx` support. Its implementation was quite complicated.
2. Invalid comment on the region where one language ends and the other starts. [Read more](https://github.com/numToStr/Comment.nvim/pull/62#issuecomment-972790418)
3. Unexpected comment on a line with multiple languages. [#144](https://github.com/numToStr/Comment.nvim/issues/144)
For advance use cases, use [nvim-ts-context-commentstring](https://github.com/JoosepAlviste/nvim-ts-context-commentstring). See [`pre_hook`](#pre-hook) section for the integration.
> **Note** - This plugin does not depend on [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) however it is recommended in order to easily install tree-sitter parsers.
<a id="hooks"></a>
### 🎣 Hooks
There are two hook methods i.e `pre_hook` and `post_hook` which are called before comment and after comment respectively. Both should be provided during [`setup()`](#setup).
<a id="pre-hook"></a>
- `pre_hook` - Called with a `ctx` argument (Read `:h comment.utils.CommentCtx`) before (un)comment. Can optionally return a `commentstring` to be used for (un)commenting.
```lua
{
pre_hook = function(ctx)
if ctx.range.srow == ctx.range.erow then
-- do something with the current line
else
-- do something with lines range
end
end,
}
```
You can also integrate [nvim-ts-context-commentstring](https://github.com/JoosepAlviste/nvim-ts-context-commentstring#commentnvim) using `pre_hook` to easily comment `tsx/jsx` files.
> **Note** - `Comment.nvim` already supports [`treesitter`](#treesitter) out-of-the-box for all the languages except `tsx/jsx`.
```lua
{
pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook(),
}
```
<a id="post-hook"></a>
- `post_hook` - This method is called after (un)commenting. It receives the same `ctx` (Read `:h comment.utils.CommentCtx`) argument as [`pre_hook`](#pre_hook).
```lua
{
post_hook = function(ctx)
if ctx.range.srow == ctx.range.erow then
-- do something with the current line
else
-- do something with lines range
end
end,
}
```
The `post_hook` can be implemented to cover some niche use cases like the following:
- Using newlines instead of padding e.g. for commenting out code in C with `#if 0`. See an example [here](https://github.com/numToStr/Comment.nvim/issues/38#issuecomment-945082507).
- Duplicating the commented block (using `pre_hook`) and moving the cursor to the next block (using `post_hook`). See [this](https://github.com/numToStr/Comment.nvim/issues/70).
> NOTE: When pressing `gc`, `gb` and friends, `cmode` (Comment mode) inside `pre_hook` will always be toggle because when pre-hook is called, in that moment we don't know whether `gc` or `gb` will comment or uncomment the lines. But luckily, we do know this before `post_hook` and this will always receive either comment or uncomment status
### 🚫 Ignoring lines
You can use `ignore` to ignore certain lines during comment/uncomment. It can takes lua regex string or a function that returns a regex string and should be provided during [`setup()`](#setup).
> NOTE: Ignore only works when with linewise comment. This is by design. As ignoring lines in block comments doesn't make that much sense.
- With `string`
```lua
-- ignores empty lines
ignore = '^$'
-- ignores line that starts with `local` (excluding any leading whitespace)
ignore = '^(%s*)local'
-- ignores any lines similar to arrow function
ignore = '^const(.*)=(%s?)%((.*)%)(%s?)=>'
```
- With `function`
```lua
{
ignore = function()
-- Only ignore empty lines for lua files
if vim.bo.filetype == 'lua' then
return '^$'
end
end,
}
```
<a id="languages"></a>
### 🗨️ Filetypes + Languages
Most languages/filetypes have native support for comments via `commentstring` but there might be a filetype that is not supported. There are two ways to enable commenting for unsupported filetypes:
1. You can set `commentstring` for that particular filetype like the following. Read `:h commentstring` for more info.
```lua
vim.bo.commentstring = '//%s'
-- or
vim.api.nvim_command('set commentstring=//%s')
```
<a id="ft-lua"></a>
2. You can also use this plugin interface to store both line and block commentstring for the filetype. You can treat this as a more powerful version of the `commentstring`. Read `:h comment.ft` for more info.
```lua
local ft = require('Comment.ft')
-- 1. Using set function
ft
-- Set only line comment
.set('yaml', '#%s')
-- Or set both line and block commentstring
.set('javascript', {'//%s', '/*%s*/'})
-- 2. Metatable magic
ft.javascript = {'//%s', '/*%s*/'}
ft.yaml = '#%s'
-- Multiple filetypes
ft({'go', 'rust'}, ft.get('c'))
ft({'toml', 'graphql'}, '#%s')
```
> PR(s) are welcome to add more commentstring inside the plugin
### 🤝 Contributing
There are multiple ways to contribute reporting/fixing bugs, feature requests. You can also submit commentstring to this plugin by updating [ft.lua](./lua/Comment/ft.lua) and sending PR.
### 📺 Videos
- [TakeTuesday E02: Comment.nvim](https://www.youtube.com/watch?v=-InmtHhk2qM) by [TJ DeVries](https://github.com/tjdevries)
### 💐 Credits
- [tcomment](https://github.com/tomtom/tcomment_vim) - To be with me forever and motivated me to write this.
- [nvim-comment](https://github.com/terrortylor/nvim-comment) - Little and less powerful cousin. Also I took some code from it.
- [kommentary](https://github.com/b3nj5m1n/kommentary) - Nicely done plugin but lacks some features. But it helped me to design this plugin.
### 🚗 Roadmap
- Doc comment i.e `/**%s*/` (js), `///%s` (rust)
- Header comment
```lua
----------------------
-- This is a header --
----------------------
```

View File

@ -0,0 +1 @@
`Comment.nvim` now has `:help` docs 🎉. Read `:h comment.api` for the Lua API documentation and usage.

View File

@ -0,0 +1,910 @@
*comment-nvim.txt* For Neovim version 0.7 Last change: 2021 July 11
_____ _ _
/ ____/ / / (_)
/ / ___ _ __ ___ _ __ ___ ___ _ __ / /_ _ ____ ___ _ __ ___
/ / / _ \/ '_ ` _ \/ '_ ` _ \ / _ \ '_ \/ __/ / '_ \ \ / / / '_ ` _ \
/ /___/ (_) / / / / / / / / / / / __/ / / / /_ _/ / / \ V // / / / / / /
\_____\___//_/ /_/ /_/_/ /_/ /_/\___/_/ /_/\__(_)_/ /_/\_/ /_/_/ /_/ /_/
· Smart and Powerful comment plugin ·
==============================================================================
Table of Contents *comment.contents*
Introduction ···················································· |comment-nvim|
Usage ·························································· |comment.usage|
Configuration ················································· |comment.config|
Keybindings ·············································· |comment.keybindings|
Plug Mappings ················································ |comment.plugmap|
Core Lua API ····················································· |comment.api|
Language/Filetype detection ······································· |comment.ft|
Utilities ······················································ |comment.utils|
Operator-mode API ············································· |comment.opfunc|
Extra API ······················································ |comment.extra|
==============================================================================
Introduction *comment-nvim*
Comment.nvim is a smart and powerful comment plugin for neovim. It supports
dot-repeat, counts, line ('//') and block ('/* */') comments, and can be used
with motion and text-objects. It has native integration with |treesitter| to
support embedded filetypes like html, vue, markdown with codeblocks etc.
*comment.dotrepeat*
Comment.nvim uses |operatorfunc| combined with |g@| to support dot-repeat, and
various marks i.e., |'[| |']| |'<| |'>| to deduce the region with the {motion}
argument provided by 'operatorfunc'. See |comment.api.call|
*comment.commentstring*
Comment.nvim picks commentstring, either linewise/blockwise, from one of the
following places
1. 'pre_hook'
If a string is returned from this function then it will be used for
(un)commenting. See |comment.config|
2. |comment.ft|
Using the commentstring table inside the plugin (using treesitter).
Fallback to |commentstring|, if not found.
3. |commentstring| - Neovim's native commentstring for the filetype
Although Comment.nvim supports native 'commentstring' but unfortunately it has
the least priority. The caveat with this approach is that if someone sets the
`commentstring`, without returning it, from the 'pre_hook' and the current
filetype also exists in the |comment.ft| then the commenting will be done using
the string in |comment.ft| instead of using 'commentstring'. To override this
behavior, you have to manually return the 'commentstring' from 'pre_hook'.
*comment.sourcecode*
Comment.nvim is FOSS and distributed under MIT license. All the source code is
available at https://github.com/numToStr/Comment.nvim
==============================================================================
Usage *comment.usage*
Before using the plugin, you need to call the `setup()` function to create the
default mappings. If you want, you can also override the default configuration
by giving it a partial 'comment.config.Config' object, it will then be merged
with the default configuration.
C.setup({config?}) *comment.usage.setup*
Configures the plugin
Parameters: ~
{config?} (CommentConfig) User configuration
Returns: ~
(CommentConfig) Returns the modified config
See: ~
|comment.config|
Usage: ~
>lua
-- Use default configuration
require('Comment').setup()
-- or with custom configuration
require('Comment').setup({
ignore = '^$',
toggler = {
line = '<leader>cc',
block = '<leader>bc',
},
opleader = {
line = '<leader>c',
block = '<leader>b',
},
})
<
==============================================================================
Configuration *comment.config*
*comment.config.defaults*
Following is the default config for the |comment.usage.setup|. If you want to
override, just modify the option that you want, then it will be merged with the
default config.
>lua
{
padding = true,
sticky = true,
ignore = nil,
toggler = { line = 'gcc', block = 'gbc' },
opleader = { line = 'gc', block = 'gb' },
extra = { above = 'gcO', below = 'gco', eol = 'gcA' },
mappings = { basic = true, extra = true },
pre_hook = nil,
post_hook = nil,
}
<
CommentConfig *comment.config.CommentConfig*
Plugin's configuration
Fields: ~
{padding} (boolean|fun():boolean) Controls space between the comment
and the line (default: 'true')
{sticky} (boolean) Whether cursor should stay at the
same position. Only works in NORMAL
mode mappings (default: 'true')
{ignore} (string|fun():string) Lua pattern used to ignore lines
during (un)comment (default: 'nil')
{mappings} (Mappings|false) Enables |comment.keybindings|
NOTE: If given 'false', then the
plugin won't create any mappings
{toggler} (Toggler) See |comment.config.Toggler|
{opleader} (Opleader) See |comment.config.Opleader|
{extra} (ExtraMapping) See |comment.config.ExtraMapping|
{pre_hook} (fun(c:CommentCtx):string) Function to call before (un)comment.
It is called with a {ctx} argument
of type |comment.utils.CommentCtx|
(default: 'nil')
{post_hook} (fun(c:CommentCtx)) Function to call after (un)comment.
It is called with a {ctx} argument
of type |comment.utils.CommentCtx|
(default: 'nil')
Mappings *comment.config.Mappings*
Create default mappings
Fields: ~
{basic} (boolean) Enables operator-pending mapping; `gcc`, `gbc`,
`gc{motion}` and `gb{motion}` (default: 'true')
{extra} (boolean) Enable extra mapping; `gco`, `gcO` and `gcA`
(default: 'true')
Toggler *comment.config.Toggler*
LHS of toggle mappings in NORMAL
Fields: ~
{line} (string) Linewise comment (default: 'gcc')
{block} (string) Blockwise comment (default: 'gbc')
Opleader *comment.config.Opleader*
LHS of operator-mode mappings in NORMAL and VISUAL mode
Fields: ~
{line} (string) Linewise comment (default: 'gc')
{block} (string) Blockwise comment (default: 'gb')
ExtraMapping *comment.config.ExtraMapping*
LHS of extra mappings
Fields: ~
{below} (string) Inserts comment below (default: 'gco')
{above} (string) Inserts comment above (default: 'gcO')
{eol} (string) Inserts comment at the end of line (default: 'gcA')
Config:get() *comment.config:get*
Get the config
Returns: ~
(CommentConfig)
Usage: ~
>lua
require('Comment.config'):get()
<
==============================================================================
Keybindings *comment.keybindings*
Comment.nvim provides default keybindings for (un)comment your code. These
keybinds are enabled upon calling |comment.usage.setup| and can be configured
or disabled, if desired.
Basic: ~
*gc*
*gb*
*gc[count]{motion}*
*gb[count]{motion}*
Toggle comment on a region using linewise/blockwise comment. In 'NORMAL'
mode, it uses 'Operator-Pending' mode to listen for an operator/motion.
In 'VISUAL' mode it simply comment the selected region.
*gcc*
*gbc*
*[count]gcc*
*[count]gbc*
Toggle comment on the current line using linewise/blockwise comment. If
prefixed with a 'v:count' then it will comment over the number of lines
corresponding to the {count}. These are only available in 'NORMAL' mode.
Extra: ~
*gco* - Inserts comment below and enters INSERT mode
*gcO* - Inserts comment above and enters INSERT mode
*gcA* - Inserts comment at the end of line and enters INSERT mode
==============================================================================
Plug Mappings *comment.plugmap*
Comment.nvim provides <Plug> mappings for most commonly used actions. These
are enabled by default and can be used to make custom keybindings. All plug
mappings have support for dot-repeat except VISUAL mode keybindings. To create
custom comment function, check out 'comment.api' section.
*<Plug>(comment_toggle_linewise)*
*<Plug>(comment_toggle_blockwise)*
Toggle comment on a region with linewise/blockwise comment in NORMAL mode.
using |Operator-Pending| mode (or |g@|) to get the region to comment.
These powers the |gc| and |gb| keybindings.
*<Plug>(comment_toggle_linewise_current)*
*<Plug>(comment_toggle_blockwise_current)*
Toggle comment on the current line with linewise/blockwise comment in
NORMAL mode. These powers the |gcc| and 'gbc' keybindings.
*<Plug>(comment_toggle_linewise_count)*
*<Plug>(comment_toggle_blockwise_count)*
Toggle comment on a region using 'v:count' with linewise/blockwise comment
in NORMAL mode. These powers the |[count]gcc| and |[count]gbc| keybindings.
*<Plug>(comment_toggle_linewise_visual)*
*<Plug>(comment_toggle_blockwise_visual)*
Toggle comment on the selected region with linewise/blockwise comment in
NORMAL mode. These powers the |{visual}gc| and |{visual}gb| keybindings.
Usage: ~
>lua
-- Toggle current line or with count
vim.keymap.set('n', 'gcc', function()
return vim.v.count == 0
and '<Plug>(comment_toggle_linewise_current)'
or '<Plug>(comment_toggle_linewise_count)'
end, { expr = true })
-- Toggle in Op-pending mode
vim.keymap.set('n', 'gc', '<Plug>(comment_toggle_linewise)')
-- Toggle in VISUAL mode
vim.keymap.set('x', 'gc', '<Plug>(comment_toggle_linewise_visual)')
<
==============================================================================
Core Lua API *comment.api*
This module provides the core lua APIs which is used by the default keybindings
and <Plug> (Read |comment.plugmap|) mappings. These API can be used to setup your
own custom keybindings or to even make your (un)comment function.
*comment.api.toggle.linewise*
*comment.api.toggle.blockwise*
api.toggle *comment.api.toggle*
Provides API to toggle comments over a region, on current-line, or with a
count using line or block comment string.
Every function takes a {motion} argument, except '*.count()' function which
takes an {count} argument, and an optional {config} parameter.
Type: ~
(table) A metatable containing API functions
See: ~
|comment.opfunc.OpMotion|
|comment.config|
Usage: ~
>lua
local api = require('Comment.api')
local config = require('Comment.config'):get()
api.toggle.linewise(motion, config?)
api.toggle.linewise.current(motion?, config?)
api.toggle.linewise.count(count, config?)
api.toggle.blockwise(motion, config?)
api.toggle.blockwise.current(motion?, config?)
api.toggle.blockwise.count(count, config?)
-- Toggle current line (linewise) using C-/
vim.keymap.set('n', '<C-_>', api.toggle.linewise.current)
-- Toggle current line (blockwise) using C-\
vim.keymap.set('n', '<C-\\>', api.toggle.blockwise.current)
-- Toggle lines (linewise) with dot-repeat support
-- Example: <leader>gc3j will comment 4 lines
vim.keymap.set(
'n', '<leader>gc', api.call('toggle.linewise', 'g@'),
{ expr = true }
)
-- Toggle lines (blockwise) with dot-repeat support
-- Example: <leader>gb3j will comment 4 lines
vim.keymap.set(
'n', '<leader>gb', api.call('toggle.blockwise', 'g@'),
{ expr = true }
)
local esc = vim.api.nvim_replace_termcodes(
'<ESC>', true, false, true
)
-- Toggle selection (linewise)
vim.keymap.set('x', '<leader>c', function()
vim.api.nvim_feedkeys(esc, 'nx', false)
api.toggle.linewise(vim.fn.visualmode())
end)
-- Toggle selection (blockwise)
vim.keymap.set('x', '<leader>b', function()
vim.api.nvim_feedkeys(esc, 'nx', false)
api.toggle.blockwise(vim.fn.visualmode())
end)
<
*comment.api.comment.linewise*
*comment.api.comment.blockwise*
api.comment *comment.api.comment*
Provides API to (only) comment a region, on current-line, or with a
count using line or block comment string.
Every function takes a {motion} argument, except '*.count()' function which
takes an {count} argument, and an optional {config} parameter.
Type: ~
(table) A metatable containing API functions
See: ~
|comment.opfunc.OpMotion|
|comment.config|
Usage: ~
>lua
local api = require('Comment.api')
local config = require('Comment.config'):get()
api.comment.linewise(motion, config?)
api.comment.linewise.current(motion?, config?)
api.comment.linewise.count(count, config?)
api.comment.blockwise(motion, config?)
api.comment.blockwise.current(motion?, config?)
api.comment.blockwise.count(count, config?)
<
*comment.api.uncomment.linewise*
*comment.api.uncomment.blockwise*
api.uncomment *comment.api.uncomment*
Provides API to (only) uncomment a region, on current-line, or with a
count using line or block comment string.
Every function takes a {motion} argument, except '*.count()' function which
takes an {count} argument, and an optional {config} parameter.
Type: ~
(table) A metatable containing API functions
See: ~
|comment.opfunc.OpMotion|
|comment.config|
Usage: ~
>lua
local api = require('Comment.api')
local config = require('Comment.config'):get()
api.uncomment.linewise(motion, config?)
api.uncomment.linewise.current(motion?, config?)
api.uncomment.linewise.count(count, config?)
api.uncomment.blockwise(motion, config?)
api.uncomment.blockwise.current(motion?, config?)
api.uncomment.blockwise.count(count, config?)
<
api.insert *comment.api.insert*
Provides API to to insert comment on previous, next or at the end-of-line.
Every function takes an optional {config} parameter.
Type: ~
(table) A metatable containing API functions
See: ~
|comment.config|
Usage: ~
>lua
local api = require('Comment.api')
local config = require('Comment.config'):get()
api.insert.linewise.above(config?)
api.insert.linewise.below(config?)
api.insert.linewise.eol(config?)
api.insert.blockwise.above(config?)
api.insert.blockwise.below(config?)
api.insert.blockwise.eol(config?)
<
api.locked({cb}) *comment.api.locked*
Wraps the given API function with 'lockmarks' to preserve marks/jumps
Parameters: ~
{cb} (string) Name of API function
Returns: ~
(fun(motion:OpMotion)) Callback function
See: ~
|lockmarks|
|comment.opfunc.OpMotion|
Usage: ~
>lua
local api = require('Comment.api')
vim.keymap.set(
'n', '<leader>c', api.locked('toggle.linewise.current')
)
local esc = vim.api.nvim_replace_termcodes(
'<ESC>', true, false, true
)
vim.keymap.set('x', '<leader>c', function()
vim.api.nvim_feedkeys(esc, 'nx', false)
api.locked('toggle.linewise')(vim.fn.visualmode())
end)
-- NOTE: `locked` method is just a wrapper around `lockmarks`
vim.api.nvim_command([[
lockmarks lua require('Comment.api').toggle.linewise.current()
]])
<
api.call({cb}, {op}) *comment.api.call*
Callback function which does the following
1. Sets 'operatorfunc' for dot-repeat
2. Preserves jumps and marks
3. Stores last cursor position
Parameters: ~
{cb} (string) Name of the API function to call
{op} ("g@"|"g@$") Operator-mode expression
Returns: ~
(fun():string) Keymap RHS callback
See: ~
|g@|
|operatorfunc|
Usage: ~
>lua
local api = require('Comment.api')
vim.keymap.set(
'n', 'gc', api.call('toggle.linewise', 'g@'),
{ expr = true }
)
vim.keymap.set(
'n', 'gcc', api.call('toggle.linewise.current', 'g@$'),
{ expr = true }
)
<
==============================================================================
Language/Filetype detection *comment.ft*
This module is the core of filetype and commentstring detection and uses the
|lua-treesitter| APIs to accurately detect filetype and gives the corresponding
commentstring, stored inside the plugin, for the filetype/langauge.
Compound (dot-separated) filetypes are also supported i.e. 'ansible.yaml',
'ios.swift' etc. The commentstring resolution will be done from left to right.
For example, If the filetype is 'ansible.yaml' then 'ansible' commenstring will
be used if found otherwise it'll fallback to 'yaml'. Read `:h 'filetype'`
ft.set({lang}, {val}) *comment.ft.set*
Sets a commentstring(s) for a filetype/language
Parameters: ~
{lang} (string) Filetype/Language of the buffer
{val} (string|string[])
Returns: ~
(table) Returns itself
Usage: ~
>lua
local ft = require('Comment.ft')
--1. Using method signature
-- Set only line comment or both
-- You can also chain the set calls
ft.set('yaml', '#%s').set('javascript', {'//%s', '/*%s*/'})
-- 2. Metatable magic
ft.javascript = {'//%s', '/*%s*/'}
ft.yaml = '#%s'
-- 3. Multiple filetypes
ft({'go', 'rust'}, {'//%s', '/*%s*/'})
ft({'toml', 'graphql'}, '#%s')
<
ft.get({lang}, {ctype?}) *comment.ft.get*
Get line/block/both commentstring(s) for a given filetype
Parameters: ~
{lang} (string) Filetype/Language of the buffer
{ctype?} (integer) See |comment.utils.ctype|. If given `nil`, it'll
return a copy of { line, block } commentstring.
Returns: ~
(nil|string|string[]) Returns stored commentstring
Usage: ~
>lua
local ft = require('Comment.ft')
local U = require('Comment.utils')
-- 1. Primary filetype
ft.get('rust', U.ctype.linewise) -- `//%s`
ft.get('rust') -- `{ '//%s', '/*%s*/' }`
-- 2. Compound filetype
-- NOTE: This will return `yaml` commenstring(s),
-- as `ansible` commentstring is not found.
ft.get('ansible.yaml', U.ctype.linewise) -- `#%s`
ft.get('ansible.yaml') -- { '#%s' }
<
ft.contains({tree}, {range}) *comment.ft.contains*
Get a language tree for a given range by walking the parse tree recursively.
This uses 'lua-treesitter' API under the hood. This can be used to calculate
language of a particular region which embedded multiple filetypes like html,
vue, markdown etc.
NOTE: This ignores `tree-sitter-comment` parser, if installed.
Parameters: ~
{tree} (userdata) Parse tree to be walked
{range} (integer[]) Range to check
{start_row, start_col, end_row, end_col}
Returns: ~
(userdata) Returns a |treesitter-languagetree|
See: ~
|treesitter-languagetree|
|lua-treesitter-core|
Usage: ~
>lua
local ok, parser = pcall(vim.treesitter.get_parser, 0)
assert(ok, "No parser found!")
local tree = require('Comment.ft').contains(parser, {0, 0, -1, 0})
print('Lang:', tree:lang())
<
ft.calculate({ctx}) *comment.ft.calculate*
Calculate commentstring with the power of treesitter
Parameters: ~
{ctx} (CommentCtx)
Returns: ~
(nil|string) Commentstring
See: ~
|comment.utils.CommentCtx|
==============================================================================
Utilities *comment.utils*
CommentCtx *comment.utils.CommentCtx*
Comment context
Fields: ~
{ctype} (integer) See |comment.utils.ctype|
{cmode} (integer) See |comment.utils.cmode|
{cmotion} (integer) See |comment.utils.cmotion|
{range} (CommentRange)
CommentRange *comment.utils.CommentRange*
Range of the selection that needs to be commented
Fields: ~
{srow} (integer) Starting row
{scol} (integer) Starting column
{erow} (integer) Ending row
{ecol} (integer) Ending column
CommentMode *comment.utils.CommentMode*
Comment modes - Can be manual or computed via operator-mode
Fields: ~
{toggle} (integer) Toggle action
{comment} (integer) Comment action
{uncomment} (integer) Uncomment action
U.cmode *comment.utils.cmode*
An object containing comment modes
Type: ~
(CommentMode)
CommentType *comment.utils.CommentType*
Comment types
Fields: ~
{linewise} (integer) Use linewise commentstring
{blockwise} (integer) Use blockwise commentstring
U.ctype *comment.utils.ctype*
An object containing comment types
Type: ~
(CommentType)
CommentMotion *comment.utils.CommentMotion*
Comment motion types
Fields: ~
{line} (integer) Line motion (ie. 'gc2j')
{char} (integer) Character/left-right motion (ie. 'gc2w')
{block} (integer) Visual operator-pending motion
{v} (integer) Visual motion (ie. 'v3jgc')
{V} (integer) Visual-line motion (ie. 'V10kgc')
U.cmotion *comment.utils.cmotion*
An object containing comment motions
Type: ~
(CommentMotion)
U.get_region({opmode?}) *comment.utils.get_region*
Get region for line movement or visual selection
NOTE: Returns the current line region, if `opmode` is not given.
Parameters: ~
{opmode?} (OpMotion)
Returns: ~
(CommentRange)
U.get_count_lines({count}) *comment.utils.get_count_lines*
Get lines from the current position to the given count
Parameters: ~
{count} (integer) Probably 'vim.v.count'
Returns: ~
(string[]) List of lines
(CommentRange)
U.get_lines({range}) *comment.utils.get_lines*
Get lines from a NORMAL/VISUAL mode
Parameters: ~
{range} (CommentRange)
Returns: ~
(string[]) List of lines
U.unwrap_cstr({cstr}) *comment.utils.unwrap_cstr*
Validates and unwraps the given commentstring
Parameters: ~
{cstr} (string) See 'commentstring'
Returns: ~
(string) Left side of the commentstring
(string) Right side of the commentstring
U.parse_cstr({cfg}, {ctx}) *comment.utils.parse_cstr*
Parses commentstring from the following places in the respective order
1. pre_hook - commentstring returned from the function
2. ft.lua - commentstring table bundled with the plugin
3. commentstring - Neovim's native. See 'commentstring'
Parameters: ~
{cfg} (CommentConfig)
{ctx} (CommentCtx)
Returns: ~
(string) Left side of the commentstring
(string) Right side of the commentstring
*comment.utils.commenter*
U.commenter({left}, {right}, {padding}, {scol?}, {ecol?}, {tabbed?})
Returns a closure which is used to do comments
If given {string[]} to the closure then it will do blockwise comment
else linewise comment will be done with the given {string}
Parameters: ~
{left} (string) Left side of the commentstring
{right} (string) Right side of the commentstring
{padding} (boolean) Is padding enabled?
{scol?} (integer) Starting column
{ecol?} (integer) Ending column
{tabbed?} (boolean) Using tab indentation
Returns: ~
(fun(line:string|string[]):string|string[])
*comment.utils.uncommenter*
U.uncommenter({left}, {right}, {padding}, {scol?}, {ecol?})
Returns a closure which is used to uncomment a line
If given {string[]} to the closure then it will block uncomment
else linewise uncomment will be done with the given {string}
Parameters: ~
{left} (string) Left side of the commentstring
{right} (string) Right side of the commentstring
{padding} (boolean) Is padding enabled?
{scol?} (integer) Starting column
{ecol?} (integer) Ending column
Returns: ~
(fun(line:string|string[]):string|string[])
*comment.utils.is_commented*
U.is_commented({left}, {right}, {padding}, {scol?}, {ecol?})
Check if the given string is commented or not
If given {string[]} to the closure, it will check the first and last line
with LHS and RHS of commentstring respectively else it will check the given
line with LHS and RHS (if given) of the commenstring
Parameters: ~
{left} (string) Left side of the commentstring
{right} (string) Right side of the commentstring
{padding} (boolean) Is padding enabled?
{scol?} (integer) Starting column
{ecol?} (integer) Ending column
Returns: ~
(fun(line:string|string[]):boolean)
==============================================================================
Operator-mode API *comment.opfunc*
Underlying functions that powers the |comment.api.toggle|, |comment.api.comment|,
and |comment.api.uncomment| lua API.
OpMotion *comment.opfunc.OpMotion*
Vim operator-mode motion enum. Read |:map-operator|
Variants: ~
("line") Vertical motion
("char") Horizontal motion
("v") Visual Block motion
("V") Visual Line motion
*comment.opfunc.opfunc*
Op.opfunc({motion?}, {cfg}, {cmode}, {ctype})
Common operatorfunc callback
This function contains the core logic for comment/uncomment
Parameters: ~
{motion?} (OpMotion) If given 'nil', it'll only (un)comment
the current line
{cfg} (CommentConfig)
{cmode} (integer) See |comment.utils.cmode|
{ctype} (integer) See |comment.utils.ctype|
*comment.opfunc.count*
Op.count({count}, {cfg}, {cmode}, {ctype})
Line commenting with count
Parameters: ~
{count} (integer) Value of |v:count|
{cfg} (CommentConfig)
{cmode} (integer) See |comment.utils.cmode|
{ctype} (integer) See |comment.utils.ctype|
OpFnParams *comment.opfunc.OpFnParams*
Operator-mode function parameters
Fields: ~
{cfg} (CommentConfig)
{cmode} (integer) See |comment.utils.cmode|
{lines} (string[]) List of lines
{rcs} (string) RHS of commentstring
{lcs} (string) LHS of commentstring
{range} (CommentRange)
Op.linewise({param}) *comment.opfunc.linewise*
Line commenting
Parameters: ~
{param} (OpFnParams)
Returns: ~
(integer) Returns a calculated comment mode
Op.blockwise({param}, {partial?}) *comment.opfunc.blockwise*
Full/Partial/Current-Line Block commenting
Parameters: ~
{param} (OpFnParams)
{partial?} (boolean) Comment the partial region (visual mode)
Returns: ~
(integer) Returns a calculated comment mode
==============================================================================
Extra API *comment.extra*
Underlying functions that powers the |comment.api.insert| lua API.
extra.insert_below({ctype}, {cfg}) *comment.extra.insert_below*
Add a comment below the current line and goes to INSERT mode
Parameters: ~
{ctype} (integer) See |comment.utils.ctype|
{cfg} (CommentConfig)
extra.insert_above({ctype}, {cfg}) *comment.extra.insert_above*
Add a comment above the current line and goes to INSERT mode
Parameters: ~
{ctype} (integer) See |comment.utils.ctype|
{cfg} (CommentConfig)
extra.insert_eol({ctype}, {cfg}) *comment.extra.insert_eol*
Add a comment at the end of current line and goes to INSERT mode
Parameters: ~
{ctype} (integer) See |comment.utils.ctype|
{cfg} (CommentConfig)
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1 @@
`Comment.nvim` now has `:help` docs 🎉. Read `:h comment.plugmap` for the `<Plug>` mappings documentation and usage.

View File

@ -0,0 +1,253 @@
---@mod comment.api Core Lua API
---@brief [[
---This module provides the core lua APIs which is used by the default keybindings
---and <Plug> (Read |comment.plugmap|) mappings. These API can be used to setup your
---own custom keybindings or to even make your (un)comment function.
---@brief ]]
local Config = require('Comment.config')
local U = require('Comment.utils')
local Op = require('Comment.opfunc')
local Ex = require('Comment.extra')
local A = vim.api
local api, core = {}, {}
---API metamethods
---@param that table
---@param ctype CommentType
---@return table
function core.__index(that, ctype)
local idxd = {}
local mode, type = that.cmode, U.ctype[ctype]
---To comment the current-line
---NOTE:
---In current-line linewise method, 'opmode' is not useful which is always equals to `char`
---but we need 'nil' here which is used for current-line
function idxd.current(_, cfg)
U.catch(Op.opfunc, nil, cfg or Config:get(), mode, type)
end
---To comment lines with a count
function idxd.count(count, cfg)
U.catch(Op.count, count or A.nvim_get_vvar('count'), cfg or Config:get(), mode, type)
end
---@private
---To comment lines with a count, also dot-repeatable
---WARN: This is not part of the API but anyone case use it, if they want
function idxd.count_repeat(_, count, cfg)
idxd.count(count, cfg)
end
return setmetatable({}, {
__index = idxd,
__call = function(_, motion, cfg)
U.catch(Op.opfunc, motion, cfg or Config:get(), mode, type)
end,
})
end
---@tag comment.api.toggle.linewise
---@tag comment.api.toggle.blockwise
---Provides API to toggle comments over a region, on current-line, or with a
---count using line or block comment string.
---
---Every function takes a {motion} argument, except '*.count()' function which
---takes an {count} argument, and an optional {config} parameter.
---@type table A metatable containing API functions
---@see comment.opfunc.OpMotion
---@see comment.config
---@usage [[
---local api = require('Comment.api')
---local config = require('Comment.config'):get()
---
---api.toggle.linewise(motion, config?)
---api.toggle.linewise.current(motion?, config?)
---api.toggle.linewise.count(count, config?)
---
---api.toggle.blockwise(motion, config?)
---api.toggle.blockwise.current(motion?, config?)
---api.toggle.blockwise.count(count, config?)
---
----- Toggle current line (linewise) using C-/
---vim.keymap.set('n', '<C-_>', api.toggle.linewise.current)
---
----- Toggle current line (blockwise) using C-\
---vim.keymap.set('n', '<C-\\>', api.toggle.blockwise.current)
---
----- Toggle lines (linewise) with dot-repeat support
----- Example: <leader>gc3j will comment 4 lines
---vim.keymap.set(
--- 'n', '<leader>gc', api.call('toggle.linewise', 'g@'),
--- { expr = true }
---)
---
----- Toggle lines (blockwise) with dot-repeat support
----- Example: <leader>gb3j will comment 4 lines
---vim.keymap.set(
--- 'n', '<leader>gb', api.call('toggle.blockwise', 'g@'),
--- { expr = true }
---)
---
---local esc = vim.api.nvim_replace_termcodes(
--- '<ESC>', true, false, true
---)
---
----- Toggle selection (linewise)
---vim.keymap.set('x', '<leader>c', function()
--- vim.api.nvim_feedkeys(esc, 'nx', false)
--- api.toggle.linewise(vim.fn.visualmode())
---end)
---
----- Toggle selection (blockwise)
---vim.keymap.set('x', '<leader>b', function()
--- vim.api.nvim_feedkeys(esc, 'nx', false)
--- api.toggle.blockwise(vim.fn.visualmode())
---end)
---@usage ]]
api.toggle = setmetatable({ cmode = U.cmode.toggle }, core)
---@tag comment.api.comment.linewise
---@tag comment.api.comment.blockwise
---Provides API to (only) comment a region, on current-line, or with a
---count using line or block comment string.
---
---Every function takes a {motion} argument, except '*.count()' function which
---takes an {count} argument, and an optional {config} parameter.
---@type table A metatable containing API functions
---@see comment.opfunc.OpMotion
---@see comment.config
---@usage [[
---local api = require('Comment.api')
---local config = require('Comment.config'):get()
---
---api.comment.linewise(motion, config?)
---api.comment.linewise.current(motion?, config?)
---api.comment.linewise.count(count, config?)
---
---api.comment.blockwise(motion, config?)
---api.comment.blockwise.current(motion?, config?)
---api.comment.blockwise.count(count, config?)
---@usage ]]
api.comment = setmetatable({ cmode = U.cmode.comment }, core)
---@tag comment.api.uncomment.linewise
---@tag comment.api.uncomment.blockwise
---Provides API to (only) uncomment a region, on current-line, or with a
---count using line or block comment string.
---
---Every function takes a {motion} argument, except '*.count()' function which
---takes an {count} argument, and an optional {config} parameter.
---@type table A metatable containing API functions
---@see comment.opfunc.OpMotion
---@see comment.config
---@usage [[
---local api = require('Comment.api')
---local config = require('Comment.config'):get()
---
---api.uncomment.linewise(motion, config?)
---api.uncomment.linewise.current(motion?, config?)
---api.uncomment.linewise.count(count, config?)
---
---api.uncomment.blockwise(motion, config?)
---api.uncomment.blockwise.current(motion?, config?)
---api.uncomment.blockwise.count(count, config?)
---@usage ]]
api.uncomment = setmetatable({ cmode = U.cmode.uncomment }, core)
---Provides API to to insert comment on previous, next or at the end-of-line.
---Every function takes an optional {config} parameter.
---@type table A metatable containing API functions
---@see comment.config
---@usage [[
---local api = require('Comment.api')
---local config = require('Comment.config'):get()
---
---api.insert.linewise.above(config?)
---api.insert.linewise.below(config?)
---api.insert.linewise.eol(config?)
---
---api.insert.blockwise.above(config?)
---api.insert.blockwise.below(config?)
---api.insert.blockwise.eol(config?)
---@usage ]]
api.insert = setmetatable({}, {
__index = function(_, ctype)
return {
above = function(cfg)
U.catch(Ex.insert_above, U.ctype[ctype], cfg or Config:get())
end,
below = function(cfg)
U.catch(Ex.insert_below, U.ctype[ctype], cfg or Config:get())
end,
eol = function(cfg)
U.catch(Ex.insert_eol, U.ctype[ctype], cfg or Config:get())
end,
}
end,
})
---Wraps the given API function with 'lockmarks' to preserve marks/jumps
---@param cb string Name of API function
---@return fun(motion:OpMotion) #Callback function
---@see lockmarks
---@see comment.opfunc.OpMotion
---@usage [[
---local api = require('Comment.api')
---
---vim.keymap.set(
--- 'n', '<leader>c', api.locked('toggle.linewise.current')
---)
---
---local esc = vim.api.nvim_replace_termcodes(
--- '<ESC>', true, false, true
---)
---vim.keymap.set('x', '<leader>c', function()
--- vim.api.nvim_feedkeys(esc, 'nx', false)
--- api.locked('toggle.linewise')(vim.fn.visualmode())
---end)
---
----- NOTE: `locked` method is just a wrapper around `lockmarks`
---vim.api.nvim_command([[
--- lockmarks lua require('Comment.api').toggle.linewise.current()
---]])
---@usage ]]
function api.locked(cb)
return function(motion)
return A.nvim_command(
('lockmarks lua require("Comment.api").%s(%s)'):format(cb, motion and ('%q'):format(motion))
)
end
end
---Callback function which does the following
--- 1. Sets 'operatorfunc' for dot-repeat
--- 2. Preserves jumps and marks
--- 3. Stores last cursor position
---@param cb string Name of the API function to call
---@param op '"g@"'|'"g@$"' Operator-mode expression
---@return fun():string #Keymap RHS callback
---@see g@
---@see operatorfunc
---@usage [[
---local api = require('Comment.api')
---vim.keymap.set(
--- 'n', 'gc', api.call('toggle.linewise', 'g@'),
--- { expr = true }
---)
---vim.keymap.set(
--- 'n', 'gcc', api.call('toggle.linewise.current', 'g@$'),
--- { expr = true }
---)
---@usage ]]
function api.call(cb, op)
return function()
A.nvim_set_option('operatorfunc', ("v:lua.require'Comment.api'.locked'%s'"):format(cb))
Config.position = Config:get().sticky and A.nvim_win_get_cursor(0) or nil
return op
end
end
return api

View File

@ -0,0 +1,134 @@
---@mod comment.config Configuration
---@tag comment.config.defaults
---@brief [[
---Following is the default config for the |comment.usage.setup|. If you want to
---override, just modify the option that you want, then it will be merged with the
---default config.
--->lua
--- {
--- padding = true,
--- sticky = true,
--- ignore = nil,
--- toggler = { line = 'gcc', block = 'gbc' },
--- opleader = { line = 'gc', block = 'gb' },
--- extra = { above = 'gcO', below = 'gco', eol = 'gcA' },
--- mappings = { basic = true, extra = true },
--- pre_hook = nil,
--- post_hook = nil,
--- }
---<
---@brief ]]
---Plugin's configuration
---@class CommentConfig
---Controls space between the comment
---and the line (default: 'true')
---@field padding boolean|fun():boolean
---Whether cursor should stay at the
---same position. Only works in NORMAL
---mode mappings (default: 'true')
---@field sticky boolean
---Lua pattern used to ignore lines
---during (un)comment (default: 'nil')
---@field ignore string|fun():string
---Enables |comment.keybindings|
---NOTE: If given 'false', then the
---plugin won't create any mappings
---@field mappings Mappings|false
---@field toggler Toggler See |comment.config.Toggler|
---@field opleader Opleader See |comment.config.Opleader|
---@field extra ExtraMapping See |comment.config.ExtraMapping|
---Function to call before (un)comment.
---It is called with a {ctx} argument
---of type |comment.utils.CommentCtx|
---(default: 'nil')
---@field pre_hook fun(c: CommentCtx): string
---Function to call after (un)comment.
---It is called with a {ctx} argument
---of type |comment.utils.CommentCtx|
---(default: 'nil')
---@field post_hook fun(c: CommentCtx)
---Create default mappings
---@class Mappings
---Enables operator-pending mapping; `gcc`, `gbc`,
---`gc{motion}` and `gb{motion}` (default: 'true')
---@field basic boolean
---Enable extra mapping; `gco`, `gcO` and `gcA`
---(default: 'true')
---@field extra boolean
---LHS of toggle mappings in NORMAL
---@class Toggler
---@field line string Linewise comment (default: 'gcc')
---@field block string Blockwise comment (default: 'gbc')
---LHS of operator-mode mappings in NORMAL and VISUAL mode
---@class Opleader
---@field line string Linewise comment (default: 'gc')
---@field block string Blockwise comment (default: 'gb')
---LHS of extra mappings
---@class ExtraMapping
---@field below string Inserts comment below (default: 'gco')
---@field above string Inserts comment above (default: 'gcO')
---@field eol string Inserts comment at the end of line (default: 'gcA')
---@private
---@class RootConfig
---@field config CommentConfig
---@field position? integer[] To be used to restore cursor position
local Config = {
state = {},
config = {
padding = true,
sticky = true,
mappings = {
basic = true,
extra = true,
},
toggler = {
line = 'gcc',
block = 'gbc',
},
opleader = {
line = 'gc',
block = 'gb',
},
extra = {
above = 'gcO',
below = 'gco',
eol = 'gcA',
},
},
}
---@package
---Updates the default config
---@param cfg? CommentConfig
---@return RootConfig
---@see comment.usage.setup
---@usage `require('Comment.config'):set({config})`
function Config:set(cfg)
if cfg then
self.config = vim.tbl_deep_extend('force', self.config, cfg)
end
return self
end
---Get the config
---@return CommentConfig
---@usage `require('Comment.config'):get()`
function Config:get()
return self.config
end
---@export Config
return setmetatable(Config, {
__index = function(this, k)
return this.state[k]
end,
__newindex = function(this, k, v)
this.state[k] = v
end,
})

View File

@ -0,0 +1,105 @@
---@mod comment.extra Extra API
---@brief [[
---Underlying functions that powers the |comment.api.insert| lua API.
---@brief ]]
local U = require('Comment.utils')
local A = vim.api
local extra = {}
-- FIXME This prints `a` in i_CTRL-o
---Moves the cursor and enters INSERT mode
---@param row integer Starting row
---@param col integer Ending column
local function move_n_insert(row, col)
A.nvim_win_set_cursor(0, { row, col })
A.nvim_feedkeys('a', 'ni', true)
end
---@param lnum integer Line index
---@param ctype integer
---@param cfg CommentConfig
local function ins_on_line(lnum, ctype, cfg)
local row, col = unpack(A.nvim_win_get_cursor(0))
---@type CommentCtx
local ctx = {
cmode = U.cmode.comment,
cmotion = U.cmotion.line,
ctype = ctype,
range = { srow = row, scol = col, erow = row, ecol = col },
}
local srow = row + lnum
local lcs, rcs = U.parse_cstr(cfg, ctx)
local padding = U.get_pad(U.is_fn(cfg.padding))
-- We need RHS of cstr, if we are doing block comments or if RHS exists
-- because even in line comment RHS do exists for some filetypes like jsx_element, ocaml
local if_rcs = U.is_empty(rcs) and rcs or padding .. rcs
A.nvim_buf_set_lines(0, srow, srow, false, { lcs .. padding .. if_rcs })
A.nvim_win_set_cursor(0, { srow + 1, 0 })
A.nvim_command('normal! ==')
move_n_insert(srow + 1, #A.nvim_get_current_line() - #if_rcs - 1)
U.is_fn(cfg.post_hook, ctx)
end
---Add a comment below the current line and goes to INSERT mode
---@param ctype integer See |comment.utils.ctype|
---@param cfg CommentConfig
function extra.insert_below(ctype, cfg)
ins_on_line(0, ctype, cfg)
end
---Add a comment above the current line and goes to INSERT mode
---@param ctype integer See |comment.utils.ctype|
---@param cfg CommentConfig
function extra.insert_above(ctype, cfg)
ins_on_line(-1, ctype, cfg)
end
---Add a comment at the end of current line and goes to INSERT mode
---@param ctype integer See |comment.utils.ctype|
---@param cfg CommentConfig
function extra.insert_eol(ctype, cfg)
local srow, scol = unpack(A.nvim_win_get_cursor(0))
---@type CommentCtx
local ctx = {
cmode = U.cmode.comment,
cmotion = U.cmotion.line,
ctype = ctype,
range = { srow = srow, scol = scol, erow = srow, ecol = scol },
}
local lcs, rcs = U.parse_cstr(cfg, ctx)
local line = A.nvim_get_current_line()
local padding = U.get_pad(U.is_fn(cfg.padding))
-- We need RHS of cstr, if we are doing block comments or if RHS exists
-- because even in line comment RHS do exists for some filetypes like jsx_element, ocaml
local if_rcs = U.is_empty(rcs) and rcs or padding .. rcs
local ecol
if U.is_empty(line) then
-- If line is empty, start comment at the correct indentation level
A.nvim_set_current_line(lcs .. padding .. if_rcs)
A.nvim_command('normal! ==')
ecol = #A.nvim_get_current_line() - #if_rcs - 1
else
-- NOTE:
-- 1. Python is the only language that recommends 2 spaces between the statement and the comment
-- 2. Other than that, I am assuming that the users wants a space b/w the end of line and start of the comment
local space = vim.bo.filetype == 'python' and ' ' or ' '
local ll = line .. space .. lcs .. padding
A.nvim_set_current_line(ll .. if_rcs)
ecol = #ll - 1
end
move_n_insert(srow, ecol)
U.is_fn(cfg.post_hook, ctx)
end
return extra

View File

@ -0,0 +1,297 @@
---@mod comment.ft Language/Filetype detection
---@brief [[
---This module is the core of filetype and commentstring detection and uses the
---|lua-treesitter| APIs to accurately detect filetype and gives the corresponding
---commentstring, stored inside the plugin, for the filetype/langauge.
---
---Compound (dot-separated) filetypes are also supported i.e. 'ansible.yaml',
---'ios.swift' etc. The commentstring resolution will be done from left to right.
---For example, If the filetype is 'ansible.yaml' then 'ansible' commenstring will
---be used if found otherwise it'll fallback to 'yaml'. Read `:h 'filetype'`
---@brief ]]
local A = vim.api
---Common commentstring shared b/w multiple languages
local M = {
cxx_l = '//%s',
cxx_b = '/*%s*/',
dbl_hash = '##%s',
dash = '--%s',
dash_bracket = '--[[%s]]',
handlebars = '{{!--%s--}}',
hash = '#%s',
hash_bracket = '#[[%s]]',
haskell_b = '{-%s-}',
fsharp_b = '(*%s*)',
html = '<!--%s-->',
latex = '%%s',
semicolon = ';%s',
lisp_l = ';;%s',
lisp_b = '#|%s|#',
twig = '{#%s#}',
vim = '"%s',
lean_b = '/-%s-/',
}
---Lang table that contains commentstring (linewise/blockwise) for multiple filetypes
---Structure = { filetype = { linewise, blockwise } }
---@type table<string,string[]>
local L = setmetatable({
arduino = { M.cxx_l, M.cxx_b },
applescript = { M.hash },
astro = { M.html },
autohotkey = { M.semicolon, M.cxx_b },
bash = { M.hash },
beancount = { M.semicolon },
bib = { M.latex },
c = { M.cxx_l, M.cxx_b },
cabal = { M.dash },
cmake = { M.hash, M.hash_bracket },
conf = { M.hash },
conkyrc = { M.dash, M.dash_bracket },
coq = { M.fsharp_b },
cpp = { M.cxx_l, M.cxx_b },
cs = { M.cxx_l, M.cxx_b },
css = { M.cxx_b, M.cxx_b },
cuda = { M.cxx_l, M.cxx_b },
dart = { M.cxx_l, M.cxx_b },
dhall = { M.dash, M.haskell_b },
dosbatch = { 'REM%s' },
dot = { M.cxx_l, M.cxx_b },
dts = { M.cxx_l, M.cxx_b },
editorconfig = { M.hash },
eelixir = { M.html, M.html },
elixir = { M.hash },
elm = { M.dash, M.haskell_b },
elvish = { M.hash },
faust = { M.cxx_l, M.cxx_b },
fennel = { M.semicolon },
fish = { M.hash },
func = { M.lisp_l },
fsharp = { M.cxx_l, M.fsharp_b },
gdb = { M.hash },
gdscript = { M.hash },
gitignore = { M.hash },
gleam = { M.cxx_l },
glsl = { M.cxx_l, M.cxx_b },
gnuplot = { M.hash, M.hash_bracket },
go = { M.cxx_l, M.cxx_b },
gomod = { M.cxx_l },
graphql = { M.hash },
groovy = { M.cxx_l, M.cxx_b },
handlebars = { M.handlebars, M.handlebars },
haskell = { M.dash, M.haskell_b },
haxe = { M.cxx_l, M.cxx_b },
heex = { M.html, M.html },
html = { M.html, M.html },
htmldjango = { M.html, M.html },
idris = { M.dash, M.haskell_b },
idris2 = { M.dash, M.haskell_b },
ini = { M.hash },
java = { M.cxx_l, M.cxx_b },
javascript = { M.cxx_l, M.cxx_b },
javascriptreact = { M.cxx_l, M.cxx_b },
jsonc = { M.cxx_l },
jsonnet = { M.cxx_l, M.cxx_b },
julia = { M.hash, '#=%s=#' },
kotlin = { M.cxx_l, M.cxx_b },
lean = { M.dash, M.lean_b },
lean3 = { M.dash, M.lean_b },
lidris = { M.dash, M.haskell_b },
lilypond = { M.latex, '%{%s%}' },
lisp = { M.lisp_l, M.lisp_b },
lua = { M.dash, M.dash_bracket },
luau = { M.dash, M.dash_bracket },
markdown = { M.html, M.html },
make = { M.hash },
mbsyncrc = { M.dbl_hash },
mermaid = { '%%%s' },
meson = { M.hash },
nextflow = { M.cxx_l, M.cxx_b },
nim = { M.hash, '#[%s]#' },
nix = { M.hash, M.cxx_b },
nu = { M.hash },
ocaml = { M.fsharp_b, M.fsharp_b },
odin = { M.cxx_l, M.cxx_b },
plantuml = { "'%s", "/'%s'/" },
purescript = { M.dash, M.haskell_b },
python = { M.hash }, -- Python doesn't have block comments
php = { M.cxx_l, M.cxx_b },
prisma = { M.cxx_l },
proto = { M.cxx_l, M.cxx_b },
quarto = { M.html, M.html },
r = { M.hash }, -- R doesn't have block comments
racket = { M.lisp_l, M.lisp_b },
rasi = { M.cxx_l, M.cxx_b },
readline = { M.hash },
rego = { M.hash },
remind = { M.hash },
rescript = { M.cxx_l, M.cxx_b },
robot = { M.hash }, -- Robotframework doesn't have block comments
ron = { M.cxx_l, M.cxx_b },
ruby = { M.hash },
rust = { M.cxx_l, M.cxx_b },
sbt = { M.cxx_l, M.cxx_b },
scala = { M.cxx_l, M.cxx_b },
scheme = { M.lisp_l, M.lisp_b },
sh = { M.hash },
solidity = { M.cxx_l, M.cxx_b },
supercollider = { M.cxx_l, M.cxx_b },
sql = { M.dash, M.cxx_b },
stata = { M.cxx_l, M.cxx_b },
svelte = { M.html, M.html },
swift = { M.cxx_l, M.cxx_b },
sxhkdrc = { M.hash },
tablegen = { M.cxx_l, M.cxx_b },
teal = { M.dash, M.dash_bracket },
terraform = { M.hash, M.cxx_b },
tex = { M.latex },
template = { M.dbl_hash },
tmux = { M.hash },
toml = { M.hash },
twig = { M.twig, M.twig },
typescript = { M.cxx_l, M.cxx_b },
typescriptreact = { M.cxx_l, M.cxx_b },
typst = { M.cxx_l, M.cxx_b },
v = { M.cxx_l, M.cxx_b },
verilog = { M.cxx_l },
vhdl = { M.dash },
vim = { M.vim },
vifm = { M.vim },
vue = { M.html, M.html },
xdefaults = { '!%s' },
xml = { M.html, M.html },
xonsh = { M.hash }, -- Xonsh doesn't have block comments
yaml = { M.hash },
yuck = { M.lisp_l },
zig = { M.cxx_l }, -- Zig doesn't have block comments
}, {
-- Support for compound filetype i.e. 'ios.swift', 'ansible.yaml' etc.
__index = function(this, k)
local base, fallback = string.match(k, '^(.-)%.(.*)')
if not (base or fallback) then
return nil
end
return this[base] or this[fallback]
end,
})
local ft = {}
---Sets a commentstring(s) for a filetype/language
---@param lang string Filetype/Language of the buffer
---@param val string|string[]
---@return table self Returns itself
---@usage [[
---local ft = require('Comment.ft')
---
-----1. Using method signature
----- Set only line comment or both
----- You can also chain the set calls
---ft.set('yaml', '#%s').set('javascript', {'//%s', '/*%s*/'})
---
----- 2. Metatable magic
---ft.javascript = {'//%s', '/*%s*/'}
---ft.yaml = '#%s'
---
----- 3. Multiple filetypes
---ft({'go', 'rust'}, {'//%s', '/*%s*/'})
---ft({'toml', 'graphql'}, '#%s')
---@usage ]]
function ft.set(lang, val)
L[lang] = type(val) == 'string' and { val } or val --[[ @as string[] ]]
return ft
end
---Get line/block/both commentstring(s) for a given filetype
---@param lang string Filetype/Language of the buffer
---@param ctype? integer See |comment.utils.ctype|. If given `nil`, it'll
---return a copy of { line, block } commentstring.
---@return nil|string|string[] #Returns stored commentstring
---@usage [[
---local ft = require('Comment.ft')
---local U = require('Comment.utils')
---
----- 1. Primary filetype
---ft.get('rust', U.ctype.linewise) -- `//%s`
---ft.get('rust') -- `{ '//%s', '/*%s*/' }`
---
----- 2. Compound filetype
----- NOTE: This will return `yaml` commenstring(s),
----- as `ansible` commentstring is not found.
---ft.get('ansible.yaml', U.ctype.linewise) -- `#%s`
---ft.get('ansible.yaml') -- { '#%s' }
---@usage ]]
function ft.get(lang, ctype)
local tuple = L[lang]
if not tuple then
return nil
end
if not ctype then
return vim.deepcopy(tuple)
end
return tuple[ctype]
end
---Get a language tree for a given range by walking the parse tree recursively.
---This uses 'lua-treesitter' API under the hood. This can be used to calculate
---language of a particular region which embedded multiple filetypes like html,
---vue, markdown etc.
---
---NOTE: This ignores `tree-sitter-comment` parser, if installed.
---@param tree userdata Parse tree to be walked
---@param range integer[] Range to check
---{start_row, start_col, end_row, end_col}
---@return userdata #Returns a |treesitter-languagetree|
---@see treesitter-languagetree
---@see lua-treesitter-core
---@usage [[
---local ok, parser = pcall(vim.treesitter.get_parser, 0)
---assert(ok, "No parser found!")
---local tree = require('Comment.ft').contains(parser, {0, 0, -1, 0})
---print('Lang:', tree:lang())
---@usage ]]
function ft.contains(tree, range)
for lang, child in pairs(tree:children()) do
if lang ~= 'comment' and child:contains(range) then
return ft.contains(child, range)
end
end
return tree
end
---Calculate commentstring with the power of treesitter
---@param ctx CommentCtx
---@return nil|string #Commentstring
---@see comment.utils.CommentCtx
function ft.calculate(ctx)
local ok, parser = pcall(vim.treesitter.get_parser, A.nvim_get_current_buf())
if not ok then
return ft.get(vim.bo.filetype, ctx.ctype) --[[ @as string ]]
end
local lang = ft.contains(parser, {
ctx.range.srow - 1,
ctx.range.scol,
ctx.range.erow - 1,
ctx.range.ecol,
}):lang()
return ft.get(lang, ctx.ctype) or ft.get(vim.bo.filetype, ctx.ctype) --[[ @as string ]]
end
---@export ft
return setmetatable(ft, {
__newindex = function(this, k, v)
this.set(k, v)
end,
__call = function(this, langs, spec)
for _, lang in ipairs(langs) do
this.set(lang, spec)
end
return this
end,
})

View File

@ -0,0 +1,138 @@
---@brief [[
---*comment-nvim.txt* For Neovim version 0.7 Last change: 2021 July 11
---
--- _____ _ _
--- / ____/ / / (_)
--- / / ___ _ __ ___ _ __ ___ ___ _ __ / /_ _ ____ ___ _ __ ___
--- / / / _ \/ '_ ` _ \/ '_ ` _ \ / _ \ '_ \/ __/ / '_ \ \ / / / '_ ` _ \
--- / /___/ (_) / / / / / / / / / / / __/ / / / /_ _/ / / \ V // / / / / / /
--- \_____\___//_/ /_/ /_/_/ /_/ /_/\___/_/ /_/\__(_)_/ /_/\_/ /_/_/ /_/ /_/
---
--- · Smart and Powerful comment plugin ·
---
---@brief ]]
---@toc comment.contents
---@mod comment-nvim Introduction
---@brief [[
---Comment.nvim is a smart and powerful comment plugin for neovim. It supports
---dot-repeat, counts, line ('//') and block ('/* */') comments, and can be used
---with motion and text-objects. It has native integration with |treesitter| to
---support embedded filetypes like html, vue, markdown with codeblocks etc.
---@brief ]]
---@tag comment.dotrepeat
---@brief [[
---Comment.nvim uses |operatorfunc| combined with |g@| to support dot-repeat, and
---various marks i.e., |'[| |']| |'<| |'>| to deduce the region with the {motion}
---argument provided by 'operatorfunc'. See |comment.api.call|
---@brief ]]
---@tag comment.commentstring
---@brief [[
---Comment.nvim picks commentstring, either linewise/blockwise, from one of the
---following places
---
--- 1. 'pre_hook'
--- If a string is returned from this function then it will be used for
--- (un)commenting. See |comment.config|
---
--- 2. |comment.ft|
--- Using the commentstring table inside the plugin (using treesitter).
--- Fallback to |commentstring|, if not found.
---
--- 3. |commentstring| - Neovim's native commentstring for the filetype
---
---Although Comment.nvim supports native 'commentstring' but unfortunately it has
---the least priority. The caveat with this approach is that if someone sets the
---`commentstring`, without returning it, from the 'pre_hook' and the current
---filetype also exists in the |comment.ft| then the commenting will be done using
---the string in |comment.ft| instead of using 'commentstring'. To override this
---behavior, you have to manually return the 'commentstring' from 'pre_hook'.
---@brief ]]
---@tag comment.sourcecode
---@brief [[
---Comment.nvim is FOSS and distributed under MIT license. All the source code is
---available at https://github.com/numToStr/Comment.nvim
---@brief ]]
---@mod comment.usage Usage
---@brief [[
---Before using the plugin, you need to call the `setup()` function to create the
---default mappings. If you want, you can also override the default configuration
---by giving it a partial 'comment.config.Config' object, it will then be merged
---with the default configuration.
---@brief ]]
local C = {}
---Configures the plugin
---@param config? CommentConfig User configuration
---@return CommentConfig #Returns the modified config
---@see comment.config
---@usage [[
----- Use default configuration
---require('Comment').setup()
---
----- or with custom configuration
---require('Comment').setup({
--- ignore = '^$',
--- toggler = {
--- line = '<leader>cc',
--- block = '<leader>bc',
--- },
--- opleader = {
--- line = '<leader>c',
--- block = '<leader>b',
--- },
---})
---@usage ]]
function C.setup(config)
local cfg = require('Comment.config'):set(config):get()
if cfg.mappings then
local api = require('Comment.api')
local vvar = vim.api.nvim_get_vvar
local K = vim.keymap.set
-- Basic Mappings
if cfg.mappings.basic then
-- NORMAL mode mappings
K('n', cfg.opleader.line, '<Plug>(comment_toggle_linewise)', { desc = 'Comment toggle linewise' })
K('n', cfg.opleader.block, '<Plug>(comment_toggle_blockwise)', { desc = 'Comment toggle blockwise' })
K('n', cfg.toggler.line, function()
return vvar('count') == 0 and '<Plug>(comment_toggle_linewise_current)'
or '<Plug>(comment_toggle_linewise_count)'
end, { expr = true, desc = 'Comment toggle current line' })
K('n', cfg.toggler.block, function()
return vvar('count') == 0 and '<Plug>(comment_toggle_blockwise_current)'
or '<Plug>(comment_toggle_blockwise_count)'
end, { expr = true, desc = 'Comment toggle current block' })
-- VISUAL mode mappings
K(
'x',
cfg.opleader.line,
'<Plug>(comment_toggle_linewise_visual)',
{ desc = 'Comment toggle linewise (visual)' }
)
K(
'x',
cfg.opleader.block,
'<Plug>(comment_toggle_blockwise_visual)',
{ desc = 'Comment toggle blockwise (visual)' }
)
end
-- Extra Mappings
if cfg.mappings.extra then
K('n', cfg.extra.below, api.insert.linewise.below, { desc = 'Comment insert below' })
K('n', cfg.extra.above, api.insert.linewise.above, { desc = 'Comment insert above' })
K('n', cfg.extra.eol, api.locked('insert.linewise.eol'), { desc = 'Comment insert end of line' })
end
end
return cfg
end
return C

View File

@ -0,0 +1,228 @@
---@mod comment.opfunc Operator-mode API
---@brief [[
---Underlying functions that powers the |comment.api.toggle|, |comment.api.comment|,
---and |comment.api.uncomment| lua API.
---@brief ]]
local U = require('Comment.utils')
local Config = require('Comment.config')
local A = vim.api
local Op = {}
---Vim operator-mode motion enum. Read |:map-operator|
---@alias OpMotion
---| '"line"' # Vertical motion
---| '"char"' # Horizontal motion
---| '"v"' # Visual Block motion
---| '"V"' # Visual Line motion
---Common operatorfunc callback
---This function contains the core logic for comment/uncomment
---@param motion? OpMotion
---If given 'nil', it'll only (un)comment
---the current line
---@param cfg CommentConfig
---@param cmode integer See |comment.utils.cmode|
---@param ctype integer See |comment.utils.ctype|
function Op.opfunc(motion, cfg, cmode, ctype)
local range = U.get_region(motion)
local cmotion = motion == nil and U.cmotion.line or U.cmotion[motion]
-- If we are doing char or visual motion on the same line
-- then we would probably want block comment instead of line comment
local is_partial = cmotion == U.cmotion.char or cmotion == U.cmotion.v
local is_blockx = is_partial and range.srow == range.erow
local lines = U.get_lines(range)
-- sometimes there might be a case when there are no lines
-- like, executing a text object returns nothing
if U.is_empty(lines) then
return
end
---@type CommentCtx
local ctx = {
cmode = cmode,
cmotion = cmotion,
ctype = is_blockx and U.ctype.blockwise or ctype,
range = range,
}
local lcs, rcs = U.parse_cstr(cfg, ctx)
---@type OpFnParams
local params = {
cfg = cfg,
lines = lines,
lcs = lcs,
rcs = rcs,
cmode = cmode,
range = range,
}
if motion ~= nil and (is_blockx or ctype == U.ctype.blockwise) then
ctx.cmode = Op.blockwise(params, is_partial)
else
ctx.cmode = Op.linewise(params)
end
-- We only need to restore cursor if both sticky and position are available
-- As this function is also called for visual mapping where we are not storing the position
--
-- And I found out that if someone presses `gc` but doesn't provide operators and
-- does visual comments then cursor jumps to previous stored position. Thus the check for visual modes
if cfg.sticky and Config.position and cmotion ~= U.cmotion.v and cmotion ~= U.cmotion.V then
A.nvim_win_set_cursor(0, Config.position)
Config.position = nil
end
U.is_fn(cfg.post_hook, ctx)
end
---Line commenting with count
---@param count integer Value of |v:count|
---@param cfg CommentConfig
---@param cmode integer See |comment.utils.cmode|
---@param ctype integer See |comment.utils.ctype|
function Op.count(count, cfg, cmode, ctype)
local lines, range = U.get_count_lines(count)
---@type CommentCtx
local ctx = {
cmode = cmode,
cmotion = U.cmotion.line,
ctype = ctype,
range = range,
}
local lcs, rcs = U.parse_cstr(cfg, ctx)
---@type OpFnParams
local params = {
cfg = cfg,
cmode = ctx.cmode,
lines = lines,
lcs = lcs,
rcs = rcs,
range = range,
}
if ctype == U.ctype.blockwise then
ctx.cmode = Op.blockwise(params)
else
ctx.cmode = Op.linewise(params)
end
U.is_fn(cfg.post_hook, ctx)
end
---Operator-mode function parameters
---@class OpFnParams
---@field cfg CommentConfig
---@field cmode integer See |comment.utils.cmode|
---@field lines string[] List of lines
---@field rcs string RHS of commentstring
---@field lcs string LHS of commentstring
---@field range CommentRange
---Line commenting
---@param param OpFnParams
---@return integer _ Returns a calculated comment mode
function Op.linewise(param)
local pattern = U.is_fn(param.cfg.ignore)
local padding = U.is_fn(param.cfg.padding)
local check_comment = U.is_commented(param.lcs, param.rcs, padding)
-- While commenting a region, there could be lines being both commented and non-commented
-- So, if any line is uncommented then we should comment the whole block or vise-versa
local cmode = U.cmode.uncomment
---When commenting multiple line, it is to be expected that indentation should be preserved
---So, When looping over multiple lines we need to store the indentation of the mininum length (except empty line)
---Which will be used to semantically comment rest of the lines
local min_indent, tabbed = -1, false
-- If the given cmode is uncomment then we actually don't want to compute the cmode or min_indent
if param.cmode ~= U.cmode.uncomment then
for _, line in ipairs(param.lines) do
-- I wish lua had `continue` statement [sad noises]
if not U.ignore(line, pattern) then
if cmode == U.cmode.uncomment and param.cmode == U.cmode.toggle and (not check_comment(line)) then
cmode = U.cmode.comment
end
if not U.is_empty(line) and param.cmode ~= U.cmode.uncomment then
local _, len = string.find(line, '^%s*')
if min_indent == -1 or min_indent > len then
min_indent, tabbed = len, string.find(line, '^\t') ~= nil
end
end
end
end
end
-- If the comment mode given is not toggle than force that mode
if param.cmode ~= U.cmode.toggle then
cmode = param.cmode
end
if cmode == U.cmode.uncomment then
local uncomment = U.uncommenter(param.lcs, param.rcs, padding)
for i, line in ipairs(param.lines) do
if not U.ignore(line, pattern) then
param.lines[i] = uncomment(line) --[[@as string]]
end
end
else
local comment = U.commenter(param.lcs, param.rcs, padding, min_indent, nil, tabbed)
for i, line in ipairs(param.lines) do
if not U.ignore(line, pattern) then
param.lines[i] = comment(line) --[[@as string]]
end
end
end
A.nvim_buf_set_lines(0, param.range.srow - 1, param.range.erow, false, param.lines)
return cmode
end
---Full/Partial/Current-Line Block commenting
---@param param OpFnParams
---@param partial? boolean Comment the partial region (visual mode)
---@return integer _ Returns a calculated comment mode
function Op.blockwise(param, partial)
local is_x = #param.lines == 1 -- current-line blockwise
local lines = is_x and param.lines[1] or param.lines
local padding = U.is_fn(param.cfg.padding)
local scol, ecol = nil, nil
if is_x or partial then
scol, ecol = param.range.scol, param.range.ecol
end
-- If given mode is toggle then determine whether to comment or not
local cmode = param.cmode
if cmode == U.cmode.toggle then
local is_cmt = U.is_commented(param.lcs, param.rcs, padding, scol, ecol)(lines)
cmode = is_cmt and U.cmode.uncomment or U.cmode.comment
end
if cmode == U.cmode.uncomment then
lines = U.uncommenter(param.lcs, param.rcs, padding, scol, ecol)(lines)
else
lines = U.commenter(param.lcs, param.rcs, padding, scol, ecol)(lines)
end
if is_x then
A.nvim_set_current_line(lines)
else
A.nvim_buf_set_lines(0, param.range.srow - 1, param.range.erow, false, lines)
end
return cmode
end
return Op

View File

@ -0,0 +1,376 @@
---@mod comment.utils Utilities
local F = require('Comment.ft')
local A = vim.api
local U = {}
---Comment context
---@class CommentCtx
---@field ctype integer See |comment.utils.ctype|
---@field cmode integer See |comment.utils.cmode|
---@field cmotion integer See |comment.utils.cmotion|
---@field range CommentRange
---Range of the selection that needs to be commented
---@class CommentRange
---@field srow integer Starting row
---@field scol integer Starting column
---@field erow integer Ending row
---@field ecol integer Ending column
---Comment modes - Can be manual or computed via operator-mode
---@class CommentMode
---@field toggle integer Toggle action
---@field comment integer Comment action
---@field uncomment integer Uncomment action
---An object containing comment modes
---@type CommentMode
U.cmode = {
toggle = 0,
comment = 1,
uncomment = 2,
}
---Comment types
---@class CommentType
---@field linewise integer Use linewise commentstring
---@field blockwise integer Use blockwise commentstring
---An object containing comment types
---@type CommentType
U.ctype = {
linewise = 1,
blockwise = 2,
}
---Comment motion types
---@class CommentMotion
---@field line integer Line motion (ie. 'gc2j')
---@field char integer Character/left-right motion (ie. 'gc2w')
---@field block integer Visual operator-pending motion
---@field v integer Visual motion (ie. 'v3jgc')
---@field V integer Visual-line motion (ie. 'V10kgc')
---An object containing comment motions
---@type CommentMotion
U.cmotion = {
line = 1,
char = 2,
block = 3,
v = 4,
V = 5,
}
---@private
---Check whether the line is empty
---@param iter string|string[]
---@return boolean
function U.is_empty(iter)
return #iter == 0
end
---@private
---Helper to get padding character
---@param flag boolean
---@return string string
function U.get_pad(flag)
return flag and ' ' or ''
end
---@private
---Helper to get padding pattern
---@param flag boolean
---@return string string
function U.get_padpat(flag)
return flag and '%s?' or ''
end
---@private
---Call a function if exists
---@param fn unknown|fun(...):unknown Wanna be function
---@return unknown
function U.is_fn(fn, ...)
if type(fn) == 'function' then
return fn(...)
end
return fn
end
---@private
---Check if the given line is ignored or not with the given pattern
---@param ln string Line to be ignored
---@param pat string Lua regex
---@return boolean
function U.ignore(ln, pat)
return pat and string.find(ln, pat) ~= nil
end
---Get region for line movement or visual selection
---NOTE: Returns the current line region, if `opmode` is not given.
---@param opmode? OpMotion
---@return CommentRange
function U.get_region(opmode)
if not opmode then
local row = unpack(A.nvim_win_get_cursor(0))
return { srow = row, scol = 0, erow = row, ecol = 0 }
end
local marks = string.match(opmode, '[vV]') and { '<', '>' } or { '[', ']' }
local sln, eln = A.nvim_buf_get_mark(0, marks[1]), A.nvim_buf_get_mark(0, marks[2])
return { srow = sln[1], scol = sln[2], erow = eln[1], ecol = eln[2] }
end
---Get lines from the current position to the given count
---@param count integer Probably 'vim.v.count'
---@return string[] #List of lines
---@return CommentRange
function U.get_count_lines(count)
local srow = unpack(A.nvim_win_get_cursor(0))
local erow = (srow + count) - 1
local lines = A.nvim_buf_get_lines(0, srow - 1, erow, false)
return lines, { srow = srow, scol = 0, erow = erow, ecol = 0 }
end
---Get lines from a NORMAL/VISUAL mode
---@param range CommentRange
---@return string[] #List of lines
function U.get_lines(range)
-- If start and end is same, then just return the current line
if range.srow == range.erow then
return { A.nvim_get_current_line() }
end
return A.nvim_buf_get_lines(0, range.srow - 1, range.erow, false)
end
---Validates and unwraps the given commentstring
---@param cstr string See 'commentstring'
---@return string string Left side of the commentstring
---@return string string Right side of the commentstring
function U.unwrap_cstr(cstr)
local left, right = string.match(cstr, '(.*)%%s(.*)')
assert(
(left or right),
{ msg = string.format('Invalid commentstring for %s! Read `:h commentstring` for help.', vim.bo.filetype) }
)
return vim.trim(left), vim.trim(right)
end
---Parses commentstring from the following places in the respective order
--- 1. pre_hook - commentstring returned from the function
--- 2. ft.lua - commentstring table bundled with the plugin
--- 3. commentstring - Neovim's native. See 'commentstring'
---@param cfg CommentConfig
---@param ctx CommentCtx
---@return string string Left side of the commentstring
---@return string string Right side of the commentstring
function U.parse_cstr(cfg, ctx)
-- 1. We ask `pre_hook` for a commentstring
local inbuilt = U.is_fn(cfg.pre_hook, ctx)
-- 2. Calculate w/ the help of treesitter
or F.calculate(ctx)
assert(inbuilt or (ctx.ctype ~= U.ctype.blockwise), {
msg = vim.bo.filetype .. " doesn't support block comments!",
})
-- 3. Last resort to use native commentstring
return U.unwrap_cstr(inbuilt or vim.bo.commentstring)
end
---Returns a closure which is used to do comments
---
---If given {string[]} to the closure then it will do blockwise comment
---else linewise comment will be done with the given {string}
---@param left string Left side of the commentstring
---@param right string Right side of the commentstring
---@param padding boolean Is padding enabled?
---@param scol? integer Starting column
---@param ecol? integer Ending column
---@param tabbed? boolean Using tab indentation
---@return fun(line: string|string[]):string|string[]
function U.commenter(left, right, padding, scol, ecol, tabbed)
local pad = U.get_pad(padding)
local ll = U.is_empty(left) and left or (left .. pad)
local rr = U.is_empty(right) and right or (pad .. right)
local empty = string.rep(tabbed and '\t' or ' ', scol or 0) .. left .. right
local is_lw = scol and not ecol
return function(line)
------------------
-- for linewise --
------------------
if is_lw then
if U.is_empty(line) then
return empty
end
-- line == 0 -> start from 0 col
if scol == 0 then
return (ll .. line .. rr)
end
local first = string.sub(line --[[@as string]], 0, scol)
local last = string.sub(line --[[@as string]], scol + 1, -1)
return first .. ll .. last .. rr
end
-------------------
-- for blockwise --
-------------------
if type(line) == 'table' then
local first, last = line[1], line[#line]
-- If both columns are given then we can assume it's a partial block
if scol and ecol then
local sfirst = string.sub(first, 0, scol)
local slast = string.sub(first, scol + 1, -1)
local efirst = string.sub(last, 0, ecol + 1)
local elast = string.sub(last, ecol + 2, -1)
line[1] = sfirst .. ll .. slast
line[#line] = efirst .. rr .. elast
else
line[1] = U.is_empty(first) and left or string.gsub(first, '^(%s*)', '%1' .. vim.pesc(ll))
line[#line] = U.is_empty(last) and right or (last .. rr)
end
return line
end
--------------------------------
-- for current-line blockwise --
--------------------------------
-- SOURCE: https://github.com/numToStr/Comment.nvim/issues/224
if ecol > #line then
return ll .. line .. rr
end
local first = string.sub(line, 0, scol)
local mid = string.sub(line, scol + 1, ecol + 1)
local last = string.sub(line, ecol + 2, -1)
return first .. ll .. mid .. rr .. last
end
end
---Returns a closure which is used to uncomment a line
---
---If given {string[]} to the closure then it will block uncomment
---else linewise uncomment will be done with the given {string}
---@param left string Left side of the commentstring
---@param right string Right side of the commentstring
---@param padding boolean Is padding enabled?
---@param scol? integer Starting column
---@param ecol? integer Ending column
---@return fun(line: string|string[]):string|string[]
function U.uncommenter(left, right, padding, scol, ecol)
local pp, plen = U.get_padpat(padding), padding and 1 or 0
local left_len, right_len = #left + plen, #right + plen
local ll = U.is_empty(left) and left or vim.pesc(left) .. pp
local rr = U.is_empty(right) and right or pp .. vim.pesc(right)
local is_lw = not (scol and scol)
local pattern = is_lw and '^(%s*)' .. ll .. '(.-)' .. rr .. '$' or ''
return function(line)
-------------------
-- for blockwise --
-------------------
if type(line) == 'table' then
local first, last = line[1], line[#line]
-- If both columns are given then we can assume it's a partial block
if scol and ecol then
local sfirst = string.sub(first, 0, scol)
local slast = string.sub(first, scol + left_len + 1, -1)
local efirst = string.sub(last, 0, ecol - right_len + 1)
local elast = string.sub(last, ecol + 2, -1)
line[1] = sfirst .. slast
line[#line] = efirst .. elast
else
line[1] = string.gsub(first, '^(%s*)' .. ll, '%1')
line[#line] = string.gsub(last, rr .. '$', '')
end
return line
end
------------------
-- for linewise --
------------------
if is_lw then
local a, b, c = string.match(line, pattern)
-- When user tries to uncomment when there is nothing to uncomment. See #221
assert(a and b, { msg = 'Nothing to uncomment!' })
-- If there is nothing after LHS then just return ''
-- bcz the line previously (before comment) was empty
return U.is_empty(b) and b or a .. b .. (c or '')
end
--------------------------------
-- for current-line blockwise --
--------------------------------
-- SOURCE: https://github.com/numToStr/Comment.nvim/issues/224
if ecol > #line then
return string.sub(line, scol + left_len + 1, #line - right_len)
end
local first = string.sub(line, 0, scol)
local mid = string.sub(line, scol + left_len + 1, ecol - right_len + 1)
local last = string.sub(line, ecol + 2, -1)
return first .. mid .. last
end
end
---Check if the given string is commented or not
---
---If given {string[]} to the closure, it will check the first and last line
---with LHS and RHS of commentstring respectively else it will check the given
---line with LHS and RHS (if given) of the commenstring
---@param left string Left side of the commentstring
---@param right string Right side of the commentstring
---@param padding boolean Is padding enabled?
---@param scol? integer Starting column
---@param ecol? integer Ending column
---@return fun(line: string|string[]):boolean
function U.is_commented(left, right, padding, scol, ecol)
local pp = U.get_padpat(padding)
local ll = U.is_empty(left) and left or '^%s*' .. vim.pesc(left) .. pp
local rr = U.is_empty(right) and right or pp .. vim.pesc(right) .. '$'
local pattern = ll .. '.-' .. rr
local is_full = scol == nil or ecol == nil
return function(line)
-------------------
-- for blockwise --
-------------------
if type(line) == 'table' then
local first, last = line[1], line[#line]
if is_full then
return (string.find(first, ll) and string.find(last, rr)) ~= nil
end
return (string.find(string.sub(first, scol + 1, -1), ll) and string.find(string.sub(last, 0, ecol + 1), rr))
~= nil
end
------------------
-- for linewise --
------------------
if is_full then
return string.find(line, pattern) ~= nil
end
--------------------------------
-- for current-line blockwise --
--------------------------------
-- SOURCE: https://github.com/numToStr/Comment.nvim/issues/224
return string.find(string.sub(line, scol + 1, (ecol > #line and #line or ecol + 1)), pattern) ~= nil
end
end
---@private
---Error handler
---@param ... unknown
function U.catch(fn, ...)
xpcall(fn, function(err)
vim.notify(string.format('[Comment.nvim] %s', err.msg), vim.log.levels.WARN)
end, ...)
end
return U

View File

@ -0,0 +1,142 @@
local K = vim.keymap.set
local call = require('Comment.api').call
---@mod comment.keybindings Keybindings
---@brief [[
---Comment.nvim provides default keybindings for (un)comment your code. These
---keybinds are enabled upon calling |comment.usage.setup| and can be configured
---or disabled, if desired.
---
---Basic: ~
---
--- *gc*
--- *gb*
--- *gc[count]{motion}*
--- *gb[count]{motion}*
---
--- Toggle comment on a region using linewise/blockwise comment. In 'NORMAL'
--- mode, it uses 'Operator-Pending' mode to listen for an operator/motion.
--- In 'VISUAL' mode it simply comment the selected region.
---
--- *gcc*
--- *gbc*
--- *[count]gcc*
--- *[count]gbc*
---
--- Toggle comment on the current line using linewise/blockwise comment. If
--- prefixed with a 'v:count' then it will comment over the number of lines
--- corresponding to the {count}. These are only available in 'NORMAL' mode.
---
---
---Extra: ~
---
--- *gco* - Inserts comment below and enters INSERT mode
--- *gcO* - Inserts comment above and enters INSERT mode
--- *gcA* - Inserts comment at the end of line and enters INSERT mode
---@brief ]]
---@mod comment.plugmap Plug Mappings
---@brief [[
---Comment.nvim provides <Plug> mappings for most commonly used actions. These
---are enabled by default and can be used to make custom keybindings. All plug
---mappings have support for dot-repeat except VISUAL mode keybindings. To create
---custom comment function, check out 'comment.api' section.
---
--- *<Plug>(comment_toggle_linewise)*
--- *<Plug>(comment_toggle_blockwise)*
---
--- Toggle comment on a region with linewise/blockwise comment in NORMAL mode.
--- using |Operator-Pending| mode (or |g@|) to get the region to comment.
--- These powers the |gc| and |gb| keybindings.
---
--- *<Plug>(comment_toggle_linewise_current)*
--- *<Plug>(comment_toggle_blockwise_current)*
---
--- Toggle comment on the current line with linewise/blockwise comment in
--- NORMAL mode. These powers the |gcc| and 'gbc' keybindings.
---
--- *<Plug>(comment_toggle_linewise_count)*
--- *<Plug>(comment_toggle_blockwise_count)*
---
--- Toggle comment on a region using 'v:count' with linewise/blockwise comment
--- in NORMAL mode. These powers the |[count]gcc| and |[count]gbc| keybindings.
---
--- *<Plug>(comment_toggle_linewise_visual)*
--- *<Plug>(comment_toggle_blockwise_visual)*
---
--- Toggle comment on the selected region with linewise/blockwise comment in
--- NORMAL mode. These powers the |{visual}gc| and |{visual}gb| keybindings.
---
---Usage: ~
--->lua
--- -- Toggle current line or with count
--- vim.keymap.set('n', 'gcc', function()
--- return vim.v.count == 0
--- and '<Plug>(comment_toggle_linewise_current)'
--- or '<Plug>(comment_toggle_linewise_count)'
--- end, { expr = true })
---
--- -- Toggle in Op-pending mode
--- vim.keymap.set('n', 'gc', '<Plug>(comment_toggle_linewise)')
---
--- -- Toggle in VISUAL mode
--- vim.keymap.set('x', 'gc', '<Plug>(comment_toggle_linewise_visual)')
---<
---@brief ]]
---@export plugs
-- Operator-Pending mappings
K(
'n',
'<Plug>(comment_toggle_linewise)',
call('toggle.linewise', 'g@'),
{ expr = true, desc = 'Comment toggle linewise' }
)
K(
'n',
'<Plug>(comment_toggle_blockwise)',
call('toggle.blockwise', 'g@'),
{ expr = true, desc = 'Comment toggle blockwise' }
)
-- Toggle mappings
K(
'n',
'<Plug>(comment_toggle_linewise_current)',
call('toggle.linewise.current', 'g@$'),
{ expr = true, desc = 'Comment toggle current line' }
)
K(
'n',
'<Plug>(comment_toggle_blockwise_current)',
call('toggle.blockwise.current', 'g@$'),
{ expr = true, desc = 'Comment toggle current block' }
)
-- Count mappings
K(
'n',
'<Plug>(comment_toggle_linewise_count)',
call('toggle.linewise.count_repeat', 'g@$'),
{ expr = true, desc = 'Comment toggle linewise with count' }
)
K(
'n',
'<Plug>(comment_toggle_blockwise_count)',
call('toggle.blockwise.count_repeat', 'g@$'),
{ expr = true, desc = 'Comment toggle blockwise with count' }
)
-- Visual-Mode mappings
K(
'x',
'<Plug>(comment_toggle_linewise_visual)',
'<ESC><CMD>lua require("Comment.api").locked("toggle.linewise")(vim.fn.visualmode())<CR>',
{ desc = 'Comment toggle linewise (visual)' }
)
K(
'x',
'<Plug>(comment_toggle_blockwise_visual)',
'<ESC><CMD>lua require("Comment.api").locked("toggle.blockwise")(vim.fn.visualmode())<CR>',
{ desc = 'Comment toggle blockwise (visual)' }
)

View File

@ -0,0 +1,4 @@
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 4
quote_style = "AutoPreferSingle"