Skip to content

gggion/org-transclusion-blocks

Repository files navigation

org-transclusion-blocks - Transclusion into Org blocks via header arguments

org-transclusion-blocks enables transclusion into any Org block (src, quote, example, export, etc.) using header arguments, similar to how Babel uses headers for code execution (:session, :results, :var). Content fetched via org-transclusion persists in the buffer file, similarly to using org-transclusion-detach

Contents

Core Capability: Headers for Transclusion

org-transclusion uses #+transclude: keywords. This package uses block headers:

Before (org-transclusion):

#+transclude: [[file:org-transclusion-blocks.el]] :src elisp :lines 869-949

After (org-transclusion-blocks):

#+HEADER: :transclude [[file:org-transclusion-blocks.el]]
#+HEADER: :transclude-keywords ":lines 869-949"
#+begin_src elisp
#+end_src

The header approach enables:

  • Property inheritance: Set :transclude once in property drawer, inherit in all subtree blocks
  • Babel integration: Combine transclusion with :session, :results, :exports in src blocks
  • Persistent content: Transcluded text remains in buffer file as if detached.
  • Block type flexibility: Use quote, example, export, or custom special blocks
  • Org syntax escaping: Automatic escaping prevents markup in transcluded Org content from breaking src blocks

Advanced Capability: Component-Based Links

For complex links or when validation is desired, decompose into semantic components.

Basic Example: File Link with Search Option

Org’s built-in file: link type supports search options (::*Heading, ::#custom-id). Decompose into components with validation:

(org-transclusion-blocks-register-type
 ;; transclusion-type
 'file-with-search                        

 ;; components 
 '(:path (:header :file-path
          :required t
          :validator ,(org-transclusion-blocks-compose-validators
                       (org-transclusion-blocks-make-non-empty-validator "file path")
                       (org-transclusion-blocks-make-predicate-validator
                        #'file-exists-p "file path")))
   :search (:header :file-search))

 ;;constructor 
 (lambda (components)
   (let ((path (plist-get components :path))
         (search (plist-get components :search)))
     (if search
         (format "file:%s::%s" path search)
       (format "file:%s" path)))))

This registration:

  • Uses org-transclusion-blocks-compose-validators to chain two validators
  • make-non-empty-validator catches empty paths
  • make-predicate-validator checks file existence via file-exists-p
  • Constructor formats standard file:PATH::SEARCH link

Usage:

#+HEADER: :transclude-type file-with-search
#+HEADER: :file-path ~/documents/notes.org
#+HEADER: :file-search *Research
#+begin_src org
#+end_src

Validation catches errors early:

Header :file-path: invalid file path: "~/nonexistent.org"

Without validation, the error occurs during fetch with less helpful message: “Failed to fetch transclusion content”.

Intermediate Example: Adding Interaction Constraints

Extend the file example with shadowing relationship:

(org-transclusion-blocks-register-type
 ;;transclusion-type
 'file-with-search

 ;; components
 '(:path (:header :file-path
          :required t
          ;; defining validators
          :validator ,(org-transclusion-blocks-compose-validators 
                       (org-transclusion-blocks-make-non-empty-validator "file path")
                       (org-transclusion-blocks-make-predicate-validator
                        #'file-exists-p "file path")))
   :search (:header :file-search
            ;; defining  header incompatibility between :lines and :thing-at-point
            :shadowed-by (:transclude-lines :transclude-thing))) ;;defining 

 ;; constructor
 (lambda (components)
   (let ((path (plist-get components :path))
         (search (plist-get components :search)))
     (if search
         (format "file:%s::%s" path search)
       (format "file:%s" path)))))

Now using both :file-search and :transclude-lines produces warning:

#+HEADER: :transclude-type file-with-search
#+HEADER: :file-path ~/notes.org
#+HEADER: :file-search *Heading
#+HEADER: :transclude-lines 10-20

Warning:

Header :transclude-lines shadows :file-search; latter will be ignored

The :shadowed-by declaration makes the relationship explicit. User discovers why their search doesn’t work before debugging.

Advanced example:

  • custom validators and constructors: already possible but figuring out how to explain clearly. you can look at the raw code for it in HERE

Features

  • Direct header transclusion: Use :transclude header in any block without configuration
  • Property inheritance: Set headers in property drawer, inherit in subtree blocks
  • Persistent content: Transcluded text saved to file, works with version control and offline
  • Block type support: Works with src, quote, example, export, verse, center, comment, and custom special blocks
  • Org syntax escaping: Automatic escaping for Org sources prevents headline/keyword collisions in src blocks
  • Component decomposition: Break complex links into validated semantic headers (requires registration)
  • Pre-validation: Catch errors before content fetch with component-specific messages
  • Interaction constraints: Declare required components, shadowing, dependencies, conflicts
  • Validator composition: Utilities for common patterns (non-empty, regexp, predicate)
  • Babel integration: Full compatibility with :session, :results, :var, :exports in src blocks
  • Introspection: Commands to discover types, inspect configurations, validate without fetching

Installation

Manual

(add-to-list 'load-path "/path/to/org-transclusion-blocks")
(require 'org-transclusion-blocks)

Quick Start

Basic Transclusion via Header

Add :transclude header to any block:

#+HEADER: :transclude [[file:~/documents/notes.org::*Research]]

Run M-x org-transclusion-blocks-add. Content from target appears in block body:

#+HEADER: :transclude [[file:~/documents/notes.org::*Research]]
#+begin_src org
* Research

Prior work in this area shows...
#+end_src

Content persists in file. Version control sees it. Works offline.

Pass Keywords to org-transclusion

Use :transclude-keywords to pass org-transclusion options:

#+HEADER: :transclude [[file:~/notes.org::*Heading]]
#+HEADER: :transclude-keywords ":only-contents :lines 1-10"
#+begin_src org
#+end_src

This passes :only-contents :lines 1-10 to org-transclusion.

Warning

Some org-transclusion keywords don’t work as intented at the moment (:level, :exclude-elements).

Inherit Headers via Properties

Set :transclude once, inherit in subtree:

* Research Notes
:PROPERTIES:
:header-args: :transclude [[file:~/research.org]]
:END:

** Introduction Section
#+HEADER: :transclude-keywords ":only-contents :lines 1-5"
#+begin_src org
#+end_src

* Literature Review Section
#+HEADER: :transclude-keywords ":only-contents :lines 10-15"
#+begin_src org
#+end_src

Both blocks inherit :transclude from property drawer. Each adds :transclude-keywords to filter differently.

Automatic Org Syntax Escaping

When transcluding from Org files into src blocks, markup is escaped automatically:

Source file (notes.org):

* Heading in source
** Sub-heading
#+KEYWORD: value
Regular text.

After transclusion into src block:

#+HEADER: :transclude [[file:notes.org::*Heading in source]]
#+begin_src org
,,* Heading in source
,,** Sub-heading
,,#+KEYWORD: value
Regular text.

Notice the commas before * and #+ characters. This prevents Org from interpreting them as markup. Without escaping, the #+KEYWORD line would close the src block prematurely.

Escaping is enabled by default for Org sources (file: links to .org files, id: links, etc.). Control per-block with :transclude-escape-org:

#+HEADER: :transclude [[file:notes.org]]
#+HEADER: :transclude-escape-org nil
#+begin_src org
#+end_src

Or disable globally:

(setq org-transclusion-blocks-escape-org-sources nil)

Non-Org sources (Python, text files, etc.) are never escaped automatically. If for example a markdown file or docstring contains * or other elements which can be interpreted as org elements, you can escape them by using :transclude-escape-org t.

Why Header-Based Transclusion?

Problem 1: org-transclusion does not persist source block headers

org-transclusion uses #+transclude: keywords that exist outside block structure, while the transcluded content is being displayed, the transclude keyword dissapears:

#+transclude: [[file:file-name.el]] :src elisp :lines 10-15
<content appears, original keyword dissapears>

This creates issues:

  • No property inheritance: Can’t set common values in property drawer
  • No Babel integration: Can’t combine with :session, :results in src blocks
  • Volatile content: the source block disappears when transclusion is inactive (unless detached)
  • Version control blind: Content not saved to file (unless detached)

Solution: headers integrate with org infrastructure

Headers work with Org’s existing systems:

Property inheritance:

:PROPERTIES:
:header-args: :transclude [[file:~/research.org]]
:END:

Babel integration:

Csv data excerpt:
#+HEADER: :transclude [[file:data.csv]]
#+HEADER: :transclude-keywords ":lines 1-10"
#+begin_src :results silent :exports both
#+end_src

#+HEADER: :results silent :exports both
#+begin_src python :session analysis
import pandas as pd
data = pd.read_csv('data.csv')
#+end_src

Persistent content (saved to file):

#+HEADER: :transclude [[file:notes.org]]
#+begin_src org
Content remains here when transclusion inactive.
Version control sees it.
#+end_src

Problem 2: complex links are unreadable or hard to manage

Consider orgit-file transclusion links for Git repository content:

#+transclude: [[orgit-file:/home/user/code/project::refs/heads/main::src/core.el::(defun process-data]] :src elisp :thing-at-point sexp

Basic sepparation into headers, while offering some respite, still lacks the ideal form for easier management:

#+HEADER: :transclude [[orgit-file:/home/user/code/project::refs/heads/main::src/core.el::(defun process-data]]
#+HEADER: :transclude-keywords ":thing-at-point sexp"
#+begin_src elisp
#+end_src

Issues:

  • Difficult to read (all components concatenated)
  • Hard to modify (must reconstruct entire link)
  • Error-prone (typos in revision, file path)
  • No validation (errors discovered during fetch, not before)

Solution: Component Decomposition with Validation

With the transclude-type constructor utilities, we can create headers for this specific transclusion link type:

#+HEADER: :transclude-type orgit-file
#+HEADER: :orgit-repo ~/code/project
#+HEADER: :orgit-rev main
#+HEADER: :orgit-file src/core.el
#+HEADER: :orgit-search (defun process-data
#+HEADER: :transclude-keyword ":thing-at-point sexp"
#+begin_src elisp
#+end_src

Benefits:

  • Readable: Each component labeled semantically
  • Modifiable: Change :orgit-rev without touching other components
  • Validated: Catch invalid git refs before fetch attempt
  • Inheritable: Set :orgit-repo in property drawer for entire subtree
  • Self-documenting: Header names explain purpose

Default Headers (No Configuration Required)

The package provides three headers that work immediately without registration:

:transclude - Direct Link Specification

Specify complete link directly:

#+HEADER: :transclude [[file:~/notes.org::*Heading]]
#+begin_src org
#+end_src

Accepts any link type org-transclusion supports:

  • File links: [[file:path.org]], [[file:path.org::*Heading]], [[file:path.org::#custom-id]]
  • ID links: [[id:uuid]]
  • Custom links: [[orgit-file:...]] (if org-transclusion-orgit installed)

:transclude-keywords - Pass org-transclusion Options

Pass options to org-transclusion’s keyword parser:

#+HEADER: :transclude [[file:notes.org]]
#+HEADER: :transclude-keywords ":only-contents :lines 10-20"
#+begin_src org
#+end_src

Equivalent to org-transclusion keyword:

#+transclude: [[file:notes.org]] :only-contents :lines 10-20

Common keywords:

  • :only-contents - Exclude heading title
  • :lines N-M - Transclude line range
  • :level N - Set headline level
  • :exclude-elements "drawer keyword" - Exclude element types
  • :expand-links - Expand relative file paths to absolute

See org-transclusion manual for complete keyword reference.

:transclude-escape-org - Control Org Syntax Escaping

Override default escaping behavior per block:

#+HEADER: :transclude [[file:notes.org]]
#+HEADER: :transclude-escape-org nil
#+begin_src org
#+end_src

Values:

  • t or yes - Force escaping (prepend commas to *, #+ lines)
  • nil or no - Disable escaping
  • Omitted - Use default (org-transclusion-blocks-escape-org-sources)

Escaping prevents Org markup in transcluded content from breaking src block structure.

Default behavior (controlled by org-transclusion-blocks-escape-org-sources):

  • Org sources (.org files, id: links): Escaped
  • Non-Org sources (Python, text, etc.): Not escaped

Block Types

The package supports any Org block with #+begin_TYPE / #+end_TYPE delimiters:

Source Blocks

#+HEADER: :transclude [[file:code-samples.el::(defun process-data]]
#+HEADER: :transclude-keywords ":thing-at-point sexp"
#+begin_src elisp
#+end_src

Quote Blocks (For Quotations)

#+HEADER: :transclude [[file:~/research/literature.org::*Smith 2020]]
#+HEADER: :transclude-keywords ":only-contents"
#+begin_quote
#+end_quote

Example Blocks (For Literal Examples)

#+HEADER: :transclude [[file:~/projects/output.log]]
#+HEADER: :transclude-keywords ":lines 1-50"
#+begin_example
#+end_example

Export Blocks (Format-Specific Content)

#+HEADER: :transclude [[file:~/templates/header.html]]
#+begin_export html
#+end_export

Custom Special Blocks

#+HEADER: :transclude [[file:~/glossary.org::*Definition]]
#+begin_definition
#+end_definition

Component-Based Links (Advanced)

Component-based headers require type registration. The package includes no default registrations - all types are user-defined.

When to Use Component-Based Links

Use components when:

  • Links are complex (multiple path segments, revisions, search terms)
  • You want validation before content fetch
  • You need property inheritance for repeated values
  • Links would benefit from semantic decomposition

Continue using :transclude header for simple links.

Registration Tutorial: Journal Link Type

Say your journal follows a date-based structure:

~/journal/2025/2025-01-15.org
~/journal/2025/2025-01-16.org

You want semantic headers instead of file paths:

#+HEADER: :transclude-type journal
#+HEADER: :journal-year 2025
#+HEADER: :journal-date 01-15
#+begin_src org
#+end_src

Step 1: Identify Components

Break the link into pieces:

  • Year (4 digits, required)
  • Date (MM-DD format, required)

Step 2: Define Component Spec

Map semantic names to header keywords:

'(:year (:header :journal-year
         :required t)
  :date (:header :journal-date
         :required t))

The :year symbol is internal (used in constructor). The :journal-year keyword appears in headers.

Step 3: Write Constructor

Transform components into file link:

(lambda (components)
  (let ((year (plist-get components :year))
        (date (plist-get components :date)))
    (format "file:~/journal/%s/%s-%s.org"
            year year date)))

Constructor receives (:year "2025" :date "01-15"), returns "file:~/journal/2025/2025-01-15.org" (without [[ ]] brackets).

Step 4: Register Type

(org-transclusion-blocks-register-type
 'journal
 '(:year (:header :journal-year
          :required t)
   :date (:header :journal-date
         :required t))
 (lambda (components)
   (let ((year (plist-get components :year))
         (date (plist-get components :date)))
     (format "file:~/journal/%s/%s-%s.org"
             year year date))))

The type is now available. Use it:

#+HEADER: :transclude-type journal
#+HEADER: :journal-year 2025
#+HEADER: :journal-date 01-15
#+begin_src org
#+end_src

Step 5: Add Validation (Optional)

Catch format errors early:

(org-transclusion-blocks-register-type
 'journal
 '(:year (:header :journal-year
          :required t
          :validator ,(org-transclusion-blocks-make-regexp-validator
                       "^[0-9]\\{4\\}$" "year (4 digits)"))
   :date (:header :journal-date
          :required t
          :validator ,(org-transclusion-blocks-make-regexp-validator
                       "^[0-9]\\{2\\}-[0-9]\\{2\\}$" "date (MM-DD)")))
 (lambda (components)
   (let ((year (plist-get components :year))
         (date (plist-get components :date)))
     (format "file:~/journal/%s/%s-%s.org"
             year year date))))

Now typos are caught before fetch:

Header :journal-date: date (MM-DD) must match pattern ^[0-9]\{2\}-[0-9]\{2\}$, got: "1-15"

Component Interactions

Components can declare relationships:

Required Components

Signals error if component missing:

'(:path (:header :my-path
         :required t))

Error if :my-path absent:

Type my-type: required component :my-path (header :my-path) is missing

Shadowing (One Overrides Another)

Warns when shadowed component present:

'(:search (:header :my-search
           :shadowed-by (:transclude-lines :transclude-thing)))

Warning if :my-search and :transclude-lines both present:

Header :transclude-lines shadows :my-search; latter will be ignored

The :my-search value is ignored when :transclude-lines present.

Dependencies (Requires Other Components)

Signals error if dependency missing:

'(:search (:header :my-search
           :requires (:path)))

Error if :my-search present but :path absent:

Component :my-search requires :path to be present

Conflicts (Mutually Exclusive)

Signals error if both present:

'(:mode-a (:header :my-mode-a
           :conflicts (:mode-b)))

Error if both :my-mode-a and :my-mode-b present:

Components :my-mode-a and :my-mode-b cannot be used together

Control warning display:

(setq org-transclusion-blocks-show-interaction-warnings nil)  ; Hide soft conflicts (shadowing)

Hard conflicts (requirements, mutual exclusions) always signal errors.

Validator Composition Utilities

Non-Empty Validator

Require non-empty string:

:validator ,(org-transclusion-blocks-make-non-empty-validator "repository path")

Error if empty:

Header :orgit-repo: repository path cannot be empty

Regexp Validator

Match regular expression:

:validator ,(org-transclusion-blocks-make-regexp-validator
             "^[0-9]\\{4\\}$" "year")

Error if pattern mismatch:

Header :journal-year: year must match pattern ^[0-9]\{4\}$, got: "25"

Predicate Validator

Use custom predicate function:

:validator ,(org-transclusion-blocks-make-predicate-validator
             #'file-exists-p "file path")

Error if predicate returns nil:

Header :file-path: invalid file path: "/nonexistent.org"

Sequential Composition

Chain validators (stops at first error):

:validator ,(org-transclusion-blocks-compose-validators
             (org-transclusion-blocks-make-non-empty-validator "path")
             (org-transclusion-blocks-make-predicate-validator
              #'file-exists-p "path"))

First checks non-empty, then checks existence.

Custom Validators

Write validator functions:

(defun my-validate-git-ref (value header-key type)
  "Validate git reference format."
  (unless (string-match-p "^[a-zA-Z0-9/_.-]+$" value)
    (user-error "%s"
                (org-transclusion-blocks-format-validation-error
                 header-key
                 "invalid git ref format"
                 value
                 "Expected: branch name, tag, or commit hash"
                 "Examples: main, v1.0.0, abc1234")))
  value)

Validator signature: (lambda (value header-key type) ...)

  • value - String from header
  • header-key - Keyword (:my-header)
  • type - Type symbol ('my-type)
  • Returns - Validated value (possibly transformed)
  • Signals - user-error with message on failure

Use org-transclusion-blocks-format-validation-error for consistent formatting.

Commands

org-transclusion-blocks-add

Fetch and insert content for block at point.

Keybinding suggestion: C-c n t

Behavior:

  1. Detects construction mode (direct :transclude, type-specific, etc.)
  2. Runs validators if type-specific components present
  3. Constructs link from headers
  4. Fetches content via org-transclusion
  5. Escapes Org syntax if applicable
  6. Inserts content into block body
  7. Shows success indicator (checkmark overlay)

With prefix argument C-u:

  • Copies source content instead of transcluding (detached)
  • Content is editable, not linked to source

org-transclusion-blocks-add-all

Process all blocks with transclusion headers in buffer or region.

Keybinding suggestion: C-c n T

Accepts scope argument:

  • nil or 'buffer - Entire buffer (default)
  • 'subtree - Current subtree only
  • 'region - Active region

Returns list of successfully processed block positions.

org-transclusion-blocks-validate-current-block

Test validators without fetching content.

Keybinding suggestion: C-c n v

Useful for:

  • Testing validator configurations
  • Debugging error messages
  • Learning what triggers validation failures

Reports validation success or shows error message.

org-transclusion-blocks-describe-type

Display comprehensive documentation for registered type.

Interactive usage: M-x org-transclusion-blocks-describe-type RET journal RET

Shows:

  • Component specifications (headers, validators, constraints)
  • Interaction relationships (required, shadowed-by, requires, conflicts)
  • Constructor function
  • Usage example with all components

Output appears in *Help/ buffer.

org-transclusion-blocks-list-types

Show all registered types with component counts.

Interactive usage: M-x org-transclusion-blocks-list-types RET

Example output:

Registered link types:

  journal               2 components
  project-file          4 components

Use org-transclusion-blocks-describe-type for detailed information about specific types.

Configuration

Minimal Configuration

(use-package org-transclusion-blocks
  :after org-transclusion
  :bind (:map org-mode-map
         ("C-c n t" . org-transclusion-blocks-add)))

This enables:

  • Header-based transclusion with :transclude
  • Property inheritance
  • Persistent content
  • Org syntax escaping

Recommended Configuration

(use-package org-transclusion-blocks
  :after org-transclusion
  :custom
  ;; Show warnings for shadowed components (default: t)
  (org-transclusion-blocks-show-interaction-warnings t)
  
  ;; Success indicator duration in seconds (default: 2.0)
  (org-transclusion-blocks-indicator-duration 3.0)
  
  ;; Enable Org syntax escaping for Org sources (default: t)
  (org-transclusion-blocks-escape-org-sources t)
  
  :bind (:map org-mode-map
         ("C-c n t" . org-transclusion-blocks-add)
         ("C-c n T" . org-transclusion-blocks-add-all)
         ("C-c n v" . org-transclusion-blocks-validate-current-block)))

Customization Variables

org-transclusion-blocks-show-interaction-warnings

Default: t

Whether to show warnings for component interactions.

When t:

  • Warns about shadowed components ("Header :foo shadows :bar")
  • Warns about mode conflicts (mixed construction forms)
  • Still signals errors for hard conflicts (requirements, mutual exclusions)

When nil:

  • Only signals errors for hard conflicts
  • No warnings for soft conflicts

org-transclusion-blocks-indicator-duration

Default: 2.0

Seconds to display success indicator (checkmark overlay) after content insertion.

Set to 0 to disable indicator entirely.

org-transclusion-blocks-timestamp-property

Default: 'org-transclusion-blocks-fetched

Text property name for storing fetch timestamp on transcluded content.

Used for future refresh functionality. Not user-visible.

org-transclusion-blocks-escape-org-sources

Default: t

Whether to escape Org syntax when transcluding from Org files.

When t (recommended):

  • Automatically escapes content from .org files, id: links, etc.
  • Prevents * (headlines), #+ (keywords) from breaking src block structure
  • Prepends comma to lines starting with Org markup

When nil:

  • No escaping
  • User must manually escape or risk syntax errors

Override per-block with :transclude-escape-org header.

Does not affect non-Org sources (Python, text, etc.).

Integration with org-transclusion

This package is a link construction framework. org-transclusion is the content extraction framework. The relationship:

  1. org-transclusion-blocks: Component headers → Link string
  2. org-transclusion: Link string → Content extraction

org-transclusion Features Still Work (IN PROGRESS)

All org-transclusion capabilities work:

  • :only-contents - Exclude heading titles
  • :lines - Line range extraction
  • :level - Headline level adjustment
  • :exclude-elements - Filter element types
  • :expand-links - Expand relative paths
  • Live sync (if enabled)
  • Export handling

Pass these via :transclude-keywords header:

#+HEADER: :transclude [[file:notes.org]]
#+HEADER: :transclude-keywords ":only-contents :level 2"
#+begin_src org
#+end_src

Compatibility with transient Branch

The upcoming transient branch of org-transclusion generalizes transclusion to any link type via org-link-open. This benefits our component-based approach:

Before transient:

  • Specialized link types require extension packages (org-transclusion-orgit)
  • Each extension implements custom navigation logic

After transient:

  • org-transclusion handles any link type via standard org-link-open
  • Component-based headers construct standard Org links
  • No extension packages needed (unless link type itself requires one, like orgit)

Your component headers construct standard links. org-transclusion navigates to them.

Package Ecosystem

org-transclusion (Required)

org-transclusion handles content fetching and extraction. org-transclusion-blocks constructs links, org-transclusion navigates and fetches.

Install via MELPA or GNU ELPA:

(use-package org-transclusion)

orgit (Optional, for Git Repository Links)

orgit provides orgit-file: link type for Git repository navigation. Useful with component-based headers:

#+HEADER: :transclude-type orgit-file
#+HEADER: :orgit-repo ~/code/project
#+HEADER: :orgit-rev v1.2.0
#+HEADER: :orgit-file src/core.el
#+begin_src elisp
#+end_src

Install via MELPA:

(use-package orgit)

org-transclusion-orgit (Optional, Pre-transient Only)

Extension enabling org-transclusion to fetch from orgit-file: links. Required if using pre-transient org-transclusion with orgit links.

Recommended Workflow

  1. Install org-transclusion (handles content fetching)
  2. Install org-transclusion-blocks (adds header-based construction)
  3. Optionally: Install orgit-file for Magit file blob links
  4. Optionally: Install org-transclusion-git

Packages compose. Each adds capability without replacing others.

Troubleshooting

“No transclusion headers found”

Symptom: Command reports no headers after invoking org-transclusion-blocks-add.

Diagnosis:

  1. Check block has at least one recognized header:
    • :transclude (direct link)
    • :transclude-type (component-based)
  2. Verify header syntax:
    #+HEADER: :transclude [[file:path.org]]  ; Correct
    #+HEADER :transclude [[file:path.org]]   ; WRONG (missing colon)
    #+HEADER: transclude [[file:path.org]]   ; WRONG (missing colon before keyword)
        

Solution: Add recognized header with correct syntax.

“Type X: required component Y is missing”

Symptom: Error message indicates missing required component.

Diagnosis: Registered type declares component as :required t but header absent.

Solution:

Option 1 - Add missing component:

#+HEADER: :transclude-type journal
#+HEADER: :journal-year 2025  ; Add this
#+HEADER: :journal-date 01-15

Option 2 - Use different construction form:

#+HEADER: :transclude [[file:~/journal/2025/2025-01-15.org]]

“Failed to fetch transclusion content”

Symptom: org-transclusion reports fetch failure.

Diagnosis: Link is constructed but target is unreachable.

Investigation:

Step 1 - Test link directly:

#+HEADER: :transclude [[file:~/notes.org::*Heading]]
                     ^ Place point here

Press C-c C-o (org-open-at-point). If this fails, link is invalid.

Step 2 - Check link type support:

Some link types require org-transclusion extensions:

  • orgit-file: - Requires org-transclusion-orgit (pre-transient) or transient branch
  • id: - Built-in support
  • file: - Built-in support

Check registered handlers: M-: org-transclusion-add-functions

Step 3 - Enable debug output:

(setq org-transclusion-debug t)

Retry org-transclusion-blocks-add. Check *Messages/ buffer for diagnostics.

Step 4 - Verify target exists:

Navigate manually to target:

  1. Open file
  2. Search for heading/pattern
  3. Confirm content exists

If target missing, link works but fetch returns empty.

Solution: Fix link or target based on diagnosis.

“Header :foo shadows :bar; latter will be ignored”

Symptom: Warning about shadowed component.

Diagnosis: Component declares :shadowed-by (:foo) and both headers present.

Example:

#+HEADER: :transclude-type my-type
#+HEADER: :my-search "pattern"        ; Will be ignored
#+HEADER: :transclude-lines 10-20     ; Takes precedence

Understanding: The :transclude-lines header overrides :my-search completely. The search value is never used.

Solution:

Option 1 - Remove shadowed header:

#+HEADER: :transclude-type my-type
#+HEADER: :transclude-lines 10-20     ; Use this

Option 2 - Remove shadowing header:

#+HEADER: :transclude-type my-type
#+HEADER: :my-search "pattern"        ; Use this

Option 3 - Disable warnings:

(setq org-transclusion-blocks-show-interaction-warnings nil)

Babel Evaluation Errors with Search Patterns

Symptom: Error “end of file during parsing” when using search patterns in src blocks.

Diagnosis: Babel evaluates values starting with ( as Elisp expressions.

Example problem:

#+HEADER: :orgit-search (defun process-data
#+begin_src elisp
#+end_src

Babel tries to evaluate (defun process-data and fails (incomplete s-expression).

Solution 1 - Quote the search term:

#+HEADER: :orgit-search "(defun process-data"
#+begin_src elisp
#+end_src

Solution 2 - Use complete s-expression:

#+HEADER: :orgit-search (defun process-data (x y) (+ x y))
#+begin_src elisp
#+end_src

Solution 3 - Use validator that catches incomplete expressions:

See “Custom Validators” section for search pattern validation example.

Note: This issue only affects src blocks (Babel processes headers). Other block types (quote, example, etc.) don’t evaluate headers, so search patterns work without quoting.

Content Appears with Unwanted Org Markup

Symptom: Transcluded content has unexpected commas before * or #+ lines.

Example:

* This line has comma
#+KEYWORD: also has comma

Diagnosis: Org syntax escaping is active.

Understanding: Escaping prevents Org from interpreting headlines and keywords in src blocks. Without escaping, / Heading would be treated as a headline, #+KEYWORD would close the block.

Solution:

If you want unescaped content (and accept the risk):

#+HEADER: :transclude [[file:notes.org]]
#+HEADER: :transclude-escape-org nil
#+begin_src org
#+end_src

Or disable globally:

(setq org-transclusion-blocks-escape-org-sources nil)

Note: Disabling escaping for Org sources in src blocks will cause syntax errors if content contains headlines or keywords. Use non-src block types (quote, example) if you need literal Org content.

Comparison with Alternatives

Direct org-transclusion

org-transclusion (keyword-based):

#+transclude: [[file:/path/to/file.org::*Heading]] :only-contents

org-transclusion-blocks (header-based):

#+HEADER: :transclude [[file:/path/to/file.org::*Heading]]
#+HEADER: :transclude-keywords ":only-contents"
#+begin_src org
#+end_src

Key Differences:

Aspectorg-transclusionorg-transclusion-blocks
Syntax#+transclude: keyword#+HEADER: arguments
DisplayOverlay (volatile)Persistent in buffer
Version controlContent not savedContent saved to file
Property inheritanceNot supportedFull support via :PROPERTIES:
Babel integrationNot applicableFull support (:session, etc.)
Block typesAny locationOrg blocks only
Offline accessRequires active transclusionWorks offline (content persisted)

Choose org-transclusion when:

  • You want live-synced overlays
  • Content should not persist in file
  • You prefer #+transclude: keyword syntax

Choose org-transclusion-blocks when:

  • You want persistent content (version control, offline)
  • You need property inheritance
  • You want Babel integration (src blocks)
  • Complex links benefit from component decomposition
  • You want validation before fetch

Both use same content-fetching machinery (org-transclusion). They differ in interface and content persistence.

Resources

Documentation

Examples

See testing/examples/ directory in repository for:

  • Custom link type registrations
  • Validator implementations
  • Constructor patterns
  • Integration with external tools
  • Property inheritance workflows

Related Packages

Contributing

Contributions welcome. Please:

  1. Open issue describing feature or bug
  2. Reference issue in pull request
  3. Add tests for new functionality
  4. Update documentation for user-visible changes

Acknowledgments

License

GPLv3 or later

About

Header based transclusion framework

Resources

License

Stars

Watchers

Forks

Packages

No packages published