Skip to content

Commit 5ee6e00

Browse files
committed
comments support
1 parent 21726d1 commit 5ee6e00

File tree

4 files changed

+143
-11
lines changed

4 files changed

+143
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- New feature: Generate and display curl command in dry run output
1111
- New command: `:HttpCopyCurl` to copy curl command for the request under cursor
1212
- New keybinding: `<leader>hc` to copy curl command
13+
- Support for comments in .http and .rest files
14+
- Full line comments starting with '#'
15+
- Inline comments from '#' to end of line
16+
1317

1418
## [1.0.0] 2024-09-17
1519
### Added

doc/http_client.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,22 @@ Dry runs can be executed even if no environment file is selected. In this case,
260260
a warning will be displayed in the dry run output if environment variables are
261261
needed but not set.
262262

263+
------------------------------------------------------------------------------
264+
COMMENTS *http_client-comments*
265+
266+
The plugin now supports comments in .http and .rest files. Comments can be used
267+
to add explanations or temporarily disable parts of requests.
268+
269+
- Full line comments start with '#' at the beginning of the line
270+
- Inline comments start with '#' and continue to the end of the line
271+
272+
Examples:
273+
274+
# This is a full line comment
275+
GET https://api.example.com/users # This is an inline comment
276+
277+
Comments are ignored when parsing and executing requests.
278+
263279
==============================================================================
264280
vim:ft=help:et:ts=4:sw=4:sts=4:norl:
265281

lua/http_client/core/parser.lua

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ local function extract_test_name(lines)
1212
return ""
1313
end
1414

15+
local function remove_comment(line)
16+
local comment_start = line:find("#")
17+
if comment_start then
18+
return line:sub(1, comment_start - 1):match("^%s*(.-)%s*$") -- Trim and remove comment part
19+
end
20+
return line:match("^%s*(.-)%s*$") -- Trim if no comment
21+
end
22+
1523
M.get_request_under_cursor = function()
1624
local current_line = vim.api.nvim_win_get_cursor(0)[1]
1725
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
@@ -21,7 +29,7 @@ M.get_request_under_cursor = function()
2129

2230
-- First pass: check if there are any ### separators
2331
for _, line in ipairs(lines) do
24-
if line:match("^###") then
32+
if line:match("^###") and not line:match("^###%s*#") then
2533
has_separators = true
2634
break
2735
end
@@ -30,7 +38,7 @@ M.get_request_under_cursor = function()
3038
if has_separators then
3139
-- Find the request that contains the cursor
3240
for i, line in ipairs(lines) do
33-
if line:match("^###") then
41+
if line:match("^###") and not line:match("^###%s*#") then
3442
if i <= current_line then
3543
request_start = i
3644
else
@@ -49,7 +57,7 @@ M.get_request_under_cursor = function()
4957
if not request_start then
5058
request_start = 1
5159
for i, line in ipairs(lines) do
52-
if line:match("^###") then
60+
if line:match("^###") and not line:match("^###%s*#") then
5361
request_end = i - 1
5462
break
5563
end
@@ -105,6 +113,7 @@ M.parse_request = function(lines)
105113

106114
for _, line in ipairs(lines) do
107115
line = trim(line)
116+
108117
if line:match("^>%s*{%%") then
109118
in_response_handler = true
110119
response_handler = ""
@@ -117,29 +126,34 @@ M.parse_request = function(lines)
117126
stage = "body"
118127
end
119128
elseif stage == "start" then
120-
-- Updated regex to make HTTP version optional
129+
line = remove_comment(line)
121130
local method, url, version = line:match("^(%S+)%s+(.+)%s+(HTTP/%S+)$")
122131
if not method then
123-
-- If no HTTP version, try matching without it
124132
method, url = line:match("^(%S+)%s+(.+)$")
125133
end
126134
if method and url then
127135
request.method = method
128136
request.url = url
129-
request.http_version = version or "HTTP/1.1" -- Default to HTTP/1.1 if not specified
137+
request.http_version = version or "HTTP/1.1"
130138
stage = "headers"
131139
end
132140
elseif stage == "headers" then
141+
line = remove_comment(line)
133142
local key, value = line:match("^([^:]+):%s*(.+)$")
134143
if key and value then
135-
request.headers[trim(key)] = trim(value)
144+
key = trim(key)
145+
value = remove_comment(value)
146+
value = trim(value)
147+
if value ~= "" then
148+
request.headers[key] = value
149+
end
136150
end
137151
elseif stage == "body" then
138-
table.insert(body_lines, line)
152+
table.insert(body_lines, remove_comment(line))
139153
end
140154
end
141155

142-
if #body_lines > 0 then
156+
if #body_lines > 0 and request.method ~= 'GET' then
143157
request.body = table.concat(body_lines, "\n")
144158
end
145159

tests/http_client/core/parser_spec.lua

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe("Parser", function()
2929
assert.is_not_nil(request)
3030
assert.are.equal(request.method, "GET")
3131
assert.are.equal(request.url, "/test1")
32-
assert.are.equal(request.body, "Body1")
32+
assert.are.equal(request.body, nil)
3333
end)
3434

3535
it("should handle cases where cursor is on line before separator", function()
@@ -91,7 +91,7 @@ describe("Parser", function()
9191
assert.are.equal(request.url, "/test")
9292
assert.are.equal(request.http_version, "HTTP/1.1")
9393
assert.are.same(request.headers, { Header1 = "Value1", Header2 = "Value2" })
94-
assert.are.equal(request.body, "Body")
94+
assert.are.equal(request.body, nil)
9595
end)
9696

9797
it("should parse a basic GET request", function()
@@ -174,6 +174,35 @@ describe("Parser", function()
174174
response_handler = "print(response.body)\n"
175175
}, request)
176176
end)
177+
178+
it("should handle a GET request with query parameters", function()
179+
local lines = {
180+
"GET /api/users?page=1&limit=10 HTTP/1.1",
181+
"Host: example.com",
182+
""
183+
}
184+
local request = parser.parse_request(lines)
185+
assert.are.same({
186+
method = "GET",
187+
url = "/api/users?page=1&limit=10",
188+
http_version = "HTTP/1.1",
189+
headers = {
190+
Host = "example.com"
191+
},
192+
body = nil
193+
}, request)
194+
end)
195+
196+
it("should handle comment on header line", function()
197+
local lines = {
198+
"GET https://test.com/",
199+
"User-Agent: heilgar/http-client",
200+
"#X-Not-in: false"
201+
}
202+
203+
local request = parser.parse_request(lines)
204+
assert.are.equal(request.headers['#X-Not-in'], nil)
205+
end)
177206
end)
178207

179208
describe("parse_all_requests", function()
@@ -198,6 +227,75 @@ describe("Parser", function()
198227
assert.are.equal(requests[2].method, "POST")
199228
assert.are.equal(requests[2].url, "/test2")
200229
end)
230+
231+
it("should parse multiple requests correctly with comments", function()
232+
local lines = {
233+
"### Test 1",
234+
"# This is a comment",
235+
"GET /test1 HTTP/1.1",
236+
"Header1: Value1 # Inline comment",
237+
"",
238+
"Body1",
239+
"### Test 2",
240+
"POST /test2 HTTP/1.1 # Another comment",
241+
"Header2: Value2",
242+
"",
243+
"Body2 # Not a comment in body"
244+
}
245+
246+
local requests = parser.parse_all_requests(lines)
247+
assert.are.equal(#requests, 2)
248+
assert.are.equal(requests[1].method, "GET")
249+
assert.are.equal(requests[1].url, "/test1")
250+
assert.are.equal(requests[1].headers["Header1"], "Value1")
251+
assert.are.equal(requests[2].method, "POST")
252+
assert.are.equal(requests[2].url, "/test2")
253+
assert.are.equal(requests[2].body, "Body2")
254+
end)
255+
256+
it("should handle requests with mixed inline comments and empty lines", function()
257+
local lines = {
258+
"### Request 1",
259+
"GET /resource1 HTTP/1.1", -- Request line
260+
"# This is a comment",
261+
"Header1: Value1 # Inline comment", -- Header with inline comment
262+
"", -- Empty line
263+
"Body1 # Comment in body", -- Body with comment
264+
"### Request 2",
265+
"POST /resource2 HTTP/1.1",
266+
"Header2: Value2",
267+
"Header3: Value3 # Another inline comment",
268+
"", -- Empty line
269+
"Body2", -- Body without comments
270+
"", -- Extra empty line
271+
"### Request 3", -- Third request
272+
"",
273+
"# Another comment",
274+
"PUT /resource3 HTTP/1.1",
275+
"", -- Empty line, no headers
276+
"Body3" -- Body without comments
277+
}
278+
279+
local requests = parser.parse_all_requests(lines)
280+
281+
assert.are.equal(#requests, 3)
282+
283+
assert.are.equal(requests[1].method, "GET")
284+
assert.are.equal(requests[1].url, "/resource1")
285+
assert.are.equal(requests[1].headers["Header1"], "Value1")
286+
assert.are.equal(requests[1].body, nil)
287+
288+
assert.are.equal(requests[2].method, "POST")
289+
assert.are.equal(requests[2].url, "/resource2")
290+
assert.are.equal(requests[2].headers["Header2"], "Value2")
291+
assert.are.equal(requests[2].headers["Header3"], "Value3")
292+
assert.are.equal(requests[2].body, "Body2")
293+
294+
assert.are.equal(requests[3].method, "PUT")
295+
assert.are.equal(requests[3].url, "/resource3")
296+
assert.is_nil(requests[3].headers["Header2"]) -- No headers
297+
assert.are.equal(requests[3].body, "Body3")
298+
end)
201299
end)
202300

203301
describe("replace_placeholders", function()

0 commit comments

Comments
 (0)