summaryrefslogtreecommitdiffstats
path: root/lua/inbox/utils.lua
blob: e9826c8155d35e960cc980f49bdc20c4f882fe78 (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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
local M = {}

---@class inbox.TextChunk
---@field [1] string text
---@field [2] string hl

---@param msg string
---@param vars table<string, string>
---@param level integer|nil
function M.notify(msg, vars, level)
	for name, value in pairs(vars) do
		string.format("%s\n%s: %s", msg, name, value)
	end

	vim.notify(msg, level, { title = "inbox.nvim" })
end

---@param msg string
---@param vars table<string, string>
function M.info(msg, vars)
	M.notify(msg, vars, vim.log.levels.INFO)
end

---@param msg string
---@param vars table<string, string>
function M.warn(msg, vars)
	M.notify(msg, vars, vim.log.levels.WARN)
end

---@param msg string
---@param vars table<string, string>
function M.error(msg, vars)
	M.notify(msg, vars, vim.log.levels.ERROR)
end

---@param text string
---@param length nil|integer
---@return string
function M.rpad(text, length)
	if not length then
		return text
	end

	if text:len() <= length then
		return text .. string.rep(" ", length - text:len())
	else
		return string.sub(text, 1, length - 3) .. "..."
	end
end

---@param cols (string | inbox.TextChunk)[]
---@param col_widths integer[]
---@return string
---@return any[][] List of highlights {group, col_start, col_end}
function M.render_row(cols, col_widths)
	local pieces = {}
	local highlights = {}

	local col = 0
	for i, chunk in ipairs(cols) do
		local text, hl
		if type(chunk) == "table" then
			text, hl = unpack(chunk) --[[@as string]]
		else
			text = chunk --[[@as string]]
		end
		text = M.rpad(text, col_widths[i])
		table.insert(pieces, text)
		local col_end = col + text:len() + 1
		if hl then
			table.insert(highlights, { hl, col, col_end })
		end
		col = col_end
	end

	return table.concat(pieces, " "), highlights
end

---@param rows (string | inbox.TextChunk)[][]
---@param col_widths integer[]
---@return string[]
---@return any[][] List of highlights {group, lnum, col_start, col_end}
function M.render_table(rows, col_widths)
	local lines = {}
	local highlights = {}

	for lnum, cols in ipairs(rows) do
		local line, line_hls = M.render_row(cols, col_widths)
		table.insert(lines, line)

		for _, hl in pairs(line_hls) do
			local group, col_start, col_end = unpack(hl)
			table.insert(highlights, { group, lnum, col_start, col_end })
		end
	end

	return lines, highlights
end

---@param winid integer
---@param cols (string | inbox.TextChunk)[]
---@param col_widths integer[]
function M.render_winbar(winid, cols, col_widths)
	local winbar = M.render_row(cols, col_widths)
	local offset = vim.fn.getwininfo(winid)[1].textoff + 1
	vim.api.nvim_set_option_value("winbar", string.rep(" ", offset) .. winbar, { scope = "local", win = winid })
end

---@param name string
---@return string sign_name
function M.sign_name(name)
	return "Inbox" .. name:gsub("^%l", string.upper)
end

---@param bufnr integer
---@param highlights any[][] List of highlights { group, lnum, col_start, col_end }
M.set_highlights = function(bufnr, highlights)
	local ns = vim.api.nvim_create_namespace("Oil")
	vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
	for _, hl in ipairs(highlights) do
		vim.api.nvim_buf_add_highlight(bufnr, ns, unpack(hl))
	end
end

---@param bufnr integer
---@param signs any[][] List of signs { name, lnum }
function M.set_signs(bufnr, signs)
	local notified = {}

	for _, sign_lnum in pairs(signs) do
		local sign, lnum = unpack(sign_lnum)
		local name = M.sign_name(sign)

		if #vim.fn.sign_getdefined(name) > 0 then
			vim.fn.sign_place(0, "inbox", name, bufnr, { lnum = lnum })
		elseif not notified[name] then
			vim.notify(("Missing sign definition for sign: %s"):format(name), vim.log.levels.ERROR)
			notified[name] = true
		end
	end
end

---@param json string
---@return table data
function M.json_decode(json)
	local results = {}
	if json and json ~= "" then
		results = vim.json.decode(json) or {}
	end
	return results
end

---@param lines string[]
---@return table<inbox.HeaderKey, string[]> headers
function M.parse_headers(lines)
	local headers = {}

	local cur_header
	for _, line in pairs(lines) do
		if line == "" then
			break
		end

		local header, value = line:match("^([A-Z][^: ]*): (.*)")

		if header then
			cur_header = header
			headers[cur_header] = {}
		else
			value = line
		end

		if cur_header then
			table.insert(headers[cur_header], value)
		else
			M.error("Failed to parse line in headers", { line = line })
		end
	end

	return headers
end

---@param bufnr integer
---@return string? maildir maildir name
---@return string? id entry id
function M.parse_scheme(bufnr)
	local bufname = vim.api.nvim_buf_get_name(bufnr or 0)

	local _, init, maildir = bufname:find("maildir://([^:]+)")
	local _, _, id = bufname:find(":(.*)", init)

	return maildir, id
end

function M.stateful_iter(table, wrap)
	local k
	local s_k, s_v = next(table)
	return function()
		local v
		k, v = next(table, k)
		if k == nil and wrap then
			k, v = s_k, s_v
		end
		return k, v
	end
end

return M