aboutsummaryrefslogtreecommitdiff
path: root/init.lua
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2024-05-02 16:00:54 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2024-05-02 16:00:54 +0200
commitaead994af26e91c3a094b010fcba3d45453c8940 (patch)
tree5841d69f14d24f7239d6d1c7edd9d5e48c735f49 /init.lua
downloadvis-lockfiles-aead994af26e91c3a094b010fcba3d45453c8940.tar.gz
vis-lockfiles-aead994af26e91c3a094b010fcba3d45453c8940.zip
initial commit
Diffstat (limited to 'init.lua')
-rw-r--r--init.lua121
1 files changed, 121 insertions, 0 deletions
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..02fee0d
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,121 @@
+-- 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 <https://www.gnu.org/licenses/>.
+local M = {}
+M.directory = '${XDG_CACHE_HOME:-${HOME}/.local/share}/vis/locks'
+
+-- Helper to execute a command and capture its output
+local function capture_cmd(cmd)
+ local p = assert(io.popen(cmd, 'r'))
+ local s = assert(p:read('*a'))
+ local success, _, status = p:close()
+ if not success then
+ local err = cmd .. ' failed with exit code: ' .. status
+ vis:info('vis-lockfiles: ' .. err)
+ end
+ return s
+end
+
+local function lock_file_path(path)
+ local filename = path:gsub('/', '%%')
+ assert(os.execute('mkdir -p "' .. M.directory .. '"'))
+ local lfile_path = capture_cmd('echo "' .. M.directory .. '/' .. filename .. '.lock"')
+ return lfile_path:sub(1, #lfile_path - 1) -- trim newline from path
+end
+
+-- determine vis' pid
+local vis_pid
+do
+ local vis_proc_file = io.open('/proc/self/stat', 'r')
+ if vis_proc_file then
+ vis_pid = vis_proc_file:read('*n')
+ vis_proc_file:close()
+
+ else -- fallback if /proc/self/stat is not available
+ local out = capture_cmd('sh -c "echo $PPID"')
+ vis_pid = tonumber(out)
+ end
+end
+assert(vis_pid)
+
+-- get a user confirmation
+-- return true if user selected yes, false otherwise
+local function confirm(path)
+ local choices = 'no\nyes'
+ local prompt = path .. ' exists. Edit anyway:'
+ local cmd = 'printf "' .. choices .. '" | vis-menu -b -p \'' .. prompt .. '\''
+
+ local choice = nil
+ -- local status, output = vis:pipe(nil, {start = 0, finish = 0}, cmd)
+ local menu = assert(io.popen(cmd))
+ local output = menu:read('*a')
+ local _, _, status = menu:close()
+ if status == 0 then
+ -- trim newline from selection
+ if output:sub(-1) == '\n' then
+ choice = output:sub(1, -2)
+ else
+ choice = output
+ end
+ end
+ vis:redraw()
+ return choice == 'yes'
+end
+
+local created_locks = {}
+local function create_lock_file(lfile_path)
+ local already_locked = os.execute('test -e ' .. '"' .. lfile_path .. '"')
+ if already_locked then
+ return false
+ end
+
+ -- create the lock file with our pid
+ local cmd = 'echo ' .. vis_pid .. ' > "' .. lfile_path .. '"'
+ assert(os.execute(cmd))
+
+ -- detect a possible race
+ local lock_file = io.open(lfile_path)
+ assert(lock_file)
+ local lock_pid = lock_file:read('*n')
+ lock_file:close()
+ local locked = tonumber(lock_pid) == vis_pid
+
+ -- remember we created the lock file
+ if locked then
+ created_locks[lfile_path] = true
+ end
+
+ return locked
+end
+
+local function delete_lock_file(lfile_path)
+ os.execute('rm ' .. '"' .. lfile_path .. '"')
+ created_locks[lfile_path] = nil
+end
+
+vis.events.subscribe(vis.events.FILE_OPEN, function(file)
+ local lfile_path = lock_file_path(file.path)
+ if not create_lock_file(lfile_path) and not confirm(lfile_path) then
+ os.exit(1)
+ end
+end)
+
+vis.events.subscribe(vis.events.FILE_CLOSE, function(file)
+ local lfile_path = lock_file_path(file.path)
+ if created_locks[lfile_path] then
+ delete_lock_file(lfile_path)
+ end
+end)
+
+return M