Skip to content

feat(capture): pass destination file to headline function#1155

Open
alecStewart1 wants to merge 1 commit into
nvim-orgmode:masterfrom
alecStewart1:capture-headline-receives-file
Open

feat(capture): pass destination file to headline function#1155
alecStewart1 wants to merge 1 commit into
nvim-orgmode:masterfrom
alecStewart1:capture-headline-receives-file

Conversation

@alecStewart1
Copy link
Copy Markdown
Contributor

@alecStewart1 alecStewart1 commented May 28, 2026

Summary

In Emacs, a function passed file+function passed to a template can technically operate on the destination file before the target is found.

In the case of my Emacs configuration, I created the following functions that I pass to file+function:

;;;###autoload
(defun capture:get-todays-timestamp (&optional inactive)
  "Get the current active or INACTIVE timestamp for today's date."
  (if inactive
      (format-time-string "[%Y-%m-%d %a]" (current-time))
    (format-time-string "<%Y-%m-%d %a>" (current-time))))

;;;###autoload
(defun capture:goto-or-create-heading (timestamp-fn)
  "Navigate to the heading matching TIMESTAMP-FN's return value, creating it if absent."
  (let* ((ts  (funcall timestamp-fn))
         (pnt (ignore-errors (org-find-olp (list ts) t))))
    (if pnt
        (goto-char pnt)
      (goto-char (point-max))
      (org-insert-heading nil nil 1)
      (end-of-line)
      (insert "[%] " ts)
      (goto-char (org-find-olp (list ts) t)))))

And passing (capture:goto-or-create-heading (capture:get-todays-timestamp)) as the second argument to file+function in a template spec will have the heading created first if it's not there, and then the point of said heading is returned.

In nvim-orgmode, there's no real way to do this, so a solution is to pass OrgFile? (? so it can be ignored by people who don't want to use it) to the function that headline can receive.

Changes

Add opts.destination_file as a argument to pcall with template_headline in _get_refile_vars.

Update OrgCaptureTemplateOpts to include OrgFile? in headline function.

Update docs to specify optional OrgFile argument that the function passed to headline can take.

Extra Notes

This extra level of power can lead to potential "footgunning" from the user.

The following is an example of me trying to mimic what I've done in Emacs above:

---@param file OrgFile
---@return string
local function headline_for_today(file)
  local today = tostring(os.date("%d-%m-%Y"))
  local headline_stars = "* " .. today

  if file:find_headline_by_title(today) then
    return today
  end

  -- Insert after any file-level #+ directives (TITLE, AUTHOR, OPTIONS, etc.)
  -- and any blank lines trailing them.
  file:update_sync(function()
    local bufnr = file:get_valid_bufnr()
    local line_count = vim.api.nvim_buf_line_count(bufnr)
    local row = 0

    while row < line_count do
      local line = vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1]
      if line:match("^#%+") or vim.trim(line or "") == "" then
        row = row + 1
      else
        break
      end
    end

    vim.api.nvim_buf_set_lines(bufnr, row, row, false, { headline_stars, "" })
  end)

  -- Force a re-read from disk.  update_sync's internal reload can
  -- short-circuit on filesystems without nanosecond mtime precision
  -- (common on musl / container filesystems), leaving the in-memory
  -- OrgFile and its memoized headline cache stale.  This explicit
  -- reload_sync matches the pattern used by Datetree for the same reason.
  file:reload_sync()

  return today
end

The comments are from what I noticed with doing a simpler version of this headline_for_today function (not accounting for top-level file attributes like #+TITLE: etc., not accounting for the memoized headline cache).

So just something to keep in mind.

Checklist

I confirm that I have:

  • Followed the
    Conventional Commits
    specification
    (e.g., feat: add new feature, fix: correct bug,
    docs: update documentation).
  • My PR title also follows the conventional commits specification.
  • Updated relevant documentation, if necessary.
  • Thoroughly tested my changes.
  • Added tests (if applicable) and verified existing tests pass with
    make test.
  • Checked for breaking changes and documented them, if any.

@alecStewart1 alecStewart1 marked this pull request as ready for review May 28, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant