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
|