mirror of
https://github.com/HiPhish/rainbow-delimiters.nvim.git
synced 2024-09-16 14:24:05 +02:00
765 lines
30 KiB
Text
765 lines
30 KiB
Text
*rainbow-delimiters.txt* Alternating highlight for delimiters in code
|
|
|
|
*rainbow-delimiters*
|
|
*rb-delimiters*
|
|
|
|
Author: Alejandro "HiPhish" Sanchez
|
|
License: Apache-2.0
|
|
Version: 0.6.1
|
|
|
|
|
|
==============================================================================
|
|
TABLE OF CONTENTS *rb-delimiters-contents*
|
|
|
|
1. Introduction .............................. |rb-delimiters-intro|
|
|
2. Setup and configuration ................... |rb-delimiters-setup|
|
|
2.1 Highlight colors ...................... |rb-delimiters-colors|
|
|
2.2 Strategies ............................ |rb-delimiters-strategy|
|
|
2.3 Queries ............................... |rb-delimiters-query|
|
|
2.4 Logging ............................... |rb-delimiters-logging|
|
|
3. Extending ................................. |rb-delimiters-extending|
|
|
3.1 The library ........................... |rb-delimiters-api|
|
|
3.2 Custom queries ........................ |rb-delimiters-custom-query|
|
|
3.3 Custom strategies ..................... |rb-delimiters-custom-strategy|
|
|
3.4 Adding new languages .................. |rb-delimiters-custom-lang|
|
|
3.5 Adding highlight tests ................ |rb-delimiters-highlight-test|
|
|
4. Recipes ................................... |rb-delimiters-recipes|
|
|
5. Acknowledgements .......................... |rb-delimiters-credit|
|
|
6. Further reading ........................... |rb-delimiters-reading|
|
|
|
|
|
|
==============================================================================
|
|
INTRODUCTION *rb-delimiters-intro*
|
|
|
|
This plugin provides alternating highlighting for delimiters in Neovim, also
|
|
known as "rainbow parentheses". Thanks to the built-in |treesitter| support
|
|
we are not limited to just parentheses. We can match any part of the document
|
|
tree, such as HTML tags or `do` / `end` pairs in Lua. We can define new
|
|
patterns for existing languages, add support for new languages and even change
|
|
the strategy used for highlighting.
|
|
|
|
|
|
==============================================================================
|
|
SETUP AND CONFIGURATION *rb-delimiters-setup*
|
|
|
|
Install Rainbow-Delimiters like any other Neovim plugin. You also need a
|
|
Tree-sitter parser for each language to want to support.
|
|
|
|
*g:rainbow_delimiters*
|
|
Configuration is done through the variable `g:rainbow_delimiters`. It is a
|
|
dictionary which can be defined both in Vim script and in Lua. The following
|
|
keys are recognized:
|
|
|
|
`strategy`
|
|
Dictionary mapping Tree-sitter language names to strategies. The empty
|
|
string is the key for the default strategy. See |rb-delimiters-strategy|
|
|
for more information about strategies.
|
|
|
|
`query`
|
|
Dictionary mapping Tree-sitter language names to queries. The empty string
|
|
is the key for the default query. See |rb-delimiters-query| for more
|
|
information about queries.
|
|
|
|
`priority`
|
|
Dictionary mapping Tree-sitter language names to highlight priority values.
|
|
The empty string is the key for the default priority. See
|
|
|vim.highlight.priorities| and |treesitter-highlight-priority| for more
|
|
information on priorities.
|
|
|
|
`highlight`
|
|
List of names of the highlight groups to use for highlighting, for more
|
|
information see |rb-delimiters-colors|.
|
|
|
|
`whitelist`
|
|
List of Tree-sitter languages for which to enabled rainbow delimiters.
|
|
Rainbow delimiters will be disabled for all other languages.
|
|
|
|
`blacklist`
|
|
List of Tree-sitter languages for which to disabled rainbow delimiters.
|
|
Rainbow delimiters will be enabled for all other languages.
|
|
|
|
`condition`
|
|
Function which receives the current buffer number and returns a boolean; can
|
|
be used to dynamically decide whether to enable (true) or disable (false)
|
|
rainbow delimiters for a given buffer. You could for example use this
|
|
predicate to disable rainbow delimiters for large files.
|
|
|
|
Superceeded by the blacklist, but superceedes the whitelist. This means if
|
|
the file type is blacklisted the condtion is ignored, but if the file type
|
|
is whitelisted or not blacklisted the condition will be evaluated and
|
|
honoured.
|
|
|
|
`log`
|
|
Settings for logging information. This is a dictionary which contains
|
|
further settings. See |rb-delimiters-logging| for details.
|
|
|
|
If neither the white- nor the blacklist are set rainbow delimiters will be
|
|
enabled for all languages. If both lists are set it is undefined which will
|
|
take precedence.
|
|
|
|
|
|
Here is an example configuration:
|
|
>vim
|
|
let g:rainbow_delimiters = {
|
|
\ 'strategy': {
|
|
\ '': rainbow_delimiters#strategy.global,
|
|
\ 'vim': rainbow_delimiters#strategy.local,
|
|
\ },
|
|
\ 'query': {
|
|
\ '': 'rainbow-delimiters',
|
|
\ 'lua': 'rainbow-blocks',
|
|
\ },
|
|
\ 'priority': {
|
|
\ '': 110,
|
|
\ 'lua': 210,
|
|
\ },
|
|
\ 'highlight': [
|
|
\ 'RainbowDelimiterRed',
|
|
\ 'RainbowDelimiterYellow',
|
|
\ 'RainbowDelimiterBlue',
|
|
\ 'RainbowDelimiterOrange',
|
|
\ 'RainbowDelimiterGreen',
|
|
\ 'RainbowDelimiterViolet',
|
|
\ 'RainbowDelimiterCyan',
|
|
\ ],
|
|
\ 'blacklist': ['c', 'cpp'],
|
|
\ }
|
|
<
|
|
|
|
Alternatively, the same configuration in Lua:
|
|
>lua
|
|
-- This module contains a number of default definitions
|
|
local rainbow_delimiters = require 'rainbow-delimiters'
|
|
|
|
---@type rainbow_delimiters.config
|
|
vim.g.rainbow_delimiters = {
|
|
strategy = {
|
|
[''] = rainbow_delimiters.strategy['global'],
|
|
commonlisp = rainbow_delimiters.strategy['local'],
|
|
},
|
|
query = {
|
|
[''] = 'rainbow-delimiters',
|
|
lua = 'rainbow-blocks',
|
|
},
|
|
priority = {
|
|
[''] = 110,
|
|
lua = 210,
|
|
},
|
|
highlight = {
|
|
'RainbowDelimiterRed',
|
|
'RainbowDelimiterYellow',
|
|
'RainbowDelimiterBlue',
|
|
'RainbowDelimiterOrange',
|
|
'RainbowDelimiterGreen',
|
|
'RainbowDelimiterViolet',
|
|
'RainbowDelimiterCyan',
|
|
},
|
|
blacklist = {'c', 'cpp'},
|
|
}
|
|
<
|
|
|
|
*rainbow-delimiters.setup*
|
|
'rainbow-delimiters.setup'.setup({config})
|
|
|
|
Some people prefer to call a Lua `setup` function, so a setup function is
|
|
available as part of a Lua module.
|
|
>lua
|
|
require 'rainbow-delimiters.setup'.setup {
|
|
strategy = {
|
|
[''] = rainbow_delimiters.strategy['global'],
|
|
commonlisp = rainbow_delimiters.strategy['local'],
|
|
},
|
|
query = {
|
|
[''] = 'rainbow-delimiters',
|
|
latex = 'rainbow-blocks',
|
|
},
|
|
highlight = {
|
|
'RainbowDelimiterRed',
|
|
'RainbowDelimiterYellow',
|
|
'RainbowDelimiterBlue',
|
|
'RainbowDelimiterOrange',
|
|
'RainbowDelimiterGreen',
|
|
'RainbowDelimiterViolet',
|
|
'RainbowDelimiterCyan',
|
|
},
|
|
blacklist = {'c', 'cpp'},
|
|
}
|
|
<
|
|
The keys are exactly the same as for |g:rainbow_delimiters|. In fact, this
|
|
function does the same as setting the variable directly.
|
|
|
|
As an aside, this is a bad practice carried over from a time when Lua support
|
|
in Neovim still had issues with Vim script interoperability, but it has
|
|
persisted through cargo-culting. You are better off not using this function.
|
|
|
|
------------------------------------------------------------------------------
|
|
HIGHLIGHT COLORS *rb-delimiters-colors*
|
|
|
|
The `highlight` setting controls which highlight group to apply. It is a list
|
|
of any number of highlight group names as strings. The default values are in
|
|
this order:
|
|
|
|
- `RainbowDelimiterRed`
|
|
- `RainbowDelimiterYellow`
|
|
- `RainbowDelimiterBlue`
|
|
- `RainbowDelimiterOrange`
|
|
- `RainbowDelimiterGreen`
|
|
- `RainbowDelimiterViolet`
|
|
- `RainbowDelimiterCyan`
|
|
|
|
These are non-standard highlight groups and I have tried to find reasonable
|
|
default values for most uses. Nevertheless, you probably want to redefine
|
|
them for your colour scheme or link them to some existing group.
|
|
|
|
The colors are intentionally not in the order of the rainbow to help make the
|
|
contrast between adjacent delimiters more noticeable. Re-order the groups in
|
|
your settings if you prefer a different order.
|
|
|
|
Example highlight group definitions:
|
|
>vim
|
|
" Link to an existing highlight group
|
|
highlight link RainbowDelimiterRed WarningMsg
|
|
|
|
" Define the highlight from scratch
|
|
highlight RainbowDelimiterOrange guifg=#d65d0e ctermfg=White
|
|
<
|
|
You will probably want to have different colours per theme. Since most themes
|
|
will lack definitions for the above groups you will need to hook in somehow.
|
|
A simple solution is to use an autocommand.
|
|
>vim
|
|
au ColorSchemePre MyTheme highlight link RainbowDelimiter MyThemeRed
|
|
au ColorSchemePre MyTheme highlight link RainbowDelimiter MyThemeYellow
|
|
" and so on...
|
|
<
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
STRATEGIES *rb-delimiters-strategy*
|
|
|
|
A strategy defines how to perform the highlighting of delimiters. For
|
|
example, the included global strategy highlights every delimiter in a buffer
|
|
and updates the highlights when the document tree changes. On the other hand,
|
|
the included local strategy highlights only the sub-tree of the document which
|
|
contains the cursor and is updated whenever the cursor moves.
|
|
|
|
The strategy is set globally with per-language overrides. The setting is a
|
|
dictionary where the empty string is the key for the default strategy and the
|
|
overrides use the name of the language as keys.
|
|
|
|
Each value can be either a strategy or a function which evaluates to a
|
|
strategy. A function can be used to defer the decision to a later point in
|
|
time.
|
|
|
|
The function has the following signature:
|
|
|
|
Parameters: ~
|
|
• {bufnr} Number of the buffer to highlight
|
|
|
|
Return: ~
|
|
Either a strategy or `nil` (or |v:null|). If `nil`, rainbow delimiters
|
|
will be disabled for that buffer.
|
|
|
|
NOTE
|
|
Functions can only be used from Lua.
|
|
|
|
>lua
|
|
local rainbow = require 'rainbow-delimiters'
|
|
|
|
strategy = {
|
|
-- Use global strategy by default
|
|
[''] = rainbow.strategy['global'],
|
|
-- Use local for HTML
|
|
html = rainbow.strategy['local'],
|
|
-- Pick the strategy for LaTeX dynamically based on the buffer size
|
|
latex = function(bufnr)
|
|
-- Disabled for very large files, global strategy for large files,
|
|
-- local strategy otherwise
|
|
local line_count = vim.api.nvim_buf_line_count(bufnr)
|
|
if line_count > 10000 then
|
|
return nil
|
|
elseif line_count > 1000 then
|
|
return rainbow.strategy['global']
|
|
end
|
|
return rainbow.strategy['local']
|
|
end
|
|
}
|
|
<
|
|
|
|
A strategy is a table which must contain specific fields. It is possible to
|
|
define your own strategy, see |rb-delimiters-custom-strategy|. The following
|
|
strategies are included:
|
|
|
|
*rb-delimiters.strategy.global*
|
|
Global~
|
|
'rainbow-delimiters'.strategy['global']
|
|
|
|
The default strategy, highlights the entire buffer. Has very simple logic.
|
|
|
|
|
|
*rb-delimiters.strategy.local*
|
|
Local~
|
|
'rainbow-delimiters'.strategy['local']
|
|
|
|
Based on the cursor position highlights only the sub-tree which contains the
|
|
cursor. Updated every time the cursor moves and uses more complex logic than
|
|
the global strategy to figure out which nodes exactly to highlight.
|
|
|
|
|
|
*rb-delimiters.strategy.noop*
|
|
No-op~
|
|
'rainbow-delimiters'.strategy['noop']
|
|
|
|
A dummy strategy which does nothing. This is only useful in testing or if you
|
|
really want an empty strategy.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
QUERIES *rb-delimiters-query*
|
|
|
|
A query defines what to match. Every language needs its own custom query.
|
|
The query setting is a table where each entry maps a language name to a query
|
|
name. The empty string is the key for the default query.
|
|
|
|
Each value in the table can be either the name of a query file or a function
|
|
which evaluates to the name of a query file. A function can be used to defer
|
|
the decision to a later point in time.
|
|
|
|
The function has the following signature:
|
|
|
|
Parameters: ~
|
|
• {bufnr} Number of the buffer to highlight
|
|
|
|
Return: ~
|
|
The name of the query as a string.
|
|
|
|
|
|
NOTE
|
|
Functions can only be used from Lua.
|
|
|
|
>lua
|
|
query = {
|
|
-- Use parentheses by default
|
|
[''] = 'rainbow-delimiters',
|
|
-- Use blocks for Lua
|
|
lua = 'rainbow-blocks',
|
|
-- Determine the query dynamically
|
|
query = function(bufnr)
|
|
-- Use blocks for read-only buffers like in `:InspectTree`
|
|
local is_nofile = vim.bo[bufnr].buftype == 'nofile'
|
|
return is_nofile and 'rainbow-blocks' or 'rainbow-delimiters'
|
|
end
|
|
}
|
|
<
|
|
If you wish to define your own custom query or add support for a new language,
|
|
consult |rb-delimiters-custom-query| for details.
|
|
|
|
For every language the query `rainbow-delimiters` is defined, which matches a
|
|
reasonable set of parentheses and similar delimiters for each language. In
|
|
addition there are the following extra queries for certain languages:
|
|
|
|
- `latex`
|
|
- `rainbow-blocks` Matches `\begin` and `\end` instructions
|
|
- `lua`
|
|
- `rainbow-blocks` Matches keyword delimiters like like `function` and
|
|
`end`, in addition to parentheses
|
|
- `javascript`
|
|
- `rainbow-delimiters-react` Includes React support, set by default for
|
|
Javascript files
|
|
- `rainbow-parens` Only parentheses without React tags
|
|
- `rainbow-tags-react` Only React tags without parentheses
|
|
- `query`
|
|
- `rainbow-blocks` Highlight named nodes and identifiers in addition to
|
|
parentheses (useful for |:InspectTree|)
|
|
- `tsx`
|
|
- `rainbow-parens` Just Typescript highlighting without React tags
|
|
- `rainbow-tags-react` Only React tags without Typescript highlighting
|
|
- `typescript`
|
|
- `rainbow-parens` Just Typescript highlighting without React tags
|
|
- `verilog`
|
|
- `rainbow-blocks` Matches keyword delimiters like `begin` and `end`, in
|
|
addition to parentheses
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
LOGGING *rb-delimiters-logging*
|
|
|
|
By default only errors are logged. You can adjust what and how to log by
|
|
adjusting the values of the `log` entry in the settings. For information how
|
|
to change settings. see |rb-delimiters-setup|.
|
|
|
|
The following settings are supported:
|
|
|
|
`file`
|
|
Path to the log file, default is `rainbow-delimiters.log` in your standard
|
|
log path (see |standard-path|).
|
|
|
|
`level`
|
|
Only messages equal to or above this value will be logged. The default is
|
|
to log warnings or above. See |log_levels| for possible values.
|
|
|
|
The log file format is a CSV file which uses the `TAB` character (ASCII
|
|
`0x09`) as the field separator and a `NL` (ASCII `0x0A`) as the record
|
|
separator.
|
|
|
|
The fields are in this order:
|
|
|
|
- Time stamp of when the message was logged in ISO 8601 format with time zone
|
|
- Log level as string
|
|
- Lua module from which the message was logged, or the empty string if outside
|
|
a module
|
|
- The logged message
|
|
|
|
|
|
|
|
==============================================================================
|
|
EXTENDING RAINBOW DELIMITERS
|
|
|
|
Rainbow delimiters are hackable, you can add your own strategies, queries for
|
|
existing languages or even queries for new languages. Strategies and queries
|
|
are split up to be independent and can be mixed arbitrarily, but there are
|
|
some rules which need to be followed.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
THE LIBRARY *rb-delimiters-api*
|
|
|
|
There is a utility library provided for people writing their own strategies.
|
|
It is available as a table under the Lua module `'rainbow-delimiters'`.
|
|
|
|
|
|
*rb-delimiters.enable*
|
|
*rb_delimiters#enable*
|
|
'rainbow-delimiters'.enable({bufnr})
|
|
Re-enable rainbow delimiters for the buffer {bufnr} (or the current buffer
|
|
if {bufnr} is `0`) after it has been disabled.
|
|
|
|
rainbow_delimiters#enable({bufnr})
|
|
Vim script binding for the above function.
|
|
|
|
|
|
*rb-delimiters.disable*
|
|
*rb_delimiters#disable*
|
|
'rainbow-delimiters'.disable({bufnr})
|
|
Disable rainbow delimiters for the buffer {bufnr} (or the current buffer
|
|
if {bufnr} is `0`).
|
|
|
|
rainbow_delimiters#disable({bufnr})
|
|
Vim script binding for the above function.
|
|
|
|
|
|
*rb-delimiters.toggle*
|
|
*rb_delimiters#toggle*
|
|
'rainbow-delimiters'.toggle({bufnr})
|
|
Toggle rainbow delimiters for the buffer {bufnr} (or the current buffer
|
|
if {bufnr} is `0`).
|
|
|
|
rainbow_delimiters#toggle({bufnr})
|
|
Vim script binding for the above function.
|
|
|
|
|
|
*rb-delimiters.is_enabled*
|
|
*rb_delimiters#is_enabled*
|
|
'rainbow-delimiters'.is_enabled({bufnr})
|
|
Check if rainbow delimiters are enabled for the buffer {bufnr} (or the
|
|
current buffer if {bufnr} is `0`).
|
|
|
|
rainbow_delimiters#is_enabled({bufnr})
|
|
Vim script binding for the above function.
|
|
|
|
|
|
*rb-delimiters.hlgroup_at*
|
|
*rainbow-delimiters#hlgroup_at*
|
|
'rainbow-delimiters'.hlgroup_at({nesting_level})
|
|
Gets the name of the highlight group set up at the given nesting level.
|
|
This function will properly roll over, meaning that if there are seven
|
|
highlight groups defined and the {nesting_level} is nine, you will get the
|
|
second highlight group.
|
|
|
|
rainbow-delimiters#hlgroup_at({nesting_level})
|
|
Vim script binding for the above function.
|
|
|
|
|
|
*rb-delimiters.strategy*
|
|
*g:rainbow_delimiters#strategy*
|
|
'rainbow-delimiters'.strategy
|
|
Table of included strategies. For more information about strategies see
|
|
|rb-delimiters-strategy|. The included ones are:
|
|
|
|
- `global` |rb-delimiters.strategy.global|
|
|
- `local` |rb-delimiters.strategy.local|
|
|
- `noop` |rb-delimiters.strategy.noop|
|
|
|
|
Do not add your own strategies to this table.
|
|
|
|
g:rainbow_delimiters#strategy
|
|
Vim script dictionary, equivalent of the above table with the same keys.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
CUSTOM STRATEGIES *rb-delimiters-custom-strategy*
|
|
|
|
A strategy is a table which must contain a certain set of fields. In
|
|
object-oriented terminology we would say that a strategy table must implement
|
|
the strategy protocol.
|
|
>
|
|
strategy = {
|
|
on_attach = function(bufnr: integer, settings: table),
|
|
on_detach = function(bufnr: integer),
|
|
on_reset = function(bufnr: integer, settings: table),
|
|
}
|
|
<
|
|
|
|
------------------------------------------------------------------------------
|
|
on_attach({bufnr}, {settings})
|
|
|
|
This function takes two arguments: the number of the buffer and the table of
|
|
settings used by the buffer. This function is generally used to set up
|
|
autocommands or other callbacks for events when the highlighting needs to be
|
|
updated.
|
|
|
|
The settings table contains the following entries:
|
|
|
|
- `strategy` Strategy in use
|
|
- `parser` Reference to the buffer parser (|treesitter-languagetree|)
|
|
- `lang` Language of the current parser
|
|
|
|
A strategy should pick the settings it needs and either cache them in an
|
|
internal table, or construct closures (e.g. for callback functions) around
|
|
them.
|
|
|
|
------------------------------------------------------------------------------
|
|
on_detach({bufnr})
|
|
|
|
This function takes one argument: the number of the buffer. This function is
|
|
generally used to clean up any custom state, autocommands and callbacks set up
|
|
in the `on_attach` function.
|
|
|
|
------------------------------------------------------------------------------
|
|
on_reset({bufnr}, {settings})
|
|
|
|
Similar to `on_attach` with the same signature, except that this function is
|
|
called when the buffer has been reset in some way, for example if the
|
|
underlying file has been modified by a code formatter. Usually the strategy
|
|
should highlight the entire buffer from scratch again because we cannot rely
|
|
on Tree-sitter to tell us what has changed.
|
|
|
|
As a rule of thumb, `on_reset` should do the work of `on_attach`, minus all
|
|
the initial setup.
|
|
|
|
------------------------------------------------------------------------------
|
|
The logic within the strategy can vary wildly between strategies. Usually you
|
|
will want to install some callback in the `on_attach` function. That callback
|
|
can then use the Tree-sitter API and the utility library (see
|
|
|rb-delimiters-api|) to select which nodes to highlight and what highlight
|
|
group to apply.
|
|
|
|
See |rb-delimiters-custom-query| for the standard capture groups used.
|
|
Selecting standard capture groups allows your strategy to work with any of the
|
|
built-in queries as well as user-specified custom queries.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
CUSTOM QUERIES *rb-delimiters-custom-query*
|
|
|
|
A query defines what exactly needs to be highlighted. Different languages
|
|
have different document trees, so you need a separate query for each language.
|
|
The queries need to define the following capture groups:
|
|
|
|
- `@container`
|
|
The entire delimited node.
|
|
- `@delimiter`
|
|
Any delimiter you want to highlight in the current `@container`.
|
|
- `@sentinel`
|
|
A marker used to signal that you are done with the `@container`. This
|
|
should almost always be put right after the last `@delimiter` in the given
|
|
`@container`.
|
|
- `@_<capture_name>`
|
|
Delimiters starting with `_` (underscore) are ignored for highlighting
|
|
purposes, but you can use them for treesitter predicates like
|
|
`#eq?`, `#any-eq?`, etc. (These are very rarely needed.)
|
|
|
|
`@container` and `@sentinel` are mandatory, and `@delimiter` will always be
|
|
present as well since `@delimiter` is what is highlighted. The captures
|
|
starting with underscore will be rarely used, since you only need them for
|
|
predicates in a few special cases.
|
|
|
|
|
|
Let's look at an example first. Here is a snippet of HTML code:
|
|
>html
|
|
<a href="https://example.com">
|
|
Example<br/>link
|
|
</a>
|
|
<
|
|
|
|
The corresponding document tree including anonymous nodes is as follows:
|
|
>query
|
|
(element ; [0, 0] - [2, 4]
|
|
(start_tag ; [0, 0] - [0, 30]
|
|
"<" ; [0, 0] - [0, 1]
|
|
(tag_name) ; [0, 1] - [0, 2]
|
|
(attribute ; [0, 3] - [0, 29]
|
|
(attribute_name) ; [0, 3] - [0, 7]
|
|
"=" ; [0, 7] - [0, 8]
|
|
(quoted_attribute_value ; [0, 8] - [0, 29]
|
|
"\"" ; [0, 8] - [0, 9]
|
|
(attribute_value) ; [0, 9] - [0, 28]
|
|
"\"")) ; [0, 28] - [0, 29]
|
|
">") ; [0, 29] - [0, 30]
|
|
(text) ; [1, 4] - [1, 11]
|
|
(element ; [1, 11] - [1, 16]
|
|
(self_closing_tag ; [1, 11] - [1, 16]
|
|
"<" ; [1, 11] - [1, 12]
|
|
(tag_name) ; [1, 12] - [1, 14]
|
|
"/>")) ; [1, 14] - [1, 16]
|
|
(text) ; [1, 16] - [1, 20]
|
|
(end_tag ; [2, 0] - [2, 4]
|
|
"</" ; [2, 0] - [2, 2]
|
|
(tag_name) ; [2, 2] - [2, 3]
|
|
">")) ; [2, 3] - [2, 4]
|
|
<
|
|
|
|
As a human I immediately perceive the entire link as one object with two
|
|
delimiters: the opening `<a>` tag and the closing `</a>` tag. Perhaps the
|
|
self-closing `<br/>` tag can be seen as an intermediate delimiter because it
|
|
does not open a new scope. On the other hand, it is part of the content of
|
|
the entire link, not one of its delimiters.
|
|
|
|
As you can see, it is up to interpretation as to what exactly constitutes a
|
|
delimiter. In this example for the sake of exhaustiveness we will consider
|
|
the `<br/>` tag a delimiter. The corresponding query is as follows:
|
|
>query
|
|
(element
|
|
(start_tag) @delimiter
|
|
(element
|
|
(self_closing_tag) @delimiter)? ; Optional!
|
|
(end_tag) @delimiter @sentinel) @container
|
|
<
|
|
Highlighting the entire tag might be too vibrant though. What if we want to
|
|
highlight only the opening and closing angle brackets? The query gets
|
|
slightly more complex because we have to descend deeper into the document
|
|
tree.
|
|
>query
|
|
(element
|
|
((start_tag
|
|
["<" ">"] @delimiter)
|
|
(element
|
|
(self_closing_tag
|
|
["<" "/>"] @delimiter))? ;Optional!
|
|
(end_tag
|
|
"</" @delimiter
|
|
">" @delimiter @sentinel))) @container
|
|
<
|
|
Note that we don't want to put the `@sentinel` on the second to last `@delimiter`
|
|
`"</"`, we need to be careful to put it on the last `@delimiter`.
|
|
|
|
You might now see why we need the `@container` capture group: there is no way
|
|
to know in general how deeply the delimiter is nested. Even for one language
|
|
our understanding of what constitutes a delimiter is up for debate. Therefore
|
|
a human must decide for each query which node is the container and which nodes
|
|
are the delimiters. Capturing this information makes it available for use in
|
|
strategies.
|
|
|
|
We are not limited to only one opening and one closing delimiter. The
|
|
included default query captures the angle brackets and the tag name, but not
|
|
attributes between the tag name and closing angle bracket. This strikes a
|
|
pleasant middle ground between the two above extremes.
|
|
>query
|
|
(element
|
|
(start_tag
|
|
"<" @delimiter
|
|
(tag_name) @delimiter
|
|
">" @delimiter)
|
|
(end_tag
|
|
"</" @delimiter
|
|
(tag_name) @delimiter
|
|
">" @delimiter @sentinel)) @container
|
|
<
|
|
Here both opening and closing tag have three delimiters each.
|
|
|
|
In HTML the terminating slash in a self-closing tag is optional. Instead of
|
|
`<br/>` we can write `<br>`. A naïve query would look like this:
|
|
>query
|
|
(element
|
|
(start_tag
|
|
"<" @delimiter
|
|
(tag_name) @delimiter @_tag_name
|
|
">" @delimiter @sentinel)) @container
|
|
<
|
|
However, this query also matches the opening tag of regular tags like `<div>`.
|
|
This is where the `@_tag_name` capture comes in. The set of self-closing tags
|
|
is finite, so we can list them explicitly. This way a regular opening tag
|
|
will not match this particular pattern.
|
|
>query
|
|
(element
|
|
(start_tag
|
|
"<" @delimiter
|
|
(tag_name) @delimiter @_tag_name
|
|
">" @delimiter @sentinel)
|
|
;; List abridged for brevity
|
|
(#any-of? @_tag_name "br" "hr" "input")) @container
|
|
<
|
|
We need the `@_tag_name` capture so that it can be used with the `#any-of?`
|
|
predicate (|treesitter-predicate-any-of?|), but the capture itself is not used
|
|
for highlighting.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
ADDING SUPPORT FOR NEW LANGUAGES *rb-delimiters-custom-lang*
|
|
|
|
Supporting a new new language requires creating one or more queries for the
|
|
language. If the query is mature enough please consider upstreaming it so
|
|
everyone can benefit.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
ADDING HIGHLIGHT TESTS *rb-delimiters-highlight-test*
|
|
|
|
Whenever you make changes to a query or add support for a new language you
|
|
should also add highlighting tests if you want to upsteam your changes. There
|
|
are two steps:
|
|
|
|
- Add a sample file to show off the highlighting
|
|
- Record the state of highlighting
|
|
|
|
See the `CONTRIBUTING` file of this repository for details.
|
|
|
|
|
|
==============================================================================
|
|
RECIPES *rb-delimiters-recipes*
|
|
|
|
Various tricks and hacks which have accumulated over time.
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
DISABLE WITHOUT BLACKLISTING
|
|
|
|
If you wish to disable rainbow delimiters for a language but for whatever
|
|
reason you do not want to blacklist this language you can create a custom
|
|
query (|rb-delimiters-custom-query|) without any patterns. Then set the name
|
|
of that query as the query for that language (|rb-delimiters-query|).
|
|
|
|
Or you could name your custom query the same as the default query (by default
|
|
`rainbow-delimiters`), then you do not have to change your configuration.
|
|
|
|
|
|
==============================================================================
|
|
ACKNOWLEDGMENTS *rb-delimiters-credit*
|
|
|
|
The original version of nvim-ts-rainbow was written by Chinmay "p00f" Dalal,
|
|
and discontinued in January of 2023. The original repositories can be found
|
|
under these URLs:
|
|
|
|
- https://sr.ht/~p00f/nvim-ts-rainbow/
|
|
- https://github.com/p00f/nvim-ts-rainbow
|
|
|
|
|
|
==============================================================================
|
|
FURTHER READING *rb-delimiters-reading*
|
|
|
|
- nvim-treesitter plugin https://github.com/nvim-treesitter/nvim-treesitter
|
|
- Official Tree-sitter website https://tree-sitter.github.io/tree-sitter/
|
|
- Neovim Tree-sitter documentation: |treesitter.txt|
|
|
|
|
|
|
==============================================================================
|
|
vim:tw=78:ts=8:sw=4:et:ft=help:norl:
|