Skip to content

Proposal: @include, @example directives #4144

@jonasfj

Description

@jonasfj

This issue proposes 3 new dartdoc directives:

  • {@include <path-to-file>[#<region>] [<options>]}, include a file region as markdown.
  • {@example <path-to-file>[#<region>] [<options>]}, include a file region as fenced code snippet.

, where

  • <path-to-file> is one of:
    • relative-path (e.g. ../test/example.dart), relative to the file that the directive appears in.
    • absolute-path (e.g. /src/myfile.dart), relative to the packageUri` of the current package
      • packageUri is lib/ folder in the root of the current package directory.
      • This the same logic as import-statements.
    • package URI (e.g. package:foo/foo.dart), URI relative to lib/ within given package.
  • <region> is a region within the file (see Region resolution below), given as fragment in the URI for the file.
  • <options> is space separated list of (optional) options:
    • lang=<language>, language marker for the example (only applicable to {@example} directive).
      Default value is inferred from the file extension of <path-to-file>.
    • indent=keep|strip, whether to keep or strip indentation (see Whitespace stripping below)

Use cases:

  • Testable code samples in documentation (by embedding regions from a test file)
  • Break large code samples into small regions, embed and explain them bit by bit.
  • Include a markdown file when writing very large documentation comments, instead of writing hundreds of lines in ///-prefixed doc comments (for better ergonomics).

Region resolution

We should allow authors to not just reference a specific file, but also a region within said file.

This proposal suggests that:

  • Region foo is referenced using a URL-fragment, like ../test/example.dart#foo.
  • Region foo is defined as:
    • Lines between #region foo and matching #endregion (allowing for nested regions)
    • Omitting lines that include #hide, #region and #endregion

Example: example/hello.dart

/// Copyright 2025 Google LLC.
/// SPDX-License-Identifier: Apache-2.0

// #region main
void main() {
  // #region hello
  print('hello world');
  // #endregion
  exit(0); // force exit! #hide
}
// #endregion
Example: example/hello.dart#hello

The region becomes:

  print('hello world');

Note: lines with #region and #endregion are not included!

Example: example/hello.dart#main The region becomes:
void main() {
  print('hello world');
}

Note: lines with #hide are not included!

Whitespace stripping

Many examples are likely to appear nested inside main()/group()/test() callbacks in a file like test/example.dart file. Thus, it's often preferable to strip indentation.

Thus, we propose a indent=strip|keep option that defaults to strip!

Stripping indentation simply means:

  • For each non-empty line, remove the minimum number of whitespaces that all non-empty lines has.

It's possible that we should do special things for \t, and that we should consider only removing an even number of whitespaces (I don't know anyone who uses indentation 1).

Example use case

With {@example} directive, we'd be able to write documentation like:

/// The following examples show to use [print]:
/// {@example example/hello.dart#hello}
void print(String line) {

And get the following markdown:

The following examples show to use [print]:
```dart
print('hello world');
```

Notice:

  • The language marker dart comes from the file extension.
  • We default to stripping indentation.

Upsides:

  • Documentation examples can be included from example/ or test/:
    • Giving users: auto-completion, syntax highlighting and formatting when writing examples.
    • Enabling documentation examples to also run as test (covered by CI).
    • Authors can omit test setup and technicalities using #region/#hide.
  • Compared to {@tool} directives, this is fast and can work on pub.dev (without security concerns).
  • Inferring language marker from file extension just works for most file types (dart, yaml, etc).
  • Stripping indentation is a good default for most use-cases.
  • Path resolution is consistent import-statements from Dart!

Downsides:

  • We've introduce a new #region/#endregion/#hide syntax! (inspired by #region in C#)
  • Stripping indentation can be a bit surprising.
  • Using the same path resolution a import-statements makes it a bit difficult to reference things from test/ and example/ folders.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions