diff --git a/autoload/rainbow_delimiters.vim b/autoload/rainbow_delimiters.vim index c698b28..c7d954f 100644 --- a/autoload/rainbow_delimiters.vim +++ b/autoload/rainbow_delimiters.vim @@ -23,4 +23,22 @@ function! rainbow_delimiters#hlgroup_at(i) return luaeval("require('rainbow-delimiters').hlgroup_at(_A)", a:i) endfunction +" Disable highlighting for the given buffer. Buffer number zero means current +" buffer. +function! rainbow_delimiters#disable(bufnr) + call luaeval("require('rainbow-delimiters').disable(_A)", a:bufnr) +endfunction + +" Enable highlighting for the given buffer. Buffer number zero means current +" buffer. +function! rainbow_delimiters#enable(bufnr) + call luaeval("require('rainbow-delimiters').enable(_A)", a:bufnr) +endfunction + +" Toggle highlighting for the given buffer. Buffer number zero means current +" buffer. +function! rainbow_delimiters#toggle(bufnr) + call luaeval("require('rainbow-delimiters').toggle(_A)", a:bufnr) +endfunction + " vim:tw=79:ts=4:sw=4:noet: diff --git a/doc/rainbow-delimiters.txt b/doc/rainbow-delimiters.txt index d9f94cd..75e0f18 100644 --- a/doc/rainbow-delimiters.txt +++ b/doc/rainbow-delimiters.txt @@ -366,6 +366,36 @@ There is a utility library provided for people writing their own strategies. It is available as a table under the Lua module `'rainbow-delimiters'`. + *rb-delimiters.enable* + *rb_delimiters#enable* +'rainbow-delimiters'.enable({bufnr}) + Re-enable rainbow delimiters for the buffer {bufnr} (or the current buffer + if {bufnr} is `0`) after it has been disabled. + +rainbow_delimiters#enable({bufnr}) + Vim script binding for the above function. + + + *rb-delimiters.disable* + *rb_delimiters#disable* +'rainbow-delimiters'.disable({bufnr}) + Disable rainbow delimiters for the buffer {bufnr} (or the current buffer + if {bufnr} is `0`). + +rainbow_delimiters#disable({bufnr}) + Vim script binding for the above function. + + + *rb-delimiters.toggle* + *rb_delimiters#toggle* +'rainbow-delimiters'.toggle({bufnr}) + Toggle rainbow delimiters for the buffer {bufnr} (or the current buffer + if {bufnr} is `0`). + +rainbow_delimiters#toggle({bufnr}) + Vim script binding for the above function. + + *rb-delimiters.hlgroup_at* *rainbow-delimiters#hlgroup_at* 'rainbow-delimiters'.hlgroup_at({nesting_level}) diff --git a/lua/rainbow-delimiters.lua b/lua/rainbow-delimiters.lua index 9fd2a80..c6a6b8f 100644 --- a/lua/rainbow-delimiters.lua +++ b/lua/rainbow-delimiters.lua @@ -17,6 +17,33 @@ local lib = require 'rainbow-delimiters.lib' +---Disable rainbow delimiters for a given buffer. +---@param bufnr number Buffer number, zero for current buffer. +local function disable(bufnr) + bufnr = bufnr > 0 and bufnr or vim.api.nvim_get_current_buf() + lib.detach(bufnr) + lib.buffers[bufnr] = false +end + +---Enable rainbow delimiters for a given buffer. +---@param bufnr number Buffer number, zero for current buffer. +local function enable(bufnr) + bufnr = bufnr > 0 and bufnr or vim.api.nvim_get_current_buf() + lib.buffers[bufnr] = nil + lib.attach(bufnr) +end + +local function toggle(bufnr) + bufnr = bufnr > 0 and bufnr or vim.api.nvim_get_current_buf() + if lib.buffers[bufnr] then + print 'turn off' + disable(bufnr) + else + print 'turn on' + enable(bufnr) + end +end + ---Public API for use in writing strategies or other custom code. local M = { hlgroup_at = lib.hlgroup_at, @@ -25,7 +52,10 @@ local M = { ['global'] = require 'rainbow-delimiters.strategy.global', ['local'] = require 'rainbow-delimiters.strategy.local', ['noop'] = require 'rainbow-delimiters.strategy.no-op', - } + }, + enable = enable, + disable = disable, + toggle = toggle, } diff --git a/lua/rainbow-delimiters/lib.lua b/lua/rainbow-delimiters/lib.lua index cccb0e6..b395aeb 100644 --- a/lua/rainbow-delimiters/lib.lua +++ b/lua/rainbow-delimiters/lib.lua @@ -16,6 +16,7 @@ --]] 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' @@ -120,6 +121,100 @@ function M.clear_namespace(bufnr, lang, line_start, line_end) end end +---Start rainbow highlighting for the given buffer +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) + + if M.buffers[bufnr] then + if M.buffers[bufnr].lang == lang then return end + -- The file type of the buffer has change, 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() + end + end + if not strategy or strategy == vim.NIL then return 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 + + parser:register_cbs { + on_detach = function(bnr) + if not M.buffers[bnr] then return end + M.detach(bufnr) + end, + on_child_removed = function(child) + M.clear_namespace(bufnr, child:lang()) + end, + } + + local 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 +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 + parser:for_each_child(function(_, lang) + M.clear_namespace(bufnr, lang) + end, true) + -- 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: diff --git a/plugin/rainbow-delimiters.lua b/plugin/rainbow-delimiters.lua index 6dfbe70..100689f 100644 --- a/plugin/rainbow-delimiters.lua +++ b/plugin/rainbow-delimiters.lua @@ -21,7 +21,6 @@ local api = vim.api local set_hl = api.nvim_set_hl local create_augroup = api.nvim_create_augroup local create_autocmd = api.nvim_create_autocmd -local get_parser = vim.treesitter.get_parser local get_lang = vim.treesitter.language.get_lang local config = require 'rainbow-delimiters.config' local log = require 'rainbow-delimiters.log' @@ -44,99 +43,6 @@ end define_hlgroups() ---- [ CALLBACK FUNCTIONS ]----------------------------------------------------- -local attach, detach - -function attach(bufnr) - 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) - - if lib.buffers[bufnr] then - if lib.buffers[bufnr].lang == lang then return end - -- The file type of the buffer has change, so we need to detach first - -- before we re-attach - 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() - end - end - if not strategy or strategy == vim.NIL then return 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 - - parser:register_cbs { - on_detach = function(bnr) - if not lib.buffers[bnr] then return end - detach(bufnr) - end, - on_child_removed = function(child) - lib.clear_namespace(bufnr, child:lang()) - end, - } - - local settings = { - strategy = strategy, - parser = parser, - lang = lang - } - lib.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) - lib.buffers[bufnr] = nil - end -end - -function detach(bufnr) - log.trace('Detaching from buffer %d.', bufnr) - if not lib.buffers[bufnr] then - return - end - - local strategy = lib.buffers[bufnr].strategy - local parser = lib.buffers[bufnr].parser - - -- Clear all the namespaces for each language - parser:for_each_child(function(_, lang) - lib.clear_namespace(bufnr, lang) - end, true) - -- 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 - lib.buffers[bufnr] = nil -end - - --- [ SET UP AUTOCOMMANDS ]---------------------------------------------------- local hl_augroup = create_augroup('TSRainbowHighlight', {}) local rb_augroup = create_augroup('TSRainbowDelimits', {}) @@ -154,14 +60,14 @@ create_autocmd('FileType', { local lang = get_lang(args.match) if not config.enabled_for(lang) then return end - attach(args.buf) + lib.attach(args.buf) end, }) create_autocmd('BufUnload', { desc = 'Detach from the current buffer', group = rb_augroup, - callback = function(args) detach(args.buf) end + callback = function(args) lib.detach(args.buf) end }) vim.g.loaded_rainbow = true diff --git a/test/vader/integration/toggle.vader b/test/vader/integration/toggle.vader new file mode 100644 index 0000000..b2b60e6 --- /dev/null +++ b/test/vader/integration/toggle.vader @@ -0,0 +1,80 @@ +# We can use functions to turn rainbow delimiters off and on again. + +Execute lua (Set up the plugin): + the_strategy = require('rainbow-delimiters.strategy.global') + vim.g.rainbow_delimiters = {strategy = {[''] = the_strategy}} + +Given lua (A Lua buffer): + print((((('Hello, world!'))))) + +Execute (Leave things as they are): + redraw! " Without this there will be no extmarks at all + +Then (Rainbow delimiters are active): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertNotEqual [], extmarks + +Execute (Turn rainbow delimiters off): + call rainbow_delimiters#disable(0) + redraw! " Without this there will be no extmarks at all + +Then (Rainbow delimiters are inactive): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertEqual [], extmarks + +Execute (Turn rainbow delimiters off a second time): + call rainbow_delimiters#disable(0) + redraw! " Without this there will be no extmarks at all + +Then (Disabling is idempotent): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertEqual [], extmarks + +Execute (Turn rainbow delimiters back on): + call rainbow_delimiters#enable(0) + redraw! " Without this there will be no extmarks at all + +Then (Rainbow delimiters are active): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertNotEqual [], extmarks + +Execute (Turn rainbow delimiters on a second time): + call rainbow_delimiters#enable(0) + redraw! " Without this there will be no extmarks at all + +Then (Enabling is idempotent): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertNotEqual [], extmarks + +Execute (Toggle rainbow delimiters off): + call rainbow_delimiters#toggle(0) + redraw! " Without this there will be no extmarks at all + +Then (Rainbow delimiters are inactive): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertEqual [], extmarks + +Execute (Toggle rainbow delimiters back on): + call rainbow_delimiters#toggle(0) + redraw! " Without this there will be no extmarks at all + +Then (Rainbow delimiters are active): + let extmarks = luaeval('vim.inspect_pos(0, 0, 5)').extmarks + \->map('v:val["ns_id"]') + \->filter({_, nsid -> nsid == luaeval('require("rainbow-delimiters.lib").nsids.lua')}) + AssertNotEqual [], extmarks + + +# vim:tw=79:ts=2:sw=2:et: