Autoreload Nagview (#942)

* Modified autoreload to use new dirwatch infrastructure, and added in nagview to verify that fs changes don't stomp on our changes, unless you want them to.

* Split out reload functionality to actual document, and added in a thread to check the document, in the cases where it wouldn't be covered by dirwatch.

* As per request from jgmdev, added in ability to show nagview always.

* Changed things over to use dirwatch.

* Made sure we redrew things, added in a contingency in 'save' for times when we load a non-existent file, and added some checks.
This commit is contained in:
Adam 2022-05-15 15:21:26 -04:00 committed by GitHub
parent 0315d397bd
commit 36c4d5d9ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 24 deletions

View File

@ -466,6 +466,10 @@ local commands = {
command.perform("doc:save-as")
end
end,
["doc:reload"] = function()
doc():reload()
end,
["file:rename"] = function()
local old_filename = doc().filename

View File

@ -31,10 +31,15 @@ end
-- In windows, this is a no-op for anything underneath a top-level directory,
-- but code should be called anyway, so we can ensure that we have a proper
-- experience across all platforms. Should be an absolute path.
-- Can also be called on individual files, though this should be used sparingly,
-- so as not to run into system limits (like in the autoreload plugin).
function dirwatch:watch(directory, bool)
if bool == false then return self:unwatch(directory) end
local info = system.get_file_info(directory)
if not info then return end
if not self.watched[directory] and not self.scanned[directory] then
if PLATFORM == "Windows" then
if info.type ~= "dir" then return self:scan(directory) end
if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then
-- Get the highest level of directory that is common to this directory, and the original.
local target = directory

View File

@ -80,6 +80,16 @@ function Doc:load(filename)
end
function Doc:reload()
if self.filename then
local sel = { self:get_selection() }
self:load(self.filename)
self:clean()
self:set_selection(table.unpack(sel))
end
end
function Doc:save(filename, abs_filename)
if not filename then
assert(self.filename, "no filename set to default to")

View File

@ -1,44 +1,100 @@
-- mod-version:3 -- lite-xl 2.1
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"
local dirwatch = require "core.dirwatch"
config.plugins.autoreload = common.merge({
always_show_nagview = false
}, config.plugins.autoreload)
local watch = dirwatch.new()
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
if doc.abs_filename:find(v.name, 1, true) == 1 then return v.watch end
end
return watch
end
local function update_time(doc)
local info = system.get_file_info(doc.filename)
times[doc] = info.modified
times[doc] = system.get_file_info(doc.filename).modified
end
local function reload_doc(doc)
local fp = io.open(doc.filename, "r")
local text = fp:read("*a")
fp:close()
local sel = { doc:get_selection() }
doc:remove(1, 1, math.huge, math.huge)
doc:insert(1, 1, text:gsub("\r", ""):gsub("\n$", ""))
doc:set_selection(table.unpack(sel))
doc:reload()
update_time(doc)
doc:clean()
core.redraw = true
core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename)
end
local on_modify = core.on_dirmonitor_modify
core.on_dirmonitor_modify = function(dir, filepath)
local abs_filename = dir.name .. PATHSEP .. filepath
for _, doc in ipairs(core.docs) do
local info = system.get_file_info(doc.filename or "")
if doc.abs_filename == abs_filename and info and times[doc] ~= info.modified then
reload_doc(doc)
break
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?", {
{ font = style.font, text = "Yes", default_yes = true },
{ font = style.font, text = "No" , default_no = true }
}, function(item)
if item.text == "Yes" then reload_doc(doc) end
doc.deferred_reload = false
end)
end
on_modify(dir, filepath)
end
local function doc_changes_visiblity(doc, visibility)
if doc and visible[doc] ~= visibility and doc.abs_filename then
visible[doc] = visibility
if visibility then check_prompt_reload(doc) end
get_project_doc_watch(doc):watch(doc.abs_filename, visibility)
end
end
local on_check = dirwatch.check
function dirwatch:check(change_callback, ...)
on_check(self, function(dir)
for _, doc in ipairs(core.docs) do
if dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename then
local info = system.get_file_info(doc.filename or "")
if info and times[doc] ~= info.modified then
if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then
reload_doc(doc)
else
doc.deferred_reload = true
if doc == core.active_view.doc then check_prompt_reload(doc) end
end
end
end
end
change_callback(dir)
end, ...)
end
local core_set_active_view = core.set_active_view
function core.set_active_view(view)
core_set_active_view(view)
doc_changes_visiblity(view.doc, true)
end
local node_set_active_view = Node.set_active_view
function Node:set_active_view(view)
if self.active_view then doc_changes_visiblity(self.active_view.doc, false) end
node_set_active_view(self, view)
doc_changes_visiblity(self.active_view.doc, true)
end
core.add_thread(function()
while true do
-- because we already hook this function above; we only
-- need to check the file.
watch:check(function() end)
coroutine.yield(0.05)
end
end)
-- patch `Doc.save|load` to store modified time
local load = Doc.load
local save = Doc.save
@ -51,6 +107,8 @@ end
Doc.save = function(self, ...)
local res = save(self, ...)
-- if starting with an unsaved document with a filename.
if not times[self] then get_project_doc_watch(self):watch(self.abs_filename, true) end
update_time(self)
return res
end

View File

@ -44,7 +44,7 @@ int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buff
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MODIFY | IN_MOVED_TO);
}