Skip to content

Commit b83ea48

Browse files
D-James-GHdan-wearearch
authored andcommitted
feat: allow selecting of env files with command
feat: running a response script to set env
1 parent f94c795 commit b83ea48

File tree

13 files changed

+236
-30
lines changed

13 files changed

+236
-30
lines changed

doc/rest-nvim.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ FEATURES *rest-nvim-features*
4343
- Run request under cursor
4444
- Syntax highlight for http files and output
4545
- Possibility of using environment variables in http files
46+
- Set environment variables based on the response
4647

4748

4849
===============================================================================
@@ -118,6 +119,9 @@ COMMANDS *rest-nvim-usage-commands*
118119
Same as `RestNvim` but it returns the cURL command without executing the
119120
request. Intended for debugging purposes.
120121

122+
- `:RestSelectEnv path/to/env`
123+
Set the path to an env file.
124+
121125

122126
===============================================================================
123127
REQUESTS *rest-nvim-usage-requests*
@@ -151,6 +155,40 @@ These environment variables can be obtained from:
151155
- File in the current working directory (env_file in config or '.env')
152156
- System
153157

158+
Environment variables can be set in .env format or in json.
159+
160+
To change the environment for the session use :RestSelectEnv path/to/environment
161+
162+
Environment variables can be set dynamically from the response body.
163+
(see rest-nvim-usage-dynamic-variables)
164+
165+
166+
===============================================================================
167+
RESPONSE SCRIPT *rest-nvim-response-script*
168+
169+
A lua script can be run after a request has completed. This script must below
170+
the body and wrapped in {% script %}. A context table is avaliable in the
171+
response script. The context table can be used to read the response and set
172+
environment variables.
173+
174+
The context table:
175+
`{`
176+
` result = res,`
177+
` pretty_print = vim.pretty_print,`
178+
` json_decode = vim.fn.json_decode,`
179+
` set_env = utils.set_env,`
180+
`}`
181+
182+
Now environment variables can be set like so:
183+
184+
`GET https://jsonplaceholder.typicode.com/posts/3`
185+
` `
186+
`{%`
187+
` `
188+
`local body = context.json_decode(context.result.body)`
189+
`context.set_env("postId", body.id)`
190+
` `
191+
`%}`
154192

155193
===============================================================================
156194
DYNAMIC VARIABLES *rest-nvim-usage-dynamic-variables*

doc/tags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ rest-nvim-intro rest-nvim.txt /*rest-nvim-intro*
66
rest-nvim-issues rest-nvim.txt /*rest-nvim-issues*
77
rest-nvim-license rest-nvim.txt /*rest-nvim-license*
88
rest-nvim-quick-start rest-nvim.txt /*rest-nvim-quick-start*
9+
rest-nvim-response-script rest-nvim.txt /*rest-nvim-response-script*
910
rest-nvim-usage rest-nvim.txt /*rest-nvim-usage*
1011
rest-nvim-usage-commands rest-nvim.txt /*rest-nvim-usage-commands*
1112
rest-nvim-usage-dynamic-variables rest-nvim.txt /*rest-nvim-usage-dynamic-variables*

lua/rest-nvim/curl/init.lua

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ M.get_or_create_buf = function()
5151
return new_bufnr
5252
end
5353

54-
local function create_callback(method, url)
54+
local function create_callback(method, url, script_str)
5555
return function(res)
5656
if res.exit ~= 0 then
5757
log.error("[rest.nvim] " .. utils.curl_error(res.exit))
@@ -68,6 +68,21 @@ local function create_callback(method, url)
6868
end
6969
end
7070

71+
if script_str ~= nil then
72+
local context = {
73+
result = res,
74+
pretty_print = vim.pretty_print,
75+
json_decode = vim.fn.json_decode,
76+
set_env = utils.set_env,
77+
}
78+
local env = { context = context }
79+
setmetatable(env, { __index = _G })
80+
local f = load(script_str, nil, "bt", env)
81+
if f ~= nil then
82+
f()
83+
end
84+
end
85+
7186
if config.get("result").show_url then
7287
--- Add metadata into the created buffer (status code, date, etc)
7388
-- Request statement (METHOD URL)
@@ -212,7 +227,7 @@ M.curl_cmd = function(opts)
212227
vim.api.nvim_echo({ { "[rest.nvim] Request preview:\n", "Comment" }, { curl_cmd } }, false, {})
213228
return
214229
else
215-
opts.callback = vim.schedule_wrap(create_callback(opts.method, opts.url))
230+
opts.callback = vim.schedule_wrap(create_callback(opts.method, opts.url, opts.script_str))
216231
curl[opts.method](opts)
217232
end
218233
end

lua/rest-nvim/init.lua

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ end
5656
rest.run_request = function(req, opts)
5757
local result = req
5858
opts = vim.tbl_deep_extend(
59-
"force", -- use value from rightmost map
60-
{verbose = false}, -- defaults
59+
"force", -- use value from rightmost map
60+
{ verbose = false }, -- defaults
6161
opts or {}
6262
)
6363

@@ -74,6 +74,7 @@ rest.run_request = function(req, opts)
7474
bufnr = result.bufnr,
7575
start_line = result.start_line,
7676
end_line = result.end_line,
77+
script_str = result.script_str,
7778
}
7879

7980
if not opts.verbose then
@@ -118,4 +119,13 @@ end
118119

119120
rest.request = request
120121

122+
rest.select_env = function(path)
123+
if path ~= nil then
124+
vim.validate({ path = { path, "string" } })
125+
config.set({ env_file = path })
126+
else
127+
print("No path given")
128+
end
129+
end
130+
121131
return rest

lua/rest-nvim/request/init.lua

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ local function get_importfile_name(bufnr, start_line, stop_line)
2121
local fileimport_spliced
2222
fileimport_line = vim.api.nvim_buf_get_lines(bufnr, import_line - 1, import_line, false)
2323
fileimport_string =
24-
string.gsub(fileimport_line[1], "<", "", 1):gsub("^%s+", ""):gsub("%s+$", "")
24+
string.gsub(fileimport_line[1], "<", "", 1):gsub("^%s+", ""):gsub("%s+$", "")
2525
fileimport_spliced = utils.replace_vars(fileimport_string)
2626
if path:new(fileimport_spliced):is_absolute() then
2727
return fileimport_spliced
@@ -56,14 +56,19 @@ local function get_body(bufnr, start_line, stop_line, has_json)
5656
end
5757

5858
local body = ""
59+
local vars = utils.read_variables()
5960
-- nvim_buf_get_lines is zero based and end-exclusive
6061
-- but start_line and stop_line are one-based and inclusive
6162
-- magically, this fits :-) start_line is the CRLF between header and body
6263
-- which should not be included in the body, stop_line is the last line of the body
6364
for _, line in ipairs(lines) do
65+
-- stop if a script opening tag is found
66+
if line:find("{%%") then
67+
break
68+
end
6469
-- Ignore commented lines with and without indent
6570
if not utils.contains_comments(line) then
66-
body = body .. utils.replace_vars(line)
71+
body = body .. utils.replace_vars(line, vars)
6772
end
6873
end
6974

@@ -80,13 +85,46 @@ local function get_body(bufnr, start_line, stop_line, has_json)
8085
json_body[key] = vim.fn.json_encode(val)
8186
end
8287
end
83-
return json_body
88+
return vim.fn.json_encode(json_body)
8489
end
8590
end
8691

92+
8793
return body
8894
end
8995

96+
local function get_response_script(bufnr, start_line, stop_line)
97+
local all_lines = vim.api.nvim_buf_get_lines(bufnr, start_line, stop_line, false)
98+
-- Check if there is a script
99+
local script_start_rel
100+
for i, line in ipairs(all_lines) do
101+
-- stop if a script opening tag is found
102+
if line:find("{%%") then
103+
script_start_rel = i
104+
break
105+
end
106+
end
107+
108+
if script_start_rel == nil then
109+
return nil
110+
end
111+
112+
-- Convert the relative script line number to the line number of the buffer
113+
local script_start = start_line + script_start_rel - 1
114+
115+
local script_lines = vim.api.nvim_buf_get_lines(bufnr, script_start, stop_line, false)
116+
local script_str = ""
117+
118+
for _, line in ipairs(script_lines) do
119+
script_str = script_str .. line .. "\n"
120+
if line:find("%%}") then
121+
break
122+
end
123+
end
124+
125+
return script_str:match("{%%(.-)%%}")
126+
end
127+
90128
-- is_request_line checks if the given line is a http request line according to RFC 2616
91129
local function is_request_line(line)
92130
local http_methods = { "GET", "POST", "PUT", "PATCH", "DELETE" }
@@ -202,7 +240,8 @@ local function end_request(bufnr, linenumber)
202240
end
203241
utils.move_cursor(bufnr, linenumber)
204242

205-
local next = vim.fn.search("^GET\\|^POST\\|^PUT\\|^PATCH\\|^DELETE", "cn", vim.fn.line("$"))
243+
local next = vim.fn.search("^GET\\|^POST\\|^PUT\\|^PATCH\\|^DELETE\\^###\\", "cn",
244+
vim.fn.line("$"))
206245

207246
-- restore cursor position
208247
utils.move_cursor(bufnr, oldlinenumber)
@@ -296,24 +335,28 @@ M.buf_get_request = function(bufnr, curpos)
296335
content_type:find("application/[^ ]*json")
297336
)
298337

338+
local script_str = get_response_script(bufnr, headers_end, end_line)
339+
340+
299341
if config.get("jump_to_request") then
300342
utils.move_cursor(bufnr, start_line)
301343
else
302344
utils.move_cursor(bufnr, curpos[2], curpos[3])
303345
end
304346

305347
return true,
306-
{
307-
method = parsed_url.method,
308-
url = parsed_url.url,
309-
http_version = parsed_url.http_version,
310-
headers = headers,
311-
raw = curl_args,
312-
body = body,
313-
bufnr = bufnr,
314-
start_line = start_line,
315-
end_line = end_line,
316-
}
348+
{
349+
method = parsed_url.method,
350+
url = parsed_url.url,
351+
http_version = parsed_url.http_version,
352+
headers = headers,
353+
raw = curl_args,
354+
body = body,
355+
bufnr = bufnr,
356+
start_line = start_line,
357+
end_line = end_line,
358+
script_str = script_str
359+
}
317360
end
318361

319362
M.print_request = function(req)

lua/rest-nvim/utils/init.lua

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,41 @@ M.move_cursor = function(bufnr, line, column)
1616
end)
1717
end
1818

19+
M.set_env = function(key, value)
20+
local variables = M.get_env_variables()
21+
variables[key] = value
22+
M.write_env_file(variables)
23+
end
24+
25+
M.write_env_file = function(variables)
26+
local env_file = "/" .. (config.get("env_file") or ".env")
27+
28+
-- Directories to search for env files
29+
local env_file_paths = {
30+
-- current working directory
31+
vim.fn.getcwd() .. env_file,
32+
-- directory of the currently opened file
33+
vim.fn.expand("%:p:h") .. env_file,
34+
}
35+
36+
-- If there's an env file in the current working dir
37+
for _, env_file_path in ipairs(env_file_paths) do
38+
if M.file_exists(env_file_path) then
39+
local file = io.open(env_file_path, "w+")
40+
if file ~= nil then
41+
if string.match(env_file_path, "(.-)%.json$") then
42+
file:write(vim.fn.json_encode(variables))
43+
else
44+
for key, value in pairs(variables) do
45+
file:write(key .. "=" .. value .. "\n")
46+
end
47+
end
48+
file:close()
49+
end
50+
end
51+
end
52+
end
53+
1954
M.uuid = function()
2055
local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
2156
return string.gsub(template, "[xy]", function(c)
@@ -43,10 +78,8 @@ M.read_file = function(file)
4378
return lines
4479
end
4580

46-
-- get_variables Reads the environment variables found in the env_file option
47-
-- (defualt: .env) specified in configuration or from the files being read
48-
-- with variables beginning with @ and returns a table with the variables
49-
M.get_variables = function()
81+
-- reads the variables contained in the current file
82+
M.get_file_variables = function()
5083
local variables = {}
5184

5285
-- If there is a line at the beginning with @ first
@@ -64,7 +97,11 @@ M.get_variables = function()
6497
end
6598
end
6699
end
67-
100+
return variables
101+
end
102+
-- Gets the variables from the currently selected env_file
103+
M.get_env_variables = function()
104+
local variables = {}
68105
local env_file = "/" .. (config.get("env_file") or ".env")
69106

70107
-- Directories to search for env files
@@ -78,12 +115,39 @@ M.get_variables = function()
78115
-- If there's an env file in the current working dir
79116
for _, env_file_path in ipairs(env_file_paths) do
80117
if M.file_exists(env_file_path) then
81-
for line in io.lines(env_file_path) do
82-
local vars = M.split(line, "%s*=%s*", 1)
83-
variables[vars[1]] = vars[2]
118+
if string.match(env_file_path, "(.-)%.json$") then
119+
local f = io.open(env_file_path, "r")
120+
if f ~= nil then
121+
local json_vars = f:read("*all")
122+
variables = vim.fn.json_decode(json_vars)
123+
f:close()
124+
end
125+
else
126+
for line in io.lines(env_file_path) do
127+
local vars = M.split(line, "%s*=%s*", 1)
128+
variables[vars[1]] = vars[2]
129+
end
84130
end
85131
end
86132
end
133+
return variables
134+
end
135+
136+
-- get_variables Reads the environment variables found in the env_file option
137+
-- (defualt: .env) specified in configuration or from the files being read
138+
-- with variables beginning with @ and returns a table with the variables
139+
M.get_variables = function()
140+
local variables = {}
141+
local file_variables = M.get_file_variables()
142+
local env_variables = M.get_env_variables()
143+
144+
for k, v in pairs(file_variables) do
145+
variables[k] = v
146+
end
147+
148+
for k, v in pairs(env_variables) do
149+
variables[k] = v
150+
end
87151

88152
-- For each variable name
89153
for name, _ in pairs(variables) do
@@ -165,8 +229,10 @@ end
165229
-- replace_vars replaces the env variables fields in the provided string
166230
-- with the env variable value
167231
-- @param str Where replace the placers for the env variables
168-
M.replace_vars = function(str)
169-
local vars = M.read_variables()
232+
M.replace_vars = function(str, vars)
233+
if vars == nil then
234+
vars = M.read_variables()
235+
end
170236
-- remove $dotenv tags, which are used by the vscode rest client for cross compatibility
171237
str = str:gsub("%$dotenv ", ""):gsub("%$DOTENV ", "")
172238

0 commit comments

Comments
 (0)