187 lines
5 KiB
Lua
187 lines
5 KiB
Lua
local enums = require("codeium.enums")
|
|
local util = require("codeium.util")
|
|
|
|
local function utf8len(str)
|
|
if not str then
|
|
return 0
|
|
end
|
|
-- TODO: Figure out how to convert the document encoding to UTF8 length
|
|
-- Bonus points for doing it with raw codepoints instead of converting the
|
|
-- string wholesale
|
|
return str:len()
|
|
end
|
|
|
|
local function codeium_to_cmp(comp, offset, right)
|
|
local documentation = comp.completion.text
|
|
|
|
local label = documentation:sub(offset)
|
|
if label:sub(-#right) == right then
|
|
label = label:sub(1, -#right - 1)
|
|
end
|
|
|
|
-- We get the completion part that has the largest offset
|
|
local max_offset = offset
|
|
if comp.completionParts then
|
|
for _, v in pairs(comp.completionParts) do
|
|
local part_offset = tonumber(v.offset)
|
|
if part_offset > max_offset then
|
|
max_offset = part_offset
|
|
end
|
|
end
|
|
end
|
|
|
|
-- We get where the suffix difference between the completion and the range of code
|
|
local suffix_diff = comp.range.endOffset - max_offset
|
|
|
|
local range = {
|
|
start = {
|
|
-- Codeium returns an empty row for the first line
|
|
line = (tonumber(comp.range.startPosition.row) or 0),
|
|
character = offset - 1,
|
|
},
|
|
["end"] = {
|
|
-- Codeium returns an empty row for the first line
|
|
line = (tonumber(comp.range.endPosition.row) or 0),
|
|
-- We only want to replace up to where the completion ends
|
|
character = (comp.range.endPosition.col or suffix_diff) - suffix_diff,
|
|
},
|
|
}
|
|
|
|
return {
|
|
type = 1,
|
|
documentation = {
|
|
kind = "markdown",
|
|
value = table.concat({
|
|
"```" .. vim.api.nvim_buf_get_option(0, "filetype"),
|
|
label,
|
|
"```",
|
|
}, "\n"),
|
|
},
|
|
label = label,
|
|
insertText = label,
|
|
textEdit = {
|
|
newText = label,
|
|
insert = range,
|
|
replace = range,
|
|
},
|
|
cmp = {
|
|
kind_text = "Codeium",
|
|
kind_hl_group = "CmpItemKindCodeium",
|
|
},
|
|
codeium_completion_id = comp.completion.completionId,
|
|
}
|
|
end
|
|
|
|
local Source = {
|
|
server = nil,
|
|
}
|
|
Source.__index = Source
|
|
|
|
function Source:new(server)
|
|
local o = {}
|
|
setmetatable(o, self)
|
|
|
|
o.server = server
|
|
return o
|
|
end
|
|
|
|
function Source:is_available()
|
|
return self.server.is_healthy()
|
|
end
|
|
|
|
function Source:get_position_encoding_kind()
|
|
return "utf-8"
|
|
end
|
|
|
|
-- Import `cmp` but don't error if it is not installed, as it might be when only using virtual text
|
|
local imported_cmp, cmp = pcall(require, "cmp")
|
|
if imported_cmp then
|
|
cmp.event:on("confirm_done", function(event)
|
|
if
|
|
event.entry
|
|
and event.entry.source
|
|
and event.entry.source.name == "codeium"
|
|
and event.entry.completion_item
|
|
and event.entry.completion_item.codeium_completion_id
|
|
and event.entry.source.source
|
|
and event.entry.source.source.server
|
|
then
|
|
event.entry.source.source.server.accept_completion(event.entry.completion_item.codeium_completion_id)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function Source:complete(params, callback)
|
|
local context = params.context
|
|
local offset = params.offset
|
|
local cursor = context.cursor
|
|
local bufnr = context.bufnr
|
|
local filetype = enums.filetype_aliases[context.filetype] or context.filetype or "text"
|
|
local language = enums.languages[filetype] or enums.languages.unspecified
|
|
local after_line = context.cursor_after_line
|
|
local before_line = context.cursor_before_line
|
|
local line_ending = util.get_newline(bufnr)
|
|
local line_ending_len = utf8len(line_ending)
|
|
local editor_options = util.get_editor_options(bufnr)
|
|
|
|
-- We need to calculate the number of bytes prior to the current character,
|
|
-- that starts with all the prior lines
|
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
|
|
|
|
-- For the current line, we want to exclude any extra characters that were
|
|
-- entered after the popup displayed
|
|
lines[cursor.row] = context.cursor_line
|
|
|
|
-- We exclude the current line from the loop below, so add it's length here
|
|
local cursor_offset = utf8len(before_line)
|
|
for i = 1, (cursor.row - 1) do
|
|
local line = lines[i]
|
|
cursor_offset = cursor_offset + utf8len(line) + line_ending_len
|
|
end
|
|
|
|
-- Ensure that there is always a newline at the end of the file
|
|
table.insert(lines, "")
|
|
local text = table.concat(lines, line_ending)
|
|
|
|
local function handle_completions(completion_items)
|
|
local duplicates = {}
|
|
local completions = {}
|
|
for _, comp in ipairs(completion_items) do
|
|
if not duplicates[comp.completion.text] then
|
|
duplicates[comp.completion.text] = true
|
|
table.insert(completions, codeium_to_cmp(comp, offset, after_line))
|
|
end
|
|
end
|
|
callback(completions)
|
|
end
|
|
|
|
local other_documents = util.get_other_documents(bufnr)
|
|
|
|
self.server.request_completion(
|
|
{
|
|
text = text,
|
|
editor_language = filetype,
|
|
language = language,
|
|
cursor_position = { row = cursor.row - 1, col = cursor.col - 1 },
|
|
absolute_uri = util.get_uri(vim.api.nvim_buf_get_name(bufnr)),
|
|
workspace_uri = util.get_uri(util.get_project_root()),
|
|
line_ending = line_ending,
|
|
cursor_offset = cursor_offset,
|
|
},
|
|
editor_options,
|
|
other_documents,
|
|
function(success, json)
|
|
if not success then
|
|
callback(nil)
|
|
end
|
|
|
|
if json and json.state and json.state.state == "CODEIUM_STATE_SUCCESS" and json.completionItems then
|
|
handle_completions(json.completionItems)
|
|
else
|
|
callback(nil)
|
|
end
|
|
end
|
|
)
|
|
end
|
|
|
|
return Source
|