From 7cd5356a6f89a46d83bbba9b7f6496b67f054629 Mon Sep 17 00:00:00 2001 From: Tom Ampuero <46233260+tampueroc@users.noreply.github.com> Date: Sun, 13 Jul 2025 21:43:11 +0100 Subject: feat(net): vim.net.request(), :edit [url] #34140 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Nvim depends on netrw to download/request URL contents. Solution: - Add `vim.net.request()` as a thin curl wrapper: - Basic GET with --silent, --show-error, --fail, --location, --retry - Optional `opts.outpath` to save to a file - Operates asynchronously. Pass an `on_response` handler to get the result. - Add integ tests (requires NVIM_TEST_INTEG to be set) to test success and 404 failure. - Health check for missing `curl`. - Handle `:edit https://…` using `vim.net.request()`. API Usage: 1. Asynchronous request: vim.net.request('https://httpbingo.org/get', { retry = 2 }, function(err, response) if err then print('Fetch failed:', err) else print('Got body of length:', #response.body) end end) 2. Download to file: vim.net.request('https://httpbingo.org/get', { outpath = 'out_async.txt' }, function(err) if err then print('Error:', err) end end) 3. Remote :edit integration (in runtime/plugin/net.lua) fetches into buffer: :edit https://httpbingo.org/get --- runtime/lua/vim/net.lua | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 runtime/lua/vim/net.lua (limited to 'runtime/lua/vim/net.lua') diff --git a/runtime/lua/vim/net.lua b/runtime/lua/vim/net.lua new file mode 100644 index 0000000000..199b115e93 --- /dev/null +++ b/runtime/lua/vim/net.lua @@ -0,0 +1,63 @@ +local M = {} + +--- Makes an HTTP GET request to the given URL (asynchronous). +--- +--- This function operates in one mode: +--- - Asynchronous (non-blocking): Returns immediately and passes the response object to the +--- provided `on_response` handler on completetion. +--- +--- @param url string The URL for the request. +--- @param opts? table Optional parameters: +--- - `verbose` (boolean|nil): Enables verbose output. +--- - `retry` (integer|nil): Number of retries on transient failures (default: 3). +--- - `outpath` (string|nil): File path to save the response body to. If set, the `body` value in the Response Object will be `true` instead of the response body. +--- @param on_response fun(err?: string, response?: { body: string|boolean }) Callback invoked on request +--- completetion. The `body` field in the response object contains the raw response data (text or binary). +--- Called with (err, nil) on failure, or (nil, { body = string|boolean }) on success. +function M.request(url, opts, on_response) + vim.validate({ + url = { url, 'string' }, + opts = { opts, 'table', true }, + on_response = { on_response, 'function' }, + }) + + opts = opts or {} + local retry = opts.retry or 3 + + -- Build curl command + local args = { 'curl' } + if opts.verbose then + table.insert(args, '--verbose') + else + vim.list_extend(args, { '--silent', '--show-error', '--fail' }) + end + vim.list_extend(args, { '--location', '--retry', tostring(retry) }) + + if opts.outpath then + vim.list_extend(args, { '--output', opts.outpath }) + end + + table.insert(args, url) + + local function on_exit(res) + local err_msg = nil + local response = nil + + if res.code ~= 0 then + err_msg = (res.stderr ~= '' and res.stderr) + or string.format('Request failed with exit code %d', res.code) + else + response = { + body = opts.outpath and true or res.stdout, + } + end + + if on_response then + on_response(err_msg, response) + end + end + + vim.system(args, {}, on_exit) +end + +return M -- cgit