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,43 @@
-- SPDX-License-Identifier: Unlicense
-- This is free and unencumbered software released into the public domain.
--
-- Anyone is free to copy, modify, publish, use, compile, sell, or distribute
-- this software, either in source code form or as a compiled binary, for any
-- purpose, commercial or non-commercial, and by any means.
--
-- In jurisdictions that recognize copyright laws, the author or authors of
-- this software dedicate any and all copyright interest in the software to
-- the public domain. We make this dedication for the benefit of the public
-- at large and to the detriment of our heirs and successors. We intend this
-- dedication to be an overt act of relinquishment in perpetuity of all
-- present and future rights to this software under copyright law.
--
-- 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 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.
--
-- For more information, please refer to <https://unlicense.org/>
return {
_all = {
coverage = false,
lua = './test/nvim-shim'
},
default = {
verbose = true,
},
unit = {
ROOT = {'./test/unit/'},
},
e2e = {
ROOT = {'./test/e2e/'},
pattern = '', -- No fancy names for E2E tests
},
}
-- vim:ft=lua

View File

@ -0,0 +1,15 @@
root = true
[*]
indent_size = tab
tab_width = 4
end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.lua]
indent_style = tab
[*.scm]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,34 @@
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: ["bug"]
assignees:
- "HiPhish"
body:
- type: 'input'
attributes:
label: 'Neovim version'
description: 'Version of your Neovim'
placeholder: '0.0.0'
- type: 'input'
attributes:
label: 'Language affected'
description: 'Leave empty if you experience the issue with any file type.'
placeholder: 'Lua'
- type: 'input'
attributes:
label: 'Query'
description: 'Leave empty if the issue is not tied to a specific query.'
placeholder: 'rainbow-delimiters'
- type: 'input'
attributes:
label: 'Strategy'
description: 'Leave empty if the issue is not tied to a specific strategy.'
placeholder: 'global'
- type: 'textarea'
attributes:
label: 'Description'
description: |
Please describe the bug in detail here. If possible provide a minimal
code sample which replicates the issue
placeholder: 'print((("Hello world!")))'

View File

@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Main repoo
url: 'https://gitlab.com/HiPhish/nvim-ts-rainbow2'
about: 'Upstream repository; you can also report bugs here on GitHub.'

View File

@ -0,0 +1,7 @@
# Tag file created by Vim
doc/tags
# Fake user directory structures
test/xdg/local/share/nvim/
test/xdg/local/state/nvim/
!test/xdg/local/share/nvim/site/pack/testing/start/rainbow-delimiters

View File

@ -0,0 +1,7 @@
- Neovim version:
- Language affected:
- Query used:
- Strategy used:
Please describe the bug in detail here. If possible provide a minimal code
sample which replicates the issue.

View File

@ -0,0 +1,6 @@
[submodule "test/xdg/local/share/nvim/site/pack/testing/start/nvim-treesitter"]
path = test/xdg/local/share/nvim/site/pack/testing/start/nvim-treesitter
url = https://github.com/nvim-treesitter/nvim-treesitter
[submodule "test/xdg/local/share/nvim/site/pack/testing/start/yo-dawg"]
path = test/xdg/local/share/nvim/site/pack/testing/start/yo-dawg
url = https://gitlab.com/HiPhish/yo-dawg.nvim.git

View File

@ -0,0 +1,4 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"workspace.ignoreDir": ["test/xdg"]
}

View File

@ -0,0 +1,2 @@
" Use the custom shim as the busted binary for testing
let g:bustedprg='./test/busted'

View File

@ -0,0 +1,309 @@
.. default-role:: code
###########
Changelog
###########
All notable changes to this project will be documented in this file. The format
is based on `Keep a Changelog`_ and this project adheres to `Semantic
Versioning`_.
Unreleased
##########
Added
=====
- Missing patterns for C++:
- `condition_clause`
- `for_statement`
- `cast_expression`
- `array_declarator`
- Missing patterns for Common Lisp:
- `loop_macro`
- Missing patterns for Javascript:
- `array_pattern`
- `for_in_statement`
- `for_statement`
- Missing patterns for Typescript:
- `enum_body`
- `interface_body`
- Missing patterns for Haskell:
- `children`
- `fields`
- `list`
- `parens`
- `prefix_id`
- `record`
- `tuple`
- `unit`
Changed
=======
- Renamed C# patterns:
- `for_each_statement` to `foreach_statement`
- `type_of_expression` to `typeof_expression`
- `size_of_expression` to `sizeof_expression`
- `implicit_stack_alloc_array_creation_expression` to `implicit_stackalloc_expression`
Removed
=======
- Deprecated patterns for C#:
- `interpolation`
- Deprecated patterns for Java:
- `condition`
- Deprecated patterns for Haskell:
- `con_list`
- `context`
- `deriving`
- `exp_arithmetic_sequence`
- `exp_lambda`
- `exp_list_comprehension`
- `exp_list`
- `exp_name`
- `exp_record`
- `exp_section_right`
- `pat_fields`
- `pat_list`
- `pat_parens`
- `pat_tuple`
- `record_fields`
- `type_list`
- `type_parens`
- `type_tuple`
- `type_tuple`
[0.4.0] - 2024-05-07
####################
Added
=====
- Public API function `is_enabled`
- Rasi support
- Svelte support
- Teal support
- Typst support
- XML support
- Missing patterns for Java:
- `array_initializer`
- `annotation_argument_list`
- `catch_clause`
- `condition`
- `constructor_body`
- `dimensions_expr`
- `enhanced_for_statement`
- `for_statement`
- `inferred_parameters`
- `parenthesized_expression`
- `resource_specification`
- `cast_expression`
- Missing patterns for Go:
- `type_assertion_expression`
- Missing patterns for Julia:
- `curly_expression`
- `tuple_expression`
- Missing patterns for Lua:
- `field`
- Missing patterns for Luadoc:
- `indexed_field`
- `tuple_type`
- Missing patterns for Python:
- `dict_pattern`
- `import_from_statement`
- `interpolation` (literal string interpolation)
- `list_pattern`
- `tuple_pattern`
- Missing patterns for R:
- `for`
- `while`
- `switch`
- `function_definition`
- Missing patterns for Rust:
- `array_type`
- Missing patterns for Starlark:
- `tuple_pattern`
Fixed
=====
- Default configuration settings override custom settings if the configuration
value was used before setting the custom value
- Switched Fennel queries to new upstream grammar (`#6132`_)
- Deleted obsolete Julia pattern `parameter_list`
.. _#6132: https://github.com/nvim-treesitter/nvim-treesitter/pull/6132
[0.3.0] 2023-12-24
##################
This release brings a plethora of missing patterns to existing queries and lets
you specify priorities and queries dynamically at runtime. This means that it
is possible to set different queries for the same language depending on
external conditions, such as whether a buffer is read-only.
And as a little extra given the date of this release, there is a new Christmas
strategy module. This will let you decorate your syntax tree in an especially
festive mood. The module is just a joke, so it will not be loaded by default
and you will have to figure out yourself how to set it up.
Added
=====
- Starlark support
- Missing patterns for Bash:
- `array`
- `function_definition`
- `arithmetic_expansion`
- `compound_statement`
- `subscript`
- Missing patterns for C:
- `enumerator_list`
- `macro_type_specifier`
- `preproc_params`
- `compound_literal_expression`
- `parenthesized_declarator`
- Missing patterns for Elixir:
- `access_call`
- Missing patterns for Fennel:
- `table_binding`
- New query for language `query`:
- `rainbow-blocks`
- New query for language `javascript`:
- `rainbow-tags-react`
- New query for language `tsx`:
- `rainbow-tags-react`
- New Christmas strategy module `rainbow-delimiters.strategy.christmas` (not
loaded by default)
Fixed
=====
- Query can be a function in configuration
- Priority can be a function in configuration
- Functions in configuration take buffer number as argument
- Updated Nim queries
[0.2.0] - 2023-11-26
####################
Added
=====
- Ability to set highlight priority
- Cue support
- Luadoc support
- Nim support
- Kotlin support
- templ support
- Terraform support
- TOML support
Fixed
=====
- Type error in local strategy
- Log error in local strategy (Neovim <0.10 only)
- Missing patterns for CSS
- `feature_query`
- `arguments`
- `attribute_selector`
- Missing patterns for Go
- `array_type`
- `slice_expression`
- Missing patterns for HCL
- `for_tuple_expr`
- `new_index`
- `expression`
- `binary_operation`
- `for_object_expr`
- `template_interpolation`
- `unary_operation`
- Missing pattern for Javascript and Typescript
- `switch_body`
- Missing patterns for Nix
- `rec_attrset_expression`
- `inherit_from`
- Missing pattern for SCSS
- `parameters`
Changed
=======
- Default highlight priority is 110 instead of 210, which is between
Tree-sitter and LSP semantic tokens
[0.1.0] - 2023-11-12
####################
Initial release
.. ----------------------------------------------------------------------------
.. _Keep a Changelog: https://keepachangelog.com/en/1.0.0/,
.. _Semantic Versioning: https://semver.org/spec/v2.0.0.html

View File

@ -0,0 +1,38 @@
.. default-role:: code
#####################
Contributor's guide
#####################
Adding support for a new language
#################################
Languages are supported through language-specific queries. Each language needs
at lease one query which matches the most common delimiters, usually `(`, `)`,
`[`, `]`, `{`, `}`, `<` and `>`. Read `:h rb-delimiters-custom-query` in the
manual first to understand how to write queries. Your query should meet the
following criteria:
- Named `rainbow-delimiters`
- Few `@delimiter` capture groups per `@container`; we do not want the default
query to be too vibrant
- Write one or more files in the language under `test/highlight/<lang>` (where
`<lang>` is the language); the standard file name is `regular.<ext>`
- The test code must have at least one instance of each pattern in the query
- The test code must not have parsing errors
- The test code should ideally have multiple levels of nesting
- The test code should compile and have no linter errors if that is feasible
(this is not a hard rule though)
If there are many test cases or if the code becomes too verbose feel free to
create multiple test files.
In addition to the queries and test file(s), please consider adding the type
annotations in `lua/rainbow-delimiters.types.lua` if you are adding queries
for a new language. You will need to update:
- `@class rainbow_delimiters.config.strategies`
- `@class rainbow_delimiters.config.queries`
- `@class rainbow_delimiters.config.priorities`
- `@alias rainbow_delimiters.language`

View File

@ -0,0 +1,335 @@
.. default-role:: code
#################################
Hacking on Rainbow Delimiters 2
#################################
Testing
#######
A test setup must meet the following criteria:
- Test definitions must be run by with Neovim as the Lua interpreter to get
access to all Neovim APIs
- Tests must not be affected by the user's own plugins and configuration
- Each test which mutates editor state must run in its own Neovim process
The first two points are achieved through a small command-line interface
adapter script (a shim). The shim exposes the command-line interface of a Lua
interpreter, and internally it sets up environment variable to point Neovim at
a prepared blank directory structure. Neovim is then called with the `-l`
flag.
We do have to use some plugins though:
- This plugin itself
- nvim-treesitter_ to install parsers for some languages
Both plugins are stored under the `$XDG_DATA_HOME` directory, the former as a
symlink and the latter as a Git submodule.
As for process isolation, this is achieved inside the tests. We start a
headless embedded Neovim instance which we control through MsgPack RPC from
inside the test. We can control and probe this process only indirectly, which
is awkward, but this is the best solution I could find.
Unit testing
============
We use busted_ for unit testing. A unit is a self-contained module which can
be used on its own independent of the editor. Execute `make unit-test` to run
unit tests. The `busted` binary must be available on the system `$PATH`.
End to end testing
==================
End-to-end tests run in a separate Neovim instance which we control via RPC.
These are tests which mutate the state of the editor, such as adding
highlighting on changes. Execute `make e2e-test` to run all end to end tests.
Running tests with Neotest-busted
=================================
To run tests the `g:bustedprg` variable must be set to `'./test/busted'`, which
is the path to the shim script. If the `exrc` option is set the variable will
be set automatically.
Design decisions
################
Tables over strings for configuration
=====================================
Strategies are given as a complex table, but a string identifier would have
been much more pleasant on the eye. Which of these two is easier to read and
write?
.. code:: lua
-- This?
settings = {
strategy = {
'global'
html = 'local'
}
}
-- Or this?
settings = {
strategy = {
require 'ts-rainbow.strategy.global'
html = require 'ts-rainbow.strategy.local'
}
}
Using strings might seem like the more elegant choice, but it it makes the code
more complicated to maintain and less flexible for the user. With tables a
user can create a new custom strategy and assign it directly without the need
to "register" them first under some name.
More importantly though, we have unlimited freedom where that table is coming
from. Suppose we wanted to add settings to a strategy. With string
identifiers we now need much more machinery to connect a string identifier and
its settings. On the other hand, we can just call a function with the settings
are arguments which returns the strategy table.
.. code:: lua
settings = {
strategy = {
require 'ts-rainbow.strategy.global',
-- Function call evaluates to a strategy table
latext = my_custom_strategy {
option_1 = true,
option_2 = 'test'
}
}
}
Strategies
##########
On container nodes
==================
Every query has to define a `container` capture in addition to `opening` and
`closing` captures. As humans we understand the code at an abstract level, but
Tree-sitter works on a more concrete level. To a human the HTML tag `<div>` is
one atomic object, but to Tree-sitter it is actually a container with further
elements.
Consider the following HTML snippet:
.. code:: html
<div>
Hello
</div>
The tree looks like this (showing anonymous nodes):
.. code::
element [0, 0] - [2, 6]
start_tag [0, 0] - [0, 5]
"<" [0, 0] - [0, 1]
tag_name [0, 1] - [0, 4]
">" [0, 4] - [0, 5]
text [1, 1] - [1, 6]
end_tag [2, 0] - [2, 6]
"</" [2, 0] - [2, 2]
tag_name [2, 2] - [2, 5]
">" [2, 5] - [2, 6]
We want to highlight the lower-level nodes like `tag_name` or `start_tag` and
`end_tag`, but we want to base our logic on the higher-level nodes like
`element`. The `@container` node will not be highlighted, we use it to
determine the nesting level or the relationship to other container nodes.
Determining the level of container node
=======================================
In order to correctly highlight containers we need to know the nesting level of
each container relative to the other containers in the document. We can use
the order in which matches are returned by the `iter_matches` method of a
query. The iterator traverses the document tree in a depth-first manner
according to the visitor patter, but matches are created upon exiting a node.
Let us look at a practical example. Here is a hypothetical tree:
.. code::
A
├─B
│ └─C
│ └─D
└─E
├─F
└─G
The nodes are returned in the following order:
#) D
#) C
#) B
#) F
#) G
#) E
#) A
We can only know how deeply nodes are nested relative to one another. We need
to build the entire tree structure to know the absolute nesting levels. Here
is an algorithm which can build up the tree, it uses the fact that the order of
nodes never skips over an ancestor.
Start with an empty stack `s = []`. For each match `m` do the following:
#) Keep popping matches off `s` up until we find a match `m'` whose
`@container` node is not a descendant of the container node of `m`. Collect
the popped matches (excluding `m'`) onto a new stack `s_m` (order does not
matter)
#) Set `s_m` as the child match stack of `m`
#) Add `m` to `s`
Eventually `s` will only contain root-level matches, i.e. matches of nesting
level one. To apply the highlighting we can then traverse the match tree,
incrementing the highlighting level by one each time we descend a level.
The order of matches among siblings in the tree does not matter. The above
algorithm uses a stack when collecting children, but any unordered
one-dimensional sequence will do. The stack `s` is important for determining
the relationship between nodes: since we know that no ancestors will be skipped
we can be certain that we can stop checking the stack for descendants of `m`
once we encounter the first non-descendant match. Otherwise we would have to
compare each match with each other match, which would tank the performance.
The local highlight strategy
============================
Consider the following bit of contrived HTML code:
.. code:: html
<div id="Alpha">
<div id="Bravo">
<div id="Charlie">
</div>
</div>
<div id="Delta">
</div>
</div>
Supposed the cursor was inside the angle brackets of `Bravo`, which tags
should we highlight? From eyeballing the obvious answer is `Alpha`, `Bravo`
and `Charlie`. Obviously `Alpha` and `Bravo` both contain the cursor within
the range, but how do we know that we need to highlight `Charlie`? `Charlie`
is contained inside `Bravo`, which contains the cursor, but on the other hand
`Delta` is contained inside `Alpha`, which also contains the cursor. We cannot
simply check whether the parent contains the cursor.
When working with the Tree-sitter API and iterating through matches and
captures we have no way of knowing that any of the captures within `Charlie`
are contained within `Bravo`. However, due to the order of traversal we do
know that `Bravo` is the lowest node to still contain the cursor.
Therefore we that the first match which contains the cursor is the lowest one.
If a match does not contain the cursor we can check whether it is a
descendant of the cursor container match.
The problem with nested languages
#################################
The language tree of a buffer is a tree of parsers. Some languages like
Markdown can contain other languages, which complicates things.
Foreign extmarks
================
Extmarks move along with the text they belong to. This is generally a good
thing, but it can become a problem if we move text from one language to
another. Consider the following Markdown code:
.. code:: markdown
Hello world
```lua
print {{{{}}}}
print {{{{}}}}
```
We can move the cursor to line 4 and move that line out of the Lua block by
executing `:move 1` to move it to the second line. However, this will preserve
the extmarks and we will end up with Lua delimiter highlighting inside
Markdown.
My solution is on every change to delete all rainbow delimiter extmarks which
do not belong to the current language.
Overwritten extmarks
====================
Take the following Markdown code:
.. code:: markdown
Hello world
```c
puts("This is an injected language")
{
{
{
{
{
return ((((((2)))))) + ((((3))))
}
}
}
}
}
```
If we put the cursor on the line with the `puts` statement and move it up one
line (`:move -2`) we get the following changes:
- Markdown
- `{ 2, 0, 3, 0 }`
This means lines 3 and 4 of the Markdown tree have changed; we have changed the
contents of the fifth line and added one more line. This is all as expected.
However, let us now move the line back down by executing `:move +1`. We get
the following changes:
- Markdown
- `{ 3, 0, 15, 0 }`
- C
- `{ 3, 0, 4, 0 }`
The changes to the C tree are what we expect. However, the changes to the
Markdown tree span the code block as well. This is a problem when we start
deleting foreign extmarks (see above). If we work from the outside we wipe out
all non-Markdown extmarks in the range, which includes the C extmarks. Then we
apply the C extmarks inside the C block, but the C change does not span the
entire C tree. Thus we will only apply highlighting to the changed C line, but
not the remainder of the C block.
The solution at the moment is to overwrite the changes of nested languages. If
the changes belong to a language tree with parent language we replace all the
changes with a range that spans the entire tree for that language.
.. _busted: https://lunarmodules.github.io/busted/#defining-tests
.. _nvim-treesitter: https://github.com/nvim-treesitter/nvim-treesitter

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,229 @@
.. default-role:: code
###############################
Rainbow delimiters for Neovim
###############################
This Neovim plugin provides alternating syntax highlighting (“rainbow
parentheses”) for Neovim, powered by `Tree-sitter`_. The goal is to have a
hackable plugin which allows for different configuration of queries and
strategies, both globally and per file type. Users can override and extend the
built-in defaults through their own configuration.
This is a fork of `nvim-ts-rainbow2`_, which was implemented as a module for
`nvim-treessiter`_. However, since nvim-treesitter has deprecated the module
system I had to create this standalone plugin.
Installation and setup
######################
Installation
============
Install it like any other Neovim plugin. You will need a Tree-sitter parser
for each language you want to use rainbow delimiters with.
Setup
=====
No configuration is needed to get started, this plugin has reasonable defaults
which you can override. Configuration is done by setting entries in the Vim
script dictionary `g:rainbow_delimiters`. Here is an example configuration:
.. code:: 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',
\ ],
\ }
The equivalent code in Lua:
.. code:: 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'],
vim = rainbow_delimiters.strategy['local'],
},
query = {
[''] = 'rainbow-delimiters',
lua = 'rainbow-blocks',
},
priority = {
[''] = 110,
lua = 210,
},
highlight = {
'RainbowDelimiterRed',
'RainbowDelimiterYellow',
'RainbowDelimiterBlue',
'RainbowDelimiterOrange',
'RainbowDelimiterGreen',
'RainbowDelimiterViolet',
'RainbowDelimiterCyan',
},
}
Please refer to the `manual`_ for more details. For those who prefer a `setup`
function there is the module `rainbow-delimiters.setup` that accepts all the
same parameters as `g:rainbow-delimiters`.
.. code:: lua
require('rainbow-delimiters.setup').setup {
strategy = {
-- ...
},
query = {
-- ...
},
highlight = {
-- ...
},
}
Help wanted
###########
There are only so many languages which I understand to the point that I can
write queries for them. If you want support for a new language please consider
contributing code. See the CONTRIBUTING_ for details.
Status of the plugin
####################
Tree-sitter support in Neovim is still experimental. This plugin and its API
should be considered stable insofar as breaking changes will only happen if
changes to Neovim necessitates them.
License
#######
Licensed under the Apache-2.0 license. Please see the `LICENSE`_ file for
details.
Migrating from nvim-ts-rainbow2
###############################
Rainbow-Delimiters uses different settings than nvim-ts-rainbow2, but
converting the configuration is straight-forward. The biggest change is where
the settings are stored.
- Settings are stored in the global variable `g:rainbow-delimiters`, which has
the same keys as the old settings
- The default strategy and query have index `''` (empty string) instead of `1`
- Default highlight groups have the prefix `RainbowDelimiter` instead of
`TSRainbow`, e.g. `RainbowDelimiterRed` instead of `TSRainbowRed`
- The default query is now called `rainbow-delimiters` instead of
`rainbow-parens`
- The public Lua module is called `rainbow-delimiters` instead of `ts-rainbow`
The name of the default query is now `rainbow-delimiters` because for some
languages like HTML the notion of "parentheses" does not make any sense. In
HTML the only meaningful delimiter is the tag. Hence the generic notion of a
"delimiter".
Attribution
###########
This is a fork of a previous Neovim plugin, the original repository is
available under https://sr.ht/~p00f/nvim-ts-rainbow/.
Attributions from the original author
=====================================
Huge thanks to @vigoux, @theHamsta, @sogaiu, @bfredl and @sunjon and
@steelsojka for all their help
.. _Tree-sitter: https://tree-sitter.github.io/tree-sitter/
.. _nvim-treesitter: https://github.com/nvim-treesitter/nvim-treesitter
.. _CONTRIBUTING: CONTRIBUTING.rst
.. _LICENSE: LICENSE
.. _manual: doc/rainbow-delimiters.txt
.. _neovim/neovim#17099: https://github.com/neovim/neovim/pull/17099
.. _nvim-ts-rainbow2: https://gitlab.com/HiPhish/nvim-ts-rainbow2
.. _nvim-treessiter: https://github.com/nvim-treesitter/nvim-treesitter
Screenshots
###########
Bash
====
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/514ed2a2-68a4-427e-aef6-7ac3a02a2ba0
:alt: Screenshot of a Bash script with alternating coloured delimiters
C
=
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/45f8e727-d507-43df-b112-a269e7262533
:alt: Screenshot of a C program with alternating coloured delimiters
Common Lisp
===========
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/5e7e15bb-a4e3-41e5-b3fc-3c4150ffd252
:alt: Screenshot of a Common Lisp program with alternating coloured delimiters
HTML
====
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/371d310c-d5a7-490d-bb55-d3fe4bd8b1a8
:alt: Screenshot of an HTML document with alternating coloured delimiters
Java
====
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/bb372051-ec5f-4c0b-a9b9-3cd37edafa4f
:alt: Screenshot of a Java program with alternating coloured delimiters
LaTeX
=====
Using the `rainbow-blocks` query to highlight the entire `\begin` and `\end`
instructions.
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/0176cc0d-b729-417e-8f85-c31da70d49f5
:alt: Screenshot of a LaTeX document with alternating coloured delimiters
Lua
===
Using the `rainbow-blocks` query to highlight the entire keywords like
`function`, `if`, `else` and `end`.
.. image:: https://github.com/HiPhish/rainbow-delimiters.nvim/assets/4954650/a915f7e0-b1c9-4af2-ae1d-f2f48aa325e5
:alt: Screenshot of a Lua script with alternating coloured delimiters

View File

@ -0,0 +1,36 @@
.. default-role:: code
#######################
Plans for this plugin
#######################
Built-in queries
################
As of version 0.8.3 Neovim can only match one node per query. This is a
limitation of Neovim and there is nothing that can be done on this end.
The global query does not updated the highlighting of injected languages.
Queries which I cannot port
===========================
I do not know enough about the following languages in order to write good
queries. Contributions are welcome.
- devicetree
- elm
- gdscript
- graphql
- kotlin
- meson
- ocaml
- ocaml_interface
- scala
- solidity
- sparql
- supercollider
- svelte
- turtle

View File

@ -0,0 +1,50 @@
" Copyright 2023 Alejandro "HiPhish" Sanchez
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.
let g:rainbow_delimiters#strategy = {
\ 'global': luaeval("require 'rainbow-delimiters.strategy.global'"),
\ 'local': luaeval("require 'rainbow-delimiters.strategy.local'"),
\ 'noop': luaeval("require 'rainbow-delimiters.strategy.no-op'"),
\ }
" Get the appropriate highlight group for the given level of nesting.
function! rainbow_delimiters#hlgroup_at(i)
return luaeval("require('rainbow-delimiters').hlgroup_at(_A)", a:i)
endfunction
" Disable highlighting for the given buffer. Buffer number zero means current
" buffer.
function! rainbow_delimiters#disable(bufnr)
call luaeval("require('rainbow-delimiters').disable(_A)", a:bufnr)
endfunction
" Enable highlighting for the given buffer. Buffer number zero means current
" buffer.
function! rainbow_delimiters#enable(bufnr)
call luaeval("require('rainbow-delimiters').enable(_A)", a:bufnr)
endfunction
" Toggle highlighting for the given buffer. Buffer number zero means current
" buffer.
function! rainbow_delimiters#toggle(bufnr)
call luaeval("require('rainbow-delimiters').toggle(_A)", a:bufnr)
endfunction
" Check if highlighting is enabled for the given buffer. Buffer number zero
" means current buffer.
function! rainbow_delimiters#is_enabled(bufnr)
return luaeval("require('rainbow-delimiters').is_enabled(_A)", a:bufnr)
endfunction
" vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,721 @@
*rainbow-delimiters.txt* Alternating highlight for delimiters in code
*rainbow-delimiters*
*rb-delimiters*
Author: Alejandro "HiPhish" Sanchez
License: Apache-2.0
Version: 0.4.0
==============================================================================
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|
4. Acknowledgements .......................... |rb-delimiters-credit|
5. 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.
`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.
==============================================================================
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:

View File

@ -0,0 +1,75 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Copyright 2020-2022 Chinmay Dalal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local lib = require 'rainbow-delimiters.lib'
---Disable rainbow delimiters for a given buffer.
---@param bufnr integer Buffer number, zero for current buffer.
local function disable(bufnr)
if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
lib.detach(bufnr)
lib.buffers[bufnr] = false
end
---Enable rainbow delimiters for a given buffer.
---@param bufnr integer Buffer number, zero for current buffer.
local function enable(bufnr)
if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
lib.buffers[bufnr] = nil
lib.attach(bufnr)
end
---Toggle rainbow delimiters for a given buffer.
---@param bufnr integer Buffer number, zero for current buffer.
local function toggle(bufnr)
if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
if lib.buffers[bufnr] then
disable(bufnr)
else
enable(bufnr)
end
end
---Check if rainbow delimiters are enabled for a given buffer.
---@param bufnr integer Buffer number, zero for current buffer.
---@return boolean # Whether or not rainbow delimiters is enabled
local function is_enabled(bufnr)
if not bufnr or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
return lib.buffers[bufnr] ~= nil and lib.buffers[bufnr] ~= false
end
---Public API for use in writing strategies or other custom code.
local M = {
hlgroup_at = lib.hlgroup_at,
---Available default highlight strategies
strategy = {
---Global highlighting strategy
['global'] = require 'rainbow-delimiters.strategy.global',
---Local highlighting strategy
['local'] = require 'rainbow-delimiters.strategy.local',
---Empty highlighting strategy for testing
['noop'] = require 'rainbow-delimiters.strategy.no-op',
},
enable = enable,
disable = disable,
toggle = toggle,
is_enabled = is_enabled,
}
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,311 @@
---@meta
--# Utility Library #--
---Strategy to use for highlighting with rainbow-delimiters
---Must implement `on_attach`, `on_detach` and `on_reset`
---@class rainbow_delimiters.strategy
---`on_attach`: setup the highlighting on attach
---@field on_attach fun(bufnr: integer, settings: rainbow_delimiters.buffer_settings)
---`on_detach`: remove any unneccesary remaining setup on detach
---@field on_detach fun(bufnr: integer)
---`on_reset`: update the highlighting on reset
---@field on_reset fun(bufnr: integer, settings: rainbow_delimiters.buffer_settings)
---@class (exact) rainbow_delimiters.buffer_settings
---@field strategy rainbow_delimiters.strategy
---@field parser LanguageTree
---@field lang string
--# Config #--
---Configuration table for rainbow-delimiters
---@class (exact) rainbow_delimiters.config
---Strategy to use for highlighting
---@field strategy rainbow_delimiters.config.strategies?
---Query to use for highlighting
---@field query rainbow_delimiters.config.queries?
---Highlight priority of rainbow delimiters
---@field priority rainbow_delimiters.config.priorities?
---Highlight colors
---@field highlight string[]?
---Whitelist for languages to highlight
---@field whitelist rainbow_delimiters.language[]?
---Blacklist for languages not to highlight
---@field blacklist rainbow_delimiters.language[]?
---Logging with log file and log level
---@field log rainbow_delimiters.logging?
---@class rainbow_delimiters.config.strategies
---@field [''] (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field astro (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field bash (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field c (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field c_sharp (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field clojure (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field commonlisp (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field cpp (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field css (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field cuda (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field cue (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field dart (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field elixir (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field elm (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field fennel (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field fish (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field go (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field haskell (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field hcl (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field html (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field janet_simple (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field java (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field javascript (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field json (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field json5 (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field jsonc (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field jsonnet (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field julia (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field kotlin (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field latex (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field lua (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field luadoc (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field make (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field markdown (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field nim (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field nix (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field perl (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field php (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field python (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field query (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field r (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field racket (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field rasi (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field regex (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field rst (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field ruby (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field rust (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field scheme (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field scss (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field sql (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field starlark (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field templ (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field terraform (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field toml (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field tsx (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field typescript (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field typst (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field verilog (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field vim (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field vimdoc (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field vue (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field yaml (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@field zig (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---User defined language, not part of rainbow_delimiters support
---@field [string] (rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?)?
---@class rainbow_delimiters.config.queries
---@field [''] (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field astro (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field bash (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field c (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field c_sharp (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field clojure (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field commonlisp (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field cpp (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field css (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field cuda (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field cue (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field dart (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field elixir (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field elm (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field fennel (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field fish (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field go (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field haskell (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field hcl (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field html (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field janet_simple (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field java (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field javascript (('rainbow-delimiters' | 'rainbow-parens' | 'rainbow-delimiters-react' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-parens' | 'rainbow-delimiters-react' | string))?
---@field json (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field json5 (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field jsonc (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field jsonnet (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field julia (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field kotlin (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field latex (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))?
---@field lua (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))?
---@field luadoc (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field make (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field markdown (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field nim (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field nix (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field perl (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field php (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field python (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field query (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))?
---@field r (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field racket (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field rasi (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field regex (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field rst (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field ruby (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field rust (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field scheme (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field scss (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field sql (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field starlark (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field templ (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field terraform (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field toml (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field tsx (('rainbow-delimiters' | 'rainbow-parens' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-parens' | string))?
---@field typescript (('rainbow-delimiters' | 'rainbow-parens' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-parens' | string))?
---@field typst (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field verilog (('rainbow-delimiters' | 'rainbow-blocks' | string) | fun(bufnr: integer): ('rainbow-delimiters' | 'rainbow-blocks' | string))?
---@field vim (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field vimdoc (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field vue (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field yaml (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---@field zig (('rainbow-delimiters' | string) | fun(bufnr: integer): ('rainbow-delimiters' | string))?
---User defined language, not part of rainbow_delimiters support
---@field [string] (string | fun(bufnr: integer): string)?
---@class rainbow_delimiters.config.priorities
---@field [''] (integer | fun(bufnr: integer): integer)?
---@field astro (integer | fun(bufnr: integer): integer)?
---@field bash (integer | fun(bufnr: integer): integer)?
---@field c (integer | fun(bufnr: integer): integer)?
---@field c_sharp (integer | fun(bufnr: integer): integer)?
---@field clojure (integer | fun(bufnr: integer): integer)?
---@field commonlisp (integer | fun(bufnr: integer): integer)?
---@field cpp (integer | fun(bufnr: integer): integer)?
---@field css (integer | fun(bufnr: integer): integer)?
---@field cuda (integer | fun(bufnr: integer): integer)?
---@field cue (integer | fun(bufnr: integer): integer)?
---@field dart (integer | fun(bufnr: integer): integer)?
---@field elixir (integer | fun(bufnr: integer): integer)?
---@field elm (integer | fun(bufnr: integer): integer)?
---@field fennel (integer | fun(bufnr: integer): integer)?
---@field fish (integer | fun(bufnr: integer): integer)?
---@field go (integer | fun(bufnr: integer): integer)?
---@field haskell (integer | fun(bufnr: integer): integer)?
---@field hcl (integer | fun(bufnr: integer): integer)?
---@field html (integer | fun(bufnr: integer): integer)?
---@field janet_simple (integer | fun(bufnr: integer): integer)?
---@field java (integer | fun(bufnr: integer): integer)?
---@field javascript (integer | fun(bufnr: integer): integer)?
---@field json (integer | fun(bufnr: integer): integer)?
---@field json5 (integer | fun(bufnr: integer): integer)?
---@field jsonc (integer | fun(bufnr: integer): integer)?
---@field jsonnet (integer | fun(bufnr: integer): integer)?
---@field julia (integer | fun(bufnr: integer): integer)?
---@field kotlin (integer | fun(bufnr: integer): integer)?
---@field latex (integer | fun(bufnr: integer): integer)?
---@field lua (integer | fun(bufnr: integer): integer)?
---@field luadoc (integer | fun(bufnr: integer): integer)?
---@field make (integer | fun(bufnr: integer): integer)?
---@field markdown (integer | fun(bufnr: integer): integer)?
---@field nim (integer | fun(bufnr: integer): integer)?
---@field nix (integer | fun(bufnr: integer): integer)?
---@field perl (integer | fun(bufnr: integer): integer)?
---@field php (integer | fun(bufnr: integer): integer)?
---@field python (integer | fun(bufnr: integer): integer)?
---@field query (integer | fun(bufnr: integer): integer)?
---@field r (integer | fun(bufnr: integer): integer)?
---@field racket (integer | fun(bufnr: integer): integer)?
---@field rasi (integer | fun(bufnr: integer): integer)?
---@field regex (integer | fun(bufnr: integer): integer)?
---@field rst (integer | fun(bufnr: integer): integer)?
---@field ruby (integer | fun(bufnr: integer): integer)?
---@field rust (integer | fun(bufnr: integer): integer)?
---@field scheme (integer | fun(bufnr: integer): integer)?
---@field scss (integer | fun(bufnr: integer): integer)?
---@field sql (integer | fun(bufnr: integer): integer)?
---@field starlark (integer | fun(bufnr: integer): integer)?
---@field templ (integer | fun(bufnr: integer): integer)?
---@field terraform (integer | fun(bufnr: integer): integer)?
---@field toml (integer | fun(bufnr: integer): integer)?
---@field tsx (integer | fun(bufnr: integer): integer)?
---@field typescript (integer | fun(bufnr: integer): integer)?
---@field typst (integer | fun(bufnr: integer): integer)?
---@field verilog (integer | fun(bufnr: integer): integer)?
---@field vim (integer | fun(bufnr: integer): integer)?
---@field vimdoc (integer | fun(bufnr: integer): integer)?
---@field vue (integer | fun(bufnr: integer): integer)?
---@field yaml (integer | fun(bufnr: integer): integer)?
---@field zig (integer | fun(bufnr: integer): integer)?
---User defined language, not part of rainbow_delimiters support
---@field [string] (integer | fun(bufnr: integer): integer)?
---@alias rainbow_delimiters.language
---| 'astro'
---| 'bash'
---| 'c'
---| 'c_sharp'
---| 'clojure'
---| 'commonlisp'
---| 'cpp'
---| 'css'
---| 'cuda'
---| 'cue'
---| 'dart'
---| 'elixir'
---| 'elm'
---| 'fennel'
---| 'fish'
---| 'go'
---| 'haskell'
---| 'hcl'
---| 'html'
---| 'janet_simple'
---| 'java'
---| 'javascript'
---| 'json'
---| 'json5'
---| 'jsonc'
---| 'jsonnet'
---| 'julia'
---| 'kotlin'
---| 'latex'
---| 'lua'
---| 'luadoc'
---| 'make'
---| 'markdown'
---| 'nim'
---| 'nix'
---| 'perl'
---| 'php'
---| 'python'
---| 'query'
---| 'r'
---| 'racket'
---| 'rasi'
---| 'regex'
---| 'rst'
---| 'ruby'
---| 'rust'
---| 'scheme'
---| 'scss'
---| 'sql'
---| 'starlark'
---| 'templ'
---| 'terraform'
---| 'toml'
---| 'tsx'
---| 'typescript'
---| 'typst'
---| 'verilog'
---| 'vim'
---| 'vimdoc'
---| 'vue'
---| 'yaml'
---| 'zig'
---User defined language, not part of rainbow_delimiters support
---| string
---@class (exact) rainbow_delimiters.logging
---@field file ('rainbow_delimiters.log' | string)?
---@field level integer

View File

@ -0,0 +1,107 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local function get_nested(table, index, key)
local result
-- 1. User setting for file type
if vim.g.rainbow_delimiters and vim.g.rainbow_delimiters[index] then
result = rawget(vim.g.rainbow_delimiters[index], key)
end
if result ~= nil then return result end
-- 2. User setting for fallback
if vim.g.rainbow_delimiters and vim.g.rainbow_delimiters[index] then
result = rawget(vim.g.rainbow_delimiters[index], '')
end
if result ~= nil then return result end
-- 3. Default setting
result = rawget(table, key)
if result ~= nil then return result end
result = require('rainbow-delimiters.default')[index][key]
return result
end
---Plugin settings lookup table. This table is only used for looking up
---values. Set `g:rainbow_delimiters` to change the values.
local M = {
query = setmetatable({}, {
__index = function(table, key)
return get_nested(table, 'query', key)
end
}),
strategy = setmetatable({}, {
__index = function(table, key)
return get_nested(table, 'strategy', key)
end
}),
priority = setmetatable({}, {
__index = function(table, key)
return get_nested(table, 'priority', key)
end
}),
log = setmetatable({}, {
__index = function(table, key)
return get_nested(table, 'log', key)
end
}),
enabled_for = function(lang)
local conf = vim.g.rainbow_delimiters
if not conf then return true end
local whitelist = conf.whitelist
local blacklist = conf.blacklist
if whitelist then
for _, v in ipairs(whitelist) do
if v == lang then return true end
end
return false
end
if blacklist then
for _, v in ipairs(blacklist) do
if v == lang then return false end
end
return true
end
return true
end
}
setmetatable(M, {
__index = function(table, key)
if key == 'highlight' then
local highlight
if vim.g.rainbow_delimiters then
highlight = rawget(vim.g.rainbow_delimiters, 'highlight')
end
if highlight and #highlight > 0 then return highlight end
highlight = require('rainbow-delimiters.default').highlight
return highlight
end
return rawget(table, key)
end,
})
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,74 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---Default plugin configuration.
---@type rainbow_delimiters.config
local M = {
---Query names by file type
query = {
[''] = 'rainbow-delimiters',
javascript = 'rainbow-delimiters-react'
},
---Highlight strategies by file type
strategy = {
[''] = require 'rainbow-delimiters.strategy.global',
},
priority = {
[''] = 110,
},
---Event logging settings
log = {
---Log level of the module, see `:h log_levels`.
level = vim.log.levels.WARN,
---File name of the log file
file = vim.fn.stdpath('log') .. '/rainbow-delimiters.log',
},
-- Highlight groups in order of display
highlight = {
-- The colours are intentionally not in the usual order to make
-- the contrast between them stronger
'RainbowDelimiterRed',
'RainbowDelimiterYellow',
'RainbowDelimiterBlue',
'RainbowDelimiterOrange',
'RainbowDelimiterGreen',
'RainbowDelimiterViolet',
'RainbowDelimiterCyan',
}
}
---If the key does not exist in the table fall back on the empty string as
---key.
local function get_with_fallback(table, key)
return rawget(table, key) or rawget(table, '')
end
setmetatable(M.query, {
__index = get_with_fallback,
})
setmetatable(M.strategy, {
__index = get_with_fallback,
})
setmetatable(M.priority, {
__index = get_with_fallback,
})
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,284 @@
---Health check module.
local M = {}
-- In Neovim 0.10 the following functions have been renamed
local start = vim.health.start or vim.health.report_start
local ok = vim.health.ok or vim.health.report_ok
local info = vim.health.info or vim.health.report_info
local warn = vim.health.warn or vim.health.report_warn
local error = vim.health.error or vim.health.report_error
local filewritable = vim.fn.filewritable
local fnamemodify = vim.fn.fnamemodify
local STRATEGY_ADVICE = "See :h rb-delimiters-strategy for the strategy protocol"
local QUERY_ADVICE = "See :h rb-delimiters-query for included standard queries."
local HLGROUP_ADVICE = "Consecutive highlight groups make delimiter levels indistinguishable, use another highlight group."
local SCHEMA_ADVICE = "This might be a typo, see :h g:rainbow_delimiters for valid entries."
---Specification of valid options. The key is the name of an option, the value
---is either true (no further validation) or a table containing the nested
---schema for the option
local schema = {
strategy = true,
query = true,
highlight = true,
priority = true,
blacklist = true,
whitelist = true,
log = {level = true, file = true},
}
---Check whether there is a parser installed for the given language.
---@param lang string
---@return boolean
local function check_parser_installed(lang)
local success = pcall(vim.treesitter.language.inspect, lang)
return success
end
---Check whether the strategy is a valid strategy.
---
---This is not a 100% reliable check; we only test the type of the argument and
---whether the table has the correct fields, but not what the callback
---functions actually do.
---@param strategy rainbow_delimiters.strategy | fun(bufnr: integer): rainbow_delimiters.strategy?
---@return boolean
local function check_strategy(strategy)
if type(strategy) == 'function' then
local finfo = debug.getinfo(strategy)
return finfo.nparams == 0 or finfo.nparams == 1
end
if type(strategy) == 'table' then
if type(strategy.on_attach) ~= 'function' then
return false
end
if type(strategy.on_detach) ~= 'function' then
return false
end
if type(strategy.on_reset) ~= 'function' then
return false
end
return true
end
return false
end
---Check whether the given query is defined for the given language.
---@param lang string
---@param name string | fun(bufnr: integer): string
---@return boolean
local function check_query(lang, name)
if type(name) == 'function' then
local finfo = debug.getinfo(name)
return finfo.nparams == 0 or finfo.nparams == 1
end
if type(name) == 'string' then
local query = vim.treesitter.query.get(lang, name)
return query ~= nil
end
return false
end
---Check whether the given priority is defined for the given language.
---@param priority integer | fun(bufnr: integer): integer
---@return boolean
local function check_priority(priority)
if type(priority) == 'function' then
local finfo = debug.getinfo(priority)
return finfo.nparams == 0 or finfo.nparams == 1
end
if type(priority) == 'number' then
return true
end
return false
end
---@param settings rainbow_delimiters.logging
local function check_logging(settings)
local level, file = settings.level, settings.file
if level then
-- Note: although the log level is an integer, Lua 5.1 only has the
-- number type
if type(level) ~= 'number' then
error('The log level must be a number', 'See :h vim.log.levels for valid log levels.')
else
ok('Valid log level.')
end
end
if file then
if type(file) ~= 'string' then
error('The log file path must be a string')
elseif filewritable(file) == 0 then
if filewritable(fnamemodify(file, ':h')) == 2 then
ok('Valid location for log file.')
else
local msg = string.format("Cannot write to file '%s'", file)
error(msg)
end
else
ok('Valid log file.')
end
end
local advice = "This might be a typo, see :h rb-delimiters-logging for valid entries."
for option in pairs(settings) do
if not schema.log[option] then
local msg = string.format("Unknown logging option '%s' in settings", option)
warn(msg, advice)
end
end
end
function M.check()
local settings = vim.g.rainbow_delimiters --[[@as rainbow_delimiters.config]]
if not settings then
return
info("No custom configuration; see :h rb-delimiters-setup for information.")
end
local whitelist = settings.whitelist
if whitelist then
start 'Parsers for whitelisted languages'
for _, lang in ipairs(whitelist) do
local success = check_parser_installed(lang)
if success then
local msg = string.format("Parser installed for '%s'", lang)
ok(msg)
else
local msg = string.format("No parser installed for '%s'", lang)
warn(msg)
end
end
end
local strategies = settings.strategy
if strategies then
start 'Custom strategies'
for lang, strategy in pairs(strategies) do
local has_strategy = check_strategy(strategy)
if lang == '' then
if has_strategy then
local msg = 'Valid custom default strategy.'
ok(msg)
else
local msg = 'Invalid custom default strategy.'
error(msg, STRATEGY_ADVICE)
end
else
local has_parser = check_parser_installed(lang)
if not has_parser then
local msg = string.format("No parser installed for '%s'", lang)
error(msg)
end
if not has_strategy then
local msg = string.format("Invalid custom strategy for '%s'", lang)
error(msg, STRATEGY_ADVICE)
end
if has_parser and has_strategy then
local msg = string.format("Valid custom strategy for '%s'.", lang)
ok(msg)
end
end
end
end
local queries = settings.query
if queries then
start 'Custom queries'
for lang, query in pairs(queries) do
if lang == '' then
if query ~= 'rainbow-delimiters' then
local msg = string.format(
"User-defined default query '%s'\
If you meant 'rainbow-delimiters' check for typos",
query
)
ok(msg)
else
local msg = "Valid custom default query"
ok(msg)
end
else
local has_lang = check_parser_installed(lang)
local has_query = check_query(lang, query)
if not has_lang then
local msg = string.format("No parser installed for '%s'.", lang)
warn(msg)
end
if not has_query then
local msg = string.format("No query named '%s' for '%s' found.", query, lang)
warn(msg, QUERY_ADVICE)
end
if has_lang and has_query then
local msg = string.format("Valid custom query for '%s'", lang)
ok(msg)
end
end
end
end
local priorities = settings.priority
if priorities then
start 'Custom priorities'
for lang, priority in pairs(priorities) do
local is_valid_prirority = check_priority(priority)
if lang == '' then
if is_valid_prirority then
local msg = "Valid custom default priority"
ok(msg)
else
local msg = "Invalid custom default priority"
error(msg)
end
else
if is_valid_prirority then
local msg = string.format("Valid custom priority for '%s'", lang)
ok(msg)
else
local msg = string.format("Invalid custom priority for '%s'", lang)
error(msg)
end
end
end
end
local hlgroups = settings.highlight
if hlgroups then
start 'Custom highlight groups'
local previous
for _, hlgroup in ipairs(hlgroups) do
local has_hlgroup = vim.fn.hlID(hlgroup) ~= 0
if has_hlgroup then
ok(string.format("Highlight group '%s' defined.", hlgroup))
else
error(string.format("Highlight group '%s' not defined.", hlgroup))
end
if previous and hlgroup == previous then
local msg = string.format("Consecutive highlight group '%s'", hlgroup)
warn(msg, HLGROUP_ADVICE)
end
previous = hlgroup
end
end
local logging = settings.log
if logging then
start 'Logging settings'
check_logging(logging)
end
for option in pairs(settings) do
if not schema[option] then
local msg = string.format("Unknown option '%s' in settings", option)
warn(msg, SCHEMA_ADVICE)
end
end
end
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,248 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Copyright 2020-2022 Chinmay Dalal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local api = vim.api
local get_query = vim.treesitter.query.get
local get_parser = vim.treesitter.get_parser
local log = require 'rainbow-delimiters.log'
local config = require 'rainbow-delimiters.config'
local util = require 'rainbow-delimiters.util'
---[ Internal ]----------------------------------------------------------------
-- The following symbols should only be used internally. In particular, they
-- should not be used by strategies, or else our strategies are using
-- undocumented APIs.
---Private library of shared internal functions and variables.
local M = {}
M.enabled_for = config.enabled_for
---Per-language namespaces. This table instantiates namespaces on demand, i.e.
---a namespace won't exist until we first try to get it from the table.
M.nsids = setmetatable({}, {
__index = function(t, k)
local result = rawget(t, k)
if result == nil then
result = vim.api.nvim_create_namespace('')
rawset(t, k, result)
end
return result
end,
-- Note: this will only catch new indices, not assignment to an already
-- existing key
__newindex = function(_, _, _)
error('Table is immutable')
end
})
---Keeps track of attached buffers. The key is the buffer number and the value
---is a table of information about that buffer (e.g. language, strategy,
---query). This also makes sure we keep track of all parsers in active use to
---prevent them from being garbage-collected.
---@type table<integer, rainbow_delimiters.buffer_settings | false>
M.buffers = {}
---[ This stuff needs to be re-exported ]--------------------------------------
-- The following entries can be used in the public API as well.
---Fetches the query object for the given language from the settings. If a
---buffer number is given it will be used as the current buffer, otherwise the
---actual current buffer is used.
---
---@param lang string Name of the language to get the query for
---@param bufnr integer Use this buffer as the current buffer
---@return Query? query The query object
function M.get_query(lang, bufnr)
local name = config['query'][lang]
if type(name) == "function" then
name = name(bufnr)
end
local query = get_query(lang, name)
if not query then
log.debug('Query %s not found for %s', name, lang)
else
log.trace('Query %s found for %s', name, lang)
end
return query
end
---Apply highlighting to a single node.
---@param bufnr integer Buffer which contains the node
---@param lang string Language of the node (to group HL into namespaces)
---@param node table Node to highlight
---@param hlgroup string Name of the highlight group to apply.
function M.highlight(bufnr, lang, node, hlgroup)
-- range of the capture, zero-indexed
local startRow, startCol, endRow, endCol = node:range()
local start, finish = {startRow, startCol}, {endRow, endCol - 1}
local priority = config.priority[lang]
if type(priority) == "function" then
priority = priority(bufnr)
end
local opts = {
regtype = 'c',
inclusive = true,
priority = priority,
}
local nsid = M.nsids[lang]
if vim.api.nvim_buf_is_loaded(bufnr) then
vim.highlight.range(bufnr, nsid, hlgroup, start, finish, opts)
end
end
---Get the appropriate highlight group for the given level of nesting.
---@param i integer One-based index into the highlight groups
---@return string hlgroup Name of the highlight groups
function M.hlgroup_at(i)
local hlgroups = config.highlight
return hlgroups[(i - 1) % #hlgroups + 1]
end
---Clears the reserved Rainbow namespace.
---
---@param bufnr integer Number of the buffer for which to clear the namespace
---@param lang string
---@param line_start integer?
---@param line_end integer?
function M.clear_namespace(bufnr, lang, line_start, line_end)
local nsid = M.nsids[lang]
if vim.api.nvim_buf_is_valid(bufnr) then
vim.api.nvim_buf_clear_namespace(bufnr, nsid, line_start or 0, line_end or -1)
end
end
---Start rainbow highlighting for the given buffer
---@param bufnr integer
function M.attach(bufnr)
-- Rainbow delimiters was explicitly disabled for this buffer
if M.buffers[bufnr] == false then return end
local lang = vim.treesitter.language.get_lang(vim.bo[bufnr].ft)
if not lang then
log.trace('Cannot attach to buffer %d, no parser for %s', bufnr, lang)
return
end
log.trace('Attaching to buffer %d with language %s.', bufnr, lang)
local settings = M.buffers[bufnr]
if settings then
-- if M.buffers[bufnr].lang == lang then return end
-- TODO: If the language is the same reload the parser
if settings.lang == lang then
local parser = get_parser(bufnr, lang)
local strategy = settings.strategy
parser:invalidate(true)
parser:parse()
strategy.on_reset(bufnr, settings)
return
end
-- The file type of the buffer has changed, so we need to detach first
-- before we re-attach
M.detach(bufnr)
end
local parser
do
local success
success, parser = pcall(get_parser, bufnr, lang)
if not success then return end
end
local strategy
do
strategy = config.strategy[lang]
if type(strategy) == 'function' then
strategy = strategy(bufnr)
end
end
-- Intentionally abort; the user has explicitly disabled rainbow delimiters
-- for this buffer, usually by setting a strategy- or query function which
-- returned nil.
if not strategy then
log.warn('No strategy defined for %s', lang)
end
if not strategy or strategy == vim.NIL then return end
parser:register_cbs {
---@param bnr integer
on_detach = function(bnr)
if not M.buffers[bnr] then return end
M.detach(bufnr)
end,
---@param child LanguageTree
on_child_removed = function(child)
M.clear_namespace(bufnr, child:lang())
end,
}
settings = {
strategy = strategy,
parser = parser,
lang = lang
}
M.buffers[bufnr] = settings
-- For now we silently discard errors, but in the future we should log
-- them.
local success, error = pcall(strategy.on_attach, bufnr, settings)
if not success then
log.error('Error attaching strategy to buffer %d: %s', bufnr, error)
M.buffers[bufnr] = nil
end
end
---Start rainbow highlighting for the given buffer
---@param bufnr integer
function M.detach(bufnr)
log.trace('Detaching from buffer %d.', bufnr)
if not M.buffers[bufnr] then
return
end
local strategy = M.buffers[bufnr].strategy
local parser = M.buffers[bufnr].parser
-- Clear all the namespaces for each language
util.for_each_child(nil, parser:lang(), parser, function(_, lang)
M.clear_namespace(bufnr, lang)
end)
-- Finally release all resources the parser is holding on to
parser:destroy()
-- For now we silently discard errors, but in the future we should log
-- them.
local success, error = pcall(strategy.on_detach, bufnr)
if not success then
log.error('Error detaching strategy from buffer %d: %s', bufnr, error)
end
M.buffers[bufnr] = nil
end
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,103 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---Logger module for rainbow delimiters. Logs any message whose log level is
---equal to or greater than the log level of the module.
local M = {}
local date = os.date
local levels = vim.log.levels
local config = require 'rainbow-delimiters.config'
---Reverse lookup table; maps a log level to its text label
local level_str = {}
for key, value in pairs(levels) do
level_str[value] = key
end
---Dynamically determines the module from which the log function was called.
---If it was called from somewhere else return the name of the plugin.
---@return string
local function get_module()
local module = debug.getinfo(4, 'S').source:match('^.+rainbow%-delimiters/(.+).lua$')
if not module then
return ''
end
return module:gsub('/', '.')
end
---@param file file*
---@param level integer
---@param module string
---@param message any
---@param ... any
local function write_log(file, level, module, message, ...)
local msg
local timestamp = date('%FT%H:%M%z')
if type(message) == 'function' then
msg = message()
else
msg = string.format(message, ...)
end
file:write(string.format('%s %s %s %s\n', timestamp, level, module, msg))
end
---@param level integer
---@param message any
---@param ... any
local function log(level, message, ...)
if level < config.log.level then return end
local file = io.open(config.log.file, 'a+')
-- Intentional: Silently discard the log if the log file cannot be opened
if not file then return end
-- Wrap inside a pcall to make sure the file gets closed even if an error
-- occurs
pcall(write_log, file, level_str[level], get_module(), message, ...)
file:close()
-- Should I also print the message?
end
---Log an error message
function M.error(message, ...)
log(levels.ERROR, message, ...)
end
---Log a warning message
function M.warn(message, ...)
log(levels.WARN, message, ...)
end
---Log a tracing message
function M.debug(message, ...)
log(levels.DEBUG, message, ...)
end
---Log a tracing message
function M.trace(message, ...)
log(levels.TRACE, message, ...)
end
---Log an info message
function M.info(message, ...)
log(levels.INFO, message, ...)
end
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,17 @@
local M = {}
---Apply the given configuration to the rainbow-delimiter settings. Will
---overwrite existing settings.
---
---@param opts rainbow_delimiters.config Settings, same format as `vim.g.rainbow_delimiters`
function M.setup(opts)
vim.g.rainbow_delimiters = opts
end
-- Make it possible to call the module directly; for backwards compatibility
-- with a previous version of this module.
setmetatable(M, {__call = function(_t, opts) M.setup(opts) end})
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,124 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---Helper library for stack-like tables.
local M = {}
---@class (exact) Stack
---@field public size fun(self: Stack): integer
---@field public peek fun(self: Stack): any
---@field public push fun(self: Stack, item: any): Stack
---@field public pop fun(self: Stack): any
---@field public iter fun(self: Stack): ((fun(i: integer, item: any): integer?, any), Stack, integer)
---@field package content any[]
---The stack metatable.
local mt = {}
---The actual iterator implementation, hidden behind the iter-method.
---@param stack Stack
---@param i integer
---@return integer?
---@return any
local function iter_stack(stack, i)
if i <= 1 then return end
return i - 1, stack.content[i - 1]
end
---@param stack Stack
---@return string
local function stack_tostring(stack)
local items = {}
for _, item in ipairs(stack.content) do
items[#items + 1] = tostring(item)
end
return string.format('[%s]', table.concat(items, ', '))
end
---[ Methods ]-----------------------------------------------------------------
---Returns the current number of items in the stack.
---@param self Stack
---@return integer size Current size of the stack
local function size(self)
return #self.content
end
---Iterate through the content of the stack from top to bottom. Each iteration
---returns the current index (one-based, counting from the bottom) and the
---current item.
---@param self Stack The stack instance
---@return fun(i: integer, stack: Stack): integer?, any
---@return Stack
---@return integer
local function iter(self)
return iter_stack, self, self:size() + 1
end
---Add a new item to the top of the stack. Modifies the stack in-place.
---@param item any The item to push onto the stack
---@return Stack stack The stack.
local function push(self, item)
self.content[self:size() + 1] = item
return self
end
---Returns the topmost item of the stack without altering the stack.
---@return any top The top-most item.
local function peek(self)
local result = self.content[self:size()]
return result
end
---Returns the topmost item of the stack and removes it from the stack.
---@return any top The top-most item.
local function pop(self)
local n = self:size()
local result = self.content[n]
self.content[n] = nil
return result
end
---[ Public module interface ]-------------------------------------------------
---Instantiates a new stack containing the given items, or the empty stack if
---the argument is `nil`.
---@param items any[]? Array of items in order from bottom to top
---@return Stack stack The new stack instance
function M.new(items)
---@type Stack
local result = {
content = {},
size = size,
iter = iter,
push = push,
pop = pop,
peek = peek,
}
setmetatable(result, mt)
for _, item in ipairs(items or {}) do result:push(item) end
return result
end
---[ Metamethods ]-------------------------------------------------------------
mt.__tostring = stack_tostring
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,75 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---Strategy decorator which makes your delimiters change colours like Christmas
---lights. This module is meant as a joke and will not be loaded by default
---with the rest of the plugin.
local M = {}
local uv = vim.loop
local lib = require 'rainbow-delimiters.lib'
local original_hlgroup_at = lib.hlgroup_at
local counter = 0
---Wrapper around the original function which applies some offset to the index.
---@param i integer
---@return string hlgroup
local function patched_hlgroup_at(i)
return original_hlgroup_at(counter + i)
end
---Wraps the given strategy with a new strategy that switches colours like a
---chain of Christmas lights.
---@param strategy rainbow_delimiters.strategy The original strategy
---@param delay integer? Time between switches in milliseconds (default 500)
---@return rainbow_delimiters.strategy christmas_lights A new strategy object
function M.lights(strategy, delay)
delay = delay or 500
local timer = uv.new_timer()
---@param bufnr integer
---@param settings rainbow_delimiters.buffer_settings
local function on_attach(bufnr, settings)
local function blink()
counter = counter + 1
local function callback()
lib.hlgroup_at = patched_hlgroup_at
strategy.on_reset(bufnr, lib.buffers[bufnr])
lib.hlgroup_at = original_hlgroup_at
end
vim.schedule(callback)
end
timer:start(0, delay, blink)
strategy.on_attach(bufnr, settings)
end
---@param bufnr integer
local function on_detach(bufnr)
timer:stop()
strategy.on_detach(bufnr)
end
return {
strategy = strategy,
on_attach = on_attach,
on_detach = on_detach,
on_reset = strategy.on_reset,
}
end
return M

View File

@ -0,0 +1,300 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Copyright 2020-2022 Chinmay Dalal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local Stack = require 'rainbow-delimiters.stack'
local lib = require 'rainbow-delimiters.lib'
local util = require 'rainbow-delimiters.util'
local log = require 'rainbow-delimiters.log'
---Strategy which highlights the entire buffer.
local M = {}
---Changes are range objects and come in two variants: one with four entries and
---one with six entries. We only want the four-entry variant. See
---`:h TSNode:range()`
---@param change integer[]
---@return integer[]
local function normalize_change(change)
local result
if #change == 4 then
result = change
elseif #change == 6 then
result = {change[1], change[2], change[4], change[5]}
else
result = {}
end
return result
end
---@param bufnr integer
---@param lang string
---@param matches Stack
---@param level integer
local function highlight_matches(bufnr, lang, matches, level)
local hlgroup = lib.hlgroup_at(level)
for _, match in matches:iter() do
for _, delimiter in match.delimiter:iter() do lib.highlight(bufnr, lang, delimiter, hlgroup) end
highlight_matches(bufnr, lang, match.children, level + 1)
end
end
---Create a new empty match_record
---@return table
local function new_match_record()
return {
delimiter = Stack.new(),
children = Stack.new(),
}
end
---Update highlights for a range. Called every time text is changed.
---@param bufnr integer Buffer number
---@param changes table List of node ranges in which the changes occurred
---@param tree TSTree TS tree
---@param lang string Language
local function update_range(bufnr, changes, tree, lang)
log.debug('Updated range with changes %s', vim.inspect(changes))
if not lib.enabled_for(lang) then return end
if vim.fn.pumvisible() ~= 0 or not lang then return end
local query = lib.get_query(lang, bufnr)
if not query then return end
local matches = Stack.new()
for _, change in ipairs(changes) do
-- This is the match record, it lists all the relevant nodes from
-- each match.
---@type table?
local match_record
local root_node = tree:root()
local start_row, end_row = change[1], change[3] + 1
lib.clear_namespace(bufnr, lang, start_row, end_row)
for qid, node, _ in query:iter_captures(root_node, bufnr, start_row, end_row) do
local name = query.captures[qid]
-- check for 'delimiter' first, since that should be the most
-- common name
if name == 'delimiter' and match_record then
match_record.delimiter:push(node)
elseif name == 'container' and not match_record then
match_record = new_match_record()
elseif name == 'container' then
-- temporarily push the match_record to matches to be retrieved
-- later, since we haven't closed it yet
matches:push(match_record)
match_record = new_match_record()
-- since we didn't close the previous match_record, it must
-- mean that the current match_record has it as an ancestor
match_record.has_ancestor = true
elseif name == 'sentinel' and match_record then
-- if we see the sentinel, then we are done with the current
-- container
if match_record.has_ancestor then
local prev_match_record = matches:pop()
if prev_match_record then
-- since we have an ancestor, it has to be the last
-- element of the stack
prev_match_record.children:push(match_record)
match_record = prev_match_record
else
-- since match_record.has_ancestor was true, we shouldn't
-- be able to get to here unless something went wrong
-- with the queries or treesitter itself
log.error([[You are missing a @container,
which should be impossible!
Please double check the queries.]])
end
else
-- if match_record doesn't have an ancestor, the sentinel
-- means that we are done with it
matches:push(match_record)
match_record = nil
end
elseif (name == 'delimiter' or name == 'sentinel') and not match_record then
log.error([[You query got the capture name %s.
But it didn't come with a container, which should be impossible!
Please double check your queries.]], name)
end -- do nothing with other capture names
end
if match_record then
-- we might have a dangling match_record, so we push it back into
-- matches
-- this should only happen when the query is on a proper subset
-- of the full tree (usually just one line)
matches:push(match_record)
end
end
-- when we capture on a row and not the full tree, we get the previous
-- containers (on earlier rows) included in the above, but not the
-- delimiters and sentinels from them, so we push them up as long as
-- we know they have an ancestor
local last_match = matches:pop()
while last_match and last_match.has_ancestor do
local prev_match = matches:pop()
if prev_match then
prev_match.children:push(last_match)
else
log.error('You are in what should be an unreachable position.')
end
last_match = prev_match
end
matches:push(last_match)
highlight_matches(bufnr, lang, matches, 1)
end
---Update highlights for every tree in given buffer.
---@param bufnr integer # Buffer number
---@param parser LanguageTree
local function full_update(bufnr, parser)
log.debug('Performing full updated on buffer %d', bufnr)
local function callback(tree, sub_parser)
local changes = {{tree:root():range()}}
update_range(bufnr, changes, tree, sub_parser:lang())
end
parser:for_each_tree(callback)
end
---Sets up all the callbacks and performs an initial highlighting
---@param bufnr integer # Buffer number
---@param parser LanguageTree
---@param start_parent_lang string? # Parent language or nil
local function setup_parser(bufnr, parser, start_parent_lang)
log.debug('Setting up parser for buffer %d', bufnr)
util.for_each_child(start_parent_lang, parser:lang(), parser, function(p, lang, parent_lang)
log.debug("Setting up parser for '%s' in buffer %d", lang, bufnr)
-- Skip languages which are not supported, otherwise we get a
-- nil-reference error
if not lib.get_query(lang, bufnr) then return end
p:register_cbs {
---@param changes table
---@param tree TSTree
on_changedtree = function(changes, tree)
log.trace('Changed tree in buffer %d with languages %s', bufnr, lang)
-- HACK: As of Neovim v0.9.1 there is no way of unregistering a
-- callback, so we use this check to abort
if not lib.buffers[bufnr] then return end
-- HACK: changes can accidentally overwrite highlighting in injected code
-- blocks.
if not parent_lang then
-- If we have no parent language, then we use changes, otherwise we use the
-- whole tree's range.
-- Normalize the changes object if we have no parent language (the one we
-- get from on_changedtree)
changes = vim.tbl_map(normalize_change, changes)
elseif parent_lang ~= lang and changes[1] then
-- We have a parent language, so we are in an injected language code
-- block, thus we update all of the current code block
changes = {{tree:root():range()}}
else
-- some languages (like rust) use injections of the language itself for
-- certain functionality (e.g., macros in rust). For these the
-- highlighting will be updated by the non-injected language part of the
-- code.
changes = {}
end
-- If a line has been moved from another region it will still carry with it
-- the extmarks from the old region. We need to clear all extmarks which
-- do not belong to the current language
for _, change in ipairs(changes) do
for key, nsid in pairs(lib.nsids) do
if key ~= lang then
-- HACK: changes in the main language sometimes need to overwrite
-- highlighting on one more line
local line_end = change[3] + (parent_lang and 0 or 1)
vim.api.nvim_buf_clear_namespace(bufnr, nsid, change[1], line_end)
end
end
end
-- only update highlighting if we have changes
if changes[1] then
update_range(bufnr, changes, tree, lang)
end
-- HACK: Since we update the whole tree when we have a parent
-- language, we need to make sure to then update all children
-- too, even if there is no change in them. This shouldn't
-- affect performance, since it only affects code nested at
-- least 2 injection languages deep.
if parent_lang then
local children = p:children()
for child_lang, child in pairs(children) do
if lang == child_lang then return end
child:for_each_tree(function(child_tree, child_p)
local child_changes = {{child_tree:root():range()}}
-- we don't need to remove old extmarks, since
-- the above code will handle that correctly
-- already, but we might have accidentally
-- removed extmarks that we need to set again
update_range(bufnr, child_changes, child_tree, child_p:lang())
end)
end
end
end,
-- New languages can be added into the text at some later time, e.g.
-- code snippets in Markdown
---@param child LanguageTree
on_child_added = function(child)
setup_parser(bufnr, child, lang)
end,
}
log.trace("Done with setting up parser for '%s' in buffer %d", lang, bufnr)
end)
full_update(bufnr, parser)
end
---on_attach implementation for the global strategy
---@param bufnr integer
---@param settings rainbow_delimiters.buffer_settings
function M.on_attach(bufnr, settings)
log.trace('global strategy on_attach')
local parser = settings.parser
setup_parser(bufnr, parser, nil)
end
---on_detach implementation for the global strategy
---@param _bufnr integer
function M.on_detach(_bufnr)
end
---on_reset implementation for the global strategy
---@param bufnr integer
---@param settings rainbow_delimiters.buffer_settings
function M.on_reset(bufnr, settings)
log.trace('global strategy on_reset')
full_update(bufnr, settings.parser)
end
return M --[[@as rainbow_delimiters.strategy]]
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,338 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local Stack = require 'rainbow-delimiters.stack'
local lib = require 'rainbow-delimiters.lib'
local log = require 'rainbow-delimiters.log'
local util = require 'rainbow-delimiters.util'
local api = vim.api
local ts = vim.treesitter
---Highlight strategy which highlights the sub-tree of the buffer which
---contains the cursor. Re-computes -highlights when the buffer contents change
---or when the cursor is moved.
local M = {}
-- Implementation note: This strategy uses a two-step process: on every change
-- to the document tree we compute the match tree and cache it, then when the
-- cursor moves we use the cached match tree and the current cursor position to
-- decide which matches to highlight.
--
-- The document tree changes rarely, so there is no need to re-compute the
-- match tree every time the cursor moves.
---Cache of match trees, maps a buffer number to its match tree. We compute
---the match tree on every change, so that when the cursor moves without
---changing the tree we don't need to re-compute it.
---
---Each match tree maps a language and TS Tree to the corresponding match tree.
---We need TS Tree because there might be multiple trees per buffer, such as a
---Markdown buffer which contains multiple code blocks.
local match_trees = {}
---Reusable autogroup for events in this strategy.
---@type integer
local augroup = api.nvim_create_augroup('TSRainbowLocalCursor', {})
---Highlights a single match with the given highlight group
---@param bufnr integer
---@param lang string
---@param match table
---@param hlgroup string
local function highlight_match(bufnr, lang, match, hlgroup)
for _, delimiter in match.delimiter:iter() do lib.highlight(bufnr, lang, delimiter, hlgroup) end
end
---Highlights all matches and their children on the stack of matches. All
---matches must be on the same level of the match tree.
---
---@param bufnr integer Number of the buffer
---@param matches Stack Stack of matches
---@param level integer Level of the matches
local function highlight_matches(bufnr, lang, matches, level)
local hlgroup = lib.hlgroup_at(level)
for _, match in matches:iter() do
highlight_match(bufnr, lang, match, hlgroup)
highlight_matches(bufnr, lang, match.children, level + 1)
end
end
---Finds a match (and its level) in the match tree whose container node is the
---given container node.
---@param matches Stack
---@param container TSNode
---@param level integer
---@return table
---@return integer
---If no match is found, return nil.
---@overload fun(matches: Stack, container: TSNode, level: integer)
local function find_container(matches, container, level)
for _, match in matches:iter() do
if match.container == container then return match, level end
local result, final_level = find_container(match.children, container, level + 1)
if result then return result, final_level end
end
end
--- Create a new empty match_record with an optionally set container
---@param container TSNode
---@return table
local function new_match_record(container)
return {
container = container,
delimiter = Stack.new(),
children = Stack.new(),
}
end
---Assembles the match tree, usually called after the document tree has
---changed.
---@param bufnr integer Buffer number
---@param changes table List of node ranges in which the changes occurred
---@param tree TSTree TS tree
---@param lang string Language
---@return Stack?
local function build_match_tree(bufnr, changes, tree, lang)
if not lib.enabled_for(lang) then return end
local query = lib.get_query(lang, bufnr)
if not query then return end
local matches = Stack.new()
for _, change in ipairs(changes) do
-- This is the match record, it lists all the relevant nodes from
-- each match.
---@type table?
local match_record
local root_node = tree:root()
local start_row, end_row = change[1], change[3] + 1
lib.clear_namespace(bufnr, lang, start_row, end_row)
for qid, node, _ in query:iter_captures(root_node, bufnr, start_row, end_row) do
local name = query.captures[qid]
-- check for 'delimiter' first, since that should be the most
-- common name
if name == 'delimiter' and match_record then
match_record.delimiter:push(node)
elseif name == 'container' and not match_record then
match_record = new_match_record(node)
elseif name == 'container' then
local prev_match_record = match_record
-- temporarily push the match_record to matches to be retrieved
-- later, since we haven't closed it yet
matches:push(match_record)
match_record = new_match_record(node)
-- since we didn't close the previous match_record, it must
-- mean that the current match_record has it as an ancestor
match_record.ancestor = prev_match_record
elseif name == 'sentinel' and match_record then
-- if we see the sentinel, then we are done with the current
-- container
if match_record.ancestor then
local prev_match_record = matches:pop()
if prev_match_record then
-- since we have an ancestor, it has to be the last
-- element of the stack
prev_match_record.children:push(match_record)
match_record = prev_match_record
else
-- since match_record.has_ancestor was true, we shouldn't
-- be able to get to here unless something went wrong
-- with the queries or treesitter itself
log.error([[You are missing a @container,
which should be impossible!
Please double check the queries.]])
end
else
-- if match_record doesn't have an ancestor, the sentinel
-- means that we are done with it
matches:push(match_record)
match_record = nil
end
elseif (name == 'delimiter' or name == 'sentinel') and not match_record then
log.error([[You query got the capture name: %s.
But it didn't come with a container, which should be impossible!
Please double check your queries.]], name)
end -- do nothing with other capture names
end
end
return matches
end
---@param bufnr integer
---@param tree TSTree
---@param lang string
local function update_local(bufnr, tree, lang)
if not lib.enabled_for(lang) then return end
local query = lib.get_query(lang, bufnr)
if not query then return end
-- Find the lowest container node which contains the cursor
local cursor_container
do
local curpos = api.nvim_win_get_cursor(0)
-- The order of traversal guarantees that the first match which
-- contains the cursor is also the lowest one.
for _, match in query:iter_matches(tree:root(), bufnr, 0, -1) do
if cursor_container then break end
for id, node in pairs(match) do
local name = query.captures[id]
if name == 'container' and ts.is_in_node_range(node, curpos[1] - 1, curpos[2]) then
cursor_container = node
break
end
end
end
end
if not cursor_container then return end
local matches_lang = match_trees[bufnr][lang]
if not matches_lang then
log.debug("Did not build any matches Stack for language '%s'", lang)
return
end
local matches = matches_lang[tree]
if not matches then
-- Note: vim.inspect(tree:root():range()) errors, so we need
-- to make it into a table instead of a list of numbers
log.debug("Did not build any matches Stack for tree '%s'", vim.inspect({tree:root():range()}))
return
end
-- Find the correct container in the match tree
local cursor_match, level = find_container(matches, cursor_container, 1)
if not cursor_match then return end
-- Highlight the container match and everything below
highlight_matches(bufnr, lang, Stack.new {cursor_match}, level)
-- Starting with the cursor match travel up and highlight every ancestor as
-- well
local ancestor = cursor_match.ancestor
level = level - 1
while ancestor do
highlight_match(bufnr, lang, ancestor, lib.hlgroup_at(level))
ancestor, level = ancestor.ancestor, level - 1
end
end
---Callback function to re-highlight the buffer according to the current cursor
---position.
---@param bufnr integer
---@param parser LanguageTree
local function local_rainbow(bufnr, parser)
parser:for_each_tree(function(tree, sub_parser)
update_local(bufnr, tree, sub_parser:lang())
end)
end
---Sets up all the callbacks and performs an initial highlighting
---@param bufnr integer # Buffer number
---@param parser LanguageTree
local function setup_parser(bufnr, parser)
log.debug('Setting up parser for buffer %d', bufnr)
util.for_each_child(nil, parser:lang(), parser, function(p, lang, _parent_lang)
log.debug("Setting up parser for '%s' in buffer %d", lang, bufnr)
-- Skip languages which are not supported, otherwise we get a
-- nil-reference error
if not lib.get_query(lang, bufnr) then return end
p:register_cbs {
---@param _changes table
---@param tree TSTree
on_changedtree = function(_changes, tree)
-- HACK: As of Neovim v0.9.1 there is no way of unregistering a
-- callback, so we use this check to abort
if not lib.buffers[bufnr] then return end
if vim.fn.pumvisible() ~= 0 then return end
-- Ideally we would only rebuild the parts of the tree that have changed,
-- but this doesn't work, so we will rebuild the entire tree
-- instead.
local fake_changes = {
{tree:root():range()}
}
match_trees[bufnr][lang] = match_trees[bufnr][lang] or {}
match_trees[bufnr][lang][tree] = build_match_tree(bufnr, fake_changes, tree, lang)
-- Re-highlight after the change
local_rainbow(bufnr, p)
end,
-- New languages can be added into the text at some later time, e.g.
-- code snippets in Markdown
---@param child LanguageTree
on_child_added = function(child)
setup_parser(bufnr, child)
end,
}
log.trace("Done with setting up parser for '%s' in buffer %d", lang, bufnr)
end)
end
---on_attach implementation for the local strategy
---@param bufnr integer
---@param settings rainbow_delimiters.buffer_settings
function M.on_attach(bufnr, settings)
local parser = settings.parser
setup_parser(bufnr, parser)
api.nvim_create_autocmd('CursorMoved', {
group = augroup,
buffer = bufnr,
callback = function(args)
util.for_each_child(nil, parser:lang(), parser, function(_, lang, _)
lib.clear_namespace(bufnr, lang)
end)
local_rainbow(args.buf, parser)
end
})
-- Build up the initial match tree
match_trees[bufnr] = {}
parser:for_each_tree(function(tree, sub_parser)
local sub_lang = sub_parser:lang()
local changes = {
{tree:root():range()}
}
match_trees[bufnr][sub_lang] = match_trees[bufnr][sub_lang] or {}
match_trees[bufnr][sub_lang][tree] = build_match_tree(bufnr, changes, tree, sub_lang)
end)
local_rainbow(bufnr, parser)
end
---on_detach implementation for the local strategy
---@param bufnr integer
function M.on_detach(bufnr)
-- Uninstall autocommand and delete cached match tree
api.nvim_clear_autocmds {
buffer = bufnr,
group = augroup,
}
match_trees[bufnr] = nil
end
---on_reset implementation for the local strategy
---@param bufnr integer
---@param settings rainbow_delimiters.buffer_settings
function M.on_reset(bufnr, settings)
local parser = settings.parser
local_rainbow(bufnr, parser)
end
return M --[[@as rainbow_delimiters.strategy]]
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,39 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---A dummy strategy which does nothing; can be used in testing.
local M = {}
---on_attach implementation for the noop strategy
---@param _bufnr integer
---@param _settings rainbow_delimiters.buffer_settings
M.on_attach = function(_bufnr, _settings)
end
---on_detach implementation for the noop strategy
---@param _bufnr integer
M.on_detach = function(_bufnr)
end
---on_reset implementation for the noop strategy
---@param _bufnr integer
---@param _settings rainbow_delimiters.buffer_settings
M.on_reset = function(_bufnr, _settings)
end
return M --[[@as rainbow_delimiters.strategy]]
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,51 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---A strategy decorator; the decorated strategy keeps track of all currently
---attached buffers. Thew new strategy has the following fields:
---
--- - strategy The wrapped strategy object
--- - buffers Table mapping of buffer number to buffer settings
--- - attachments Number of currently active attachments
---
---@param strategy table
local function track(strategy)
local buffers = {}
local attachments = {0} -- Table because I want to pass it by reference
return {
strategy = strategy,
buffers = buffers,
attachments = attachments,
on_attach = function(bufnr, settings, ...)
buffers[bufnr] = settings
attachments[1] = attachments[1] + 1
strategy.on_attach(bufnr, settings, ...)
end,
on_detach = function(bufnr, ...)
buffers[bufnr] = nil
attachments[1] = attachments[1] - 1
strategy.on_detach(bufnr, ...)
end,
on_reset = function(...)
strategy.on_reset(...)
end,
}
end
return track
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,42 @@
--[[
Copyright 2023 Alejandro "HiPhish" Sanchez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
---Internal helper functions. This module will probably be removed when I no
---longer need the helpers.
local M = {}
---Similar to the function `LanguageTree:for_each_child` which has been
---deprecated. Applies the thunk to the language tree and each of its
---descendants recursively.
---
---See also https://github.com/neovim/neovim/pull/25154 for a better
---replacement.
---@param parent_lang string? # Parent language or nil
---@param lang string
---@param language_tree LanguageTree
---@param thunk fun(p: LanguageTree, lang: string, parent_lang: string?)
function M.for_each_child(parent_lang, lang, language_tree, thunk)
thunk(language_tree, lang, parent_lang)
local children = language_tree:children()
for child_lang, child in pairs(children) do
M.for_each_child(lang, child_lang, child, thunk)
end
end
return M
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,40 @@
# SPDX-License-Identifier: Unlicense
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or distribute
# this software, either in source code form or as a compiled binary, for any
# purpose, commercial or non-commercial, and by any means.
#
# In jurisdictions that recognize copyright laws, the author or authors of
# this software dedicate any and all copyright interest in the software to
# the public domain. We make this dedication for the benefit of the public
# at large and to the detriment of our heirs and successors. We intend this
# dedication to be an overt act of relinquishment in perpetuity of all
# present and future rights to this software under copyright law.
#
# 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 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.
#
# For more information, please refer to <https://unlicense.org/>
.PHONY: check unit-test e2e-test clean
check: unit-test e2e-test
unit-test:
@./test/busted --run unit
e2e-test:
@./test/busted --run e2e
clean:
@rm -rf test/xdg/local/state/nvim/*
@rm -rf test/xdg/local/share/nvim/site/pack/testing/start/nvim-treesitter/parser/*
@# The symlink might have been left over from a failed test run
@rm -rf test/xdg/local/share/nvim/site/pack/self-*

View File

@ -0,0 +1,75 @@
-- Copyright 2023 Alejandro "HiPhish" Sanchez
-- Copyright 2020-2022 Chinmay Dalal
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
if vim.g.loaded_rainbow then
return
end
local api = vim.api
local set_hl = api.nvim_set_hl
local create_augroup = api.nvim_create_augroup
local create_autocmd = api.nvim_create_autocmd
local get_lang = vim.treesitter.language.get_lang
local config = require 'rainbow-delimiters.config'
local log = require 'rainbow-delimiters.log'
local lib = require 'rainbow-delimiters.lib'
--- [ DEFINE HIGHLIGHT GROUPS ]------------------------------------------------
local function define_hlgroups()
log.trace 'Define highlight groups'
set_hl(0, 'RainbowDelimiterRed' , {default = true, fg = '#cc241d', ctermfg= 'Red' })
set_hl(0, 'RainbowDelimiterOrange', {default = true, fg = '#d65d0e', ctermfg= 'White' })
set_hl(0, 'RainbowDelimiterYellow', {default = true, fg = '#d79921', ctermfg= 'Yellow' })
set_hl(0, 'RainbowDelimiterGreen' , {default = true, fg = '#689d6a', ctermfg= 'Green' })
set_hl(0, 'RainbowDelimiterCyan' , {default = true, fg = '#a89984', ctermfg= 'Cyan' })
set_hl(0, 'RainbowDelimiterBlue' , {default = true, fg = '#458588', ctermfg= 'Blue' })
set_hl(0, 'RainbowDelimiterViolet', {default = true, fg = '#b16286', ctermfg= 'Magenta'})
end
define_hlgroups()
--- [ SET UP AUTOCOMMANDS ]----------------------------------------------------
local hl_augroup = create_augroup('TSRainbowHighlight', {})
local rb_augroup = create_augroup('TSRainbowDelimits', {})
create_autocmd('ColorScheme', {
desc = 'Re-apply highlight group definitions when the colour scheme changes',
group = hl_augroup,
callback = define_hlgroups
})
create_autocmd('FileType', {
desc = 'Attach to a new buffer',
group = rb_augroup,
callback = function(args)
local lang = get_lang(args.match)
if not config.enabled_for(lang) then return end
lib.attach(args.buf)
end,
})
create_autocmd('BufUnload', {
desc = 'Detach from the current buffer',
group = rb_augroup,
callback = function(args) lib.detach(args.buf) end
})
vim.g.loaded_rainbow = true
-- vim:tw=79:ts=4:sw=4:noet:

View File

@ -0,0 +1,13 @@
(element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(interpolation
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,37 @@
(command_substitution
"$(" @delimiter
")" @delimiter @sentinel) @container
(expansion
"${" @delimiter
(":-" @delimiter)?
"}" @delimiter @sentinel) @container
;;; The double-bracket variant is a bashism
(test_command
["[[" "["] @delimiter
["]]" "]"] @delimiter @sentinel) @container
(subshell
"(" @delimiter
")" @delimiter @sentinel) @container
(array
"(" @delimiter
")" @delimiter @sentinel) @container
(function_definition
"(" @delimiter
")" @delimiter @sentinel) @container
(arithmetic_expansion
"$((" @delimiter
"))" @delimiter @sentinel) @container
(compound_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(subscript
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,67 @@
(parameter_list
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(compound_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(initializer_list
"{" @delimiter
"}" @delimiter @sentinel) @container
; This highlights the nested levels in an array differently
; although they are the same level in terms of the nesting
; of delimiters
(subscript_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(field_declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_declarator
"[" @delimiter
"]" @delimiter @sentinel) @container
(sizeof_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
; Comment out the following to not highlight type casts
(cast_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(enumerator_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(macro_type_specifier
"(" @delimiter
")" @delimiter @sentinel) @container
(preproc_params
"(" @delimiter
")" @delimiter @sentinel) @container
(compound_literal_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_declarator
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,143 @@
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parameter_list
"(" @delimiter
")" @delimiter @sentinel) @container
(if_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(foreach_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(while_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(do_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_type
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(attribute_argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(attribute_list
"[" @delimiter
"]" @delimiter @sentinel) @container
(switch_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(switch_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(switch_expression
"{" @delimiter
"}" @delimiter @sentinel) @container
(default_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(catch_declaration
"(" @delimiter
")" @delimiter @sentinel) @container
(catch_filter_clause
"(" @delimiter
")" @delimiter @sentinel) @container
(using_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(lock_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(cast_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(typeof_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(sizeof_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(checked_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(accessor_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(anonymous_object_creation_expression
"{" @delimiter
"}" @delimiter @sentinel) @container
(enum_member_declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(type_parameter_list
"<" @delimiter
">" @delimiter @sentinel) @container
(type_argument_list
"<" @delimiter
">" @delimiter @sentinel) @container
(initializer_expression
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_rank_specifier
"[" @delimiter
"]" @delimiter @sentinel) @container
(bracketed_argument_list
"[" @delimiter
"]" @delimiter @sentinel) @container
(implicit_array_creation_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(implicit_stackalloc_expression
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,19 @@
(list_lit
"(" @delimiter
")" @delimiter @sentinel) @container
(vec_lit
"[" @delimiter
"]" @delimiter @sentinel) @container
(map_lit
"{" @delimiter
"}" @delimiter @sentinel) @container
(set_lit
"{" @delimiter
"}" @delimiter @sentinel) @container
(anon_fn_lit
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,13 @@
(list_lit
"(" @delimiter
_*
")" @delimiter @sentinel) @container
(defun
"(" @delimiter
_*
")" @delimiter @sentinel) @container
(loop_macro
"(" @delimiter
")" @delimiter @sentinel ) @container

View File

@ -0,0 +1,61 @@
;;; NOTE: The C and C++ grammar have diverged, so I cannot include the C query.
(parameter_list
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(condition_clause
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(compound_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(cast_expression
"(" @delimiter
")" @delimiter @sentinel)
(initializer_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_declarator
"[" @delimiter
"]" @delimiter @sentinel) @container
(subscript_argument_list
"[" @delimiter
"]" @delimiter @sentinel) @container
(field_declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(template_parameter_list
"<" @delimiter
">" @delimiter @sentinel) @container
(initializer_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(template_argument_list
"<" @delimiter
">" @delimiter @sentinel) @container

View File

@ -0,0 +1,19 @@
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(parenthesized_query
"(" @delimiter
")" @delimiter @sentinel) @container
(feature_query
"(" @delimiter
")" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(attribute_selector
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,5 @@
; inherits: cpp
(kernel_call_syntax
"<<<" @delimiter
">>>" @delimiter @sentinel) @container

View File

@ -0,0 +1,31 @@
(struct_lit
"{" @delimiter
"}" @delimiter @sentinel) @container
(list_lit
"[" @delimiter
"]" @delimiter @sentinel) @container
(label
"[" @delimiter
"]" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(attribute
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(import_spec_list
"(" @delimiter
")" @delimiter @sentinel) @container
(interpolation
"\\(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,31 @@
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(class_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(formal_parameter_list
"(" @delimiter
")" @delimiter @sentinel) @container
(optional_formal_parameters
"{" @delimiter
"}" @delimiter @sentinel) @container
(list_literal
"[" @delimiter
"]" @delimiter @sentinel) @container
(set_or_map_literal
"{" @delimiter
"}" @delimiter @sentinel) @container
(type_arguments
"<" @delimiter
">" @delimiter @sentinel) @container

View File

@ -0,0 +1,34 @@
(call
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container)
(block
"(" @delimiter
")" @delimiter @sentinel) @container
(string
(interpolation
"#{" @delimiter
"}" @delimiter @sentinel) @container)
(tuple
"{" @delimiter
"}" @delimiter @sentinel) @container
(list
"[" @delimiter
"]" @delimiter @sentinel) @container
(access_call
"[" @delimiter
"]" @delimiter @sentinel) @container
(bitstring
"<<" @delimiter
">>" @delimiter @sentinel) @container
(map
"%" @delimiter
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,63 @@
(exposing_list
"(" @delimiter
")" @delimiter @sentinel) @container
(exposed_operator
"(" @delimiter
")" @delimiter @sentinel) @container
(exposed_union_constructors
"(" @delimiter
")" @delimiter @sentinel) @container
(_
"(" @delimiter
.
(type_expression)
.
")" @delimiter @sentinel
) @container
(_
"(" @delimiter
.
[(pattern) (union_pattern)]
.
")" @delimiter @sentinel
) @container
(record_expr
"{" @delimiter
"}" @delimiter @sentinel) @container
(record_type
"{" @delimiter
"}" @delimiter @sentinel) @container
(record_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(tuple_expr
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_type
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_pattern
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expr
"(" @delimiter
")" @delimiter @sentinel) @container
(list_expr
"[" @delimiter
"]" @delimiter @sentinel) @container
(list_pattern
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,3 @@
(_
open: ["(" "[" "{"] @delimiter
close: [")" "]" "}"] @delimiter @sentinel) @container

View File

@ -0,0 +1,15 @@
(command_substitution
"(" @delimiter
")" @delimiter @sentinel) @container
(concatenation
"[" @delimiter
"]" @delimiter @sentinel) @container
(list_element_access
"[" @delimiter
"]" @delimiter @sentinel) @container
(brace_expansion
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,79 @@
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(import_spec_list
"(" @delimiter
")" @delimiter @sentinel) @container
(var_declaration
"(" @delimiter
")" @delimiter @sentinel) @container
(const_declaration
"(" @delimiter
")" @delimiter @sentinel) @container
(type_assertion_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(field_declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parameter_list
"(" @delimiter
")" @delimiter @sentinel) @container
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(expression_switch_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(type_switch_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(literal_value
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_type
"[" @delimiter
"]" @delimiter @sentinel) @container
(slice_type
"[" @delimiter
"]" @delimiter @sentinel) @container
(map_type
"[" @delimiter
"]" @delimiter @sentinel) @container
(interface_type
"{" @delimiter
"}" @delimiter @sentinel) @container
(type_parameter_list
"[" @delimiter
"]" @delimiter @sentinel) @container
(type_arguments
"[" @delimiter
"]" @delimiter @sentinel) @container
(index_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(slice_expression
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,39 @@
(parens
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple
"(" @delimiter
")" @delimiter @sentinel) @container
(unit
"(" @delimiter
")" @delimiter @sentinel) @container
(exports
"(" @delimiter
")" @delimiter @sentinel) @container
(children
"(" @delimiter
")" @delimiter @sentinel) @container
(import_list
"(" @delimiter
")" @delimiter @sentinel) @container
(prefix_id
"(" @delimiter
")" @delimiter @sentinel) @container
(list
"[" @delimiter
"]" @delimiter @sentinel) @container
(fields
"{" @delimiter
"}" @delimiter @sentinel) @container
(record
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,47 @@
(tuple
(tuple_start "[") @delimiter
(tuple_end "]") @delimiter @sentinel) @container
(for_tuple_expr
(tuple_start "[") @delimiter
(tuple_end "]") @delimiter @sentinel) @container
(new_index
"[" @delimiter
"]" @delimiter @sentinel) @container
(function_call
"(" @delimiter
")" @delimiter @sentinel) @container
(expression
"(" @delimiter
")" @delimiter @sentinel) @container
(binary_operation
"(" @delimiter
")" @delimiter @sentinel) @container
(unary_operation
"(" @delimiter
")" @delimiter @sentinel) @container
(block
(block_start "{") @delimiter
(block_end "}") @delimiter @sentinel) @container
(object
(object_start "{") @delimiter
(object_end "}") @delimiter @sentinel) @container
(for_object_expr
(object_start "{") @delimiter
(object_end "}") @delimiter @sentinel) @container
(template_interpolation
(template_interpolation_start) @delimiter
(template_interpolation_end) @delimiter @sentinel) @container
(_
(template_directive_start) @delimiter
(template_directive_end) @delimiter @sentinel) @container

View File

@ -0,0 +1,69 @@
;;; A pair of delimiter tags with any content in-between.
;;; Last tag should be a sentinel.
;;; If instead you want rainbow-delimiters to only highlight
;;; the tag names without any of "<", "</", ">" or "/>", then
;;; you can make your own query file, e.g.,
;;; 'rainbow-tag-names'
;;; and use the following with
;;; x @delimiter
;;; deleted for x equal to any of "<", "</", ">" or "/>".
(element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(element
(self_closing_tag
"<" @delimiter
(tag_name) @delimiter
"/>" @delimiter @sentinel)) @container
(element
(start_tag
"<" @delimiter
(tag_name) @delimiter @_tag_name
">" @delimiter @sentinel)
(#any-of? @_tag_name
"area"
"base"
"br"
"col"
"embed"
"hr"
"img"
"input"
"link"
"meta"
"param"
"source"
"track"
"wbr")
) @container
(style_element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(element (self_closing_tag) @delimiter)*
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(script_element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container

View File

@ -0,0 +1,23 @@
(par_tup_lit
"(" @delimiter
")" @delimiter @sentinel) @container
(par_arr_lit
"@(" @delimiter
")" @delimiter @sentinel) @container
(sqr_tup_lit
"[" @delimiter
"]" @delimiter @sentinel) @container
(sqr_arr_lit
"@[" @delimiter
"]" @delimiter @sentinel) @container
(struct_lit
"{" @delimiter
"}" @delimiter @sentinel) @container
(tbl_lit
"@{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,70 @@
(class_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_initializer
"{" @delimiter
"}" @delimiter @sentinel) @container
(formal_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(resource_specification
"(" @delimiter
")" @delimiter @sentinel) @container
(catch_clause
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(cast_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(inferred_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(annotation_argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(enhanced_for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(constructor_body
"{" @delimiter
"}" @delimiter @sentinel) @container
;; Treat it as a single delimiter because it will always have the same color
(dimensions) @container @delimiter @sentinel
(dimensions_expr
"[" @delimiter
"]" @delimiter @sentinel) @container
(array_access
"[" @delimiter
"]" @delimiter @sentinel) @container
(type_arguments
"<" @delimiter
">" @delimiter @sentinel) @container

View File

@ -0,0 +1,102 @@
;;; This query includes React support as well.
;; String interpolation inside template strings
(template_substitution
"${" @delimiter
"}" @delimiter @sentinel) @container
(object
"{" @delimiter
"}" @delimiter @sentinel) @container
(statement_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(class_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(switch_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(formal_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(for_in_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(subscript_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(named_imports
"{" @delimiter
"}" @delimiter @sentinel) @container
(export_clause
"{" @delimiter
"}" @delimiter @sentinel) @container
(object_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container
(array_pattern
"[" @delimiter
"]" @delimiter @sentinel) @container
;;; React.js support
(jsx_element
open_tag: (jsx_opening_element
"<" @delimiter
name: (identifier) @delimiter
">" @delimiter)
close_tag: (jsx_closing_element
"</" @delimiter
name: (identifier) @delimiter
">" @delimiter @sentinel)) @container
(jsx_element
open_tag: (jsx_opening_element
"<" @delimiter
name: (member_expression) @delimiter
">" @delimiter)
close_tag: (jsx_closing_element
"</" @delimiter
name: (member_expression) @delimiter
">" @delimiter @sentinel)) @container
(jsx_self_closing_element
"<" @delimiter
name: (identifier) @delimiter
"/>" @delimiter @sentinel) @container
(jsx_self_closing_element
"<" @delimiter
name: (member_expression) @delimiter
"/>" @delimiter @sentinel) @container
(jsx_expression
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,68 @@
;;; This query only covers Javascript without any React support. It exists so
;;; that Typescript query can inherit it.
;; String interpolation inside template strings
(template_substitution
"${" @delimiter
"}" @delimiter @sentinel) @container
(object
"{" @delimiter
"}" @delimiter @sentinel) @container
(statement_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(class_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(switch_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(formal_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(for_in_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(subscript_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(named_imports
"{" @delimiter
"}" @delimiter @sentinel) @container
(export_clause
"{" @delimiter
"}" @delimiter @sentinel) @container
(object_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container
(array_pattern
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,68 @@
;;; This query only covers Javascript without any React support. It exists so
;;; that Typescript query can inherit it.
;; String interpolation inside template strings
(template_substitution
"${" @delimiter
"}" @delimiter @sentinel) @container
(object
"{" @delimiter
"}" @delimiter @sentinel) @container
(statement_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(class_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(switch_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(formal_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(for_in_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(subscript_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(named_imports
"{" @delimiter
"}" @delimiter @sentinel) @container
(export_clause
"{" @delimiter
"}" @delimiter @sentinel) @container
(object_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container
(array_pattern
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,29 @@
(jsx_element
open_tag: (jsx_opening_element
"<" @delimiter
name: (identifier) @delimiter
">" @delimiter)
close_tag: (jsx_closing_element
"</" @delimiter
name: (identifier) @delimiter
">" @delimiter @sentinel)) @container
(jsx_element
open_tag: (jsx_opening_element
"<" @delimiter
name: (member_expression) @delimiter
">" @delimiter)
close_tag: (jsx_closing_element
"</" @delimiter
name: (member_expression) @delimiter
">" @delimiter @sentinel)) @container
(jsx_self_closing_element
"<" @delimiter
name: (identifier) @delimiter
"/>" @delimiter @sentinel) @container
(jsx_self_closing_element
"<" @delimiter
name: (member_expression) @delimiter
"/>" @delimiter @sentinel) @container

View File

@ -0,0 +1,7 @@
(object
"{" @delimiter
"}" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,39 @@
(anonymous_function
"(" @delimiter
")" @delimiter @sentinel) @container
(functioncall
"(" @delimiter
")" @delimiter @sentinel) @container
(bind
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesis
"(" @delimiter
")" @delimiter @sentinel) @container
(field
"(" @delimiter
")" @delimiter @sentinel) @container
(fieldname
"[" @delimiter
"]" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container
(forloop
"[" @delimiter
"]" @delimiter @sentinel) @container
(indexing
"[" @delimiter
"]" @delimiter @sentinel) @container
(object
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,27 @@
(vector_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(matrix_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(comprehension_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(tuple_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(curly_expression
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,55 @@
(class_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(function_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(control_structure_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(lambda_literal
"{" @delimiter
"}" @delimiter @sentinel) @container
(primary_constructor
"(" @delimiter
")" @delimiter @sentinel) @container
(function_value_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(value_arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(multi_variable_declaration
"(" @delimiter
")" @delimiter @sentinel) @container
(for_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(when_expression
(when_subject
"(" @delimiter
")" @delimiter)?
"{" @delimiter
"}" @delimiter @sentinel) @container
(indexing_suffix
"[" @delimiter
"]" @delimiter @sentinel) @container
(type_parameters
"<" @delimiter
">" @delimiter @sentinel) @container
(type_arguments
"<" @delimiter
">" @delimiter @sentinel) @container

View File

@ -0,0 +1,11 @@
(inline_formula
"$" @delimiter
"$" @delimiter @sentinel) @container
(generic_environment
(begin) @delimiter
(end) @delimiter @sentinel) @container
(math_environment
(begin) @delimiter
(end) @delimiter @sentinel) @container

View File

@ -0,0 +1,15 @@
(curly_group
"{" @delimiter
"}" @delimiter @sentinel) @container
(curly_group_text
"{" @delimiter
"}" @delimiter @sentinel) @container
(curly_group_text_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(inline_formula
"$" @delimiter
"$" @delimiter @sentinel) @container

View File

@ -0,0 +1,69 @@
;;; Note: Some patterns are commented out because currently Neovim can capture
;;; only one node at a time. Once it becomes possible to capture multiple
;;; nodes they should be commented back in.
(function_declaration
"function" @delimiter
"end" @delimiter @sentinel) @container
(function_definition
"function" @delimiter
"end" @delimiter @sentinel) @container
(if_statement
"if" @delimiter
"then" @delimiter
(elseif_statement
"elseif" @delimiter
"then" @delimiter)*
(else_statement
"else" @delimiter)?
"end" @delimiter @sentinel) @container
(while_statement
"while" @delimiter
"do" @delimiter
"end" @delimiter @sentinel) @container
(repeat_statement
"repeat" @delimiter
"until" @delimiter @sentinel) @container
(for_statement
"for" @delimiter
(for_generic_clause
"in" @delimiter)?
"do" @delimiter
"end" @delimiter @sentinel) @container
(do_statement
"do" @delimiter
"end" @delimiter @sentinel) @container
;;; Copied over from rainbow-parens
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(table_constructor
"{" @delimiter
"}" @delimiter @sentinel) @container
(bracket_index_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(field
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,23 @@
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(table_constructor
"{" @delimiter
"}" @delimiter @sentinel) @container
(bracket_index_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(field
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,40 @@
(function_type
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_type
"(" @delimiter
")" @delimiter @sentinel) @container
;;; I wanted to use something like
; (union_type
; "|" @delimiter @sentinel
; ) @container
;;; too, but it doesn't fully work with the current parser
(array_type
"[" @delimiter
"]" @delimiter @sentinel) @container
(table_type
"<" @delimiter
">" @delimiter @sentinel) @container
(table_literal_type
"{" @delimiter
"}" @delimiter @sentinel) @container
(indexed_field
"[" @delimiter
"]" @delimiter @sentinel) @container
(tuple_type
"(" @delimiter
")" @delimiter @sentinel) @container
(_
"[" @delimiter
.
field: (_)
.
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1 @@
;;; Nothing I can think of, everything gets taken from the injected bash query.

View File

@ -0,0 +1,3 @@
;;; Intentionally empty. Markdown can have other languages embedded and we
;;; want to let those grammars handle the highlighting. This query only exists
;;; to satisfy the requirements of this plugin.

View File

@ -0,0 +1,47 @@
(array_construction
"[" @delimiter
"]" @delimiter @sentinel) @container
(tuple_construction
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_deconstruct_declaration
"(" @delimiter
")" @delimiter @sentinel) @container
(curly_construction
"{" @delimiter
":"? @delimiter
"}" @delimiter @sentinel) @container
(parenthesized
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parameter_declaration_list
"(" @delimiter
")" @delimiter @sentinel) @container
(bracket_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(field_declaration_list
"[" @delimiter
"]" @delimiter @sentinel) @container
(generic_parameter_list
"[" @delimiter
"]" @delimiter @sentinel) @container
(cast
"[" @delimiter
"]" @delimiter
"(" @delimiter
")" @delimiter @sentinel) @container
(term_rewriting_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(curly_expression
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,27 @@
(attrset_expression
("{" @delimiter)
("}" @delimiter @sentinel)) @container
(rec_attrset_expression
("{" @delimiter)
("}" @delimiter @sentinel)) @container
(formals
("{" @delimiter)
("}" @delimiter @sentinel)) @container
(list_expression
("[" @delimiter)
("]" @delimiter @sentinel)) @container
(parenthesized_expression
("(" @delimiter)
(")" @delimiter @sentinel)) @container
(interpolation
("${" @delimiter)
("}" @delimiter @sentinel)) @container
(inherit_from
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,86 @@
(argument
"(" @delimiter
")" @delimiter @sentinel) @container
(array
"(" @delimiter
")" @delimiter @sentinel) @container
(array_dereference
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_ref
"[" @delimiter
"]" @delimiter @sentinel) @container
(array_ref
"(" @delimiter
")" @delimiter @sentinel) @container
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(hash_access_variable
"->{" @delimiter
"}" @delimiter @sentinel) @container
(hash_access_variable
"{" @delimiter
"}" @delimiter @sentinel) @container
(hash_ref
"+" @delimiter
"{" @delimiter
"}" @delimiter @sentinel) @container
(multi_var_declaration
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(standalone_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(parenthesized_argument
"(" @delimiter
")" @delimiter @sentinel) @container
(list_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(word_list_qw
(start_delimiter_qw) @delimiter
(end_delimiter_qw) @delimiter @sentinel) @container
(regex_pattern_qr
(start_delimiter) @delimiter
(end_delimiter) @delimiter @sentinel) @container
(command_qx_quoted
(start_delimiter) @delimiter
(end_delimiter) @delimiter @sentinel) @container
(string_qq_quoted
(start_delimiter) @delimiter
(end_delimiter) @delimiter @sentinel) @container
(patter_matcher_m
(start_delimiter) @delimiter
(end_delimiter) @delimiter @sentinel) @container
(substitution_pattern_s
(start_delimiter) @delimiter
(separator_delimiter) @delimiter
(end_delimiter) @delimiter @sentinel) @container
(transliteration_tr_or_y
(start_delimiter) @delimiter
(separator_delimiter) @delimiter
(end_delimiter) @delimiter @sentinel) @container

View File

@ -0,0 +1,31 @@
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(formal_parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(compound_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(encapsed_string
"{" @delimiter
"}" @delimiter @sentinel) @container
(array_creation_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(subscript_expression
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,75 @@
;; NOTE: When updating this file update the Starlark test file as well if
;; applicable.
(list
"[" @delimiter
"]" @delimiter @sentinel) @container
(list_pattern
"[" @delimiter
"]" @delimiter @sentinel) @container
(list_comprehension
"[" @delimiter
"]" @delimiter @sentinel) @container
(dictionary
"{" @delimiter
"}" @delimiter @sentinel) @container
(dict_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(dictionary_comprehension
"{" @delimiter
"}" @delimiter @sentinel) @container
(set
"{" @delimiter
"}" @delimiter @sentinel) @container
(set_comprehension
"{" @delimiter
"}" @delimiter @sentinel) @container
(tuple
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_pattern
"(" @delimiter
")" @delimiter @sentinel) @container
(generator_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(subscript
"[" @delimiter
"]" @delimiter @sentinel) @container
(type_parameter
"[" @delimiter
"]" @delimiter @sentinel) @container
(import_from_statement
"(" @delimiter
")" @delimiter @sentinel) @container
(string
(interpolation
"{" @delimiter
"}" @delimiter @sentinel) @container)

View File

@ -0,0 +1,28 @@
;; Note: These queries are very useful when looking at a large
;; tree of queries like in `InspectTree`
(named_node
"(" @delimiter
(identifier) @delimiter
")" @delimiter @sentinel) @container
(grouping
"(" @delimiter
")" @delimiter @sentinel) @container
(list
"[" @delimiter
"]" @delimiter @sentinel) @container
(predicate
"(" @delimiter
")" @delimiter @sentinel) @container
(field_definition
(identifier) @delimiter @sentinel) @container
;; For more highlighting the following can be added too:
; (parameters
; (identifier) @delimiter @sentinel) @container
;
; (negated_field
; (identifier) @delimiter @sentinel) @container

View File

@ -0,0 +1,15 @@
(named_node
"(" @delimiter
")" @delimiter @sentinel) @container
(grouping
"(" @delimiter
")" @delimiter @sentinel) @container
(list
"[" @delimiter
"]" @delimiter @sentinel) @container
(predicate
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,36 @@
(call
"(" @delimiter
")" @delimiter @sentinel) @container
(subset
"[" @delimiter
"]" @delimiter @sentinel) @container
(subset2
"[[" @delimiter
"]]" @delimiter @sentinel) @container
(if
"(" @delimiter
")" @delimiter @sentinel) @container
(for
"(" @delimiter
")" @delimiter @sentinel) @container
(while
"(" @delimiter
")" @delimiter @sentinel) @container
(switch
"(" @delimiter
")" @delimiter @sentinel) @container
(function_definition
(formal_parameters
"(" @delimiter
")" @delimiter @sentinel)) @container
(brace_list
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,14 @@
(list
"(" @delimiter
(dot)? @delimiter
")" @delimiter @sentinel) @container
(list
"[" @delimiter
(dot)? @delimiter
"]" @delimiter @sentinel) @container
(list
"{" @delimiter
(dot)? @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,52 @@
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(environ_value
"$" @delimiter
"{" @delimiter
"}" @delimiter @sentinel) @container
(environ_value
"(" @delimiter
")" @delimiter @sentinel) @container
(list_value
"[" @delimiter
"]" @delimiter @sentinel) @container
(distance_calc
"(" @delimiter
")" @delimiter @sentinel) @container
(feature_query
"(" @delimiter
")" @delimiter @sentinel) @container
(reference_value
"(" @delimiter
")" @delimiter @sentinel) @container
(rgb_color
"(" @delimiter
")" @delimiter @sentinel) @container
(hsl_color
"(" @delimiter
")" @delimiter @sentinel) @container
(hwb_color
"(" @delimiter
")" @delimiter @sentinel) @container
(cmyk_color
"(" @delimiter
")" @delimiter @sentinel) @container
(url_image
"(" @delimiter
")" @delimiter @sentinel) @container
(gradient_image
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,17 @@
(anonymous_capturing_group
"(" @delimiter
")" @delimiter @sentinel) @container
;;; The inversion `^` should be an opening node as well
(character_class
"[" @delimiter
"]" @delimiter @sentinel) @container
(count_quantifier
"{" @delimiter
"}" @delimiter @sentinel) @container
;;; We should probably include the character after `?` like `=` as well
(lookaround_assertion
"(?" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,3 @@
;;; Intentionally empty. ReStructuredText can have other languages embedded
;;; and we want to let those grammars handle the highlighting. This query only
;;; exists to satisfy the requirements of this plugin.

View File

@ -0,0 +1,19 @@
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(hash
"{" @delimiter
"}" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container
(parenthesized_statements
"(" @delimiter
")" @delimiter @sentinel) @container
(block_parameters
"|" @delimiter
"|" @delimiter @sentinel) @container

View File

@ -0,0 +1,133 @@
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(field_declaration_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(ordered_field_declaration_list
"(" @delimiter
")" @delimiter @sentinel) @container
(enum_variant_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(use_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(field_initializer_list
"{" @delimiter
"}" @delimiter @sentinel) @container
(parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(block
"{" @delimiter
"}" @delimiter @sentinel) @container
(match_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(tuple_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_type
"(" @delimiter
")" @delimiter @sentinel) @container
(token_tree
"{" @delimiter
"}" @delimiter @sentinel) @container
(token_tree
"(" @delimiter
")" @delimiter @sentinel) @container
(token_tree
"[" @delimiter
"]" @delimiter @sentinel) @container
(token_tree_pattern
"(" @delimiter
")" @delimiter @sentinel) @container
(token_repetition_pattern
"$" @delimiter
"(" @delimiter
")" @delimiter @sentinel) @container
(token_repetition
"$" @delimiter
"(" @delimiter
")" @delimiter @sentinel) @container
(attribute_item
"[" @delimiter
"]" @delimiter @sentinel) @container
(inner_attribute_item
"[" @delimiter
"]" @delimiter @sentinel) @container
(type_arguments
"<" @delimiter
">" @delimiter @sentinel) @container
(type_parameters
"<" @delimiter
">" @delimiter @sentinel) @container
(closure_parameters
"|" @delimiter
"|" @delimiter @sentinel) @container
(array_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(array_type
"[" @delimiter
"]" @delimiter @sentinel) @container
(index_expression
"[" @delimiter
"]" @delimiter @sentinel) @container
(tuple_struct_pattern
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_pattern
"(" @delimiter
")" @delimiter @sentinel) @container
(struct_pattern
"{" @delimiter
"}" @delimiter @sentinel) @container
(slice_pattern
"[" @delimiter
"]" @delimiter @sentinel) @container
(macro_definition
"{" @delimiter
"}" @delimiter @sentinel) @container
(visibility_modifier
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,7 @@
(list
"(" @delimiter
")" @delimiter @sentinel) @container
(list
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,5 @@
; inherits: css
(parameters
"(" @delimiter
")" @delimiter @sentinel) @container

View File

@ -0,0 +1,28 @@
(subquery
"(" @delimiter
")" @delimiter @sentinel) @container
(invocation
"(" @delimiter
")" @delimiter @sentinel) @container
(list
"(" @delimiter
")" @delimiter @sentinel) @container
(where
"(" @delimiter
")" @delimiter @sentinel) @container
(binary_expression
"(" @delimiter
")" @delimiter @sentinel
) @container
; The following can cause problems with (((())))
(term
"(" @delimiter
; ("(" ")")* ; to fix _some_ problems, this can be uncommented
")" @delimiter @sentinel
) @container

View File

@ -0,0 +1,56 @@
;; This is mostly identical to Python, without `generator_expression`.
;; NOTE: if you update queries for Python, please consider adding the changes
;; to this file as well, given that the tree-sitter's node types exist. See
;; https://github.com/amaanq/tree-sitter-starlark/blob/master/src/node-types.json
(list
"[" @delimiter
"]" @delimiter @sentinel) @container
(list_comprehension
"[" @delimiter
"]" @delimiter @sentinel) @container
(dictionary
"{" @delimiter
"}" @delimiter @sentinel) @container
(dictionary_comprehension
"{" @delimiter
"}" @delimiter @sentinel) @container
(set
"{" @delimiter
"}" @delimiter @sentinel) @container
(set_comprehension
"{" @delimiter
"}" @delimiter @sentinel) @container
(tuple
"(" @delimiter
")" @delimiter @sentinel) @container
(tuple_pattern
"(" @delimiter
")" @delimiter @sentinel) @container
(parameters
"(" @delimiter
")" @delimiter @sentinel) @container
(argument_list
"(" @delimiter
")" @delimiter @sentinel) @container
(parenthesized_expression
"(" @delimiter
")" @delimiter @sentinel) @container
(subscript
"[" @delimiter
"]" @delimiter @sentinel) @container
(type_parameter
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,35 @@
(script_element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(style_element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(element
(self_closing_tag
"<" @delimiter
(tag_name) @delimiter
"/>" @delimiter @sentinel)) @container
(element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container

View File

@ -0,0 +1,53 @@
(script_element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(style_element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(element
(self_closing_tag
"<" @delimiter
(tag_name) @delimiter
"/>" @delimiter @sentinel)) @container
(element
(start_tag
"<" @delimiter
(tag_name) @delimiter
">" @delimiter)
(end_tag
"</" @delimiter
(tag_name) @delimiter
">" @delimiter @sentinel)) @container
(const_expr
"{" @delimiter
"@" @delimiter
(special_block_keyword) @delimiter
"}" @delimiter @sentinel) @container
(each_statement
(each_start_expr
"{" @delimiter
"#" @delimiter
(special_block_keyword) @delimiter
"}" @delimiter) @container
(each_end_expr
"{" @delimiter
"/" @delimiter
(special_block_keyword) @delimiter
"}" @delimiter @sentinel)) @container

View File

@ -0,0 +1,19 @@
(arguments
"(" @delimiter
")" @delimiter @sentinel) @container
(table_constructor
"{" @delimiter
"}" @delimiter @sentinel) @container
(table_entry
"[" @delimiter
"]" @delimiter @sentinel) @container
(index
"[" @delimiter
"]" @delimiter @sentinel) @container
(field
"[" @delimiter
"]" @delimiter @sentinel) @container

View File

@ -0,0 +1,61 @@
; inherits: go
;; HTML elements
(element
(tag_start
"<" @delimiter
(element_identifier) @delimiter
">" @delimiter)
(tag_end
"</" @delimiter
(element_identifier) @delimiter
">" @delimiter @sentinel)) @container
(element
(self_closing_tag
"<" @delimiter
(element_identifier) @delimiter
"/>" @delimiter @sentinel)) @container
(style_element
("<" @delimiter
"style" @delimiter
">" @delimiter)
("</" @delimiter
"style" @delimiter
">" @delimiter @sentinel)) @container
(script_element
("<" @delimiter
"script" @delimiter
">" @delimiter)
("</" @delimiter
"script" @delimiter
">" @delimiter @sentinel)) @container
;; Brackets
(component_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(script_block
"{" @delimiter
"}" @delimiter @sentinel) @container
(css_declaration
"{" @delimiter
"}" @delimiter @sentinel) @container
(component_switch_statement
"{" @delimiter
"}" @delimiter @sentinel) @container
(component_children_expression
"{" @delimiter
"}" @delimiter @sentinel) @container
(expression
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,15 @@
(table
"[" @delimiter
"]" @delimiter @sentinel) @container
(array
"[" @delimiter
"]" @delimiter @sentinel) @container
(inline_table
"{" @delimiter
"}" @delimiter @sentinel) @container
(table_array_element
"[[" @delimiter
"]]" @delimiter @sentinel) @container

View File

@ -0,0 +1,36 @@
; inherits: typescript
(jsx_element
open_tag: (jsx_opening_element
"<" @delimiter
name: (identifier) @delimiter
">" @delimiter)
close_tag: (jsx_closing_element
"</" @delimiter
name: (identifier) @delimiter
">" @delimiter @sentinel)) @container
(jsx_element
open_tag: (jsx_opening_element
"<" @delimiter
name: (member_expression) @delimiter
">" @delimiter)
close_tag: (jsx_closing_element
"</" @delimiter
name: (member_expression) @delimiter
">" @delimiter @sentinel)) @container
(jsx_self_closing_element
"<" @delimiter
name: (identifier) @delimiter
"/>" @delimiter @sentinel) @container
(jsx_self_closing_element
"<" @delimiter
name: (member_expression) @delimiter
"/>" @delimiter @sentinel) @container
(jsx_expression
"{" @delimiter
"}" @delimiter @sentinel) @container

View File

@ -0,0 +1,4 @@
; inherits: typescript
;;; This query exists for people who only want to highlight parentheses without
;;; tags.

View File

@ -0,0 +1,4 @@
; inherits: javascript
;;; This query exists for people who only want to highlight tags without
;;; parentheses.

View File

@ -0,0 +1,25 @@
; inherits: javascript
(interface_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(enum_body
"{" @delimiter
"}" @delimiter @sentinel) @container
(type_arguments
"<" @delimiter
">" @delimiter @sentinel) @container
(type_parameters
"<" @delimiter
">" @delimiter @sentinel) @container
(lookup_type
"[" @delimiter
"]" @delimiter @sentinel) @container
(object_type
"{" @delimiter
"}" @delimiter @sentinel) @container

Some files were not shown because too many files have changed in this diff Show More