Skip to content

Commit 70eefb5

Browse files
feat: run request by name
1 parent 16ba718 commit 70eefb5

File tree

5 files changed

+134
-56
lines changed

5 files changed

+134
-56
lines changed

lua/rest-nvim/commands.lua

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ local commands = {}
3939
local dotenv = require("rest-nvim.dotenv")
4040
local request = require("rest-nvim.request")
4141
local logger = require("rest-nvim.logger")
42+
local parser = require("rest-nvim.parser")
4243

4344

4445
---@type table<string, RestCmd>
@@ -47,12 +48,27 @@ local rest_command_tbl = {
4748
-- TODO: run request by name
4849
impl = function(args, opts)
4950
if #args > 1 then
50-
vim.notify("Running request by name isn't supported yet", vim.log.levels.INFO)
51+
vim.notify("Running multiple request isn't supported yet", vim.log.levels.WARN)
52+
return
53+
elseif #args == 1 then
54+
-- TODO: get request by name
55+
request.run_by_name(args[1])
5156
return
5257
end
5358
-- TODO: open result window here (use `:horizontal`)
5459
request.run()
5560
end,
61+
---@return string[]
62+
complete = function (args)
63+
local names = parser.get_request_names(0)
64+
local matches = vim.iter(names):filter(function (name)
65+
return name:find("^" .. vim.pesc(args))
66+
end):map(function (name)
67+
name = name:gsub("%s+", "\\ ")
68+
return name
69+
end):totable()
70+
return matches
71+
end
5672
},
5773
last = {
5874
impl = function(_, _)

lua/rest-nvim/parser/init.lua

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ local config = require("rest-nvim.config")
1616
---@field __TYPE BodyType
1717
---@field data any
1818

19+
local NAMED_REQUEST_QUERY = vim.treesitter.query.parse("http", [[
20+
(section
21+
(request_separator
22+
value: (_) @name)
23+
request: (_)) @request
24+
(section
25+
(comment
26+
name: (_) @keyword
27+
value: (_) @name
28+
(#eq? @keyword "name"))
29+
request: (_)) @request
30+
]])
31+
1932
---@param node TSNode
2033
---@param field string
2134
---@param source Source
@@ -105,7 +118,6 @@ function M.parse_body(body_node, source, context)
105118
name = get_node_field_text(body_node, "name", source),
106119
path = path,
107120
}
108-
vim.print(body.data.path)
109121
elseif body.__TYPE == "graphql" then
110122
logger.error("graphql body is not supported yet")
111123
end
@@ -129,10 +141,16 @@ function M.create_context(source)
129141
return ctx
130142
end
131143

132-
---@return TSNode? node TSNode with type `request_section`
144+
---@return TSNode? node TSNode with type `section`
133145
function M.get_cursor_request_node()
134146
local node = vim.treesitter.get_node()
135-
return node and utils.ts_find(node, "request_section")
147+
if node then
148+
node = utils.ts_find(node, "section")
149+
if not node or #node:field("request") < 1 then
150+
return
151+
end
152+
end
153+
return node
136154
end
137155

138156
---@return TSNode[]
@@ -148,6 +166,22 @@ function M.get_all_request_node()
148166
return reqs
149167
end
150168

169+
---@return TSNode?
170+
function M.get_request_node_by_name(name)
171+
local source = 0
172+
local _, tree = utils.ts_parse_source(source)
173+
local query = NAMED_REQUEST_QUERY
174+
for id, node, _metadata, _match in query:iter_captures(tree:root(), source) do
175+
local capture_name = query.captures[id]
176+
if capture_name == "name" and vim.treesitter.get_node_text(node, source) == name then
177+
local find = utils.ts_find(node, "section")
178+
if find then
179+
return find
180+
end
181+
end
182+
end
183+
end
184+
151185
---@param vd_node TSNode
152186
---@param source Source
153187
---@param ctx Context
@@ -186,34 +220,19 @@ function M.parse_request_handler(handler_node, source, context)
186220
return script.load_handler(str, context)
187221
end
188222

189-
---@param node TSNode
190-
---@param kind? string[]
191-
---@return TSNode[] siblings
192-
local function collect_prev_siblings(node, kind)
193-
local siblings = {}
194-
local n = node:prev_named_sibling()
195-
while n and n:type() ~= "request_separator" do
196-
if not kind or vim.tbl_contains(kind, n:type()) then
197-
table.insert(siblings, 0, n)
198-
end
199-
n = n:prev_named_sibling()
200-
end
201-
return siblings
202-
end
203-
204-
---@param node TSNode
205-
---@param kind? string[]
206-
---@return TSNode[] siblings
207-
local function collect_next_siblings(node, kind)
208-
local siblings = {}
209-
local n = node:next_named_sibling()
210-
while n and n:type() ~= "request_separator" do
211-
if not kind or vim.tbl_contains(kind, n:type()) then
212-
table.insert(siblings, n)
223+
---@param source Source
224+
---@return string[]
225+
function M.get_request_names(source)
226+
local _, tree = utils.ts_parse_source(source)
227+
local query = NAMED_REQUEST_QUERY
228+
local result = {}
229+
for id, node, _metadata, _match in query:iter_captures(tree:root(), source) do
230+
local capture_name = query.captures[id]
231+
if capture_name == "name" then
232+
table.insert(result, vim.treesitter.get_node_text(node, source))
213233
end
214-
n = n:next_named_sibling()
215234
end
216-
return siblings
235+
return result
217236
end
218237

219238
---Parse the request node and create Request object. Returns `nil` if parsing
@@ -223,7 +242,7 @@ end
223242
---@param context? Context
224243
---@return Request|nil
225244
function M.parse(node, source, context)
226-
assert(node:type() == "request_section")
245+
assert(node:type() == "section")
227246
context = context or Context:new()
228247
-- request should not include error
229248
if node:has_error() then
@@ -235,11 +254,14 @@ function M.parse(node, source, context)
235254
logger.error("request section doesn't have request node")
236255
return nil
237256
end
257+
local body
238258
local body_node = req_node:field("body")[1]
239-
local body = body_node and M.parse_body(body_node, source, context)
240-
if body_node and not body then
241-
logger.error("parsing body failed")
242-
return nil
259+
if body_node then
260+
body = M.parse_body(body_node, source, context)
261+
if not body then
262+
logger.error("parsing body failed")
263+
return nil
264+
end
243265
end
244266
local method = get_node_field_text(req_node, "method", source)
245267
if not method then
@@ -252,15 +274,19 @@ function M.parse(node, source, context)
252274
config.encode_url and utils.escape or nil
253275
)
254276

255-
-- FIXME: use query instead
256-
local pre_script_nodes = collect_prev_siblings(req_node, {"pre_request_script"})
257-
for _, script_node in ipairs(pre_script_nodes) do
258-
M.parse_pre_request_script(script_node, source, context)
277+
local name
278+
local handlers = {}
279+
for child, _ in node:iter_children() do
280+
local node_type = child:type()
281+
if node_type == "pre_request_script" then
282+
M.parse_pre_request_script(child, source, context)
283+
elseif node_type == "res_handler_script" then
284+
table.insert(handlers, M.parse_request_handler(child, source, context))
285+
elseif node_type == "request_separator" then
286+
name = get_node_field_text(child, "value", source)
287+
end
259288
end
260-
local handler_nodes = collect_next_siblings(req_node, {"res_handler_script"})
261-
local handlers = vim.iter(handler_nodes):map(function (n)
262-
return M.parse_request_handler(n, source, context)
263-
end):totable()
289+
264290
local headers = parse_headers(req_node, source, context)
265291
-- HACK: check if url doesn't have host
266292
if headers["host"] and url[1] == "/" then
@@ -269,6 +295,7 @@ function M.parse(node, source, context)
269295
end
270296
---@type Request
271297
return {
298+
name = name,
272299
context = context,
273300
method = method,
274301
url = url,

lua/rest-nvim/request.lua

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ local response = require("rest-nvim.response")
2424
local rest_nvim_last_request = nil
2525

2626
---@param req Request
27-
---@return boolean ok
2827
local function run_request(req)
2928
logger.debug("run_request")
3029
local client = require("rest-nvim.client.curl.cli")
@@ -59,18 +58,17 @@ local function run_request(req)
5958
-- NOTE: wrap with schedule to set vim variable outside of lua callback loop
6059
vim.schedule(ui.update)
6160
end)
62-
-- FIXME: use future instead of returning true here
63-
return true
61+
-- FIXME: return future to pass the command state
6462
end
6563

6664
---run request in current cursor position
67-
---@return boolean ok
6865
function M.run()
6966
logger.info("starting request")
7067
local req_node = parser.get_cursor_request_node()
7168
if not req_node then
7269
logger.error("failed to find request at cursor position")
73-
return false
70+
vim.notify("failed to find request at cursor position", vim.log.levels.ERROR)
71+
return
7472
end
7573
local ctx = parser.create_context(0)
7674
if vim.b._rest_nvim_env_file then
@@ -79,28 +77,52 @@ function M.run()
7977
local req = parser.parse(req_node, 0, ctx)
8078
if not req then
8179
logger.error("failed to parse request")
82-
return false
80+
vim.notify("failed to parse request", vim.log.levels.ERROR)
81+
return
82+
end
83+
local highlight = config.highlight
84+
if highlight.enable then
85+
utils.ts_highlight_node(0, req_node, require("rest-nvim.api").namespace)
86+
end
87+
run_request(req)
88+
end
89+
90+
---@param name string
91+
function M.run_by_name(name)
92+
local req_node = parser.get_request_node_by_name(name)
93+
if not req_node then
94+
logger.error("failed to find request by name: " .. name)
95+
vim.notify("failed to find request by name: " .. name, vim.log.levels.ERROR)
96+
return
97+
end
98+
local ctx = parser.create_context(0)
99+
if vim.b._rest_nvim_env_file then
100+
ctx:load_file(vim.b._rest_nvim_env_file)
101+
end
102+
local req = parser.parse(req_node, 0, ctx)
103+
if not req then
104+
logger.error("failed to parse request")
105+
vim.notify("failed to parse request", vim.log.levels.ERROR)
106+
return
83107
end
84108
local highlight = config.highlight
85109
if highlight.enable then
86110
utils.ts_highlight_node(0, req_node, require("rest-nvim.api").namespace)
87111
end
88-
return run_request(req)
112+
run_request(req)
89113
end
90114

91115
---run last request
92-
---@return boolean ok
93116
function M.run_last()
94117
local req = rest_nvim_last_request
95118
if not req then
96119
vim.notify("No last request found", vim.log.levels.WARN)
97120
return false
98121
end
99-
return run_request(req)
122+
run_request(req)
100123
end
101124

102125
---run all requests in current file with same context
103-
---@return boolean ok
104126
function M.run_all()
105127
local reqs = parser.get_all_request_node()
106128
local ctx = parser.create_context(0)
@@ -110,13 +132,13 @@ function M.run_all()
110132
vim.notify("Parsing request failed. See `:Rest logs` for more info", vim.log.levels.ERROR)
111133
return false
112134
end
135+
-- FIXME: wait for previous request ends
113136
local ok = run_request(req)
114137
if not ok then
115138
vim.notify("Running request failed. See `:Rest logs` for more info", vim.log.levels.ERROR)
116-
return false
139+
return
117140
end
118141
end
119-
return true
120142
end
121143

122144
return M

spec/api_spec.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,17 @@ X-DATE: {{$date}}
201201
assert.same(nil, vim.env["bar"])
202202
assert.same("old", vim.env["baz"])
203203
end)
204+
it("capture all request names", function ()
205+
local source = [[
206+
### first named request
207+
GET http://localhost:80
208+
### request separator that isn't a request name
209+
###
210+
# @name=second named request
211+
# additional comments
212+
GET http://localhost:80
213+
]]
214+
local names = parser.get_request_names(source)
215+
assert.same({"first named request", "second named request"}, names)
216+
end)
204217
end)

0 commit comments

Comments
 (0)