Skip to content

Commit

Permalink
parse requests with treesitter (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
treywood authored Aug 22, 2024
1 parent 8a0093e commit 14babc6
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 2 deletions.
2 changes: 2 additions & 0 deletions lua/kulala/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ M.defaults = {
winbar = false,
-- enable reading vscode rest client environment variables
vscode_rest_client_environmentvars = false,
-- parse requests with tree-sitter
treesitter = false,
}

M.default_contenttype = {
Expand Down
13 changes: 11 additions & 2 deletions lua/kulala/parser/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ local GRAPHQL_PARSER = require("kulala.parser.graphql")
local REQUEST_VARIABLES = require("kulala.parser.request_variables")
local STRING_UTILS = require("kulala.utils.string")
local PARSER_UTILS = require("kulala.parser.utils")
local TS = require("kulala.parser.treesitter")
local PLUGIN_TMP_DIR = FS.get_plugin_tmp_dir()
local Scripts = require("kulala.scripts")
local Logger = require("kulala.logger")
Expand Down Expand Up @@ -387,8 +388,16 @@ function M.parse(start_request_linenr)
},
}

local document_variables, requests = M.get_document()
local req = M.get_request_at(requests, start_request_linenr)
local req, document_variables
if CONFIG:get().treesitter then
local root = vim.treesitter.get_parser(0, "http"):parse()[1]:root()
document_variables = TS.get_document_variables(root)
req = TS.get_request_at_cursor(root)
else
local requests
document_variables, requests = M.get_document()
req = M.get_request_at(requests, start_request_linenr)
end
Scripts.javascript.run("pre_request", req.scripts.pre_request)
local env = ENV_PARSER.get_env()

Expand Down
141 changes: 141 additions & 0 deletions lua/kulala/parser/treesitter.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
local CONFIG = require("kulala.config")
local FS = require("kulala.utils.fs")

local M = {}

local function get_node_text(node)
if not node then
return nil
end

local text = vim.treesitter.get_node_text(node, 0)
return text:gsub("^%s*(.-)%s*$", "%1")
end

local function get_fields(node)
local tbl = {}
for child, field in node:iter_children() do
if field then
tbl[field] = get_node_text(child)
end
end
return tbl
end

M.get_document_variables = function(root)
local vars = {}

local query = vim.treesitter.query.parse("http", "(variable_declaration) @variable")
for _, node in query:iter_captures(root, 0) do
local fields = get_fields(node)
vars[fields.name] = fields.value
end

return vars
end

local function new_request()
return {
url = "",
method = "",
http_version = "",
headers = {},
body = "",
metadata = {},
show_icon_line_number = nil,
redirect_response_body_to_files = {},
start_line = 0,
block_line_count = 0,
lines_length = 0,
scripts = {
pre_request = { inline = {}, files = {} },
post_request = { inline = {}, files = {} },
},
}
end

M.get_request_at_cursor = function(root)
local pos_node = vim.treesitter.get_node()
if not pos_node then
return nil
end

local section_query = vim.treesitter.query.parse("http", "(section) @section")
local request_query = vim.treesitter.query.parse("http", [[
(comment name: (_) value: (_)) @meta
(pre_request_script
(script)? @script.pre.inline
(path)? @script.pre.file)
(request
header: (header)? @header
body: (external_body)? @body.external) @request
(res_handler_script
(script)? @script.post.inline
(path)? @script.post.file)
]])

local start_pos, _, end_pos, _ = pos_node:range()
for _, section_node in section_query:iter_captures(root, 0, start_pos, end_pos+1) do
local req = new_request()

for i, node in request_query:iter_captures(section_node, 0) do
local capture = request_query.captures[i]
local fields = get_fields(node)

if capture == "request" then
local start_line, _, end_line, _ = node:range()
req.url = fields.url
req.method = fields.method
req.http_version = fields.http_version
req.body = fields.body
req.start_line = start_line
req.block_line_count = end_line - start_line
req.lines_length = end_line - start_line

req.show_icon_line_number = nil
local show_icons = CONFIG.get().show_icons
if show_icons ~= nil then
if show_icons == "on_request" then
req.show_icon_line_number = start_line + 1
elseif show_icons == "above_req" then
req.show_icon_line_number = start_line
elseif show_icons == "below_req" then
req.show_icon_line_number = end_line
end
end

elseif capture == "header" then
req.headers[fields.name] = fields.value

elseif capture == "meta" then
table.insert(req.metadata, fields)

elseif capture == "script.pre.inline" then
local script = get_node_text(node):gsub("{%%%s*(.-)%s*%%}", "%1")
table.insert(req.scripts.pre_request.inline, script)

elseif capture == "script.pre.file" then
local file = get_node_text(node)
table.insert(req.scripts.pre_request.files, file)

elseif capture == "script.post.inline" then
local script = get_node_text(node):gsub("{%%%s*(.-)%s*%%}", "%1")
table.insert(req.scripts.post_request.inline, script)

elseif capture == "script.post.file" then
local file = get_node_text(node)
table.insert(req.scripts.post_request.files, file)

elseif capture == "body.external" then
req.body = FS.read_file(fields.path)
end
end

return req
end
end

return M

0 comments on commit 14babc6

Please sign in to comment.