A Neovim plugin for setting the commentstring
option based on the cursor
location in the file. The location is checked via treesitter queries.
This is useful when there are embedded languages in certain types of files. For example, Vue files can have many different sections, each of which can have a different style for comments.
Note that this plugin only changes the commentstring
setting. It does not
add any mappings for commenting. It is recommended to use a commenting plugin
like vim-commentary
alongside this
plugin.
Requirements:
Installation:
Use your favorite plugin manager. For example, here's how it would look like with Packer:
use 'JoosepAlviste/nvim-ts-context-commentstring'
Setup:
Enable the module from nvim-treesitter
setup
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true
}
}
Don't forget to use lua
heredoc if
you're using init.vim
Recommended: Using a commenting plugin
It is recommended to use a commenting plugin like
vim-commentary
together with this
plugin. vim-commentary
provides the mappings for commenting which use the
commentstring
setting. This plugin adds to that by correctly setting the
commentstring
setting so that vim-commentary
can do its thing even in more
complex filetypes.
There are ways to make this plugin more efficient with some commenting plugins. See the Integrations section for more information.
Currently, the following languages are supported when they are injected with
language tree (see
lua/ts_context_commentstring/internal.lua
):
javascript
typescript
tsx
css
scss
php
html
svelte
vue
handlebars
glimmer
graphql
lua
This means that in any filetype, if the given languages are injected, this
plugin should detect them and correctly set the commentstring
. For example,
Vue files can be injected with css
or javascript
. Even though we don't
configure anything for Vue explicitly, the commentstring
updating logic should
still work.
Support for more languages can be added quite easily by passing a config
table
when configuring the plugin:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
config = {
css = '// %s'
}
}
}
Additionally, some languages are not injected with language tree, but have
multiple commenting styles in the same language. One such example is JavaScript
with JSX. The JSX section is not an injected language, but a part of the tree
generated by the javascript
treesitter parser.
In this more complex case, this plugin supports adding queries for specific
treesitter nodes. Each node can have its own unique commenting style. For
example, here's how the default configuration for javascript
would look like:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
config = {
javascript = {
__default = '// %s',
jsx_element = '{/* %s */}',
jsx_fragment = '{/* %s */}',
jsx_attribute = '// %s',
comment = '// %s'
}
}
}
}
The __default
value is used when none of the other node types are seen. The
rest of the keys refer to the type of the treesitter node. In this example, if
your cursor is inside a jsx_element
, then the {/* %s */}
commentstring
will be set.
Note that the language refers to the treesitter language, not the filetype or the file extension.
Additionally, it is possible to have each commentstring
configuration be a
table with custom keys. This can be used to configure separate single and
multi-line comment styles (useful when integrating with a commenting plugin):
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
config = {
typescript = { __default = '// %s', __multiline = '/* %s */' }
}
}
}
Then, the custom key can be passed to update_commentstring
:
require('ts_context_commentstring.internal').update_commentstring({
key = '__multiline',
})
Finally, it is possible to customize the tree traversal start location when
calling update_commentstring
, this is useful in commenting plugin
integrations. There are some useful helper functions exported from
ts_context_commentstring.utils
:
require('ts_context_commentstring.internal').calculate_commentstring {
location = require('ts_context_commentstring.utils').get_cursor_location(),
}
If you want to calculate your own commentstring
you are able to do so with
the custom_calculation
option:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
custom_calculation = function(node, language_tree)
...
end
}
}
This is a function that takes in the current node and the language tree which
could be used for context like figuring out which language you should use a
commentstring
for. You can also for example figure out which type the current
node is. You need to return a commentstring
in the custom_calculation
if you
want it to be set.
The default behavior is to trigger commentstring
updating on CursorHold
. If
your updatetime
setting is set to a high value, then the updating might not
be triggered. Let me know if you'd like to have this be customized by creating
an issue. Another candidate might be the CursorMoved
autocommand.
The default CursorHold
autocommand can be disabled by passing enable_autocmd = false
when setting up the plugin:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
enable_autocmd = false,
}
}
Then, you can call the update_commentstring
function manually:
nnoremap <leader>c <cmd>lua require('ts_context_commentstring.internal').update_commentstring()<cr>
Note: It is not necessary to use this option if you are using
vim-commentary
, the integration is set up automatically.
For some commenting plugins, it's possible to trigger the commentstring
calculation only when it is actually needed. Some commenting plugins require
more configuration than others.
Let me know if you'd like to see more integrations for other commenting plugins. A PR is always appreciated :)
There is an existing integration with vim-commentary
, which triggers the
commentstring
updating logic only when needed (before commenting with gc
).
If vim-commentary
is detected, then this plugin automatically sets up
vim-commentary
mappings to first update the commentstring
, and then trigger
vim-commentary
.
You can override default mappings, or disable them by specifying false
.
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
commentary_integration = {
-- change default mapping
Commentary = 'g/',
-- disable default mapping
CommentaryLine = false,
},
}
}
kommentary
can also trigger the commentstring
updating logic before
commenting. However, it requires some configuration to set up.
First, disable the CursorHold
autocommand of this plugin:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
enable_autocmd = false,
}
}
Then, configure kommentary
to trigger the commentstring
updating logic with
its hook_function
configuration:
require('kommentary.config').configure_language('typescriptreact', {
single_line_comment_string = 'auto',
multi_line_comment_strings = 'auto',
hook_function = function()
require('ts_context_commentstring.internal').update_commentstring()
end,
})
Note that currently the 'default'
language configuration
does not run
hook_function
, so explicit language configurations will need to be
supplied as shown above. See
issue #19.
nvim-comment
can easily be configured to trigger the commentstring
updating
logic before commenting.
First, disable the CursorHold
autocommand of this plugin:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
enable_autocmd = false,
}
}
Then, configure nvim_comment
to trigger the commentstring
updating logic with
its hook
configuration:
require("nvim_comment").setup({
hook = function()
require("ts_context_commentstring.internal").update_commentstring()
end,
})
First, disable the CursorHold
autocommand of this plugin:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
enable_autocmd = false,
}
}
Then, configure Comment.nvim
to trigger the commentstring
updating logic
with its pre_hook
configuration:
require('Comment').setup {
pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook(),
}
mini.comment
also relies on commentstring
option and implements hook functionality. It can be used to configure updating commentstring
before commenting.
First, disable the CursorHold
autocommand of this plugin:
require'nvim-treesitter.configs'.setup {
context_commentstring = {
enable = true,
enable_autocmd = false,
}
}
Then, configure mini.comment
to trigger the commentstring
updating logic by supplying custom config.hooks.pre
:
require('mini.comment').setup({
hooks = {
pre = function()
require('ts_context_commentstring.internal').update_commentstring()
end,
},
})
React:
Svelte:
HTML:
Nesting:
I injected HTML into JavaScript strings and created multiple levels of nesting with language tree. This sort of nesting of languages works without any extra configuration in the plugin.