feat(buffer): render forge links as inline conceal overlays
Problem: forge tokens were stripped from the buffer and shown as EOL virtual text via `%l`. The token disappeared from the editable line, and multi-ref tasks broke. Solution: compute `forge_spans` in `views.lua` with byte offsets for each forge token in the rendered line. In `apply_inline_row()`, place extmarks with `conceal=''` and `virt_text_pos='inline'` to visually replace each raw token with its formatted label. Clear stale `forge_spans` on dirty rows to prevent `end_col` out-of-range errors after edits like `dd`.
This commit is contained in:
parent
0a64691edd
commit
819d27d751
2 changed files with 47 additions and 4 deletions
|
|
@ -168,6 +168,19 @@ local function apply_inline_row(bufnr, row, m, icons)
|
|||
virt_text_pos = 'overlay',
|
||||
priority = 100,
|
||||
})
|
||||
if m.forge_spans then
|
||||
local forge = require('pending.forge')
|
||||
for _, span in ipairs(m.forge_spans) do
|
||||
local label_text, hl_group = forge.format_label(span.ref, span.cache)
|
||||
vim.api.nvim_buf_set_extmark(bufnr, ns_inline, row, span.col_start, {
|
||||
end_col = span.col_end,
|
||||
conceal = '',
|
||||
virt_text = { { label_text, hl_group } },
|
||||
virt_text_pos = 'inline',
|
||||
priority = 90,
|
||||
})
|
||||
end
|
||||
end
|
||||
elseif m.type == 'header' then
|
||||
local line = vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] or ''
|
||||
vim.api.nvim_buf_set_extmark(bufnr, ns_inline, row, 0, {
|
||||
|
|
@ -213,6 +226,7 @@ function M.reapply_dirty_inline(bufnr)
|
|||
local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1] or ''
|
||||
local old_status = m.status
|
||||
m.status = infer_status(line) or m.status
|
||||
m.forge_spans = nil
|
||||
log.debug(
|
||||
('reapply_dirty: row=%d line=%q old_status=%s new_status=%s'):format(
|
||||
row,
|
||||
|
|
@ -379,10 +393,6 @@ local function setup_syntax(bufnr)
|
|||
syntax match taskCheckbox /\[!\]/ contained containedin=taskLine
|
||||
syntax match taskLine /^\/\d\+\/- \[.\] .*$/ contains=taskId,taskCheckbox
|
||||
]])
|
||||
local forge = require('pending.forge')
|
||||
for _, pat in ipairs(forge.conceal_patterns()) do
|
||||
vim.cmd('syntax match forgeRef /' .. pat .. '/ conceal contained containedin=taskLine')
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
local config = require('pending.config')
|
||||
local forge = require('pending.forge')
|
||||
local parse = require('pending.parse')
|
||||
|
||||
---@class pending.ForgeLineMeta
|
||||
---@field ref pending.ForgeRef
|
||||
---@field cache? pending.ForgeCache
|
||||
---@field col_start integer
|
||||
---@field col_end integer
|
||||
|
||||
---@class pending.LineMeta
|
||||
---@field type 'task'|'header'|'blank'|'filter'
|
||||
---@field id? integer
|
||||
|
|
@ -14,6 +21,7 @@ local parse = require('pending.parse')
|
|||
---@field recur? string
|
||||
---@field forge_ref? pending.ForgeRef
|
||||
---@field forge_cache? pending.ForgeCache
|
||||
---@field forge_spans? pending.ForgeLineMeta[]
|
||||
|
||||
---@class pending.views
|
||||
local M = {}
|
||||
|
|
@ -43,6 +51,27 @@ local function format_due(due)
|
|||
return formatted
|
||||
end
|
||||
|
||||
---@param task pending.Task
|
||||
---@param prefix_len integer
|
||||
---@return pending.ForgeLineMeta[]?
|
||||
local function compute_forge_spans(task, prefix_len)
|
||||
local refs = forge.find_refs(task.description)
|
||||
if #refs == 0 then
|
||||
return nil
|
||||
end
|
||||
local cache = task._extra and task._extra._forge_cache or nil
|
||||
local spans = {}
|
||||
for _, r in ipairs(refs) do
|
||||
table.insert(spans, {
|
||||
ref = r.ref,
|
||||
cache = cache,
|
||||
col_start = prefix_len + r.start_byte,
|
||||
col_end = prefix_len + r.end_byte,
|
||||
})
|
||||
end
|
||||
return spans
|
||||
end
|
||||
|
||||
---@type table<string, integer>
|
||||
local status_rank = { wip = 0, pending = 1, blocked = 2, done = 3 }
|
||||
|
||||
|
|
@ -178,6 +207,7 @@ function M.category_view(tasks)
|
|||
local prefix = '/' .. task.id .. '/'
|
||||
local state = state_char(task)
|
||||
local line = prefix .. '- [' .. state .. '] ' .. task.description
|
||||
local prefix_len = #prefix + #('- [' .. state .. '] ')
|
||||
table.insert(lines, line)
|
||||
table.insert(meta, {
|
||||
type = 'task',
|
||||
|
|
@ -191,6 +221,7 @@ function M.category_view(tasks)
|
|||
recur = task.recur,
|
||||
forge_ref = task._extra and task._extra._forge_ref or nil,
|
||||
forge_cache = task._extra and task._extra._forge_cache or nil,
|
||||
forge_spans = compute_forge_spans(task, prefix_len),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
@ -231,6 +262,7 @@ function M.priority_view(tasks)
|
|||
local prefix = '/' .. task.id .. '/'
|
||||
local state = task.status == 'done' and 'x' or (task.priority > 0 and '!' or ' ')
|
||||
local line = prefix .. '- [' .. state .. '] ' .. task.description
|
||||
local prefix_len = #prefix + #('- [' .. state .. '] ')
|
||||
table.insert(lines, line)
|
||||
table.insert(meta, {
|
||||
type = 'task',
|
||||
|
|
@ -245,6 +277,7 @@ function M.priority_view(tasks)
|
|||
recur = task.recur,
|
||||
forge_ref = task._extra and task._extra._forge_ref or nil,
|
||||
forge_cache = task._extra and task._extra._forge_cache or nil,
|
||||
forge_spans = compute_forge_spans(task, prefix_len),
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue