Skip to content

Commit 60eee6c

Browse files
author
lex
committed
feat(prompt): structured options with name|key|description tuples and jq-style dynamic mapping
- parse_structured_options: parse name|key|description tuples from <<var [...] - parse_dynamic_mapping: parse pipe + {name:path,key:path,desc:path} syntax - apply_jq_mapping: map response data to structured picker items - handle_prompt_variables: integrated both static tuples and dynamic mapping - highlight: PostePromptOptSep, PostePromptMapping{Field,Colon,Path} groups - completion: prompt_mapping context for field name completion - docs: new Prompt Variables section in README with full syntax examples - tests: 22 test cases covering all parsing and mapping scenarios
1 parent 2aceb6c commit 60eee6c

11 files changed

Lines changed: 1042 additions & 38 deletions

PROMPT_ENHANCE_PLAN.md

Lines changed: 518 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,80 @@ Requires **blink.cmp**. Auto-registers as `poste_sql` source provider.
369369
:PosteSQLCmpStatus " SQL completion status
370370
```
371371

372+
## Prompt Variables
373+
374+
Prompt variables allow interactive input when running a request. When you execute a request with `<<` directives, Poste will prompt you for values before sending.
375+
376+
### Text Input
377+
378+
```
379+
<<username
380+
```
381+
382+
Shows a text input prompt for `username`. Press `<Enter>` to confirm or `<Esc>` to cancel.
383+
384+
### Selection from List (Simple Strings)
385+
386+
```
387+
<<method [GET, POST, PUT, DELETE]
388+
```
389+
390+
Shows a picker with the listed options. Selecting one substitutes `@method = <selected>`.
391+
392+
### Selection with Display Name, Key, and Description
393+
394+
```
395+
<<method [GET|get|Send a GET request, POST|post|Send a POST request, PUT|put|Send a PUT request]
396+
```
397+
398+
Each option is formatted as `name|key|description`:
399+
- **name** — shown in the picker
400+
- **key** — the actual value substituted into the request
401+
- **description** — secondary text displayed alongside the name
402+
403+
Picker shows:
404+
```
405+
GET Send a GET request
406+
POST Send a POST request
407+
PUT Send a PUT request
408+
```
409+
410+
Selecting `GET` writes `@method = get` (the key, not the display name) into the request.
411+
412+
Two-field form (name|key, no description):
413+
```
414+
<<method [GET|get, POST|post]
415+
```
416+
417+
### Dynamic Options from Another Request
418+
419+
```http
420+
### Get commits
421+
GET https://api.github.com/repos/folke/snacks.nvim/commits
422+
423+
### Send email
424+
<<email [{{1.response.body | {name: .[].commit.author.name, key: .[].commit.author.email, desc: .[].commit.author.name} }}]
425+
426+
POST https://httpbin.org/post
427+
Content-Type: application/json
428+
429+
{"email": "{{email}}"}
430+
```
431+
432+
The pipe `|` separates the response reference from a **jq-style mapping expression**:
433+
434+
```
435+
{{<request_ref> | {name: <path>, key: <path>, desc: <path>} }}
436+
```
437+
438+
- `<request_ref>` — standard cross-request reference (e.g., `1.response.body`)
439+
- `<path>` — dot-separated path into the response, using `[]` for array iteration (jq-compatible)
440+
441+
If the response body is already in `{name, key, description}` format, the mapping can be omitted:
442+
```
443+
<<email [{{1.response.body}}]
444+
```
445+
372446
## SQL Features
373447

374448
### Connection management

examples/http/dynamic_prompts.feature.http

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
### jq
55
GET https://api.github.com/repos/jqlang/jq/commits?per_page=5
66

7+
### prompt enhance
8+
<<method [ {{jq.response.body | {name: .[].commit.author.name, key: .[].author.id, desc: .[].commit.author.email} }} ]
9+
GET https://httpbin.org/get?method={{method}}
10+
711
### Select items
812
@key = fruit
913
<<item [{{jq.response.body.[].parents[].sha}}]
@@ -20,6 +24,10 @@ GET {{base_url}}/get?fruit={{fruit}}
2024
<<method [GET, POST, PUT, DELETE]
2125
GET https://httpbin.org/get?method={{method}}
2226

27+
### Static Options
28+
<<method [GET|get|get something, POST|post|post something, PUT|put|put something, DELETE|del|delete obj]
29+
GET https://httpbin.org/get?method={{method}}
30+
2331
### Text Input
2432
<<username
2533
GET https://httpbin.org/get?user={{username}}

lua/poste/http/completion.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ M._test = {
235235
mime_types = data.mime_types,
236236
header_values = data.header_values,
237237
magic_var_defs = data.magic_var_defs,
238+
prompt_mapping_fields = data.prompt_mapping_fields,
238239
}
239240

240241
return M

lua/poste/http/context_detector.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ local function detect_context(line_before_cursor, buf, cursor_line, cursor_col)
116116
if last_open and (not last_close or last_close > last_open) then
117117
-- Cursor is inside an unclosed {{...}}
118118
local after_open = line_before_cursor:sub(#line_before_cursor - last_open + 2)
119+
-- Check if this is a prompt mapping context (contains | {)
120+
if after_open:match("|%s*{%s*$") or after_open:match("|%s*{%s*%w*$") then
121+
return "prompt_mapping", after_open
122+
end
119123
return "variable", after_open
120124
end
121125

lua/poste/http/data.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,4 +875,14 @@ M.script_api_docs = {
875875
},
876876
}
877877

878+
---------------------------------------------------------------------------
879+
-- Prompt mapping fields (for jq-style mapping completion)
880+
---------------------------------------------------------------------------
881+
M.prompt_mapping_fields = {
882+
{ name = "name", desc = "Display name in picker" },
883+
{ name = "key", desc = "Substitution value" },
884+
{ name = "desc", desc = "Description shown in picker (alias for description)" },
885+
{ name = "description", desc = "Description shown in picker" },
886+
}
887+
878888
return M

lua/poste/http/highlights.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ function M.setup()
6464
{ "PostePromptMarker", "PreProc" },
6565
{ "PostePromptVar", "PosteVarDef" },
6666
{ "PostePromptOpts", "String" },
67+
{ "PostePromptOptSep", "Delimiter" },
68+
{ "PostePromptMappingField", "Type" },
69+
{ "PostePromptMappingColon", "Operator" },
70+
{ "PostePromptMappingPath", "Identifier" },
6771
{ "PostePreScript", "PreProc" },
6872
{ "PosteAssertion", "PreProc" },
6973
{ "PosteScriptMarker", "Special" },
@@ -193,6 +197,7 @@ function M.setup()
193197
"PosteImport", "PosteImportPath", "PosteImportAliasOpt", "PosteImportAlias",
194198
"PosteRun", "PosteRunTarget", "PosteRunVarDef", "PosteRunVarAssign", "PosteRunVarValue",
195199
"PostePromptMarker", "PostePromptVar", "PostePromptOpts",
200+
"PostePromptOptSep", "PostePromptMappingField", "PostePromptMappingColon", "PostePromptMappingPath",
196201
"PostePreScript", "PosteAssertion", "PosteScriptMarker", "PosteExternalScript",
197202
"PosteFileUpload", "PosteFileRef",
198203
"PosteJsonString", "PosteJsonNumber", "PosteJsonBoolean", "PosteJsonNull",

lua/poste/http/item_builder.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@ function M.get_items_for_context(line_before_cursor, buf, cursor_line, cursor_co
351351
if values then
352352
items = M.build_items(values, KIND_VALUE)
353353
end
354+
elseif ctx == "prompt_mapping" then
355+
items = M.build_keyword_items(data.prompt_mapping_fields, KIND_PROPERTY)
356+
return items
354357
end
355358

356359
return items

0 commit comments

Comments
 (0)