From 5973328edaedb04425e2f6cb63b2efab4aeb5fcb Mon Sep 17 00:00:00 2001 From: Shadman Date: Mon, 7 Jul 2025 05:17:06 +0600 Subject: [PATCH] feat(options): per-buffer 'busy' status #34493 Problem: Plugins cannot mark a buffer as "busy". Solution: - Add a buffer-local 'busy' option. - Show a busy indicator in the default 'statusline'. --- runtime/doc/news.txt | 2 + runtime/doc/options.txt | 9 ++++- runtime/doc/vim_diff.txt | 1 + runtime/lua/vim/_meta/options.lua | 10 ++++- src/nvim/buffer_defs.h | 2 + src/nvim/option.c | 2 + src/nvim/option_vars.h | 1 + src/nvim/options.lua | 16 ++++++++ .../functional/legacy/autocmd_option_spec.lua | 3 +- test/functional/ui/statusline_spec.lua | 37 ++++++++++++++++++- 10 files changed, 77 insertions(+), 6 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 76cc711c63..29b39d8994 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -224,6 +224,7 @@ OPTIONS • 'pummaxwidth' sets maximum width for the completion popup menu. • 'winborder' "bold" style. • |g:clipboard| accepts a string name to force any builtin clipboard tool. +• 'busy' sets a buffer "busy" status. Indicated in the default statusline. PERFORMANCE @@ -265,6 +266,7 @@ UI • Error messages are more concise: • "Error detected while processing:" changed to "Error in:". • "Error executing Lua:" changed to "Lua:". +• 'busy' status is shown in default statusline with symbol ◐ VIMSCRIPT diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 33a464ea7f..55f8ea2158 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1217,6 +1217,13 @@ A jump table for the options with a short description can be found at |Q_op|. without saving. For writing there must be matching |BufWriteCmd|, |FileWriteCmd| or |FileAppendCmd| autocommands. + *'busy'* +'busy' number (default 0) + local to buffer + Sets a buffer "busy" status. Indicated in the default statusline. + When busy status is larger then 0 busy flag is shown in statusline. + The semantics of "busy" are arbitrary, typically decided by the plugin that owns the buffer. + *'casemap'* *'cmp'* 'casemap' 'cmp' string (default "internal,keepascii") global @@ -6184,7 +6191,7 @@ A jump table for the options with a short description can be found at |Q_op|. an expensive expression can negatively affect render performance. *'statusline'* *'stl'* *E540* *E542* -'statusline' 'stl' string (default "%<%f %h%w%m%r %=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}") +'statusline' 'stl' string (default "%<%f %h%w%m%r %=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &busy > 0 ? '◐ ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}") global or local to window |global-local| Sets the |status-line|. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index b9b8d0cbb4..899a7ce665 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -404,6 +404,7 @@ Options: - 'ttimeout', 'ttimeoutlen' behavior was simplified - 'winblend' pseudo-transparency in floating windows |api-floatwin| - 'winhighlight' window-local highlights +- 'busy' busy status for buffers Performance: - Signs are implemented using Nvim's internal "marktree" (btree) structure. diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 962f5d6812..60715def3e 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -664,6 +664,14 @@ vim.o.bt = vim.o.buftype vim.bo.buftype = vim.o.buftype vim.bo.bt = vim.bo.buftype +--- Sets a buffer "busy" status. Indicated in the default statusline. +--- When busy status is larger then 0 busy flag is shown in statusline. +--- The semantics of "busy" are arbitrary, typically decided by the plugin that owns the buffer. +--- +--- @type integer +vim.o.busy = 0 +vim.bo.busy = vim.o.busy + --- Specifies details about changing the case of letters. It may contain --- these words, separated by a comma: --- internal Use internal case mapping functions, the current @@ -6853,7 +6861,7 @@ vim.wo.stc = vim.wo.statuscolumn --- --- --- @type string -vim.o.statusline = "%<%f %h%w%m%r %=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}" +vim.o.statusline = "%<%f %h%w%m%r %=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &busy > 0 ? '◐ ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}" vim.o.stl = vim.o.statusline vim.wo.statusline = vim.o.statusline vim.wo.stl = vim.wo.statusline diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 8f19c62655..ffb5b1428b 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -12,6 +12,7 @@ #include "nvim/option_defs.h" #include "nvim/os/fs_defs.h" #include "nvim/statusline_defs.h" +#include "nvim/types_defs.h" #include "nvim/undo_defs.h" /// Reference to a buffer that stores the value of buf_free_count. @@ -522,6 +523,7 @@ struct file_buffer { int b_p_bomb; ///< 'bomb' char *b_p_bh; ///< 'bufhidden' char *b_p_bt; ///< 'buftype' + OptInt b_p_busy; ///< 'busy' int b_has_qf_entry; ///< quickfix exists for buffer int b_p_bl; ///< 'buflisted' OptInt b_p_channel; ///< 'channel' diff --git a/src/nvim/option.c b/src/nvim/option.c index 9ad4970d1f..2391f9bdd5 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4681,6 +4681,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(buf->b_p_bt); case kOptBuflisted: return &(buf->b_p_bl); + case kOptBusy: + return &(buf->b_p_busy); case kOptChannel: return &(buf->b_p_channel); case kOptCopyindent: diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index 09b3a9637b..5911694879 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -280,6 +280,7 @@ EXTERN char *p_bsk; ///< 'backupskip' EXTERN char *p_breakat; ///< 'breakat' EXTERN char *p_bh; ///< 'bufhidden' EXTERN char *p_bt; ///< 'buftype' +EXTERN OptInt p_busy; ///< 'busy' EXTERN char *p_cmp; ///< 'casemap' EXTERN unsigned cmp_flags; EXTERN char *p_enc; ///< 'encoding' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index bc21674204..d320e0928a 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -950,6 +950,21 @@ local options = { type = 'string', varname = 'p_bt', }, + { + defaults = 0, + desc = [=[ + Sets a buffer "busy" status. Indicated in the default statusline. + When busy status is larger then 0 busy flag is shown in statusline. + The semantics of "busy" are arbitrary, typically decided by the plugin that owns the buffer. + ]=], + full_name = 'busy', + redraw = { 'statuslines' }, + noglob = true, + scope = { 'buf' }, + short_desc = N_('buffer is busy'), + type = 'number', + varname = 'p_busy', + }, { abbreviation = 'cmp', defaults = 'internal,keepascii', @@ -8640,6 +8655,7 @@ local options = { '%=', "%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}", "%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}", + "%{% &busy > 0 ? '◐ ' : '' %}", "%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", }), desc = [=[ diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index e616cbde6b..fca368f6bd 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -355,8 +355,7 @@ describe('au OptionSet', function() it('with string global-local (to window) option', function() local oldval = eval('&statusline') - local default_statusline = - "%<%f %h%w%m%r %=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}" + local default_statusline = api.nvim_get_option_info2('statusline', {}).default command('set statusline=foo') expected_combination({ diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 6b24e857a2..b7129dec24 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -785,8 +785,15 @@ describe('default statusline', function() exec_lua("vim.o.statusline = 'asdf'") eq('asdf', eval('&statusline')) - local default_statusline = - "%<%f %h%w%m%r %=%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}" + local default_statusline = table.concat({ + '%<', + '%f %h%w%m%r ', + '%=', + "%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}", + "%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}", + "%{% &busy > 0 ? '◐ ' : '' %}", + "%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", + }) exec_lua("vim.o.statusline = ''") @@ -799,4 +806,30 @@ describe('default statusline', function() | ]]) end) + + it('shows busy status when buffer is set to be busy', function() + exec_lua("vim.o.statusline = ''") + + screen:expect([[ + ^ | + {1:~ }|*13 + {3:[No Name] 0,0-1 All}| + | + ]]) + exec_lua('vim.o.busy = 1') + screen:expect([[ + ^ | + {1:~ }|*13 + {3:[No Name] ◐ 0,0-1 All}| + | + ]]) + + exec_lua('vim.o.busy = 0') + screen:expect([[ + ^ | + {1:~ }|*13 + {3:[No Name] 0,0-1 All}| + | + ]]) + end) end)