aboutsummaryrefslogtreecommitdiff
path: root/runtime/plugin/net.lua
blob: a7002b8ac3376ea650927ada9b1bd46496b6caec (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
if vim.g.loaded_nvim_net_plugin ~= nil then
  return
end
vim.g.loaded_nvim_net_plugin = true

local augroup = vim.api.nvim_create_augroup('nvim.net.remotefile', {})
local url_patterns = { 'http://*', 'https://*' }

local archive_patterns = {
  { suffix = '.tar.gz', kind = 'tar' },
  { suffix = '.tar.bz2', kind = 'tar' },
  { suffix = '.tar.xz', kind = 'tar' },
  { suffix = '.txz', kind = 'tar' },
  { suffix = '.tar', kind = 'tar' },
  { suffix = '.zip', kind = 'zip' },
}

---@param url string
---@return string
local function url_path(url)
  return (url:gsub('[?#].*$', ''))
end

---@param url string
---@return 'zip'|'tar'?
local function archive_type(url)
  local path = url_path(url)
  for _, pattern in ipairs(archive_patterns) do
    if vim.endswith(path, pattern.suffix) then
      return pattern.kind
    end
  end
end

vim.api.nvim_create_autocmd('BufReadCmd', {
  group = augroup,
  pattern = url_patterns,
  desc = 'Edit remote files (:edit https://example.com)',
  callback = function(ev)
    if vim.fn.executable('curl') ~= 1 then
      vim.notify('vim.net.request: curl not found', vim.log.levels.WARN)
      return
    end

    local url = ev.file
    local archive_kind = archive_type(url)

    vim.notify(('Fetching %s …'):format(url), vim.log.levels.INFO)

    if archive_kind then
      -- XXX: zipPlugin.vim, tarPlugin.vim don't work with non-file buffers.
      local tmpfile = vim.fn.tempname()

      vim.net.request(
        url,
        { outpath = tmpfile },
        vim.schedule_wrap(function(err)
          if err then
            vim.notify(('Failed to fetch %s: %s'):format(url, err), vim.log.levels.ERROR)
            return
          end

          if archive_kind == 'zip' then
            if vim.fn.exists('#zip') == 1 then
              vim.fn['zip#Browse'](tmpfile)
            elseif vim.fn.exists('#nvim.zip') == 1 then
              require('nvim.zip').browse(ev.buf, tmpfile)
            end
          else
            vim.fn['tar#Browse'](tmpfile)
          end

          vim.bo[ev.buf].modified = false
          vim.bo[ev.buf].buftype = 'nofile'
          vim.notify(('Loaded %s'):format(url), vim.log.levels.INFO)
        end)
      )
      return
    end

    vim.net.request(
      url,
      { outbuf = ev.buf },
      vim.schedule_wrap(function(err, _)
        if err then
          vim.notify(('Failed to fetch %s: %s'):format(url, err), vim.log.levels.ERROR)
          return
        end

        vim.api.nvim_exec_autocmds('BufRead', { group = 'filetypedetect', buf = ev.buf })
        vim.bo[ev.buf].modified = false
        vim.bo[ev.buf].buftype = 'nofile'
        vim.notify(('Loaded %s'):format(url), vim.log.levels.INFO)
      end)
    )
  end,
})

vim.api.nvim_create_autocmd('FileReadCmd', {
  group = augroup,
  pattern = url_patterns,
  desc = 'Read remote files (:read https://example.com)',
  callback = function(ev)
    if vim.fn.executable('curl') ~= 1 then
      vim.notify('vim.net.request: curl not found', vim.log.levels.WARN)
      return
    end

    local url = ev.file
    vim.notify(('Fetching %s …'):format(url), vim.log.levels.INFO)

    vim.net.request(
      url,
      {},
      vim.schedule_wrap(function(err, response)
        if err or not response then
          vim.notify(('Failed to fetch %s: %s'):format(url, err), vim.log.levels.ERROR)
          return
        end

        -- Start inserting the response at the line number given by read (e.g. :10read).
        -- FIXME: Doesn't work for :0read as '[ is set to 1. See #7177 for possible solutions.
        local start = vim.fn.line("'[")
        local lines = vim.split(response.body or '', '\n', { plain = true })
        vim.api.nvim_buf_set_lines(ev.buf, start, start, true, lines)
        vim.notify(('Loaded %s'):format(url), vim.log.levels.INFO)
      end)
    )
  end,
})