aboutsummaryrefslogtreecommitdiff
path: root/init.lua
blob: 5ef18a88be63bf3886b4a49e26977fa15a29c8d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
-- 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 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

  vis:command('open')
  local prompt_win = vis.win
  prompt_win.file:insert(0, msg)
  prompt_win.selection.pos = 0

  vis:redraw()

  local menu_prompt = '(E)dit anyway, (Q)uit, [A]bort'
  local cmd = 'printf %s \'' .. choices .. '\' | vis-menu -b -p \'' .. menu_prompt .. '\''

  local status, output = vis:pipe(prompt_win.file, {start = 0, finish = 0}, cmd)
  vis:redraw()

  local result = false
  if status == 0 then
    if output:sub(1, 1) == 'e' then
      result = true
    elseif output:sub(1, 1) == 'q' then
      vis:exit(1)
    end
  end

  -- Queue up the closing of our prompt window
  pending_action = function()
    if count_windows() == 1 then
      return false
    end

    -- There are more windows than our prompt window, therefore we can close it
    for win in vis:windows() do
      if win.file:content(0, win.file.size) == msg then
        win:close(true)
        break
      end
    end

    -- We closed our prompt window or did not find it
    return true
  end

  return result
end

local created_locks = {}

vis.events.subscribe(vis.events.FILE_OPEN, function(file)
  if created_locks[file.path] or not file.path then
    -- either a noname buffer, or already locked by us
    return
  end

  local initial_window = vis.win == nil
  local cur_file = vis.win and vis.win.file and (vis.win.file.path or vis.win.file.name)

  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
  elseif not prompt(file.path, lfile_path, lock_pid) then
    -- There is no window, therefore abort means exit
    if initial_window then
      vis:exit(1)
    end

    local prev_pending_action = pending_action
    pending_action = function()
      if prev_pending_action and not prev_pending_action() then
        return false
      end

      pending_action = nil
      vis:command('o ' .. (cur_file or ''))
      pending_action = function()
        os.execute('echo close locked file action >> lockfiles.log')
        if count_windows() == 1 then
          return false
        end

        -- close the window containing the locked file
        for win in vis:windows() do
          os.execute('echo ' .. ((win.file and win.file.path) or 'nil') .. ' >> lockfiles.log')
          os.execute('printf %s \'' .. debug.traceback() .. '\n\' >> lockfiles.log')
          if (win.file and win.file.path) == file.path then
            win:close()
            return true
          end
        end

        return false
      end
    end
  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