added opencode

This commit is contained in:
liph
2026-03-19 15:19:56 +01:00
parent a22912ce30
commit f12b027aa5
20 changed files with 1300 additions and 305 deletions
+78 -68
View File
@@ -1,35 +1,35 @@
-- Helper function to extract clean email address
-- Neovim ftplugin for mail with robust abook integration
-- Helper function to extract clean email address or formatted address
local function extract_email(address_string)
-- Match email in angle brackets: "Name <email@example.com>"
local email = address_string:match("<([^>]+)>")
if email then
return email
local name, email = address_string:match("^([^\t]+)\t([^\t]+)")
if name and email then
if name == "" or name == email then
return email
else
return string.format("%s <%s>", name, email)
end
end
-- Match standalone email: "email@example.com"
email = address_string:match("([%w%._%+-]+@[%w%._%+-]+)")
if email then
return email
end
-- Fallback: return as-is if no pattern matches
return address_string
end
-- Option 2: Omnifunc completion
-- Omnifunc completion for email addresses
function _G.mail_complete(findstart, base)
if findstart == 1 then
-- Find start of word
local line = vim.api.nvim_get_current_line()
local col = vim.fn.col(".")
local line_to_cursor = line:sub(1, col - 1)
-- Only on header lines
if not line_to_cursor:match("^%s*[ToCcBcFrom]+:%s*") then
return -1
end
-- Detect if we are on a header line that takes email addresses
if not line_to_cursor:match("^%s*[Tt][Oo]:") and
not line_to_cursor:match("^%s*[Cc][Cc]:") and
not line_to_cursor:match("^%s*[Bb][Cc][Cc]:") and
not line_to_cursor:match("^%s*[Ff][Rr][Oo][Mm]:") and
not line_to_cursor:match("^%s*[Rr][Ee][Pp][Ll][Yy]%-[Tt][Oo]:") then
return -1
end
-- Find start of current address
-- Find start of current address (after comma or space)
local start = line_to_cursor:reverse():find("[%s,]")
if start then
return col - start
@@ -37,90 +37,100 @@ function _G.mail_complete(findstart, base)
return line_to_cursor:find(":") or 0
end
else
-- Get completions
local handle = io.popen(string.format('abook --mutt-query "%s" 2>/dev/null | tail -n +2', base))
if not handle then
return {}
end
local result = handle:read("*a")
handle:close()
-- Query abook
local cmd = string.format('abook --mutt-query "%s" 2>/dev/null', base)
local handle = io.popen(cmd)
if not handle then return {} end
-- Skip the first line (header)
local header = handle:read("*l")
local matches = {}
for address in result:gmatch("[^\r\n]+") do
if address ~= "" then
-- Extract clean email only
local clean_email = extract_email(address)
table.insert(matches, clean_email)
for line in handle:lines() do
if line ~= "" then
-- abook --mutt-query returns "email\tname\t..."
-- Use a more lenient pattern to capture both fields even if one is empty
local email = line:match("^([^\t]+)")
local name = line:match("^[^\t]+\t([^\t]*)")
if email then
local formatted = (name and name ~= "") and string.format("%s <%s>", name, email) or email
table.insert(matches, { word = formatted, abbr = line:gsub("\t", " | "):gsub("%s+$", "") })
end
end
end
handle:close()
return matches
end
end
-- Set omnifunc
vim.bo.omnifunc = "v:lua.mail_complete"
-- Set omnifunc locally for the buffer
vim.opt_local.omnifunc = "v:lua.mail_complete"
-- Map Tab to trigger completion on header lines
-- Trigger completion on Tab in header lines
vim.keymap.set("i", "<Tab>", function()
local line = vim.api.nvim_get_current_line()
if line:match("^%s*[ToCcBcFrom]+:%s*") then
local col = vim.fn.col(".")
local line_to_cursor = line:sub(1, col - 1)
-- Check if we are on a header line
local is_header = line_to_cursor:match("^%s*[Tt][Oo]:") or
line_to_cursor:match("^%s*[Cc][Cc]:") or
line_to_cursor:match("^%s*[Bb][Cc][Cc]:") or
line_to_cursor:match("^%s*[Ff][Rr][Oo][Mm]:") or
line_to_cursor:match("^%s*[Rr][Ee][Pp][Ll][Yy]%-[Tt][Oo]:")
if is_header then
return "<C-x><C-o>"
else
return "<Tab>"
end
return "<Tab>"
end, { expr = true, buffer = true })
-- Option 3: Interactive fzf picker
-- Notify that the plugin is loaded (silent)
-- vim.notify("Mail ftplugin loaded")
-- Interactive picker using vim.ui.select (works with fzf-lua/telescope)
local function pick_email()
-- Get all addresses
local handle = io.popen('abook --mutt-query "" 2>/dev/null | tail -n +2')
local handle = io.popen('abook --mutt-query "" 2>/dev/null')
if not handle then
vim.notify("Could not query abook", vim.log.levels.ERROR)
return
end
local addresses = {}
local display_addresses = {}
-- Skip header
handle:read("*l")
local items = {}
for line in handle:lines() do
if line ~= "" then
local clean_email = extract_email(line)
table.insert(addresses, clean_email)
-- Show full format in picker, but insert clean email
table.insert(display_addresses, line .. "" .. clean_email)
local email, name = line:match("^([^\t]+)\t([^\t]+)")
if email then
local formatted = name ~= "" and string.format("%s <%s>", name, email) or email
table.insert(items, { display = line:gsub("\t", " | "), value = formatted })
end
end
end
handle:close()
if #addresses == 0 then
if #items == 0 then
vim.notify("No addresses in abook", vim.log.levels.WARN)
return
end
-- Use vim.ui.select (works with fzf-lua if installed)
vim.ui.select(display_addresses, {
prompt = "Select recipient:",
format_item = function(item)
return item
end,
}, function(choice, idx)
if choice and idx then
local email = addresses[idx]
-- Insert at cursor
vim.ui.select(items, {
prompt = "Select Recipient:",
format_item = function(item) return item.display end,
}, function(choice)
if choice then
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local line = vim.api.nvim_get_current_line()
local new_line = line:sub(1, col) .. email .. line:sub(col + 1)
local new_line = line:sub(1, col) .. choice.value .. line:sub(col + 1)
vim.api.nvim_set_current_line(new_line)
-- Move cursor after inserted text
vim.api.nvim_win_set_cursor(0, { row, col + #email })
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
end
end)
end
-- Map Ctrl+f to pick email (Option 3)
vim.keymap.set("i", "<C-f>", pick_email, { buffer = true, desc = "Pick email address with fzf" })
-- Optional: Map Ctrl+a for Tab completion manually (in case Tab doesn't work)
vim.keymap.set("i", "<C-a>", "<C-x><C-o>", { buffer = true, desc = "Trigger address completion" })
-- Map Ctrl+f for the picker
vim.keymap.set("i", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })