neovim icon indicating copy to clipboard operation
neovim copied to clipboard

feat(lua): add minimal vim.net.download wrapper using curl

Open tampueroc opened this issue 6 months ago • 11 comments
trafficstars

This PR introduces a minimal vim.net.download() implementation as a simple wrapper around curl, following the approach suggested in this comment.

The goal is to start small and provide the foundational download functionality first and then add more advanced features (e.g. authentication, headers, proxy support) in future PRs.

This PR is related to issue #23232

Implementation

  • Uses vim.system() with curl to perform the download.
  • Automatically adds --retry , --location (follow redirects) and --output plus the URL.
  • Accepts an opts table and an optional on_exit callback.
  • If you pass an on_exit callback, it runs in the background and invokes on_exit(err or nil) when done.
  • If no callback is given, it blocks until completion and returns true on success or false, errmsg on failure.

Proposed API Usage

  1. Basic synchronous download:

     local ok, err = vim.net.download("https://httpbingo.org/anything", "./anything.txt")
     if not ok then
       print("Download failed:", err)
     end
    
  2. Synchronous with custom options:

    local ok, err = vim.net.download("https://httpbingo.org/anything", "./anything.txt", {
       verbose = true,
       retry = 5,
       }
    )
    if ok then
      print("Done")
    else
      print("Error:", err)
    end
    
  3. Asynchronous with callback

    vim.net.download("https://httpbingo.org/anything", "./anything.txt", { retry = 2 }, function(err)
        if err then
            print("Download failed:", err)
        else
            print("Download successful!")
        end
    end)
    

Next Steps

After initial feedback, the implementation can be extended incrementally, adding vim.net.request() or broader curl options support in follow-up PRs.

tampueroc avatar May 11 '25 09:05 tampueroc

Needs docs and a news entry. It would help if the proposed API (for several representative use cases) was added to the PR description.

clason avatar May 11 '25 09:05 clason

We should also add a health check for this (like we do for rg); see https://github.com/nvim-treesitter/nvim-treesitter/blob/e0d029bfdc1ea0da00356203b5afbe70ca9c74c1/lua/nvim-treesitter/health.lua#L87-L93

Here it's important to show the full version output, including all build flags (since there are severely gimped curl versions out there that can lead to hard to diagnose problems). We possibly want to also print all relevant environment variables that affect curl operations.

clason avatar May 11 '25 09:05 clason

You need to add the new module to https://github.com/neovim/neovim/blob/59c45b22d9616cda448b3c3dddd3e9274c830ca3/src/gen/gen_vimdoc.lua#L135

clason avatar May 11 '25 11:05 clason

You need to add the new module to

https://github.com/neovim/neovim/blob/59c45b22d9616cda448b3c3dddd3e9274c830ca3/src/gen/gen_vimdoc.lua#L135

Thank you! I’ve been struggling to get the docs right (embarrassingly).

On my side I still have pending

  • verbose flag (agree on this one for sure)
  • retry flag (idem)

tampueroc avatar May 11 '25 11:05 tampueroc

@clason Sorry to bother, but could you guide me a bit on the current CI issue? For some reason it's only failing on the test / ubuntu asan clang unittest unit tests...

tampueroc avatar May 11 '25 13:05 tampueroc

Shouldn't something like this at least support a progress report and cancellation mechanism? Otherwise, plugin authors will end up reinventing a more functional version of this function anyway

miroshQa avatar May 11 '25 15:05 miroshQa

As a plugin author, I don't need it. Let's keep this to MVP.

clason avatar May 11 '25 15:05 clason

would it be possible to also introduce an auth flag via either to authenticate using either basic auth or via token ? Either that or an option to provide extra arguments directly to curl

mike325 avatar May 12 '25 08:05 mike325

No, at least not in this PR. You can (and should) use environment variables for local configuration.

Reminder: This is meant to be a simple function mainly for the (internal!) purpose of downloading spell files, WASM parsers, and bootstrapping plugin managers. It is not meant to replace every vim.system call to curl, otherwise it'll end up with the exact same complexity and nothing is gained. So "advanced usage" like that is out of scope here and will require a manual vim.system call (which is easy enough). What exactly "advanced" means can be discussed, but not here -- this PR is strictly scoped to the minimal set of flags required for the stated purpose.

clason avatar May 12 '25 08:05 clason

Hello, in the last push I implemented as per the feedback:

  • async/sync behaviour based on the on_exit argument along the corresponding functional tests
  • health check for env vars upper or lowercase as per curl documentation

I see that is failing on test/functional/terminal/tui_spec.lua for mac os intel, any help with that?

tampueroc avatar May 12 '25 18:05 tampueroc

for the (internal!) purpose of downloading spell files, WASM parsers

Sorry if this is off-topic, but does that mean Nvim has plan to include the plugin nvim-treesitter by default?

brianhuster avatar May 13 '25 00:05 brianhuster

As mentioned in https://github.com/neovim/neovim/pull/34140#issuecomment-2984316759 , I suggest starting with a vim.net.fetch() which, at first, might only do the basic "download" case, while keep the door open to expand it later.

The parameters of the proposed request() and download() already look nearly the same. So that suggests that we have a basic interface that allows future expansion. And they don't need to be separate functions.

justinmk avatar Jun 18 '25 14:06 justinmk

I suggest starting with a vim.net.fetch() which, at first, might only do the basic "download" case, while keep the door open to expand it later.

But isn't that exactly what this PR does (modulo fetch vs. download) naming? I don't understand why you closed this.

clason avatar Jun 18 '25 14:06 clason

Either this PR or https://github.com/neovim/neovim/pull/34140 should be closed. We don't need two separate interfaces until proven otherwise.

justinmk avatar Jun 18 '25 14:06 justinmk

Either this PR or https://github.com/neovim/neovim/pull/34140 should be closed. We don't need two separate interfaces until proven otherwise.

Yes we do -- one high-level function for downloading stuff and one (possibly private) low-level function driving :e. I thought that was what you wanted?

clason avatar Jun 18 '25 15:06 clason

I've said to "start with a basic download functionality", but I didn't mean to later add a different non-basic function. I meant that the function (vim.net.fetch()) is, initially, providing a simple feature, and later the same function can be expanded to do more complex things.

justinmk avatar Jun 18 '25 15:06 justinmk

I don't think that's a good idea (that's exactly what killed the last attempt!) but will hold my peace from here on out. It's all yours.

clason avatar Jun 18 '25 15:06 clason

Hi all, I'm closing this PR in favor of #34140, which consolidates download and fetch into a single vim.net.request() API. Thanks for the feedback here, please continue discussion over there!

tampueroc avatar Jun 24 '25 08:06 tampueroc