feat(file-token): add file: inline metadata token with gf navigation

Problem: there was no way to link a task to a specific location in a
source file, or to quickly jump from a task to the relevant code.

Solution: add a file:<path>:<line> inline token that stores a relative
file reference in task._extra.file. Virtual text renders basename:line
in a new PendingFile highlight group. A buffer-local gf mapping
(configurable via keymaps.goto_file) opens the file at the given line.
M.add_here() lets users attach the current cursor position to any task
via vim.ui.select(). M.edit() gains -file support to clear the
reference. <Plug>(pending-goto-file) and <Plug>(pending-add-here) are
exposed for custom mappings.
This commit is contained in:
Barrett Ruth 2026-02-26 18:20:32 -05:00
parent 3da23c924a
commit 7835dc4687
9 changed files with 257 additions and 9 deletions

View file

@ -30,7 +30,7 @@ concealed tokens and are never visible during editing.
Features: ~
- Oil-style buffer editing: standard Vim motions for all task operations
- Inline metadata syntax: `due:`, `cat:`, and `rec:` tokens parsed on `:w`
- Inline metadata syntax: `due:`, `cat:`, `rec:`, and `file:` tokens parsed on `:w`
- Relative date input: `today`, `tomorrow`, `+Nd`, `+Nw`, `+Nm`, weekday
names, month names, ordinals, and more
- Recurring tasks with automatic next-date spawning on completion
@ -101,6 +101,7 @@ Supported tokens: ~
`due:<name>` Resolve a named date (see |pending-dates| below).
`cat:Name` Move the task to the named category on save.
`rec:<pattern>` Set a recurrence rule (see |pending-recurrence|).
`file:<path>:<n>` Attach a file reference (see |pending-file-token|).
The token name for due dates defaults to `due` and is configurable via
`date_syntax` in |pending-config|. The token name for recurrence defaults to
@ -118,10 +119,44 @@ placed under the `Errands` category header.
Parsing stops at the first token that is not a recognised metadata token.
Repeated tokens of the same type also stop parsing — only one `due:`, one
`cat:`, and one `rec:` per task line are consumed.
`cat:`, one `rec:`, and one `file:` per task line are consumed.
Omnifunc completion is available for all three token types. In insert mode,
type `due:`, `cat:`, or `rec:` and press `<C-x><C-o>` to see suggestions.
Omnifunc completion is available for `due:`, `cat:`, and `rec:` token types.
In insert mode, type the token prefix and press `<C-x><C-o>` to see
suggestions.
==============================================================================
FILE TOKEN *pending-file-token*
The `file:` inline token attaches a source file reference to a task. The
syntax is: >
file:<relative-path>:<line-number>
<
The path is stored relative to the directory containing the data file. The
token is rendered as virtual text at the end of the task line, showing only
the basename and line number (e.g. `auth.lua:42`) using the |PendingFile|
highlight group.
Example: >
Fix null pointer file:src/auth.lua:42
Update tests file:spec/parse_spec.lua:100
<
`gf` in normal mode in the task buffer follows the file reference, opening
the file and jumping to the specified line. The default key is `gf` and can
be changed via the `goto_file` keymap in |pending-config|. Set it to `false`
to disable.
To attach the current file and cursor position to an existing task, invoke
|<Plug>(pending-add-here)| from any source file. A `vim.ui.select()` picker
lists all active tasks; selecting one records the current file and line.
To clear a file reference with `:Pending edit`: >vim
:Pending edit 5 -file
<
==============================================================================
DATE INPUT *pending-dates*
@ -268,6 +303,29 @@ COMMANDS *pending-commands*
`gcal` Google Calendar one-way push. See |pending-gcal|.
*:Pending-edit*
:Pending edit {id} [{operations}]
Edit metadata on an existing task without opening the buffer. {id} is the
numeric task ID. One or more operations follow: >vim
:Pending edit 5 due:tomorrow cat:Work +!
:Pending edit 5 -due -cat -rec
:Pending edit 5 rec:!weekly due:fri
:Pending edit 5 -file
<
Operations: ~
`due:<date>` Set due date (accepts all |pending-dates| vocabulary).
`cat:<name>` Set category.
`rec:<pattern>` Set recurrence (prefix `!` for completion-based).
`+!` Add priority flag.
`-!` Remove priority flag.
`-due` Clear due date.
`-cat` Clear category.
`-rec` Clear recurrence.
`-file` Clear the attached file reference (see |pending-file-token|).
Tab completion is available for IDs, field names, date values, categories,
and recurrence patterns.
*:Pending-undo*
:Pending undo
Undo the last `:w` save, restoring the task store to its previous state.
@ -295,6 +353,7 @@ Default buffer-local keys: ~
`U` Undo the last `:w` save (`undo`)
`o` Insert a new task line below (`open_line`)
`O` Insert a new task line above (`open_line_above`)
`gf` Open the file attached to the task under the cursor (`goto_file`)
`zc` Fold the current category section (category view only)
`zo` Unfold the current category section (category view only)
@ -396,6 +455,21 @@ All motions support count: `3]]` jumps three headers forward. `]]` and
<Plug>(pending-prev-task)
Jump to the previous task line, skipping headers and blanks.
*<Plug>(pending-goto-file)*
<Plug>(pending-goto-file)
Open the file attached to the task under the cursor. If the cursor is not
on a task line, or the task has no file reference, a warning is shown. If
the referenced file cannot be read, an error is shown.
See |pending-file-token|.
*<Plug>(pending-add-here)*
<Plug>(pending-add-here)
Attach the current file and cursor line to an existing task. Invoke from
any source file (not the pending buffer itself) to open a picker listing
all active tasks. The selected task receives a `file:` reference pointing
to the current buffer's file and the cursor's line number.
See |pending-file-token|.
Example configuration: >lua
vim.keymap.set('n', '<leader>t', '<Plug>(pending-open)')
vim.keymap.set('n', '<leader>T', '<Plug>(pending-toggle)')
@ -452,6 +526,7 @@ loads: >lua
prev_header = '[[',
next_task = ']t',
prev_task = '[t',
goto_file = 'gf',
},
sync = {
gcal = {
@ -512,6 +587,11 @@ Fields: ~
See |pending-mappings| for the full list of actions
and their default keys.
{goto_file} (string|false, default: 'gf')
Open the file attached to the task under the
cursor. Set to `false` to disable. See
|pending-file-token|.
{debug} (boolean, default: false)
Enable diagnostic logging. When `true`, textobj
motions, mapping registration, and cursor jumps
@ -760,6 +840,12 @@ PendingRecur Applied to the recurrence indicator virtual text shown
alongside due dates for recurring tasks.
Default: links to `DiagnosticInfo`.
*PendingFile*
PendingFile Applied to the file reference virtual text shown for tasks
that have a `file:` token attached (see |pending-file-token|).
Displays the basename and line number (e.g. `auth.lua:42`).
Default: links to `Directory`.
To override a group in your colorscheme or config: >lua
vim.api.nvim_set_hl(0, 'PendingDue', { fg = '#aaaaaa', italic = true })
<