feat(parse): add inline metadata parser
Problem: need to extract due dates and categories from task descriptions typed in the buffer. Solution: add right-to-left token parser for configurable date syntax (default 'due:') and cat: metadata keys. Supports Category: prefix syntax for :Todo add commands.
This commit is contained in:
parent
607a9868d9
commit
a8eaa27106
1 changed files with 98 additions and 0 deletions
98
lua/todo/parse.lua
Normal file
98
lua/todo/parse.lua
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue