Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse requests with treesitter #181

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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