2 files changed,
239 insertions(+),
412 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-04-19 22:44:58 +0300
Authored at:
2026-04-19 13:59:57 +0300
Change ID:
lrsxvsrtppqnytzqzqmryxoxzytwtnzl
Parent:
7c9dc5c
jump to
| M | lua/curl.lua |
| M | lua/curl/health.lua |
M
lua/curl.lua
··· 1 -local curl = {} 2 -curl.config = { 3 - curl = nil, -- path to binary 4 - default_flags = { "-i" }, 5 - open_cmd = "vsplit", 6 - map_execute = "<CR>", -- in query buffer 7 -} 8 - 9 1 local H = { 10 2 cache_dir = vim.fs.joinpath(vim.fn.stdpath "data", "curl_cache"), 11 - query_buf = nil, 12 - query_file = nil, 13 3 query_buffers = {}, 14 4 query_autocmd_set = false, 15 5 output_buf = nil, ··· 17 7 running_request = nil, 18 8 } 19 9 20 -local function validate_config(config) 21 - vim.validate("curl.config", config, "table") 22 - vim.validate("curl.config.curl", config.curl, { "string", "nil" }) 23 - vim.validate("curl.config.default_flags", config.default_flags, "table") 24 - vim.validate("curl.config.open_cmd", config.open_cmd, "string") 25 - vim.validate("curl.config.map_execute", config.map_execute, "string") 26 - for i, flag in ipairs(config.default_flags) do 27 - vim.validate("curl.config.default_flags[" .. i .. "]", flag, "string") 10 +local curl = {} 11 +curl.config = { 12 + curl = nil, -- path to binary 13 + default_flags = { "-i" }, 14 + open_cmd = "vsplit", 15 + map_execute = "<CR>", 16 +} 17 + 18 +function curl.setup(opts) 19 + opts = opts or {} 20 + vim.validate("opts", opts, "table") 21 + curl.config = vim.tbl_deep_extend("force", curl.config, opts) 22 + vim.filetype.add { extension = { curl = "curl" } } 23 + 24 + if not H.command_created then 25 + vim.api.nvim_create_user_command("Curl", function() 26 + curl.open() 27 + end, { desc = "Open curl.nvim query buffer" }) 28 + H.command_created = true 28 29 end 29 -end 30 30 31 -local function is_valid_buf(buf) 32 - return type(buf) == "number" and buf > 0 and vim.api.nvim_buf_is_valid(buf) 31 + if not H.query_autocmd_set then 32 + vim.api.nvim_create_autocmd( 33 + { "BufReadPost", "BufNewFile", "BufEnter", "BufWinEnter", "BufFilePost" }, 34 + { 35 + group = vim.api.nvim_create_augroup("curl_nvim_query_files", { clear = true }), 36 + pattern = "*.curl", 37 + callback = function(args) 38 + H.attach_curl_buffer(args.buf) 39 + end, 40 + } 41 + ) 42 + for _, buf in ipairs(vim.api.nvim_list_bufs()) do 43 + H.attach_curl_buffer(buf) 44 + end 45 + H.query_autocmd_set = true 46 + end 33 47 end 34 48 35 -local function is_valid_win(win) 36 - return type(win) == "number" and win > 0 and vim.api.nvim_win_is_valid(win) 49 +function curl.open() 50 + local query_buf = H.get_query_buffer() 51 + if not query_buf then return end 52 + local query_win = H.find_window(query_buf) 53 + if query_win and vim.api.nvim_win_is_valid(query_win) then 54 + vim.api.nvim_set_current_win(query_win) 55 + else 56 + vim.api.nvim_set_current_buf(query_buf) 57 + end 37 58 end 38 59 39 -local function set_buffer_option(buf, name, value) 40 - vim.api.nvim_set_option_value(name, value, { buf = buf }) 41 -end 60 +function curl.execute() 61 + local query_buf = vim.api.nvim_get_current_buf() 62 + local query_file = vim.api.nvim_buf_get_name(query_buf) 63 + if not H.is_curl_file(query_file) then 64 + vim.notify("[curl.nvim] execute from a .curl request buffer", vim.log.levels.WARN) 65 + return 66 + end 67 + H.query_buffers[query_file] = query_buf 42 68 43 -local function configure_scratch_buffer(buf) 44 - set_buffer_option(buf, "buftype", "nofile") 45 - set_buffer_option(buf, "bufhidden", "hide") 46 - set_buffer_option(buf, "swapfile", false) 47 -end 69 + local lines = vim.api.nvim_buf_get_lines(query_buf, 0, -1, false) 70 + local start_line, end_line = H.find_query_block(lines, vim.api.nvim_win_get_cursor(0)[1]) 71 + if not start_line then 72 + vim.notify("[curl.nvim] cursor is not on a query line", vim.log.levels.INFO) 73 + return 74 + end 75 + 76 + local command, err = H.build_command(vim.list_slice(lines, start_line, end_line)) 77 + if not command then 78 + vim.notify("[curl.nvim] " .. (err or "invalid query"), vim.log.levels.WARN) 79 + return 80 + end 81 + 82 + H.save_query_buffer(query_buf, query_file) 83 + H.open_output_window(query_buf) 84 + 85 + if H.running_request then 86 + pcall(function() 87 + H.running_request:kill(15) 88 + end) 89 + H.running_request = nil 90 + end 48 91 49 -local function configure_query_buffer(buf) 50 - set_buffer_option(buf, "bufhidden", "hide") 51 - set_buffer_option(buf, "swapfile", false) 52 - set_buffer_option(buf, "modifiable", true) 92 + H.running_request = vim.system(command, { text = true }, function(result) 93 + H.running_request = nil 94 + local output = result.stdout or "" 95 + if result.stderr and result.stderr ~= "" then 96 + output = output == "" and result.stderr or (output .. "\n\n" .. result.stderr) 97 + end 98 + if output == "" then output = "curl exited with code " .. result.code end 99 + H.write_formatted_output(output) 100 + end) 53 101 end 54 102 55 -local function trim_lines(lines) 56 - while #lines > 1 and lines[#lines] == "" do 57 - table.remove(lines) 58 - end 59 - return lines 103 +function H.is_valid_buf(buf) 104 + return type(buf) == "number" and buf > 0 and vim.api.nvim_buf_is_valid(buf) 60 105 end 61 106 62 -local function trim(s) 63 - local from = s:match "^%s*()" 64 - return from > #s and "" or s:match(".*%S", from) 107 +function H.is_curl_file(path) 108 + return type(path) == "string" and path ~= "" and path:sub(-5) == ".curl" 65 109 end 66 110 67 -local function is_query_start(line) 111 +function H.is_query_start(line) 68 112 if not line then return false end 69 113 return line:match "^%s*curl%s" or line:match "^%s*curl$" or line:match "^%s*https?://" 70 114 end 71 115 72 -local function is_curl_file(path) 73 - return type(path) == "string" and path ~= "" and path:sub(-5) == ".curl" 74 -end 75 - 76 -local function get_or_create_buffer(name) 77 - local existing = vim.fn.bufnr("^" .. name .. "$") 78 - if existing ~= -1 and is_valid_buf(existing) then return existing end 116 +local function save_query_buffer(buf, file) 117 + if not H.is_valid_buf(buf) then return end 118 + file = file or vim.api.nvim_buf_get_name(buf) 119 + if not H.is_curl_file(file) then return end 79 120 80 - local buf = vim.api.nvim_create_buf(false, true) 81 - vim.api.nvim_buf_set_name(buf, name) 82 - configure_scratch_buffer(buf) 83 - return buf 121 + local ok = pcall(vim.fn.writefile, vim.api.nvim_buf_get_lines(buf, 0, -1, false), file, "b") 122 + if ok then 123 + pcall(vim.api.nvim_set_option_value, "modified", false, { buf = buf }) 124 + else 125 + vim.notify("[curl.nvim] failed to save request file: " .. file, vim.log.levels.ERROR) 126 + end 84 127 end 85 128 86 -local function ensure_dir(path) 87 - if vim.fn.isdirectory(path) == 1 then return true end 88 - return vim.fn.mkdir(path, "p") == 1 89 -end 129 +function H.query_cache_file() 130 + if vim.fn.isdirectory(H.cache_dir) ~= 1 and vim.fn.mkdir(H.cache_dir, "p") ~= 1 then 131 + vim.notify("[curl.nvim] failed to create cache dir: " .. H.cache_dir, vim.log.levels.ERROR) 132 + return nil 133 + end 90 134 91 -local function get_workspace_id() 92 135 local cwd = vim.fn.getcwd() 93 136 local base = vim.fn.fnamemodify(cwd, ":t") 94 137 if base == "" then base = "root" end 95 - return base .. "_" .. vim.fn.sha256(cwd):sub(1, 8) 138 + return vim.fs.joinpath(H.cache_dir, base .. "_" .. vim.fn.sha256(cwd):sub(1, 8) .. ".curl") 139 +end 140 + 141 +function H.attach_curl_buffer(buf) 142 + if not H.is_valid_buf(buf) then return false end 143 + local name = vim.api.nvim_buf_get_name(buf) 144 + if not H.is_curl_file(name) then return false end 145 + 146 + vim.api.nvim_set_option_value("filetype", "curl", { buf = buf }) 147 + vim.api.nvim_set_option_value("syntax", "sh", { buf = buf }) 148 + vim.api.nvim_set_option_value("commentstring", "# %s", { buf = buf }) 149 + pcall(vim.treesitter.language.register, "bash", "curl") 150 + vim.keymap.set("n", curl.config.map_execute, curl.execute, { 151 + buffer = buf, 152 + noremap = true, 153 + silent = true, 154 + desc = "Execute curl query under cursor", 155 + }) 156 + H.query_buffers[name] = buf 157 + return true 96 158 end 97 159 98 -local function get_cache_file() 99 - if not ensure_dir(H.cache_dir) then 100 - vim.notify("[curl.nvim] failed to create cache dir: " .. H.cache_dir, vim.log.levels.ERROR) 101 - return nil 102 - end 160 +function H.get_query_buffer() 161 + local file = H.query_cache_file() 162 + if not file then return nil end 103 163 104 - local cwd = vim.fn.getcwd() 105 - local hash = vim.fn.sha256(cwd) 106 - local new_file = vim.fs.joinpath(H.cache_dir, get_workspace_id() .. ".curl") 107 - local old_file = vim.fs.joinpath(H.cache_dir, hash) 164 + local buf = H.query_buffers[file] 165 + if not H.is_valid_buf(buf) then 166 + buf = vim.fn.bufadd(file) 167 + vim.fn.bufload(buf) 168 + vim.api.nvim_set_option_value("bufhidden", "hide", { buf = buf }) 169 + vim.api.nvim_set_option_value("swapfile", false, { buf = buf }) 170 + vim.api.nvim_set_option_value("modifiable", true, { buf = buf }) 108 171 109 - if vim.uv.fs_stat(old_file) then 110 - if not vim.uv.fs_stat(new_file) then 111 - vim.fn.rename(old_file, new_file) 112 - else 113 - vim.fn.rename(old_file, old_file .. ".archive") 114 - end 172 + vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI", "BufLeave", "VimLeavePre" }, { 173 + group = vim.api.nvim_create_augroup("curl_nvim_cache_" .. buf, { clear = true }), 174 + buffer = buf, 175 + callback = function() 176 + save_query_buffer(buf, file) 177 + end, 178 + }) 115 179 end 116 180 117 - return new_file 181 + H.attach_curl_buffer(buf) 182 + return buf 118 183 end 119 184 120 -local function save_query_buffer(buf, file) 121 - if not is_valid_buf(buf) then return end 122 - file = file or vim.api.nvim_buf_get_name(buf) 123 - if not is_curl_file(file) then return end 185 +local function get_output_buffer() 186 + if H.is_valid_buf(H.output_buf) then return H.output_buf end 124 187 125 - local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) 126 - local ok = pcall(vim.fn.writefile, lines, file, "b") 127 - if ok then 128 - pcall(vim.api.nvim_set_option_value, "modified", false, { buf = buf }) 188 + local existing = vim.fn.bufnr "^Curl output$" 189 + if existing ~= -1 and H.is_valid_buf(existing) then 190 + H.output_buf = existing 129 191 else 130 - vim.notify("[curl.nvim] failed to save request file: " .. file, vim.log.levels.ERROR) 192 + H.output_buf = vim.api.nvim_create_buf(false, true) 193 + vim.api.nvim_buf_set_name(H.output_buf, "Curl output") 194 + vim.api.nvim_set_option_value("buftype", "nofile", { buf = H.output_buf }) 195 + vim.api.nvim_set_option_value("bufhidden", "hide", { buf = H.output_buf }) 196 + vim.api.nvim_set_option_value("swapfile", false, { buf = H.output_buf }) 131 197 end 198 + 199 + vim.api.nvim_set_option_value("filetype", "json", { buf = H.output_buf }) 200 + vim.api.nvim_set_option_value("modifiable", false, { buf = H.output_buf }) 201 + return H.output_buf 132 202 end 133 203 134 -local function find_window_in_current_tab(bufnr) 204 +function H.find_window(buf) 135 205 for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do 136 - if vim.api.nvim_win_get_buf(win) == bufnr then return win end 206 + if vim.api.nvim_win_get_buf(win) == buf then return win end 137 207 end 138 - return nil 139 208 end 140 209 141 -local function is_json_start_line(line) 142 - return trim(line):match "^[%[%{]" ~= nil 143 -end 210 +function H.open_output_window(query_buf) 211 + local output_buf = get_output_buffer() 212 + local output_win = H.find_window(output_buf) 213 + if output_win and vim.api.nvim_win_is_valid(output_win) then return output_win end 144 214 145 -local function schedule_output_write(text, is_json) 146 - vim.schedule(function() 147 - H.write_output(text, is_json) 148 - end) 215 + local query_win = H.find_window(query_buf) or vim.api.nvim_get_current_win() 216 + if not vim.api.nvim_win_is_valid(query_win) then return nil end 217 + 218 + vim.api.nvim_set_current_win(query_win) 219 + vim.cmd(curl.config.open_cmd) 220 + vim.api.nvim_win_set_buf(0, output_buf) 221 + vim.api.nvim_set_current_win(query_win) 222 + return vim.api.nvim_get_current_win() 149 223 end 150 224 151 -local function extract_headers_and_json(output) 152 - local normalized = output:gsub("\r\n", "\n"):gsub("\r", "\n") 153 - if is_json_start_line(normalized) then return {}, normalized end 225 +function H.find_query_block(lines, cursor_line) 226 + if not lines[cursor_line] or vim.trim(lines[cursor_line]) == "" then return nil end 154 227 155 - local lines = vim.split(normalized, "\n", { plain = true }) 156 - local json_index = nil 157 - for i, line in ipairs(lines) do 158 - if is_json_start_line(line) then 159 - json_index = i 228 + local start_line 229 + for i = cursor_line, 1, -1 do 230 + if H.is_query_start(lines[i]) then 231 + start_line = i 160 232 break 161 233 end 162 - end 163 - 164 - if not json_index then return nil, nil end 165 - 166 - local headers = {} 167 - for i = 1, json_index - 1 do 168 - table.insert(headers, trim(lines[i])) 234 + if vim.trim(lines[i]) == "" then break end 169 235 end 236 + if not start_line then return nil end 170 237 171 - local json_lines = {} 172 - for i = json_index, #lines do 173 - table.insert(json_lines, lines[i]) 238 + local end_line = #lines 239 + for i = start_line + 1, #lines do 240 + if H.is_query_start(lines[i]) or vim.trim(lines[i]) == "" then 241 + end_line = i - 1 242 + break 243 + end 174 244 end 175 245 176 - return headers, table.concat(json_lines, "\n") 177 -end 178 - 179 -local function merge_headers_and_json(headers, json) 180 - if not headers or #headers == 0 then return json end 181 - local merged = vim.deepcopy(headers) 182 - vim.list_extend(merged, vim.split(json, "\n", { plain = true })) 183 - return table.concat(merged, "\n") 246 + if cursor_line < start_line or cursor_line > end_line then return nil end 247 + return start_line, end_line 184 248 end 185 249 186 -local function shell_split(input) 187 - local args = {} 188 - local current = {} 189 - local quote = nil 250 +function H.shell_split(input) 251 + local args, current, quote = {}, {}, nil 190 252 local i = 1 191 - 192 253 while i <= #input do 193 254 local ch = input:sub(i, i) 194 - 195 255 if quote == "'" then 196 256 if ch == "'" then 197 257 quote = nil ··· 222 282 table.insert(current, ch) 223 283 end 224 284 end 225 - 226 285 i = i + 1 227 286 end 228 287 ··· 231 290 return args 232 291 end 233 292 234 -local function resolve_query_buffer() 235 - if is_valid_buf(H.query_buf) then return H.query_buf end 236 - 237 - local current = vim.api.nvim_get_current_buf() 238 - local current_name = vim.api.nvim_buf_get_name(current) 239 - if is_curl_file(current_name) then 240 - H.set_query_context(current, current_name) 241 - return current 242 - end 243 - 244 - return H.get_query_buffer() 245 -end 246 - 247 -local function stop_running_request() 248 - if not H.running_request then return end 249 - pcall(function() 250 - H.running_request:kill(15) 251 - end) 252 - H.running_request = nil 253 -end 254 - 255 -local function result_to_output(result) 256 - local chunks = {} 257 - if result.stdout and result.stdout ~= "" then table.insert(chunks, result.stdout) end 258 - if result.stderr and result.stderr ~= "" then table.insert(chunks, result.stderr) end 259 - 260 - if #chunks == 0 then return "curl exited with code " .. result.code end 261 - 262 - return table.concat(chunks, "\n\n") 263 -end 264 - 265 -function H.set_query_mapping(buf) 266 - if not is_valid_buf(buf) then return end 267 - 268 - vim.keymap.set("n", curl.config.map_execute, curl.execute, { 269 - buffer = buf, 270 - noremap = true, 271 - silent = true, 272 - desc = "Execute curl query under cursor", 273 - }) 274 -end 275 - 276 -function H.attach_curl_buffer(buf) 277 - if not is_valid_buf(buf) then return false end 278 - local name = vim.api.nvim_buf_get_name(buf) 279 - if not is_curl_file(name) then return false end 280 - 281 - set_buffer_option(buf, "filetype", "curl") 282 - set_buffer_option(buf, "syntax", "sh") 283 - set_buffer_option(buf, "commentstring", "# %s") 284 - pcall(vim.treesitter.language.register, "bash", "curl") 285 - H.set_query_mapping(buf) 286 - H.query_buffers[name] = buf 287 - return true 288 -end 289 - 290 -local function create_query_buffer(file) 291 - local buf = vim.fn.bufadd(file) 292 - vim.fn.bufload(buf) 293 - configure_query_buffer(buf) 294 - H.attach_curl_buffer(buf) 295 - 296 - local group = vim.api.nvim_create_augroup("curl_nvim_cache_" .. buf, { clear = true }) 297 - vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI", "BufLeave", "VimLeavePre" }, { 298 - group = group, 299 - buffer = buf, 300 - callback = function() 301 - save_query_buffer(buf, file) 302 - end, 303 - }) 304 - 305 - return buf 306 -end 307 - 308 -function H.set_query_context(buf, file) 309 - if not is_valid_buf(buf) then return false end 310 - file = file or vim.api.nvim_buf_get_name(buf) 311 - if not is_curl_file(file) then return false end 312 - 313 - H.query_buf = buf 314 - H.query_file = file 315 - H.query_buffers[file] = buf 316 - return true 317 -end 318 - 319 -function H.setup_filetype() 320 - vim.filetype.add { 321 - extension = { curl = "curl" }, 322 - } 323 -end 324 - 325 -function H.setup_command() 326 - if H.command_created then return end 327 - 328 - vim.api.nvim_create_user_command("Curl", function() 329 - curl.open() 330 - end, { desc = "Open curl.nvim query buffer" }) 331 - H.command_created = true 332 -end 333 - 334 -function H.get_query_buffer() 335 - local file = get_cache_file() 336 - if not file then return nil end 337 - 338 - local existing = H.query_buffers[file] 339 - if not is_valid_buf(existing) then existing = create_query_buffer(file) end 340 - 341 - H.query_buffers[file] = existing 342 - H.set_query_context(existing, file) 343 - H.set_query_mapping(existing) 344 - return existing 345 -end 346 - 347 -function H.setup_query_file_autocmd() 348 - if H.query_autocmd_set then return end 349 - 350 - local group = vim.api.nvim_create_augroup("curl_nvim_query_files", { clear = true }) 351 - vim.api.nvim_create_autocmd( 352 - { "BufReadPost", "BufNewFile", "BufEnter", "BufWinEnter", "BufFilePost" }, 353 - { 354 - group = group, 355 - pattern = "*.curl", 356 - callback = function(args) 357 - H.attach_curl_buffer(args.buf) 358 - end, 359 - } 360 - ) 361 - 362 - for _, buf in ipairs(vim.api.nvim_list_bufs()) do 363 - H.attach_curl_buffer(buf) 364 - end 365 - 366 - H.query_autocmd_set = true 367 -end 368 - 369 -function H.get_output_buffer() 370 - if is_valid_buf(H.output_buf) then return H.output_buf end 371 - 372 - H.output_buf = get_or_create_buffer "Curl output" 373 - set_buffer_option(H.output_buf, "filetype", "json") 374 - set_buffer_option(H.output_buf, "modifiable", false) 375 - return H.output_buf 376 -end 377 - 378 -function H.open_output_window() 379 - local output_buf = H.get_output_buffer() 380 - local query_buf = resolve_query_buffer() 381 - if not query_buf then return nil end 382 - 383 - local query_win = find_window_in_current_tab(query_buf) or vim.api.nvim_get_current_win() 384 - local output_win = find_window_in_current_tab(output_buf) 385 - 386 - if is_valid_win(output_win) then return output_win end 387 - 388 - vim.api.nvim_set_current_win(query_win) 389 - vim.cmd(curl.config.open_cmd) 390 - output_win = vim.api.nvim_get_current_win() 391 - vim.api.nvim_win_set_buf(output_win, output_buf) 392 - vim.api.nvim_set_current_win(query_win) 393 - return output_win 394 -end 395 - 396 -function H.find_query_block(lines, cursor_line) 397 - if not lines[cursor_line] or vim.trim(lines[cursor_line]) == "" then return nil end 398 - 399 - local start_line = nil 400 - for line_no = cursor_line, 1, -1 do 401 - local line = lines[line_no] 402 - if is_query_start(line) then 403 - start_line = line_no 404 - break 405 - end 406 - if vim.trim(line) == "" then break end 407 - end 408 - if not start_line then return nil end 409 - 410 - local end_line = #lines 411 - for line_no = start_line + 1, #lines do 412 - local line = lines[line_no] 413 - if is_query_start(line) or vim.trim(line) == "" then 414 - end_line = line_no - 1 415 - break 416 - end 417 - end 418 - 419 - if cursor_line < start_line or cursor_line > end_line then return nil end 420 - return start_line, end_line 421 -end 422 - 423 293 function H.build_command(query_lines) 424 - local binary = curl.config.curl or "curl" 425 294 local body_parts = {} 426 - 427 295 for _, line in ipairs(query_lines) do 428 - local trimmed = vim.trim(line) 429 - if trimmed ~= "" and not trimmed:match "^#" then 430 - table.insert(body_parts, trimmed:gsub("\\%s*$", "")) 431 - end 296 + local s = vim.trim(line) 297 + if s ~= "" and not s:match "^#" then table.insert(body_parts, s:gsub("\\%s*$", "")) end 432 298 end 433 299 434 300 local body = vim.trim(table.concat(body_parts, " ")) 435 301 if body == "" then return nil, "empty query" end 436 302 437 - local parsed, parse_err = shell_split(body) 438 - if not parsed or #parsed == 0 then return nil, parse_err end 303 + local parsed, err = H.shell_split(body) 304 + if not parsed or #parsed == 0 then return nil, err end 439 305 if parsed[1] == "curl" then table.remove(parsed, 1) end 440 306 441 - local command = { binary, "--silent", "--show-error" } 442 - vim.list_extend(command, curl.config.default_flags) 443 - vim.list_extend(command, parsed) 444 - return command, nil 445 -end 446 - 447 -function H.write_formatted_output(output) 448 - if output == "" or vim.fn.executable "jq" ~= 1 then 449 - schedule_output_write(output, false) 450 - return 451 - end 452 - 453 - local headers, json_candidate = extract_headers_and_json(output) 454 - if not json_candidate or json_candidate == "" then 455 - schedule_output_write(output, false) 456 - return 457 - end 458 - 459 - vim.system({ "jq", "." }, { 460 - text = true, 461 - stdin = json_candidate, 462 - }, function(result) 463 - local text = output 464 - local is_json = false 465 - 466 - if result.code == 0 and result.stdout and result.stdout ~= "" then 467 - is_json = true 468 - text = merge_headers_and_json(headers, result.stdout) 469 - end 470 - 471 - schedule_output_write(text, is_json) 472 - end) 307 + local cmd = { curl.config.curl or "curl", "--silent", "--show-error" } 308 + vim.list_extend(cmd, curl.config.default_flags or {}) 309 + vim.list_extend(cmd, parsed) 310 + return cmd 473 311 end 474 312 475 313 function H.write_output(text, is_json) 476 - local output_buf = H.get_output_buffer() 477 - local lines = trim_lines(vim.split(text, "\n", { plain = true })) 314 + local buf = get_output_buffer() 315 + local lines = vim.split(text or "", "\n", { plain = true }) 316 + while #lines > 1 and lines[#lines] == "" do 317 + table.remove(lines) 318 + end 478 319 if #lines == 0 then lines = { "" } end 479 320 480 - set_buffer_option(output_buf, "modifiable", true) 481 - vim.api.nvim_buf_set_lines(output_buf, 0, -1, false, lines) 482 - set_buffer_option(output_buf, "modifiable", false) 483 - set_buffer_option(output_buf, "filetype", is_json and "json" or "text") 321 + vim.api.nvim_set_option_value("modifiable", true, { buf = buf }) 322 + vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) 323 + vim.api.nvim_set_option_value("modifiable", false, { buf = buf }) 324 + vim.api.nvim_set_option_value("filetype", is_json and "json" or "text", { buf = buf }) 484 325 end 485 326 486 -function curl.setup(opts) 487 - local config = vim.tbl_deep_extend("force", curl.config, opts or {}) 488 - validate_config(config) 489 - curl.config = config 490 - 491 - H.setup_filetype() 492 - H.setup_command() 493 - H.setup_query_file_autocmd() 494 -end 495 - 496 -function curl.open() 497 - local query_buf = H.get_query_buffer() 498 - if not query_buf then return end 499 - 500 - local query_win = find_window_in_current_tab(query_buf) 501 - if is_valid_win(query_win) then 502 - vim.api.nvim_set_current_win(query_win) 503 - else 504 - vim.api.nvim_set_current_buf(query_buf) 505 - end 506 -end 507 - 508 -function curl.execute() 509 - local query_buf = vim.api.nvim_get_current_buf() 510 - local query_file = vim.api.nvim_buf_get_name(query_buf) 511 - if not is_curl_file(query_file) then 512 - vim.notify("[curl.nvim] execute from a .curl request buffer", vim.log.levels.WARN) 327 +function H.write_formatted_output(output) 328 + if output == "" or vim.fn.executable "jq" ~= 1 then 329 + vim.schedule(function() 330 + H.write_output(output, false) 331 + end) 513 332 return 514 333 end 515 - H.set_query_context(query_buf, query_file) 516 334 517 - local lines = vim.api.nvim_buf_get_lines(query_buf, 0, -1, false) 518 - local cursor_line = vim.api.nvim_win_get_cursor(0)[1] 519 - local start_line, end_line = H.find_query_block(lines, cursor_line) 520 - if not start_line then 521 - vim.notify("[curl.nvim] cursor is not on a query line", vim.log.levels.INFO) 522 - return 335 + local lines = vim.split(output:gsub("\r\n", "\n"):gsub("\r", "\n"), "\n", { plain = true }) 336 + local json_index 337 + for i, line in ipairs(lines) do 338 + if vim.trim(line):match "^[%[%{]" then 339 + json_index = i 340 + break 341 + end 523 342 end 524 - 525 - local query_lines = vim.list_slice(lines, start_line, end_line) 526 - local command, command_err = H.build_command(query_lines) 527 - if not command then 528 - vim.notify("[curl.nvim] " .. (command_err or "invalid query"), vim.log.levels.WARN) 343 + if not json_index then 344 + vim.schedule(function() 345 + H.write_output(output, false) 346 + end) 529 347 return 530 348 end 531 349 532 - save_query_buffer(query_buf, query_file) 533 - H.open_output_window() 534 - stop_running_request() 350 + local headers = {} 351 + for i = 1, json_index - 1 do 352 + table.insert(headers, vim.trim(lines[i])) 353 + end 354 + local json_body = table.concat(vim.list_slice(lines, json_index, #lines), "\n") 535 355 536 - H.running_request = vim.system(command, { text = true }, function(result) 537 - H.running_request = nil 538 - H.write_formatted_output(result_to_output(result)) 356 + vim.system({ "jq", "." }, { text = true, stdin = json_body }, function(result) 357 + local text, is_json = output, false 358 + if result.code == 0 and result.stdout and result.stdout ~= "" then 359 + is_json = true 360 + text = #headers > 0 and (table.concat(headers, "\n") .. "\n" .. result.stdout) 361 + or result.stdout 362 + end 363 + vim.schedule(function() 364 + H.write_output(text, is_json) 365 + end) 539 366 end) 540 367 end 541 368
M
lua/curl/health.lua
··· 1 -local function check_binary(bin, msg, optional) 1 +local function check(bin, msg, optional) 2 2 if vim.fn.executable(bin) == 1 then 3 3 vim.health.ok(bin .. " found on PATH: `" .. vim.fn.exepath(bin) .. "`") 4 4 return ··· 21 21 end 22 22 23 23 vim.health.start "Required dependencies" 24 - check_binary("curl", "required to run requests", false) 24 + check("curl", "required to run requests", false) 25 25 26 26 vim.health.start "Optional dependencies" 27 - check_binary("jq", "used for JSON formatting in output buffers", true) 27 + check("jq", "used for JSON formatting in output buffers", true) 28 28 end, 29 29 }