-- Copyright (c) 2024 Florian Fischer. All rights reserved. -- -- vis-lockfiles is free software: you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free Software -- Foundation, either version 3 of the License, or (at your option) any later -- version. -- -- vis-lockfiles is distributed in the hope that it will be useful, but WITHOUT -- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License along with -- vis-lockfiles found in the LICENSE file. -- If not, see . local source_str = debug.getinfo(1, 'S').source:sub(2) local script_path = source_str:match('(.*/)') local lockfiles = dofile(script_path .. 'lockfiles.lua') -- function used for all actions resulting from a FILE_OPEN event that can not -- be done during the handler local pending_action = nil --[[ local function count_windows() -- Count the currently open vis windows local n = 0 for _ in vis:windows() do n = n + 1 end return n end ]] -- local M = {} M.directory = '${XDG_STATE_HOME:-${HOME}/.local/state}/vis/locks' M.directory_expanded = nil local function get_lock_dir() if M.directory_expanded ~= M.directory then M.directory = lockfiles.capture_cmd('printf %s "' .. M.directory .. '"') M.directory_expanded = M.directory end return M.directory end -- get a user confirmation -- return true if user selected yes, false otherwise local function prompt(path, lfile_path, pid) local choices = 'a\ne\nq' local msg = 'Lock file found!\n\t lock file: "' .. lfile_path .. '"\n\tfile name: ' .. path .. '\n\tprocess ID: ' .. pid local menu_prompt = '(E)dit anyway, (Q)uit, [A]bort' local cmd1 = 'printf >/dev/tty %s \'' .. msg .. '\n\'' local cmd2 = 'printf %s \'' .. choices .. '\'' local cmd3 = 'vis-menu -b -p \'' .. menu_prompt .. '\'' local cmd = cmd1 .. ';' .. cmd2 .. '|' .. cmd3 local status, output = vis:pipe(cmd, true) vis:redraw() if status == 0 then if output:sub(1, 1) == 'e' then return true elseif output:sub(1, 1) == 'q' then vis:exit(1) end end -- else 'abort' or dialog canceled local hnd1 hnd1 = function() if vis.win.file and vis.win.file.path == path then vis.events.unsubscribe(vis.events.UI_DRAW, hnd1) vis:command('q') vis:command('new') end end vis.events.subscribe(vis.events.UI_DRAW, hnd1) vis:redraw() end local created_locks = {} vis.events.subscribe(vis.events.WIN_OPEN, function(win) local file = win.file if created_locks[file.path] or not file.path then -- either a noname buffer, or already locked by us return end local lfile_path = lockfiles.lock_file_path(get_lock_dir(), file.path) local lock_pid = lockfiles.create_lock_file(get_lock_dir(), lfile_path) if lockfiles.vis_pid == lock_pid then created_locks[file.path] = lfile_path else prompt(file.path, lfile_path, lock_pid) end end) vis.events.subscribe(vis.events.FILE_CLOSE, function(file) if not file.path then return end local lfile_path = created_locks[file.path] if lfile_path then lockfiles.delete_lock_file(lfile_path) created_locks[file.path] = nil end end) vis.events.subscribe(vis.events.QUIT, function() for _, lfile_path in pairs(created_locks) do if lfile_path then lockfiles.delete_lock_file(lfile_path) end end created_locks = {} end) vis.events.subscribe(vis.events.UI_DRAW, function() if not pending_action or not vis.win then return end if pending_action() then pending_action = nil end end) return M