local M = {} M.memo = setmetatable( { put = function(cache, params, result) local node = cache for i = 1, #params do local param = vim.inspect(params[i]) node.children = node.children or {} node.children[param] = node.children[param] or {} node = node.children[param] end node.result = result end, get = function(cache, params) local node = cache for i = 1, #params do local param = vim.inspect(params[i]) node = node.children and node.children[param] if not node then return nil end end return node.result end }, { __call = function(memo, func) local cache = {} return function(...) local params = {...} local result = memo.get(cache, params) if not result then result = {func(...)} memo.put(cache, params, result) end return unpack(result) end end } ) M.error_handler = function(err) if vim.g.indent_blankline_debug then vim.cmd("echohl Error") vim.cmd('echomsg "' .. err .. '"') vim.cmd("echohl None") end end M.is_indent_blankline_enabled = M.memo( function( b_enabled, g_enabled, filetype, filetype_include, filetype_exclude, buftype, buftype_exclude, bufname_exclude, bufname) if b_enabled ~= nil then return b_enabled end if g_enabled == 0 then return false end for _, ft in ipairs(filetype_exclude) do if ft == filetype then return false end end for _, bt in ipairs(buftype_exclude) do if bt == buftype then return false end end for _, bn in ipairs(bufname_exclude) do if vim.fn["matchstr"](bufname, bn) == bufname then return false end end if #filetype_include > 0 then for _, ft in ipairs(filetype_include) do if ft == filetype then return true end end return false end return true end ) M.clear_line_indent = function(buf, lnum) xpcall(vim.api.nvim_buf_clear_namespace, M.error_handler, buf, vim.g.indent_blankline_namespace, lnum - 1, lnum) end M.get_from_list = function(list, i) return list[((i - 1) % #list) + 1] end M._if = function(bool, a, b) if bool then return a else return b end end M.find_indent = function(line, shiftwidth, strict_tabs) local indent = 0 local spaces = 0 for ch in line:gmatch(".") do if ch == " " then if strict_tabs and indent == 0 and spaces ~= 0 then return 0, false end indent = indent + math.floor(spaces / shiftwidth) + 1 spaces = 0 elseif ch == " " then if strict_tabs and indent ~= 0 then return indent, true end spaces = spaces + 1 else break end end indent = indent + math.floor(spaces / shiftwidth) return indent, spaces % shiftwidth ~= 0 end M.get_current_context = function(type_patterns) local ts_utils = require "nvim-treesitter.ts_utils" local cursor_node = ts_utils.get_node_at_cursor() while cursor_node do local node_type = cursor_node:type() for _, rgx in ipairs(type_patterns) do if node_type:find(rgx) then local node_start, _, node_end, _ = cursor_node:range() if node_start ~= node_end then return true, node_start + 1, node_end + 1 end node_start, node_end = nil, nil end end cursor_node = cursor_node:parent() end return false end M.reset_highlights = function() local whitespace_highlight = vim.fn.synIDtrans(vim.fn.hlID("Whitespace")) local label_highlight = vim.fn.synIDtrans(vim.fn.hlID("Label")) local whitespace_fg = { vim.fn.synIDattr(whitespace_highlight, "fg", "gui"), vim.fn.synIDattr(whitespace_highlight, "fg", "cterm") } local label_fg = { vim.fn.synIDattr(label_highlight, "fg", "gui"), vim.fn.synIDattr(label_highlight, "fg", "cterm") } for highlight_name, highlight in pairs( { IndentBlanklineChar = whitespace_fg, IndentBlanklineSpaceChar = whitespace_fg, IndentBlanklineSpaceCharBlankline = whitespace_fg, IndentBlanklineContextChar = label_fg } ) do local current_highlight = vim.fn.synIDtrans(vim.fn.hlID(highlight_name)) if vim.fn.synIDattr(current_highlight, "fg") == "" and vim.fn.synIDattr(current_highlight, "bg") == "" then vim.cmd( string.format( "highlight %s guifg=%s ctermfg=%s gui=nocombine cterm=nocombine", highlight_name, M._if(highlight[1] == "", "NONE", highlight[1]), M._if(highlight[2] == "", "NONE", highlight[2]) ) ) end end end return M