# SharedContext vs ScopeContext vs SubdocumentContext in Quarkdown: Key Differences Explained

> Understand Quarkdown contexts. Learn the key differences between SharedContext, ScopeContext, and SubdocumentContext for efficient document processing and variable management.

- Repository: [Giorgio Garofalo/quarkdown](https://github.com/iamgio/quarkdown)
- Tags: deep-dive
- Published: 2026-04-29

---

**SharedContext delegates every mutable property to its parent via Kotlin property delegation, while SubdocumentContext isolates document metadata for independent file processing, and ScopeContext extends SubdocumentContext to re-share document info for lambda execution while keeping variable libraries isolated.**

Quarkdown's compilation pipeline in `iamgio/quarkdown` relies on a mutable **Context** object to manage flavor settings, libraries, document metadata, and media storage. When the compiler evaluates code in isolated environments—such as sub-documents, lambda bodies, or temporary file-system changes—it forks the current context into one of three specialized types. Understanding when to use **SharedContext**, **ScopeContext**, or **SubdocumentContext** is essential for debugging pipeline behavior and extending the compiler's functionality.

## Core Concept: Context Forking in Quarkdown

The compilation process uses a hierarchical context system centered around the `MutableContext` class. When a pipeline stage requires a sandboxed environment, the compiler instantiates a forked context that selectively shares or isolates state. Each fork type determines exactly what data is shared versus copied, preventing side effects from leaking across document boundaries while maintaining necessary references to parent resources.

## SharedContext: Zero-Copy State Delegation

**SharedContext** (defined in [`SharedContext.kt`](https://github.com/iamgio/quarkdown/blob/main/SharedContext.kt)) extends `MutableContext` and functions as a transparent wrapper around its parent. It delegates every mutable property—including `documentInfo`, `mediaStorage`, `attributes`, and `libraries`—directly to the parent instance using Kotlin's `by parent::property` syntax.

### Implementation via Property Delegation

In [`SharedContext.kt`](https://github.com/iamgio/quarkdown/blob/main/SharedContext.kt), the class overrides mutable properties with full delegation:

```kotlin
override var documentInfo by parent::documentInfo
override var mediaStorage by parent::mediaStorage
override var attributes by parent::attributes

```

This means no data is copied; all reads and writes operate on the parent's storage. Only the `fileSystem` property can be overridden during construction.

### Primary Use Cases

Use **SharedContext** when internal helper functions require a temporary file-system change but must preserve all document metadata. For example, an `.include` directive that switches to a different base directory uses this context to avoid duplicating the entire context state while keeping `documentInfo` synchronized.

## SubdocumentContext: Isolated Document Processing

**SubdocumentContext** (defined in [`SubdocumentContext.kt`](https://github.com/iamgio/quarkdown/blob/main/SubdocumentContext.kt)) serves as the foundation for processing included files or linked sub-documents. It creates a local copy of `documentInfo` while sharing immutable environment settings like `flavor`, `loadableLibraries`, and `localizationTables`.

### What Gets Isolated vs Shared

The constructor creates a fresh `MutableContext` with specific isolation rules:

- **Isolated**: A new `documentInfo` instance (local metadata changes do not affect the parent) and an empty `libraries` set.
- **Shared**: `options`, `loadableLibraries`, `localizationTables`, `sharedSubdocumentsData`, and optionally the `fileSystem` via the private `_fileSystem` parameter.

This isolation prevents metadata pollution when the compiler processes `@include` directives or external markdown files.

### When the Compiler Uses It

The pipeline creates a `SubdocumentContext` via `Context.fork()` whenever it starts processing a sub-document, link target, or included file. This ensures that title, author, or attribute changes inside the included file remain local to that compilation unit.

## ScopeContext: Lambda Execution Environments

**ScopeContext** (defined in [`ScopeContext.kt`](https://github.com/iamgio/quarkdown/blob/main/ScopeContext.kt)) extends `SubdocumentContext` but immediately re-shares specific parent properties to create a hybrid isolation model. It is designed specifically for lambda execution and loop blocks where document metadata should remain visible but variable declarations must stay local.

### Selective Property Sharing

Unlike its parent class, **ScopeContext** delegates `documentInfo`, `attributes`, and `mediaStorage` back to the parent:

```kotlin
override var documentInfo by parent::documentInfo
override var attributes by parent::attributes
override var mediaStorage by parent::mediaStorage

```

However, it initializes with an empty set of libraries, ensuring that variables declared inside the lambda scope do not leak into the surrounding document.

### Variable Scoping in Lambda Bodies

When executing lambda functions, the compiler builds a **ScopeContext** so that the lambda can read and write the same document metadata as its parent, while new function declarations are stored in the isolated `libraries` set. The surrounding document remains unaffected by temporary variables defined inside the lambda.

## Practical Implementation Examples

The following Kotlin snippets demonstrate how to instantiate each context type during compiler development:

```kotlin
// Assume ctx is the current MutableContext

// 1. Fork a SubdocumentContext to compile an included file
val subDocCtx: SubdocumentContext = ctx.fork().also { subCtx ->
    // subCtx.documentInfo is a *copy*; changes here won't affect ctx
    subCtx.documentInfo.title = "Included Chapter"
}

// 2. Create a ScopeContext for a lambda body
val lambdaScope: ScopeContext = ctx.fork().also { scope ->
    // scope.documentInfo points to the parent's documentInfo
    scope.documentInfo.author = "Lambda Author"
    // New variables declared here are stored in scope.libraries only
}

// 3. Use a SharedContext when only the file system should differ
val shared: SharedContext = SharedContext(
    parent = ctx,
    fileSystem = CustomFileSystem(root = "/tmp/isolated")
)
// All other mutable state is the same object as in ctx
shared.documentInfo.title = "Will also change in parent"

```

## Key Source Files

Understanding these context implementations requires examining the source code in the `quarkdown-core` module:

- **[Context.kt](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdash/core/context/Context.kt)** – Defines the core `Context` interface and base `MutableContext` class.
- **[SharedContext.kt](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdash/core/context/SharedContext.kt)** – Implements the full delegation pattern for zero-copy state sharing.
- **[SubdocumentContext.kt](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdash/core/context/SubdocumentContext.kt)** – Handles metadata isolation for sub-document processing.
- **[ScopeContext.kt](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdash/core/context/ScopeContext.kt)** – Provides the lambda execution environment with selective property sharing.

## Summary

- **SharedContext** delegates every mutable property (`documentInfo`, `mediaStorage`, `libraries`) to its parent via Kotlin property delegation, making it ideal for temporary file-system changes without state duplication.
- **SubdocumentContext** creates isolated copies of `documentInfo` and initializes with empty libraries, perfect for processing included files where metadata must remain local.
- **ScopeContext** extends SubdocumentContext but re-shares `documentInfo`, `attributes`, and `mediaStorage` with the parent, enabling lambda execution that accesses document metadata while keeping variable declarations scoped.
- The compiler chooses the context type based on whether the operation requires full isolation (SubdocumentContext), selective metadata sharing (ScopeContext), or zero-copy delegation (SharedContext).

## Frequently Asked Questions

### What is the parent class of SharedContext, ScopeContext, and SubdocumentContext?

**SharedContext** and **SubdocumentContext** both extend `MutableContext` (defined in [`Context.kt`](https://github.com/iamgio/quarkdown/blob/main/Context.kt)), while **ScopeContext** extends `SubdocumentContext`. This inheritance hierarchy allows ScopeContext to build upon the isolation logic of SubdocumentContext while selectively re-sharing specific properties with the parent.

### When should I use SharedContext instead of SubdocumentContext?

Use **SharedContext** when you need to temporarily modify only the file system (or another single property) while keeping all document metadata, media storage, and libraries identical to the parent. Use **SubdocumentContext** when processing a separate markdown file where changes to `documentInfo` (like title or author) must not propagate back to the parent document.

### How does ScopeContext handle variable isolation in lambda functions?

**ScopeContext** initializes with an empty set of libraries while delegating `documentInfo`, `attributes`, and `mediaStorage` to the parent. This design allows lambda bodies to read and modify document-wide metadata but stores any newly declared variables or functions in the local `libraries` set, preventing scope leakage into the surrounding document.

### Can I manually create these contexts outside the standard fork() method?

Yes, all three classes are public and can be instantiated directly. For example, you can construct a `SharedContext` by passing a parent `MutableContext` and an optional custom `FileSystem` parameter. However, the standard approach within the Quarkdown compiler is to use `Context.fork()` which automatically selects the appropriate context type based on the compilation stage.