aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/options.txt1
-rw-r--r--runtime/doc/ui.txt21
-rw-r--r--src/nvim/api/ui.c1
-rw-r--r--src/nvim/api/ui_events.in.h2
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua15
-rw-r--r--src/nvim/generators/gen_options.lua1
-rw-r--r--src/nvim/option.c42
-rw-r--r--src/nvim/option_defs.h3
-rw-r--r--src/nvim/options.lua26
-rw-r--r--src/nvim/tui/tui.c9
-rw-r--r--src/nvim/ui.c1
-rw-r--r--src/nvim/ui_bridge.c24
-rw-r--r--test/functional/terminal/tui_spec.lua53
-rw-r--r--test/functional/ui/options_spec.lua66
-rw-r--r--test/functional/ui/screen.lua5
15 files changed, 255 insertions, 15 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 4180ca21f2..d3072d83e2 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -6192,7 +6192,6 @@ A jump table for the options with a short description can be found at |Q_op|.
When on, uses |highlight-guifg| and |highlight-guibg| attributes in
the terminal (thus using 24-bit color). Requires a ISO-8613-3
compatible terminal.
- Must be set at startup (in your |init.vim| or |--cmd|).
*'terse'* *'noterse'*
'terse' boolean (default off)
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index abbd063483..e1c5523202 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -80,6 +80,27 @@ Global Events *ui-global*
Some keys are missing in some modes.
+["option_set", name, value]
+ The value of ui related option `name` changed. The sent options are
+ listed below:
+
+ 'arabicshape'
+ 'ambiwith'
+ 'emoji'
+ 'guifont'
+ 'guifontset'
+ 'guifontwide'
+ 'showtabline'
+ 'termguicolors'
+
+ Options are not added to the list if their effects are already taken
+ care of. For instance, instead of forwarding the raw 'mouse' option
+ value, `mouse_on` and `mouse_off` directly indicate if mouse support
+ is active right now. Some options like 'ambiwith' have already taken
+ effect on the grid, where appropriate empty cells are added, however
+ an ui might still use these options when rendering raw text sent from
+ Nvim, like the text of the cmdline when |ui-ext-cmdline| is set.
+
["mode_change", mode, mode_idx]
The mode changed. The first parameter `mode` is a string representing
the current mode. `mode_idx` is an index into the array received in
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index a9eaccfac5..35508fde6b 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -93,6 +93,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->suspend = remote_ui_suspend;
ui->set_title = remote_ui_set_title;
ui->set_icon = remote_ui_set_icon;
+ ui->option_set = remote_ui_option_set;
ui->event = remote_ui_event;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 847b21072a..3d2253e918 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -58,6 +58,8 @@ void set_title(String title)
FUNC_API_SINCE(3);
void set_icon(String icon)
FUNC_API_SINCE(3);
+void option_set(String name, Object value)
+ FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
void popupmenu_show(Array items, Integer selected, Integer row, Integer col)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index d2b90db707..2666ca6e6f 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -37,7 +37,7 @@ function write_arglist(output, ev, need_copy)
for j = 1, #ev.parameters do
local param = ev.parameters[j]
local kind = string.upper(param[1])
- local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING")
+ local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT")
output:write(' ADD(args, ')
if do_copy then
output:write('copy_object(')
@@ -91,7 +91,7 @@ for i = 1, #events do
recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
argc = argc+2
elseif param[1] == 'Array' then
- send = send..' Array copy_'..param[2]..' = copy_array('..param[2]..');\n'
+ send = send..' Array '..copy..' = copy_array('..param[2]..');\n'
argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
recv = (recv..' Array '..param[2]..
' = (Array){.items = argv['..argc..'],'..
@@ -99,6 +99,15 @@ for i = 1, #events do
recv_argv = recv_argv..', '..param[2]
recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n'
argc = argc+2
+ elseif param[1] == 'Object' then
+ send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
+ send = send..' *'..copy..' = copy_object('..param[2]..');\n'
+ argv = argv..', '..copy
+ recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
+ recv_argv = recv_argv..', '..param[2]
+ recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'..
+ ' xfree(argv['..argc..']);\n')
+ argc = argc+1
elseif param[1] == 'Integer' or param[1] == 'Boolean' then
argv = argv..', INT2PTR('..param[2]..')'
recv_argv = recv_argv..', PTR2INT(argv['..argc..'])'
@@ -119,7 +128,7 @@ for i = 1, #events do
write_signature(bridge_output, ev, 'UI *ui')
bridge_output:write('\n{\n')
bridge_output:write(send)
- bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
+ bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n')
end
end
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 36562c0be9..fdc00d5dc0 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -36,6 +36,7 @@ local redraw_flags={
all_windows='P_RALL',
everything='P_RCLR',
curswant='P_CURSWANT',
+ ui_option='P_UI_OPTION',
}
local list_flags={
diff --git a/src/nvim/option.c b/src/nvim/option.c
index f8a05f133d..499cf79836 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -74,6 +74,7 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
@@ -248,6 +249,7 @@ typedef struct vimoption {
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_NDNAME 0x20000000U ///< only normal dir name chars allowed
+#define P_UI_OPTION 0x40000000U ///< send option to remote ui
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \
@@ -1188,6 +1190,7 @@ do_set (
set_options_default(OPT_FREE | opt_flags);
didset_options();
didset_options2();
+ ui_refresh_options();
redraw_all_later(CLEAR);
} else {
showoptions(1, opt_flags);
@@ -1815,6 +1818,10 @@ do_set (
NULL, false, NULL);
reset_v_option_vars();
xfree(saved_origval);
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ STRING_OBJ(cstr_as_string(*(char **)varp)));
+ }
}
} else {
// key code option(FIXME(tarruda): Show a warning or something
@@ -2417,6 +2424,10 @@ static char *set_string_option(const int opt_idx, const char *const value,
NULL, false, NULL);
reset_v_option_vars();
xfree(saved_oldval);
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ STRING_OBJ(cstr_as_string((char *)(*varp))));
+ }
}
return r;
@@ -4024,6 +4035,10 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
(char_u *) options[opt_idx].fullname,
NULL, false, NULL);
reset_v_option_vars();
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ BOOLEAN_OBJ(value));
+ }
}
comp_col(); /* in case 'ruler' or 'showcmd' changed */
@@ -4429,6 +4444,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
(char_u *) options[opt_idx].fullname,
NULL, false, NULL);
reset_v_option_vars();
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ INTEGER_OBJ(value));
+ }
}
comp_col(); /* in case 'columns' or 'ls' changed */
@@ -4999,6 +5018,29 @@ static int optval_default(vimoption_T *p, char_u *varp)
return STRCMP(*(char_u **)varp, p->def_val[dvi]) == 0;
}
+/// Send update to UIs with values of UI relevant options
+void ui_refresh_options(void)
+{
+ for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
+ uint32_t flags = options[opt_idx].flags;
+ if (!(flags & P_UI_OPTION)) {
+ continue;
+ }
+ String name = cstr_as_string(options[opt_idx].fullname);
+ void *varp = options[opt_idx].var;
+ Object value = OBJECT_INIT;
+ if (flags & P_BOOL) {
+ value = BOOLEAN_OBJ(*(int *)varp);
+ } else if (flags & P_NUM) {
+ value = INTEGER_OBJ(*(long *)varp);
+ } else if (flags & P_STRING) {
+ // cstr_as_string handles NULL string
+ value = STRING_OBJ(cstr_as_string(*(char **)varp));
+ }
+ ui_call_option_set(name, value);
+ }
+}
+
/*
* showoneopt: show the value of one option
* must not be called with a hidden option!
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index b16f222705..1b978137ae 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -447,6 +447,9 @@ EXTERN char_u *p_popt; // 'printoptions'
EXTERN char_u *p_header; // 'printheader'
EXTERN int p_prompt; // 'prompt'
EXTERN char_u *p_guicursor; // 'guicursor'
+EXTERN char_u *p_guifont; // 'guifont'
+EXTERN char_u *p_guifontset; // 'guifontset'
+EXTERN char_u *p_guifontwide; // 'guifontwide'
EXTERN char_u *p_hf; // 'helpfile'
EXTERN long p_hh; // 'helpheight'
EXTERN char_u *p_hlg; // 'helplang'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 16f472230a..45efd49391 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -68,7 +68,8 @@ return {
type='bool', scope={'global'},
vi_def=true,
vim=true,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
+
varname='p_arshape',
defaults={if_true={vi=true}}
},
@@ -91,7 +92,7 @@ return {
full_name='ambiwidth', abbreviation='ambw',
type='string', scope={'global'},
vi_def=true,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
varname='p_ambw',
defaults={if_true={vi="single"}}
},
@@ -661,7 +662,7 @@ return {
full_name='emoji', abbreviation='emo',
type='bool', scope={'global'},
vi_def=true,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
varname='p_emoji',
defaults={if_true={vi=true}}
},
@@ -1021,23 +1022,26 @@ return {
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ varname='p_guifont',
+ redraw={'everything', 'ui_option'},
+ defaults={if_true={vi=""}}
},
{
full_name='guifontset', abbreviation='gfs',
type='string', list='onecomma', scope={'global'},
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ varname='p_guifontset',
+ redraw={'everything', 'ui_option'},
+ defaults={if_true={vi=""}}
},
{
full_name='guifontwide', abbreviation='gfw',
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
vi_def=true,
- redraw={'everything'},
- enable_if=false,
+ redraw={'everything', 'ui_option'},
+ varname='p_guifontwide',
+ defaults={if_true={vi=""}}
},
{
full_name='guioptions', abbreviation='go',
@@ -2164,7 +2168,7 @@ return {
full_name='showtabline', abbreviation='stal',
type='number', scope={'global'},
vi_def=true,
- redraw={'all_windows'},
+ redraw={'all_windows', 'ui_option'},
varname='p_stal',
defaults={if_true={vi=1}}
},
@@ -2435,7 +2439,7 @@ return {
full_name='termguicolors', abbreviation='tgc',
type='bool', scope={'global'},
vi_def=false,
- redraw={'everything'},
+ redraw={'everything', 'ui_option'},
varname='p_tgc',
defaults={if_true={vi=false}}
},
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 9ff1acf64a..49aa41b9b0 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -151,6 +151,7 @@ UI *tui_start(void)
ui->suspend = tui_suspend;
ui->set_title = tui_set_title;
ui->set_icon = tui_set_icon;
+ ui->option_set= tui_option_set;
ui->event = tui_event;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
@@ -1136,6 +1137,14 @@ static void tui_set_icon(UI *ui, String icon)
{
}
+static void tui_option_set(UI *ui, String name, Object value)
+{
+ if (strequal(name.data, "termguicolors")) {
+ // NB: value for bridge is set in ui_bridge.c
+ ui->rgb = value.data.boolean;
+ }
+}
+
// NB: if we start to use this, the ui_bridge must be updated
// to make a copy for the tui thread
static void tui_event(UI *ui, char *name, Array args, bool *args_consumed)
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 3b8b3ac9a7..81da88c54a 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -339,6 +339,7 @@ void ui_attach_impl(UI *ui)
}
uis[ui_count++] = ui;
+ ui_refresh_options();
ui_refresh();
}
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 7573fa1653..0a69cf0ecb 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -66,6 +66,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
rv->bridge.suspend = ui_bridge_suspend;
rv->bridge.set_title = ui_bridge_set_title;
rv->bridge.set_icon = ui_bridge_set_icon;
+ rv->bridge.option_set = ui_bridge_option_set;
rv->scheduler = scheduler;
for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) {
@@ -144,6 +145,29 @@ static void ui_bridge_highlight_set_event(void **argv)
xfree(argv[1]);
}
+static void ui_bridge_option_set(UI *ui, String name, Object value)
+{
+ // Assumes bridge is only used by TUI
+ if (strequal(name.data, "termguicolors")) {
+ ui->rgb = value.data.boolean;
+ }
+ String copy_name = copy_string(name);
+ Object *copy_value = xmalloc(sizeof(Object));
+ *copy_value = copy_object(value);
+ UI_BRIDGE_CALL(ui, option_set, 4, ui,
+ copy_name.data, INT2PTR(copy_name.size), copy_value);
+}
+static void ui_bridge_option_set_event(void **argv)
+{
+ UI *ui = UI(argv[0]);
+ String name = (String){ .data = argv[1], .size = (size_t)argv[2] };
+ Object value = *(Object *)argv[3];
+ ui->option_set(ui, name, value);
+ api_free_string(name);
+ api_free_object(value);
+ xfree(argv[3]);
+}
+
static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index bf3c6bdb3a..745bfeecf9 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -4,6 +4,7 @@ local global_helpers = require('test.helpers')
local uname = global_helpers.uname
local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers')
+local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
local feed_data = thelpers.feed_data
local feed_command = helpers.feed_command
@@ -179,6 +180,58 @@ describe('tui', function()
{3:-- TERMINAL --} |
]])
end)
+
+ it('allows termguicolors to be set at runtime', function()
+ screen:set_option('rgb', true)
+ screen:set_default_attr_ids({
+ [1] = {reverse = true},
+ [2] = {foreground = 13, special = Screen.colors.Grey0},
+ [3] = {special = Screen.colors.Grey0, bold = true, reverse = true},
+ [4] = {bold = true},
+ [5] = {special = Screen.colors.Grey0, reverse = true, foreground = 4},
+ [6] = {foreground = 4, special = Screen.colors.Grey0},
+ [7] = {special = Screen.colors.Grey0, reverse = true, foreground = Screen.colors.SeaGreen4},
+ [8] = {foreground = Screen.colors.SeaGreen4, special = Screen.colors.Grey0},
+ [9] = {special = Screen.colors.Grey0, bold = true, foreground = Screen.colors.Blue1},
+ })
+
+ feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n')
+ feed_data('i')
+ feed_data('\022\007') -- ctrl+g
+ feed_data('\028\014') -- crtl+\ ctrl+N
+ feed_data(':set termguicolors?\n')
+ screen:expect([[
+ {5:^}{6:G} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ notermguicolors |
+ {4:-- TERMINAL --} |
+ ]])
+
+ feed_data(':set termguicolors\n')
+ screen:expect([[
+ {7:^}{8:G} |
+ {9:~ }|
+ {9:~ }|
+ {9:~ }|
+ {3:[No Name] [+] }|
+ |
+ {4:-- TERMINAL --} |
+ ]])
+
+ feed_data(':set notermguicolors\n')
+ screen:expect([[
+ {5:^}{6:G} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ |
+ {4:-- TERMINAL --} |
+ ]])
+ end)
end)
describe('tui with non-tty file descriptors', function()
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
new file mode 100644
index 0000000000..14f40b3ec1
--- /dev/null
+++ b/test/functional/ui/options_spec.lua
@@ -0,0 +1,66 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+
+describe('ui receives option updates', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(20,5)
+ screen:attach()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ local defaults = {
+ ambiwidth='single',
+ arabicshape=true,
+ emoji=true,
+ guifont='',
+ guifontset='',
+ guifontwide='',
+ showtabline=1,
+ termguicolors=false,
+ }
+
+ it("for defaults", function()
+ screen:expect(function()
+ eq(defaults, screen.options)
+ end)
+ end)
+
+ it("when setting options", function()
+ local changed = {}
+ for k,v in pairs(defaults) do
+ changed[k] = v
+ end
+
+ command("set termguicolors")
+ changed.termguicolors = true
+ screen:expect(function()
+ eq(changed, screen.options)
+ end)
+
+ command("set guifont=Comic\\ Sans")
+ changed.guifont = "Comic Sans"
+ screen:expect(function()
+ eq(changed, screen.options)
+ end)
+
+ command("set showtabline=0")
+ changed.showtabline = 0
+ screen:expect(function()
+ eq(changed, screen.options)
+ end)
+
+ command("set all&")
+ screen:expect(function()
+ eq(defaults, screen.options)
+ end)
+ end)
+end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 075d8c40d7..696feabeed 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -137,6 +137,7 @@ function Screen.new(width, height)
visual_bell = false,
suspended = false,
mode = 'normal',
+ options = {},
_default_attr_ids = nil,
_default_attr_ignore = nil,
_mouse_enabled = true,
@@ -482,6 +483,10 @@ function Screen:_handle_set_icon(icon)
self.icon = icon
end
+function Screen:_handle_option_set(name, value)
+ self.options[name] = value
+end
+
function Screen:_clear_block(top, bot, left, right)
for i = top, bot do
self:_clear_row_section(i, left, right)