diff --git a/lua/todo/parse.lua b/lua/todo/parse.lua new file mode 100644 index 0000000..0e8660c --- /dev/null +++ b/lua/todo/parse.lua @@ -0,0 +1,98 @@ +local config = require('todo.config') + +---@class todo.parse +local M = {} + +---@param s string +---@return boolean +local function is_valid_date(s) + local y, m, d = s:match('^(%d%d%d%d)-(%d%d)-(%d%d)$') + if not y then + return false + end + y, m, d = tonumber(y), tonumber(m), tonumber(d) + if m < 1 or m > 12 then + return false + end + if d < 1 or d > 31 then + return false + end + local t = os.time({ year = y, month = m, day = d }) + local check = os.date('*t', t) + return check.year == y and check.month == m and check.day == d +end + +---@return string +local function date_key() + return config.get().date_syntax or 'due' +end + +---@param text string +---@return string description +---@return { due?: string, cat?: string } metadata +function M.body(text) + local tokens = {} + for token in text:gmatch('%S+') do + table.insert(tokens, token) + end + + local metadata = {} + local i = #tokens + local dk = date_key() + local date_pattern = '^' .. vim.pesc(dk) .. ':(%d%d%d%d%-%d%d%-%d%d)$' + + while i >= 1 do + local token = tokens[i] + local due_val = token:match(date_pattern) + if due_val then + if metadata.due then + break + end + if not is_valid_date(due_val) then + break + end + metadata.due = due_val + i = i - 1 + else + local cat_val = token:match('^cat:(%S+)$') + if cat_val then + if metadata.cat then + break + end + metadata.cat = cat_val + i = i - 1 + else + break + end + end + end + + local desc_tokens = {} + for j = 1, i do + table.insert(desc_tokens, tokens[j]) + end + local description = table.concat(desc_tokens, ' ') + + return description, metadata +end + +---@param text string +---@return string description +---@return { due?: string, cat?: string } metadata +function M.command_add(text) + local cat_prefix = text:match('^(%S.-):%s') + if cat_prefix then + local first_char = cat_prefix:sub(1, 1) + if first_char == first_char:upper() and first_char ~= first_char:lower() then + local rest = text:sub(#cat_prefix + 2):match('^%s*(.+)$') + if rest then + local desc, meta = M.body(rest) + meta.cat = meta.cat or cat_prefix + return desc, meta + end + end + end + return M.body(text) +end + +return M