local config = require('pending.config') local forge = require('pending.forge') ---@class pending.CompletionItem ---@field word string ---@field info string ---@class pending.complete local M = {} ---@return string local function date_key() return config.get().date_syntax or 'due' end ---@return string local function recur_key() return config.get().recur_syntax or 'rec' end ---@return string[] local function get_categories() local s = require('pending.buffer').store() if not s then return {} end local seen = {} local result = {} for _, task in ipairs(s:active_tasks()) do local cat = task.category if cat and not seen[cat] then seen[cat] = true table.insert(result, cat) end end table.sort(result) return result end ---@return pending.CompletionItem[] local function date_completions() return { { word = 'today', info = "Today's date" }, { word = 'tomorrow', info = "Tomorrow's date" }, { word = 'yesterday', info = "Yesterday's date" }, { word = '+1d', info = '1 day from today' }, { word = '+2d', info = '2 days from today' }, { word = '+3d', info = '3 days from today' }, { word = '+1w', info = '1 week from today' }, { word = '+2w', info = '2 weeks from today' }, { word = '+1m', info = '1 month from today' }, { word = 'mon', info = 'Next Monday' }, { word = 'tue', info = 'Next Tuesday' }, { word = 'wed', info = 'Next Wednesday' }, { word = 'thu', info = 'Next Thursday' }, { word = 'fri', info = 'Next Friday' }, { word = 'sat', info = 'Next Saturday' }, { word = 'sun', info = 'Next Sunday' }, { word = 'eod', info = 'End of day (today)' }, { word = 'eow', info = 'End of week (Sunday)' }, { word = 'eom', info = 'End of month' }, { word = 'eoq', info = 'End of quarter' }, { word = 'eoy', info = 'End of year (Dec 31)' }, { word = 'sow', info = 'Start of week (Monday)' }, { word = 'som', info = 'Start of month' }, { word = 'soq', info = 'Start of quarter' }, { word = 'soy', info = 'Start of year (Jan 1)' }, { word = 'later', info = 'Someday (sentinel date)' }, { word = 'today@08:00', info = 'Today at 08:00' }, { word = 'today@09:00', info = 'Today at 09:00' }, { word = 'today@10:00', info = 'Today at 10:00' }, { word = 'today@12:00', info = 'Today at 12:00' }, { word = 'today@14:00', info = 'Today at 14:00' }, { word = 'today@17:00', info = 'Today at 17:00' }, } end ---@type table local recur_descriptions = { daily = 'Every day', weekdays = 'Monday through Friday', weekly = 'Every week', biweekly = 'Every 2 weeks', monthly = 'Every month', quarterly = 'Every 3 months', yearly = 'Every year', ['2d'] = 'Every 2 days', ['3d'] = 'Every 3 days', ['2w'] = 'Every 2 weeks', ['3w'] = 'Every 3 weeks', ['2m'] = 'Every 2 months', ['3m'] = 'Every 3 months', ['6m'] = 'Every 6 months', ['2y'] = 'Every 2 years', } ---@return pending.CompletionItem[] local function recur_completions() local recur = require('pending.recur') local list = recur.shorthand_list() local result = {} for _, s in ipairs(list) do local desc = recur_descriptions[s] or s table.insert(result, { word = s, info = desc }) end for _, s in ipairs(list) do local desc = recur_descriptions[s] or s table.insert(result, { word = '!' .. s, info = desc .. ' (from completion date)' }) end return result end ---@param source string ---@return boolean function M._is_forge_source(source) for _, b in ipairs(forge.backends()) do if b.shorthand == source then return true end end return false end ---@type string? local _complete_source = nil ---@param findstart integer ---@param base string ---@return integer|table[] function M.omnifunc(findstart, base) if findstart == 1 then local line = vim.api.nvim_get_current_line() local col = vim.api.nvim_win_get_cursor(0)[2] local before = line:sub(1, col) local dk = date_key() local rk = recur_key() local ck = config.get().category_syntax or 'cat' local checks = { { vim.pesc(dk) .. ':([%S]*)$', dk }, { vim.pesc(ck) .. ':([%S]*)$', ck }, { vim.pesc(rk) .. ':([%S]*)$', rk }, } for _, b in ipairs(forge.backends()) do table.insert(checks, { vim.pesc(b.shorthand) .. ':([%S]*)$', b.shorthand }) end for _, check in ipairs(checks) do local start = before:find(check[1]) if start then local colon_pos = before:find(':', start, true) if colon_pos then _complete_source = check[2] return colon_pos end end end _complete_source = nil return -1 end local matches = {} local source = _complete_source or '' local dk = date_key() local rk = recur_key() if source == dk then for _, c in ipairs(date_completions()) do if base == '' or c.word:sub(1, #base) == base then table.insert(matches, { word = c.word, menu = '[' .. source .. ']', info = c.info }) end end elseif source == (config.get().category_syntax or 'cat') then for _, c in ipairs(get_categories()) do if base == '' or c:sub(1, #base) == base then table.insert(matches, { word = c, menu = '[' .. source .. ']' }) end end elseif source == rk then for _, c in ipairs(recur_completions()) do if base == '' or c.word:sub(1, #base) == base then table.insert(matches, { word = c.word, menu = '[' .. source .. ']', info = c.info }) end end elseif M._is_forge_source(source) then local s = require('pending.buffer').store() if s then local seen = {} for _, task in ipairs(s:tasks()) do if task._extra and task._extra._forge_ref then local ref = task._extra._forge_ref local key = ref.owner .. '/' .. ref.repo if not seen[key] then seen[key] = true local word_num = key .. '#' if base == '' or word_num:sub(1, #base) == base then table.insert(matches, { word = word_num, menu = '[' .. source .. ']' }) end if base == '' or key:sub(1, #base) == base then table.insert( matches, { word = key, menu = '[' .. source .. ']', info = 'Bare repo link' } ) end end end end end end return matches end return M