another commit
This commit is contained in:
@@ -1,18 +1,12 @@
|
||||
-- Neovim ftplugin for mail with robust abook integration
|
||||
-- Improved Neovim ftplugin for mail with robust abook integration
|
||||
-- Debugging enabled: check :messages for logs
|
||||
|
||||
-- Helper function to extract clean email address or formatted address
|
||||
local function extract_email(address_string)
|
||||
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
|
||||
return address_string
|
||||
local function log(msg)
|
||||
-- print("[abook] " .. msg)
|
||||
end
|
||||
|
||||
log("ftplugin/mail.lua loaded for filetype: " .. vim.bo.filetype)
|
||||
|
||||
-- Omnifunc completion for email addresses
|
||||
function _G.mail_complete(findstart, base)
|
||||
if findstart == 1 then
|
||||
@@ -20,14 +14,14 @@ function _G.mail_complete(findstart, base)
|
||||
local col = vim.fn.col(".")
|
||||
local line_to_cursor = line:sub(1, col - 1)
|
||||
|
||||
-- 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
|
||||
-- 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 (after comma or space)
|
||||
local start = line_to_cursor:reverse():find("[%s,]")
|
||||
@@ -38,21 +32,15 @@ function _G.mail_complete(findstart, base)
|
||||
end
|
||||
else
|
||||
-- Query abook
|
||||
log("Querying abook for: " .. base)
|
||||
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 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 line ~= "" and not line:match("^%s*$") then
|
||||
local email, 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+$", "") })
|
||||
@@ -60,11 +48,12 @@ function _G.mail_complete(findstart, base)
|
||||
end
|
||||
end
|
||||
handle:close()
|
||||
log("Found " .. #matches .. " matches")
|
||||
return matches
|
||||
end
|
||||
end
|
||||
|
||||
-- Set omnifunc locally for the buffer
|
||||
-- Set omnifunc locally
|
||||
vim.opt_local.omnifunc = "v:lua.mail_complete"
|
||||
|
||||
-- Trigger completion on Tab in header lines
|
||||
@@ -73,7 +62,6 @@ vim.keymap.set("i", "<Tab>", function()
|
||||
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
|
||||
@@ -87,27 +75,22 @@ vim.keymap.set("i", "<Tab>", function()
|
||||
return "<Tab>"
|
||||
end, { expr = true, buffer = true })
|
||||
|
||||
-- Notify that the plugin is loaded (silent)
|
||||
-- vim.notify("Mail ftplugin loaded")
|
||||
|
||||
-- Interactive picker using vim.ui.select (works with fzf-lua/telescope)
|
||||
-- Interactive picker
|
||||
local function pick_email()
|
||||
log("Manual picker triggered")
|
||||
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
|
||||
|
||||
-- Skip header
|
||||
handle:read("*l")
|
||||
|
||||
local items = {}
|
||||
for line in handle:lines() do
|
||||
if line ~= "" then
|
||||
if line ~= "" and not line:match("^%s*$") then
|
||||
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 })
|
||||
local formatted = (name and name ~= "") and string.format("%s <%s>", name, email) or email
|
||||
table.insert(items, { display = line:gsub("\t", " | "):gsub("%s+$", ""), value = formatted })
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -118,19 +101,42 @@ local function pick_email()
|
||||
return
|
||||
end
|
||||
|
||||
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) .. choice.value .. line:sub(col + 1)
|
||||
vim.api.nvim_set_current_line(new_line)
|
||||
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
|
||||
end
|
||||
end)
|
||||
table.sort(items, function(a, b) return a.display < b.display end)
|
||||
|
||||
-- Use Snacks.picker if available, otherwise fallback to vim.ui.select
|
||||
if _G.Snacks and Snacks.picker then
|
||||
Snacks.picker.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 before = line:sub(1, col)
|
||||
local after = line:sub(col + 1)
|
||||
if before:match("[:%,]$") then choice.value = " " .. choice.value end
|
||||
vim.api.nvim_set_current_line(before .. choice.value .. after)
|
||||
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
|
||||
end
|
||||
end)
|
||||
else
|
||||
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 before = line:sub(1, col)
|
||||
local after = line:sub(col + 1)
|
||||
if before:match("[:%,]$") then choice.value = " " .. choice.value end
|
||||
vim.api.nvim_set_current_line(before .. choice.value .. after)
|
||||
vim.api.nvim_win_set_cursor(0, { row, col + #choice.value })
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Map Ctrl+f for the picker
|
||||
-- Keybinds
|
||||
vim.keymap.set("i", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })
|
||||
vim.keymap.set("n", "<C-f>", pick_email, { buffer = true, desc = "Pick email from abook" })
|
||||
|
||||
@@ -13,7 +13,7 @@ return {
|
||||
workspaces = {
|
||||
{
|
||||
name = "liph",
|
||||
path = "/mnt/flash1/podman/nextcloud/config/obsidian/vaults",
|
||||
path = "/mnt/flash1/podman/lxc_servarr/nextcloud/config/obsidian/vaults",
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user