mirror of
https://github.com/zoriya/auto-save.nvim.git
synced 2026-06-02 02:35:01 +00:00
BREAKING CHANGES: plugin rewrite
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
local config = {}
|
||||
|
||||
config.options = {
|
||||
enabled = true, -- start auto-save when the plugin is loaded (i.e. when your package manager loads it)
|
||||
execution_message = {
|
||||
message = function() -- message to print on save
|
||||
return ("AutoSave: saved at " .. vim.fn.strftime("%H:%M:%S"))
|
||||
end,
|
||||
dim = 0.18, -- dim the color of `message`
|
||||
cleaning_interval = 4000, -- (milliseconds) automatically clean MsgArea after displaying `message`. See :h MsgArea
|
||||
},
|
||||
trigger_events = {"InsertLeave", "TextChanged"}, -- vim events that trigger auto-save. See :h events
|
||||
-- function that determines whether to save the current buffer or not
|
||||
-- return true: if buffer is ok to be saved
|
||||
-- return false: if it's not ok to be saved
|
||||
condition = function(buf)
|
||||
local fn = vim.fn
|
||||
local utils = require("auto-save.utils.data")
|
||||
|
||||
if
|
||||
fn.getbufvar(buf, "&modifiable") == 1 or
|
||||
utils.not_in(fn.getbufvar(buf, "&filetype"), {}) then
|
||||
return true -- met condition(s), can save
|
||||
end
|
||||
return false -- can't save
|
||||
end,
|
||||
write_all_buffers = false, -- write all buffers when the current one meets `condition`
|
||||
debounce_delay = 135, -- saves the file at most every `debounce_delay` milliseconds
|
||||
callbacks = { -- functions to be executed at different intervals
|
||||
enabling = nil, -- ran when enabling auto-save
|
||||
disabling = nil, -- ran when disabling auto-save
|
||||
before_asserting_save = nil, -- ran before checking `condition`
|
||||
before_saving = nil, -- ran before doing the actual save
|
||||
after_saving = nil -- ran after doing the actual save
|
||||
}
|
||||
}
|
||||
|
||||
function config.set_options(opts)
|
||||
opts = opts or {}
|
||||
config.options = vim.tbl_deep_extend("keep", opts, config.options)
|
||||
end
|
||||
|
||||
return config
|
||||
@@ -0,0 +1,149 @@
|
||||
local M = {}
|
||||
|
||||
local cnf = require("auto-save.config").options
|
||||
local callback = require("auto-save.utils.data").do_callback
|
||||
local colors = require("auto-save.utils.colors")
|
||||
local echo = require("auto-save.utils.echo")
|
||||
local autosave_running
|
||||
local api = vim.api
|
||||
local g = vim.g
|
||||
local fn = vim.fn
|
||||
local cmd = vim.cmd
|
||||
local o = vim.o
|
||||
|
||||
api.nvim_create_augroup("AutoSave", {
|
||||
clear = true,
|
||||
})
|
||||
|
||||
local global_vars = {}
|
||||
|
||||
local function set_buf_var(buf, name, value)
|
||||
if buf == nil then
|
||||
global_vars[name] = value
|
||||
else
|
||||
api.nvim_buf_set_var(buf, 'autosave_' .. name, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_buf_var(buf, name)
|
||||
if buf == nil then
|
||||
return global_vars[name]
|
||||
end
|
||||
local success, mod = pcall(api.nvim_buf_get_var, buf, 'autosave_' .. name)
|
||||
return success and mod or nil
|
||||
end
|
||||
|
||||
local function debounce(lfn, duration)
|
||||
local function inner_debounce()
|
||||
local buf = api.nvim_get_current_buf()
|
||||
if not get_buf_var(buf, 'queued') then
|
||||
vim.defer_fn(function()
|
||||
set_buf_var(buf, 'queued', false)
|
||||
lfn(buf)
|
||||
end, duration)
|
||||
set_buf_var(buf, 'queued', true)
|
||||
end
|
||||
end
|
||||
|
||||
return inner_debounce
|
||||
end
|
||||
|
||||
function M.save(buf)
|
||||
buf = buf or api.nvim_get_current_buf()
|
||||
|
||||
callback("before_asserting_save")
|
||||
|
||||
if cnf.condition(buf) == false then
|
||||
return
|
||||
end
|
||||
|
||||
if not api.nvim_buf_get_option(buf, 'modified') then
|
||||
return
|
||||
end
|
||||
|
||||
callback("before_saving")
|
||||
|
||||
if g.auto_save_abort == true then
|
||||
return
|
||||
end
|
||||
|
||||
if cnf.write_all_buffers then
|
||||
cmd("silent! wall")
|
||||
else
|
||||
api.nvim_buf_call(buf, function () cmd("silent! write") end)
|
||||
end
|
||||
|
||||
callback("after_saving")
|
||||
|
||||
api.nvim_echo({ { (type(cnf.execution_message.message) == "function" and cnf.execution_message.message() or cnf.execution_message.message), 'AutoSaveText' } }, true, {})
|
||||
if cnf.execution_message.cleaning_interval > 0 then
|
||||
fn.timer_start(
|
||||
cnf.execution_message.cleaning_interval,
|
||||
function()
|
||||
cmd([[echon '']])
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local save_func = (cnf.debounce_delay > 0 and debounce(M.save, cnf.debounce_delay) or M.save)
|
||||
|
||||
local function perform_save()
|
||||
g.auto_save_abort = false
|
||||
save_func()
|
||||
end
|
||||
|
||||
function M.on()
|
||||
api.nvim_create_autocmd(cnf.trigger_events, {
|
||||
callback = function()
|
||||
perform_save()
|
||||
end,
|
||||
pattern = "*",
|
||||
group = "AutoSave",
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("VimEnter", {
|
||||
callback = function()
|
||||
if cnf.execution_message.dim > 0 then
|
||||
MSG_AREA = colors.get_hl("MsgArea")
|
||||
MSG_AREA.background = (MSG_AREA.background or colors.get_hl("Normal")["background"])
|
||||
local foreground = (
|
||||
o.background == "dark" and
|
||||
colors.darken((MSG_AREA.background or "#000000"), cnf.execution_message.dim, MSG_AREA.foreground) or
|
||||
colors.lighten((MSG_AREA.background or "#ffffff"), cnf.execution_message.dim, MSG_AREA.foreground)
|
||||
)
|
||||
|
||||
colors.highlight("AutoSaveText", { fg = foreground })
|
||||
end
|
||||
end,
|
||||
once = true,
|
||||
group = "AutoSave",
|
||||
})
|
||||
|
||||
callback("enabling")
|
||||
autosave_running = true
|
||||
end
|
||||
|
||||
function M.off()
|
||||
|
||||
api.nvim_create_augroup("AutoSave", {
|
||||
clear = true,
|
||||
})
|
||||
|
||||
callback("disabling")
|
||||
autosave_running = false
|
||||
end
|
||||
|
||||
function M.toggle()
|
||||
if autosave_running then
|
||||
M.off()
|
||||
else
|
||||
M.on()
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup(custom_opts)
|
||||
require("auto-save.config").set_options(custom_opts)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,70 @@
|
||||
local M = {}
|
||||
---@param hex_str string hexadecimal value of a color
|
||||
local hex_to_rgb = function(hex_str)
|
||||
local hex = "[abcdef0-9][abcdef0-9]"
|
||||
local pat = "^#(" .. hex .. ")(" .. hex .. ")(" .. hex .. ")$"
|
||||
hex_str = string.lower(hex_str)
|
||||
|
||||
assert(string.find(hex_str, pat) ~= nil, "hex_to_rgb: invalid hex_str: " .. tostring(hex_str))
|
||||
|
||||
local red, green, blue = string.match(hex_str, pat)
|
||||
return { tonumber(red, 16), tonumber(green, 16), tonumber(blue, 16) }
|
||||
end
|
||||
|
||||
function M.highlight(group, color, force)
|
||||
if color.link then
|
||||
vim.api.nvim_set_hl(0, group, {
|
||||
link = color.link,
|
||||
})
|
||||
else
|
||||
if color.style then
|
||||
for _, style in ipairs(color.style) do
|
||||
color[style] = true
|
||||
end
|
||||
end
|
||||
color.style = nil
|
||||
if force then
|
||||
vim.cmd("hi " .. group .. " guifg=" .. (color.fg or "NONE") .. " guibg=" .. (color.bg or "NONE"))
|
||||
return
|
||||
end
|
||||
vim.api.nvim_set_hl(0, group, color)
|
||||
end
|
||||
end
|
||||
|
||||
function M.get_hl(name)
|
||||
local ok, hl = pcall(vim.api.nvim_get_hl_by_name, name, true)
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
for _, key in pairs({ "foreground", "background", "special" }) do
|
||||
if hl[key] then
|
||||
hl[key] = string.format("#%06x", hl[key])
|
||||
end
|
||||
end
|
||||
return hl
|
||||
end
|
||||
|
||||
---@param fg string forecrust color
|
||||
---@param bg string background color
|
||||
---@param alpha number number between 0 and 1. 0 results in bg, 1 results in fg
|
||||
function M.blend(fg, bg, alpha)
|
||||
bg = hex_to_rgb(bg)
|
||||
fg = hex_to_rgb(fg)
|
||||
|
||||
local blendChannel = function(i)
|
||||
local ret = (alpha * fg[i] + ((1 - alpha) * bg[i]))
|
||||
return math.floor(math.min(math.max(0, ret), 255) + 0.5)
|
||||
end
|
||||
|
||||
return string.format("#%02X%02X%02X", blendChannel(1), blendChannel(2), blendChannel(3))
|
||||
end
|
||||
|
||||
function M.darken(hex, amount, bg)
|
||||
return M.blend(hex, bg or M.bg, math.abs(amount))
|
||||
end
|
||||
|
||||
function M.lighten(hex, amount, fg)
|
||||
return M.blend(hex, fg or M.fg, math.abs(amount))
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,25 @@
|
||||
local M = {}
|
||||
|
||||
local cnf = require("auto-save.config").options
|
||||
|
||||
function M.set_of(list)
|
||||
local set = {}
|
||||
for i = 1, #list do
|
||||
set[list[i]] = true
|
||||
end
|
||||
return set
|
||||
end
|
||||
|
||||
function M.not_in(var, arr)
|
||||
if M.set_of(arr)[var] == nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function M.do_callback(callback_name)
|
||||
if type(cnf.callbacks[callback_name]) == "function" then
|
||||
cnf.callbacks[callback_name]()
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,25 @@
|
||||
local TITLE = "auto-save"
|
||||
|
||||
return function(msg, kind)
|
||||
local has_notify_plugin = pcall(require, "notify")
|
||||
local level = {}
|
||||
|
||||
if kind == "error" then
|
||||
level.log = vim.log.levels.ERROR
|
||||
level.type = "error"
|
||||
elseif kind == "warn" then
|
||||
level.log = vim.log.levels.WARN
|
||||
level.type = "error"
|
||||
else
|
||||
level.log = kind or vim.log.levels.INFO
|
||||
level.type = "info"
|
||||
end
|
||||
|
||||
if has_notify_plugin then
|
||||
vim.notify(msg, level.log, {
|
||||
title = TITLE,
|
||||
})
|
||||
else
|
||||
vim.notify(("%s (%s): %s"):format(TITLE, level.type, msg), level.log)
|
||||
end
|
||||
end
|
||||
@@ -1,29 +0,0 @@
|
||||
local config = {}
|
||||
|
||||
config.options = {
|
||||
enabled = true,
|
||||
execution_message = "AutoSave: saved at " .. vim.fn.strftime("%H:%M:%S"),
|
||||
events = {"InsertLeave", "TextChanged"},
|
||||
conditions = {
|
||||
exists = true,
|
||||
filename_is_not = {},
|
||||
filetype_is_not = {},
|
||||
modifiable = true,
|
||||
},
|
||||
write_all_buffers = false,
|
||||
on_off_commands = false,
|
||||
clean_command_line_interval = 0,
|
||||
debounce_delay = 135
|
||||
}
|
||||
|
||||
function config.set_options(opts)
|
||||
opts = opts or {}
|
||||
|
||||
for opt, _ in pairs(opts) do
|
||||
if (config.options[opt] ~= nil) then -- not nil
|
||||
config.options[opt] = opts[opt]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return config
|
||||
@@ -1,28 +0,0 @@
|
||||
local opts = require("autosave.config").options
|
||||
local cmd = vim.cmd
|
||||
|
||||
local M = {}
|
||||
|
||||
local function setup_load()
|
||||
if opts["enabled"] == true then
|
||||
vim.g.autosave_state = true
|
||||
require("autosave.main").main("on")
|
||||
else
|
||||
vim.g.autosave_state = false
|
||||
end
|
||||
end
|
||||
|
||||
local function setup_commands()
|
||||
if opts["on_off_commands"] == true then
|
||||
cmd([[command! ASOn lua require'autosave.main'.main('on')]])
|
||||
cmd([[command! ASOff lua require'autosave.main'.main('off')]])
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup(custom_opts)
|
||||
require("autosave.config").set_options(custom_opts)
|
||||
setup_load()
|
||||
setup_commands()
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,51 +0,0 @@
|
||||
local autocmds = require("autosave.modules.autocmds")
|
||||
local autosave = require("autosave")
|
||||
local g = vim.g
|
||||
local M = {}
|
||||
require("autosave.utils.viml_funcs")
|
||||
|
||||
local function on()
|
||||
|
||||
if (autosave.hook_before_on ~= nil) then
|
||||
autosave.hook_before_on()
|
||||
end
|
||||
|
||||
autocmds.load_autocommands()
|
||||
g.autosave_state = true
|
||||
|
||||
if (autosave.hook_after_on ~= nil) then
|
||||
autosave.hook_after_on()
|
||||
end
|
||||
end
|
||||
|
||||
local function off()
|
||||
|
||||
if (autosave.hook_before_off ~= nil) then
|
||||
autosave.hook_before_off()
|
||||
end
|
||||
|
||||
autocmds.unload_autocommands()
|
||||
g.autosave_state = false
|
||||
|
||||
if (autosave.hook_after_off ~= nil) then
|
||||
autosave.hook_after_off()
|
||||
end
|
||||
end
|
||||
|
||||
function M.main(option)
|
||||
option = option or 'load'
|
||||
|
||||
if (option == 'toggle') then
|
||||
if (g.autosave_state == true) then
|
||||
off()
|
||||
else
|
||||
on()
|
||||
end
|
||||
elseif (option == 'on') then
|
||||
on()
|
||||
elseif (option == 'off') then
|
||||
off()
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,192 +0,0 @@
|
||||
local api = vim.api
|
||||
local fn = vim.fn
|
||||
local cmd = vim.cmd
|
||||
|
||||
local opts = require("autosave.config").options
|
||||
local autosave = require("autosave")
|
||||
local default_events = { "InsertLeave", "TextChanged" }
|
||||
|
||||
local modified
|
||||
|
||||
local M = {}
|
||||
|
||||
local function table_has_value(tbl, value)
|
||||
for key, _ in pairs(tbl) do
|
||||
if tbl[key] == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function set_modified(value)
|
||||
modified = value
|
||||
end
|
||||
|
||||
local function get_modified()
|
||||
return modified
|
||||
end
|
||||
|
||||
local function actual_save()
|
||||
-- might use update, but in that case it can't be checked if a file was modified and so it will always
|
||||
-- print opts["execution_message"]
|
||||
if api.nvim_eval([[&modified]]) == 1 then
|
||||
local first_char_pos = fn.getpos("'[")
|
||||
local last_char_pos = fn.getpos("']")
|
||||
|
||||
if opts["write_all_buffers"] then
|
||||
cmd("silent! wall")
|
||||
else
|
||||
cmd("silent! write")
|
||||
end
|
||||
|
||||
fn.setpos("'[", first_char_pos)
|
||||
fn.setpos("']", last_char_pos)
|
||||
|
||||
if get_modified() == nil or get_modified() == false then
|
||||
set_modified(true)
|
||||
end
|
||||
|
||||
M.message_and_interval()
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_user_conditions()
|
||||
local sc_exists, sc_filename, sc_filetype, sc_modifiable = true, true, true, true
|
||||
|
||||
for condition, value in pairs(opts["conditions"]) do
|
||||
if condition == "exists" then
|
||||
if value == true then
|
||||
if fn.filereadable(fn.expand("%:p")) == 0 then
|
||||
sc_exists = false
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif condition == "modifiable" then
|
||||
if value == true then
|
||||
if api.nvim_eval([[&modifiable]]) == 0 then
|
||||
sc_modifiable = false
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif condition == "filename_is_not" then
|
||||
if not (next(opts["conditions"]["filename_is_not"]) == nil) then
|
||||
if table_has_value(opts["conditions"]["filename_is_not"], vim.fn.expand('%:t')) == true then
|
||||
sc_filename = false
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif condition == "filetype_is_not" then
|
||||
if not (next(opts["conditions"]["filetype_is_not"]) == nil) then
|
||||
if table_has_value(opts["conditions"]["filetype_is_not"], api.nvim_eval([[&filetype]])) == true then
|
||||
sc_filetype = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return { sc_exists, sc_filename, sc_filetype, sc_modifiable }
|
||||
end
|
||||
|
||||
local function assert_return(values, expected)
|
||||
for key, value in pairs(values) do
|
||||
if value ~= expected then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function M.message_and_interval()
|
||||
if get_modified() == true then
|
||||
set_modified(false)
|
||||
if opts["execution_message"] ~= "" then
|
||||
print(opts["execution_message"])
|
||||
end
|
||||
|
||||
if opts["clean_command_line_interval"] > 0 then
|
||||
cmd(
|
||||
[[call timer_start(]]
|
||||
.. opts["clean_command_line_interval"]
|
||||
.. [[, funcref('g:AutoSaveClearCommandLine'))]]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function debounce(lfn, duration)
|
||||
local queued = false
|
||||
|
||||
local function inner_debounce()
|
||||
if not queued then
|
||||
vim.defer_fn(function()
|
||||
queued = false
|
||||
lfn()
|
||||
end, duration)
|
||||
queued = true
|
||||
end
|
||||
end
|
||||
|
||||
return inner_debounce
|
||||
end
|
||||
|
||||
function M.do_save()
|
||||
if assert_return(assert_user_conditions(), true) then
|
||||
M.debounced_save()
|
||||
end
|
||||
end
|
||||
|
||||
function M.save()
|
||||
if autosave.hook_before_saving ~= nil then
|
||||
autosave.hook_before_saving()
|
||||
end
|
||||
|
||||
M.do_save()
|
||||
|
||||
if autosave.hook_after_saving ~= nil then
|
||||
autosave.hook_after_saving()
|
||||
end
|
||||
end
|
||||
|
||||
local function get_events()
|
||||
if next(opts["events"]) == nil or opts["events"] == nil then
|
||||
return default_events
|
||||
else
|
||||
return opts["events"]
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_events()
|
||||
return table.concat(get_events(), ",")
|
||||
end
|
||||
|
||||
function M.load_autocommands()
|
||||
if opts["debounce_delay"] == 0 then
|
||||
M.debounced_save = actual_save
|
||||
else
|
||||
M.debounced_save = debounce(actual_save, opts["debounce_delay"])
|
||||
end
|
||||
|
||||
api.nvim_exec([[
|
||||
aug autosave_save
|
||||
au!
|
||||
au ]] .. parse_events() .. [[ * execute "lua require'autosave.modules.autocmds'.save()"
|
||||
aug END
|
||||
]], false)
|
||||
end
|
||||
|
||||
function M.unload_autocommands()
|
||||
api.nvim_exec(
|
||||
[[
|
||||
aug autosave_save
|
||||
au!
|
||||
aug END
|
||||
]],
|
||||
false
|
||||
)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,12 +0,0 @@
|
||||
local api = vim.api
|
||||
|
||||
api.nvim_exec(
|
||||
[[
|
||||
function! g:AutoSaveClearCommandLine(timer)
|
||||
if mode() != 'c'
|
||||
echon ''
|
||||
endif
|
||||
endfunction
|
||||
]],
|
||||
false
|
||||
)
|
||||
Reference in New Issue
Block a user