92 lines
2.9 KiB
Lua
92 lines
2.9 KiB
Lua
-- saveplace.lua --- remember the cursor position per file (Arc 3 Q#PS3b).
|
|
--
|
|
-- Records the active file buffer's (cursor byte, view_top) on save and
|
|
-- at quit, and restores it when the file is reopened. Storage is the
|
|
-- `places` state file, one `<cursor> <view_top> <path>` line per file
|
|
-- (numbers first so the path, which may contain spaces, is the
|
|
-- whitespace-split remainder). LRU-capped.
|
|
--
|
|
-- On by default; disable from init.lua with
|
|
-- `pmacs.saveplace.enable(false)`. Inert when no state dir is
|
|
-- configured (cfg(test) / no HOME), so the lib suite writes nothing.
|
|
--
|
|
-- Framing: docs/persistence-framing.md.
|
|
|
|
pmacs.saveplace = pmacs.saveplace or {}
|
|
|
|
local STATE_KEY = "places"
|
|
local MAX_ENTRIES = 200
|
|
|
|
local enabled = true
|
|
function pmacs.saveplace.enable(on)
|
|
enabled = (on ~= false)
|
|
end
|
|
|
|
local function active_ready()
|
|
return enabled and pmacs.state.available() and pmacs.editor.file_path() ~= nil
|
|
end
|
|
|
|
-- Load the places file into an ordered list of {path, cursor, view_top}
|
|
-- (most-recently-recorded first) plus a path->index lookup.
|
|
local function load_places()
|
|
local list, index = {}, {}
|
|
local text = pmacs.state.read(STATE_KEY)
|
|
if not text then return list, index end
|
|
for line in text:gmatch("([^\n]+)") do
|
|
-- "<cursor> <view_top> <path>"
|
|
local cur, vt, path = line:match("^(%d+)%s+(%d+)%s+(.+)$")
|
|
if path and not index[path] then
|
|
list[#list + 1] = { path = path, cursor = tonumber(cur), view_top = tonumber(vt) }
|
|
index[path] = #list
|
|
end
|
|
end
|
|
return list, index
|
|
end
|
|
|
|
local function save_places(list)
|
|
local lines = {}
|
|
for i = 1, math.min(#list, MAX_ENTRIES) do
|
|
local e = list[i]
|
|
lines[#lines + 1] = string.format("%d %d %s", e.cursor, e.view_top, e.path)
|
|
end
|
|
pmacs.state.write(STATE_KEY, table.concat(lines, "\n") .. (#lines > 0 and "\n" or ""))
|
|
end
|
|
|
|
-- Record the active buffer's place, moving it to the front (LRU).
|
|
local function record_active()
|
|
if not active_ready() then return end
|
|
local path = pmacs.editor.file_path()
|
|
local cursor = pmacs.editor.cursor()
|
|
local view_top = pmacs.editor.view_top()
|
|
local list, index = load_places()
|
|
if index[path] then table.remove(list, index[path]) end
|
|
table.insert(list, 1, { path = path, cursor = cursor, view_top = view_top })
|
|
save_places(list)
|
|
end
|
|
|
|
-- Restore the just-loaded file's place, if we have one.
|
|
local function restore_active()
|
|
if not active_ready() then return end
|
|
local path = pmacs.editor.file_path()
|
|
local list, index = load_places()
|
|
local i = index[path]
|
|
if not i then return end
|
|
local e = list[i]
|
|
pmacs.editor.goto_byte(e.cursor)
|
|
pmacs.editor.set_view_top(e.view_top)
|
|
end
|
|
|
|
-- Record on save and on quit; restore on open. before-save /
|
|
-- before-quit are short-circuit hooks — returning nil never vetoes.
|
|
pmacs.hook.add("buffer.before-save", function()
|
|
pcall(record_active)
|
|
end)
|
|
|
|
pmacs.hook.add("editor.before-quit", function()
|
|
pcall(record_active)
|
|
end)
|
|
|
|
pmacs.hook.add("buffer.after-load", function()
|
|
pcall(restore_active)
|
|
end)
|