diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 1864ba169f..66c295a2f5 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -162,10 +162,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dict opt "UI already attached to channel: %" PRId64, channel_id); return; } + if (!ui_can_attach_more()) { + api_set_error(err, kErrorTypeException, "Maximum UI count reached"); + return; + } if (width <= 0 || height <= 0) { - api_set_error(err, kErrorTypeValidation, - "Expected width > 0 and height > 0"); + api_set_error(err, kErrorTypeValidation, "Expected width > 0 and height > 0"); return; } RemoteUI *ui = xcalloc(1, sizeof(RemoteUI)); diff --git a/src/nvim/ui.c b/src/nvim/ui.c index cc4e6d27eb..44ca7ea6bc 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -371,9 +371,14 @@ void do_autocmd_uienter_all(void) } } +bool ui_can_attach_more(void) +{ + return ui_count < MAX_UI_COUNT; +} + void ui_attach_impl(RemoteUI *ui, uint64_t chanid) { - if (ui_count == MAX_UI_COUNT) { + if (ui_count >= MAX_UI_COUNT) { abort(); } if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug] @@ -419,7 +424,7 @@ void ui_detach_impl(RemoteUI *ui, uint64_t chanid) } } - if (shift_index == MAX_UI_COUNT) { + if (shift_index >= MAX_UI_COUNT) { abort(); } diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua index 190489bac5..5ef1cd6c18 100644 --- a/test/functional/api/ui_spec.lua +++ b/test/functional/api/ui_spec.lua @@ -71,6 +71,23 @@ describe('nvim_ui_attach()', function() pcall_err(request, 'nvim_ui_attach', 40, 10, { rgb = false }) ) end) + + it('does not crash if maximum UI count is reached', function() + t.skip(t.is_os('win'), 'n.connect() hangs on Windows') + local server = api.nvim_get_vvar('servername') + local screens = {} --- @type test.functional.ui.screen[] + for i = 1, 16 do + screens[i] = Screen.new(nil, nil, nil, n.connect(server)) + end + eq( + -- 0 is kErrorTypeException + { false, { 0, 'Maximum UI count reached' } }, + { n.connect(server):request('nvim_ui_attach', 80, 24, {}) } + ) + for i = 1, 16 do + screens[i]:detach() + end + end) end) describe('nvim_ui_send', function() @@ -100,9 +117,9 @@ describe('nvim_ui_send', function() poke_eventloop() screen:expect([[ - ^ | - {1:~ }|*8 - | + ^ | + {1:~ }|*8 + | ]]) eq('Hello world', table.concat(read_data)) @@ -130,9 +147,9 @@ describe('nvim_ui_send', function() poke_eventloop() screen:expect([[ - ^ | - {1:~ }|*8 - | + ^ | + {1:~ }|*8 + | ]]) eq('', table.concat(read_data)) diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua index 047cc9df96..36c3d43492 100644 --- a/test/functional/ui/embed_spec.lua +++ b/test/functional/ui/embed_spec.lua @@ -299,7 +299,7 @@ end) describe('--embed --listen UI', function() it('waits for connection on listening address', function() - t.skip(t.is_os('win')) + t.skip(t.is_os('win'), 'n.connect() hangs on Windows') clear() local child_server = assert(n.new_pipename()) fn.jobstart({