Regenerate nvim config
This commit is contained in:
@ -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:
|
||||
@ -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
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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:
|
||||
Reference in New Issue
Block a user