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:
		
							parent
							
								
									7affb8cbfb
								
							
						
					
					
						commit
						a6ab121d12
					
				| @ -67,6 +67,9 @@ telescope.setup( | ||||
|     } | ||||
| ) | ||||
| 
 | ||||
| -- load the theme_switcher extension | ||||
| require("telescope").load_extension("themes") | ||||
| 
 | ||||
| if | ||||
|     not pcall( | ||||
|         function() | ||||
|  | ||||
							
								
								
									
										185
									
								
								lua/telescope/_extensions/themes.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								lua/telescope/_extensions/themes.lua
									
									
									
									
									
										Normal 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 | ||||
| @ -1,9 +1,9 @@ | ||||
| local chad_theme = require("user_config").ui.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) | ||||
|     require "highlights" | ||||
|     return true | ||||
|  | ||||
							
								
								
									
										96
									
								
								lua/utils.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								lua/utils.lua
									
									
									
									
									
										Normal 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 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user