├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ └── bug-report.md └── workflows │ ├── luacheck.yml │ └── stylua.yml ├── .gitignore ├── LICENSE ├── README.md ├── lua ├── rainbow.lua └── rainbow │ ├── internal.lua │ └── levels.lua ├── plugin └── rainbow.vim ├── queries ├── angle │ └── parens.scm ├── bash │ └── parens.scm ├── c │ └── parens.scm ├── c_sharp │ └── parens.scm ├── clojure │ └── parens.scm ├── commonlisp │ └── parens.scm ├── cpp │ └── parens.scm ├── css │ └── parens.scm ├── cuda │ └── parens.scm ├── curly │ └── parens.scm ├── dart │ └── parens.scm ├── devicetree │ └── parens.scm ├── ecma │ └── parens.scm ├── elixir │ └── parens.scm ├── elm │ └── parens.scm ├── fennel │ └── parens.scm ├── fish │ └── parens.scm ├── gdscript │ └── parens.scm ├── go │ └── parens.scm ├── graphql │ └── parens.scm ├── haskell │ └── parens.scm ├── hcl │ └── parens.scm ├── html │ └── parens.scm ├── java │ └── parens.scm ├── javascript │ └── parens.scm ├── json │ └── parens.scm ├── json5 │ └── parens.scm ├── jsonc │ └── parens.scm ├── jsx │ └── parens.scm ├── julia │ └── parens.scm ├── kotlin │ └── parens.scm ├── latex │ └── parens.scm ├── lua │ └── parens.scm ├── make │ └── parens.scm ├── meson │ └── parens.scm ├── nix │ └── parens.scm ├── ocaml │ └── parens.scm ├── ocaml_interface │ └── parens.scm ├── ocamllex │ └── parens.scm ├── php │ └── parens.scm ├── python │ └── parens.scm ├── query │ └── parens.scm ├── r │ └── parens.scm ├── racket │ └── parens.scm ├── regex │ └── parens.scm ├── rescript │ └── parens.scm ├── round │ └── parens.scm ├── ruby │ └── parens.scm ├── rust │ └── parens.scm ├── scala │ └── parens.scm ├── scheme │ └── parens.scm ├── scss │ └── parens.scm ├── solidity │ └── parens.scm ├── sparql │ └── parens.scm ├── square │ └── parens.scm ├── supercollider │ └── parens.scm ├── svelte │ └── parens.scm ├── teal │ └── parens.scm ├── tsx │ └── parens.scm ├── turtle │ └── parens.scm ├── typescript │ └── parens.scm ├── verilog │ └── parens.scm ├── vim │ └── parens.scm ├── vue │ └── parens.scm ├── yaml │ └── parens.scm └── zig │ └── parens.scm ├── screenshots ├── cpp.png ├── fnltreesitter.png ├── fnlwezterm.png ├── java.png └── latex_.png ├── stylua.toml └── test └── highlight ├── highlight_spec.lua ├── javascript ├── extended.js └── regular.js ├── jsx ├── extended.jsx └── regular.jsx └── tsx ├── extended.tsx └── regular.tsx /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | tab_width = 8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | charset = utf-8 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Found a bug? 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | 15 | **Steps to reproduce** 16 | 17 | Steps to reproduce the behavior. 18 | Please also paste the code which has buggy highlighting here. 19 | 20 | _Issues without example snippets will be closed_ 21 | 22 | 23 | **Expected behavior** 24 | 25 | A clear and concise description of what you expected to happen. 26 | 27 | 28 | **Screenshots** 29 | 30 | If applicable, add screenshots to help explain your problem. 31 | -------------------------------------------------------------------------------- /.github/workflows/luacheck.yml: -------------------------------------------------------------------------------- 1 | name: luacheck 2 | on: 3 | push: {branches: ["master"]} 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | 7 | jobs: 8 | luacheck: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Install and run Luacheck 13 | run: | 14 | sudo apt-get install luarocks 15 | luarocks install --local luacheck 16 | /home/runner/.luarocks/bin/luacheck . -q --globals "vim" --allow-defined-top 17 | -------------------------------------------------------------------------------- /.github/workflows/stylua.yml: -------------------------------------------------------------------------------- 1 | name: stylua 2 | on: 3 | push: {branches: ["master"]} 4 | 5 | jobs: 6 | stylua: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | with: 11 | fetch-depth: 0 12 | - name: Wait for luacheck to succeed 13 | uses: fountainhead/action-wait-for-check@v1.0.0 14 | id: wait-for-luacheck 15 | with: 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | checkName: luacheck 18 | ref: ${{ github.sha }} 19 | - name: Install and run stylua 20 | if: steps.wait-for-luacheck.outputs.conclusion == 'success' 21 | run: | 22 | wget "https://github.com/JohnnyMorganz/StyLua/releases/download/v0.6.0/stylua-0.6.0-linux.zip" -O /home/runner/stylua.zip 23 | unzip /home/runner/stylua.zip -d /home/runner 24 | chmod +x /home/runner/stylua 25 | /home/runner/stylua --config-path=stylua.toml lua/rainbow.lua lua/rainbow/internal.lua 26 | - name: Commit files 27 | run: | 28 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 29 | git config --local user.name "github-actions[bot]" 30 | if ! [[ -z $(git status -s) ]]; then 31 | git commit -m "Format lua" lua/* 32 | fi 33 | - name: Push changes 34 | uses: ad-m/github-push-action@master 35 | with: 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | branch: ${{ github.ref }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lua/rainbow/colors.lua 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NO LONGER MAINTAINED 2 | This plugin is no longer maintained. 3 | 4 | I'm archiving the GitHub repo and closing the sourcehut issue tracker. Feel free to fork 5 | 6 | # 🌈 nvim-ts-rainbow 🌈 7 | Rainbow parentheses for neovim using tree-sitter. 8 | This is a module for [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter), not a standalone plugin. It requires and is configured via nvim-treesitter 9 | 10 | Should work with any language supported by nvim-treesitter. If any language is missing, please open an issue/PR. 11 | 12 | Only neovim nightly is targeted. 13 | 14 | ## Warning/notice/whatever 15 | 16 | The queries might be out of date at any time, keeping up with them for languages I don't use is not feasible. If you get errors like `invalid node at position xxx`, try removing this plugin first before opening an issue in nvim-treesitter. If it fixes the problem, open an issue/PR here. 17 | 18 | ## Installation and setup 19 | Install and set up nvim-treesitter according to their documentation. Install this plugin, then add a `rainbow` section in the [call to `require("nvim-treesitter.configs").setup()`](https://github.com/nvim-treesitter/nvim-treesitter#modules): 20 | ```lua 21 | require("nvim-treesitter.configs").setup { 22 | highlight = { 23 | -- ... 24 | }, 25 | -- ... 26 | rainbow = { 27 | enable = true, 28 | -- disable = { "jsx", "cpp" }, list of languages you want to disable the plugin for 29 | extended_mode = true, -- Also highlight non-bracket delimiters like html tags, boolean or table: lang -> boolean 30 | max_file_lines = nil, -- Do not enable for files with more than n lines, int 31 | -- colors = {}, -- table of hex strings 32 | -- termcolors = {} -- table of colour name strings 33 | } 34 | } 35 | ``` 36 | 37 | If you want to enable it only for some filetypes and disable it for everything else, see https://github.com/p00f/nvim-ts-rainbow/issues/30#issuecomment-850991264 38 | 39 | ### Colours 40 | To change the colours you can set them in the setup: 41 | ```lua 42 | require'nvim-treesitter.configs'.setup{ 43 | rainbow = { 44 | -- Setting colors 45 | colors = { 46 | -- Colors here 47 | }, 48 | -- Term colors 49 | termcolors = { 50 | -- Term colors here 51 | } 52 | }, 53 | } 54 | ``` 55 | 56 | If you want to override some colours (you can only change colours 1 through 7 this way), you can do it in your init.vim: (thanks @delphinus !). You can also use this while writing a colorscheme 57 | ```vim 58 | hi rainbowcol1 guifg=#123456 59 | ``` 60 | ## Screenshots 61 | - Java 62 | 63 | ![alt text](https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/master/screenshots/java.png) 64 | 65 | The screenshots below use a different colorscheme 66 | - Fennel: 67 | 68 | ![alt text](https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/master/screenshots/fnlwezterm.png) 69 | ![alt text](https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/master/screenshots/fnltreesitter.png) 70 | - C++: 71 | 72 | ![alt text](https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/master/screenshots/cpp.png) 73 | - Latex (with tag begin-end matching) 74 | 75 | ![alt text](https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/master/screenshots/latex_.png) 76 | ## Credits 77 | Huge thanks to @vigoux, @theHamsta, @sogaiu, @bfredl and @sunjon and @steelsojka for all their help 78 | -------------------------------------------------------------------------------- /lua/rainbow.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Copyright 2020-2022 Chinmay Dalal 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | --]] 16 | 17 | local queries = require("nvim-treesitter.query") 18 | 19 | local M = {} 20 | 21 | function M.init() 22 | require("nvim-treesitter").define_modules({ 23 | rainbow = { 24 | module_path = "rainbow.internal", 25 | is_supported = function(lang) 26 | return queries.get_query(lang, "parens") ~= nil 27 | end, 28 | extended_mode = true, 29 | colors = { 30 | "#cc241d", 31 | "#a89984", 32 | "#b16286", 33 | "#d79921", 34 | "#689d6a", 35 | "#d65d0e", 36 | "#458588", 37 | }, 38 | termcolors = { 39 | "Red", 40 | "Green", 41 | "Yellow", 42 | "Blue", 43 | "Magenta", 44 | "Cyan", 45 | "White", 46 | }, 47 | }, 48 | }) 49 | end 50 | 51 | return M 52 | -------------------------------------------------------------------------------- /lua/rainbow/internal.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Copyright 2020-2022 Chinmay Dalal 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | --]] 16 | 17 | local queries = require("nvim-treesitter.query") 18 | local parsers = require("nvim-treesitter.parsers") 19 | local configs = require("nvim-treesitter.configs") 20 | local api = vim.api 21 | 22 | local add_predicate = vim.treesitter.query.add_predicate 23 | local nsid = vim.api.nvim_create_namespace("rainbow_ns") 24 | local extended_languages = { "latex", "html", "verilog", "jsx" } 25 | local colors = configs.get_module("rainbow").colors 26 | local termcolors = configs.get_module("rainbow").termcolors 27 | 28 | --- Maps a buffer ID to the buffer's parser; retaining a reference prevents the 29 | --- parser from getting garbage-collected. 30 | local buffer_parsers = {} 31 | 32 | --- Find the nesting level of a node. 33 | --- @param node table # Node to find the level of 34 | --- @param len number # Number of colours 35 | --- @param levels table # Levels for the language 36 | local function color_no(node, len, levels) 37 | local counter = 0 38 | local current = node 39 | local found = false 40 | while current:parent() ~= nil do 41 | if levels then 42 | if levels[current:type()] then 43 | counter = counter + 1 44 | found = true 45 | end 46 | else 47 | counter = counter + 1 48 | found = true 49 | end 50 | current = current:parent() 51 | end 52 | if not found then 53 | return 1 54 | elseif counter % len == 0 then 55 | return len 56 | else 57 | return (counter % len) 58 | end 59 | end 60 | 61 | --- Update highlights for a range. Called every time text is changed. 62 | --- @param bufnr number # Buffer number 63 | --- @param changes table # Range of text changes 64 | --- @param tree table # Syntax tree 65 | --- @param lang string # Language 66 | local function update_range(bufnr, changes, tree, lang) 67 | if vim.fn.pumvisible() ~= 0 or not lang then 68 | return 69 | end 70 | 71 | for _, change in ipairs(changes) do 72 | local root_node = tree:root() 73 | local query = queries.get_query(lang, "parens") 74 | local levels = require("rainbow.levels")[lang] 75 | if query ~= nil then 76 | for _, node, _ in query:iter_captures(root_node, bufnr, change[1], change[3] + 1) do 77 | -- set colour for this nesting level 78 | if not node:has_error() then 79 | local color_no_ = color_no(node, #colors, levels) 80 | local startRow, startCol, endRow, endCol = node:range() -- range of the capture, zero-indexed 81 | if vim.fn.has("nvim-0.7") == 1 then 82 | vim.highlight.range( 83 | bufnr, 84 | nsid, 85 | ("rainbowcol" .. color_no_), 86 | { startRow, startCol }, 87 | { endRow, endCol - 1 }, 88 | { 89 | regtype = "b", 90 | inclusive = true, 91 | priority = 210, 92 | } 93 | ) 94 | else 95 | vim.highlight.range( 96 | bufnr, 97 | nsid, 98 | ("rainbowcol" .. color_no_), 99 | { startRow, startCol }, 100 | { endRow, endCol - 1 }, 101 | "blockwise", 102 | true, 103 | 210 104 | ) 105 | end 106 | end 107 | end 108 | end 109 | end 110 | end 111 | 112 | --- Update highlights for every tree in given buffer. 113 | --- @param bufnr number # Buffer number 114 | local function full_update(bufnr) 115 | local parser = buffer_parsers[bufnr] 116 | parser:for_each_tree(function(tree, sub_parser) 117 | update_range(bufnr, { { tree:root():range() } }, tree, sub_parser:lang()) 118 | end) 119 | end 120 | 121 | --- Register predicates for extended mode. 122 | --- @param config table # Configuration for the `rainbow` module in nvim-treesitter 123 | local function register_predicates(config) 124 | local extended_mode 125 | 126 | if type(config.extended_mode) == "table" then 127 | extended_mode = {} 128 | for _, lang in pairs(config.extended_mode) do 129 | extended_mode[lang] = true 130 | end 131 | elseif type(config.extended_mode) == "boolean" then 132 | extended_mode = config.extended_mode 133 | else 134 | vim.api.nvim_err_writeln("nvim-ts-rainbow: `extended_mode` can be a boolean or a table") 135 | end 136 | 137 | for _, lang in ipairs(extended_languages) do 138 | local enable_extended_mode 139 | if type(extended_mode) == "table" then 140 | enable_extended_mode = extended_mode[lang] 141 | else 142 | enable_extended_mode = extended_mode 143 | end 144 | add_predicate(lang .. "-extended-rainbow-mode?", function() 145 | return enable_extended_mode 146 | end, true) 147 | end 148 | end 149 | 150 | local state_table = {} 151 | 152 | local M = {} 153 | 154 | --- Define highlight groups. This had to be a function to allow an autocmd doing this at colorscheme change. 155 | function M.defhl() 156 | for i = 1, math.max(#colors, #termcolors) do 157 | local s = string.format("highlight default rainbowcol%d", i) 158 | if #colors > 0 then 159 | s = s .. " guifg=" .. colors[(i % #colors == 0) and #colors or (i % #colors)] 160 | end 161 | if #termcolors > 0 then 162 | s = s 163 | .. " ctermfg=" 164 | .. termcolors[(i % #termcolors == 0) and #termcolors or (i % #termcolors)] 165 | end 166 | vim.cmd(s) 167 | end 168 | end 169 | 170 | M.defhl() 171 | 172 | --- Attach module to buffer. Called when new buffer is opened or `:TSBufEnable rainbow`. 173 | --- @param bufnr number # Buffer number 174 | --- @param lang string # Buffer language 175 | function M.attach(bufnr, lang) 176 | local config = configs.get_module("rainbow") 177 | local max_file_lines = config.max_file_lines 178 | if max_file_lines ~= nil and vim.api.nvim_buf_line_count(bufnr) > max_file_lines then 179 | return 180 | end 181 | register_predicates(config) 182 | local parser = parsers.get_parser(bufnr, lang) 183 | parser:register_cbs({ 184 | on_changedtree = function(changes, tree) 185 | if state_table[bufnr] then 186 | update_range(bufnr, changes, tree, lang) 187 | else 188 | return 189 | end 190 | end, 191 | }) 192 | buffer_parsers[bufnr] = parser 193 | state_table[bufnr] = true 194 | full_update(bufnr) 195 | end 196 | 197 | --- Detach module from buffer. Called when `:TSBufDisable rainbow`. 198 | --- @param bufnr number # Buffer number 199 | function M.detach(bufnr) 200 | state_table[bufnr] = false 201 | if vim.treesitter.highlighter.hl_map then 202 | vim.treesitter.highlighter.hl_map["punctuation.bracket"] = "TSPunctBracket" 203 | else 204 | vim.api.nvim_set_hl(0, "@punctuation.bracket", { link = "TSPunctBracket" }) 205 | end 206 | vim.api.nvim_buf_clear_namespace(bufnr, nsid, 0, -1) 207 | buffer_parsers[bufnr] = nil 208 | end 209 | 210 | if vim.fn.has("nvim-0.7") == 1 then 211 | api.nvim_create_augroup("RainbowParser", {}) 212 | api.nvim_create_autocmd("FileType", { 213 | group = "RainbowParser", 214 | pattern = "*", 215 | callback = function() 216 | local bufnr = api.nvim_get_current_buf() 217 | if state_table[bufnr] then 218 | local lang = parsers.get_buf_lang(bufnr) 219 | local parser = parsers.get_parser(bufnr, lang) 220 | buffer_parsers[bufnr] = parser 221 | end 222 | end, 223 | }) 224 | end 225 | 226 | return M 227 | -------------------------------------------------------------------------------- /lua/rainbow/levels.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Copyright 2020-2022 Chinmay Dalal 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | --]] 16 | 17 | return { 18 | python = { 19 | tuple = true, 20 | interpolation = true, 21 | list = true, 22 | dictionary = true, 23 | set = true, 24 | subscript = true, 25 | argument_list = true, 26 | parameters = true, 27 | parenthesized_expression = true, 28 | generator_expression = true, 29 | list_comprehension = true, 30 | dictionary_comprehension = true, 31 | set_comprehension = true, 32 | }, 33 | rust = { 34 | arguments = true, 35 | token_tree = true, 36 | array_expression = true, 37 | attribute_item = true, 38 | block = true, 39 | closure_expression = true, 40 | declaration_list = true, 41 | field_declaration_list = true, 42 | index_expression = true, 43 | macro_definition = true, 44 | macro_rule = true, 45 | match_block = true, 46 | meta_arguments = true, 47 | parameters = true, 48 | parenthesized_expression = true, 49 | struct_expression = true, 50 | tuple_expression = true, 51 | tuple_pattern = true, 52 | tuple_struct_pattern = true, 53 | tuple_type = true, 54 | type_arguments = true, 55 | type_parameters = true, 56 | unit_type = true, 57 | use_list = true, 58 | }, 59 | query = { 60 | grouping = true, 61 | list = true, 62 | named_node = true, 63 | }, 64 | c = { 65 | array_declarator = true, 66 | call_expression = true, 67 | compound_statement = true, 68 | enumerator_list = true, 69 | field_declaration_list = true, 70 | for_statement = true, 71 | function_definition = true, 72 | initializer_list = true, 73 | parameter_list = true, 74 | parenthesized_expression = true, 75 | subscript_expression = true, 76 | }, 77 | cpp = { 78 | array_declarator = true, 79 | call_expression = true, 80 | cast_expression = true, 81 | compound_statement = true, 82 | condition_clause = true, 83 | declaration_list = true, 84 | enumerator_list = true, 85 | field_declaration_list = true, 86 | initializer_list = true, 87 | parameter_list = true, 88 | parenthesized_expression = true, 89 | preproc_params = true, 90 | lambda_capture_specifier = true, 91 | subscript_expression = true, 92 | template_parameter_list = true, 93 | template_type = true, 94 | }, 95 | fennel = { 96 | ["for"] = true, 97 | ["local"] = true, 98 | each = true, 99 | each_clause = true, 100 | fn = true, 101 | global = true, 102 | hashfn = true, 103 | lambda = true, 104 | let = true, 105 | let_clause = true, 106 | list = true, 107 | match = true, 108 | parameters = true, 109 | quoted_list = true, 110 | quoted_sequential_table = true, 111 | sequential_table = true, 112 | set = true, 113 | table = true, 114 | var = true, 115 | }, 116 | tsx = { 117 | array = true, 118 | array_pattern = true, 119 | array_type = true, 120 | class_body = true, 121 | call_expression = true, 122 | formal_parameters = true, 123 | jsx_element = true, 124 | jsx_expression = true, 125 | jsx_self_closing_element = true, 126 | object = true, 127 | object_pattern = true, 128 | object_type = true, 129 | parenthesized_expression = true, 130 | statement_block = true, 131 | type_arguments = true, 132 | }, 133 | javascript = { 134 | array = true, 135 | call_expression = true, 136 | class_body = true, 137 | formal_parameters = true, 138 | -- NOTE: nvim-treesitter uses the javascript parser for jsx too 139 | jsx_element = true, 140 | jsx_expression = true, 141 | jsx_self_closing_element = true, 142 | new_expression = true, 143 | object = true, 144 | parenthesized_expression = true, 145 | statement_block = true, 146 | subscript_expression = true, 147 | template_substitution = true, 148 | }, 149 | elixir = { 150 | arguments = true, 151 | binary = true, 152 | block = true, 153 | interpolation = true, 154 | list = true, 155 | map = true, 156 | sigil = true, 157 | struct = true, 158 | tuple = true, 159 | }, 160 | html = { 161 | element = true, 162 | script_element = true, 163 | style_element = true, 164 | }, 165 | lua = { 166 | arguments = true, 167 | bracket_index_expression = true, 168 | parameters = true, 169 | parenthesized_expression = true, 170 | table_constructor = true, 171 | }, 172 | commonlisp = { 173 | list_lit = true, 174 | }, 175 | make = { 176 | command_substitution = true, 177 | function_call = true, 178 | substitution_reference = true, 179 | variable_reference = true, 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /plugin/rainbow.vim: -------------------------------------------------------------------------------- 1 | lua require "rainbow".init() 2 | autocmd ColorScheme * lua require "rainbow.internal".defhl() 3 | -------------------------------------------------------------------------------- /queries/angle/parens.scm: -------------------------------------------------------------------------------- 1 | "<" @left 2 | ">" @right 3 | -------------------------------------------------------------------------------- /queries/bash/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: round,curly,square 2 | (_ 3 | "[[" @left 4 | "]]" @right) 5 | (command_substitution 6 | "$(" @left 7 | ")" @right) 8 | (expansion 9 | "${" @left 10 | "}" @right) 11 | -------------------------------------------------------------------------------- /queries/c/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/c_sharp/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly,angle 2 | -------------------------------------------------------------------------------- /queries/clojure/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/commonlisp/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round 2 | -------------------------------------------------------------------------------- /queries/cpp/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: c 2 | 3 | (template_argument_list 4 | "<" @left ">" @right) 5 | 6 | (template_parameter_list 7 | "<" @left ">" @right) 8 | -------------------------------------------------------------------------------- /queries/css/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/cuda/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: cpp 2 | 3 | (kernel_call_syntax 4 | "<<<" @left ">>>" @right) 5 | -------------------------------------------------------------------------------- /queries/curly/parens.scm: -------------------------------------------------------------------------------- 1 | "{" @left 2 | "}" @right 3 | -------------------------------------------------------------------------------- /queries/dart/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/devicetree/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly,angle 2 | -------------------------------------------------------------------------------- /queries/ecma/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | 3 | (template_substitution 4 | "${" @left 5 | "}" @right) 6 | -------------------------------------------------------------------------------- /queries/elixir/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | (bitstring 3 | "<<" @left 4 | ">>" @right) 5 | (map 6 | "%" @left 7 | "{" @left 8 | "}" @right) 9 | (interpolation 10 | "#{" @left 11 | "}" @right) 12 | (sigil 13 | (sigil_name) @left 14 | (sigil_modifiers) @right) 15 | -------------------------------------------------------------------------------- /queries/elm/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/fennel/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/fish/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/gdscript/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/go/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/graphql/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/haskell/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/hcl/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/html/parens.scm: -------------------------------------------------------------------------------- 1 | 2 | "<" @left 3 | ">" @right 4 | "" @right 6 | 7 | 8 | [(tag_name) 9 | (#html-extended-rainbow-mode?)] @paren 10 | -------------------------------------------------------------------------------- /queries/java/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/javascript/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: ecma,jsx 2 | -------------------------------------------------------------------------------- /queries/json/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,curly 2 | -------------------------------------------------------------------------------- /queries/json5/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,curly 2 | -------------------------------------------------------------------------------- /queries/jsonc/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,curly 2 | -------------------------------------------------------------------------------- /queries/jsx/parens.scm: -------------------------------------------------------------------------------- 1 | ((jsx_opening_element 2 | "<" @left-tag-start 3 | name: _ @left 4 | ">" @left-tag-end 5 | ) (#jsx-extended-rainbow-mode? @left-tag-start @left-tag-end @left)) 6 | 7 | ((jsx_self_closing_element 8 | "<"+ @self-closing-tag-start 9 | name: _ @self-closing (#jsx-extended-rainbow-mode?) 10 | ; Combining "/" with "<" results in no JSX highlights 11 | ["/" ">"]+ @self-closing-tag-end 12 | ) (#jsx-extended-rainbow-mode? @self-closing-tag-start @self-closing-tag-end @self-closing)) 13 | 14 | ((jsx_closing_element 15 | ; Combining "/" with "<" results in no JSX highlights 16 | ["<" "/"]+ @right-tag-start 17 | name: _ @right 18 | ">" @right-tag-end 19 | ) (#jsx-extended-rainbow-mode? @right-tag-start @right-tag-end @right)) 20 | -------------------------------------------------------------------------------- /queries/julia/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/kotlin/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: round,curly 2 | 3 | ; TODO: enable square when the parser is fixed 4 | -------------------------------------------------------------------------------- /queries/latex/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | "$" @left 3 | "\\[" @left 4 | "\\]" @right 5 | "\\(" @left 6 | "\\)" @right 7 | 8 | [ 9 | "\\begin" 10 | (#latex-extended-rainbow-mode?)] @left 11 | [ 12 | "\\end" 13 | (#latex-extended-rainbow-mode?)] @right 14 | -------------------------------------------------------------------------------- /queries/lua/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/make/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits round, curly 2 | (function_call ")" @right) 3 | (function_call "(" @left) 4 | 5 | 6 | (substitution_reference ")" @right) 7 | (substitution_reference "(" @left) 8 | 9 | 10 | (variable_reference ")" @right) 11 | (variable_reference "(" @left) 12 | -------------------------------------------------------------------------------- /queries/meson/parens.scm: -------------------------------------------------------------------------------- 1 | [ 2 | "(" 3 | "[" 4 | ] @left 5 | [ 6 | ")" 7 | "]" 8 | ] @right 9 | -------------------------------------------------------------------------------- /queries/nix/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/ocaml/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | (_ "[|" @left 3 | "|]" @right) 4 | ;(_ "[<" @left ; TODO: 5 | ; ">]" @right) 6 | -------------------------------------------------------------------------------- /queries/ocaml_interface/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: ocaml 2 | -------------------------------------------------------------------------------- /queries/ocamllex/parens.scm: -------------------------------------------------------------------------------- 1 | (character_set ["[" @left "]" @right]) 2 | (parenthesized_regexp ["(" @left ")" @right]) 3 | -------------------------------------------------------------------------------- /queries/php/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | (_ (php_tag) @left "?>" @right) ; TODO: broken 3 | -------------------------------------------------------------------------------- /queries/python/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/query/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round 2 | -------------------------------------------------------------------------------- /queries/r/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/racket/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/regex/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | ["(?" 3 | "(?:" 4 | "(?<" 5 | ">"] @paren 6 | 7 | ; TODO: 8 | -------------------------------------------------------------------------------- /queries/rescript/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | 3 | (type_parameters 4 | "<" @paren 5 | ">" @paren) 6 | 7 | (type_arguments 8 | "<" @paren 9 | ">" @paren) 10 | -------------------------------------------------------------------------------- /queries/round/parens.scm: -------------------------------------------------------------------------------- 1 | "(" @left 2 | ")" @right 3 | -------------------------------------------------------------------------------- /queries/ruby/parens.scm: -------------------------------------------------------------------------------- 1 | [ 2 | "(" 3 | ")" 4 | "[" 5 | "]" 6 | "{" 7 | "}" 8 | "%w(" 9 | "%i(" 10 | "#{" 11 | ] @paren 12 | -------------------------------------------------------------------------------- /queries/rust/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | (type_arguments 3 | "<" @left 4 | ">" @right) 5 | (type_parameters 6 | "<" @left 7 | ">" @right) 8 | -------------------------------------------------------------------------------- /queries/scala/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/scheme/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round 2 | -------------------------------------------------------------------------------- /queries/scss/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/solidity/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/sparql/parens.scm: -------------------------------------------------------------------------------- 1 | [ 2 | "(" 3 | ")" 4 | "[" 5 | "]" 6 | "{" 7 | "}" 8 | (nil) 9 | (anon) 10 | ] @paren 11 | -------------------------------------------------------------------------------- /queries/square/parens.scm: -------------------------------------------------------------------------------- 1 | "[" @left 2 | "]" @right 3 | -------------------------------------------------------------------------------- /queries/supercollider/parens.scm: -------------------------------------------------------------------------------- 1 | ["{" "}" "[" "]" "(" ")" "|"] @paren 2 | -------------------------------------------------------------------------------- /queries/svelte/parens.scm: -------------------------------------------------------------------------------- 1 | [ 2 | "{" 3 | "}" 4 | ] @paren 5 | -------------------------------------------------------------------------------- /queries/teal/parens.scm: -------------------------------------------------------------------------------- 1 | ["{" "}" "[" "]" "(" ")"] @paren 2 | -------------------------------------------------------------------------------- /queries/tsx/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: typescript,jsx 2 | -------------------------------------------------------------------------------- /queries/turtle/parens.scm: -------------------------------------------------------------------------------- 1 | [ 2 | "(" 3 | ")" 4 | "[" 5 | "]" 6 | (anon) 7 | ] @paren 8 | -------------------------------------------------------------------------------- /queries/typescript/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: ecma 2 | (type_arguments 3 | "<" @paren 4 | ">" @paren) 5 | -------------------------------------------------------------------------------- /queries/verilog/parens.scm: -------------------------------------------------------------------------------- 1 | ["[" "]" "(" ")"] @paren 2 | 3 | ["begin" 4 | (#verilog-extended-rainbow-mode?) 5 | ] @left 6 | 7 | ["end" 8 | (#verilog-extended-rainbow-mode?) 9 | ] @right 10 | -------------------------------------------------------------------------------- /queries/vim/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: square,round,curly 2 | -------------------------------------------------------------------------------- /queries/vue/parens.scm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /queries/yaml/parens.scm: -------------------------------------------------------------------------------- 1 | [ 2 | "[" 3 | "]" 4 | "{" 5 | "}" 6 | ] @paren 7 | -------------------------------------------------------------------------------- /queries/zig/parens.scm: -------------------------------------------------------------------------------- 1 | ; inherits: round,square,curly 2 | -------------------------------------------------------------------------------- /screenshots/cpp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/ef95c15a935f97c65a80e48e12fe72d49aacf9b9/screenshots/cpp.png -------------------------------------------------------------------------------- /screenshots/fnltreesitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/ef95c15a935f97c65a80e48e12fe72d49aacf9b9/screenshots/fnltreesitter.png -------------------------------------------------------------------------------- /screenshots/fnlwezterm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/ef95c15a935f97c65a80e48e12fe72d49aacf9b9/screenshots/fnlwezterm.png -------------------------------------------------------------------------------- /screenshots/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/ef95c15a935f97c65a80e48e12fe72d49aacf9b9/screenshots/java.png -------------------------------------------------------------------------------- /screenshots/latex_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p00f/nvim-ts-rainbow/ef95c15a935f97c65a80e48e12fe72d49aacf9b9/screenshots/latex_.png -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | indent_width = 4 2 | column_width = 100 3 | indent_type = "Spaces" 4 | -------------------------------------------------------------------------------- /test/highlight/highlight_spec.lua: -------------------------------------------------------------------------------- 1 | ---@class ExpectedRainbowHighlight 2 | ---@field line number 0-based index of the line to be highlighted 3 | ---@field column number 0-based index of the character to be highlighted 4 | ---@field rainbow_level string The expected level of the rainbow to use in the highlight 5 | 6 | ---@param source_code_lines string[] 7 | ---@return ExpectedRainbowHighlight[] 8 | local function get_expected_highlights(source_code_lines) 9 | ---@type ExpectedRainbowHighlight[] 10 | local expected_highlights = {} 11 | 12 | for line_index, line in ipairs(source_code_lines) do 13 | local matches = string.find(line, "hl[%s%d]*") 14 | if matches ~= nil then 15 | for char_index = 1, #line do 16 | local char = line:sub(char_index, char_index) 17 | if string.match(char, "%d") then 18 | table.insert(expected_highlights, { 19 | -- NOTE: subtract 1 to get the index to be 0-based, 20 | -- and another 1 because the hl-line describes the 21 | -- expected highlights for the line above the hl-line 22 | line = line_index - 2, 23 | column = char_index - 1, 24 | rainbow_level = char, 25 | }) 26 | end 27 | end 28 | end 29 | end 30 | 31 | return expected_highlights 32 | end 33 | 34 | ---Returns the name of the rainbow highlight group for a given level 35 | ---@param level string 36 | ---@return string 37 | local function get_rainbow_highlight_group_name(level) 38 | return "rainbowcol" .. level 39 | end 40 | 41 | ---Prepares a line with the invalid column marked by ^. 42 | ---Used in error reporting. 43 | ---@param invalid_column number 0-based 44 | ---@return string 45 | local function get_error_marker_line(invalid_column) 46 | return string.rep(" ", invalid_column) .. "^" 47 | end 48 | 49 | ---Prints the line with an error marker line that points to some character. 50 | ---@param line string The line to print 51 | ---@param line_number number The line number to be printed 52 | ---@param error_marker_column number 0-based column number to place the error marker 53 | local function print_line_with_error_marker(line, line_number, error_marker_column) 54 | local line_number_width = 3 55 | 56 | print(string.format("%" .. line_number_width .. "d: %s", line_number, line)) 57 | print( 58 | string.format( 59 | "%" .. line_number_width .. "s %s\n", 60 | "", 61 | get_error_marker_line(error_marker_column) 62 | ) 63 | ) 64 | end 65 | 66 | ---@class HighlightedSymbol 67 | ---@field line number 0-based 68 | ---@field column number 0-based 69 | ---@field hl_group string 70 | 71 | ---@param extmarks table[] Extmark returned from nvim_buf_get_extmarks 72 | ---@return HighlightedSymbol[] 73 | local function get_highlighted_symbols_from_extmarks(extmarks) 74 | ---@type HighlightedSymbol[] 75 | local highlighted_symbols = {} 76 | 77 | for _, extmark in ipairs(extmarks) do 78 | -- TODO: support multi-line extmarks 79 | local line = extmark[2] 80 | local start_col = extmark[3] 81 | local details = extmark[4] 82 | ---Extmark is end_col-exclusive 83 | local end_col = details.end_col 84 | 85 | for col = start_col, end_col - 1 do 86 | table.insert(highlighted_symbols, { 87 | line = line, 88 | column = col, 89 | hl_group = details.hl_group, 90 | }) 91 | end 92 | end 93 | 94 | return highlighted_symbols 95 | end 96 | 97 | ---Prunes duplicate highlighted symbols ensuring that each symbol is highlighted 98 | ---with a single highlight group. 99 | ---nvim-ts-rainbow sometimes sets duplicated extmarks to highlight symbols. 100 | ---Not pruning duplicates would mean errors would be reported multiple times 101 | --(once for each duplicate extmark). 102 | ---@param highlighted_symbols HighlightedSymbol[] The table of highlighted symbols. it will be modified in place. 103 | ---@param source_code_lines string[] Source code lines used for error reporting 104 | local function remove_duplicate_highlighted_symbols(highlighted_symbols, source_code_lines) 105 | local multiple_highlights_for_symbols = false 106 | 107 | -- NOTE: manual index tracking because one loop iteration can remove 108 | -- multiple elements. Using iterators could iterate over removed indices 109 | local index = 1 110 | while index <= #highlighted_symbols do 111 | local symbol = highlighted_symbols[index] 112 | 113 | -- The body of the loop tries to prune duplicates in the range of 114 | -- index+1..#highlighted_symbols 115 | 116 | -- NOTE: loop from the end to allow removing elements in the loop while 117 | -- preserving indices that will be looped over in the future 118 | for other_symbol_index = #highlighted_symbols, index + 1, -1 do 119 | local other_symbol = highlighted_symbols[other_symbol_index] 120 | if symbol.line == other_symbol.line and symbol.column == other_symbol.column then 121 | if symbol.hl_group == other_symbol.hl_group then 122 | table.remove(highlighted_symbols, other_symbol_index) 123 | else 124 | print("Symbol has multiple different highlight groups assigned to it.") 125 | print( 126 | string.format( 127 | "Found highlight groups: %s %s", 128 | symbol.hl_group, 129 | other_symbol.hl_group 130 | ) 131 | ) 132 | 133 | print_line_with_error_marker( 134 | source_code_lines[symbol.line + 1], 135 | symbol.line + 1, 136 | symbol.column 137 | ) 138 | multiple_highlights_for_symbols = true 139 | end 140 | end 141 | end 142 | 143 | index = index + 1 144 | end 145 | 146 | assert(not multiple_highlights_for_symbols, "There are multiple highlights for some symbols") 147 | end 148 | 149 | local function verify_highlights_in_file(filename) 150 | local extended_mode = string.find(filename, "extended") ~= nil 151 | local rainbow_module = require("nvim-treesitter.configs").get_module("rainbow") 152 | rainbow_module.extended_mode = extended_mode 153 | 154 | vim.cmd.edit(filename) 155 | 156 | local source_code_lines = vim.api.nvim_buf_get_lines(0, 0, -1, 1) 157 | 158 | vim.api.nvim_buf_set_lines(0, 0, -1, true, source_code_lines) 159 | 160 | local rainbow_ns_id = vim.api.nvim_get_namespaces().rainbow_ns 161 | assert.not_equal(nil, rainbow_ns_id, "rainbow namespace not found") 162 | 163 | local parser = require("nvim-treesitter.parsers").get_parser(0) 164 | assert.truthy(parser, "Parser not found") 165 | parser:parse() 166 | 167 | -- NOTE: nvim_buf_get_extmarks does not return extmarks that contain 168 | -- some range. It only returns extmarks within the given range. 169 | -- We cannot use it to look for extmarks for a given symbol, because sometimes 170 | -- the extmarks are for a range (e.g. when highlighting a JSX tag, the 171 | -- whole range "div" is a single extmark and asking for an extmark for 172 | -- the position of "i" returns nothing). 173 | -- Thus, we must filter through all extmarks set on the buffer and 174 | -- check each symbol. 175 | local extmarks = vim.api.nvim_buf_get_extmarks(0, rainbow_ns_id, 0, -1, { details = true }) 176 | local highlighted_symbols = get_highlighted_symbols_from_extmarks(extmarks) 177 | 178 | remove_duplicate_highlighted_symbols(highlighted_symbols, source_code_lines) 179 | 180 | local some_symbol_not_highlighted = false 181 | local invalid_highlight = false 182 | for _, expected_highlight in ipairs(get_expected_highlights(source_code_lines)) do 183 | local symbol_highlighted = false 184 | 185 | -- NOTE: loop from the end to allow removing elements inside of the loop 186 | -- without changing the indices that will be looped over 187 | for i = #highlighted_symbols, 1, -1 do 188 | local highlighted_symbol = highlighted_symbols[i] 189 | if 190 | highlighted_symbol.line == expected_highlight.line 191 | and highlighted_symbol.column == expected_highlight.column 192 | then 193 | symbol_highlighted = true 194 | 195 | local expected_highlight_group = 196 | get_rainbow_highlight_group_name(expected_highlight.rainbow_level) 197 | if expected_highlight_group ~= highlighted_symbol.hl_group then 198 | invalid_highlight = true 199 | print( 200 | string.format( 201 | 'Invalid rainbow highlight group. Expected "%s", found "%s"', 202 | expected_highlight_group, 203 | highlighted_symbol.hl_group 204 | ) 205 | ) 206 | print_line_with_error_marker( 207 | source_code_lines[expected_highlight.line + 1], 208 | expected_highlight.line + 1, 209 | expected_highlight.column 210 | ) 211 | end 212 | 213 | -- NOTE: remove the matched highlighted symbol to later 214 | -- check that all highlighted symbols matched some expected 215 | -- highlight 216 | table.remove(highlighted_symbols, i) 217 | end 218 | end 219 | 220 | if not symbol_highlighted then 221 | print( 222 | string.format( 223 | 'No highlight groups detected. Expected "%s".', 224 | get_rainbow_highlight_group_name(expected_highlight.rainbow_level) 225 | ) 226 | ) 227 | print_line_with_error_marker( 228 | source_code_lines[expected_highlight.line + 1], 229 | expected_highlight.line + 1, 230 | expected_highlight.column 231 | ) 232 | some_symbol_not_highlighted = true 233 | end 234 | end 235 | 236 | for _, symbol in ipairs(highlighted_symbols) do 237 | print( 238 | string.format( 239 | 'Symbol was extraneously highlighted with highlight group "%s"', 240 | symbol.hl_group 241 | ) 242 | ) 243 | print_line_with_error_marker( 244 | source_code_lines[symbol.line + 1], 245 | symbol.line + 1, 246 | symbol.column 247 | ) 248 | end 249 | assert(not invalid_highlight, "Some symbol was incorrectly highlighted") 250 | assert(not some_symbol_not_highlighted, "Some symbol was not highlighted") 251 | assert(#highlighted_symbols == 0, "Extraneous highlights") 252 | end 253 | 254 | describe("Highlighting integration tests", function() 255 | local files = vim.fn.glob("test/highlight/**/*.*", nil, true) 256 | 257 | for _, filename in ipairs(files) do 258 | if not string.match(filename, "highlight_spec.lua") then 259 | it(filename, function() 260 | verify_highlights_in_file(filename) 261 | end) 262 | end 263 | end 264 | end) 265 | -------------------------------------------------------------------------------- /test/highlight/javascript/extended.js: -------------------------------------------------------------------------------- 1 | // NOTE: javascript parser should allow JSX 2 | 3 | const abc = {}; 4 | // hl 11 5 | 6 | // NOTE: there are syntax errors because of using // as comments in JSX. 7 | // Using {/* */} as comments would lead to the braces being highlighted 8 | // which would require more and more comments 9 | 10 | // prettier-ignore 11 | function MyComponent() { 12 | // hl 11 1 13 | return ( 14 | // hl 2 15 |
16 | // hl 33333 17 | Hello world 18 | // hl 444444 4444444 19 | 20 | // hl 4444 44 21 |
22 | //hl 333333 23 | ); 24 | // hl2 25 | } 26 | // hl1 27 | -------------------------------------------------------------------------------- /test/highlight/javascript/regular.js: -------------------------------------------------------------------------------- 1 | // prettier-ignore 2 | (function () { 3 | //hl2 33 3 4 | const abc = { 5 | // hl 4 6 | nested: { 7 | // hl 5 8 | evenMore: { 9 | // hl 6 10 | another: [ 11 | // hl 7 12 | { 13 | //hl1 14 | level: "", 15 | }, 16 | //hl1 17 | ], 18 | //hl7 19 | }, 20 | //hl6 21 | }, 22 | //hl5 23 | }; 24 | //hl4 25 | 26 | class Foo { 27 | // hl 4 28 | method() { 29 | // hl 55 5 30 | fn() 31 | // hl 66 32 | } 33 | //hl5 34 | } 35 | //hl4 36 | })(); 37 | //hl3211 38 | -------------------------------------------------------------------------------- /test/highlight/jsx/extended.jsx: -------------------------------------------------------------------------------- 1 | const abc = {}; 2 | // hl 11 3 | 4 | // NOTE: there are syntax errors because of using // as comments in JSX. 5 | // Using {/* */} as comments would lead to the braces being highlighted 6 | // which would require more and more comments 7 | 8 | // prettier-ignore 9 | function MyComponent() { 10 | // hl 11 1 11 | return ( 12 | // hl 2 13 |
14 | // hl 33333 15 | Hello world 16 | // hl 444444 4444444 17 | 18 | // hl 4444 44 19 |
20 | //hl 333333 21 | ); 22 | // hl2 23 | } 24 | // hl1 25 | -------------------------------------------------------------------------------- /test/highlight/jsx/regular.jsx: -------------------------------------------------------------------------------- 1 | const abc = {}; 2 | // hl 11 3 | // prettier-ignore 4 | function MyComponent() { 5 | // hl 11 1 6 | return ( 7 | // hl 2 8 |
9 | Hello world 10 | 11 |
12 | ); 13 | // hl2 14 | } 15 | // hl1 16 | -------------------------------------------------------------------------------- /test/highlight/tsx/extended.tsx: -------------------------------------------------------------------------------- 1 | const abc = {}; 2 | // hl 11 3 | 4 | // NOTE: there are syntax errors because of using // as comments in JSX. 5 | // Using {/* */} as comments would lead to the braces being highlighted 6 | // which would require more and more comments 7 | 8 | // prettier-ignore 9 | function MyComponent() { 10 | // hl 11 1 11 | return ( 12 | // hl 2 13 |
14 | // hl 33333 15 | Hello world 16 | // hl 444444 4444444 17 | 18 | // hl 4444 44 19 |
20 | //hl 333333 21 | ); 22 | // hl2 23 | } 24 | // hl1 25 | 26 | // prettier-ignore 27 | (function () { 28 | //hl2 33 3 29 | const abc = { 30 | // hl 4 31 | nested: { 32 | // hl 5 33 | evenMore: { 34 | // hl 6 35 | another: [ 36 | // hl 7 37 | { 38 | //hl1 39 | level: "", 40 | }, 41 | //hl1 42 | ], 43 | //hl7 44 | }, 45 | //hl6 46 | }, 47 | //hl5 48 | }; 49 | //hl4 50 | 51 | class Foo { 52 | // hl 4 53 | method() { 54 | // hl 55 5 55 | fn() 56 | // hl 66 57 | } 58 | //hl5 59 | } 60 | //hl4 61 | })(); 62 | //hl3211 63 | -------------------------------------------------------------------------------- /test/highlight/tsx/regular.tsx: -------------------------------------------------------------------------------- 1 | const abc = {}; 2 | // hl 11 3 | // prettier-ignore 4 | function MyComponent() { 5 | // hl 11 1 6 | return ( 7 | // hl 2 8 |
9 | Hello world 10 | 11 |
12 | ); 13 | // hl2 14 | } 15 | // hl1 16 | 17 | // prettier-ignore 18 | (function () { 19 | //hl2 33 3 20 | const abc = { 21 | // hl 4 22 | nested: { 23 | // hl 5 24 | evenMore: { 25 | // hl 6 26 | another: [ 27 | // hl 7 28 | { 29 | //hl1 30 | level: "", 31 | }, 32 | //hl1 33 | ], 34 | //hl7 35 | }, 36 | //hl6 37 | }, 38 | //hl5 39 | }; 40 | //hl4 41 | 42 | class Foo { 43 | // hl 4 44 | method() { 45 | // hl 55 5 46 | fn() 47 | // hl 66 48 | } 49 | //hl5 50 | } 51 | //hl4 52 | })(); 53 | //hl3211 54 | --------------------------------------------------------------------------------