From f5839915537597ae047a0a9fd674bc377071f1b3 Mon Sep 17 00:00:00 2001 From: Shubh Date: Sat, 22 Aug 2026 20:19:55 +0530 Subject: [PATCH] fix(url): escape cmd.exe special chars in vim.ui.open() URL #41394 Problem: On Windows, vim.ui.open() passes URLs to `cmd.exe /c start`. cmd.exe treats `&` as a command separator, so query strings are truncated. Solution: Caret-escape `&|<>^%!` in URIs when the open handler is cmd.exe. --- runtime/lua/vim/ui.lua | 2 ++ test/functional/lua/ui_spec.lua | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index c34c28f23a..9abea5cf62 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -199,6 +199,8 @@ function M.open(path, opt) if open_cmd[1] == 'xdg-open' then job_opt.stdout = false job_opt.stderr = false + elseif open_cmd[1] == 'cmd.exe' and is_uri then + path = path:gsub('([&|<>^%%!])', '^%1') -- Escape cmd.exe special chars. #41337 end cmd = vim.list_extend(open_cmd, { path }) end diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua index 8cd371b714..bcd7ff7419 100644 --- a/test/functional/lua/ui_spec.lua +++ b/test/functional/lua/ui_spec.lua @@ -129,6 +129,28 @@ describe('vim.ui', function() ) end) + it('escapes cmd.exe metacharacters in URIs #41337', function() + eq( + { 'cmd.exe', '/c', 'start', '', 'https://example.com/?q=^&^|^<^>^^^%^!' }, + exec_lua(function() + vim.fn.has = function(feat) + return feat == 'win32' and 1 or 0 + end + local captured --- @type string[] + vim.system = function(cmd) + captured = cmd + return { + wait = function() + return { code = 0 } + end, + } + end + vim.ui.open('https://example.com/?q=&|<>^%!') + return captured + end) + ) + end) + it('opt.cmd #29490', function() t.matches( 'ENOENT: no such file or directory',