nvchad/lua/utils.lua

319 lines
10 KiB
Lua

local M = {}
-- 1st arg as current theme, 2nd as new theme
M.change_theme = function(current_theme, new_theme)
if current_theme == nil or new_theme == nil then
print "Error: Provide current and new theme name"
return false
end
if current_theme == new_theme then
return
end
local file = vim.fn.stdpath "config" .. "/lua/chadrc.lua"
-- store in data variable
local data = assert(M.file("r", file))
-- escape characters which can be parsed as magic chars
current_theme = current_theme:gsub("%p", "%%%0")
new_theme = new_theme:gsub("%p", "%%%0")
local find = "theme = .?" .. current_theme .. ".?"
local replace = 'theme = "' .. new_theme .. '"'
local content = string.gsub(data, find, replace)
-- see if the find string exists in file
if content == data then
print("Error: Cannot change default theme with " .. new_theme .. ", edit " .. file .. " manually")
return false
else
assert(M.file("w", file, content))
end
end
M.clear_cmdline = function()
vim.defer_fn(function()
vim.cmd "echo"
end, 0)
end
M.close_buffer = function(bufexpr, force)
-- This is a modification of a NeoVim plugin from
-- Author: ojroques - Olivier Roques
-- Src: https://github.com/ojroques/nvim-bufdel
-- (Author has okayed copy-paste)
-- Options
local opts = {
next = 'cycle', -- how to retrieve the next buffer
quit = false, -- exit when last buffer is deleted
--TODO make this a chadrc flag/option
}
-- ----------------
-- Helper functions
-- ----------------
-- Switch to buffer 'buf' on each window from list 'windows'
local function switch_buffer(windows, buf)
local cur_win = vim.fn.winnr()
for _, winid in ipairs(windows) do
vim.cmd(string.format('%d wincmd w', vim.fn.win_id2win(winid)))
vim.cmd(string.format('buffer %d', buf))
end
vim.cmd(string.format('%d wincmd w', cur_win)) -- return to original window
end
-- Select the first buffer with a number greater than given buffer
local function get_next_buf(buf)
local next = vim.fn.bufnr('#')
if opts.next == 'alternate' and vim.fn.buflisted(next) == 1 then
return next
end
for i = 0, vim.fn.bufnr('$') - 1 do
next = (buf + i) % vim.fn.bufnr('$') + 1 -- will loop back to 1
if vim.fn.buflisted(next) == 1 then
return next
end
end
end
-- ----------------
-- End helper functions
-- ----------------
local buf = vim.fn.bufnr()
if vim.fn.buflisted(buf) == 0 then -- exit if buffer number is invalid
return
end
if #vim.fn.getbufinfo({buflisted = 1}) < 2 then
if opts.quit then
-- exit when there is only one buffer left
if force then
vim.cmd('qall!')
else
vim.cmd('confirm qall')
end
return
end
local chad_term, type = pcall(function()
return vim.api.nvim_buf_get_var(buf, "term_type")
end)
if chad_term then
-- Must be a window type
vim.cmd(string.format('setlocal nobl', buf))
vim.cmd('enew')
return
end
-- don't exit and create a new empty buffer
vim.cmd('enew')
vim.cmd('bp')
end
local next_buf = get_next_buf(buf)
local windows = vim.fn.getbufinfo(buf)[1].windows
-- force deletion of terminal buffers to avoid the prompt
if force or vim.fn.getbufvar(buf, '&buftype') == 'terminal' then
local chad_term, type = pcall(function()
return vim.api.nvim_buf_get_var(buf, "term_type")
end)
-- TODO this scope is error prone, make resilient
if chad_term then
if type == "wind" then
-- hide from bufferline
vim.cmd(string.format('%d bufdo setlocal nobl', buf))
-- swtich to another buff
-- TODO switch to next bufffer, this works too
vim.cmd('BufferLineCycleNext')
else
local cur_win = vim.fn.winnr()
-- we can close this window
vim.cmd(string.format('%d wincmd c', cur_win))
return
end
else
switch_buffer(windows, next_buf)
vim.cmd(string.format('bd! %d', buf))
end
else
switch_buffer(windows, next_buf)
vim.cmd(string.format('silent! confirm bd %d', buf))
end
-- revert buffer switches if user has canceled deletion
if vim.fn.buflisted(buf) == 1 then
switch_buffer(windows, buf)
end
end
-- 1st arg - r or w
-- 2nd arg - file path
-- 3rd arg - content if 1st arg is w
-- return file data on read, nothing on write
M.file = function(mode, filepath, content)
local data
local fd = assert(vim.loop.fs_open(filepath, mode, 438))
local stat = assert(vim.loop.fs_fstat(fd))
if stat.type ~= "file" then
data = false
else
if mode == "r" then
data = assert(vim.loop.fs_read(fd, stat.size, 0))
else
assert(vim.loop.fs_write(fd, content, 0))
data = true
end
end
assert(vim.loop.fs_close(fd))
return data
end
-- return a table of available themes
M.list_themes = function(return_type)
local themes = {}
-- folder where theme files are stored
local themes_folder = vim.fn.stdpath "config" .. "/lua/themes"
-- list all the contents of the folder and filter out files with .lua extension, then append to themes table
local fd = vim.loop.fs_scandir(themes_folder)
if fd then
while true do
local name, typ = vim.loop.fs_scandir_next(fd)
if name == nil then
break
end
if typ ~= "directory" and string.find(name, ".lua") then
-- return the table values as keys if specified
if return_type == "keys_as_value" then
themes[vim.fn.fnamemodify(name, ":r")] = true
else
table.insert(themes, vim.fn.fnamemodify(name, ":r"))
end
end
end
end
return themes
end
-- reload a plugin ( will try to load even if not loaded)
-- can take a string or list ( table )
-- return true or false
M.reload_plugin = function(plugins)
local status = true
local function _reload_plugin(plugin)
local loaded = package.loaded[plugin]
if loaded then
package.loaded[plugin] = nil
end
if not pcall(require, plugin) then
print("Error: Cannot load " .. plugin .. " plugin!")
status = false
end
end
if type(plugins) == "string" then
_reload_plugin(plugins)
elseif type(plugins) == "table" then
for _, plugin in ipairs(plugins) do
_reload_plugin(plugin)
end
end
return status
end
-- reload themes without restarting vim
-- if no theme name given then reload the current theme
M.reload_theme = function(theme_name)
local reload_plugin = require("utils").reload_plugin
-- if theme name is empty or nil, then reload the current theme
if theme_name == nil or theme_name == "" then
theme_name = vim.g.nvchad_theme
end
if not pcall(require, "themes/" .. theme_name) then
print("No such theme ( " .. theme_name .. " )")
return false
end
vim.g.nvchad_theme = theme_name
-- reload the base16 theme
local ok, base16 = pcall(require, "base16")
if not ok then
print "Error: Cannot load base16 plugin!"
return false
end
base16(base16.themes(theme_name), true)
if
not reload_plugin {
"highlights",
"plugins.bufferline",
"galaxyline",
"plugins.statusline",
}
then
print "Error: Not able to reload all plugins."
return false
end
-- yes, this is very hacky, but due to new_async in
-- https://github.com/glepnir/galaxyline.nvim/blob/main/lua/galaxyline/provider.lua#L5-L36
-- it doesn't work properly and some statusline stuff dissapears
local vcs = require "galaxyline.provider_vcs"
local fileinfo = require "galaxyline.provider_fileinfo"
local buffer = require "galaxyline.provider_buffer"
local extension = require "galaxyline.provider_extensions"
local whitespace = require "galaxyline.provider_whitespace"
local lspclient = require "galaxyline.provider_lsp"
_G.galaxyline_providers = {
BufferIcon = buffer.get_buffer_type_icon,
BufferNumber = buffer.get_buffer_number,
FileTypeName = buffer.get_buffer_filetype,
GitBranch = vcs.get_git_branch,
DiffAdd = vcs.diff_add,
DiffModified = vcs.diff_modified,
DiffRemove = vcs.diff_remove,
LineColumn = fileinfo.line_column,
FileFormat = fileinfo.get_file_format,
FileEncode = fileinfo.get_file_encode,
FileSize = fileinfo.get_file_size,
FileIcon = fileinfo.get_file_icon,
FileName = fileinfo.get_current_file_name,
SFileName = fileinfo.filename_in_special_buffer,
LinePercent = fileinfo.current_line_percent,
ScrollBar = extension.scrollbar_instance,
VistaPlugin = extension.vista_nearest,
WhiteSpace = whitespace.get_item,
GetLspClient = lspclient.get_lsp_client,
}
local diagnostic = require "galaxyline.provider_diagnostic"
_G.galaxyline_providers.DiagnosticError = diagnostic.get_diagnostic_error
_G.galaxyline_providers.DiagnosticWarn = diagnostic.get_diagnostic_warn
_G.galaxyline_providers.DiagnosticHint = diagnostic.get_diagnostic_hint
_G.galaxyline_providers.DiagnosticInfo = diagnostic.get_diagnostic_info
return true
end
-- toggle between 2 themes
-- argument should be a table with 2 theme names
M.toggle_theme = function(themes)
local current_theme = vim.g.current_nvchad_theme or vim.g.nvchad_theme
for _, name in ipairs(themes) do
if name ~= current_theme then
if require("utils").reload_theme(name) then
-- open a buffer and close it to reload the statusline
vim.cmd "new|bwipeout"
vim.g.current_nvchad_theme = name
if M.change_theme(vim.g.nvchad_theme, name) then
vim.g.nvchad_theme = name
end
end
end
end
end
return M