From e326b5641ec94faf59db80553c82250bf1223b6f Mon Sep 17 00:00:00 2001 From: Rasmus Bertell Date: Tue, 21 Nov 2023 22:31:30 +0200 Subject: [PATCH 1/3] feat: refactor writing to buffer --- lua/rest-nvim/curl/init.lua | 77 ++++++++++++------------ lua/rest-nvim/init.lua | 12 ++-- lua/rest-nvim/request/init.lua | 38 ++++++------ lua/rest-nvim/utils/init.lua | 103 ++++++++++++++++++++++++++++++++- 4 files changed, 167 insertions(+), 63 deletions(-) diff --git a/lua/rest-nvim/curl/init.lua b/lua/rest-nvim/curl/init.lua index 36b032b4..ebfebb26 100644 --- a/lua/rest-nvim/curl/init.lua +++ b/lua/rest-nvim/curl/init.lua @@ -76,20 +76,14 @@ M.get_or_create_buf = function() local existing_bufnr = vim.fn.bufnr(tmp_name) if existing_bufnr ~= -1 then -- Set modifiable - vim.api.nvim_set_option_value("modifiable", true, { buf = existing_bufnr}) + vim.api.nvim_set_option_value("modifiable", true, { buf = existing_bufnr }) -- Prevent modified flag - vim.api.nvim_set_option_value("buftype", "nofile", { buf = existing_bufnr}) + vim.api.nvim_set_option_value("buftype", "nofile", { buf = existing_bufnr }) -- Delete buffer content - vim.api.nvim_buf_set_lines( - existing_bufnr, - 0, - vim.api.nvim_buf_line_count(existing_bufnr) - 1, - false, - {} - ) + vim.api.nvim_buf_set_lines(existing_bufnr, 0, -1, false, {}) -- Make sure the filetype of the buffer is httpResult so it will be highlighted - vim.api.nvim_set_option_value("ft", "httpResult", { buf = existing_bufnr } ) + vim.api.nvim_set_option_value("ft", "httpResult", { buf = existing_bufnr }) return existing_bufnr end @@ -116,9 +110,29 @@ local function create_callback(curl_cmd, opts) return end local res_bufnr = M.get_or_create_buf() - local header_lines = res.headers + + local headers = utils.filter(res.headers, function(value) + return value ~= "" + end, false) + + headers = utils.map(headers, function(value) + local _, _, http, status = string.find(value, "^(HTTP.*)%s+(%d+)%s*$") + vim.print(value, http, status) + + if http and status then + return http .. " " .. utils.http_status(tonumber(status)) + end + + return value + end) + + headers = utils.split_list(headers, function(value) + return string.find(value, "^HTTP.*$") + end) + res.headers = parse_headers(res.headers) - local content_type = res.headers[utils.key(res.headers,'content-type')] + + local content_type = utils.get_value(res.headers, "content-type") if content_type then content_type = content_type:match("application/([-a-z]+)") or content_type:match("text/(%l+)") end @@ -139,45 +153,32 @@ local function create_callback(curl_cmd, opts) end end - -- This can be quite verbose so let user control it - if config.get("result").show_curl_command then - vim.api.nvim_buf_set_lines(res_bufnr, 0, 0, false, { "Command: " .. curl_cmd }) - end - if config.get("result").show_url then --- Add metadata into the created buffer (status code, date, etc) -- Request statement (METHOD URL) - vim.api.nvim_buf_set_lines(res_bufnr, 0, 0, false, { method:upper() .. " " .. url }) + utils.write_block(res_bufnr, { method:upper() .. " " .. url }, false) + end + + -- This can be quite verbose so let user control it + if config.get("result").show_curl_command then + utils.write_block(res_bufnr, { "Command: " .. curl_cmd }, true) end if config.get("result").show_http_info then - local line_count = vim.api.nvim_buf_line_count(res_bufnr) - local separator = config.get("result").show_url and 0 or 1 -- HTTP version, status code and its meaning, e.g. HTTP/1.1 200 OK - vim.api.nvim_buf_set_lines( - res_bufnr, - line_count - separator, - line_count - separator, - false, - { "HTTP/1.1 " .. utils.http_status(res.status) } - ) + utils.write_block(res_bufnr, { "HTTP/1.1 " .. utils.http_status(res.status) }, false) end if config.get("result").show_headers then - local line_count = vim.api.nvim_buf_line_count(res_bufnr) -- Headers, e.g. Content-Type: application/json - vim.api.nvim_buf_set_lines( - res_bufnr, - line_count + 1, - line_count + 1 + #header_lines, - false, - header_lines - ) + for _, header_block in ipairs(headers) do + utils.write_block(res_bufnr, header_block, true) + end end --- Add the curl command results into the created buffer local formatter = config.get("result").formatters[content_type] - -- formate response body + -- format response body if type(formatter) == "function" then local ok, out = pcall(formatter, res.body) -- check if formatter ran successfully @@ -220,8 +221,8 @@ local function create_callback(curl_cmd, opts) buf_content = buf_content .. "\n#+END" local lines = utils.split(buf_content, "\n") - local line_count = vim.api.nvim_buf_line_count(res_bufnr) - 1 - vim.api.nvim_buf_set_lines(res_bufnr, line_count, line_count + #lines, false, lines) + + utils.write_block(res_bufnr, lines) -- Only open a new split if the buffer is not loaded into the current window if vim.fn.bufwinnr(res_bufnr) == -1 then diff --git a/lua/rest-nvim/init.lua b/lua/rest-nvim/init.lua index fe94261d..d8ccd375 100644 --- a/lua/rest-nvim/init.lua +++ b/lua/rest-nvim/init.lua @@ -83,7 +83,6 @@ local function load_external_payload(fileimport_string) end end - -- @param headers table HTTP headers -- @param payload table of the form { external = bool, filename_tpl= path, body_tpl = string } -- with body_tpl an array of lines @@ -101,7 +100,7 @@ local function splice_body(headers, payload) else lines = payload.body_tpl end - local content_type = headers[utils.key(headers,"content-type")] or "" + local content_type = utils.get_value(headers, "content-type") or "" local has_json = content_type:find("application/[^ ]*json") local body = "" @@ -140,8 +139,9 @@ end rest.run_request = function(req, opts) -- TODO rename result to request local result = req - local curl_raw_args = config.get("skip_ssl_verification") and vim.list_extend(result.raw, { "-k" }) - or result.raw + local curl_raw_args = config.get("skip_ssl_verification") + and vim.list_extend(result.raw, { "-k" }) + or result.raw opts = vim.tbl_deep_extend( "force", -- use value from rightmost map defaultRequestOpts, @@ -153,7 +153,9 @@ rest.run_request = function(req, opts) local spliced_body = nil if not req.body.inline and req.body.filename_tpl then curl_raw_args = vim.tbl_extend("force", curl_raw_args, { - '--data-binary', '@'..load_external_payload(req.body.filename_tpl)}) + "--data-binary", + "@" .. load_external_payload(req.body.filename_tpl), + }) else spliced_body = splice_body(result.headers, result.body) end diff --git a/lua/rest-nvim/request/init.lua b/lua/rest-nvim/request/init.lua index 957fc6a7..e41125d5 100644 --- a/lua/rest-nvim/request/init.lua +++ b/lua/rest-nvim/request/init.lua @@ -21,10 +21,10 @@ local function get_importfile_name(bufnr, start_line, stop_line) local fileimport_inlined fileimport_line = vim.api.nvim_buf_get_lines(bufnr, import_line - 1, import_line, false) -- check second char against '@' (meaning "dont inline") - fileimport_inlined = string.sub(fileimport_line[1], 2, 2) ~= '@' - fileimport_string = string.gsub(fileimport_line[1], "<@?", "", 1):gsub("^%s+", ""):gsub("%s+$", "") + fileimport_inlined = string.sub(fileimport_line[1], 2, 2) ~= "@" + fileimport_string = + string.gsub(fileimport_line[1], "<@?", "", 1):gsub("^%s+", ""):gsub("%s+$", "") return fileimport_inlined, fileimport_string - end return nil end @@ -42,7 +42,7 @@ local function get_body(bufnr, start_line, stop_line) local inline, importfile = get_importfile_name(bufnr, start_line, stop_line) local lines -- an array of strings if importfile ~= nil then - return { external = true; inline = inline; filename_tpl = importfile } + return { external = true, inline = inline, filename_tpl = importfile } else lines = vim.api.nvim_buf_get_lines(bufnr, start_line, stop_line, false) end @@ -63,7 +63,7 @@ local function get_body(bufnr, start_line, stop_line) end end - return { external = false; inline = false; body_tpl = lines2 } + return { external = false, inline = false, body_tpl = lines2 } end local function get_response_script(bufnr, start_line, stop_line) @@ -285,9 +285,9 @@ M.buf_get_request = function(bufnr, curpos) local curl_args, body_start = get_curl_args(bufnr, headers_end, end_line) - local host = headers[utils.key(headers,"host")] or "" + local host = utils.get_value(headers, "host") or "" parsed_url.url = host:gsub("%s+", "") .. parsed_url.url - headers[utils.key(headers,"host")] = nil + headers[utils.key(headers, "host")] = nil local body = get_body(bufnr, body_start, end_line) @@ -302,17 +302,17 @@ M.buf_get_request = function(bufnr, curpos) end local req = { - method = parsed_url.method, - url = parsed_url.url, - http_version = parsed_url.http_version, - headers = headers, - raw = curl_args, - body = body, - bufnr = bufnr, - start_line = start_line, - end_line = end_line, - script_str = script_str, - } + method = parsed_url.method, + url = parsed_url.url, + http_version = parsed_url.http_version, + headers = headers, + raw = curl_args, + body = body, + bufnr = bufnr, + start_line = start_line, + end_line = end_line, + script_str = script_str, + } return true, req end @@ -393,7 +393,7 @@ M.highlight = function(bufnr, start_line, end_line) higroup, { start_line - 1, 0 }, { end_line - 1, end_column }, - { regtype = "c"; inclusive = false } + { regtype = "c", inclusive = false } ) vim.defer_fn(function() diff --git a/lua/rest-nvim/utils/init.lua b/lua/rest-nvim/utils/init.lua index 25dcc77d..8cb6b997 100644 --- a/lua/rest-nvim/utils/init.lua +++ b/lua/rest-nvim/utils/init.lua @@ -182,7 +182,7 @@ M.get_variables = function() if variables[name]:match(oname) then -- Add that into the variable -- I.E if @url={{path}}:{{port}}/{{source}} - -- Substitue in path, port and source + -- Substitute in path, port and source variables[name] = variables[name]:gsub("{{" .. oname .. "}}", ovalue) end end @@ -317,6 +317,16 @@ M.key = function(tbl, key) return key end +--- Get the table value or nil if not found +--- +--- @param tbl (table) Table to iterate over +--- @param key (string) The key to the value case insensitive +--- +--- @return any +M.get_value = function(tbl, key) + return tbl[M.key(tbl, key)] +end + -- tbl_to_str recursively converts the provided table into a json string -- @param tbl Table to convert into a String -- @param json If the string should use a key:value syntax @@ -417,6 +427,97 @@ M.contains_comments = function(str) return str:find("^#") or str:find("^%s+#") end +--- Filter a table and return filtered copy +--- +--- @param tbl table The table to filter +--- @param filter function The filtering function, parameters are value, key and table +--- @param preserve_keys boolean? Should the copied table preserve keys or not, default true +--- +--- @return List|table +M.filter = function(tbl, filter, preserve_keys) + local out = {} + + preserve_keys = preserve_keys and true + + for key, val in ipairs(tbl) do + if filter(val, key, tbl) then + if preserve_keys then + out[key] = val + else + table.insert(out, val) + end + end + end + + return out +end + +--- Make a copy of the table applying the transformation function to each element. +--- Does not preserve the keys of the original table. +--- +--- @param tbl table The table to filter +--- @param transform function The transformation function, parameters are value, key and table +--- +--- @return List +M.map = function(tbl, transform) + local out = {} + + for key, val in ipairs(tbl) do + table.insert(out, transform(val, key, tbl)) + end + + return out +end + +--- Wrapper around nvim_buf_set_lines +--- +--- @param buffer integer The target buffer +--- @param block List The list of lines to write +--- @param newline boolean? Add a newline to the end, default false +--- +--- @return nil +M.write_block = function(buffer, block, newline) + local content = vim.api.nvim_buf_get_lines(buffer, 0, -1, false) + local first_line = false + + if #content == 1 and content[1] == "" then + first_line = true + end + + vim.api.nvim_buf_set_lines(buffer, first_line and 0 or -1, -1, false, block) + + if newline then + vim.api.nvim_buf_set_lines(buffer, -1, -1, false, { "" }) + end +end + +--- Split table on the elements where the function returns true +--- +--- @param tbl List +--- @param index function +--- @param inclusive boolean? If true the split value is in the first table, default false +--- +--- @return List[] +M.split_list = function(tbl, index, inclusive) + local out = { {} } + + for key, val in ipairs(tbl) do + if index(val, key, tbl) then + table.insert(out, {}) + + if inclusive then + table.insert(out[#out - 1], val) + else + table.insert(out[#out], val) + end + else + table.insert(out[#out], val) + end + end + + return out +end + -- http_status returns the status code and the meaning, e.g. 200 OK -- see https://httpstatuses.com/ for reference -- @param code The request status code From 042d6d25fe81ea83fe1e1002a844e06025d464b5 Mon Sep 17 00:00:00 2001 From: Rasmus Bertell Date: Thu, 23 Nov 2023 21:33:18 +0200 Subject: [PATCH 2/3] fix: remove unnecessary edits --- lua/rest-nvim/curl/init.lua | 3 +-- lua/rest-nvim/init.lua | 10 ++++------ lua/rest-nvim/request/init.lua | 5 ++--- lua/rest-nvim/utils/init.lua | 10 ---------- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/lua/rest-nvim/curl/init.lua b/lua/rest-nvim/curl/init.lua index ebfebb26..6fb760fd 100644 --- a/lua/rest-nvim/curl/init.lua +++ b/lua/rest-nvim/curl/init.lua @@ -117,7 +117,6 @@ local function create_callback(curl_cmd, opts) headers = utils.map(headers, function(value) local _, _, http, status = string.find(value, "^(HTTP.*)%s+(%d+)%s*$") - vim.print(value, http, status) if http and status then return http .. " " .. utils.http_status(tonumber(status)) @@ -132,7 +131,7 @@ local function create_callback(curl_cmd, opts) res.headers = parse_headers(res.headers) - local content_type = utils.get_value(res.headers, "content-type") + local content_type = res.headers[utils.key(res.headers, "content-type")] if content_type then content_type = content_type:match("application/([-a-z]+)") or content_type:match("text/(%l+)") end diff --git a/lua/rest-nvim/init.lua b/lua/rest-nvim/init.lua index d8ccd375..2d5c280d 100644 --- a/lua/rest-nvim/init.lua +++ b/lua/rest-nvim/init.lua @@ -100,7 +100,7 @@ local function splice_body(headers, payload) else lines = payload.body_tpl end - local content_type = utils.get_value(headers, "content-type") or "" + local content_type = headers[utils.key(headers, "content-type")] or "" local has_json = content_type:find("application/[^ ]*json") local body = "" @@ -139,9 +139,8 @@ end rest.run_request = function(req, opts) -- TODO rename result to request local result = req - local curl_raw_args = config.get("skip_ssl_verification") - and vim.list_extend(result.raw, { "-k" }) - or result.raw + local curl_raw_args = config.get("skip_ssl_verification") and vim.list_extend(result.raw, { "-k" }) + or result.raw opts = vim.tbl_deep_extend( "force", -- use value from rightmost map defaultRequestOpts, @@ -153,8 +152,7 @@ rest.run_request = function(req, opts) local spliced_body = nil if not req.body.inline and req.body.filename_tpl then curl_raw_args = vim.tbl_extend("force", curl_raw_args, { - "--data-binary", - "@" .. load_external_payload(req.body.filename_tpl), + "--data-binary", "@" .. load_external_payload(req.body.filename_tpl), }) else spliced_body = splice_body(result.headers, result.body) diff --git a/lua/rest-nvim/request/init.lua b/lua/rest-nvim/request/init.lua index e41125d5..3ec7d258 100644 --- a/lua/rest-nvim/request/init.lua +++ b/lua/rest-nvim/request/init.lua @@ -22,8 +22,7 @@ local function get_importfile_name(bufnr, start_line, stop_line) fileimport_line = vim.api.nvim_buf_get_lines(bufnr, import_line - 1, import_line, false) -- check second char against '@' (meaning "dont inline") fileimport_inlined = string.sub(fileimport_line[1], 2, 2) ~= "@" - fileimport_string = - string.gsub(fileimport_line[1], "<@?", "", 1):gsub("^%s+", ""):gsub("%s+$", "") + fileimport_string = string.gsub(fileimport_line[1], "<@?", "", 1):gsub("^%s+", ""):gsub("%s+$", "") return fileimport_inlined, fileimport_string end return nil @@ -285,7 +284,7 @@ M.buf_get_request = function(bufnr, curpos) local curl_args, body_start = get_curl_args(bufnr, headers_end, end_line) - local host = utils.get_value(headers, "host") or "" + local host = headers[utils.key(headers, "host")] or "" parsed_url.url = host:gsub("%s+", "") .. parsed_url.url headers[utils.key(headers, "host")] = nil diff --git a/lua/rest-nvim/utils/init.lua b/lua/rest-nvim/utils/init.lua index 8cb6b997..94375551 100644 --- a/lua/rest-nvim/utils/init.lua +++ b/lua/rest-nvim/utils/init.lua @@ -317,16 +317,6 @@ M.key = function(tbl, key) return key end ---- Get the table value or nil if not found ---- ---- @param tbl (table) Table to iterate over ---- @param key (string) The key to the value case insensitive ---- ---- @return any -M.get_value = function(tbl, key) - return tbl[M.key(tbl, key)] -end - -- tbl_to_str recursively converts the provided table into a json string -- @param tbl Table to convert into a String -- @param json If the string should use a key:value syntax From 0d22f4a22b17a2dde63ecc8d5541e101e739511e Mon Sep 17 00:00:00 2001 From: Rasmus Bertell Date: Thu, 23 Nov 2023 21:38:18 +0200 Subject: [PATCH 3/3] fix: remove stylua edits --- lua/rest-nvim/init.lua | 6 +++--- lua/rest-nvim/request/init.lua | 35 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lua/rest-nvim/init.lua b/lua/rest-nvim/init.lua index 2d5c280d..fe94261d 100644 --- a/lua/rest-nvim/init.lua +++ b/lua/rest-nvim/init.lua @@ -83,6 +83,7 @@ local function load_external_payload(fileimport_string) end end + -- @param headers table HTTP headers -- @param payload table of the form { external = bool, filename_tpl= path, body_tpl = string } -- with body_tpl an array of lines @@ -100,7 +101,7 @@ local function splice_body(headers, payload) else lines = payload.body_tpl end - local content_type = headers[utils.key(headers, "content-type")] or "" + local content_type = headers[utils.key(headers,"content-type")] or "" local has_json = content_type:find("application/[^ ]*json") local body = "" @@ -152,8 +153,7 @@ rest.run_request = function(req, opts) local spliced_body = nil if not req.body.inline and req.body.filename_tpl then curl_raw_args = vim.tbl_extend("force", curl_raw_args, { - "--data-binary", "@" .. load_external_payload(req.body.filename_tpl), - }) + '--data-binary', '@'..load_external_payload(req.body.filename_tpl)}) else spliced_body = splice_body(result.headers, result.body) end diff --git a/lua/rest-nvim/request/init.lua b/lua/rest-nvim/request/init.lua index 3ec7d258..957fc6a7 100644 --- a/lua/rest-nvim/request/init.lua +++ b/lua/rest-nvim/request/init.lua @@ -21,9 +21,10 @@ local function get_importfile_name(bufnr, start_line, stop_line) local fileimport_inlined fileimport_line = vim.api.nvim_buf_get_lines(bufnr, import_line - 1, import_line, false) -- check second char against '@' (meaning "dont inline") - fileimport_inlined = string.sub(fileimport_line[1], 2, 2) ~= "@" + fileimport_inlined = string.sub(fileimport_line[1], 2, 2) ~= '@' fileimport_string = string.gsub(fileimport_line[1], "<@?", "", 1):gsub("^%s+", ""):gsub("%s+$", "") return fileimport_inlined, fileimport_string + end return nil end @@ -41,7 +42,7 @@ local function get_body(bufnr, start_line, stop_line) local inline, importfile = get_importfile_name(bufnr, start_line, stop_line) local lines -- an array of strings if importfile ~= nil then - return { external = true, inline = inline, filename_tpl = importfile } + return { external = true; inline = inline; filename_tpl = importfile } else lines = vim.api.nvim_buf_get_lines(bufnr, start_line, stop_line, false) end @@ -62,7 +63,7 @@ local function get_body(bufnr, start_line, stop_line) end end - return { external = false, inline = false, body_tpl = lines2 } + return { external = false; inline = false; body_tpl = lines2 } end local function get_response_script(bufnr, start_line, stop_line) @@ -284,9 +285,9 @@ M.buf_get_request = function(bufnr, curpos) local curl_args, body_start = get_curl_args(bufnr, headers_end, end_line) - local host = headers[utils.key(headers, "host")] or "" + local host = headers[utils.key(headers,"host")] or "" parsed_url.url = host:gsub("%s+", "") .. parsed_url.url - headers[utils.key(headers, "host")] = nil + headers[utils.key(headers,"host")] = nil local body = get_body(bufnr, body_start, end_line) @@ -301,17 +302,17 @@ M.buf_get_request = function(bufnr, curpos) end local req = { - method = parsed_url.method, - url = parsed_url.url, - http_version = parsed_url.http_version, - headers = headers, - raw = curl_args, - body = body, - bufnr = bufnr, - start_line = start_line, - end_line = end_line, - script_str = script_str, - } + method = parsed_url.method, + url = parsed_url.url, + http_version = parsed_url.http_version, + headers = headers, + raw = curl_args, + body = body, + bufnr = bufnr, + start_line = start_line, + end_line = end_line, + script_str = script_str, + } return true, req end @@ -392,7 +393,7 @@ M.highlight = function(bufnr, start_line, end_line) higroup, { start_line - 1, 0 }, { end_line - 1, end_column }, - { regtype = "c", inclusive = false } + { regtype = "c"; inclusive = false } ) vim.defer_fn(function()