diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index 597e3de4..cbecad23 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -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 diff --git a/data/core/dirwatch.lua b/data/core/dirwatch.lua index 68f62082..fcbd019d 100644 --- a/data/core/dirwatch.lua +++ b/data/core/dirwatch.lua @@ -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 diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index f9369a46..0683b8b0 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -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") diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index 596b94f7..172edea2 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -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 diff --git a/src/api/dirmonitor/inotify.c b/src/api/dirmonitor/inotify.c index 70e536dd..697e1815 100644 --- a/src/api/dirmonitor/inotify.c +++ b/src/api/dirmonitor/inotify.c @@ -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); }