Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions lua/rest-nvim/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@
--- but directly insert curl command as a comment
--- instead.
---
--- encodeuri Encodes selected URI text while preserving
--- characters that are part of the URI syntax.
---
--- encodeuricomponents Encodes selected URI text while including
--- characters that are part of the URI syntax.
---
--- decodeuri Decodes selected URI text.
---
---NOTE: All `:Rest` commands opening new window support |command-modifiers|.
---For example, you can run `:hor Rest open` to open result pane in horizontal
---split or run `:tab Rest logs` to open logs file in a new tab.
Expand All @@ -61,6 +69,7 @@ local function logger() return require("rest-nvim.logger") end
local function parser() return require("rest-nvim.parser") end
local function ui() return require("rest-nvim.ui.result") end
local function config() return require("rest-nvim.config") end
local function utils() return require("rest-nvim.utils") end
-- stylua: ignore end

---Open window based on command mods and return new window identifier
Expand Down Expand Up @@ -90,6 +99,24 @@ local function open_result_ui(opts)
vim.cmd.wincmd("p")
end

local function get_selected(buf)
local _, srow, scol = unpack(vim.fn.getpos("'<"))
local _, erow, ecol = unpack(vim.fn.getpos("'>"))
return vim.fn.getregion({ buf, srow, scol, 0 }, { buf, erow, ecol, 0 }),
vim.fn.getregionpos({ buf, srow, scol, 0 }, { buf, erow, ecol, 0 }, {
selection = true
})
end

local function encode_selected(encode)
local texts, ranges = get_selected(0)
local range = ranges[1]
local rep = vim.tbl_map(encode, texts)
local _, srow, scol = unpack(range[1])
local _, erow, ecol = unpack(range[2])
vim.api.nvim_buf_set_text(0, srow - 1, scol - 1, erow - 1, ecol, rep)
end

---@type table<string, RestCmd>
local rest_command_tbl = {
open = {
Expand All @@ -98,6 +125,30 @@ local rest_command_tbl = {
ui().enter(winnr)
end,
},
encodeuri = {
impl = function(_, opts)
if opts.range < 2 then
return
end
encode_selected(utils().encodeURI)
end,
},
encodeuricomponents = {
impl = function(_, opts)
if opts.range < 2 then
return
end
encode_selected(utils().encodeURIComponents)
end
},
decodeuri = {
impl = function(_, opts)
if opts.range < 2 then
return
end
encode_selected(utils().url_decode)
end,
},
run = {
impl = function(args, opts)
if vim.bo.filetype ~= "http" then
Expand Down Expand Up @@ -315,6 +366,7 @@ function commands.setup()
nargs = "+",
desc = "Run your HTTP requests",
bar = true,
range = true,
complete = function(arg_lead, cmdline, _)
local rest_commands = vim.tbl_keys(rest_command_tbl)
local subcmd, subcmd_arg_lead = cmdline:match("Rest*%s(%S+)%s(.*)$")
Expand Down
64 changes: 63 additions & 1 deletion lua/rest-nvim/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ local utils = {}
-- NOTE: vim.loop has been renamed to vim.uv in Neovim >= 0.10 and will be removed later
local uv = vim.uv or vim.loop

-- TODO: refactor the escape algorithm
-- - grab string after `?`
-- - split them with `&`, `=`
-- - url-encode each uricomponents
-- - concat again to original text
-- - rename option name to `smart_encode_uri`

---Encodes a string into its escaped hexadecimal representation
---@param str string Binary string to be encoded
---@param only_necessary? boolean Encode only necessary characters
---@return string
function utils.escape(str, only_necessary)
local ignore = "%w%-%.%_%~%+"
local ignore = "%w%-%.%_%~%+%%"
if only_necessary then
ignore = ignore .. "%:%/%?%=%&%#%@"
end
Expand All @@ -34,6 +41,8 @@ function utils.escape(str, only_necessary)
return encoded
end

-- TODO: proper decodeURI from https://chromium.googlesource.com/v8/v8/+/3.26.4/src/uri.js#213

---@param str string
function utils.url_decode(str)
str = string.gsub(str, "%+", " ")
Expand All @@ -43,6 +52,59 @@ function utils.url_decode(str)
return str
end

---@param uri string
---@param unescape fun(c:string):string
---@return string
local function encode(uri, unescape)
uri = uri:gsub(".", function(c)
c = unescape(c) and c or string.format("%%%02X", c:byte())
return c
end)
return uri
end

---@param c string
---@return boolean
local function is_alphanumeric(c)
-- stylua: ignore
return ('A' <= c and c <= 'Z')
or ('a' <= c and c <= 'z')
or ('0' <= c and c <= '9')
end

---reference: https://chromium.googlesource.com/v8/v8/+/3.26.4/src/uri.js#329
---@param uri string
---@return string
function utils.encodeURI(uri)
local function unescape_predicate(c)
return is_alphanumeric(c)
or c == "!"
or ("#" <= c and c <= "$") -- #$
or ("&" <= c and c <= "/") -- &'()*+,-./
or (":" <= c and c <= ";") -- :;
or c == "="
or ("?" <= c and c <= "@") -- ?@
or c == "_"
or c == "~"
end
return encode(uri, unescape_predicate)
end

---reference: https://chromium.googlesource.com/v8/v8/+/3.26.4/src/uri.js#358
---@param uri string
---@return string
function utils.encodeURIComponents(uri)
local function unescape_predicate(c)
return is_alphanumeric(c)
or c == "!"
or ("(" <= c and c <= "*") -- ()*
or ("-" <= c and c <= ".") -- -.
or c == "_"
or c == "~"
end
return encode(uri, unescape_predicate)
end

---Check if a file exists in the given `path`
---@param path string file path
---@return boolean
Expand Down
Loading