A first working, publishable version of colorsaver.nvim. This plugin connects to the ColorScheme event to detect changes to the colorscheme. Any time that changes, it saves the colorscheme's name to a local file. In addition to that, it can watch that local file for changes. This way, when one `nvim` process changes the colorscheme, all other instances will automatically reload the colorscheme.
220 lines
6.7 KiB
Lua
220 lines
6.7 KiB
Lua
local M = {}
|
|
|
|
-- load modules
|
|
local autocommands = require("colorsaver.autocommands")
|
|
local config = require("colorsaver.config")
|
|
local file_watcher = require("colorsaver.file_watcher")
|
|
local logger = require("colorsaver.logger")
|
|
local utils = require("colorsaver.utils")
|
|
|
|
-- our module local data (and these are just placeholder values, they're overwritten below)
|
|
local autocmd_group = nil
|
|
local color_file_path = vim.fn.stdpath("data") .. "/colorsaver"
|
|
local colorfile_watcher = nil
|
|
local log = logger.new("warn", "colorsaver")
|
|
|
|
--- save_colorscheme(new_scheme)
|
|
--
|
|
-- This function saves a new colorscheme to a file. If no new_scheme is provided, it saves the default colorscheme.
|
|
--
|
|
-- Parameters:
|
|
-- - new_scheme (string): The name of the new colorscheme to save.
|
|
--
|
|
-- Returns:
|
|
-- - success (boolean): true if the colorscheme was saved successfully, false otherwise.
|
|
--
|
|
-- Raises:
|
|
-- - None
|
|
--
|
|
-- Example usage:
|
|
-- save_colorscheme("my_colorscheme")
|
|
--
|
|
local function save_colorscheme(new_scheme)
|
|
local colorscheme_name = new_scheme
|
|
if not vim.tbl_contains(vim.fn.getcompletion("", "color"), colorscheme_name) then
|
|
log:warn("Didn't find " .. colorscheme_name .. " in the list of colorschemes")
|
|
return false
|
|
end
|
|
|
|
local file, err = io.open(color_file_path, "w")
|
|
if not file then
|
|
log:error("Failed to open " .. color_file_path .. " for writing: " .. err)
|
|
return false
|
|
end
|
|
|
|
local success, write_err = pcall(function()
|
|
file:write(colorscheme_name)
|
|
end)
|
|
|
|
local close_ok, close_err = pcall(function()
|
|
file:close()
|
|
end)
|
|
|
|
if not success then
|
|
log:error("Failed to write colorscheme to " .. color_file_path .. ": " .. write_err)
|
|
return false
|
|
elseif not close_ok then
|
|
log:error("Failed to close the file " .. color_file_path .. ": " .. close_err)
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--- load_colorscheme()
|
|
--
|
|
-- This function loads a colorscheme for the current session in Neovim. It first
|
|
-- attempts to read the colorscheme from a specified file path. If the file exists
|
|
-- and is not empty, the content of the file is used as the colorscheme.
|
|
--
|
|
-- Parameters:
|
|
-- None
|
|
--
|
|
-- Returns:
|
|
-- success (boolean): true if the colorscheme was loaded successfully, false otherwise.
|
|
--
|
|
-- Raises:
|
|
-- None
|
|
--
|
|
-- Example usage:
|
|
-- load_colorscheme()
|
|
--
|
|
local function load_colorscheme()
|
|
local colorscheme = M.default -- Fallback to default colorscheme
|
|
|
|
local file, err = io.open(color_file_path, "r")
|
|
if not file then
|
|
log:warn("Failed to open " .. color_file_path .. " for reading: " .. err)
|
|
return false
|
|
end
|
|
|
|
local content, read_err = file:read("*a")
|
|
if read_err then
|
|
log:error("Failed to read from " .. color_file_path .. ": " .. read_err)
|
|
file:close()
|
|
return false
|
|
end
|
|
|
|
local close_ok, close_err = file:close()
|
|
if not close_ok then
|
|
log:error("Failed to close " .. color_file_path .. ": " .. close_err)
|
|
return false
|
|
end
|
|
|
|
colorscheme = content ~= "" and content or colorscheme
|
|
|
|
if not (colorscheme and colorscheme ~= "") then
|
|
log:error("Invalid colorscheme name found in " .. color_file_path .. ".")
|
|
return false
|
|
end
|
|
|
|
-- disable ColorScheme autocmd so we don't get into a bad loop; this is
|
|
-- because we're about to call `colorscheme` which will trigger ColorScheme
|
|
-- and cause us to write the file again, thus retriggering the
|
|
-- load_colorscheme function since the file changed... bad loop
|
|
if autocmd_group then
|
|
autocmd_group:clear()
|
|
end
|
|
|
|
log:info("Switching to '" .. colorscheme .. "'")
|
|
local ok, cmd_err = pcall(vim.api.nvim_command, "colorscheme " .. colorscheme)
|
|
if not ok then
|
|
log:error("Failed to apply colorscheme '" .. colorscheme .. "': " .. cmd_err)
|
|
return false
|
|
end
|
|
|
|
-- and re-enable the autocommands
|
|
if autocmd_group then
|
|
autocmd_group:restore()
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--- Callback function for file change events.
|
|
--
|
|
-- This function is called when a file change event is triggered. It handles
|
|
-- the event by reloading the colorscheme from the updated file. If there is an
|
|
-- error, it logs the error message and stops the event.
|
|
--
|
|
-- Parameters:
|
|
-- - err (string|nil) The error message, if any. If there is no error, this parameter is nil.
|
|
-- - filename (string) The name of the file that triggered the event.
|
|
-- - events (table) A table containing information about the events that occurred.
|
|
--
|
|
-- Returns:
|
|
-- None
|
|
--
|
|
-- Raises:
|
|
-- None
|
|
--
|
|
local function on_change(err, filename)
|
|
vim.schedule(function()
|
|
if err then
|
|
-- If there's an error, notify and stop the event
|
|
log:error("Error watching file " .. tostring(filename) .. ": " .. tostring(err))
|
|
return
|
|
end
|
|
|
|
-- Reload the colorscheme from the updated file
|
|
load_colorscheme()
|
|
end)
|
|
end
|
|
|
|
--- Sets up the ColorSaver module with the provided options.
|
|
--
|
|
-- Parameters:
|
|
-- - opts (table): A table containing the options for configuring the ColorSaver module. The following keys are supported:
|
|
-- - filename (string): The name of the file where the colorscheme will be saved.
|
|
-- - log_level (string): The log level for the ColorSaver module.
|
|
-- - debounce_ms (number): The debounce time in milliseconds for saving the colorscheme.
|
|
-- - auto_load (boolean): Whether to enable autoloading of the colorscheme file.
|
|
--
|
|
-- Returns:
|
|
-- None
|
|
--
|
|
-- Raises:
|
|
-- None
|
|
--
|
|
-- This function sets up the ColorSaver module by performing the following steps:
|
|
-- 1. Extracts and validates the options provided.
|
|
-- 2. Sets up local data, including the file path for saving the colorscheme and a logger instance.
|
|
-- 3. Loads the colorscheme at startup.
|
|
-- 4. Captures the new colorscheme and saves it on the "ColorScheme" event.
|
|
-- 5. Sets up autoloading of the colorscheme file if enabled.
|
|
--
|
|
function M.setup(opts)
|
|
-- Extract options and validate them
|
|
M.config = config.setup(opts, log)
|
|
|
|
-- Set up some local data
|
|
color_file_path = vim.fn.stdpath("data") .. "/" .. M.config.filename
|
|
log = logger.new(M.config.log_level, "colorsaver")
|
|
|
|
-- Load the colorscheme at startup.
|
|
load_colorscheme()
|
|
|
|
-- Capture the new colorscheme and save it on the ColorScheme event.
|
|
local debounced_save_colorscheme = utils.debounce(save_colorscheme, M.config.debounce_ms)
|
|
autocmd_group = autocommands.new("ColorSaverGroup", {
|
|
{
|
|
events = { "ColorScheme" },
|
|
patterns = { "*" },
|
|
callback = function(info)
|
|
vim.schedule(function()
|
|
debounced_save_colorscheme(info.match) -- info.match is the colorscheme parameter (i.e. name)
|
|
end)
|
|
end,
|
|
},
|
|
})
|
|
|
|
-- Setup autoloading if enabled
|
|
if M.config.auto_load then
|
|
local debounced_on_change = utils.debounce(on_change, M.config.debounce_ms)
|
|
colorfile_watcher = file_watcher.new(color_file_path, debounced_on_change)
|
|
colorfile_watcher:start()
|
|
end
|
|
end
|
|
|
|
return M
|