local M = {} local function printf(...) print(string.format(...)) end local sprintf = string.format local function cmdf(...) vim.cmd(sprintf(...)) end local fn, api = vim.fn, vim.api M.printf = printf M.sprintf = sprintf M.cmdf = cmdf function M.get_cursor_pos() return {fn.line('.'), fn.col('.')} end function M.debounce(func, timeout) local timer_id return function(...) if timer_id ~= nil then fn.timer_stop(timer_id) end local args = {...} local function cb() func(args) timer_id = nil end timer_id = fn.timer_start(timeout, cb) end end -- FIXME function M.throttle(func, timeout) local timer_id local did_call = false return function(...) local args = {...} if timer_id == nil then func(unpack(args)) local function cb() timer_id = nil if did_call then func(unpack(args)) did_call = false end end timer_id = fn.timer_start(timeout, cb) else did_call = true end end end function M.run_colorscheme(colorscheme,callback) local ok,err = pcall(function() vim.cmd("colorscheme " .. colorscheme) callback() end) if (not ok) then vim.notify(err) end end -- Convert UTF-8 hex code to character function M.u(code) if type(code) == 'string' then code = tonumber('0x' .. code) end local c = string.char if code <= 0x7f then return c(code) end local t = {} if code <= 0x07ff then t[1] = c(bit.bor(0xc0, bit.rshift(code, 6))) t[2] = c(bit.bor(0x80, bit.band(code, 0x3f))) elseif code <= 0xffff then t[1] = c(bit.bor(0xe0, bit.rshift(code, 12))) t[2] = c(bit.bor(0x80, bit.band(bit.rshift(code, 6), 0x3f))) t[3] = c(bit.bor(0x80, bit.band(code, 0x3f))) else t[1] = c(bit.bor(0xf0, bit.rshift(code, 18))) t[2] = c(bit.bor(0x80, bit.band(bit.rshift(code, 12), 0x3f))) t[3] = c(bit.bor(0x80, bit.band(bit.rshift(code, 6), 0x3f))) t[4] = c(bit.bor(0x80, bit.band(code, 0x3f))) end return table.concat(t) end function _G.dump(...) local args = {...} if #args == 1 then print(vim.inspect(args[1])) else print(vim.inspect(args)) end end function M.load(path) local ok, mod = pcall(require, path) if not ok then printf('Error loading module `%s`', path) print(mod) else local loadfunc if mod == true then -- Module doesn't export anything return elseif type(mod) == "table" and mod.setup ~= nil then loadfunc = mod.setup elseif type(mod) == "function" then loadfunc = mod end local ok, err = pcall(loadfunc) if not ok then printf("Error loading module `%s`", path) print(err) end end end -- Get information about highlight group function M.hl_by_name(hl_group) local hl = api.nvim_get_hl_by_name(hl_group, true) if hl.foreground ~= nil then hl.fg = sprintf('#%x', hl.foreground) end if hl.background ~= nil then hl.bg = sprintf('#%x', hl.background) end return hl end function M.isempty(s) return s == nil or s == "" end function M.get_buf_option(opt) local status_ok, buf_option = pcall(vim.api.nvim_buf_get_option, 0, opt) if not status_ok then return nil else return buf_option end end -- Define a new highlight group -- TODO: rewrite to `nvim_set_hl()` when API will be stable function M.highlight(cfg) local command = "highlight" if cfg.bang == true then command = command .. '!' end if #cfg == 2 and type(cfg[1]) == 'string' and type(cfg[2]) == 'string' then -- :highlight link vim.cmd(command.." link "..cfg[1].." "..cfg[2]) return end local guifg = cfg.fg or cfg.guifg local guibg = cfg.bg or cfg.guibg local gui = cfg.gui local guisp = cfg.guisp if type(cfg.override) == 'string' then local existing = api.nvim_get_hl_by_name(cfg.override, true) if existing.foreground ~= nil then guifg = sprintf('#%x', existing.foreground) end if existing.background ~= nil then guibg = sprintf('#%x', existing.background) end if existing.special ~= nil then guibg = sprintf('#%x', existing.background) end if existing.undercurl == true then gui = "undercurl" elseif existing.underline == true then gui = "underline" end end command = command .. ' ' .. cfg[1] if guifg ~= nil then command = command .. ' guifg=' .. guifg end if guibg ~= nil then command = command .. ' guibg=' .. guibg end if gui ~= nil then command = command .. ' gui=' .. gui end if guisp ~= nil then command = command .. ' guisp=' .. guisp end vim.cmd(command) end local autocmd_fn_index = 0 -- WIP: function M.autocmd(event_name, pattern, callback) local fn_name = 'lua_autocmd' .. autocmd_fn_index autocmd_fn_index = autocmd_fn_index + 1 _G[fn_name] = callback cmdf('autocmd %s %s call v:lua.%s()', event_name, pattern, fn_name) end function M.glob_exists(path) return fn.empty(fn.glob(path)) == 0 end do local show_diagnostics = vim.lsp.diagnostic.show_line_diagnostics local cursor_pos = M.get_cursor_pos() local debounced = M.debounce(show_diagnostics, 300) M.show_lsp_diagnostics = function() local cursor_pos2 = M.get_cursor_pos() -- TODO: doesn't work when both diagnostics and popup is shown if cursor_pos[1] ~= cursor_pos2[1] and cursor_pos[2] ~= cursor_pos2[2] then cursor_pos = cursor_pos2 debounced() end end end function M.id_generator(start) local cnt = start or 0 return function() local result = cnt cnt = cnt + 1 return result end end do local map_func_counter = 0 function M.map(mode, lhs, fn, opts) local name = 'map_func_' .. map_func_counter _G[name] = fn local rhs = ':call v:lua.' .. name .. '()' api.nvim_set_keymap(mode, lhs, rhs, opts) map_func_counter = map_func_counter + 1 end end for _, mode in ipairs {'', 'n', 'i', 'c', 'x'} do M[mode .. 'noremap'] = function(lhs, fn, opts) local mapopts = opts or {} mapopts.noremap = true return M.map(mode, lhs, fn, mapopts) end end function M.log_time(fn, label) return function(...) local now = os.clock() fn(...) print( ((label and (label .. ' ')) or '') .. (math.floor((os.clock() - now) * 1e6) / 1000) .. "ms." ) end end -- TODO: fix function function M.require_mod(modname) local ok,mod = pcall(require,modname) local notify_ok,notify = pcall(require,"notify") if (not ok) then local errmsg = "Failed to load " .. modname if (notify_ok) then notify( errmsg, "error", { title = "require" } ) else vim.notify(errmsg) end return end return mod end function M.index_of(t, v, eqfn) eqfn = eqfn or (function(el) return el == v end) for i, value in ipairs(t) do if eqfn(value, v) then return i end end return -1 end local globalfn_counter = 0 function M.defglobalfn(func) assert(type(func) == "function") local name = "_lua_fn_" .. globalfn_counter _G[name] = func globalfn_counter = globalfn_counter + 1 return name end return M