From 74fcd19ac2b51c0e8b2a6cdc0e49f5d04e651db0 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sun, 23 Jun 2024 04:01:50 +0200 Subject: [PATCH] fix(autoreload): wait a second before automatically reloading a file (#1823) This is needed because we use `mtime` to determine if a file has actually changed, but on most systems this has a resolution of 1 second (and we truncate it to an integer anyways). Without this wait, we would skip reloading a file that has been changed multiple times in the same `mtime` second, thus losing some data. --- data/plugins/autoreload.lua | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index f1ba6e0d..7086fb35 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -1,7 +1,6 @@ -- mod-version:3 local core = require "core" local config = require "core.config" -local style = require "core.style" local Doc = require "core.doc" local Node = require "core.node" local common = require "core.common" @@ -26,7 +25,7 @@ local times = setmetatable({}, { __mode = "k" }) local visible = setmetatable({}, { __mode = "k" }) local function get_project_doc_watch(doc) - for i, v in ipairs(core.project_directories) do + for _, v in ipairs(core.project_directories) do if doc.abs_filename:find(v.name, 1, true) == 1 then return v.watch end end return watch @@ -43,6 +42,34 @@ local function reload_doc(doc) core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename) end + +local timers = setmetatable({}, { __mode = "k" }) + +local function delayed_reload(doc, mtime) + if timers[doc] then + -- If mtime remains the same, there's no need to restart the timer + -- as we're waiting a full second anyways. + if not mtime or timers[doc].mtime ~= mtime then + timers[doc] = { last_trigger = system.get_time(), mtime = mtime } + end + return + end + + timers[doc] = { last_trigger = system.get_time(), mtime = mtime } + core.add_thread(function() + local diff = system.get_time() - timers[doc].last_trigger + -- Wait a second before triggering a reload because we're using mtime + -- to determine if a file has changed, and on many systems it has a + -- resolution of 1 second. + while diff < 1 do + coroutine.yield(diff) + diff = system.get_time() - timers[doc].last_trigger + end + timers[doc] = nil + reload_doc(doc) + end) +end + local function check_prompt_reload(doc) if doc and doc.deferred_reload then core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", { @@ -71,7 +98,7 @@ function dirwatch:check(change_callback, ...) local info = system.get_file_info(doc.filename or "") if info and info.type == "file" and times[doc] ~= info.modified then if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then - reload_doc(doc) + delayed_reload(doc, info.modified) else doc.deferred_reload = true if doc == core.active_view.doc then check_prompt_reload(doc) end