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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
diff --git a/lua/mini/starter.lua b/lua/mini/starter.lua
index 66d46a52..9b7e4397 100644
--- a/lua/mini/starter.lua
+++ b/lua/mini/starter.lua
@@ -138,7 +138,7 @@
--- local timer = vim.loop.new_timer()
--- local n_seconds = 0
--- timer:start(0, 1000, vim.schedule_wrap(function()
---- if vim.bo.filetype ~= 'ministarter' then
+--- if vim.bo.filetype ~= 'dashboard' then
--- timer:stop()
--- return
--- end
@@ -239,6 +239,9 @@ MiniStarter.config = {
-- If `nil` (default), default items will be used (see |mini.starter|).
items = nil,
+ -- Empty lines at the top of the content.
+ top_padding = 0,
+
-- Header to be displayed before items. Converted to single string via
-- `tostring` (use `\n` to display several lines). If function, it is
-- evaluated first. If `nil` (default), polite greeting will be used.
@@ -249,6 +252,12 @@ MiniStarter.config = {
-- evaluated first. If `nil` (default), default usage help will be shown.
footer = nil,
+ -- Amount of empty lines between items and the footer.
+ footer_offset = 1,
+
+ -- Picture to show to the side of the content.
+ picture = nil,
+
-- Array of functions to be applied consecutively to initial content.
-- Each function should take and return content for Starter buffer (see
-- |mini.starter| and |MiniStarter.get_content()| for more details).
@@ -373,7 +382,7 @@ MiniStarter.refresh = function(buf_id)
data.footer = H.normalize_header_footer(config.footer or H.default_footer)
-- Evaluate content
- local content = H.make_initial_content(data.header, items, data.footer)
+ local content = H.make_initial_content(data.header, items, data.footer, config)
local hooks = config.content_hooks or H.default_content_hooks
for _, f in ipairs(hooks) do
content = f(content, buf_id)
@@ -400,6 +409,16 @@ MiniStarter.refresh = function(buf_id)
-- Apply current query (clear command line afterwards)
H.make_query(buf_id)
+
+ -- The picture drawn here used to get cleared at some point
+ -- that _usually_ came before any scheduled timers fired.
+ -- That seems to not happen anymore (nvim-0.11.1, kitty 0.42.1).
+ H.draw_picture(config, false)
+ --local timer = vim.loop.new_timer()
+ --timer:start(0, 0, vim.schedule_wrap(function()
+ -- timer:stop()
+ -- H.draw_picture(config, false)
+ --end))
end
--- Close Starter buffer
@@ -729,18 +748,45 @@ MiniStarter.gen_hook.aligning = function(horizontal, vertical)
local win_id = vim.fn.bufwinid(buf_id)
if win_id < 0 then return end
+ local config = H.get_config()
+
local line_strings = MiniStarter.content_to_lines(content)
+ H.win_width = vim.api.nvim_win_get_width(win_id)
+ H.win_height = vim.api.nvim_win_get_height(win_id)
+
-- Align horizontally
-- Don't use `string.len()` to account for multibyte characters
local lines_width = vim.tbl_map(function(l) return vim.fn.strdisplaywidth(l) end, line_strings)
- local min_right_space = vim.api.nvim_win_get_width(win_id) - math.max(unpack(lines_width))
+ -- Ignore the max line width
+ local min_right_space = H.win_width -- - math.max(unpack(lines_width))
local left_pad = math.max(math.floor(horiz_coef * min_right_space), 0)
-- Align vertically
- local bottom_space = vim.api.nvim_win_get_height(win_id) - #line_strings
+ local bottom_space = H.win_height - #line_strings
local top_pad = math.max(math.floor(vert_coef * bottom_space), 0)
+ -- Position picture
+ if config.picture ~= nil then
+ if config.picture.width == nil then config.picture.width = 25 end
+ if config.picture.position ~= 'right' then config.picture.position = 'left' end
+ if config.picture.padding == nil then config.picture.padding = {} end
+ if config.picture.padding.left == nil then config.picture.padding.left = 0 end
+ if config.picture.padding.top == nil then config.picture.padding.top = 0 end
+ if config.picture.padding.text == nil then config.picture.padding.text = 0 end
+
+ local picture_left_pad = (config.picture.width / 2.0) - config.picture.padding.left
+
+ left_pad = left_pad + config.picture.padding.text
+ if config.picture.position == 'left' then
+ H.picture_x = left_pad - picture_left_pad
+ else
+ H.picture_x = left_pad + picture_left_pad
+ end
+ if H.picture_x < 0.0 then H.picture_x = 0.0 end
+ H.picture_y = top_pad + config.picture.padding.top + config.top_padding
+ end
+
return MiniStarter.gen_hook.padding(left_pad, top_pad)(content)
end
end
@@ -1003,7 +1049,7 @@ H.default_footer = [[
Type query to filter items
<BS> deletes latest character from query
<Esc> resets current query
-<Down/Up>, <C-n/p>, <M-j/k> move current item
+<C-j/k> move current item
<CR> executes action of current item
<C-c> closes this buffer]]
@@ -1044,8 +1090,54 @@ end
H.apply_config = function(config) MiniStarter.config = config end
+H.is_file_readable = function(path)
+ return vim.fn.filereadable(vim.fn.expand(path)) ~= 0
+end
+
+H.draw_picture = function(config, clear)
+ if config ~= nil then
+ if config.picture == nil or config.picture.path == nil or not H.is_file_readable(config.picture.path) then return end
+ else
+ -- No picture, so nothing to clear
+ if H.picture_x == nil or H.picture_y == nil then return end
+ end
+ local cmd = ''
+ if clear and not config.picture.kitty then
+ cmd = ('for i in $(seq %d %d); do tput cup $i 0 && tput el; done'):format(0, H.win_height - 1)
+ else
+ cmd = ('chafa %s --align left,top -s %dx%d 2>&1'):format(config.picture.path, config.picture.width, (H.win_height - 2) - H.picture_y)
+ end
+ -- Resizing will not clear on non-kitty mode
+ local output = ''
+ if config.picture.kitty then
+ output = output .. '\027_Ga=d\027\\'
+ end
+ if not clear or not config.picture.kitty then
+ local handle = io.popen(cmd)
+ output = output .. handle:read('*a')
+ -- `rc` doesn't work when run from inside vim (last tested in neovim-0.9)
+ local rc = {handle:close()}
+ end
+ local tty, err = io.open('/proc/self/fd/0', 'wb')
+ if tty then
+ tty:write('\x1b[s')
+ if not clear then
+ tty:write(('\x1b[%d;%dH'):format(H.picture_y, H.picture_x))
+ elseif not config.picture.kitty then
+ tty:write('\x1b[48;5;0m')
+ end
+ tty:write(output)
+ if not clear then
+ tty:write('\x1b[u')
+ end
+ tty:close()
+ else
+ H.echo('Error opening tty: ' .. err)
+ end
+end
+
H.create_autocommands = function(config)
- local gr = vim.api.nvim_create_augroup('MiniStarter', {})
+ H.augroup = vim.api.nvim_create_augroup('MiniStarter', {})
if config.autoopen then
local on_vimenter = function()
@@ -1057,7 +1149,7 @@ H.create_autocommands = function(config)
vim.cmd('noautocmd lua MiniStarter.open()')
end
- local au_opts = { group = gr, nested = true, once = true, callback = on_vimenter, desc = 'Open on VimEnter' }
+ local au_opts = { group = H.augroup, nested = true, once = true, callback = on_vimenter, desc = 'Open on VimEnter' }
vim.api.nvim_create_autocmd('VimEnter', au_opts)
end
@@ -1103,9 +1195,11 @@ H.normalize_header_footer = function(x)
end
-- Work with buffer content ---------------------------------------------------
-H.make_initial_content = function(header, items, footer)
+H.make_initial_content = function(header, items, footer, config)
local content = {}
+ H.content_add_empty_lines(content, config.top_padding)
+
-- Add header lines
for _, l in ipairs(header) do
H.content_add_line(content, { H.content_unit(l, 'header', 'MiniStarterHeader') })
@@ -1116,7 +1210,7 @@ H.make_initial_content = function(header, items, footer)
H.content_add_items(content, items)
-- Add footer lines
- H.content_add_empty_lines(content, #footer > 0 and 1 or 0)
+ H.content_add_empty_lines(content, #footer > 0 and config.footer_offset or 0)
for _, l in ipairs(footer) do
H.content_add_line(content, { H.content_unit(l, 'footer', 'MiniStarterFooter') })
end
@@ -1319,11 +1413,23 @@ H.make_buffer_autocmd = function(buf_id)
au('VimResized', function() MiniStarter.refresh(buf_id) end, 'Refresh')
au('CursorMoved', function() H.position_cursor_on_current_item(buf_id) end, 'Position cursor')
+ local config = H.get_config()
+
local cache_showtabline = vim.o.showtabline
au('BufLeave', function()
if vim.o.cmdheight > 0 then vim.cmd("echo ''") end
if vim.o.showtabline == 1 then vim.o.showtabline = cache_showtabline end
+ H.draw_picture(config, true)
end, 'On BufLeave')
+
+ au('BufEnter', function()
+ H.draw_picture(config, false)
+ --local timer = vim.loop.new_timer()
+ --timer:start(0, 0, vim.schedule_wrap(function()
+ -- timer:stop()
+ -- H.draw_picture(config, false)
+ --end))
+ end, 'On BufEnter')
end
H.apply_buffer_options = function(buf_id)
@@ -1336,7 +1442,7 @@ H.apply_buffer_options = function(buf_id)
vim.api.nvim_feedkeys('\28\14', 'nx', false)
-- Having `noautocmd` is crucial for performance: ~9ms without it, ~1.6ms with it
- vim.cmd('noautocmd silent! set filetype=ministarter')
+ vim.cmd('noautocmd silent! set filetype=dashboard')
local options = {
-- Taken from 'vim-startify'
@@ -1383,12 +1489,8 @@ H.apply_buffer_mappings = function(buf_id)
buf_keymap('<CR>', 'MiniStarter.eval_current_item()')
- buf_keymap('<Up>', [[MiniStarter.update_current_item('prev')]])
- buf_keymap('<C-p>', [[MiniStarter.update_current_item('prev')]])
- buf_keymap('<M-k>', [[MiniStarter.update_current_item('prev')]])
- buf_keymap('<Down>', [[MiniStarter.update_current_item('next')]])
- buf_keymap('<C-n>', [[MiniStarter.update_current_item('next')]])
- buf_keymap('<M-j>', [[MiniStarter.update_current_item('next')]])
+ buf_keymap('<C-k>', [[MiniStarter.update_current_item('prev')]])
+ buf_keymap('<C-j>', [[MiniStarter.update_current_item('next')]])
-- Make all special symbols to update query
for _, key in ipairs(vim.split(H.get_config().query_updaters, '')) do
@@ -1464,21 +1566,21 @@ H.is_something_shown = function()
end
-- Utilities ------------------------------------------------------------------
-H.error = function(msg) error('(mini.starter) ' .. msg, 0) end
+H.error = function(msg) error('' .. msg, 0) end
H.check_type = function(name, val, ref, allow_nil)
if type(val) == ref or (ref == 'callable' and vim.is_callable(val)) or (allow_nil and val == nil) then return end
H.error(string.format('`%s` should be %s, not %s', name, ref, type(val)))
end
-H.set_buf_name = function(buf_id, name) vim.api.nvim_buf_set_name(buf_id, 'ministarter://' .. buf_id .. '/' .. name) end
+H.set_buf_name = function(buf_id, name) vim.api.nvim_buf_set_name(buf_id, '*dashboard*') end
H.echo = function(msg, is_important)
if H.get_config().silent then return end
-- Construct message chunks
msg = type(msg) == 'string' and { { msg } } or msg
- table.insert(msg, 1, { '(mini.starter) ', 'WarningMsg' })
+ table.insert(msg, 1, { '', 'Warning' })
-- Avoid hit-enter-prompt
local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
@@ -1527,7 +1629,7 @@ H.eval_fun_or_string = function(x, string_as_cmd)
if type(x) == 'function' then return x() end
if type(x) == 'string' then
if string_as_cmd then
- vim.cmd(x)
+ pcall(function() vim.cmd(x) end)
else
return x
end
|