local source = {} local utf8 = require("cmp_dictionary.lib.utf8") local config = require("cmp_dictionary.config") local caches = require("cmp_dictionary.caches") local db = require("cmp_dictionary.db") function source.new() return setmetatable({}, { __index = source }) end ---@return string function source.get_keyword_pattern() return [[\k\+]] end local candidate_cache = { req = "", items = {}, } ---@param str string ---@return boolean local function is_capital(str) return str:find("^%u") and true or false end ---@param str string ---@return string local function to_lower_first(str) local l = str:gsub("^.", string.lower) return l end ---@param str string ---@return string local function to_upper_first(str) local u = str:gsub("^.", string.upper) return u end ---@param req string ---@param isIncomplete boolean ---@return table function source.get_candidate(req, isIncomplete) if candidate_cache.req == req then return { items = candidate_cache.items, isIncomplete = isIncomplete } end local items local request = config.get("sqlite") and db.request or caches.request items, isIncomplete = request(req, isIncomplete) if config.get("first_case_insensitive") then local pre, post = to_upper_first, to_lower_first if is_capital(req) then pre, post = post, pre end for _, item in ipairs(request(pre(req), isIncomplete)) do table.insert(items, { label = post(item.label), detail = item.detail }) end end candidate_cache.req = req candidate_cache.items = items return { items = items, isIncomplete = isIncomplete } end ---@param request cmp.SourceCompletionApiParams ---@param callback fun(response: lsp.CompletionResponse|nil) function source.complete(_, request, callback) -- Clear the cache since the dictionary has been updated. if config.get("sqlite") then if db.is_just_updated() then candidate_cache = {} end else if caches.is_just_updated() then candidate_cache = {} end end local exact = config.get("exact") ---@type string local line = request.context.cursor_before_line local offset = request.offset line = line:sub(offset) if line == "" then return end local req, isIncomplete if exact > 0 then local line_len = utf8.len(line) if line_len <= exact then req = line isIncomplete = line_len < exact else local last = exact if line_len ~= #line then last = utf8.offset(line, exact + 1) - 1 end req = line:sub(1, last) isIncomplete = false end else -- must be -1 req = line isIncomplete = true end callback(source.get_candidate(req, isIncomplete)) end function source.resolve(_, completion_item, callback) if config.get("sqlite") then db.document(completion_item, callback) else require("cmp_dictionary.document")(completion_item, callback) end end return source