Approach for including external source content in DocC (snippets beyond Swift) — #1436


DocC’s Snippets feature today lets you reference and slice Swift source files from within a catalog for inclusion in articles.

[Issue #1436]( The ability to include source content (markdown, or programming sources) from outside the catalog (beyond Swift snippets) · Issue #1436 · swiftlang/swift-docc · GitHub )

proposes extending this in two directions:

(1) content from outside the catalog — e.g. referencing a README at the repo root or generated build artifacts without moving them into the catalog;

(2) source from languages other than Swift — e.g. C/C++ (and others) so that validated, tested source (or slices) can be included in DocC articles, which is especially important for Swift interop documentation. I’d like to use this thread to align on an approach before or while contributing to this feature.

Motivation

Markdown / content outside the catalog

- Reference and include files (e.g. README) that live at the repo root or elsewhere in the project, without duplicating them into the catalog.

- Same idea for generated sources that are part of the build and are awkward to incorporate today.

Source files beyond Swift

- Today: only Swift is supported for the “snippet from file” workflow; other languages require copying snippets into the catalog (high maintenance).

- Goal: point the catalog at a specific source file (any supported language), and use it like a snippet — including optional slicing (similar to Swift snippet comments) — so it can be included in articles.

- Enables better documentation for Swift ↔ C/C++ (and other) interop, and “here’s how this code works” documentation that links to real source and shows portions inline.

Importance: Reduces friction for managing snippets, especially for non-Swift languages, and makes it easier to keep documentation in sync with actual source.

Current behavior

- Snippets today: DocC Snippets are Swift-only and live inside the catalog (e.g. in a `Snippets` directory). You reference them from articles and can slice by line or by special comments.

- External content: There’s no built-in way to “include” or “reference” a file that lives outside the catalog (markdown or source) and have it rendered as first-class content/snippet.

- Alternatives today: Manually copy external source or markdown into the catalog and maintain the duplicate.

Suggested approach (options to discuss)

Option A: “External snippet” / “external content” resource type

Introduce a catalog-level concept (e.g. “external snippet” or “external content”) where the catalog declares a path (relative to repo root or to the catalog) to a file outside the catalog. DocC then treats it like a snippet: load the file, optionally slice it (with language-specific or generic slice rules), and make it referenceable in articles.

Pros: Explicit, declarative; one place to define what’s included; works for both markdown and source.

Cons: Requires defining schema (e.g. in Info.plist or a manifest), resolution rules (relative to what?), and behavior when the file is missing or moved.

Slicing for non-Swift: Either define a simple slice format (e.g. line ranges, or a comment convention like `// snippet: id`) that multiple languages can use, or start with “whole file” and add slicing per language later.

Option B: Inline path / reference in markup

Allow article/tutorial markup to reference an external file by path (e.g. a directive like `@SourceFile(path: "Sources/Foo/bar.c")` or similar). DocC resolves the path relative to a defined root (catalog or repo) and embeds the content (or a slice) as a code listing.

Pros: Very flexible; authors reference exactly where they need; no separate “registry” of external files.

Cons: Path handling and security (what roots are allowed); harder to validate “all referenced files exist” in one pass; need a clear story for slicing (e.g. line range or named region).

Option C: Extend Snippets directory semantics

Keep a single “Snippets” (or “Content”) area but allow it to contain references (symlinks, or manifest entries) to files outside the catalog. DocC would follow the reference, detect language from extension (or manifest), and support slicing where defined (e.g. Swift-style comments for Swift; line ranges or a simple convention for others).

Pros: Reuses existing “snippet” mental model; incremental extension.

Cons: May blur “inside catalog” vs “outside”; need clear rules for resolution and fallbacks.

Option D: Phased rollout

- Phase 1: External markdown only — e.g. “include file at path X” for `.md` files (README, etc.), no new snippet semantics.

- Phase 2: External source files — support at least one non-Swift language (e.g. C/C++) with whole-file or line-range inclusion.

- Phase 3: Slicing for non-Swift (e.g. named regions via comments or line ranges) and more languages.

Scope and open questions

- Paths: Resolve relative to catalog root, package root, or a configurable “content root”? How to handle packages with multiple catalogs?

- Slicing for non-Swift: Prefer a common convention (e.g. `// snippet: name` / `# snippet: name`) or language-specific rules? Or only line ranges in v1?

- Validation: Should DocC fail the build if a referenced external file is missing, or warn and leave a placeholder?

- SourceKit-LSP / IDE: For in-editor DocC preview (e.g. via sourcekit-lsp), how should “external” paths be resolved when the workspace might not have the same layout as the package root?

- Security / sandboxing: Any constraints on which paths can be referenced (e.g. only under package root)?

Possible next steps

1. Align on approach in this thread (e.g. Option A vs B vs C, and phased vs big-bang).

2. Spec / design doc: Document the chosen approach (schema, resolution rules, slicing, supported languages for v1).

3. Implement in swift-docc: External content loading, path resolution, and rendering as code listings or included markdown.

4. Slicing: Implement line-range or named-region slicing for at least one non-Swift language (e.g. C/C++).

5. Documentation and examples: How to reference README, how to add a C file as a “snippet,” and how SourceKit-LSP / tooling discovers external paths (if relevant).

6. SourceKit-LSP (if needed): If LSP’s DocC integration needs to resolve external paths for preview, add support there once the DocC contract is clear.

References

- [swift-docc #1436 — The ability to include source content (markdown, or programming sources) from outside the catalog (beyond Swift snippets)]

- [DocC documentation]

- DocC Snippets (current): see official DocC docs for how Swift snippets work today — for contrast with the proposed extension.


I’d appreciate feedback on which option (or combination) best fits the project’s goals, on phased vs non-phased rollout, and on any edge cases or existing workflows (e.g. multi-package, generated docs) that this should account for.

1 Like

A couple of insights that might help your planning: The existing snippets works by converting a Swift file into the equivalent of a symbolgraph that's primarily "source content" - so those files can be included as symbols into the rest of the content, primarily using the @snippet directive, which points to the relevant symbol.

DocC itself has no insight or knowledge into a Swift project, where files are located, or even how to get them - it's primary (& only) concern is combining the collection of symbolgraphs and markdown into an archive format. The format of the symbol graph data structures are encapsulated using symbolkit, and primarily aligns with what the Swift compiler outputs for symbol information.

The bits that do coordination exist in the swift-docc-plugin - that's where the knowledge of the Package.swift structure exists, and the code/processing (called extract snippet) that converts a source file into a symbol-like structure to be ingested & used within a DocC catalog and it's combination process.

This detail makes option B quite a bit trickier - as it doesn't know anything about the Package.swift structure (or Xcode structure, where there's not a package.swift)
and isn't quite aligned with how things work today. If you want to allow referencing anything in Sources/, the current system would be implying that you'd want to pre-process everything in there into symbol-like structures in order to read them in any use them.

I suspect you might have assumed the file that it grabs from the path in @Snippets is known up front, but it isn't - that's a reference to where it expects to find a symbol, not structured as a "request to get this file" - which puts the ordering into an odd space, as you find you want to convert some (all?) of any number of source files into snippet-like symbols prior to handing that to DocC. That's a huge open-ended list when you don't constrain it to "only this directory".

Dig a little deeper to unfurl the challenges and how it's working today - as the options that you laid out above don't really pass the "how it works today", and seems to confuse a number of inputs. Look at the specifics of what DocC takes as inputs, and what it has responsibility for - then back up into how you can either fit into that (which is what the original Snippets did), or expand it - and what implications that has. Then look at where that coordination needs to happen, and what options you have - all of the coordination today is with the swift-docc-plugin, which has the references to the Swift package structure to know about file locations, can do the pre-processing, and basically handles all the orchestration.

Adding to what Joe already said;
DocC knows very little about the snippets; it doesn't inspect what language the snippets are in or where their original source files are located. Instead all of that information comes from another tool that packages up all the information that DocC needs as "symbol" elements in a symbol graph file (separate from the symbol graph file that describes the framework's API).

For Swift snippets that happens in a tool called snippet-extract that's built together with the Swift-DocC Plugin. That's also where the Swift snippet parsing and the transformation into symbol graph "symbol" elements happens.

If someone created one of these snippet symbol graph files representing code in C or C++ and passed that to docc convert, DocC should be able to resolve the snippet paths, match the names slices and extract only those lines for display on the page.

The primary problem then becomes making the tool that orchestrates the build call the tool that creates those snippet symbol graph files and pass those files as input to the docc convert call that the build orchestration tool makes. For Swift snippets, that documentation build orchestration is done by the Swift-DocC Plugin. It is the tool responsible for calling snippet-extract, calling other tools to extract symbol graph files representing a certain target's API, and gathering those files and other inputs to determine what flags to pass to docc.


If the workflow we're talking about is swift package generate-documentation, then the Swift-DocC plugin is the tool that orchestrates that documentation build (pending any changes from [Pitch] Documentation generation for any SwiftPM package). If you want that workflow to call a tool that extracts snippets for more languages then you would either need to:

  • make a new tool and have the DocC Plugin check that it's available and then call it
  • update the existing snippet-extract tool to support parsing the snippet slices for more languages (potentially with different code comment syntax)

Currently, the Swift-DocC Plugin is also what's responsible for finding the snippet files in the "/Snippets" directory. If we wanted to allow referencing certain files in for example "/Sources" as well, then the configuration about which source files to extract snippets for would need to be some configuration that's aimed at the plugin (for example a command line option). The DocC Plugin is also what would be responsible for validating that the referenced files exist and constrain which file paths are allowed to be referenced.

Both of these pieces—what source languages of snippets can be extracted and new configuration to support extracting snippets from a different location—can be implemented rather independently but it might still be valuable having a high-level design discussion about which tools are responsible for which behaviors—especially with regards to future extensibility.

2 Likes

I bumped into this limitation independently as I was trying to improve swift-java documentation, and I've prepared a PR over here that's sufficient to at least start including snippets from other-language-sources.

Here's the PR which lifts the language restriction for scanning: Snippets can be not only .swift files by ktoso · Pull Request #120 · swiftlang/swift-docc-plugin · GitHub

I think there's a number of additional steps we can take here... but this would already unblock swift-java and enable multi language snippets like these (and we validate they compile using external infra):

I'd welcome a review of the PR and would love to chat about taking next steps to improve this further.

4 Likes