Implement theme switcher

using telescope picker

ask if want to change default theme, change value in user_config.lua

load it as a telescope extension

live preview of themes

Co-authored-by: Galen Rowell <growell3@gmail.com>
This commit is contained in:
Akianonymus 2021-07-30 22:41:56 +05:30 committed by Galen Rowell
parent 7affb8cbfb
commit a6ab121d12
4 changed files with 286 additions and 2 deletions

View file

@ -67,6 +67,9 @@ telescope.setup(
} }
) )
-- load the theme_switcher extension
require("telescope").load_extension("themes")
if if
not pcall( not pcall(
function() function()

View file

@ -0,0 +1,185 @@
-- This file can be loaded as a telescope extension
local M = {}
-- 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
error("No such theme ( " .. theme_name .. " )")
end
vim.g.nvchad_theme = theme_name
-- reload the base16 theme
local ok, base16 = pcall(require, "base16")
if not ok then
error("Error: Cannot load base16 plugin!")
end
base16(base16.themes[theme_name], true)
reload_plugin {
"highlights",
"plugins.bufferline",
"galaxyline",
"plugins.statusline"
}
-- now send the provider info to actual refresh
require("galaxyline.provider").async_load_providers:send()
return true
-- open a buffer and close it to reload the statusline
-- vim.cmd("new|bwipeout")
-- commented out here as it will not work with telescope picker
end
-- Custom theme picker
-- Most of the code is copied from telescope colorscheme plugin, mostly for preview creation
M.theme_switcher = function(opts)
local pickers, finders, previewers, actions, action_state, utils, conf
if pcall(require, "telescope") then
pickers = require "telescope.pickers"
finders = require "telescope.finders"
previewers = require "telescope.previewers"
actions = require "telescope.actions"
action_state = require "telescope.actions.state"
utils = require "telescope.utils"
conf = require("telescope.config").values
else
error "Cannot find telescope!"
end
local local_utils = require "utils"
local reload_theme = M.reload_theme
-- get a table of available themes
local themes = local_utils.list_themes()
if next(themes) ~= nil then
-- save this to use it for later to restore if theme not changed
local current_theme = vim.g.nvchad_theme
local new_theme = ""
local change = false
-- buffer number and name
local bufnr = vim.api.nvim_get_current_buf()
local bufname = vim.api.nvim_buf_get_name(bufnr)
local previewer
-- in case its not a normal buffer
if vim.fn.buflisted(bufnr) ~= 1 then
local deleted = false
local function del_win(win_id)
if win_id and vim.api.nvim_win_is_valid(win_id) then
utils.buf_delete(vim.api.nvim_win_get_buf(win_id))
pcall(vim.api.nvim_win_close, win_id, true)
end
end
previewer =
previewers.new {
preview_fn = function(_, entry, status)
if not deleted then
deleted = true
del_win(status.preview_win)
del_win(status.preview_border_win)
end
reload_theme(entry.value)
end
}
else
-- show current buffer content in previewer
previewer =
previewers.new_buffer_previewer {
get_buffer_by_name = function()
return bufname
end,
define_preview = function(self, entry)
if vim.loop.fs_stat(bufname) then
conf.buffer_previewer_maker(bufname, self.state.bufnr, {bufname = self.state.bufname})
else
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines)
end
reload_theme(entry.value)
end
}
end
local picker =
pickers.new(
{
prompt_title = "Set NvChad color",
finder = finders.new_table(themes),
previewer = previewer,
sorter = conf.generic_sorter(opts),
attach_mappings = function()
actions.select_default:replace(
-- if a entry is selected, change current_theme to that
function(prompt_bufnr)
local selection = action_state.get_selected_entry()
new_theme = selection.value
change = true
actions.close(prompt_bufnr)
end
)
return true
end
}
)
-- rewrite picker.close_windows
local close_windows = picker.close_windows
picker.close_windows = function(status)
close_windows(status)
-- now apply the theme, if success, then ask for default theme change
local final_theme
if change then
final_theme = new_theme
else
final_theme = current_theme
end
if reload_theme(final_theme) then
if change then
-- ask for confirmation to set as default theme
local ans = string.lower(vim.fn.input("Set " .. new_theme .. " as default theme ? [y/N] ")) == "y"
if ans then
local_utils.change_theme(current_theme, final_theme)
else
-- will be used in restoring nvchad theme var
final_theme = current_theme
end
end
-- open a buffer and close it to reload the statusline
vim.cmd("new|bwipeout")
end
-- set nvchad_theme global var
vim.g.nvchad_theme = current_theme
end
-- launch the telescope picker
picker:find()
else
print("No themes found in " .. themes_folder)
end
end
-- register theme swticher as themes to telescope
local present, telescope = pcall(require, "telescope")
if present then
return telescope.register_extension {
exports = {
themes = M.theme_switcher
}
}
else
error "Cannot find telescope!"
end

View file

@ -1,9 +1,9 @@
local chad_theme = require("user_config").ui.theme local chad_theme = require("user_config").ui.theme
vim.g.nvchad_theme = chad_theme vim.g.nvchad_theme = chad_theme
local present2, base16 = pcall(require, "base16") local present, base16 = pcall(require, "base16")
if present2 then if present then
base16(base16.themes[chad_theme], true) base16(base16.themes[chad_theme], true)
require "highlights" require "highlights"
return true return true

96
lua/utils.lua Normal file
View file

@ -0,0 +1,96 @@
local M = {}
-- reload a plugin ( will try to load even if not loaded)
-- can take a string or list ( table )
M.reload_plugin = function(plugins)
local function _reload_plugin(plugin)
local loaded = package.loaded[plugin]
if loaded then
package.loaded[plugin] = nil
end
if not pcall(require, plugin) then
error("Error: Cannot load " .. plugin .. " plugin!")
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
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
-- 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
-- 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
error "Provide current and new theme name"
end
if current_theme == new_theme then
return
end
local file = vim.fn.stdpath("config") .. "/lua/user_config.lua"
-- store in data variable
local data = assert(M.file("r", file))
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
error("Cannot change default theme with " .. new_theme .. ", edit " .. file .. " manually")
else
assert(M.file("w", file, content))
end
end
return M