Skip to content

Commit

Permalink
feat(batch): support basic batching functionalities
Browse files Browse the repository at this point in the history
Add to batch, get batch size, and clear batch. Insert batch log statement will be implemented next
  • Loading branch information
Goose97 committed Nov 21, 2024
1 parent 2e46e0f commit 04554fe
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 54 deletions.
171 changes: 128 additions & 43 deletions lua/neolog/actions.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---@class NeologActions
--- @field log_templates { [string]: NeologLogTemplates }
local M = {}
--- @field batches { [string]: TSNode[] }
local M = { log_templates = {}, batches = {} }

local utils = require("neolog.utils")

Expand Down Expand Up @@ -259,34 +260,18 @@ local function pick_best_node(nodes, selection_range)
return best_node or nodes[#nodes]
end

---@param log_template string
---@param lang string
---@param position LogPosition
---@return LogStatementInsert[]
local function build_capture_log_statements(log_template, lang, position)
---@return {log_container: TSNode, logable_range: logable_range?, log_targets: TSNode[]}[]
local function capture_log_targets(lang)
local selection_range = utils.get_selection_range()

local log_containers = query_log_target_container(lang, selection_range)

---@type LogStatementInsert[]
local to_insert = {}
local result = {}

for _, container in ipairs(log_containers) do
local log_targets = find_log_target(container.container, lang)
local logable_range = container.logable_range

local insert_row

if logable_range then
insert_row = logable_range[1]
else
if position == "above" then
insert_row = container.container:start()
else
insert_row = container.container:end_() + 1
end
end
for _, log_container in ipairs(log_containers) do
local log_targets = find_log_target(log_container.container, lang)

-- Filter targets that intersect with the given range
log_targets = utils.array_filter(log_targets, function(node)
return utils.ranges_intersect(selection_range, utils.get_ts_node_range(node))
end)
Expand All @@ -298,15 +283,41 @@ local function build_capture_log_statements(log_template, lang, position)
return pick_best_node(group, selection_range)
end)

-- Filter targets that intersect with the given range
table.insert(result, {
log_container = log_container.container,
logable_range = log_container.logable_range,
log_targets = log_targets,
})
end

return result
end

---@param log_template string
---@param lang string
---@param position LogPosition
---@return LogStatementInsert[]
local function build_capture_log_statements(log_template, lang, position)
local to_insert = {}

for _, entry in ipairs(capture_log_targets(lang)) do
local log_targets = entry.log_targets
local log_container = entry.log_container
local logable_range = entry.logable_range
local insert_row = logable_range and logable_range[1]
or ({
above = log_container:start(),
below = log_container:end_() + 1,
})[position]

for _, log_target in ipairs(log_targets) do
local content, insert_cursor_offset = build_log_statement(log_template, {
identifier = function()
local bufnr = vim.api.nvim_get_current_buf()
return vim.treesitter.get_node_text(log_target, bufnr)
end,
line_number = function()
return log_target:start() + 1
return tostring(log_target:start() + 1)
end,
})

Expand Down Expand Up @@ -340,42 +351,53 @@ local function build_non_capture_log_statement(log_template, position)
}
end

---@class LogStatementInsert
---@field content string[] The log statement content
---@field row number The (0-indexed) row number to insert
---@field insert_cursor_offset number? The offset of the %insert_cursor placeholder if any
---@field log_target TSNode? The log target node

--- @alias LogPosition "above" | "below"

--- Insert log statement for the current identifier at the cursor
--- @class InsertLogOptions
--- @field template string? Which template to use. Defaults to `default`
--- @field position LogPosition
function M.insert_log(opts)
opts = vim.tbl_deep_extend("force", { template = "default" }, opts or {})

---@param template_set string
---@return string?, string?
local function get_lang_log_template(template_set)
local lang = get_lang(vim.bo.filetype)
if not lang then
vim.notify("Cannot determine language for current buffer", vim.log.levels.ERROR)
return
end

local log_template_set = M.log_templates[opts.template]
local log_template_set = M.log_templates[template_set]
if not log_template_set then
vim.notify(string.format("Log template '%s' is not found", opts.template), vim.log.levels.ERROR)
vim.notify(string.format("Log template '%s' is not found", template_set), vim.log.levels.ERROR)
return
end

local log_template_lang = log_template_set[lang]
if not log_template_lang then
vim.notify(
string.format("Log template '%s' does not have '%s' language template", opts.template, lang),
string.format("Log template '%s' does not have '%s' language template", template_set, lang),
vim.log.levels.ERROR
)
return
end

return log_template_lang, lang
end

---@class LogStatementInsert
---@field content string[] The log statement content
---@field row number The (0-indexed) row number to insert
---@field insert_cursor_offset number? The offset of the %insert_cursor placeholder if any
---@field log_target TSNode? The log target node

--- @alias LogPosition "above" | "below"

--- Insert log statement for the current identifier at the cursor
--- @class InsertLogOptions
--- @field template string? Which template to use. Defaults to `default`
--- @field position LogPosition
--- @param opts InsertLogOptions
function M.insert_log(opts)
opts = vim.tbl_deep_extend("force", { template = "default" }, opts or {})
local log_template_lang, lang = get_lang_log_template(opts.template)
if not log_template_lang or not lang then
return
end

-- There are two kinds of log statements:
-- 1. Capture log statements: log statements that contain %identifier placeholder
-- We need to capture the log target in the selection range and replace it
Expand All @@ -389,6 +411,69 @@ function M.insert_log(opts)
after_insert_log_statements(to_insert)
end

---Add log target to the log batch
--- @class AddLogToBatchOptions
--- @field batch_name string? Which batch to add to. Defaults to `default`
function M.add_log_target_to_batch(batch_name)
batch_name = batch_name or "default"

-- local log_template_lang, lang = get_lang_log_template(opts.template)
-- if not log_template_lang or not lang then
-- return
-- end
--
-- if not log_template_lang:find("%%identifier") then
-- vim.notify("Batch log statements must include %identifier placeholder", vim.log.levels.ERROR)
-- return
-- end

local lang = get_lang(vim.bo.filetype)
if not lang then
vim.notify("Cannot determine language for current buffer", vim.log.levels.ERROR)
return
end

---@type TSNode[]
local to_add = {}

for _, entry in ipairs(capture_log_targets(lang)) do
for _, log_target in ipairs(entry.log_targets) do
table.insert(to_add, log_target)
end
end

to_add = utils.array_sort_with_index(to_add, function(a, b)
local result = utils.compare_ts_node_start(a[1], b[1])
return result == "equal" and a[2] < b[2] or result == "before"
end)

---@type TSNode[]
local batch = M.batches[batch_name]
if not batch then
batch = {}
M.batches[batch_name] = batch
end

vim.list_extend(batch, to_add)
end

---@param batch_name string?
function M.get_batch_size(batch_name)
batch_name = batch_name or "default"
local batch = M.batches[batch_name]
if not batch then
return 0
end

return #batch
end

---@param batch_name string?
function M.clear_batch(batch_name)
batch_name = batch_name or "default"
M.batches[batch_name] = nil
end

-- Register the custom predicate
---@param templates { [string]: NeologLogTemplates }
function M.setup(templates)
Expand Down
10 changes: 0 additions & 10 deletions lua/neolog/utils.lua
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
local M = {}

function M.array_includes(array, value)
for _, v in ipairs(array) do
if v == value then
return true
end
end

return false
end

function M.array_find(array, predicate)
for i, v in ipairs(array) do
if predicate(v, i) then
Expand Down
47 changes: 46 additions & 1 deletion tests/neolog/actions/neolog_actions_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ local helper = require("tests.neolog.helper")
local actions = require("neolog.actions")
local assert = require("luassert")

describe("neolog.actions", function()
describe("neolog.actions single log", function()
before_each(function()
neolog.setup()
end)
Expand Down Expand Up @@ -212,3 +212,48 @@ describe("neolog.actions", function()
end)
end)
end)

describe("neolog.actions batch log", function()
before_each(function()
neolog.setup()
end)

it("supports adding log targets to the batch, getting batch size, and clearing batch", function()
local input = [[
// Comment
const fo|o = "foo"
const bar = "bar"
const baz = "baz"
]]

-- Default batch is `default`
helper.assert_scenario({
input = input,
filetype = "javascript",
action = function()
vim.cmd("normal! V2j")
actions.add_log_target_to_batch()
end,
expected = function()
assert.are.same(3, actions.get_batch_size())
end,
})

helper.assert_scenario({
input = input,
filetype = "javascript",
action = function()
vim.cmd("normal! V2j")
actions.add_log_target_to_batch("testing")
end,
expected = function()
assert.are.same(3, actions.get_batch_size("testing"))
end,
})

actions.clear_batch()
assert.are.same(0, actions.get_batch_size())
actions.clear_batch("testing")
assert.are.same(0, actions.get_batch_size("testing"))
end)
end)

0 comments on commit 04554fe

Please sign in to comment.