irongit

Configure synchronization of files and directories through a plugin.

160 lines3.8 KBLua
1local rsync = require("syncmap.rsync")
2local utils = require("syncmap.utils")
3local log = require("syncmap.log")
4local simple_cmd = require("syncmap.simple_cmd")
5local M = {
6 opts = require("syncmap.default"),
7}
8
9M.dir = vim.fn.expand("~/.local/share/nvim/syncmap/")
10
11---Make sure the directory exists
12if vim.fn.isdirectory(M.dir) == 0 then
13 vim.fn.mkdir(M.dir, "p")
14end
15
16M.state_file = vim.fn.expand("~/.local/share/nvim/syncmap/state.json")
17
18-- Create file if it doesn't exist
19local function create_state_file()
20 if vim.fn.filereadable(M.state_file) == 0 then
21 local fd = io.open(M.state_file, "w")
22 if fd then
23 fd:write("{}") -- empty JSON object
24 fd:close()
25 else
26 utils.log("Failed to create state file", vim.log.levels.ERROR)
27 end
28 end
29end
30create_state_file()
31
32---@alias RunningPid string|integer The process id for a SyncmapWatchItem
33---@alias RunningTag string A key based on src:dst for a SyncmapWatchItem
34---@alias RunningSrcs table<RsyncPath, RunningPid>
35
36---A dictionairy keyed on the src for a match located in ~/.local/share/nvim/syncmap/state.json
37---@type RunningSrcs
38M.active = {}
39
40---Load the existing config if possible
41local function load_state()
42 local content = vim.fn.readfile(M.state_file)
43 local joined = table.concat(content, "\n")
44 local ok, parsed = pcall(vim.json.decode, joined)
45 if ok and type(parsed) == "table" then
46 M.active = parsed
47 else
48 utils.log("Failed to parse state file", vim.log.levels.ERROR)
49 end
50end
51load_state()
52
53---Removes stale states, calls reverse_rsync on new folders, and starts watch on dead pids
54function M.sync()
55 local opts = M.opts
56 ---@type table<RunningTag, SyncmapWatchItem>
57 local lookup = {}
58 for _, m in ipairs(opts.map) do
59 local tag = vim.fn.expand(m[1]) .. ":" .. vim.fn.expand(m[2])
60 lookup[tag] = m
61 end
62
63 -- Iterate active and sync what exists
64 for tag, pid in pairs(M.active) do
65 if not lookup[tag] then
66 log.info(string.format("Removed from the config.\n%s\nRemoving the process %s.", tag, pid))
67 M.active[tag] = nil
68 simple_cmd.kill(pid)
69 elseif not simple_cmd.exists(pid) then
70 local m = lookup[tag]
71 local r = utils.row_to_rsync_params(m)
72 local cmd_str
73 M.active[tag], cmd_str = rsync.spawn_watcher(r)
74 log.info(
75 string.format(
76 "In active table but the process %s is not running.\n%s\n%s\nStarting a new process at %s.",
77 pid,
78 cmd_str,
79 tag,
80 M.active[tag]
81 )
82 )
83 end
84 end
85
86 ---Iterate config and add new items
87 for _, m in ipairs(opts.map) do
88 local tag = vim.fn.expand(m[1]) .. ":" .. vim.fn.expand(m[2])
89 if not M.active[tag] then
90 local r = utils.row_to_rsync_params(m)
91 local cmd_str
92 M.active[tag], cmd_str = rsync.spawn_watcher(r)
93 log.info(
94 string.format(
95 "%s\nIn the config but not in the active table.\n%s\nNew process is %s.",
96 tag,
97 cmd_str,
98 M.active[tag]
99 )
100 )
101 end
102 end
103 M.save()
104end
105
106function M.save()
107 local ok, encoded = pcall(vim.json.encode, M.active)
108 if not ok then
109 log.error("Failed to encode state")
110 return
111 end
112
113 local fd = io.open(M.state_file, "w")
114 if not fd then
115 log.error("Failed to open state file for writing")
116 return
117 end
118
119 fd:write(encoded)
120 fd:close()
121end
122
123---Kills all the active inotifywatch servers
124function M.killall()
125 local s = ""
126 for tag, pid in pairs(M.active) do
127 s = string.format("%s\n%d: %s", s, pid, tag)
128 simple_cmd.kill(pid)
129 end
130 log.info(string.format("Killed the following watch items\n%s", s))
131 M.active = {}
132end
133
134---Clears the saved information by deleting the state file
135function M.clean()
136 local ok, err = os.remove(M.state_file)
137 if not ok then
138 log.error("Failed to delete state file: " .. err)
139 else
140 log.info("State file deleted")
141 end
142end
143
144---Restarts Syncmap
145function M.restart()
146 M.killall()
147 M.sync()
148end
149
150---List active
151function M.show_active()
152 vim.print(M.active)
153end
154
155---Shows the current options of Syncmap
156function M.show_opts()
157 vim.print(M.opts)
158end
159
160return M