From 8d11a389c73ac777ae139f0fecea949f18c9d9da Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 11:14:44 +0200 Subject: [PATCH 01/22] Add e2e showcase tests. --- examples/showcase/README.md | 99 +++++ .../code/java/org/showcase/CommentModes.java | 14 + .../code/java/org/showcase/Greeting.java | 17 + .../java/org/showcase/MultiPartWorkflow.java | 23 ++ .../org/showcase/OverlappingFragments.java | 28 ++ .../java/org/showcase/PatternSamples.java | 22 ++ .../kotlin/org/showcase/KotlinGreeting.kt | 11 + examples/showcase/code/text/glob-patterns.txt | 5 + examples/showcase/configuration/README.md | 56 +++ .../docs/include-exclude/excluded.md | 8 + .../docs/include-exclude/included.md | 11 + .../docs/multiple/java/greeting.md | 10 + .../docs/multiple/kotlin/greeting.md | 12 + .../docs/named-sources/java-greeting.md | 11 + .../docs/named-sources/kotlin-greeting.md | 12 + .../docs/named-sources/text-line.md | 9 + .../configuration/docs/root-source/go-mod.md | 49 +++ .../configuration/docs/root-source/version.md | 8 + .../docs/single-source/greeting.md | 11 + .../configuration/include-exclude.yml | 6 + .../configuration/multiple-embeddings.yml | 13 + .../showcase/configuration/named-sources.yml | 10 + .../showcase/configuration/root-source.yml | 6 + .../showcase/configuration/single-source.yml | 4 + .../showcase/docs/01-whole-file-source.md | 20 + .../showcase/docs/02-source-line-pattern.md | 8 + examples/showcase/docs/03-named-fragment.md | 11 + .../docs/04-paired-instruction-tag.md | 36 ++ .../showcase/docs/05-named-source-root.md | 13 + .../showcase/docs/06-start-end-pattern.md | 18 + .../showcase/docs/07-multi-line-pattern.md | 18 + .../docs/08-escaped-glob-character.md | 9 + .../showcase/docs/09-escaped-newline-text.md | 11 + .../showcase/docs/10-comment-filtering.md | 19 + .../docs/11-multi-part-fragment-separator.md | 16 + .../showcase/docs/12-overlapping-fragments.md | 16 + .../docs/13-markdown-fence-shielding.md | 11 + examples/showcase/docs/html-showcase.html | 17 + examples/showcase/docs/ignored-by-exclude.md | 9 + examples/showcase/embed-code.yml | 14 + .../negative/docs/invalid-attributes.md | 8 + .../negative/docs/missing-code-fence.md | 7 + .../negative/docs/missing-fragment.md | 8 + .../showcase/negative/docs/missing-pattern.md | 8 + .../showcase/negative/docs/missing-source.md | 8 + .../showcase/negative/docs/stale-snippet.md | 11 + .../negative/docs/unclosed-code-fence.md | 9 + .../showcase/negative/processing-errors.yml | 13 + examples/showcase/negative/stale.yml | 6 + examples/showcase/showcase_test.go | 354 ++++++++++++++++++ go.mod | 2 +- 51 files changed, 1134 insertions(+), 1 deletion(-) create mode 100644 examples/showcase/README.md create mode 100644 examples/showcase/code/java/org/showcase/CommentModes.java create mode 100644 examples/showcase/code/java/org/showcase/Greeting.java create mode 100644 examples/showcase/code/java/org/showcase/MultiPartWorkflow.java create mode 100644 examples/showcase/code/java/org/showcase/OverlappingFragments.java create mode 100644 examples/showcase/code/java/org/showcase/PatternSamples.java create mode 100644 examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt create mode 100644 examples/showcase/code/text/glob-patterns.txt create mode 100644 examples/showcase/configuration/README.md create mode 100644 examples/showcase/configuration/docs/include-exclude/excluded.md create mode 100644 examples/showcase/configuration/docs/include-exclude/included.md create mode 100644 examples/showcase/configuration/docs/multiple/java/greeting.md create mode 100644 examples/showcase/configuration/docs/multiple/kotlin/greeting.md create mode 100644 examples/showcase/configuration/docs/named-sources/java-greeting.md create mode 100644 examples/showcase/configuration/docs/named-sources/kotlin-greeting.md create mode 100644 examples/showcase/configuration/docs/named-sources/text-line.md create mode 100644 examples/showcase/configuration/docs/root-source/go-mod.md create mode 100644 examples/showcase/configuration/docs/root-source/version.md create mode 100644 examples/showcase/configuration/docs/single-source/greeting.md create mode 100644 examples/showcase/configuration/include-exclude.yml create mode 100644 examples/showcase/configuration/multiple-embeddings.yml create mode 100644 examples/showcase/configuration/named-sources.yml create mode 100644 examples/showcase/configuration/root-source.yml create mode 100644 examples/showcase/configuration/single-source.yml create mode 100644 examples/showcase/docs/01-whole-file-source.md create mode 100644 examples/showcase/docs/02-source-line-pattern.md create mode 100644 examples/showcase/docs/03-named-fragment.md create mode 100644 examples/showcase/docs/04-paired-instruction-tag.md create mode 100644 examples/showcase/docs/05-named-source-root.md create mode 100644 examples/showcase/docs/06-start-end-pattern.md create mode 100644 examples/showcase/docs/07-multi-line-pattern.md create mode 100644 examples/showcase/docs/08-escaped-glob-character.md create mode 100644 examples/showcase/docs/09-escaped-newline-text.md create mode 100644 examples/showcase/docs/10-comment-filtering.md create mode 100644 examples/showcase/docs/11-multi-part-fragment-separator.md create mode 100644 examples/showcase/docs/12-overlapping-fragments.md create mode 100644 examples/showcase/docs/13-markdown-fence-shielding.md create mode 100644 examples/showcase/docs/html-showcase.html create mode 100644 examples/showcase/docs/ignored-by-exclude.md create mode 100644 examples/showcase/embed-code.yml create mode 100644 examples/showcase/negative/docs/invalid-attributes.md create mode 100644 examples/showcase/negative/docs/missing-code-fence.md create mode 100644 examples/showcase/negative/docs/missing-fragment.md create mode 100644 examples/showcase/negative/docs/missing-pattern.md create mode 100644 examples/showcase/negative/docs/missing-source.md create mode 100644 examples/showcase/negative/docs/stale-snippet.md create mode 100644 examples/showcase/negative/docs/unclosed-code-fence.md create mode 100644 examples/showcase/negative/processing-errors.yml create mode 100644 examples/showcase/negative/stale.yml create mode 100644 examples/showcase/showcase_test.go diff --git a/examples/showcase/README.md b/examples/showcase/README.md new file mode 100644 index 00000000..a26ee5bc --- /dev/null +++ b/examples/showcase/README.md @@ -0,0 +1,99 @@ +# Embed Code Showcase + +This folder is an opt-in, executable guide to `embed-code-go`. It is not part of +the normal `go test ./...` flow. The Go test is guarded by the `showcase` build +tag, so run it only when you want to verify the examples end to end. + +Run commands from the repository root. + +The showcase-owned source examples live under [code](code/). Repository-root +source examples are kept in the configuration showcase only. + +## Positive Flow + +Refresh the generated snippets: + +```bash +go run ./main.go -mode embed -config-path examples/showcase/embed-code.yml +``` + +Verify that the snippets are up-to-date: + +```bash +go run ./main.go -mode check -config-path examples/showcase/embed-code.yml +``` + +Run the opt-in test: + +```bash +go test -tags showcase ./examples/showcase +``` + +The positive showcase covers: + +| Case | File | What it verifies | +|--------------------------|------------------------------------------------------------------------------|-----------------------------------------------------------------------| +| Whole file | [docs/01-whole-file-source.md](docs/01-whole-file-source.md) | Omitting selection attributes embeds the whole showcase source file. | +| Source line pattern | [docs/02-source-line-pattern.md](docs/02-source-line-pattern.md) | A showcase source file can be matched with a `line` pattern. | +| Named fragment | [docs/03-named-fragment.md](docs/03-named-fragment.md) | `fragment` uses `#docfragment` markers and omits marker lines. | +| Paired instruction tags | [docs/04-paired-instruction-tag.md](docs/04-paired-instruction-tag.md) | Paired tags are preferred; self-closing tags are still supported. | +| Named source roots | [docs/05-named-source-root.md](docs/05-named-source-root.md) | `$java/...` and `$kotlin/...` select different configured roots. | +| Start and end patterns | [docs/06-start-end-pattern.md](docs/06-start-end-pattern.md) | `start` and `end` select an inclusive source range. | +| Multi-line patterns | [docs/07-multi-line-pattern.md](docs/07-multi-line-pattern.md) | `\n` matches consecutive source lines. | +| Escaped glob characters | [docs/08-escaped-glob-character.md](docs/08-escaped-glob-character.md) | `\*` matches a literal asterisk. | +| Escaped newline text | [docs/09-escaped-newline-text.md](docs/09-escaped-newline-text.md) | `\\n` matches a literal backslash-n sequence. | +| Comment filtering | [docs/10-comment-filtering.md](docs/10-comment-filtering.md) | `comments="documentation"` keeps Java documentation comments. | +| Multi-part fragments | [docs/11-multi-part-fragment-separator.md](docs/11-multi-part-fragment-separator.md) | Repeated fragment markers are joined with the configured separator. | +| Overlapping fragments | [docs/12-overlapping-fragments.md](docs/12-overlapping-fragments.md) | Multiple fragment names may share marker lines. | +| Markdown fence shielding | [docs/13-markdown-fence-shielding.md](docs/13-markdown-fence-shielding.md) | Instruction-looking text inside a regular fence is ignored. | +| HTML documents | [docs/html-showcase.html](docs/html-showcase.html) | HTML files can be scanned when included by configuration. | +| Excludes | [docs/ignored-by-exclude.md](docs/ignored-by-exclude.md) | Excluded files are not processed even when they contain instructions. | + +## Negative Flow + +The negative examples are intentionally broken. These commands should fail. + +```bash +go run ./main.go -mode check -config-path examples/showcase/negative/processing-errors.yml +go run ./main.go -mode check -config-path examples/showcase/negative/stale.yml +``` + +The processing-error config verifies: + +| Case | File | Expected behavior | +|---------------------|------------------------------------------------------------------------------|------------------------------------------------------| +| Missing source | [negative/docs/missing-source.md](negative/docs/missing-source.md) | Reports that the code file cannot be found. | +| Missing fragment | [negative/docs/missing-fragment.md](negative/docs/missing-fragment.md) | Reports that the requested fragment cannot be found. | +| Missing pattern | [negative/docs/missing-pattern.md](negative/docs/missing-pattern.md) | Reports that no source line matches the pattern. | +| Invalid attributes | [negative/docs/invalid-attributes.md](negative/docs/invalid-attributes.md) | Rejects mutually exclusive selection attributes. | +| Missing code fence | [negative/docs/missing-code-fence.md](negative/docs/missing-code-fence.md) | Requires a fence immediately after an instruction. | +| Unclosed code fence | [negative/docs/unclosed-code-fence.md](negative/docs/unclosed-code-fence.md) | Reports an instruction fence that reaches EOF. | + +The stale config verifies: + +| Case | File | Expected behavior | +|---------------|--------------------------------------------------------------------|------------------------------------------------------------------------| +| Stale snippet | [negative/docs/stale-snippet.md](negative/docs/stale-snippet.md) | Check mode reports the file as needing an update without rewriting it. | + +## Configuration Flow + +Configuration examples live under [configuration](configuration/). They cover a +repository-root source, a single source root, named source roots, +include/exclude patterns, and the `embeddings` list for multiple documentation +roots. + +| Case | Config | Docs root | +|----------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------| +| Repository root | [configuration/root-source.yml](configuration/root-source.yml) | [configuration/docs/root-source](configuration/docs/root-source/) | +| Single source root | [configuration/single-source.yml](configuration/single-source.yml) | [configuration/docs/single-source](configuration/docs/single-source/) | +| Named source roots | [configuration/named-sources.yml](configuration/named-sources.yml) | [configuration/docs/named-sources](configuration/docs/named-sources/) | +| Include and exclude | [configuration/include-exclude.yml](configuration/include-exclude.yml) | [configuration/docs/include-exclude](configuration/docs/include-exclude/) | +| Multiple embeddings | [configuration/multiple-embeddings.yml](configuration/multiple-embeddings.yml) | [configuration/docs/multiple](configuration/docs/multiple/) | + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml +``` diff --git a/examples/showcase/code/java/org/showcase/CommentModes.java b/examples/showcase/code/java/org/showcase/CommentModes.java new file mode 100644 index 00000000..3cbf65fd --- /dev/null +++ b/examples/showcase/code/java/org/showcase/CommentModes.java @@ -0,0 +1,14 @@ +package org.showcase; + +/** + * Creates public greetings. + */ +public interface CommentModes { + /* + * Internal implementation note. + */ + String URL = "http://example.org/*not-comment*/"; + + // Regular inline comment. + String greet(String name); // trailing inline comment. +} diff --git a/examples/showcase/code/java/org/showcase/Greeting.java b/examples/showcase/code/java/org/showcase/Greeting.java new file mode 100644 index 00000000..60ab7c9c --- /dev/null +++ b/examples/showcase/code/java/org/showcase/Greeting.java @@ -0,0 +1,17 @@ +package org.showcase; + +// #docfragment "Greeter class" +public final class Greeting { + private Greeting() {} + + // #docfragment "main()" + public static void main(String[] args) { + System.out.println(greeting("Ada")); + } + // #enddocfragment "main()" + + public static String greeting(String name) { + return "Hello, " + name + "!"; + } +} +// #enddocfragment "Greeter class" diff --git a/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java b/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java new file mode 100644 index 00000000..71c4ba34 --- /dev/null +++ b/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java @@ -0,0 +1,23 @@ +package org.showcase; + +// #docfragment "Workflow" +public final class MultiPartWorkflow { + // #enddocfragment "Workflow" + private MultiPartWorkflow() {} + + // #docfragment "Workflow" + public static void start() { + // #enddocfragment "Workflow" + System.out.println("Internal setup is hidden."); + // #docfragment "Workflow" + System.out.println("Start workflow"); + } + // #enddocfragment "Workflow" + + public static void finish() { + System.out.println("Finish workflow"); + } + +// #docfragment "Workflow" +} +// #enddocfragment "Workflow" diff --git a/examples/showcase/code/java/org/showcase/OverlappingFragments.java b/examples/showcase/code/java/org/showcase/OverlappingFragments.java new file mode 100644 index 00000000..602ec4a7 --- /dev/null +++ b/examples/showcase/code/java/org/showcase/OverlappingFragments.java @@ -0,0 +1,28 @@ +package org.showcase; + +// #docfragment "Class wrapper", "Greeting method" +public final class OverlappingFragments { + // #enddocfragment "Class wrapper", "Greeting method" + private OverlappingFragments() {} + + // #docfragment "Class wrapper" + public static void boot() { + // #enddocfragment "Class wrapper" + System.out.println("Boot details are hidden."); + // #docfragment "Class wrapper" + System.out.println("Boot complete"); + } + // #enddocfragment "Class wrapper" + + // #docfragment "Greeting method" + public static String greeting(String name) { + // #enddocfragment "Greeting method" + var normalized = name.trim(); + // #docfragment "Greeting method" + return "Hello, " + normalized + "!"; + } + // #enddocfragment "Greeting method" + +// #docfragment "Class wrapper", "Greeting method" +} +// #enddocfragment "Class wrapper", "Greeting method" diff --git a/examples/showcase/code/java/org/showcase/PatternSamples.java b/examples/showcase/code/java/org/showcase/PatternSamples.java new file mode 100644 index 00000000..8d429f8a --- /dev/null +++ b/examples/showcase/code/java/org/showcase/PatternSamples.java @@ -0,0 +1,22 @@ +package org.showcase; + +class PatternSamples { + + private static final String ESCAPED_NEWLINE = "\n"; + + @Scenario + @Name("adds two numbers") + void addsTwoNumbers() { + int total = 1 + 1; + + assertEquals(2, total); + } + + @Scenario + @Name("subtracts two numbers") + void subtractsTwoNumbers() { + int total = 2 - 1; + + assertEquals(1, total); + } +} diff --git a/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt b/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt new file mode 100644 index 00000000..5b06ade2 --- /dev/null +++ b/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt @@ -0,0 +1,11 @@ +package org.showcase + +object KotlinGreeting { + + // #docfragment "main()" + @JvmStatic + fun main(args: Array) { + println("Hello from Kotlin") + } + // #enddocfragment "main()" +} diff --git a/examples/showcase/code/text/glob-patterns.txt b/examples/showcase/code/text/glob-patterns.txt new file mode 100644 index 00000000..d7529af1 --- /dev/null +++ b/examples/showcase/code/text/glob-patterns.txt @@ -0,0 +1,5 @@ + padded text +Use * to multiply +The total is $5 +The value ends with $ +^ starts with caret diff --git a/examples/showcase/configuration/README.md b/examples/showcase/configuration/README.md new file mode 100644 index 00000000..872da7a4 --- /dev/null +++ b/examples/showcase/configuration/README.md @@ -0,0 +1,56 @@ +# Configuration Examples + +These examples show the supported YAML configuration shapes. Run commands from +the repository root. + +## Repository Root Source + +[root-source.yml](root-source.yml) uses the repository root as a named source +root. Instructions in [docs/root-source](docs/root-source/) embed files from +the project root with the `$repo` prefix. + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml +``` + +## Single Showcase Source Root + +[single-source.yml](single-source.yml) uses one unnamed `code-path`. +Instructions in [docs/single-source](docs/single-source/) use paths relative to +that root without a `$name` prefix. + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml +``` + +## Named Source Roots + +[named-sources.yml](named-sources.yml) defines Java, Kotlin, and text source +roots. Instructions in [docs/named-sources](docs/named-sources/) choose a +source root with `$java`, `$kotlin`, or `$text`. + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml +``` + +## Include And Exclude Patterns + +[include-exclude.yml](include-exclude.yml) processes Markdown files in +[docs/include-exclude](docs/include-exclude/) but excludes +[excluded.md](docs/include-exclude/excluded.md). That file intentionally +references a missing source file, so the check succeeds only when +`doc-excludes` is applied. + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml +``` + +## Multiple Embeddings + +[multiple-embeddings.yml](multiple-embeddings.yml) uses the `embeddings` list +to process two independent documentation roots in +[docs/multiple](docs/multiple/) in one run. + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml +``` diff --git a/examples/showcase/configuration/docs/include-exclude/excluded.md b/examples/showcase/configuration/docs/include-exclude/excluded.md new file mode 100644 index 00000000..a21fb471 --- /dev/null +++ b/examples/showcase/configuration/docs/include-exclude/excluded.md @@ -0,0 +1,8 @@ +# Excluded Document + +The config excludes this file. The intentionally missing source file proves +that excluded documents are not processed. + + +```java +``` diff --git a/examples/showcase/configuration/docs/include-exclude/included.md b/examples/showcase/configuration/docs/include-exclude/included.md new file mode 100644 index 00000000..7faa5e71 --- /dev/null +++ b/examples/showcase/configuration/docs/include-exclude/included.md @@ -0,0 +1,11 @@ +# Included Document + +The `doc-includes` pattern selects this Markdown file, so the instruction is +processed normally. + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` diff --git a/examples/showcase/configuration/docs/multiple/java/greeting.md b/examples/showcase/configuration/docs/multiple/java/greeting.md new file mode 100644 index 00000000..17616836 --- /dev/null +++ b/examples/showcase/configuration/docs/multiple/java/greeting.md @@ -0,0 +1,10 @@ +# Java Embedding Entry + +This document is processed by the `java-guide` entry in an `embeddings` config. + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` diff --git a/examples/showcase/configuration/docs/multiple/kotlin/greeting.md b/examples/showcase/configuration/docs/multiple/kotlin/greeting.md new file mode 100644 index 00000000..81c89d47 --- /dev/null +++ b/examples/showcase/configuration/docs/multiple/kotlin/greeting.md @@ -0,0 +1,12 @@ +# Kotlin Embedding Entry + +This document is processed by the `kotlin-guide` entry in the same +`embeddings` config. + + +```kotlin +@JvmStatic +fun main(args: Array) { + println("Hello from Kotlin") +} +``` diff --git a/examples/showcase/configuration/docs/named-sources/java-greeting.md b/examples/showcase/configuration/docs/named-sources/java-greeting.md new file mode 100644 index 00000000..6107d347 --- /dev/null +++ b/examples/showcase/configuration/docs/named-sources/java-greeting.md @@ -0,0 +1,11 @@ +# Named Java Source + +This config has multiple named source roots. The `$java` prefix chooses the Java +root before resolving the relative path. + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` diff --git a/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md b/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md new file mode 100644 index 00000000..f3b6160c --- /dev/null +++ b/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md @@ -0,0 +1,12 @@ +# Named Kotlin Source + +The same config can embed from a different root by changing the source-root +prefix in the instruction. + + +```kotlin +@JvmStatic +fun main(args: Array) { + println("Hello from Kotlin") +} +``` diff --git a/examples/showcase/configuration/docs/named-sources/text-line.md b/examples/showcase/configuration/docs/named-sources/text-line.md new file mode 100644 index 00000000..f2e02fb8 --- /dev/null +++ b/examples/showcase/configuration/docs/named-sources/text-line.md @@ -0,0 +1,9 @@ +# Named Text Source + +Named roots do not need to be language-specific. This example embeds one line +from the text source root. + + +```text +The total is $5 +``` diff --git a/examples/showcase/configuration/docs/root-source/go-mod.md b/examples/showcase/configuration/docs/root-source/go-mod.md new file mode 100644 index 00000000..cbc414e0 --- /dev/null +++ b/examples/showcase/configuration/docs/root-source/go-mod.md @@ -0,0 +1,49 @@ +# Repository Root Source + +This configuration example uses the repository root as a named source root. The +instruction embeds `go.mod` through the `$repo` prefix. + + +```go +// Copyright 2026, TeamDev. All rights reserved. +// +// Redistribution and use in source and/or binary forms, with or without +// modification, must retain the above copyright notice and the following +// disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +module embed-code/embed-code-go + +go 1.22.1 + +require ( + github.com/bmatcuk/doublestar/v4 v4.6.1 + github.com/gobwas/glob v0.2.3 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.2 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect + github.com/stretchr/testify v1.9.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + golang.org/x/tools v0.24.0 // indirect +) +``` diff --git a/examples/showcase/configuration/docs/root-source/version.md b/examples/showcase/configuration/docs/root-source/version.md new file mode 100644 index 00000000..4e822b86 --- /dev/null +++ b/examples/showcase/configuration/docs/root-source/version.md @@ -0,0 +1,8 @@ +# Repository Root Line Pattern + +The same root-source configuration can select a single line from a root file. + + +```go +const Version = "1.2.2" +``` diff --git a/examples/showcase/configuration/docs/single-source/greeting.md b/examples/showcase/configuration/docs/single-source/greeting.md new file mode 100644 index 00000000..91bb961c --- /dev/null +++ b/examples/showcase/configuration/docs/single-source/greeting.md @@ -0,0 +1,11 @@ +# Single Source Root + +This config uses one unnamed `code-path`, so instructions refer to source files +relative to that root without a `$name` prefix. + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` diff --git a/examples/showcase/configuration/include-exclude.yml b/examples/showcase/configuration/include-exclude.yml new file mode 100644 index 00000000..d165aaf1 --- /dev/null +++ b/examples/showcase/configuration/include-exclude.yml @@ -0,0 +1,6 @@ +code-path: examples/showcase/code/java +docs-path: examples/showcase/configuration/docs/include-exclude +doc-includes: + - "**/*.md" +doc-excludes: + - excluded.md diff --git a/examples/showcase/configuration/multiple-embeddings.yml b/examples/showcase/configuration/multiple-embeddings.yml new file mode 100644 index 00000000..0316823f --- /dev/null +++ b/examples/showcase/configuration/multiple-embeddings.yml @@ -0,0 +1,13 @@ +embeddings: + - name: java-guide + code-path: examples/showcase/code/java + docs-path: examples/showcase/configuration/docs/multiple/java + doc-includes: + - "**/*.md" + - name: kotlin-guide + code-path: + - name: kotlin + path: examples/showcase/code/kotlin + docs-path: examples/showcase/configuration/docs/multiple/kotlin + doc-includes: + - "**/*.md" diff --git a/examples/showcase/configuration/named-sources.yml b/examples/showcase/configuration/named-sources.yml new file mode 100644 index 00000000..a624a75f --- /dev/null +++ b/examples/showcase/configuration/named-sources.yml @@ -0,0 +1,10 @@ +code-path: + - name: java + path: examples/showcase/code/java + - name: kotlin + path: examples/showcase/code/kotlin + - name: text + path: examples/showcase/code/text +docs-path: examples/showcase/configuration/docs/named-sources +doc-includes: + - "**/*.md" diff --git a/examples/showcase/configuration/root-source.yml b/examples/showcase/configuration/root-source.yml new file mode 100644 index 00000000..f7209a30 --- /dev/null +++ b/examples/showcase/configuration/root-source.yml @@ -0,0 +1,6 @@ +code-path: + - name: repo + path: . +docs-path: examples/showcase/configuration/docs/root-source +doc-includes: + - "**/*.md" diff --git a/examples/showcase/configuration/single-source.yml b/examples/showcase/configuration/single-source.yml new file mode 100644 index 00000000..45fac634 --- /dev/null +++ b/examples/showcase/configuration/single-source.yml @@ -0,0 +1,4 @@ +code-path: examples/showcase/code/java +docs-path: examples/showcase/configuration/docs/single-source +doc-includes: + - "**/*.md" diff --git a/examples/showcase/docs/01-whole-file-source.md b/examples/showcase/docs/01-whole-file-source.md new file mode 100644 index 00000000..ed7016ed --- /dev/null +++ b/examples/showcase/docs/01-whole-file-source.md @@ -0,0 +1,20 @@ +# Whole File From A Showcase Source + +Omitting `fragment`, `start`, `end`, and `line` embeds the whole source file. + + +```java +package org.showcase; + +public final class Greeting { + private Greeting() {} + + public static void main(String[] args) { + System.out.println(greeting("Ada")); + } + + public static String greeting(String name) { + return "Hello, " + name + "!"; + } +} +``` diff --git a/examples/showcase/docs/02-source-line-pattern.md b/examples/showcase/docs/02-source-line-pattern.md new file mode 100644 index 00000000..93c342aa --- /dev/null +++ b/examples/showcase/docs/02-source-line-pattern.md @@ -0,0 +1,8 @@ +# One Line From A Showcase Source + +The `line` attribute embeds only the first source line matching the pattern. + + +```java +return "Hello, " + name + "!"; +``` diff --git a/examples/showcase/docs/03-named-fragment.md b/examples/showcase/docs/03-named-fragment.md new file mode 100644 index 00000000..2e6e641e --- /dev/null +++ b/examples/showcase/docs/03-named-fragment.md @@ -0,0 +1,11 @@ +# Named Fragment + +The `fragment` attribute embeds lines between matching `#docfragment` and +`#enddocfragment` markers. Marker lines are not rendered. + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` diff --git a/examples/showcase/docs/04-paired-instruction-tag.md b/examples/showcase/docs/04-paired-instruction-tag.md new file mode 100644 index 00000000..bf4dcb61 --- /dev/null +++ b/examples/showcase/docs/04-paired-instruction-tag.md @@ -0,0 +1,36 @@ +# Paired Instruction Tag + +Instructions may be self-closing or paired. The self-closing form is supported, +but this showcase uses paired tags because some Markdown renderers display the +XML-style self-closing tag awkwardly. + +## Short Paired Tag + +This compact paired form is useful when all attributes fit naturally in one +line. + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` + +## Paired Tag With A Larger Fragment + +The same paired form works for larger snippets too. + + +```java +public final class Greeting { + private Greeting() {} + + public static void main(String[] args) { + System.out.println(greeting("Ada")); + } + + public static String greeting(String name) { + return "Hello, " + name + "!"; + } +} +``` diff --git a/examples/showcase/docs/05-named-source-root.md b/examples/showcase/docs/05-named-source-root.md new file mode 100644 index 00000000..f138e1c3 --- /dev/null +++ b/examples/showcase/docs/05-named-source-root.md @@ -0,0 +1,13 @@ +# Named Source Roots + +When a configuration has named code paths, the instruction chooses one with the +`$name/relative/path` prefix. This example reads Kotlin code from the `kotlin` +source root while the Java examples read from `java`. + + +```kotlin +@JvmStatic +fun main(args: Array) { + println("Hello from Kotlin") +} +``` diff --git a/examples/showcase/docs/06-start-end-pattern.md b/examples/showcase/docs/06-start-end-pattern.md new file mode 100644 index 00000000..416d9c20 --- /dev/null +++ b/examples/showcase/docs/06-start-end-pattern.md @@ -0,0 +1,18 @@ +# Start And End Patterns + +The `start` and `end` attributes select an inclusive source range. Patterns are +glob-like and the end search starts after the matched start. + + +```java +@Scenario +@Name("adds two numbers") +void addsTwoNumbers() { + int total = 1 + 1; + + assertEquals(2, total); +} +``` diff --git a/examples/showcase/docs/07-multi-line-pattern.md b/examples/showcase/docs/07-multi-line-pattern.md new file mode 100644 index 00000000..b30b84bd --- /dev/null +++ b/examples/showcase/docs/07-multi-line-pattern.md @@ -0,0 +1,18 @@ +# Multi-Line Patterns + +Use `\n` inside a pattern when the match should span consecutive source lines. +Each pattern line still uses the same glob syntax. + + +```java +@Scenario +@Name("adds two numbers") +void addsTwoNumbers() { + int total = 1 + 1; + + assertEquals(2, total); +} +``` diff --git a/examples/showcase/docs/08-escaped-glob-character.md b/examples/showcase/docs/08-escaped-glob-character.md new file mode 100644 index 00000000..e2e78ecb --- /dev/null +++ b/examples/showcase/docs/08-escaped-glob-character.md @@ -0,0 +1,9 @@ +# Escaped Glob Characters + +Backslashes match glob control characters literally. This case embeds a line +that contains a literal asterisk. + + +```text +Use * to multiply +``` diff --git a/examples/showcase/docs/09-escaped-newline-text.md b/examples/showcase/docs/09-escaped-newline-text.md new file mode 100644 index 00000000..61d7dda5 --- /dev/null +++ b/examples/showcase/docs/09-escaped-newline-text.md @@ -0,0 +1,11 @@ +# Escaped Newline Text + +Use `\\n` when the source line contains the characters backslash and `n`. +Quote characters may be escaped as `\"` inside instruction attributes. + + +```java +private static final String ESCAPED_NEWLINE = "\n"; +``` diff --git a/examples/showcase/docs/10-comment-filtering.md b/examples/showcase/docs/10-comment-filtering.md new file mode 100644 index 00000000..ad0aeaef --- /dev/null +++ b/examples/showcase/docs/10-comment-filtering.md @@ -0,0 +1,19 @@ +# Comment Filtering + +The `comments` attribute controls which recognized comments remain in the +rendered snippet. This example keeps documentation comments and removes regular +comments from Java source. + + +```java +package org.showcase; + +/** + * Creates public greetings. + */ +public interface CommentModes { + String URL = "http://example.org/*not-comment*/"; + + String greet(String name); +} +``` diff --git a/examples/showcase/docs/11-multi-part-fragment-separator.md b/examples/showcase/docs/11-multi-part-fragment-separator.md new file mode 100644 index 00000000..9ff4c429 --- /dev/null +++ b/examples/showcase/docs/11-multi-part-fragment-separator.md @@ -0,0 +1,16 @@ +# Multi-Part Fragment Separator + +Fragments with the same name may appear in several source regions. The rendered +parts are joined in source order with the configured separator. + + +```java +public final class MultiPartWorkflow { + // ... + public static void start() { + // ... + System.out.println("Start workflow"); + } +// ... +} +``` diff --git a/examples/showcase/docs/12-overlapping-fragments.md b/examples/showcase/docs/12-overlapping-fragments.md new file mode 100644 index 00000000..51362fe3 --- /dev/null +++ b/examples/showcase/docs/12-overlapping-fragments.md @@ -0,0 +1,16 @@ +# Overlapping Fragments + +Several fragments can open or close on the same marker line. This example uses +an overlapping fragment that shares the class wrapper with another fragment. + + +```java +public final class OverlappingFragments { + // ... + public static String greeting(String name) { + // ... + return "Hello, " + normalized + "!"; + } +// ... +} +``` diff --git a/examples/showcase/docs/13-markdown-fence-shielding.md b/examples/showcase/docs/13-markdown-fence-shielding.md new file mode 100644 index 00000000..4f6d2c4d --- /dev/null +++ b/examples/showcase/docs/13-markdown-fence-shielding.md @@ -0,0 +1,11 @@ +# Instructions Inside Markdown Fences + +Instruction-looking text inside an ordinary Markdown code fence is preserved as +documentation content. It is not executed because the parser tracks Markdown +fence state before looking for instructions. + +````markdown + +```go +``` +```` diff --git a/examples/showcase/docs/html-showcase.html b/examples/showcase/docs/html-showcase.html new file mode 100644 index 00000000..b3a62d0a --- /dev/null +++ b/examples/showcase/docs/html-showcase.html @@ -0,0 +1,17 @@ + + + +

HTML Embedding Showcase

+

+ HTML files are scanned too when the include patterns allow them. The + instruction still needs a Markdown code fence after it. +

+ + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` + + diff --git a/examples/showcase/docs/ignored-by-exclude.md b/examples/showcase/docs/ignored-by-exclude.md new file mode 100644 index 00000000..f1c49501 --- /dev/null +++ b/examples/showcase/docs/ignored-by-exclude.md @@ -0,0 +1,9 @@ +# Excluded Showcase File + +The positive configuration excludes this file. Its intentionally broken +instruction verifies that `doc-excludes` prevents selected files from being +processed. + + +```go +``` diff --git a/examples/showcase/embed-code.yml b/examples/showcase/embed-code.yml new file mode 100644 index 00000000..b2a3bb02 --- /dev/null +++ b/examples/showcase/embed-code.yml @@ -0,0 +1,14 @@ +code-path: + - name: java + path: examples/showcase/code/java + - name: kotlin + path: examples/showcase/code/kotlin + - name: text + path: examples/showcase/code/text +docs-path: examples/showcase/docs +doc-includes: + - "**/*.md" + - "**/*.html" +doc-excludes: + - ignored-by-exclude.md +separator: "// ..." diff --git a/examples/showcase/negative/docs/invalid-attributes.md b/examples/showcase/negative/docs/invalid-attributes.md new file mode 100644 index 00000000..d8feab3f --- /dev/null +++ b/examples/showcase/negative/docs/invalid-attributes.md @@ -0,0 +1,8 @@ +# Invalid Attributes + +This scenario fails because `fragment` cannot be combined with `line`, `start`, +or `end`. + + +```java +``` diff --git a/examples/showcase/negative/docs/missing-code-fence.md b/examples/showcase/negative/docs/missing-code-fence.md new file mode 100644 index 00000000..32c060ad --- /dev/null +++ b/examples/showcase/negative/docs/missing-code-fence.md @@ -0,0 +1,7 @@ +# Missing Code Fence + +This scenario fails because every active instruction must be followed by a +Markdown code fence. + + +This line is not a code fence. diff --git a/examples/showcase/negative/docs/missing-fragment.md b/examples/showcase/negative/docs/missing-fragment.md new file mode 100644 index 00000000..7a89643e --- /dev/null +++ b/examples/showcase/negative/docs/missing-fragment.md @@ -0,0 +1,8 @@ +# Missing Fragment + +This scenario fails because the source file exists, but the requested named +fragment does not. + + +```java +``` diff --git a/examples/showcase/negative/docs/missing-pattern.md b/examples/showcase/negative/docs/missing-pattern.md new file mode 100644 index 00000000..32360d6a --- /dev/null +++ b/examples/showcase/negative/docs/missing-pattern.md @@ -0,0 +1,8 @@ +# Missing Pattern + +This scenario fails because the source file exists, but no line matches the +requested line pattern. + + +```java +``` diff --git a/examples/showcase/negative/docs/missing-source.md b/examples/showcase/negative/docs/missing-source.md new file mode 100644 index 00000000..5aeabd7b --- /dev/null +++ b/examples/showcase/negative/docs/missing-source.md @@ -0,0 +1,8 @@ +# Missing Source + +This scenario fails because the instruction points to a file that cannot be +resolved from the configured source roots. + + +```java +``` diff --git a/examples/showcase/negative/docs/stale-snippet.md b/examples/showcase/negative/docs/stale-snippet.md new file mode 100644 index 00000000..b74d5c29 --- /dev/null +++ b/examples/showcase/negative/docs/stale-snippet.md @@ -0,0 +1,11 @@ +# Stale Snippet + +This scenario is syntactically valid, but check mode reports it as stale because +the rendered code fence does not match the current source fragment. + + +```java +public static void main(String[] args) { + System.out.println("Out of date"); +} +``` diff --git a/examples/showcase/negative/docs/unclosed-code-fence.md b/examples/showcase/negative/docs/unclosed-code-fence.md new file mode 100644 index 00000000..a89f5cd5 --- /dev/null +++ b/examples/showcase/negative/docs/unclosed-code-fence.md @@ -0,0 +1,9 @@ +# Unclosed Code Fence + +This scenario fails because the opening fence after the instruction is never +closed. + + +```java +public static void main(String[] args) { +} diff --git a/examples/showcase/negative/processing-errors.yml b/examples/showcase/negative/processing-errors.yml new file mode 100644 index 00000000..90cc680d --- /dev/null +++ b/examples/showcase/negative/processing-errors.yml @@ -0,0 +1,13 @@ +code-path: + - name: repo + path: . + - name: java + path: examples/showcase/code/java +docs-path: examples/showcase/negative/docs +doc-includes: + - missing-source.md + - missing-fragment.md + - missing-pattern.md + - invalid-attributes.md + - missing-code-fence.md + - unclosed-code-fence.md diff --git a/examples/showcase/negative/stale.yml b/examples/showcase/negative/stale.yml new file mode 100644 index 00000000..0cd2bd65 --- /dev/null +++ b/examples/showcase/negative/stale.yml @@ -0,0 +1,6 @@ +code-path: + - name: java + path: examples/showcase/code/java +docs-path: examples/showcase/negative/docs +doc-includes: + - stale-snippet.md diff --git a/examples/showcase/showcase_test.go b/examples/showcase/showcase_test.go new file mode 100644 index 00000000..46a2c55b --- /dev/null +++ b/examples/showcase/showcase_test.go @@ -0,0 +1,354 @@ +//go:build showcase + +// Copyright 2026, TeamDev. All rights reserved. +// +// Redistribution and use in source and/or binary forms, with or without +// modification, must retain the above copyright notice and the following +// disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package showcase_test + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +// TestShowcasePositiveFlow verifies the showcase docs can be checked, detected as stale, +// repaired with embed mode, and checked again without changing repository files. +func TestShowcasePositiveFlow(t *testing.T) { + repoRoot := findRepoRoot(t) + docsRoot := copyShowcaseDocs(t, repoRoot, "docs") + configPath := writeShowcaseConfig(t, repoRoot, docsRoot) + + checkOutput, err := runEmbedCode(t, repoRoot, "check", configPath) + if err != nil { + t.Fatalf("expected positive showcase check to pass:\n%s", checkOutput) + } + + staleDoc := filepath.Join(docsRoot, "01-whole-file-source.md") + replaceInFile(t, staleDoc, "package org.showcase;", "package stale.showcase;") + + staleOutput, err := runEmbedCode(t, repoRoot, "check", configPath) + if err == nil { + t.Fatalf("expected stale showcase check to fail:\n%s", staleOutput) + } + assertOutputContains(t, staleOutput, "File to update:") + assertOutputContains(t, staleOutput, "01-whole-file-source.md") + + embedOutput, err := runEmbedCode(t, repoRoot, "embed", configPath) + if err != nil { + t.Fatalf("expected positive showcase embed to repair stale doc:\n%s", embedOutput) + } + assertOutputContains(t, embedOutput, "Embedding process finished.") + + finalOutput, err := runEmbedCode(t, repoRoot, "check", configPath) + if err != nil { + t.Fatalf("expected positive showcase check to pass after embed:\n%s", finalOutput) + } +} + +// TestShowcaseNegativeScenarios verifies each negative document fails with its expected reason. +func TestShowcaseNegativeScenarios(t *testing.T) { + repoRoot := findRepoRoot(t) + + cases := []struct { + name string + doc string + sources []namedSource + expected []string + }{ + { + name: "missing source", + doc: "missing-source.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "code file `$java/org/showcase/DoesNotExist.java", + "not found", + }, + }, + { + name: "missing fragment", + doc: "missing-fragment.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "fragment `does not exist`", + "not found", + }, + }, + { + name: "missing pattern", + doc: "missing-pattern.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "matches the line pattern", + "doesNotExistPattern", + }, + }, + { + name: "invalid attributes", + doc: "invalid-attributes.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "must NOT specify both a fragment name and start/end/line patterns", + }, + }, + { + name: "missing code fence", + doc: "missing-code-fence.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "expected a markdown code fence after the embedding instruction", + }, + }, + { + name: "unclosed code fence", + doc: "unclosed-code-fence.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "the markdown code fence after the embedding instruction is not closed", + }, + }, + { + name: "stale snippet", + doc: "stale-snippet.md", + sources: []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + }, + expected: []string{ + "File to update:", + "stale-snippet.md", + "the documentation files are not up-to-date with code files", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + docsRoot := copyShowcaseDocs(t, repoRoot, filepath.Join("negative", "docs")) + configPath := writeSingleDocConfig(t, docsRoot, tc.sources, tc.doc) + + output, err := runEmbedCode(t, repoRoot, "check", configPath) + if err == nil { + t.Fatalf("expected negative scenario to fail:\n%s", output) + } + for _, expected := range tc.expected { + assertOutputContains(t, output, expected) + } + }) + } +} + +// TestShowcaseConfigurationExamples verifies the runnable configuration examples. +func TestShowcaseConfigurationExamples(t *testing.T) { + repoRoot := findRepoRoot(t) + + configs := []string{ + "root-source.yml", + "single-source.yml", + "named-sources.yml", + "include-exclude.yml", + "multiple-embeddings.yml", + } + + for _, config := range configs { + t.Run(config, func(t *testing.T) { + configPath := filepath.Join("examples", "showcase", "configuration", config) + output, err := runEmbedCode(t, repoRoot, "check", configPath) + if err != nil { + t.Fatalf("expected configuration example to pass:\n%s", output) + } + }) + } +} + +type namedSource struct { + name string + path string +} + +// findRepoRoot returns the repository root by walking up from this test file. +func findRepoRoot(t *testing.T) string { + t.Helper() + + _, filePath, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("could not locate showcase test file") + } + + return filepath.Clean(filepath.Join(filepath.Dir(filePath), "..", "..")) +} + +// copyShowcaseDocs copies one showcase documentation folder to a temporary test directory. +func copyShowcaseDocs(t *testing.T, repoRoot string, relativeSource string) string { + t.Helper() + + sourceRoot := filepath.Join(repoRoot, "examples", "showcase", relativeSource) + targetRoot := filepath.Join(t.TempDir(), "docs") + copyDir(t, sourceRoot, targetRoot) + + return targetRoot +} + +// copyDir recursively copies a directory tree while preserving regular file permissions. +func copyDir(t *testing.T, sourceRoot string, targetRoot string) { + t.Helper() + + err := filepath.WalkDir(sourceRoot, func(path string, entry os.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + + relativePath, err := filepath.Rel(sourceRoot, path) + if err != nil { + return err + } + targetPath := filepath.Join(targetRoot, relativePath) + if entry.IsDir() { + return os.MkdirAll(targetPath, 0o755) + } + if !entry.Type().IsRegular() { + return nil + } + + data, err := os.ReadFile(path) + if err != nil { + return err + } + info, err := entry.Info() + if err != nil { + return err + } + + return os.WriteFile(targetPath, data, info.Mode()) + }) + if err != nil { + t.Fatalf("failed to copy showcase docs: %v", err) + } +} + +// writeShowcaseConfig creates a temp config that points at copied positive docs. +func writeShowcaseConfig(t *testing.T, repoRoot string, docsRoot string) string { + t.Helper() + + return writeConfig(t, docsRoot, []namedSource{ + {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + {name: "kotlin", path: filepath.Join(repoRoot, "examples", "showcase", "code", "kotlin")}, + {name: "text", path: filepath.Join(repoRoot, "examples", "showcase", "code", "text")}, + }, []string{"**/*.md", "**/*.html"}, []string{"ignored-by-exclude.md"}) +} + +// writeSingleDocConfig creates a temp config for one negative showcase document. +func writeSingleDocConfig( + t *testing.T, + docsRoot string, + sources []namedSource, + docInclude string, +) string { + t.Helper() + + return writeConfig(t, docsRoot, sources, []string{docInclude}, nil) +} + +// writeConfig writes a YAML config with absolute source and documentation paths. +func writeConfig( + t *testing.T, + docsRoot string, + sources []namedSource, + includes []string, + excludes []string, +) string { + t.Helper() + + var builder strings.Builder + builder.WriteString("code-path:\n") + for _, source := range sources { + builder.WriteString(fmt.Sprintf(" - name: %s\n", source.name)) + builder.WriteString(fmt.Sprintf(" path: %s\n", filepath.ToSlash(source.path))) + } + builder.WriteString(fmt.Sprintf("docs-path: %s\n", filepath.ToSlash(docsRoot))) + builder.WriteString("doc-includes:\n") + for _, include := range includes { + builder.WriteString(fmt.Sprintf(" - %q\n", include)) + } + if len(excludes) > 0 { + builder.WriteString("doc-excludes:\n") + for _, exclude := range excludes { + builder.WriteString(fmt.Sprintf(" - %q\n", exclude)) + } + } + builder.WriteString("separator: \"// ...\"\n") + + configPath := filepath.Join(t.TempDir(), "embed-code.yml") + if err := os.WriteFile(configPath, []byte(builder.String()), 0o644); err != nil { + t.Fatalf("failed to write temp config: %v", err) + } + + return configPath +} + +// runEmbedCode executes the CLI through `go run` and returns combined output. +func runEmbedCode(t *testing.T, repoRoot string, mode string, configPath string) (string, error) { + t.Helper() + + cmd := exec.Command("go", "run", "./main.go", "-mode", mode, "-config-path", configPath) + cmd.Dir = repoRoot + output, err := cmd.CombinedOutput() + + return string(output), err +} + +// replaceInFile replaces one expected substring in a copied documentation file. +func replaceInFile(t *testing.T, path string, oldText string, newText string) { + t.Helper() + + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("failed to read %s: %v", path, err) + } + content := string(data) + if !strings.Contains(content, oldText) { + t.Fatalf("expected %s to contain %q", path, oldText) + } + content = strings.Replace(content, oldText, newText, 1) + if err = os.WriteFile(path, []byte(content), 0o644); err != nil { + t.Fatalf("failed to write %s: %v", path, err) + } +} + +// assertOutputContains fails the test when a command output does not include a substring. +func assertOutputContains(t *testing.T, output string, expected string) { + t.Helper() + + if !strings.Contains(output, expected) { + t.Fatalf("expected output to contain %q:\n%s", expected, output) + } +} diff --git a/go.mod b/go.mod index 637cf52d..0f202b08 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -// Copyright 2024, TeamDev. All rights reserved. +// Copyright 2026, TeamDev. All rights reserved. // // Redistribution and use in source and/or binary forms, with or without // modification, must retain the above copyright notice and the following From 53718347044845baa4b85e8354f218bcfe3f5a3e Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 17:13:45 +0200 Subject: [PATCH 02/22] Add more descriptions. --- EMBEDDING.md | 5 ++ README.md | 5 ++ examples/showcase/README.md | 23 +++++++- examples/showcase/configuration/README.md | 22 +++++++- .../docs/include-exclude/excluded.md | 6 +++ .../docs/include-exclude/included.md | 7 +++ .../docs/multiple/java/greeting.md | 7 +++ .../docs/multiple/kotlin/greeting.md | 7 +++ .../docs/named-sources/java-greeting.md | 6 +++ .../docs/named-sources/kotlin-greeting.md | 6 +++ .../docs/named-sources/text-line.md | 7 +++ .../configuration/docs/root-source/go-mod.md | 52 ++++--------------- .../configuration/docs/root-source/version.md | 7 +++ .../docs/single-source/greeting.md | 6 +++ .../showcase/docs/01-whole-file-source.md | 11 +++- .../showcase/docs/02-source-line-pattern.md | 13 ++++- examples/showcase/docs/03-named-fragment.md | 11 +++- .../docs/04-paired-instruction-tag.md | 27 ++-------- .../showcase/docs/05-named-source-root.md | 11 ++-- .../showcase/docs/06-start-end-pattern.md | 11 +++- .../showcase/docs/07-multi-line-pattern.md | 11 +++- .../docs/08-escaped-glob-character.md | 10 +++- .../showcase/docs/09-escaped-newline-text.md | 10 +++- .../showcase/docs/10-comment-filtering.md | 12 +++-- .../docs/11-multi-part-fragment-separator.md | 11 +++- .../showcase/docs/12-overlapping-fragments.md | 11 +++- .../docs/13-markdown-fence-shielding.md | 11 ++-- examples/showcase/docs/html-showcase.html | 9 +++- examples/showcase/docs/ignored-by-exclude.md | 12 +++-- .../negative/docs/invalid-attributes.md | 10 +++- .../negative/docs/missing-code-fence.md | 11 +++- .../negative/docs/missing-fragment.md | 9 +++- .../showcase/negative/docs/missing-pattern.md | 11 +++- .../showcase/negative/docs/missing-source.md | 10 +++- .../showcase/negative/docs/stale-snippet.md | 11 +++- .../negative/docs/unclosed-code-fence.md | 10 +++- 36 files changed, 308 insertions(+), 111 deletions(-) diff --git a/EMBEDDING.md b/EMBEDDING.md index 4b091d83..d0ea407e 100644 --- a/EMBEDDING.md +++ b/EMBEDDING.md @@ -2,6 +2,11 @@ The `embed-code` utility uses a custom `` tag to insert code snippets from source files into Markdown documentation. +For executable examples of the embedding features described here, see the +[embed-code showcase](examples/showcase/README.md). The showcase uses paired +instruction tags and dedicated source fixtures so it can double as a guide and +an opt-in end-to-end test. + ## Embedding options There are two ways to specify which code fragment to embed: diff --git a/README.md b/README.md index bf339950..7265c99c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,11 @@ This project is the implementation of `embed-code` utility written in Go. For the details of the usage in the documentation and the code, please refer to the [EMBEDDING.md](EMBEDDING.md). +For a runnable guide with positive examples, negative examples, and YAML +configuration shapes, see the [embed-code showcase](examples/showcase/README.md). +The showcase is an opt-in end-to-end test and is not part of the normal +`go test ./...` flow. + ## Running Embed Code operates in two modes: diff --git a/examples/showcase/README.md b/examples/showcase/README.md index a26ee5bc..7a1f5af4 100644 --- a/examples/showcase/README.md +++ b/examples/showcase/README.md @@ -9,6 +9,16 @@ Run commands from the repository root. The showcase-owned source examples live under [code](code/). Repository-root source examples are kept in the configuration showcase only. +## How To Use This Guide + +Read the files in [docs](docs/) from `01` onward when learning the embedding +syntax for the first time. Each file owns one positive case, explains how the +instruction is resolved, and shows the rendered code fence that embed mode owns. + +Then inspect [negative/docs](negative/docs/) to see the failures that check mode +reports without rewriting files. Finally, read [configuration](configuration/) +to compare the YAML shapes that point documentation roots at source roots. + ## Positive Flow Refresh the generated snippets: @@ -29,6 +39,11 @@ Run the opt-in test: go test -tags showcase ./examples/showcase ``` +The test copies the showcase docs to a temporary directory, runs check mode, +intentionally makes one copied snippet stale, repairs it with embed mode, and +then verifies the negative and configuration cases. The build tag keeps this +larger documentation test out of the default test flow. + The positive showcase covers: | Case | File | What it verifies | @@ -51,7 +66,9 @@ The positive showcase covers: ## Negative Flow -The negative examples are intentionally broken. These commands should fail. +The negative examples are intentionally broken. They are still documentation: +each file describes the mistake, the expected failure reason, and what a user +should fix in a real document. These commands should fail. ```bash go run ./main.go -mode check -config-path examples/showcase/negative/processing-errors.yml @@ -82,6 +99,10 @@ repository-root source, a single source root, named source roots, include/exclude patterns, and the `embeddings` list for multiple documentation roots. +Each configuration has its own docs root so the examples can be run separately. +Open the YAML file first, then follow the linked docs root to see how the +instructions use that configuration. + | Case | Config | Docs root | |----------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------| | Repository root | [configuration/root-source.yml](configuration/root-source.yml) | [configuration/docs/root-source](configuration/docs/root-source/) | diff --git a/examples/showcase/configuration/README.md b/examples/showcase/configuration/README.md index 872da7a4..74747e64 100644 --- a/examples/showcase/configuration/README.md +++ b/examples/showcase/configuration/README.md @@ -1,7 +1,10 @@ # Configuration Examples -These examples show the supported YAML configuration shapes. Run commands from -the repository root. +These examples show the supported YAML configuration shapes. + +Each YAML file has a matching docs root under [docs](docs/). Read the YAML file +first, then open the linked docs folder to see how instructions use that source +configuration. ## Repository Root Source @@ -9,6 +12,10 @@ the repository root. root. Instructions in [docs/root-source](docs/root-source/) embed files from the project root with the `$repo` prefix. +Use this shape only when documentation really needs files from the repository +root. The main embedding showcase avoids root sources so ordinary examples stay +independent from project metadata. + ```bash go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml ``` @@ -19,6 +26,9 @@ go run ./main.go -mode check -config-path examples/showcase/configuration/root-s Instructions in [docs/single-source](docs/single-source/) use paths relative to that root without a `$name` prefix. +This is the simplest configuration for one source tree and one documentation +tree. + ```bash go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml ``` @@ -29,6 +39,8 @@ go run ./main.go -mode check -config-path examples/showcase/configuration/single roots. Instructions in [docs/named-sources](docs/named-sources/) choose a source root with `$java`, `$kotlin`, or `$text`. +Use this shape when one docs tree needs snippets from several source trees. + ```bash go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml ``` @@ -41,6 +53,9 @@ go run ./main.go -mode check -config-path examples/showcase/configuration/named- references a missing source file, so the check succeeds only when `doc-excludes` is applied. +Use this shape to skip drafts, generated docs, deprecated pages, or any file +that should not be scanned for active instructions. + ```bash go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml ``` @@ -51,6 +66,9 @@ go run ./main.go -mode check -config-path examples/showcase/configuration/includ to process two independent documentation roots in [docs/multiple](docs/multiple/) in one run. +Use this shape when one command should process several independent +documentation targets with different source roots or settings. + ```bash go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml ``` diff --git a/examples/showcase/configuration/docs/include-exclude/excluded.md b/examples/showcase/configuration/docs/include-exclude/excluded.md index a21fb471..991f2a33 100644 --- a/examples/showcase/configuration/docs/include-exclude/excluded.md +++ b/examples/showcase/configuration/docs/include-exclude/excluded.md @@ -3,6 +3,12 @@ The config excludes this file. The intentionally missing source file proves that excluded documents are not processed. +## How It Works + +[../../include-exclude.yml](../../include-exclude.yml) lists this file in +`doc-excludes`. The instruction points at a missing source file, but the command +still succeeds because excluded files are skipped before instruction parsing. + ```java ``` diff --git a/examples/showcase/configuration/docs/include-exclude/included.md b/examples/showcase/configuration/docs/include-exclude/included.md index 7faa5e71..a7dd199a 100644 --- a/examples/showcase/configuration/docs/include-exclude/included.md +++ b/examples/showcase/configuration/docs/include-exclude/included.md @@ -3,6 +3,13 @@ The `doc-includes` pattern selects this Markdown file, so the instruction is processed normally. +## How It Works + +[../../include-exclude.yml](../../include-exclude.yml) includes Markdown files +under this docs root. Because this file is not listed in `doc-excludes`, check +mode resolves the instruction and compares the rendered fence with the Java +source fragment. + ```java public static void main(String[] args) { diff --git a/examples/showcase/configuration/docs/multiple/java/greeting.md b/examples/showcase/configuration/docs/multiple/java/greeting.md index 17616836..727e27d1 100644 --- a/examples/showcase/configuration/docs/multiple/java/greeting.md +++ b/examples/showcase/configuration/docs/multiple/java/greeting.md @@ -2,6 +2,13 @@ This document is processed by the `java-guide` entry in an `embeddings` config. +## How It Works + +[../../../multiple-embeddings.yml](../../../multiple-embeddings.yml) contains a +`java-guide` entry with its own `code-path` and `docs-path`. This document lives +under that entry's docs root, so the unprefixed source path resolves against the +Java source tree. + ```java public static void main(String[] args) { diff --git a/examples/showcase/configuration/docs/multiple/kotlin/greeting.md b/examples/showcase/configuration/docs/multiple/kotlin/greeting.md index 81c89d47..6a78a25e 100644 --- a/examples/showcase/configuration/docs/multiple/kotlin/greeting.md +++ b/examples/showcase/configuration/docs/multiple/kotlin/greeting.md @@ -3,6 +3,13 @@ This document is processed by the `kotlin-guide` entry in the same `embeddings` config. +## How It Works + +[../../../multiple-embeddings.yml](../../../multiple-embeddings.yml) contains a +separate `kotlin-guide` entry. That entry defines a named `kotlin` source root, +so this instruction uses `$kotlin` even though it is processed by the same +top-level command as the Java entry. + ```kotlin @JvmStatic diff --git a/examples/showcase/configuration/docs/named-sources/java-greeting.md b/examples/showcase/configuration/docs/named-sources/java-greeting.md index 6107d347..655e3925 100644 --- a/examples/showcase/configuration/docs/named-sources/java-greeting.md +++ b/examples/showcase/configuration/docs/named-sources/java-greeting.md @@ -3,6 +3,12 @@ This config has multiple named source roots. The `$java` prefix chooses the Java root before resolving the relative path. +## How It Works + +[../../named-sources.yml](../../named-sources.yml) defines a source root named +`java`. The instruction must include `$java` so the resolver knows which source +tree owns `org/showcase/Greeting.java`. + ```java public static void main(String[] args) { diff --git a/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md b/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md index f3b6160c..9cb7f174 100644 --- a/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md +++ b/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md @@ -3,6 +3,12 @@ The same config can embed from a different root by changing the source-root prefix in the instruction. +## How It Works + +[../../named-sources.yml](../../named-sources.yml) also defines a source root +named `kotlin`. Changing the prefix to `$kotlin` resolves the same relative +package path under the Kotlin source tree. + ```kotlin @JvmStatic diff --git a/examples/showcase/configuration/docs/named-sources/text-line.md b/examples/showcase/configuration/docs/named-sources/text-line.md index f2e02fb8..38d679f7 100644 --- a/examples/showcase/configuration/docs/named-sources/text-line.md +++ b/examples/showcase/configuration/docs/named-sources/text-line.md @@ -3,6 +3,13 @@ Named roots do not need to be language-specific. This example embeds one line from the text source root. +## How It Works + +[../../named-sources.yml](../../named-sources.yml) defines a source root named +`text`. The `line` pattern matches one plain-text line from +`glob-patterns.txt`, showing that source roots can point at any supported text +fixture, not only programming language files. + ```text The total is $5 diff --git a/examples/showcase/configuration/docs/root-source/go-mod.md b/examples/showcase/configuration/docs/root-source/go-mod.md index cbc414e0..40262959 100644 --- a/examples/showcase/configuration/docs/root-source/go-mod.md +++ b/examples/showcase/configuration/docs/root-source/go-mod.md @@ -1,49 +1,17 @@ # Repository Root Source This configuration example uses the repository root as a named source root. The -instruction embeds `go.mod` through the `$repo` prefix. +instruction embeds the module declaration from `go.mod` through the `$repo` +prefix. - -```go -// Copyright 2026, TeamDev. All rights reserved. -// -// Redistribution and use in source and/or binary forms, with or without -// modification, must retain the above copyright notice and the following -// disclaimer. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -module embed-code/embed-code-go +## How It Works -go 1.22.1 +[../../root-source.yml](../../root-source.yml) maps the repository root to the +name `repo`. The instruction uses `$repo/go.mod` and a `line` pattern so this +configuration test proves root-source lookup without embedding the whole +project metadata file. -require ( - github.com/bmatcuk/doublestar/v4 v4.6.1 - github.com/gobwas/glob v0.2.3 - github.com/onsi/ginkgo/v2 v2.20.2 - github.com/onsi/gomega v1.34.2 - gopkg.in/yaml.v3 v3.0.1 -) - -require ( - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect - github.com/stretchr/testify v1.9.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect - golang.org/x/tools v0.24.0 // indirect -) + +```go +module embed-code/embed-code-go ``` diff --git a/examples/showcase/configuration/docs/root-source/version.md b/examples/showcase/configuration/docs/root-source/version.md index 4e822b86..08cfef44 100644 --- a/examples/showcase/configuration/docs/root-source/version.md +++ b/examples/showcase/configuration/docs/root-source/version.md @@ -2,6 +2,13 @@ The same root-source configuration can select a single line from a root file. +## How It Works + +The `$repo` prefix points at the repository root configured in +[../../root-source.yml](../../root-source.yml). The anchored pattern selects the +`Version` constant from `main.go`, which keeps the example small while proving +that root files can be used as sources. + ```go const Version = "1.2.2" diff --git a/examples/showcase/configuration/docs/single-source/greeting.md b/examples/showcase/configuration/docs/single-source/greeting.md index 91bb961c..64cc31c3 100644 --- a/examples/showcase/configuration/docs/single-source/greeting.md +++ b/examples/showcase/configuration/docs/single-source/greeting.md @@ -3,6 +3,12 @@ This config uses one unnamed `code-path`, so instructions refer to source files relative to that root without a `$name` prefix. +## How It Works + +[../../single-source.yml](../../single-source.yml) points `code-path` directly +at `examples/showcase/code/java`. The instruction therefore uses +`org/showcase/Greeting.java` instead of `$java/org/showcase/Greeting.java`. + ```java public static void main(String[] args) { diff --git a/examples/showcase/docs/01-whole-file-source.md b/examples/showcase/docs/01-whole-file-source.md index ed7016ed..1f27a4e9 100644 --- a/examples/showcase/docs/01-whole-file-source.md +++ b/examples/showcase/docs/01-whole-file-source.md @@ -1,6 +1,15 @@ # Whole File From A Showcase Source -Omitting `fragment`, `start`, `end`, and `line` embeds the whole source file. +This is the smallest useful instruction: it names a source file and leaves the +selection attributes empty. + +## How It Works + +The `$java` prefix selects the Java source root from +[../embed-code.yml](../embed-code.yml). Because `fragment`, `start`, `end`, and +`line` are omitted, embed mode copies every line from +[../code/java/org/showcase/Greeting.java](../code/java/org/showcase/Greeting.java) +into the following code fence. ```java diff --git a/examples/showcase/docs/02-source-line-pattern.md b/examples/showcase/docs/02-source-line-pattern.md index 93c342aa..d8559b4b 100644 --- a/examples/showcase/docs/02-source-line-pattern.md +++ b/examples/showcase/docs/02-source-line-pattern.md @@ -1,8 +1,17 @@ # One Line From A Showcase Source -The `line` attribute embeds only the first source line matching the pattern. +Use `line` when the documentation needs one source line instead of a whole +fragment. - +## How It Works + +The pattern is matched against +[../code/java/org/showcase/Greeting.java](../code/java/org/showcase/Greeting.java). +The opening quote is escaped because instruction attributes are parsed as XML, +and the trailing `*` lets the pattern match the rest of the return expression. +Only the first matching source line is rendered into the fence. + + ```java return "Hello, " + name + "!"; ``` diff --git a/examples/showcase/docs/03-named-fragment.md b/examples/showcase/docs/03-named-fragment.md index 2e6e641e..9d9d667d 100644 --- a/examples/showcase/docs/03-named-fragment.md +++ b/examples/showcase/docs/03-named-fragment.md @@ -1,7 +1,14 @@ # Named Fragment -The `fragment` attribute embeds lines between matching `#docfragment` and -`#enddocfragment` markers. Marker lines are not rendered. +Use `fragment` when the source file already marks a reusable documentation +region. + +## How It Works + +[../code/java/org/showcase/Greeting.java](../code/java/org/showcase/Greeting.java) +wraps the `main()` method with matching `#docfragment` and `#enddocfragment` +comments. The instruction resolves the named region, removes the marker lines, +normalizes indentation, and replaces the following fence with the method body. ```java diff --git a/examples/showcase/docs/04-paired-instruction-tag.md b/examples/showcase/docs/04-paired-instruction-tag.md index bf4dcb61..5c202059 100644 --- a/examples/showcase/docs/04-paired-instruction-tag.md +++ b/examples/showcase/docs/04-paired-instruction-tag.md @@ -4,10 +4,12 @@ Instructions may be self-closing or paired. The self-closing form is supported, but this showcase uses paired tags because some Markdown renderers display the XML-style self-closing tag awkwardly. -## Short Paired Tag +## How It Works -This compact paired form is useful when all attributes fit naturally in one -line. +The active instruction below has an opening `` tag and a matching +closing tag. No content is required between them; the rendered snippet still +comes from the source file and the following code fence. The whole showcase uses +this paired form so Markdown previews display the instructions consistently. ```java @@ -15,22 +17,3 @@ public static void main(String[] args) { System.out.println(greeting("Ada")); } ``` - -## Paired Tag With A Larger Fragment - -The same paired form works for larger snippets too. - - -```java -public final class Greeting { - private Greeting() {} - - public static void main(String[] args) { - System.out.println(greeting("Ada")); - } - - public static String greeting(String name) { - return "Hello, " + name + "!"; - } -} -``` diff --git a/examples/showcase/docs/05-named-source-root.md b/examples/showcase/docs/05-named-source-root.md index f138e1c3..f703b725 100644 --- a/examples/showcase/docs/05-named-source-root.md +++ b/examples/showcase/docs/05-named-source-root.md @@ -1,8 +1,13 @@ # Named Source Roots -When a configuration has named code paths, the instruction chooses one with the -`$name/relative/path` prefix. This example reads Kotlin code from the `kotlin` -source root while the Java examples read from `java`. +Named roots let one documentation set embed source from several directories. + +## How It Works + +[../embed-code.yml](../embed-code.yml) defines `java`, `kotlin`, and `text` +source roots. The `$kotlin` prefix chooses the Kotlin root before resolving +`org/showcase/KotlinGreeting.kt`. The same docs root can therefore mix Java, +Kotlin, and text snippets without changing the command line. ```kotlin diff --git a/examples/showcase/docs/06-start-end-pattern.md b/examples/showcase/docs/06-start-end-pattern.md index 416d9c20..6e5db41b 100644 --- a/examples/showcase/docs/06-start-end-pattern.md +++ b/examples/showcase/docs/06-start-end-pattern.md @@ -1,7 +1,14 @@ # Start And End Patterns -The `start` and `end` attributes select an inclusive source range. Patterns are -glob-like and the end search starts after the matched start. +Use `start` and `end` when the source does not contain named fragment markers. + +## How It Works + +The `start` pattern finds the first line in +[../code/java/org/showcase/PatternSamples.java](../code/java/org/showcase/PatternSamples.java) +that contains `@Scenario`. The `end` pattern then searches after that start +match and stops at the first line that is exactly four spaces followed by `}`. +Both boundary lines are included in the rendered snippet. ```text diff --git a/examples/showcase/docs/09-escaped-newline-text.md b/examples/showcase/docs/09-escaped-newline-text.md index 61d7dda5..ca4214cf 100644 --- a/examples/showcase/docs/09-escaped-newline-text.md +++ b/examples/showcase/docs/09-escaped-newline-text.md @@ -1,7 +1,13 @@ # Escaped Newline Text -Use `\\n` when the source line contains the characters backslash and `n`. -Quote characters may be escaped as `\"` inside instruction attributes. +Pattern escaping distinguishes a real multi-line pattern from source text that +contains the characters backslash and `n`. + +## How It Works + +The pattern uses `\\n` because the source line contains a string literal with +backslash-n text. The quote characters are written as `\"` so the instruction +remains valid XML. The result is one source line, not a two-line match. ```java diff --git a/examples/showcase/docs/11-multi-part-fragment-separator.md b/examples/showcase/docs/11-multi-part-fragment-separator.md index 9ff4c429..4ccb5864 100644 --- a/examples/showcase/docs/11-multi-part-fragment-separator.md +++ b/examples/showcase/docs/11-multi-part-fragment-separator.md @@ -1,7 +1,14 @@ # Multi-Part Fragment Separator -Fragments with the same name may appear in several source regions. The rendered -parts are joined in source order with the configured separator. +One named fragment can be split across several source regions. + +## How It Works + +[../code/java/org/showcase/MultiPartWorkflow.java](../code/java/org/showcase/MultiPartWorkflow.java) +opens and closes the `Workflow` fragment multiple times. Embed mode collects +each part in source order and joins the parts with the `separator` configured in +[../embed-code.yml](../embed-code.yml). This is useful when a guide needs the +shape of a class but wants to hide internal lines between selected regions. ```java diff --git a/examples/showcase/docs/12-overlapping-fragments.md b/examples/showcase/docs/12-overlapping-fragments.md index 51362fe3..c6c2a6ce 100644 --- a/examples/showcase/docs/12-overlapping-fragments.md +++ b/examples/showcase/docs/12-overlapping-fragments.md @@ -1,7 +1,14 @@ # Overlapping Fragments -Several fragments can open or close on the same marker line. This example uses -an overlapping fragment that shares the class wrapper with another fragment. +Several fragments can open or close on the same marker line. + +## How It Works + +[../code/java/org/showcase/OverlappingFragments.java](../code/java/org/showcase/OverlappingFragments.java) +uses marker lines that name both `Class wrapper` and `Greeting method`. The +instruction asks only for `Greeting method`, so embed mode keeps the shared +class wrapper, skips unrelated method details, and renders the selected method +inside the wrapper. ```java diff --git a/examples/showcase/docs/13-markdown-fence-shielding.md b/examples/showcase/docs/13-markdown-fence-shielding.md index 4f6d2c4d..a3afb2b7 100644 --- a/examples/showcase/docs/13-markdown-fence-shielding.md +++ b/examples/showcase/docs/13-markdown-fence-shielding.md @@ -1,8 +1,13 @@ # Instructions Inside Markdown Fences -Instruction-looking text inside an ordinary Markdown code fence is preserved as -documentation content. It is not executed because the parser tracks Markdown -fence state before looking for instructions. +Documentation sometimes needs to show an instruction as plain text. + +## How It Works + +The instruction-looking text below is inside an ordinary Markdown fence, so it +is preserved as documentation content. The parser tracks code-fence state before +looking for active instructions, which prevents examples from accidentally +running while they are being explained. ````markdown diff --git a/examples/showcase/docs/html-showcase.html b/examples/showcase/docs/html-showcase.html index b3a62d0a..a1080cb3 100644 --- a/examples/showcase/docs/html-showcase.html +++ b/examples/showcase/docs/html-showcase.html @@ -3,8 +3,13 @@

HTML Embedding Showcase

- HTML files are scanned too when the include patterns allow them. The - instruction still needs a Markdown code fence after it. + HTML files are scanned when the include patterns allow them. The instruction + below uses the same paired tag form as the Markdown examples and still needs + a Markdown code fence immediately after it. +

+

+ The source root comes from embed-code.yml, and + the rendered snippet is checked the same way as a Markdown document.

diff --git a/examples/showcase/docs/ignored-by-exclude.md b/examples/showcase/docs/ignored-by-exclude.md index f1c49501..8797906e 100644 --- a/examples/showcase/docs/ignored-by-exclude.md +++ b/examples/showcase/docs/ignored-by-exclude.md @@ -1,8 +1,14 @@ # Excluded Showcase File -The positive configuration excludes this file. Its intentionally broken -instruction verifies that `doc-excludes` prevents selected files from being -processed. +This file is intentionally present in the positive docs root but absent from +the positive processing flow. + +## How It Works + +[../embed-code.yml](../embed-code.yml) lists this file in `doc-excludes`, so +embed mode and check mode skip it even though it matches the include patterns. +The missing source path proves the exclude is active: processing this file would +fail immediately. ```go diff --git a/examples/showcase/negative/docs/invalid-attributes.md b/examples/showcase/negative/docs/invalid-attributes.md index d8feab3f..eb7b17d1 100644 --- a/examples/showcase/negative/docs/invalid-attributes.md +++ b/examples/showcase/negative/docs/invalid-attributes.md @@ -1,7 +1,13 @@ # Invalid Attributes -This scenario fails because `fragment` cannot be combined with `line`, `start`, -or `end`. +This scenario shows a structurally invalid instruction. + +## How It Fails + +`fragment` selects a named source region, while `line`, `start`, and `end` +select by pattern. The instruction combines `fragment` and `line`, so the tool +rejects it before reading the source file. In a real guide, choose one selection +style for each instruction. ```java diff --git a/examples/showcase/negative/docs/missing-code-fence.md b/examples/showcase/negative/docs/missing-code-fence.md index 32c060ad..1e51cdec 100644 --- a/examples/showcase/negative/docs/missing-code-fence.md +++ b/examples/showcase/negative/docs/missing-code-fence.md @@ -1,7 +1,14 @@ # Missing Code Fence -This scenario fails because every active instruction must be followed by a -Markdown code fence. +This scenario shows what happens when an active instruction has no owned code +fence. + +## How It Fails + +Every instruction must be followed immediately by a Markdown code fence. The +plain text line below is not a fence, so the parser reports the missing fence at +the instruction. In a real guide, add an opening and closing fence after the +instruction, even if the fence starts empty. This line is not a code fence. diff --git a/examples/showcase/negative/docs/missing-fragment.md b/examples/showcase/negative/docs/missing-fragment.md index 7a89643e..31bbd6a7 100644 --- a/examples/showcase/negative/docs/missing-fragment.md +++ b/examples/showcase/negative/docs/missing-fragment.md @@ -1,8 +1,15 @@ # Missing Fragment -This scenario fails because the source file exists, but the requested named +This scenario shows what happens when the source file exists but the named fragment does not. +## How It Fails + +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +contains fragments such as `main()`, but it does not contain `does not exist`. +Check mode reports the missing fragment. In a real guide, use an existing +fragment name or add matching source markers. + ```java ``` diff --git a/examples/showcase/negative/docs/missing-pattern.md b/examples/showcase/negative/docs/missing-pattern.md index 32360d6a..d63b94c1 100644 --- a/examples/showcase/negative/docs/missing-pattern.md +++ b/examples/showcase/negative/docs/missing-pattern.md @@ -1,7 +1,14 @@ # Missing Pattern -This scenario fails because the source file exists, but no line matches the -requested line pattern. +This scenario shows what happens when a line pattern matches nothing. + +## How It Fails + +The source file is found, but no line in +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +matches `doesNotExistPattern`. Check mode reports the unmatched pattern. In a +real guide, loosen the glob pattern, add anchors only where needed, or point the +instruction at the intended source file. ```java diff --git a/examples/showcase/negative/docs/missing-source.md b/examples/showcase/negative/docs/missing-source.md index 5aeabd7b..26fc0710 100644 --- a/examples/showcase/negative/docs/missing-source.md +++ b/examples/showcase/negative/docs/missing-source.md @@ -1,7 +1,13 @@ # Missing Source -This scenario fails because the instruction points to a file that cannot be -resolved from the configured source roots. +This scenario shows what happens when the `file` attribute cannot be resolved. + +## How It Fails + +The `$java` root exists, but `org/showcase/DoesNotExist.java` is not present +under [../../code/java](../../code/java/). Check mode reports the missing source +file and leaves the document unchanged. In a real guide, fix the path or add the +missing source file. ```java diff --git a/examples/showcase/negative/docs/stale-snippet.md b/examples/showcase/negative/docs/stale-snippet.md index b74d5c29..5b6b4241 100644 --- a/examples/showcase/negative/docs/stale-snippet.md +++ b/examples/showcase/negative/docs/stale-snippet.md @@ -1,7 +1,14 @@ # Stale Snippet -This scenario is syntactically valid, but check mode reports it as stale because -the rendered code fence does not match the current source fragment. +This scenario is syntactically valid, but the rendered code is out of date. + +## How It Fails + +Check mode resolves the `main()` fragment from +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +and compares it with the existing fence. The fence contains different text, so +check mode reports the document as stale without rewriting it. Embed mode would +replace the fence with the current source fragment. ```java diff --git a/examples/showcase/negative/docs/unclosed-code-fence.md b/examples/showcase/negative/docs/unclosed-code-fence.md index a89f5cd5..35315ded 100644 --- a/examples/showcase/negative/docs/unclosed-code-fence.md +++ b/examples/showcase/negative/docs/unclosed-code-fence.md @@ -1,7 +1,13 @@ # Unclosed Code Fence -This scenario fails because the opening fence after the instruction is never -closed. +This scenario shows what happens when the instruction has an opening fence but +no closing fence. + +## How It Fails + +The parser finds the opening fence after the instruction and then reaches the +end of the file before seeing a matching closing fence. In a real guide, close +the fence with the same marker style and at least the same marker length. ```java From f7150420859315cc6458bb6bf9fa2c55f15709cf Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 17:25:13 +0200 Subject: [PATCH 03/22] Rearrange files. --- examples/showcase/README.md | 119 +----------------- .../showcase/{ => embedding}/embed-code.yml | 0 .../negative/docs/invalid-attributes.md | 0 .../negative/docs/missing-code-fence.md | 0 .../negative/docs/missing-fragment.md | 0 .../negative/docs/missing-pattern.md | 0 .../negative/docs/missing-source.md | 0 .../negative/docs/stale-snippet.md | 0 .../negative/docs/unclosed-code-fence.md | 0 .../negative/processing-errors.yml | 0 .../{ => embedding}/negative/stale.yml | 0 .../positive}/01-whole-file-source.md | 0 .../positive}/02-source-line-pattern.md | 0 .../positive}/03-named-fragment.md | 0 .../positive}/04-paired-instruction-tag.md | 0 .../positive}/05-named-source-root.md | 0 .../positive}/06-start-end-pattern.md | 0 .../positive}/07-multi-line-pattern.md | 0 .../positive}/08-escaped-glob-character.md | 0 .../positive}/09-escaped-newline-text.md | 0 .../positive}/10-comment-filtering.md | 0 .../11-multi-part-fragment-separator.md | 0 .../positive}/12-overlapping-fragments.md | 0 .../positive}/13-markdown-fence-shielding.md | 0 .../positive}/html-showcase.html | 0 .../positive}/ignored-by-exclude.md | 0 26 files changed, 6 insertions(+), 113 deletions(-) rename examples/showcase/{ => embedding}/embed-code.yml (100%) rename examples/showcase/{ => embedding}/negative/docs/invalid-attributes.md (100%) rename examples/showcase/{ => embedding}/negative/docs/missing-code-fence.md (100%) rename examples/showcase/{ => embedding}/negative/docs/missing-fragment.md (100%) rename examples/showcase/{ => embedding}/negative/docs/missing-pattern.md (100%) rename examples/showcase/{ => embedding}/negative/docs/missing-source.md (100%) rename examples/showcase/{ => embedding}/negative/docs/stale-snippet.md (100%) rename examples/showcase/{ => embedding}/negative/docs/unclosed-code-fence.md (100%) rename examples/showcase/{ => embedding}/negative/processing-errors.yml (100%) rename examples/showcase/{ => embedding}/negative/stale.yml (100%) rename examples/showcase/{docs => embedding/positive}/01-whole-file-source.md (100%) rename examples/showcase/{docs => embedding/positive}/02-source-line-pattern.md (100%) rename examples/showcase/{docs => embedding/positive}/03-named-fragment.md (100%) rename examples/showcase/{docs => embedding/positive}/04-paired-instruction-tag.md (100%) rename examples/showcase/{docs => embedding/positive}/05-named-source-root.md (100%) rename examples/showcase/{docs => embedding/positive}/06-start-end-pattern.md (100%) rename examples/showcase/{docs => embedding/positive}/07-multi-line-pattern.md (100%) rename examples/showcase/{docs => embedding/positive}/08-escaped-glob-character.md (100%) rename examples/showcase/{docs => embedding/positive}/09-escaped-newline-text.md (100%) rename examples/showcase/{docs => embedding/positive}/10-comment-filtering.md (100%) rename examples/showcase/{docs => embedding/positive}/11-multi-part-fragment-separator.md (100%) rename examples/showcase/{docs => embedding/positive}/12-overlapping-fragments.md (100%) rename examples/showcase/{docs => embedding/positive}/13-markdown-fence-shielding.md (100%) rename examples/showcase/{docs => embedding/positive}/html-showcase.html (100%) rename examples/showcase/{docs => embedding/positive}/ignored-by-exclude.md (100%) diff --git a/examples/showcase/README.md b/examples/showcase/README.md index 7a1f5af4..b8574dfb 100644 --- a/examples/showcase/README.md +++ b/examples/showcase/README.md @@ -1,120 +1,13 @@ # Embed Code Showcase -This folder is an opt-in, executable guide to `embed-code-go`. It is not part of -the normal `go test ./...` flow. The Go test is guarded by the `showcase` build -tag, so run it only when you want to verify the examples end to end. - -Run commands from the repository root. - -The showcase-owned source examples live under [code](code/). Repository-root -source examples are kept in the configuration showcase only. +This is an executable showcase guide to `embed-code-go` and the end-to-end tests. ## How To Use This Guide -Read the files in [docs](docs/) from `01` onward when learning the embedding -syntax for the first time. Each file owns one positive case, explains how the -instruction is resolved, and shows the rendered code fence that embed mode owns. - -Then inspect [negative/docs](negative/docs/) to see the failures that check mode -reports without rewriting files. Finally, read [configuration](configuration/) -to compare the YAML shapes that point documentation roots at source roots. - -## Positive Flow - -Refresh the generated snippets: - -```bash -go run ./main.go -mode embed -config-path examples/showcase/embed-code.yml -``` - -Verify that the snippets are up-to-date: - -```bash -go run ./main.go -mode check -config-path examples/showcase/embed-code.yml -``` - -Run the opt-in test: - -```bash -go test -tags showcase ./examples/showcase -``` - -The test copies the showcase docs to a temporary directory, runs check mode, -intentionally makes one copied snippet stale, repairs it with embed mode, and -then verifies the negative and configuration cases. The build tag keeps this -larger documentation test out of the default test flow. - -The positive showcase covers: - -| Case | File | What it verifies | -|--------------------------|------------------------------------------------------------------------------|-----------------------------------------------------------------------| -| Whole file | [docs/01-whole-file-source.md](docs/01-whole-file-source.md) | Omitting selection attributes embeds the whole showcase source file. | -| Source line pattern | [docs/02-source-line-pattern.md](docs/02-source-line-pattern.md) | A showcase source file can be matched with a `line` pattern. | -| Named fragment | [docs/03-named-fragment.md](docs/03-named-fragment.md) | `fragment` uses `#docfragment` markers and omits marker lines. | -| Paired instruction tags | [docs/04-paired-instruction-tag.md](docs/04-paired-instruction-tag.md) | Paired tags are preferred; self-closing tags are still supported. | -| Named source roots | [docs/05-named-source-root.md](docs/05-named-source-root.md) | `$java/...` and `$kotlin/...` select different configured roots. | -| Start and end patterns | [docs/06-start-end-pattern.md](docs/06-start-end-pattern.md) | `start` and `end` select an inclusive source range. | -| Multi-line patterns | [docs/07-multi-line-pattern.md](docs/07-multi-line-pattern.md) | `\n` matches consecutive source lines. | -| Escaped glob characters | [docs/08-escaped-glob-character.md](docs/08-escaped-glob-character.md) | `\*` matches a literal asterisk. | -| Escaped newline text | [docs/09-escaped-newline-text.md](docs/09-escaped-newline-text.md) | `\\n` matches a literal backslash-n sequence. | -| Comment filtering | [docs/10-comment-filtering.md](docs/10-comment-filtering.md) | `comments="documentation"` keeps Java documentation comments. | -| Multi-part fragments | [docs/11-multi-part-fragment-separator.md](docs/11-multi-part-fragment-separator.md) | Repeated fragment markers are joined with the configured separator. | -| Overlapping fragments | [docs/12-overlapping-fragments.md](docs/12-overlapping-fragments.md) | Multiple fragment names may share marker lines. | -| Markdown fence shielding | [docs/13-markdown-fence-shielding.md](docs/13-markdown-fence-shielding.md) | Instruction-looking text inside a regular fence is ignored. | -| HTML documents | [docs/html-showcase.html](docs/html-showcase.html) | HTML files can be scanned when included by configuration. | -| Excludes | [docs/ignored-by-exclude.md](docs/ignored-by-exclude.md) | Excluded files are not processed even when they contain instructions. | - -## Negative Flow - -The negative examples are intentionally broken. They are still documentation: -each file describes the mistake, the expected failure reason, and what a user -should fix in a real document. These commands should fail. - -```bash -go run ./main.go -mode check -config-path examples/showcase/negative/processing-errors.yml -go run ./main.go -mode check -config-path examples/showcase/negative/stale.yml -``` - -The processing-error config verifies: - -| Case | File | Expected behavior | -|---------------------|------------------------------------------------------------------------------|------------------------------------------------------| -| Missing source | [negative/docs/missing-source.md](negative/docs/missing-source.md) | Reports that the code file cannot be found. | -| Missing fragment | [negative/docs/missing-fragment.md](negative/docs/missing-fragment.md) | Reports that the requested fragment cannot be found. | -| Missing pattern | [negative/docs/missing-pattern.md](negative/docs/missing-pattern.md) | Reports that no source line matches the pattern. | -| Invalid attributes | [negative/docs/invalid-attributes.md](negative/docs/invalid-attributes.md) | Rejects mutually exclusive selection attributes. | -| Missing code fence | [negative/docs/missing-code-fence.md](negative/docs/missing-code-fence.md) | Requires a fence immediately after an instruction. | -| Unclosed code fence | [negative/docs/unclosed-code-fence.md](negative/docs/unclosed-code-fence.md) | Reports an instruction fence that reaches EOF. | - -The stale config verifies: - -| Case | File | Expected behavior | -|---------------|--------------------------------------------------------------------|------------------------------------------------------------------------| -| Stale snippet | [negative/docs/stale-snippet.md](negative/docs/stale-snippet.md) | Check mode reports the file as needing an update without rewriting it. | - -## Configuration Flow - -Configuration examples live under [configuration](configuration/). They cover a -repository-root source, a single source root, named source roots, -include/exclude patterns, and the `embeddings` list for multiple documentation -roots. - -Each configuration has its own docs root so the examples can be run separately. -Open the YAML file first, then follow the linked docs root to see how the -instructions use that configuration. +Guide is divided on two categories: -| Case | Config | Docs root | -|----------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------| -| Repository root | [configuration/root-source.yml](configuration/root-source.yml) | [configuration/docs/root-source](configuration/docs/root-source/) | -| Single source root | [configuration/single-source.yml](configuration/single-source.yml) | [configuration/docs/single-source](configuration/docs/single-source/) | -| Named source roots | [configuration/named-sources.yml](configuration/named-sources.yml) | [configuration/docs/named-sources](configuration/docs/named-sources/) | -| Include and exclude | [configuration/include-exclude.yml](configuration/include-exclude.yml) | [configuration/docs/include-exclude](configuration/docs/include-exclude/) | -| Multiple embeddings | [configuration/multiple-embeddings.yml](configuration/multiple-embeddings.yml) | [configuration/docs/multiple](configuration/docs/multiple/) | +1. [Configuration](configuration/README.md) - describes how to configure the whole embed-code application. +2. [Embedding](embedding/) - describes how to work with the embedding instructions. -```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml -``` +## How To Run Tests +... diff --git a/examples/showcase/embed-code.yml b/examples/showcase/embedding/embed-code.yml similarity index 100% rename from examples/showcase/embed-code.yml rename to examples/showcase/embedding/embed-code.yml diff --git a/examples/showcase/negative/docs/invalid-attributes.md b/examples/showcase/embedding/negative/docs/invalid-attributes.md similarity index 100% rename from examples/showcase/negative/docs/invalid-attributes.md rename to examples/showcase/embedding/negative/docs/invalid-attributes.md diff --git a/examples/showcase/negative/docs/missing-code-fence.md b/examples/showcase/embedding/negative/docs/missing-code-fence.md similarity index 100% rename from examples/showcase/negative/docs/missing-code-fence.md rename to examples/showcase/embedding/negative/docs/missing-code-fence.md diff --git a/examples/showcase/negative/docs/missing-fragment.md b/examples/showcase/embedding/negative/docs/missing-fragment.md similarity index 100% rename from examples/showcase/negative/docs/missing-fragment.md rename to examples/showcase/embedding/negative/docs/missing-fragment.md diff --git a/examples/showcase/negative/docs/missing-pattern.md b/examples/showcase/embedding/negative/docs/missing-pattern.md similarity index 100% rename from examples/showcase/negative/docs/missing-pattern.md rename to examples/showcase/embedding/negative/docs/missing-pattern.md diff --git a/examples/showcase/negative/docs/missing-source.md b/examples/showcase/embedding/negative/docs/missing-source.md similarity index 100% rename from examples/showcase/negative/docs/missing-source.md rename to examples/showcase/embedding/negative/docs/missing-source.md diff --git a/examples/showcase/negative/docs/stale-snippet.md b/examples/showcase/embedding/negative/docs/stale-snippet.md similarity index 100% rename from examples/showcase/negative/docs/stale-snippet.md rename to examples/showcase/embedding/negative/docs/stale-snippet.md diff --git a/examples/showcase/negative/docs/unclosed-code-fence.md b/examples/showcase/embedding/negative/docs/unclosed-code-fence.md similarity index 100% rename from examples/showcase/negative/docs/unclosed-code-fence.md rename to examples/showcase/embedding/negative/docs/unclosed-code-fence.md diff --git a/examples/showcase/negative/processing-errors.yml b/examples/showcase/embedding/negative/processing-errors.yml similarity index 100% rename from examples/showcase/negative/processing-errors.yml rename to examples/showcase/embedding/negative/processing-errors.yml diff --git a/examples/showcase/negative/stale.yml b/examples/showcase/embedding/negative/stale.yml similarity index 100% rename from examples/showcase/negative/stale.yml rename to examples/showcase/embedding/negative/stale.yml diff --git a/examples/showcase/docs/01-whole-file-source.md b/examples/showcase/embedding/positive/01-whole-file-source.md similarity index 100% rename from examples/showcase/docs/01-whole-file-source.md rename to examples/showcase/embedding/positive/01-whole-file-source.md diff --git a/examples/showcase/docs/02-source-line-pattern.md b/examples/showcase/embedding/positive/02-source-line-pattern.md similarity index 100% rename from examples/showcase/docs/02-source-line-pattern.md rename to examples/showcase/embedding/positive/02-source-line-pattern.md diff --git a/examples/showcase/docs/03-named-fragment.md b/examples/showcase/embedding/positive/03-named-fragment.md similarity index 100% rename from examples/showcase/docs/03-named-fragment.md rename to examples/showcase/embedding/positive/03-named-fragment.md diff --git a/examples/showcase/docs/04-paired-instruction-tag.md b/examples/showcase/embedding/positive/04-paired-instruction-tag.md similarity index 100% rename from examples/showcase/docs/04-paired-instruction-tag.md rename to examples/showcase/embedding/positive/04-paired-instruction-tag.md diff --git a/examples/showcase/docs/05-named-source-root.md b/examples/showcase/embedding/positive/05-named-source-root.md similarity index 100% rename from examples/showcase/docs/05-named-source-root.md rename to examples/showcase/embedding/positive/05-named-source-root.md diff --git a/examples/showcase/docs/06-start-end-pattern.md b/examples/showcase/embedding/positive/06-start-end-pattern.md similarity index 100% rename from examples/showcase/docs/06-start-end-pattern.md rename to examples/showcase/embedding/positive/06-start-end-pattern.md diff --git a/examples/showcase/docs/07-multi-line-pattern.md b/examples/showcase/embedding/positive/07-multi-line-pattern.md similarity index 100% rename from examples/showcase/docs/07-multi-line-pattern.md rename to examples/showcase/embedding/positive/07-multi-line-pattern.md diff --git a/examples/showcase/docs/08-escaped-glob-character.md b/examples/showcase/embedding/positive/08-escaped-glob-character.md similarity index 100% rename from examples/showcase/docs/08-escaped-glob-character.md rename to examples/showcase/embedding/positive/08-escaped-glob-character.md diff --git a/examples/showcase/docs/09-escaped-newline-text.md b/examples/showcase/embedding/positive/09-escaped-newline-text.md similarity index 100% rename from examples/showcase/docs/09-escaped-newline-text.md rename to examples/showcase/embedding/positive/09-escaped-newline-text.md diff --git a/examples/showcase/docs/10-comment-filtering.md b/examples/showcase/embedding/positive/10-comment-filtering.md similarity index 100% rename from examples/showcase/docs/10-comment-filtering.md rename to examples/showcase/embedding/positive/10-comment-filtering.md diff --git a/examples/showcase/docs/11-multi-part-fragment-separator.md b/examples/showcase/embedding/positive/11-multi-part-fragment-separator.md similarity index 100% rename from examples/showcase/docs/11-multi-part-fragment-separator.md rename to examples/showcase/embedding/positive/11-multi-part-fragment-separator.md diff --git a/examples/showcase/docs/12-overlapping-fragments.md b/examples/showcase/embedding/positive/12-overlapping-fragments.md similarity index 100% rename from examples/showcase/docs/12-overlapping-fragments.md rename to examples/showcase/embedding/positive/12-overlapping-fragments.md diff --git a/examples/showcase/docs/13-markdown-fence-shielding.md b/examples/showcase/embedding/positive/13-markdown-fence-shielding.md similarity index 100% rename from examples/showcase/docs/13-markdown-fence-shielding.md rename to examples/showcase/embedding/positive/13-markdown-fence-shielding.md diff --git a/examples/showcase/docs/html-showcase.html b/examples/showcase/embedding/positive/html-showcase.html similarity index 100% rename from examples/showcase/docs/html-showcase.html rename to examples/showcase/embedding/positive/html-showcase.html diff --git a/examples/showcase/docs/ignored-by-exclude.md b/examples/showcase/embedding/positive/ignored-by-exclude.md similarity index 100% rename from examples/showcase/docs/ignored-by-exclude.md rename to examples/showcase/embedding/positive/ignored-by-exclude.md From ae386027559df997d2d97e18df3324789cdb25d2 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 17:40:42 +0200 Subject: [PATCH 04/22] Fix paths. --- examples/showcase/README.md | 34 +++++++++++++++++-- examples/showcase/embedding/embed-code.yml | 2 +- .../negative/docs/missing-fragment.md | 2 +- .../negative/docs/missing-pattern.md | 2 +- .../embedding/negative/docs/missing-source.md | 2 +- .../embedding/negative/docs/stale-snippet.md | 2 +- .../embedding/negative/processing-errors.yml | 2 +- .../showcase/embedding/negative/stale.yml | 2 +- .../positive/01-whole-file-source.md | 2 +- .../positive/02-source-line-pattern.md | 2 +- .../embedding/positive/03-named-fragment.md | 2 +- .../positive/06-start-end-pattern.md | 2 +- .../positive/08-escaped-glob-character.md | 2 +- .../11-multi-part-fragment-separator.md | 2 +- .../positive/12-overlapping-fragments.md | 2 +- examples/showcase/showcase_test.go | 4 +-- 16 files changed, 48 insertions(+), 18 deletions(-) diff --git a/examples/showcase/README.md b/examples/showcase/README.md index b8574dfb..847e4039 100644 --- a/examples/showcase/README.md +++ b/examples/showcase/README.md @@ -7,7 +7,37 @@ This is an executable showcase guide to `embed-code-go` and the end-to-end tests Guide is divided on two categories: 1. [Configuration](configuration/README.md) - describes how to configure the whole embed-code application. -2. [Embedding](embedding/) - describes how to work with the embedding instructions. +2. [Embedding](embedding/positive/) - describes how to work with the embedding instructions. ## How To Run Tests -... + +Run commands from the repository root. + +Run the opt-in end-to-end test with the `showcase` build tag: + +```bash +go test -tags showcase ./examples/showcase +``` + +Verify the positive embedding examples: + +```bash +go run ./main.go -mode check -config-path examples/showcase/embedding/embed-code.yml +``` + +Verify the configuration examples: + +```bash +go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml +go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml +``` + +The negative examples are intentionally broken, so these commands should fail: + +```bash +go run ./main.go -mode check -config-path examples/showcase/embedding/negative/processing-errors.yml +go run ./main.go -mode check -config-path examples/showcase/embedding/negative/stale.yml +``` diff --git a/examples/showcase/embedding/embed-code.yml b/examples/showcase/embedding/embed-code.yml index b2a3bb02..981ced65 100644 --- a/examples/showcase/embedding/embed-code.yml +++ b/examples/showcase/embedding/embed-code.yml @@ -5,7 +5,7 @@ code-path: path: examples/showcase/code/kotlin - name: text path: examples/showcase/code/text -docs-path: examples/showcase/docs +docs-path: examples/showcase/embedding/positive doc-includes: - "**/*.md" - "**/*.html" diff --git a/examples/showcase/embedding/negative/docs/missing-fragment.md b/examples/showcase/embedding/negative/docs/missing-fragment.md index 31bbd6a7..63ea22ab 100644 --- a/examples/showcase/embedding/negative/docs/missing-fragment.md +++ b/examples/showcase/embedding/negative/docs/missing-fragment.md @@ -5,7 +5,7 @@ fragment does not. ## How It Fails -[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +[../../../code/java/org/showcase/Greeting.java](../../../code/java/org/showcase/Greeting.java) contains fragments such as `main()`, but it does not contain `does not exist`. Check mode reports the missing fragment. In a real guide, use an existing fragment name or add matching source markers. diff --git a/examples/showcase/embedding/negative/docs/missing-pattern.md b/examples/showcase/embedding/negative/docs/missing-pattern.md index d63b94c1..a107ef8b 100644 --- a/examples/showcase/embedding/negative/docs/missing-pattern.md +++ b/examples/showcase/embedding/negative/docs/missing-pattern.md @@ -5,7 +5,7 @@ This scenario shows what happens when a line pattern matches nothing. ## How It Fails The source file is found, but no line in -[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +[../../../code/java/org/showcase/Greeting.java](../../../code/java/org/showcase/Greeting.java) matches `doesNotExistPattern`. Check mode reports the unmatched pattern. In a real guide, loosen the glob pattern, add anchors only where needed, or point the instruction at the intended source file. diff --git a/examples/showcase/embedding/negative/docs/missing-source.md b/examples/showcase/embedding/negative/docs/missing-source.md index 26fc0710..a2ac946a 100644 --- a/examples/showcase/embedding/negative/docs/missing-source.md +++ b/examples/showcase/embedding/negative/docs/missing-source.md @@ -5,7 +5,7 @@ This scenario shows what happens when the `file` attribute cannot be resolved. ## How It Fails The `$java` root exists, but `org/showcase/DoesNotExist.java` is not present -under [../../code/java](../../code/java/). Check mode reports the missing source +under [../../../code/java](../../../code/java/). Check mode reports the missing source file and leaves the document unchanged. In a real guide, fix the path or add the missing source file. diff --git a/examples/showcase/embedding/negative/docs/stale-snippet.md b/examples/showcase/embedding/negative/docs/stale-snippet.md index 5b6b4241..f4b28cc9 100644 --- a/examples/showcase/embedding/negative/docs/stale-snippet.md +++ b/examples/showcase/embedding/negative/docs/stale-snippet.md @@ -5,7 +5,7 @@ This scenario is syntactically valid, but the rendered code is out of date. ## How It Fails Check mode resolves the `main()` fragment from -[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +[../../../code/java/org/showcase/Greeting.java](../../../code/java/org/showcase/Greeting.java) and compares it with the existing fence. The fence contains different text, so check mode reports the document as stale without rewriting it. Embed mode would replace the fence with the current source fragment. diff --git a/examples/showcase/embedding/negative/processing-errors.yml b/examples/showcase/embedding/negative/processing-errors.yml index 90cc680d..e04d3a5e 100644 --- a/examples/showcase/embedding/negative/processing-errors.yml +++ b/examples/showcase/embedding/negative/processing-errors.yml @@ -3,7 +3,7 @@ code-path: path: . - name: java path: examples/showcase/code/java -docs-path: examples/showcase/negative/docs +docs-path: examples/showcase/embedding/negative/docs doc-includes: - missing-source.md - missing-fragment.md diff --git a/examples/showcase/embedding/negative/stale.yml b/examples/showcase/embedding/negative/stale.yml index 0cd2bd65..13477876 100644 --- a/examples/showcase/embedding/negative/stale.yml +++ b/examples/showcase/embedding/negative/stale.yml @@ -1,6 +1,6 @@ code-path: - name: java path: examples/showcase/code/java -docs-path: examples/showcase/negative/docs +docs-path: examples/showcase/embedding/negative/docs doc-includes: - stale-snippet.md diff --git a/examples/showcase/embedding/positive/01-whole-file-source.md b/examples/showcase/embedding/positive/01-whole-file-source.md index 1f27a4e9..6a1f6ba5 100644 --- a/examples/showcase/embedding/positive/01-whole-file-source.md +++ b/examples/showcase/embedding/positive/01-whole-file-source.md @@ -8,7 +8,7 @@ selection attributes empty. The `$java` prefix selects the Java source root from [../embed-code.yml](../embed-code.yml). Because `fragment`, `start`, `end`, and `line` are omitted, embed mode copies every line from -[../code/java/org/showcase/Greeting.java](../code/java/org/showcase/Greeting.java) +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) into the following code fence. diff --git a/examples/showcase/embedding/positive/02-source-line-pattern.md b/examples/showcase/embedding/positive/02-source-line-pattern.md index d8559b4b..53abf674 100644 --- a/examples/showcase/embedding/positive/02-source-line-pattern.md +++ b/examples/showcase/embedding/positive/02-source-line-pattern.md @@ -6,7 +6,7 @@ fragment. ## How It Works The pattern is matched against -[../code/java/org/showcase/Greeting.java](../code/java/org/showcase/Greeting.java). +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java). The opening quote is escaped because instruction attributes are parsed as XML, and the trailing `*` lets the pattern match the rest of the return expression. Only the first matching source line is rendered into the fence. diff --git a/examples/showcase/embedding/positive/03-named-fragment.md b/examples/showcase/embedding/positive/03-named-fragment.md index 9d9d667d..0dba0b96 100644 --- a/examples/showcase/embedding/positive/03-named-fragment.md +++ b/examples/showcase/embedding/positive/03-named-fragment.md @@ -5,7 +5,7 @@ region. ## How It Works -[../code/java/org/showcase/Greeting.java](../code/java/org/showcase/Greeting.java) +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) wraps the `main()` method with matching `#docfragment` and `#enddocfragment` comments. The instruction resolves the named region, removes the marker lines, normalizes indentation, and replaces the following fence with the method body. diff --git a/examples/showcase/embedding/positive/06-start-end-pattern.md b/examples/showcase/embedding/positive/06-start-end-pattern.md index 6e5db41b..ed316c79 100644 --- a/examples/showcase/embedding/positive/06-start-end-pattern.md +++ b/examples/showcase/embedding/positive/06-start-end-pattern.md @@ -5,7 +5,7 @@ Use `start` and `end` when the source does not contain named fragment markers. ## How It Works The `start` pattern finds the first line in -[../code/java/org/showcase/PatternSamples.java](../code/java/org/showcase/PatternSamples.java) +[../../code/java/org/showcase/PatternSamples.java](../../code/java/org/showcase/PatternSamples.java) that contains `@Scenario`. The `end` pattern then searches after that start match and stops at the first line that is exactly four spaces followed by `}`. Both boundary lines are included in the rendered snippet. diff --git a/examples/showcase/embedding/positive/08-escaped-glob-character.md b/examples/showcase/embedding/positive/08-escaped-glob-character.md index a0fce7eb..f64aad51 100644 --- a/examples/showcase/embedding/positive/08-escaped-glob-character.md +++ b/examples/showcase/embedding/positive/08-escaped-glob-character.md @@ -6,7 +6,7 @@ one literally. ## How It Works The pattern `Use \* to multiply` treats `*` as source text instead of a wildcard. -It matches the line in [../code/text/glob-patterns.txt](../code/text/glob-patterns.txt) +It matches the line in [../../code/text/glob-patterns.txt](../../code/text/glob-patterns.txt) that contains a literal asterisk and embeds only that line. diff --git a/examples/showcase/embedding/positive/11-multi-part-fragment-separator.md b/examples/showcase/embedding/positive/11-multi-part-fragment-separator.md index 4ccb5864..6894cad6 100644 --- a/examples/showcase/embedding/positive/11-multi-part-fragment-separator.md +++ b/examples/showcase/embedding/positive/11-multi-part-fragment-separator.md @@ -4,7 +4,7 @@ One named fragment can be split across several source regions. ## How It Works -[../code/java/org/showcase/MultiPartWorkflow.java](../code/java/org/showcase/MultiPartWorkflow.java) +[../../code/java/org/showcase/MultiPartWorkflow.java](../../code/java/org/showcase/MultiPartWorkflow.java) opens and closes the `Workflow` fragment multiple times. Embed mode collects each part in source order and joins the parts with the `separator` configured in [../embed-code.yml](../embed-code.yml). This is useful when a guide needs the diff --git a/examples/showcase/embedding/positive/12-overlapping-fragments.md b/examples/showcase/embedding/positive/12-overlapping-fragments.md index c6c2a6ce..a5e0ebeb 100644 --- a/examples/showcase/embedding/positive/12-overlapping-fragments.md +++ b/examples/showcase/embedding/positive/12-overlapping-fragments.md @@ -4,7 +4,7 @@ Several fragments can open or close on the same marker line. ## How It Works -[../code/java/org/showcase/OverlappingFragments.java](../code/java/org/showcase/OverlappingFragments.java) +[../../code/java/org/showcase/OverlappingFragments.java](../../code/java/org/showcase/OverlappingFragments.java) uses marker lines that name both `Class wrapper` and `Greeting method`. The instruction asks only for `Greeting method`, so embed mode keeps the shared class wrapper, skips unrelated method details, and renders the selected method diff --git a/examples/showcase/showcase_test.go b/examples/showcase/showcase_test.go index 46a2c55b..6f400f15 100644 --- a/examples/showcase/showcase_test.go +++ b/examples/showcase/showcase_test.go @@ -34,7 +34,7 @@ import ( // repaired with embed mode, and checked again without changing repository files. func TestShowcasePositiveFlow(t *testing.T) { repoRoot := findRepoRoot(t) - docsRoot := copyShowcaseDocs(t, repoRoot, "docs") + docsRoot := copyShowcaseDocs(t, repoRoot, filepath.Join("embedding", "positive")) configPath := writeShowcaseConfig(t, repoRoot, docsRoot) checkOutput, err := runEmbedCode(t, repoRoot, "check", configPath) @@ -153,7 +153,7 @@ func TestShowcaseNegativeScenarios(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - docsRoot := copyShowcaseDocs(t, repoRoot, filepath.Join("negative", "docs")) + docsRoot := copyShowcaseDocs(t, repoRoot, filepath.Join("embedding", "negative", "docs")) configPath := writeSingleDocConfig(t, docsRoot, tc.sources, tc.doc) output, err := runEmbedCode(t, repoRoot, "check", configPath) From c0db8e7c185f60b2e9dfecff4771c831736f865c Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 18:36:49 +0200 Subject: [PATCH 05/22] Add embedding README. --- examples/showcase/README.md | 2 +- examples/showcase/embedding/README.md | 65 +++++++++++++++++++ .../positive/08-escaped-glob-character.md | 15 ----- .../positive/09-escaped-newline-text.md | 17 ----- ...ment-filtering.md => comment-filtering.md} | 0 ...ielding.md => markdown-fence-shielding.md} | 0 ...-line-pattern.md => multi-line-pattern.md} | 0 ...or.md => multi-part-fragment-separator.md} | 0 ...03-named-fragment.md => named-fragment.md} | 0 ...ed-source-root.md => named-source-root.md} | 0 ...-fragments.md => overlapping-fragments.md} | 0 ...ction-tag.md => paired-instruction-tag.md} | 0 .../embedding/positive/pattern-escaping.md | 47 ++++++++++++++ ...line-pattern.md => source-line-pattern.md} | 0 ...rt-end-pattern.md => start-end-pattern.md} | 0 ...le-file-source.md => whole-file-source.md} | 0 examples/showcase/showcase_test.go | 4 +- 17 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 examples/showcase/embedding/README.md delete mode 100644 examples/showcase/embedding/positive/08-escaped-glob-character.md delete mode 100644 examples/showcase/embedding/positive/09-escaped-newline-text.md rename examples/showcase/embedding/positive/{10-comment-filtering.md => comment-filtering.md} (100%) rename examples/showcase/embedding/positive/{13-markdown-fence-shielding.md => markdown-fence-shielding.md} (100%) rename examples/showcase/embedding/positive/{07-multi-line-pattern.md => multi-line-pattern.md} (100%) rename examples/showcase/embedding/positive/{11-multi-part-fragment-separator.md => multi-part-fragment-separator.md} (100%) rename examples/showcase/embedding/positive/{03-named-fragment.md => named-fragment.md} (100%) rename examples/showcase/embedding/positive/{05-named-source-root.md => named-source-root.md} (100%) rename examples/showcase/embedding/positive/{12-overlapping-fragments.md => overlapping-fragments.md} (100%) rename examples/showcase/embedding/positive/{04-paired-instruction-tag.md => paired-instruction-tag.md} (100%) create mode 100644 examples/showcase/embedding/positive/pattern-escaping.md rename examples/showcase/embedding/positive/{02-source-line-pattern.md => source-line-pattern.md} (100%) rename examples/showcase/embedding/positive/{06-start-end-pattern.md => start-end-pattern.md} (100%) rename examples/showcase/embedding/positive/{01-whole-file-source.md => whole-file-source.md} (100%) diff --git a/examples/showcase/README.md b/examples/showcase/README.md index 847e4039..1ac8e032 100644 --- a/examples/showcase/README.md +++ b/examples/showcase/README.md @@ -7,7 +7,7 @@ This is an executable showcase guide to `embed-code-go` and the end-to-end tests Guide is divided on two categories: 1. [Configuration](configuration/README.md) - describes how to configure the whole embed-code application. -2. [Embedding](embedding/positive/) - describes how to work with the embedding instructions. +2. [Embedding](embedding/README.md) - describes how to work with the embedding instructions. ## How To Run Tests diff --git a/examples/showcase/embedding/README.md b/examples/showcase/embedding/README.md new file mode 100644 index 00000000..e65b3a15 --- /dev/null +++ b/examples/showcase/embedding/README.md @@ -0,0 +1,65 @@ +# Embedding Examples + +This folder is a runnable guide to embedding instructions. The positive +examples show supported features, and the negative examples show the failures a +user should expect when an instruction is malformed or stale. + +Run the positive examples from the repository root: + +```bash +go run ./main.go -mode check -config-path examples/showcase/embedding/embed-code.yml +``` + +## Feature Examples + +### Simple Embedding + +- [Whole file source](positive/whole-file-source.md) + shows how to embed the whole source file. +- [Paired instruction tag](positive/paired-instruction-tag.md) + shows the preferred paired tag form used throughout the showcase. +- [Named source root](positive/named-source-root.md) + shows how to use different configured source trees. + +### Line, Range, And Glob Matching + +- [Source line pattern](positive/source-line-pattern.md) + embeds the first source line that matches a `line` pattern. +- [Start and end patterns](positive/start-end-pattern.md) + embeds an inclusive source range selected by `start` and `end`. +- [Multi-line patterns](positive/multi-line-pattern.md) + uses `\n` to match consecutive source lines. +- [Pattern escaping](positive/pattern-escaping.md) + shows literal `*`, trailing `$`, leading `^`, and backslash-n text matches. + +### Fragments + +- [Named fragment](positive/named-fragment.md) + embeds a region wrapped with `#docfragment` and `#enddocfragment` markers. +- [Multi-part fragment separator](positive/multi-part-fragment-separator.md) + joins repeated fragment parts with the configured separator. +- [Overlapping fragments](positive/overlapping-fragments.md) + shows fragment markers that share source lines. + +### Rendered Content And Documents + +- [Comment filtering](positive/comment-filtering.md) + keeps documentation comments while removing regular comments. +- [Markdown fence shielding](positive/markdown-fence-shielding.md) + shows that instruction-looking text inside ordinary code fences is inert. +- [HTML showcase](positive/html-showcase.html) + shows that HTML documents can be processed when the include patterns allow them. +- [Excluded document](positive/ignored-by-exclude.md) + shows that `doc-excludes` prevents a matching file from being processed. + +## Negative Examples + +The negative examples are intentionally broken and should fail in check mode. +Use them to recognize common diagnostics: + +```bash +go run ./main.go -mode check -config-path examples/showcase/embedding/negative/processing-errors.yml +go run ./main.go -mode check -config-path examples/showcase/embedding/negative/stale.yml +``` + +The cases live in [negative/docs](negative/docs/). diff --git a/examples/showcase/embedding/positive/08-escaped-glob-character.md b/examples/showcase/embedding/positive/08-escaped-glob-character.md deleted file mode 100644 index f64aad51..00000000 --- a/examples/showcase/embedding/positive/08-escaped-glob-character.md +++ /dev/null @@ -1,15 +0,0 @@ -# Escaped Glob Characters - -Glob characters are useful in patterns, but sometimes the source text contains -one literally. - -## How It Works - -The pattern `Use \* to multiply` treats `*` as source text instead of a wildcard. -It matches the line in [../../code/text/glob-patterns.txt](../../code/text/glob-patterns.txt) -that contains a literal asterisk and embeds only that line. - - -```text -Use * to multiply -``` diff --git a/examples/showcase/embedding/positive/09-escaped-newline-text.md b/examples/showcase/embedding/positive/09-escaped-newline-text.md deleted file mode 100644 index ca4214cf..00000000 --- a/examples/showcase/embedding/positive/09-escaped-newline-text.md +++ /dev/null @@ -1,17 +0,0 @@ -# Escaped Newline Text - -Pattern escaping distinguishes a real multi-line pattern from source text that -contains the characters backslash and `n`. - -## How It Works - -The pattern uses `\\n` because the source line contains a string literal with -backslash-n text. The quote characters are written as `\"` so the instruction -remains valid XML. The result is one source line, not a two-line match. - - -```java -private static final String ESCAPED_NEWLINE = "\n"; -``` diff --git a/examples/showcase/embedding/positive/10-comment-filtering.md b/examples/showcase/embedding/positive/comment-filtering.md similarity index 100% rename from examples/showcase/embedding/positive/10-comment-filtering.md rename to examples/showcase/embedding/positive/comment-filtering.md diff --git a/examples/showcase/embedding/positive/13-markdown-fence-shielding.md b/examples/showcase/embedding/positive/markdown-fence-shielding.md similarity index 100% rename from examples/showcase/embedding/positive/13-markdown-fence-shielding.md rename to examples/showcase/embedding/positive/markdown-fence-shielding.md diff --git a/examples/showcase/embedding/positive/07-multi-line-pattern.md b/examples/showcase/embedding/positive/multi-line-pattern.md similarity index 100% rename from examples/showcase/embedding/positive/07-multi-line-pattern.md rename to examples/showcase/embedding/positive/multi-line-pattern.md diff --git a/examples/showcase/embedding/positive/11-multi-part-fragment-separator.md b/examples/showcase/embedding/positive/multi-part-fragment-separator.md similarity index 100% rename from examples/showcase/embedding/positive/11-multi-part-fragment-separator.md rename to examples/showcase/embedding/positive/multi-part-fragment-separator.md diff --git a/examples/showcase/embedding/positive/03-named-fragment.md b/examples/showcase/embedding/positive/named-fragment.md similarity index 100% rename from examples/showcase/embedding/positive/03-named-fragment.md rename to examples/showcase/embedding/positive/named-fragment.md diff --git a/examples/showcase/embedding/positive/05-named-source-root.md b/examples/showcase/embedding/positive/named-source-root.md similarity index 100% rename from examples/showcase/embedding/positive/05-named-source-root.md rename to examples/showcase/embedding/positive/named-source-root.md diff --git a/examples/showcase/embedding/positive/12-overlapping-fragments.md b/examples/showcase/embedding/positive/overlapping-fragments.md similarity index 100% rename from examples/showcase/embedding/positive/12-overlapping-fragments.md rename to examples/showcase/embedding/positive/overlapping-fragments.md diff --git a/examples/showcase/embedding/positive/04-paired-instruction-tag.md b/examples/showcase/embedding/positive/paired-instruction-tag.md similarity index 100% rename from examples/showcase/embedding/positive/04-paired-instruction-tag.md rename to examples/showcase/embedding/positive/paired-instruction-tag.md diff --git a/examples/showcase/embedding/positive/pattern-escaping.md b/examples/showcase/embedding/positive/pattern-escaping.md new file mode 100644 index 00000000..8c54a4fb --- /dev/null +++ b/examples/showcase/embedding/positive/pattern-escaping.md @@ -0,0 +1,47 @@ +# Pattern Escaping + +Pattern escaping distinguishes glob syntax from source text that happens to use +the same characters. + +## Literal Asterisk + +The pattern `Use \* to multiply` treats `*` as source text instead of a +wildcard. It matches a line in +[../../code/text/glob-patterns.txt](../../code/text/glob-patterns.txt). + + +```text +Use * to multiply +``` + +## Literal Dollar At The End + +`$` is an end anchor only at the end of a pattern. Use `$$` there when the +source line itself ends with a dollar sign. + + +```text +The value ends with $ +``` + +## Literal Caret At The Start + +`^` is a start anchor only at the start of a pattern. Use `^^` there when the +source line itself starts with a caret. + + +```text +^ starts with caret +``` + +## Literal Backslash-N Text + +Use `\\n` when the source line contains the characters backslash and `n`. The +quote characters are written as `\"` so the instruction remains valid XML. + + +```java +private static final String ESCAPED_NEWLINE = "\n"; +``` diff --git a/examples/showcase/embedding/positive/02-source-line-pattern.md b/examples/showcase/embedding/positive/source-line-pattern.md similarity index 100% rename from examples/showcase/embedding/positive/02-source-line-pattern.md rename to examples/showcase/embedding/positive/source-line-pattern.md diff --git a/examples/showcase/embedding/positive/06-start-end-pattern.md b/examples/showcase/embedding/positive/start-end-pattern.md similarity index 100% rename from examples/showcase/embedding/positive/06-start-end-pattern.md rename to examples/showcase/embedding/positive/start-end-pattern.md diff --git a/examples/showcase/embedding/positive/01-whole-file-source.md b/examples/showcase/embedding/positive/whole-file-source.md similarity index 100% rename from examples/showcase/embedding/positive/01-whole-file-source.md rename to examples/showcase/embedding/positive/whole-file-source.md diff --git a/examples/showcase/showcase_test.go b/examples/showcase/showcase_test.go index 6f400f15..2ea535d1 100644 --- a/examples/showcase/showcase_test.go +++ b/examples/showcase/showcase_test.go @@ -42,7 +42,7 @@ func TestShowcasePositiveFlow(t *testing.T) { t.Fatalf("expected positive showcase check to pass:\n%s", checkOutput) } - staleDoc := filepath.Join(docsRoot, "01-whole-file-source.md") + staleDoc := filepath.Join(docsRoot, "whole-file-source.md") replaceInFile(t, staleDoc, "package org.showcase;", "package stale.showcase;") staleOutput, err := runEmbedCode(t, repoRoot, "check", configPath) @@ -50,7 +50,7 @@ func TestShowcasePositiveFlow(t *testing.T) { t.Fatalf("expected stale showcase check to fail:\n%s", staleOutput) } assertOutputContains(t, staleOutput, "File to update:") - assertOutputContains(t, staleOutput, "01-whole-file-source.md") + assertOutputContains(t, staleOutput, "whole-file-source.md") embedOutput, err := runEmbedCode(t, repoRoot, "embed", configPath) if err != nil { From c321c61fbeb0132aefb4791def64fa347b5cf2e1 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 20:08:40 +0200 Subject: [PATCH 06/22] Improve showcase embedding readme. --- examples/showcase/embedding/README.md | 24 +++++++++---------- examples/showcase/embedding/embed-code.yml | 2 -- .../embedding/positive/ignored-by-exclude.md | 15 ------------ ...-instruction-tag.md => instruction-tag.md} | 0 examples/showcase/showcase_test.go | 11 ++------- 5 files changed, 13 insertions(+), 39 deletions(-) delete mode 100644 examples/showcase/embedding/positive/ignored-by-exclude.md rename examples/showcase/embedding/positive/{paired-instruction-tag.md => instruction-tag.md} (100%) diff --git a/examples/showcase/embedding/README.md b/examples/showcase/embedding/README.md index e65b3a15..625968f4 100644 --- a/examples/showcase/embedding/README.md +++ b/examples/showcase/embedding/README.md @@ -14,10 +14,10 @@ go run ./main.go -mode check -config-path examples/showcase/embedding/embed-code ### Simple Embedding -- [Whole file source](positive/whole-file-source.md) +- [Whole file source](positive/whole-file-source.md) shows how to embed the whole source file. -- [Paired instruction tag](positive/paired-instruction-tag.md) - shows the preferred paired tag form used throughout the showcase. +- [Instruction tag](positive/instruction-tag.md) + shows the preferred way to use `` tag. - [Named source root](positive/named-source-root.md) shows how to use different configured source trees. @@ -30,31 +30,29 @@ go run ./main.go -mode check -config-path examples/showcase/embedding/embed-code - [Multi-line patterns](positive/multi-line-pattern.md) uses `\n` to match consecutive source lines. - [Pattern escaping](positive/pattern-escaping.md) - shows literal `*`, trailing `$`, leading `^`, and backslash-n text matches. + shows how to escape special characters. ### Fragments -- [Named fragment](positive/named-fragment.md) +- [Named fragment](positive/named-fragment.md) embeds a region wrapped with `#docfragment` and `#enddocfragment` markers. - [Multi-part fragment separator](positive/multi-part-fragment-separator.md) joins repeated fragment parts with the configured separator. -- [Overlapping fragments](positive/overlapping-fragments.md) +- [Overlapping fragments](positive/overlapping-fragments.md) shows fragment markers that share source lines. ### Rendered Content And Documents -- [Comment filtering](positive/comment-filtering.md) - keeps documentation comments while removing regular comments. -- [Markdown fence shielding](positive/markdown-fence-shielding.md) +- [Comment filtering](positive/comment-filtering.md) + shows how to omit comments in the source code. +- [Markdown fence shielding](positive/markdown-fence-shielding.md) shows that instruction-looking text inside ordinary code fences is inert. -- [HTML showcase](positive/html-showcase.html) +- [HTML showcase](positive/html-showcase.html) shows that HTML documents can be processed when the include patterns allow them. -- [Excluded document](positive/ignored-by-exclude.md) - shows that `doc-excludes` prevents a matching file from being processed. ## Negative Examples -The negative examples are intentionally broken and should fail in check mode. +The negative examples are intentionally broken and should fail. Use them to recognize common diagnostics: ```bash diff --git a/examples/showcase/embedding/embed-code.yml b/examples/showcase/embedding/embed-code.yml index 981ced65..1f88c8a7 100644 --- a/examples/showcase/embedding/embed-code.yml +++ b/examples/showcase/embedding/embed-code.yml @@ -9,6 +9,4 @@ docs-path: examples/showcase/embedding/positive doc-includes: - "**/*.md" - "**/*.html" -doc-excludes: - - ignored-by-exclude.md separator: "// ..." diff --git a/examples/showcase/embedding/positive/ignored-by-exclude.md b/examples/showcase/embedding/positive/ignored-by-exclude.md deleted file mode 100644 index 8797906e..00000000 --- a/examples/showcase/embedding/positive/ignored-by-exclude.md +++ /dev/null @@ -1,15 +0,0 @@ -# Excluded Showcase File - -This file is intentionally present in the positive docs root but absent from -the positive processing flow. - -## How It Works - -[../embed-code.yml](../embed-code.yml) lists this file in `doc-excludes`, so -embed mode and check mode skip it even though it matches the include patterns. -The missing source path proves the exclude is active: processing this file would -fail immediately. - - -```go -``` diff --git a/examples/showcase/embedding/positive/paired-instruction-tag.md b/examples/showcase/embedding/positive/instruction-tag.md similarity index 100% rename from examples/showcase/embedding/positive/paired-instruction-tag.md rename to examples/showcase/embedding/positive/instruction-tag.md diff --git a/examples/showcase/showcase_test.go b/examples/showcase/showcase_test.go index 2ea535d1..78ff77e6 100644 --- a/examples/showcase/showcase_test.go +++ b/examples/showcase/showcase_test.go @@ -263,7 +263,7 @@ func writeShowcaseConfig(t *testing.T, repoRoot string, docsRoot string) string {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, {name: "kotlin", path: filepath.Join(repoRoot, "examples", "showcase", "code", "kotlin")}, {name: "text", path: filepath.Join(repoRoot, "examples", "showcase", "code", "text")}, - }, []string{"**/*.md", "**/*.html"}, []string{"ignored-by-exclude.md"}) + }, []string{"**/*.md", "**/*.html"}) } // writeSingleDocConfig creates a temp config for one negative showcase document. @@ -275,7 +275,7 @@ func writeSingleDocConfig( ) string { t.Helper() - return writeConfig(t, docsRoot, sources, []string{docInclude}, nil) + return writeConfig(t, docsRoot, sources, []string{docInclude}) } // writeConfig writes a YAML config with absolute source and documentation paths. @@ -284,7 +284,6 @@ func writeConfig( docsRoot string, sources []namedSource, includes []string, - excludes []string, ) string { t.Helper() @@ -299,12 +298,6 @@ func writeConfig( for _, include := range includes { builder.WriteString(fmt.Sprintf(" - %q\n", include)) } - if len(excludes) > 0 { - builder.WriteString("doc-excludes:\n") - for _, exclude := range excludes { - builder.WriteString(fmt.Sprintf(" - %q\n", exclude)) - } - } builder.WriteString("separator: \"// ...\"\n") configPath := filepath.Join(t.TempDir(), "embed-code.yml") From d4838043714544687de96eff5ad6717fc704f628 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 20:36:18 +0200 Subject: [PATCH 07/22] Remove redundant example. --- examples/showcase/README.md | 15 +- examples/showcase/configuration/README.md | 147 +++++++++++++----- .../configuration/docs/root-source/go-mod.md | 17 -- .../configuration/docs/root-source/version.md | 15 -- .../showcase/configuration/root-source.yml | 6 - examples/showcase/embedding/README.md | 6 +- examples/showcase/showcase_test.go | 1 - 7 files changed, 114 insertions(+), 93 deletions(-) delete mode 100644 examples/showcase/configuration/docs/root-source/go-mod.md delete mode 100644 examples/showcase/configuration/docs/root-source/version.md delete mode 100644 examples/showcase/configuration/root-source.yml diff --git a/examples/showcase/README.md b/examples/showcase/README.md index 1ac8e032..843e9439 100644 --- a/examples/showcase/README.md +++ b/examples/showcase/README.md @@ -22,22 +22,21 @@ go test -tags showcase ./examples/showcase Verify the positive embedding examples: ```bash -go run ./main.go -mode check -config-path examples/showcase/embedding/embed-code.yml +go run ./main.go -mode=check -config-path=examples/showcase/embedding/embed-code.yml ``` Verify the configuration examples: ```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml -go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/named-sources.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/include-exclude.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/multiple-embeddings.yml ``` The negative examples are intentionally broken, so these commands should fail: ```bash -go run ./main.go -mode check -config-path examples/showcase/embedding/negative/processing-errors.yml -go run ./main.go -mode check -config-path examples/showcase/embedding/negative/stale.yml +go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/processing-errors.yml +go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/stale.yml ``` diff --git a/examples/showcase/configuration/README.md b/examples/showcase/configuration/README.md index 74747e64..55940386 100644 --- a/examples/showcase/configuration/README.md +++ b/examples/showcase/configuration/README.md @@ -1,74 +1,135 @@ # Configuration Examples -These examples show the supported YAML configuration shapes. +This folder is a runnable guide to YAML configuration. Start with the smallest +working config, then add only the options your documentation needs. -Each YAML file has a matching docs root under [docs](docs/). Read the YAML file -first, then open the linked docs folder to see how instructions use that source -configuration. +## Minimal Config -## Repository Root Source +A configuration needs one source root and one documentation root: -[root-source.yml](root-source.yml) uses the repository root as a named source -root. Instructions in [docs/root-source](docs/root-source/) embed files from -the project root with the `$repo` prefix. +```yaml +code-path: examples/showcase/code/java +docs-path: examples/showcase/configuration/docs/single-source +``` + +This shape is shown by [single-source.yml](single-source.yml). + +The command scans files under `docs-path`, finds `` instructions, +and resolves each instruction's `file` path from `code-path`. For example, see +instruction in [docs/single-source/greeting.md](docs/single-source/greeting.md). -Use this shape only when documentation really needs files from the repository -root. The main embedding showcase avoids root sources so ordinary examples stay -independent from project metadata. +Run this example (from the project root): ```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/root-source.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml ``` -## Single Showcase Source Root +## How Paths Are Selected -[single-source.yml](single-source.yml) uses one unnamed `code-path`. -Instructions in [docs/single-source](docs/single-source/) use paths relative to -that root without a `$name` prefix. +The showcase commands are meant to run from the repository root. Relative paths +in `code-path` and `docs-path` are resolved from the command's current working +directory. -This is the simplest configuration for one source tree and one documentation -tree. +`docs-path` selects the documentation root to scan. `doc-includes` and +`doc-excludes` are then matched relative to that documentation root. -```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/single-source.yml -``` +`code-path` selects where source files come from: -## Named Source Roots +- With one unnamed `code-path`, an instruction such as + `file="org/showcase/Greeting.java"` is resolved relative to that source root. +- With named source roots, an instruction such as + `file="$kotlin/org/showcase/KotlinGreeting.kt"` first selects the `kotlin` + source root, then resolves the remaining path inside that root. -[named-sources.yml](named-sources.yml) defines Java, Kotlin, and text source -roots. Instructions in [docs/named-sources](docs/named-sources/) choose a -source root with `$java`, `$kotlin`, or `$text`. +## Add Document Selection -Use this shape when one docs tree needs snippets from several source trees. +Add `doc-includes` when only some files under `docs-path` should be scanned. +Add `doc-excludes` when selected files should be skipped: + +```yaml +code-path: examples/showcase/code/java +docs-path: examples/showcase/configuration/docs/include-exclude +doc-includes: + - "**/*.md" +doc-excludes: + - excluded.md +``` + +This shape is shown by [include-exclude.yml](include-exclude.yml). It processes +[docs/include-exclude/included.md](docs/include-exclude/included.md) and skips +[docs/include-exclude/excluded.md](docs/include-exclude/excluded.md). + +Use include and exclude patterns to skip drafts, generated docs, deprecated +pages, or any file that should not be scanned for active instructions. ```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/named-sources.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/include-exclude.yml +``` + +## Add Named Source Roots + +Use named source roots when one documentation tree embeds snippets from several +source trees: + +```yaml +code-path: + - name: java + path: examples/showcase/code/java + - name: kotlin + path: examples/showcase/code/kotlin + - name: text + path: examples/showcase/code/text +docs-path: examples/showcase/configuration/docs/named-sources ``` -## Include And Exclude Patterns +This shape is shown by [named-sources.yml](named-sources.yml). Its docs live in +[docs/named-sources](docs/named-sources/). -[include-exclude.yml](include-exclude.yml) processes Markdown files in -[docs/include-exclude](docs/include-exclude/) but excludes -[excluded.md](docs/include-exclude/excluded.md). That file intentionally -references a missing source file, so the check succeeds only when -`doc-excludes` is applied. +Instructions choose a source root with the `$name` prefix: + +```markdown + +``` -Use this shape to skip drafts, generated docs, deprecated pages, or any file -that should not be scanned for active instructions. +Run the named-source example: ```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/include-exclude.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/named-sources.yml ``` -## Multiple Embeddings +## Add Multiple Documentation Targets + +Use `embeddings` when one command should process several independent +documentation targets. Each entry has its own `name`, `code-path`, `docs-path`, +and optional settings: + +```yaml +embeddings: + - name: java-guide + code-path: examples/showcase/code/java + docs-path: examples/showcase/configuration/docs/multiple/java + - name: kotlin-guide + code-path: + - name: kotlin + path: examples/showcase/code/kotlin + docs-path: examples/showcase/configuration/docs/multiple/kotlin +``` + +This shape is shown by [multiple-embeddings.yml](multiple-embeddings.yml). It +processes [docs/multiple/java](docs/multiple/java/) and +[docs/multiple/kotlin](docs/multiple/kotlin/) in one run. + +```bash +go run ./main.go -mode=check -config-path=examples/showcase/configuration/multiple-embeddings.yml +``` -[multiple-embeddings.yml](multiple-embeddings.yml) uses the `embeddings` list -to process two independent documentation roots in -[docs/multiple](docs/multiple/) in one run. +## All Configuration Checks -Use this shape when one command should process several independent -documentation targets with different source roots or settings. +Run commands from the project root. ```bash -go run ./main.go -mode check -config-path examples/showcase/configuration/multiple-embeddings.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/named-sources.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/include-exclude.yml +go run ./main.go -mode=check -config-path=examples/showcase/configuration/multiple-embeddings.yml ``` diff --git a/examples/showcase/configuration/docs/root-source/go-mod.md b/examples/showcase/configuration/docs/root-source/go-mod.md deleted file mode 100644 index 40262959..00000000 --- a/examples/showcase/configuration/docs/root-source/go-mod.md +++ /dev/null @@ -1,17 +0,0 @@ -# Repository Root Source - -This configuration example uses the repository root as a named source root. The -instruction embeds the module declaration from `go.mod` through the `$repo` -prefix. - -## How It Works - -[../../root-source.yml](../../root-source.yml) maps the repository root to the -name `repo`. The instruction uses `$repo/go.mod` and a `line` pattern so this -configuration test proves root-source lookup without embedding the whole -project metadata file. - - -```go -module embed-code/embed-code-go -``` diff --git a/examples/showcase/configuration/docs/root-source/version.md b/examples/showcase/configuration/docs/root-source/version.md deleted file mode 100644 index 08cfef44..00000000 --- a/examples/showcase/configuration/docs/root-source/version.md +++ /dev/null @@ -1,15 +0,0 @@ -# Repository Root Line Pattern - -The same root-source configuration can select a single line from a root file. - -## How It Works - -The `$repo` prefix points at the repository root configured in -[../../root-source.yml](../../root-source.yml). The anchored pattern selects the -`Version` constant from `main.go`, which keeps the example small while proving -that root files can be used as sources. - - -```go -const Version = "1.2.2" -``` diff --git a/examples/showcase/configuration/root-source.yml b/examples/showcase/configuration/root-source.yml deleted file mode 100644 index f7209a30..00000000 --- a/examples/showcase/configuration/root-source.yml +++ /dev/null @@ -1,6 +0,0 @@ -code-path: - - name: repo - path: . -docs-path: examples/showcase/configuration/docs/root-source -doc-includes: - - "**/*.md" diff --git a/examples/showcase/embedding/README.md b/examples/showcase/embedding/README.md index 625968f4..50854948 100644 --- a/examples/showcase/embedding/README.md +++ b/examples/showcase/embedding/README.md @@ -7,7 +7,7 @@ user should expect when an instruction is malformed or stale. Run the positive examples from the repository root: ```bash -go run ./main.go -mode check -config-path examples/showcase/embedding/embed-code.yml +go run ./main.go -mode=check -config-path=examples/showcase/embedding/embed-code.yml ``` ## Feature Examples @@ -56,8 +56,8 @@ The negative examples are intentionally broken and should fail. Use them to recognize common diagnostics: ```bash -go run ./main.go -mode check -config-path examples/showcase/embedding/negative/processing-errors.yml -go run ./main.go -mode check -config-path examples/showcase/embedding/negative/stale.yml +go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/processing-errors.yml +go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/stale.yml ``` The cases live in [negative/docs](negative/docs/). diff --git a/examples/showcase/showcase_test.go b/examples/showcase/showcase_test.go index 78ff77e6..742caa8b 100644 --- a/examples/showcase/showcase_test.go +++ b/examples/showcase/showcase_test.go @@ -172,7 +172,6 @@ func TestShowcaseConfigurationExamples(t *testing.T) { repoRoot := findRepoRoot(t) configs := []string{ - "root-source.yml", "single-source.yml", "named-sources.yml", "include-exclude.yml", From 3f0d78e71d9908cd2d13334fc04fb97fde770e8f Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 20:47:21 +0200 Subject: [PATCH 08/22] Improve readability. --- examples/showcase/configuration/README.md | 24 ++++--------------- .../docs/single-source/greeting.md | 3 +-- .../showcase/configuration/single-source.yml | 2 -- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/examples/showcase/configuration/README.md b/examples/showcase/configuration/README.md index 55940386..6aabe277 100644 --- a/examples/showcase/configuration/README.md +++ b/examples/showcase/configuration/README.md @@ -12,35 +12,21 @@ code-path: examples/showcase/code/java docs-path: examples/showcase/configuration/docs/single-source ``` -This shape is shown by [single-source.yml](single-source.yml). +This config is shown by [single-source.yml](single-source.yml). -The command scans files under `docs-path`, finds `` instructions, +The application scans files under `docs-path`, finds `` instructions, and resolves each instruction's `file` path from `code-path`. For example, see instruction in [docs/single-source/greeting.md](docs/single-source/greeting.md). +Relative paths in `code-path` and `docs-path` are resolved +from the command's current working directory. + Run this example (from the project root): ```bash go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml ``` -## How Paths Are Selected - -The showcase commands are meant to run from the repository root. Relative paths -in `code-path` and `docs-path` are resolved from the command's current working -directory. - -`docs-path` selects the documentation root to scan. `doc-includes` and -`doc-excludes` are then matched relative to that documentation root. - -`code-path` selects where source files come from: - -- With one unnamed `code-path`, an instruction such as - `file="org/showcase/Greeting.java"` is resolved relative to that source root. -- With named source roots, an instruction such as - `file="$kotlin/org/showcase/KotlinGreeting.kt"` first selects the `kotlin` - source root, then resolves the remaining path inside that root. - ## Add Document Selection Add `doc-includes` when only some files under `docs-path` should be scanned. diff --git a/examples/showcase/configuration/docs/single-source/greeting.md b/examples/showcase/configuration/docs/single-source/greeting.md index 64cc31c3..0fbb97fb 100644 --- a/examples/showcase/configuration/docs/single-source/greeting.md +++ b/examples/showcase/configuration/docs/single-source/greeting.md @@ -1,7 +1,6 @@ # Single Source Root -This config uses one unnamed `code-path`, so instructions refer to source files -relative to that root without a `$name` prefix. +This config uses one unnamed `code-path`. ## How It Works diff --git a/examples/showcase/configuration/single-source.yml b/examples/showcase/configuration/single-source.yml index 45fac634..bf2e5036 100644 --- a/examples/showcase/configuration/single-source.yml +++ b/examples/showcase/configuration/single-source.yml @@ -1,4 +1,2 @@ code-path: examples/showcase/code/java docs-path: examples/showcase/configuration/docs/single-source -doc-includes: - - "**/*.md" From 4ee5be60ea7a59f60330aaae4fbaf0a239ef87fa Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 21:02:11 +0200 Subject: [PATCH 09/22] Improve comments filtering doc. --- EMBEDDING.md | 22 +-- .../embedding/positive/comment-filtering.md | 125 +++++++++++++++++- 2 files changed, 132 insertions(+), 15 deletions(-) diff --git a/EMBEDDING.md b/EMBEDDING.md index d0ea407e..f4d7edcb 100644 --- a/EMBEDDING.md +++ b/EMBEDDING.md @@ -214,18 +214,18 @@ Not all languages distinguish documentation from regular comments or inline from The table below lists the supported languages and supported `comments` modes for them: -| Language | Extensions | Supported `comments` modes | -|------------------------|---------------------------------------------------------|--------------------------------------------------------------| -| Java, Kotlin, Groovy | `.java`, `.kt`, `.kts`, `.groovy` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | -| C# | `.cs` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | +| Language | Extensions | Supported `comments` modes | +|------------------------|----------------------------------------------------------|--------------------------------------------------------------| +| Java, Kotlin, Groovy | `.java`, `.kt`, `.kts`, `.groovy` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | +| C# | `.cs` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | | C, C++ | `.c`, `.h`, `.cc`, `.cpp`, `.cxx`, `.hh`, `.hpp`, `.hxx` | `all`, `none`, `inline`, `block` | -| JavaScript, TypeScript | `.js`, `.jsx`, `.ts`, `.tsx` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | -| Go | `.go` | `all`, `none`, `inline`, `block` | -| Protobuf | `.proto` | `all`, `none`, `inline`, `block` | -| Python | `.py`, `.pyi`, `.pyw` | `all`, `none` | -| YAML | `.yml`, `.yaml` | `all`, `none` | -| XML, HTML | `.xml`, `.html`, `.htm` | `all`, `none` | -| Visual Basic | `.vb`, `.bas`, `.vbs`, `.vbscript` | `all`, `none`, `documentation`, `regular` | +| JavaScript, TypeScript | `.js`, `.jsx`, `.ts`, `.tsx` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | +| Go | `.go` | `all`, `none`, `inline`, `block` | +| Protobuf | `.proto` | `all`, `none`, `inline`, `block` | +| Python | `.py`, `.pyi`, `.pyw` | `all`, `none` | +| YAML | `.yml`, `.yaml` | `all`, `none` | +| XML, HTML | `.xml`, `.html`, `.htm` | `all`, `none` | +| Visual Basic | `.vb`, `.bas`, `.vbs`, `.vbscript` | `all`, `none`, `documentation`, `regular` | ## Advanced use cases diff --git a/examples/showcase/embedding/positive/comment-filtering.md b/examples/showcase/embedding/positive/comment-filtering.md index 7f273bbd..1b9f1cd6 100644 --- a/examples/showcase/embedding/positive/comment-filtering.md +++ b/examples/showcase/embedding/positive/comment-filtering.md @@ -5,10 +5,71 @@ implementation notes. ## How It Works -The instruction embeds the whole Java file and applies -`comments="documentation"`. Javadoc is retained, regular block comments and -inline comments are removed, and comment-like text inside string literals stays -unchanged because it is not a real comment. +The instruction first resolves the source content, then applies comment +filtering before rendering the code fence. If `comments` is omitted, the default +is `all`, so every recognized comment remains in the snippet. + +Supported modes are: + +- `all` keeps every comment. +- `none` removes every recognized comment. +- `documentation` keeps documentation comments, such as Javadoc. +- `regular` keeps non-documentation line and block comments. +- `inline` keeps non-documentation line comments, such as `//`. +- `block` keeps non-documentation block comments, such as `/* */`. + +Comment support depends on the source file extension. Unknown extensions are +embedded unchanged, and not every supported language distinguishes +documentation, regular, inline, and block comments. See +[Comment filtering](../../../../EMBEDDING.md#comment-filtering) for the full +language matrix. + +The examples below embed the same Java file with different `comments` modes so +the rendered output can be compared directly. + +## All Comments + +`comments="all"` keeps every recognized comment. Omitting `comments` would +produce the same result because `all` is the default. + + +```java +package org.showcase; + +/** + * Creates public greetings. + */ +public interface CommentModes { + /* + * Internal implementation note. + */ + String URL = "http://example.org/*not-comment*/"; + + // Regular inline comment. + String greet(String name); // trailing inline comment. +} +``` + +## No Comments + +`comments="none"` removes every recognized comment. + + +```java +package org.showcase; + +public interface CommentModes { + String URL = "http://example.org/*not-comment*/"; + + String greet(String name); +} +``` + +## Documentation Comments + +`comments="documentation"` keeps Javadoc and removes regular block and inline +comments. Comment-like text inside string literals stays unchanged because it is +not a real comment. ```java @@ -23,3 +84,59 @@ public interface CommentModes { String greet(String name); } ``` + +## Regular Comments + +`comments="regular"` keeps non-documentation line and block comments and removes +documentation comments. + + +```java +package org.showcase; + +public interface CommentModes { + /* + * Internal implementation note. + */ + String URL = "http://example.org/*not-comment*/"; + + // Regular inline comment. + String greet(String name); // trailing inline comment. +} +``` + +## Inline Comments + +`comments="inline"` keeps non-documentation line comments and removes block and +documentation comments. + + +```java +package org.showcase; + +public interface CommentModes { + String URL = "http://example.org/*not-comment*/"; + + // Regular inline comment. + String greet(String name); // trailing inline comment. +} +``` + +## Block Comments + +`comments="block"` keeps non-documentation block comments and removes inline and +documentation comments. + + +```java +package org.showcase; + +public interface CommentModes { + /* + * Internal implementation note. + */ + String URL = "http://example.org/*not-comment*/"; + + String greet(String name); +} +``` From 9d21addfcbea62f2974934c47214aea480f28098 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 21:08:26 +0200 Subject: [PATCH 10/22] Improve 'Instruction Tag' doc. --- .../embedding/positive/html-showcase.html | 2 +- .../embedding/positive/instruction-tag.md | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/showcase/embedding/positive/html-showcase.html b/examples/showcase/embedding/positive/html-showcase.html index a1080cb3..0650e80f 100644 --- a/examples/showcase/embedding/positive/html-showcase.html +++ b/examples/showcase/embedding/positive/html-showcase.html @@ -4,7 +4,7 @@

HTML Embedding Showcase

HTML files are scanned when the include patterns allow them. The instruction - below uses the same paired tag form as the Markdown examples and still needs + below uses the same tag form as the Markdown examples and still needs a Markdown code fence immediately after it.

diff --git a/examples/showcase/embedding/positive/instruction-tag.md b/examples/showcase/embedding/positive/instruction-tag.md index 5c202059..ca75d49e 100644 --- a/examples/showcase/embedding/positive/instruction-tag.md +++ b/examples/showcase/embedding/positive/instruction-tag.md @@ -1,15 +1,10 @@ -# Paired Instruction Tag +# Instruction Tag Instructions may be self-closing or paired. The self-closing form is supported, -but this showcase uses paired tags because some Markdown renderers display the +but it is preferred to use paired tags because some Markdown renderers display the XML-style self-closing tag awkwardly. -## How It Works - -The active instruction below has an opening `` tag and a matching -closing tag. No content is required between them; the rendered snippet still -comes from the source file and the following code fence. The whole showcase uses -this paired form so Markdown previews display the instructions consistently. +### Paired tag version ```java @@ -17,3 +12,12 @@ public static void main(String[] args) { System.out.println(greeting("Ada")); } ``` + +### Self-closing tag version + + +```java +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +``` From 825644aa8c9c9fbf9c6c9d743b7e16e30b07adad Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 21:17:05 +0200 Subject: [PATCH 11/22] Improve 'Multi-Line Patterns' doc. --- .../embedding/positive/multi-line-pattern.md | 28 +++++++++++++++---- .../positive/multi-part-fragment-separator.md | 8 ++++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/examples/showcase/embedding/positive/multi-line-pattern.md b/examples/showcase/embedding/positive/multi-line-pattern.md index 3e382328..bb8ef89e 100644 --- a/examples/showcase/embedding/positive/multi-line-pattern.md +++ b/examples/showcase/embedding/positive/multi-line-pattern.md @@ -4,11 +4,16 @@ Use `\n` inside a pattern when one source line is not specific enough. ## How It Works -The `start` value is split into two consecutive line patterns: one that matches -the `@Scenario` line and one that matches the display-name line. The `end` -value works the same way for the assertion and closing brace. Each pattern line -still uses the normal glob rules, so anchors are optional unless you need exact -line boundaries. +A multi-line pattern is a sequence of ordinary line patterns separated by `\n`. +The match succeeds only when those patterns match neighboring source lines in +the same order. This works for `start`, `end`, and `line` patterns. + +Each part keeps the same glob behavior as a one-line pattern. When a part does +not start with `^`, it may begin anywhere in the source line. When it does not +end with `$`, it may stop before the source line ends. Add `^`, `$`, or both to +the individual part that needs a stricter boundary. + +## Start And End Pattern +```java +@Scenario +@Name("adds two numbers") +``` diff --git a/examples/showcase/embedding/positive/multi-part-fragment-separator.md b/examples/showcase/embedding/positive/multi-part-fragment-separator.md index 6894cad6..99e44e43 100644 --- a/examples/showcase/embedding/positive/multi-part-fragment-separator.md +++ b/examples/showcase/embedding/positive/multi-part-fragment-separator.md @@ -1,14 +1,16 @@ # Multi-Part Fragment Separator -One named fragment can be split across several source regions. +Fragments can be multipart, if fragment with the same name +is started and ended in the file several times. ## How It Works [../../code/java/org/showcase/MultiPartWorkflow.java](../../code/java/org/showcase/MultiPartWorkflow.java) opens and closes the `Workflow` fragment multiple times. Embed mode collects each part in source order and joins the parts with the `separator` configured in -[../embed-code.yml](../embed-code.yml). This is useful when a guide needs the -shape of a class but wants to hide internal lines between selected regions. +[../embed-code.yml](../embed-code.yml). Separator is `...` by default. +This is useful when a guide needs the shape of a class +but wants to hide internal lines between selected fragment parts. ```java From 836517fc5eff0db3f0499d3455c4adab3ea2dc33 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 21:23:23 +0200 Subject: [PATCH 12/22] Improve 'Named Fragment' doc. --- .../positive/multi-part-fragment-separator.md | 10 ++--- .../embedding/positive/named-fragment.md | 37 ++++++++++++++++--- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/examples/showcase/embedding/positive/multi-part-fragment-separator.md b/examples/showcase/embedding/positive/multi-part-fragment-separator.md index 99e44e43..61b4b367 100644 --- a/examples/showcase/embedding/positive/multi-part-fragment-separator.md +++ b/examples/showcase/embedding/positive/multi-part-fragment-separator.md @@ -6,11 +6,11 @@ is started and ended in the file several times. ## How It Works [../../code/java/org/showcase/MultiPartWorkflow.java](../../code/java/org/showcase/MultiPartWorkflow.java) -opens and closes the `Workflow` fragment multiple times. Embed mode collects -each part in source order and joins the parts with the `separator` configured in -[../embed-code.yml](../embed-code.yml). Separator is `...` by default. -This is useful when a guide needs the shape of a class -but wants to hide internal lines between selected fragment parts. +opens and closes the `Workflow` fragment multiple times. +Embed mode collects each part in source order and joins the parts +with the `separator` configured in [../embed-code.yml](../embed-code.yml). + +Separator is `...` by default. ```java diff --git a/examples/showcase/embedding/positive/named-fragment.md b/examples/showcase/embedding/positive/named-fragment.md index 0dba0b96..47534a8c 100644 --- a/examples/showcase/embedding/positive/named-fragment.md +++ b/examples/showcase/embedding/positive/named-fragment.md @@ -1,14 +1,41 @@ # Named Fragment -Use `fragment` when the source file already marks a reusable documentation -region. +Use `fragment` when the source file can mark a stable region that documentation +may reuse. Named fragments are usually easier to maintain than line patterns +when the example has a clear semantic boundary, such as a method, class, or +configuration block. ## How It Works +A named fragment is declared in the source file with `#docfragment "name"` +before the first line to include and `#enddocfragment "name"` after the last +line to include. The marker text can sit inside the comment syntax of the source +language, so Java uses `//`, Kotlin uses `//`, and HTML can use ``. + +The `fragment` value in the embedding instruction must match the source marker +name exactly. During embed mode or check mode, embed-code resolves the named +region, removes the marker lines, normalizes common indentation, and compares or +updates the following code fence. If the fragment name is not present in the +source file, the run reports the missing fragment. + +## Source Markers + [../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) -wraps the `main()` method with matching `#docfragment` and `#enddocfragment` -comments. The instruction resolves the named region, removes the marker lines, -normalizes indentation, and replaces the following fence with the method body. +declares the `main()` fragment like this: + +```java +// #docfragment "main()" +public static void main(String[] args) { + System.out.println(greeting("Ada")); +} +// #enddocfragment "main()" +``` + +## Embedding Instruction + +The `file` attribute points to the source file, and `fragment` selects the +named region inside that file. A named fragment cannot be combined with +`start`, `end`, or `line`; use one source-selection method per instruction. ```java From f3378e1302766bf5ce7a648a67557a83e25bd0f9 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 21:48:41 +0200 Subject: [PATCH 13/22] Improve all embedding docs. --- .../embedding/positive/comment-filtering.md | 12 +++++---- .../embedding/positive/html-showcase.html | 7 +++--- .../embedding/positive/instruction-tag.md | 20 +++++++++++---- .../positive/markdown-fence-shielding.md | 17 +++++++++---- .../embedding/positive/multi-line-pattern.md | 19 +++++++++----- .../positive/multi-part-fragment-separator.md | 25 +++++++++++++------ .../embedding/positive/named-fragment.md | 2 +- .../embedding/positive/named-source-root.md | 15 ++++++++--- .../positive/overlapping-fragments.md | 15 ++++++++--- .../embedding/positive/pattern-escaping.md | 11 ++++++++ .../embedding/positive/source-line-pattern.md | 22 ++++++++++------ .../embedding/positive/start-end-pattern.md | 23 ++++++++++++----- .../embedding/positive/whole-file-source.md | 20 +++++++++------ 13 files changed, 148 insertions(+), 60 deletions(-) diff --git a/examples/showcase/embedding/positive/comment-filtering.md b/examples/showcase/embedding/positive/comment-filtering.md index 1b9f1cd6..e1b43bcc 100644 --- a/examples/showcase/embedding/positive/comment-filtering.md +++ b/examples/showcase/embedding/positive/comment-filtering.md @@ -5,9 +5,10 @@ implementation notes. ## How It Works -The instruction first resolves the source content, then applies comment -filtering before rendering the code fence. If `comments` is omitted, the default -is `all`, so every recognized comment remains in the snippet. +The instruction first resolves the requested source content using `file` plus +any `fragment`, `start`, `end`, or `line` selection. It then applies comment +filtering before comparing or rendering the code fence. If `comments` is +omitted, the default is `all`, so every recognized comment remains in the snippet. Supported modes are: @@ -19,8 +20,9 @@ Supported modes are: - `block` keeps non-documentation block comments, such as `/* */`. Comment support depends on the source file extension. Unknown extensions are -embedded unchanged, and not every supported language distinguishes -documentation, regular, inline, and block comments. See +embedded unchanged. Not every supported language distinguishes documentation, +regular, inline, and block comments, so unsupported categories simply have no +comments to keep. See [Comment filtering](../../../../EMBEDDING.md#comment-filtering) for the full language matrix. diff --git a/examples/showcase/embedding/positive/html-showcase.html b/examples/showcase/embedding/positive/html-showcase.html index 0650e80f..af867f94 100644 --- a/examples/showcase/embedding/positive/html-showcase.html +++ b/examples/showcase/embedding/positive/html-showcase.html @@ -4,12 +4,13 @@

HTML Embedding Showcase

HTML files are scanned when the include patterns allow them. The instruction - below uses the same tag form as the Markdown examples and still needs - a Markdown code fence immediately after it. + below uses the same tag form as the Markdown examples and still needs a + Markdown code fence immediately after it.

The source root comes from embed-code.yml, and - the rendered snippet is checked the same way as a Markdown document. + the rendered snippet is checked the same way as a Markdown document. Embed + mode can update the fenced snippet in an HTML file.

diff --git a/examples/showcase/embedding/positive/instruction-tag.md b/examples/showcase/embedding/positive/instruction-tag.md index ca75d49e..71b09a4d 100644 --- a/examples/showcase/embedding/positive/instruction-tag.md +++ b/examples/showcase/embedding/positive/instruction-tag.md @@ -1,10 +1,16 @@ # Instruction Tag -Instructions may be self-closing or paired. The self-closing form is supported, -but it is preferred to use paired tags because some Markdown renderers display the -XML-style self-closing tag awkwardly. +An embedding instruction is an XML-like tag placed immediately before the code +fence that embed-code should manage. The tag contains source-selection +attributes such as `file`, `fragment`, `line`, etc. -### Paired tag version +The following Markdown fences keeps the language label used by renderers for +syntax highlighting. + +## Paired Tag + +The paired form is preferred in Markdown because it is displayed consistently +by most renderers. ```java @@ -13,7 +19,11 @@ public static void main(String[] args) { } ``` -### Self-closing tag version +## Self-Closing Tag + +The self-closing form is supported and resolves the same source content, +but it is preferred to use paired tags elsewhere because they tend to look better +in Markdown previews. ```java diff --git a/examples/showcase/embedding/positive/markdown-fence-shielding.md b/examples/showcase/embedding/positive/markdown-fence-shielding.md index a3afb2b7..4638674b 100644 --- a/examples/showcase/embedding/positive/markdown-fence-shielding.md +++ b/examples/showcase/embedding/positive/markdown-fence-shielding.md @@ -1,13 +1,20 @@ # Instructions Inside Markdown Fences -Documentation sometimes needs to show an instruction as plain text. +Use a normal Markdown fence when documentation needs to show an embedding +instruction as plain text. This lets guides explain the syntax without causing +the example instruction to run. ## How It Works -The instruction-looking text below is inside an ordinary Markdown fence, so it -is preserved as documentation content. The parser tracks code-fence state before -looking for active instructions, which prevents examples from accidentally -running while they are being explained. +The parser tracks ordinary code fences before it looks for active +`` instructions. Instruction-looking text inside a fence is +therefore preserved as documentation content, not treated as a real instruction. + +An active instruction must appear outside a fence and must be followed by its +own managed code fence. The nested fence in this example is only part of the +displayed Markdown snippet. + +## Shielded Example ````markdown diff --git a/examples/showcase/embedding/positive/multi-line-pattern.md b/examples/showcase/embedding/positive/multi-line-pattern.md index bb8ef89e..eab3dfb0 100644 --- a/examples/showcase/embedding/positive/multi-line-pattern.md +++ b/examples/showcase/embedding/positive/multi-line-pattern.md @@ -1,6 +1,7 @@ # Multi-Line Patterns -Use `\n` inside a pattern when one source line is not specific enough. +Use `\n` inside a pattern when one source line is not specific enough to select +the right source range. ## How It Works @@ -8,13 +9,19 @@ A multi-line pattern is a sequence of ordinary line patterns separated by `\n`. The match succeeds only when those patterns match neighboring source lines in the same order. This works for `start`, `end`, and `line` patterns. -Each part keeps the same glob behavior as a one-line pattern. When a part does -not start with `^`, it may begin anywhere in the source line. When it does not -end with `$`, it may stop before the source line ends. Add `^`, `$`, or both to -the individual part that needs a stricter boundary. +Spaces around `\n` are ignored, so `Scenario \n adds two numbers` is treated as +two line patterns: `Scenario` and `adds two numbers`. Each part keeps the same +glob behavior as a one-line pattern. When a part does not start with `^`, it may +begin anywhere in the source line. When it does not end with `$`, it may stop +before the source line ends. Add `^`, `$`, or both to the individual part that +needs a stricter boundary. ## Start And End Pattern +Here the `start` value matches the `@Scenario` line followed immediately by the +display-name line. The `end` value does the same for the assertion line and the +closing brace. + ```java diff --git a/examples/showcase/embedding/positive/named-fragment.md b/examples/showcase/embedding/positive/named-fragment.md index 47534a8c..dbcc4e4f 100644 --- a/examples/showcase/embedding/positive/named-fragment.md +++ b/examples/showcase/embedding/positive/named-fragment.md @@ -1,7 +1,7 @@ # Named Fragment Use `fragment` when the source file can mark a stable region that documentation -may reuse. Named fragments are usually easier to maintain than line patterns +should reuse. Named fragments are usually easier to maintain than line patterns when the example has a clear semantic boundary, such as a method, class, or configuration block. diff --git a/examples/showcase/embedding/positive/named-source-root.md b/examples/showcase/embedding/positive/named-source-root.md index f703b725..febeaee3 100644 --- a/examples/showcase/embedding/positive/named-source-root.md +++ b/examples/showcase/embedding/positive/named-source-root.md @@ -1,13 +1,20 @@ # Named Source Roots -Named roots let one documentation set embed source from several directories. +Use named source roots when one documentation set needs snippets from several +source trees. Names keep instructions explicit and avoid relying on whichever +configured root happens to contain a matching relative path. ## How It Works +In config file, each entry in `code-path` can have a `name` and a `path`. +When an instruction starts its `file` value with `$name/`, embed-code selects +only that named root and then resolves the remaining relative path inside it. + [../embed-code.yml](../embed-code.yml) defines `java`, `kotlin`, and `text` -source roots. The `$kotlin` prefix chooses the Kotlin root before resolving -`org/showcase/KotlinGreeting.kt`. The same docs root can therefore mix Java, -Kotlin, and text snippets without changing the command line. +source roots. The `$kotlin` prefix below chooses the Kotlin root before +resolving `org/showcase/KotlinGreeting.kt`. + +## Embedding Instruction ```kotlin diff --git a/examples/showcase/embedding/positive/overlapping-fragments.md b/examples/showcase/embedding/positive/overlapping-fragments.md index a5e0ebeb..62765415 100644 --- a/examples/showcase/embedding/positive/overlapping-fragments.md +++ b/examples/showcase/embedding/positive/overlapping-fragments.md @@ -1,14 +1,21 @@ # Overlapping Fragments -Several fragments can open or close on the same marker line. +Use overlapping fragments when different documentation examples need to share +some source lines but hide different details. One marker line can name several +fragments, and each named fragment is resolved independently. ## How It Works +A marker can open or close multiple fragments by listing several quoted names: +`#docfragment "Class wrapper", "Greeting method"`. + +## Embedding Instruction + [../../code/java/org/showcase/OverlappingFragments.java](../../code/java/org/showcase/OverlappingFragments.java) uses marker lines that name both `Class wrapper` and `Greeting method`. The -instruction asks only for `Greeting method`, so embed mode keeps the shared -class wrapper, skips unrelated method details, and renders the selected method -inside the wrapper. +instruction asks only for `Greeting method`, so the rendered snippet keeps the +shared class wrapper and the greeting method while replacing skipped details +with the configured separator. ```java diff --git a/examples/showcase/embedding/positive/pattern-escaping.md b/examples/showcase/embedding/positive/pattern-escaping.md index 8c54a4fb..adf9e068 100644 --- a/examples/showcase/embedding/positive/pattern-escaping.md +++ b/examples/showcase/embedding/positive/pattern-escaping.md @@ -3,6 +3,17 @@ Pattern escaping distinguishes glob syntax from source text that happens to use the same characters. +## How It Works + +Patterns use glob control characters, so `*`, `?`, and character classes have +special meaning unless they are escaped with a backslash. The anchors `^` and +`$` are special only at the beginning and end of a pattern part. Use `^^` at the +beginning to match a literal caret and `$$` at the end to match a literal dollar +sign. + +The sequence `\n` separates consecutive pattern lines. Use `\\n` when the source +line contains the literal characters `\n`. + ## Literal Asterisk The pattern `Use \* to multiply` treats `*` as source text instead of a diff --git a/examples/showcase/embedding/positive/source-line-pattern.md b/examples/showcase/embedding/positive/source-line-pattern.md index 53abf674..21e4ca07 100644 --- a/examples/showcase/embedding/positive/source-line-pattern.md +++ b/examples/showcase/embedding/positive/source-line-pattern.md @@ -1,15 +1,23 @@ -# One Line From A Showcase Source +# Source Line Pattern Use `line` when the documentation needs one source line instead of a whole -fragment. +fragment or range. ## How It Works -The pattern is matched against -[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java). -The opening quote is escaped because instruction attributes are parsed as XML, -and the trailing `*` lets the pattern match the rest of the return expression. -Only the first matching source line is rendered into the fence. +The `line` attribute uses the same glob-style pattern syntax as `start` and`end`. +By default, embed-code behaves as if `*` exists at the beginning and end +of the pattern, so `Hello` can match any source line that contains `Hello`. +Use `^` or `$` when the match must start or end at a line boundary. + +Only the first matching source line is rendered into the fence. A `line` pattern +cannot be combined with `fragment`, `start`, or `end`. + +## Embedding Instruction + +The instruction below searches +[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +and renders the first line that contains `Hello`. ```java diff --git a/examples/showcase/embedding/positive/start-end-pattern.md b/examples/showcase/embedding/positive/start-end-pattern.md index ed316c79..04682430 100644 --- a/examples/showcase/embedding/positive/start-end-pattern.md +++ b/examples/showcase/embedding/positive/start-end-pattern.md @@ -1,14 +1,25 @@ # Start And End Patterns -Use `start` and `end` when the source does not contain named fragment markers. +Use `start` and `end` patterns to select the code snippet. ## How It Works -The `start` pattern finds the first line in -[../../code/java/org/showcase/PatternSamples.java](../../code/java/org/showcase/PatternSamples.java) -that contains `@Scenario`. The `end` pattern then searches after that start -match and stops at the first line that is exactly four spaces followed by `}`. -Both boundary lines are included in the rendered snippet. +`start` and `end` select an inclusive source range. Embed-code first searches +for the `start` pattern, then searches for the `end` pattern after that match. +Both matched boundary lines are included in the rendered snippet. + +By default, embed-code behaves as if `*` exists at the beginning and end +of the pattern, so `Hello` can match any source line that contains `Hello`. +Use `^` or `$` when the match must start or end at a line boundary. + +If `start` is omitted, the range starts at the beginning of the file. +If `end` is omitted, it continues to the end of the file. + +## Embedding Instruction + +The instruction below finds the first `@Scenario` in +[../../code/java/org/showcase/PatternSamples.java](../../code/java/org/showcase/PatternSamples.java). +It then stops at the next line that is exactly four spaces followed by `}`. ```java From 24f5f2dd6d5c85907336cc6f48f86ebc586dc183 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 10 Jun 2026 21:56:10 +0200 Subject: [PATCH 14/22] Improve showcase test. --- examples/showcase/showcase_test.go | 277 ++++++++++++++--------------- 1 file changed, 131 insertions(+), 146 deletions(-) diff --git a/examples/showcase/showcase_test.go b/examples/showcase/showcase_test.go index 742caa8b..2133368d 100644 --- a/examples/showcase/showcase_test.go +++ b/examples/showcase/showcase_test.go @@ -28,58 +28,108 @@ import ( "runtime" "strings" "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -// TestShowcasePositiveFlow verifies the showcase docs can be checked, detected as stale, -// repaired with embed mode, and checked again without changing repository files. -func TestShowcasePositiveFlow(t *testing.T) { - repoRoot := findRepoRoot(t) - docsRoot := copyShowcaseDocs(t, repoRoot, filepath.Join("embedding", "positive")) - configPath := writeShowcaseConfig(t, repoRoot, docsRoot) +// TestShowcase runs the showcase example suite. +func TestShowcase(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Showcase Suite") +} + +var _ = Describe("Showcase", func() { + var repoRoot string - checkOutput, err := runEmbedCode(t, repoRoot, "check", configPath) - if err != nil { - t.Fatalf("expected positive showcase check to pass:\n%s", checkOutput) - } + BeforeEach(func() { + repoRoot = findRepoRoot() + }) - staleDoc := filepath.Join(docsRoot, "whole-file-source.md") - replaceInFile(t, staleDoc, "package org.showcase;", "package stale.showcase;") + Describe("embedding examples", func() { + It("should check, detect staleness, embed, and recheck positive examples", func() { + docsRoot := copyShowcaseDocs(repoRoot, filepath.Join("embedding", "positive")) + configPath := writeShowcaseConfig(repoRoot, docsRoot) - staleOutput, err := runEmbedCode(t, repoRoot, "check", configPath) - if err == nil { - t.Fatalf("expected stale showcase check to fail:\n%s", staleOutput) - } - assertOutputContains(t, staleOutput, "File to update:") - assertOutputContains(t, staleOutput, "whole-file-source.md") + checkOutput, err := runEmbedCode(repoRoot, "check", configPath) + Expect(err).ShouldNot(HaveOccurred(), "expected positive showcase check to pass:\n%s", checkOutput) - embedOutput, err := runEmbedCode(t, repoRoot, "embed", configPath) - if err != nil { - t.Fatalf("expected positive showcase embed to repair stale doc:\n%s", embedOutput) - } - assertOutputContains(t, embedOutput, "Embedding process finished.") + staleDoc := filepath.Join(docsRoot, "whole-file-source.md") + replaceInFile(staleDoc, "package org.showcase;", "package stale.showcase;") - finalOutput, err := runEmbedCode(t, repoRoot, "check", configPath) - if err != nil { - t.Fatalf("expected positive showcase check to pass after embed:\n%s", finalOutput) - } + staleOutput, err := runEmbedCode(repoRoot, "check", configPath) + Expect(err).Should(HaveOccurred(), "expected stale showcase check to fail:\n%s", staleOutput) + Expect(staleOutput).Should(ContainSubstring("File to update:")) + Expect(staleOutput).Should(ContainSubstring("whole-file-source.md")) + + embedOutput, err := runEmbedCode(repoRoot, "embed", configPath) + Expect(err).ShouldNot(HaveOccurred(), "expected positive showcase embed to repair stale doc:\n%s", embedOutput) + Expect(embedOutput).Should(ContainSubstring("Embedding process finished.")) + + finalOutput, err := runEmbedCode(repoRoot, "check", configPath) + Expect(err).ShouldNot(HaveOccurred(), "expected positive showcase check to pass after embed:\n%s", finalOutput) + }) + + Describe("negative examples", func() { + for _, tc := range negativeShowcaseCases() { + tc := tc + + It("should report "+tc.name, func() { + docsRoot := copyShowcaseDocs(repoRoot, filepath.Join("embedding", "negative", "docs")) + configPath := writeSingleDocConfig( + docsRoot, + []namedSource{javaSource(repoRoot)}, + tc.doc, + ) + + output, err := runEmbedCode(repoRoot, "check", configPath) + Expect(err).Should(HaveOccurred(), "expected negative scenario to fail:\n%s", output) + for _, expected := range tc.expected { + Expect(output).Should(ContainSubstring(expected)) + } + }) + } + }) + }) + + Describe("configuration examples", func() { + for _, config := range []string{ + "single-source.yml", + "named-sources.yml", + "include-exclude.yml", + "multiple-embeddings.yml", + } { + config := config + + It("should check "+config, func() { + configPath := filepath.Join("examples", "showcase", "configuration", config) + + output, err := runEmbedCode(repoRoot, "check", configPath) + Expect(err).ShouldNot(HaveOccurred(), "expected configuration example to pass:\n%s", output) + }) + } + }) +}) + +// negativeShowcaseCase describes one intentionally broken showcase document. +type negativeShowcaseCase struct { + name string + doc string + expected []string } -// TestShowcaseNegativeScenarios verifies each negative document fails with its expected reason. -func TestShowcaseNegativeScenarios(t *testing.T) { - repoRoot := findRepoRoot(t) +// namedSource is the named code source path. +type namedSource struct { + name string + path string +} - cases := []struct { - name string - doc string - sources []namedSource - expected []string - }{ +// negativeShowcaseCases returns the expected failures for the broken embedding examples. +func negativeShowcaseCases() []negativeShowcaseCase { + return []negativeShowcaseCase{ { name: "missing source", doc: "missing-source.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "code file `$java/org/showcase/DoesNotExist.java", "not found", @@ -88,9 +138,6 @@ func TestShowcaseNegativeScenarios(t *testing.T) { { name: "missing fragment", doc: "missing-fragment.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "fragment `does not exist`", "not found", @@ -99,9 +146,6 @@ func TestShowcaseNegativeScenarios(t *testing.T) { { name: "missing pattern", doc: "missing-pattern.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "matches the line pattern", "doesNotExistPattern", @@ -110,9 +154,6 @@ func TestShowcaseNegativeScenarios(t *testing.T) { { name: "invalid attributes", doc: "invalid-attributes.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "must NOT specify both a fragment name and start/end/line patterns", }, @@ -120,9 +161,6 @@ func TestShowcaseNegativeScenarios(t *testing.T) { { name: "missing code fence", doc: "missing-code-fence.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "expected a markdown code fence after the embedding instruction", }, @@ -130,9 +168,6 @@ func TestShowcaseNegativeScenarios(t *testing.T) { { name: "unclosed code fence", doc: "unclosed-code-fence.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "the markdown code fence after the embedding instruction is not closed", }, @@ -140,9 +175,6 @@ func TestShowcaseNegativeScenarios(t *testing.T) { { name: "stale snippet", doc: "stale-snippet.md", - sources: []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, - }, expected: []string{ "File to update:", "stale-snippet.md", @@ -150,76 +182,44 @@ func TestShowcaseNegativeScenarios(t *testing.T) { }, }, } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - docsRoot := copyShowcaseDocs(t, repoRoot, filepath.Join("embedding", "negative", "docs")) - configPath := writeSingleDocConfig(t, docsRoot, tc.sources, tc.doc) - - output, err := runEmbedCode(t, repoRoot, "check", configPath) - if err == nil { - t.Fatalf("expected negative scenario to fail:\n%s", output) - } - for _, expected := range tc.expected { - assertOutputContains(t, output, expected) - } - }) - } } -// TestShowcaseConfigurationExamples verifies the runnable configuration examples. -func TestShowcaseConfigurationExamples(t *testing.T) { - repoRoot := findRepoRoot(t) - - configs := []string{ - "single-source.yml", - "named-sources.yml", - "include-exclude.yml", - "multiple-embeddings.yml", - } - - for _, config := range configs { - t.Run(config, func(t *testing.T) { - configPath := filepath.Join("examples", "showcase", "configuration", config) - output, err := runEmbedCode(t, repoRoot, "check", configPath) - if err != nil { - t.Fatalf("expected configuration example to pass:\n%s", output) - } - }) +// javaSource returns the Java showcase source root. +func javaSource(repoRoot string) namedSource { + return namedSource{ + name: "java", + path: filepath.Join(repoRoot, "examples", "showcase", "code", "java"), } } -type namedSource struct { - name string - path string -} - // findRepoRoot returns the repository root by walking up from this test file. -func findRepoRoot(t *testing.T) string { - t.Helper() +func findRepoRoot() string { + GinkgoHelper() _, filePath, _, ok := runtime.Caller(0) - if !ok { - t.Fatal("could not locate showcase test file") - } + Expect(ok).Should(BeTrue(), "could not locate showcase test file") return filepath.Clean(filepath.Join(filepath.Dir(filePath), "..", "..")) } // copyShowcaseDocs copies one showcase documentation folder to a temporary test directory. -func copyShowcaseDocs(t *testing.T, repoRoot string, relativeSource string) string { - t.Helper() +func copyShowcaseDocs(repoRoot string, relativeSource string) string { + GinkgoHelper() sourceRoot := filepath.Join(repoRoot, "examples", "showcase", relativeSource) - targetRoot := filepath.Join(t.TempDir(), "docs") - copyDir(t, sourceRoot, targetRoot) + tempRoot, err := os.MkdirTemp("", "embed-code-showcase-docs-*") + Expect(err).ShouldNot(HaveOccurred()) + DeferCleanup(os.RemoveAll, tempRoot) + + targetRoot := filepath.Join(tempRoot, "docs") + copyDir(sourceRoot, targetRoot) return targetRoot } // copyDir recursively copies a directory tree while preserving regular file permissions. -func copyDir(t *testing.T, sourceRoot string, targetRoot string) { - t.Helper() +func copyDir(sourceRoot string, targetRoot string) { + GinkgoHelper() err := filepath.WalkDir(sourceRoot, func(path string, entry os.DirEntry, walkErr error) error { if walkErr != nil { @@ -249,17 +249,15 @@ func copyDir(t *testing.T, sourceRoot string, targetRoot string) { return os.WriteFile(targetPath, data, info.Mode()) }) - if err != nil { - t.Fatalf("failed to copy showcase docs: %v", err) - } + Expect(err).ShouldNot(HaveOccurred(), "failed to copy showcase docs") } // writeShowcaseConfig creates a temp config that points at copied positive docs. -func writeShowcaseConfig(t *testing.T, repoRoot string, docsRoot string) string { - t.Helper() +func writeShowcaseConfig(repoRoot string, docsRoot string) string { + GinkgoHelper() - return writeConfig(t, docsRoot, []namedSource{ - {name: "java", path: filepath.Join(repoRoot, "examples", "showcase", "code", "java")}, + return writeConfig(docsRoot, []namedSource{ + javaSource(repoRoot), {name: "kotlin", path: filepath.Join(repoRoot, "examples", "showcase", "code", "kotlin")}, {name: "text", path: filepath.Join(repoRoot, "examples", "showcase", "code", "text")}, }, []string{"**/*.md", "**/*.html"}) @@ -267,24 +265,22 @@ func writeShowcaseConfig(t *testing.T, repoRoot string, docsRoot string) string // writeSingleDocConfig creates a temp config for one negative showcase document. func writeSingleDocConfig( - t *testing.T, docsRoot string, sources []namedSource, docInclude string, ) string { - t.Helper() + GinkgoHelper() - return writeConfig(t, docsRoot, sources, []string{docInclude}) + return writeConfig(docsRoot, sources, []string{docInclude}) } // writeConfig writes a YAML config with absolute source and documentation paths. func writeConfig( - t *testing.T, docsRoot string, sources []namedSource, includes []string, ) string { - t.Helper() + GinkgoHelper() var builder strings.Builder builder.WriteString("code-path:\n") @@ -299,19 +295,22 @@ func writeConfig( } builder.WriteString("separator: \"// ...\"\n") - configPath := filepath.Join(t.TempDir(), "embed-code.yml") - if err := os.WriteFile(configPath, []byte(builder.String()), 0o644); err != nil { - t.Fatalf("failed to write temp config: %v", err) - } + tempRoot, err := os.MkdirTemp("", "embed-code-showcase-config-*") + Expect(err).ShouldNot(HaveOccurred()) + DeferCleanup(os.RemoveAll, tempRoot) + + configPath := filepath.Join(tempRoot, "embed-code.yml") + Expect(os.WriteFile(configPath, []byte(builder.String()), 0o644)). + Should(Succeed(), "failed to write temp config") return configPath } // runEmbedCode executes the CLI through `go run` and returns combined output. -func runEmbedCode(t *testing.T, repoRoot string, mode string, configPath string) (string, error) { - t.Helper() +func runEmbedCode(repoRoot string, mode string, configPath string) (string, error) { + GinkgoHelper() - cmd := exec.Command("go", "run", "./main.go", "-mode", mode, "-config-path", configPath) + cmd := exec.Command("go", "run", "./main.go", "-mode="+mode, "-config-path="+configPath) cmd.Dir = repoRoot output, err := cmd.CombinedOutput() @@ -319,28 +318,14 @@ func runEmbedCode(t *testing.T, repoRoot string, mode string, configPath string) } // replaceInFile replaces one expected substring in a copied documentation file. -func replaceInFile(t *testing.T, path string, oldText string, newText string) { - t.Helper() +func replaceInFile(path string, oldText string, newText string) { + GinkgoHelper() data, err := os.ReadFile(path) - if err != nil { - t.Fatalf("failed to read %s: %v", path, err) - } + Expect(err).ShouldNot(HaveOccurred(), "failed to read %s", path) content := string(data) - if !strings.Contains(content, oldText) { - t.Fatalf("expected %s to contain %q", path, oldText) - } + Expect(content).Should(ContainSubstring(oldText), "expected %s to contain %q", path, oldText) content = strings.Replace(content, oldText, newText, 1) - if err = os.WriteFile(path, []byte(content), 0o644); err != nil { - t.Fatalf("failed to write %s: %v", path, err) - } -} - -// assertOutputContains fails the test when a command output does not include a substring. -func assertOutputContains(t *testing.T, output string, expected string) { - t.Helper() - - if !strings.Contains(output, expected) { - t.Fatalf("expected output to contain %q:\n%s", expected, output) - } + Expect(os.WriteFile(path, []byte(content), 0o644)). + Should(Succeed(), "failed to write %s", path) } From 4a19e6ba74ceadadc2cb558f3cc6b8e2f5d45442 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 16 Jun 2026 20:51:51 +0200 Subject: [PATCH 15/22] Add copyrights. --- .../code/java/org/showcase/CommentModes.java | 20 ++++++ .../code/java/org/showcase/Greeting.java | 20 ++++++ .../java/org/showcase/MultiPartWorkflow.java | 20 ++++++ .../org/showcase/OverlappingFragments.java | 20 ++++++ .../java/org/showcase/PatternSamples.java | 20 ++++++ .../kotlin/org/showcase/KotlinGreeting.kt | 20 ++++++ .../embedding/positive/comment-filtering.md | 63 +++++++++++++++++++ .../embedding/positive/html-showcase.html | 19 ++++++ .../embedding/positive/whole-file-source.md | 20 ++++++ 9 files changed, 222 insertions(+) diff --git a/examples/showcase/code/java/org/showcase/CommentModes.java b/examples/showcase/code/java/org/showcase/CommentModes.java index 3cbf65fd..600856c0 100644 --- a/examples/showcase/code/java/org/showcase/CommentModes.java +++ b/examples/showcase/code/java/org/showcase/CommentModes.java @@ -1,3 +1,23 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; /** diff --git a/examples/showcase/code/java/org/showcase/Greeting.java b/examples/showcase/code/java/org/showcase/Greeting.java index 60ab7c9c..9197a622 100644 --- a/examples/showcase/code/java/org/showcase/Greeting.java +++ b/examples/showcase/code/java/org/showcase/Greeting.java @@ -1,3 +1,23 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; // #docfragment "Greeter class" diff --git a/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java b/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java index 71c4ba34..a58fcfc6 100644 --- a/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java +++ b/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java @@ -1,3 +1,23 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; // #docfragment "Workflow" diff --git a/examples/showcase/code/java/org/showcase/OverlappingFragments.java b/examples/showcase/code/java/org/showcase/OverlappingFragments.java index 602ec4a7..ca8f845f 100644 --- a/examples/showcase/code/java/org/showcase/OverlappingFragments.java +++ b/examples/showcase/code/java/org/showcase/OverlappingFragments.java @@ -1,3 +1,23 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; // #docfragment "Class wrapper", "Greeting method" diff --git a/examples/showcase/code/java/org/showcase/PatternSamples.java b/examples/showcase/code/java/org/showcase/PatternSamples.java index 8d429f8a..b4315df2 100644 --- a/examples/showcase/code/java/org/showcase/PatternSamples.java +++ b/examples/showcase/code/java/org/showcase/PatternSamples.java @@ -1,3 +1,23 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; class PatternSamples { diff --git a/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt b/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt index 5b06ade2..17e41d3b 100644 --- a/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt +++ b/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt @@ -1,3 +1,23 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase object KotlinGreeting { diff --git a/examples/showcase/embedding/positive/comment-filtering.md b/examples/showcase/embedding/positive/comment-filtering.md index e1b43bcc..fd5e12c0 100644 --- a/examples/showcase/embedding/positive/comment-filtering.md +++ b/examples/showcase/embedding/positive/comment-filtering.md @@ -36,6 +36,26 @@ produce the same result because `all` is the default. ```java +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; /** @@ -58,6 +78,7 @@ public interface CommentModes { ```java + package org.showcase; public interface CommentModes { @@ -75,6 +96,7 @@ not a real comment. ```java + package org.showcase; /** @@ -94,6 +116,26 @@ documentation comments. ```java +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; public interface CommentModes { @@ -114,6 +156,7 @@ documentation comments. ```java + package org.showcase; public interface CommentModes { @@ -131,6 +174,26 @@ documentation comments. ```java +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; public interface CommentModes { diff --git a/examples/showcase/embedding/positive/html-showcase.html b/examples/showcase/embedding/positive/html-showcase.html index af867f94..b8f1b1f4 100644 --- a/examples/showcase/embedding/positive/html-showcase.html +++ b/examples/showcase/embedding/positive/html-showcase.html @@ -1,4 +1,23 @@ +

HTML Embedding Showcase

diff --git a/examples/showcase/embedding/positive/whole-file-source.md b/examples/showcase/embedding/positive/whole-file-source.md index 9c421086..5d6c47e1 100644 --- a/examples/showcase/embedding/positive/whole-file-source.md +++ b/examples/showcase/embedding/positive/whole-file-source.md @@ -19,6 +19,26 @@ into the following code fence, without the embed-code-related instructions. ```java +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package org.showcase; public final class Greeting { From 929de681c7fcd4f4ff0fc30a381b38626244710d Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 16 Jun 2026 20:55:43 +0200 Subject: [PATCH 16/22] Remove `examples` folder from path. --- EMBEDDING.md | 2 +- README.md | 2 +- .../configuration/include-exclude.yml | 6 --- .../configuration/multiple-embeddings.yml | 13 ------ .../showcase/configuration/named-sources.yml | 10 ----- .../showcase/configuration/single-source.yml | 2 - examples/showcase/embedding/embed-code.yml | 12 ------ .../showcase/embedding/negative/stale.yml | 6 --- {examples/showcase => showcase}/README.md | 16 ++++---- .../code/java/org/showcase/CommentModes.java | 0 .../code/java/org/showcase/Greeting.java | 0 .../java/org/showcase/MultiPartWorkflow.java | 0 .../org/showcase/OverlappingFragments.java | 0 .../java/org/showcase/PatternSamples.java | 0 .../kotlin/org/showcase/KotlinGreeting.kt | 0 .../code/text/glob-patterns.txt | 0 .../configuration/README.md | 40 +++++++++---------- .../docs/include-exclude/excluded.md | 0 .../docs/include-exclude/included.md | 0 .../docs/multiple/java/greeting.md | 0 .../docs/multiple/kotlin/greeting.md | 0 .../docs/named-sources/java-greeting.md | 0 .../docs/named-sources/kotlin-greeting.md | 0 .../docs/named-sources/text-line.md | 0 .../docs/single-source/greeting.md | 2 +- showcase/configuration/include-exclude.yml | 6 +++ .../configuration/multiple-embeddings.yml | 13 ++++++ showcase/configuration/named-sources.yml | 10 +++++ showcase/configuration/single-source.yml | 2 + .../showcase => showcase}/embedding/README.md | 6 +-- showcase/embedding/embed-code.yml | 12 ++++++ .../negative/docs/invalid-attributes.md | 0 .../negative/docs/missing-code-fence.md | 0 .../negative/docs/missing-fragment.md | 0 .../negative/docs/missing-pattern.md | 0 .../embedding/negative/docs/missing-source.md | 0 .../embedding/negative/docs/stale-snippet.md | 0 .../negative/docs/unclosed-code-fence.md | 0 .../embedding/negative/processing-errors.yml | 4 +- showcase/embedding/negative/stale.yml | 6 +++ .../embedding/positive/comment-filtering.md | 0 .../embedding/positive/html-showcase.html | 0 .../embedding/positive/instruction-tag.md | 0 .../positive/markdown-fence-shielding.md | 0 .../embedding/positive/multi-line-pattern.md | 0 .../positive/multi-part-fragment-separator.md | 0 .../embedding/positive/named-fragment.md | 0 .../embedding/positive/named-source-root.md | 0 .../positive/overlapping-fragments.md | 0 .../embedding/positive/pattern-escaping.md | 0 .../embedding/positive/source-line-pattern.md | 0 .../embedding/positive/start-end-pattern.md | 0 .../embedding/positive/whole-file-source.md | 0 .../showcase => showcase}/showcase_test.go | 12 +++--- 54 files changed, 91 insertions(+), 91 deletions(-) delete mode 100644 examples/showcase/configuration/include-exclude.yml delete mode 100644 examples/showcase/configuration/multiple-embeddings.yml delete mode 100644 examples/showcase/configuration/named-sources.yml delete mode 100644 examples/showcase/configuration/single-source.yml delete mode 100644 examples/showcase/embedding/embed-code.yml delete mode 100644 examples/showcase/embedding/negative/stale.yml rename {examples/showcase => showcase}/README.md (50%) rename {examples/showcase => showcase}/code/java/org/showcase/CommentModes.java (100%) rename {examples/showcase => showcase}/code/java/org/showcase/Greeting.java (100%) rename {examples/showcase => showcase}/code/java/org/showcase/MultiPartWorkflow.java (100%) rename {examples/showcase => showcase}/code/java/org/showcase/OverlappingFragments.java (100%) rename {examples/showcase => showcase}/code/java/org/showcase/PatternSamples.java (100%) rename {examples/showcase => showcase}/code/kotlin/org/showcase/KotlinGreeting.kt (100%) rename {examples/showcase => showcase}/code/text/glob-patterns.txt (100%) rename {examples/showcase => showcase}/configuration/README.md (64%) rename {examples/showcase => showcase}/configuration/docs/include-exclude/excluded.md (100%) rename {examples/showcase => showcase}/configuration/docs/include-exclude/included.md (100%) rename {examples/showcase => showcase}/configuration/docs/multiple/java/greeting.md (100%) rename {examples/showcase => showcase}/configuration/docs/multiple/kotlin/greeting.md (100%) rename {examples/showcase => showcase}/configuration/docs/named-sources/java-greeting.md (100%) rename {examples/showcase => showcase}/configuration/docs/named-sources/kotlin-greeting.md (100%) rename {examples/showcase => showcase}/configuration/docs/named-sources/text-line.md (100%) rename {examples/showcase => showcase}/configuration/docs/single-source/greeting.md (86%) create mode 100644 showcase/configuration/include-exclude.yml create mode 100644 showcase/configuration/multiple-embeddings.yml create mode 100644 showcase/configuration/named-sources.yml create mode 100644 showcase/configuration/single-source.yml rename {examples/showcase => showcase}/embedding/README.md (88%) create mode 100644 showcase/embedding/embed-code.yml rename {examples/showcase => showcase}/embedding/negative/docs/invalid-attributes.md (100%) rename {examples/showcase => showcase}/embedding/negative/docs/missing-code-fence.md (100%) rename {examples/showcase => showcase}/embedding/negative/docs/missing-fragment.md (100%) rename {examples/showcase => showcase}/embedding/negative/docs/missing-pattern.md (100%) rename {examples/showcase => showcase}/embedding/negative/docs/missing-source.md (100%) rename {examples/showcase => showcase}/embedding/negative/docs/stale-snippet.md (100%) rename {examples/showcase => showcase}/embedding/negative/docs/unclosed-code-fence.md (100%) rename {examples/showcase => showcase}/embedding/negative/processing-errors.yml (70%) create mode 100644 showcase/embedding/negative/stale.yml rename {examples/showcase => showcase}/embedding/positive/comment-filtering.md (100%) rename {examples/showcase => showcase}/embedding/positive/html-showcase.html (100%) rename {examples/showcase => showcase}/embedding/positive/instruction-tag.md (100%) rename {examples/showcase => showcase}/embedding/positive/markdown-fence-shielding.md (100%) rename {examples/showcase => showcase}/embedding/positive/multi-line-pattern.md (100%) rename {examples/showcase => showcase}/embedding/positive/multi-part-fragment-separator.md (100%) rename {examples/showcase => showcase}/embedding/positive/named-fragment.md (100%) rename {examples/showcase => showcase}/embedding/positive/named-source-root.md (100%) rename {examples/showcase => showcase}/embedding/positive/overlapping-fragments.md (100%) rename {examples/showcase => showcase}/embedding/positive/pattern-escaping.md (100%) rename {examples/showcase => showcase}/embedding/positive/source-line-pattern.md (100%) rename {examples/showcase => showcase}/embedding/positive/start-end-pattern.md (100%) rename {examples/showcase => showcase}/embedding/positive/whole-file-source.md (100%) rename {examples/showcase => showcase}/showcase_test.go (95%) diff --git a/EMBEDDING.md b/EMBEDDING.md index f4d7edcb..2c372152 100644 --- a/EMBEDDING.md +++ b/EMBEDDING.md @@ -3,7 +3,7 @@ The `embed-code` utility uses a custom `` tag to insert code snippets from source files into Markdown documentation. For executable examples of the embedding features described here, see the -[embed-code showcase](examples/showcase/README.md). The showcase uses paired +[embed-code showcase](showcase/README.md). The showcase uses paired instruction tags and dedicated source fixtures so it can double as a guide and an opt-in end-to-end test. diff --git a/README.md b/README.md index 7265c99c..4b9e7361 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This project is the implementation of `embed-code` utility written in Go. For the details of the usage in the documentation and the code, please refer to the [EMBEDDING.md](EMBEDDING.md). For a runnable guide with positive examples, negative examples, and YAML -configuration shapes, see the [embed-code showcase](examples/showcase/README.md). +configuration shapes, see the [embed-code showcase](showcase/README.md). The showcase is an opt-in end-to-end test and is not part of the normal `go test ./...` flow. diff --git a/examples/showcase/configuration/include-exclude.yml b/examples/showcase/configuration/include-exclude.yml deleted file mode 100644 index d165aaf1..00000000 --- a/examples/showcase/configuration/include-exclude.yml +++ /dev/null @@ -1,6 +0,0 @@ -code-path: examples/showcase/code/java -docs-path: examples/showcase/configuration/docs/include-exclude -doc-includes: - - "**/*.md" -doc-excludes: - - excluded.md diff --git a/examples/showcase/configuration/multiple-embeddings.yml b/examples/showcase/configuration/multiple-embeddings.yml deleted file mode 100644 index 0316823f..00000000 --- a/examples/showcase/configuration/multiple-embeddings.yml +++ /dev/null @@ -1,13 +0,0 @@ -embeddings: - - name: java-guide - code-path: examples/showcase/code/java - docs-path: examples/showcase/configuration/docs/multiple/java - doc-includes: - - "**/*.md" - - name: kotlin-guide - code-path: - - name: kotlin - path: examples/showcase/code/kotlin - docs-path: examples/showcase/configuration/docs/multiple/kotlin - doc-includes: - - "**/*.md" diff --git a/examples/showcase/configuration/named-sources.yml b/examples/showcase/configuration/named-sources.yml deleted file mode 100644 index a624a75f..00000000 --- a/examples/showcase/configuration/named-sources.yml +++ /dev/null @@ -1,10 +0,0 @@ -code-path: - - name: java - path: examples/showcase/code/java - - name: kotlin - path: examples/showcase/code/kotlin - - name: text - path: examples/showcase/code/text -docs-path: examples/showcase/configuration/docs/named-sources -doc-includes: - - "**/*.md" diff --git a/examples/showcase/configuration/single-source.yml b/examples/showcase/configuration/single-source.yml deleted file mode 100644 index bf2e5036..00000000 --- a/examples/showcase/configuration/single-source.yml +++ /dev/null @@ -1,2 +0,0 @@ -code-path: examples/showcase/code/java -docs-path: examples/showcase/configuration/docs/single-source diff --git a/examples/showcase/embedding/embed-code.yml b/examples/showcase/embedding/embed-code.yml deleted file mode 100644 index 1f88c8a7..00000000 --- a/examples/showcase/embedding/embed-code.yml +++ /dev/null @@ -1,12 +0,0 @@ -code-path: - - name: java - path: examples/showcase/code/java - - name: kotlin - path: examples/showcase/code/kotlin - - name: text - path: examples/showcase/code/text -docs-path: examples/showcase/embedding/positive -doc-includes: - - "**/*.md" - - "**/*.html" -separator: "// ..." diff --git a/examples/showcase/embedding/negative/stale.yml b/examples/showcase/embedding/negative/stale.yml deleted file mode 100644 index 13477876..00000000 --- a/examples/showcase/embedding/negative/stale.yml +++ /dev/null @@ -1,6 +0,0 @@ -code-path: - - name: java - path: examples/showcase/code/java -docs-path: examples/showcase/embedding/negative/docs -doc-includes: - - stale-snippet.md diff --git a/examples/showcase/README.md b/showcase/README.md similarity index 50% rename from examples/showcase/README.md rename to showcase/README.md index 843e9439..a26378c9 100644 --- a/examples/showcase/README.md +++ b/showcase/README.md @@ -16,27 +16,27 @@ Run commands from the repository root. Run the opt-in end-to-end test with the `showcase` build tag: ```bash -go test -tags showcase ./examples/showcase +go test -tags showcase ./showcase ``` Verify the positive embedding examples: ```bash -go run ./main.go -mode=check -config-path=examples/showcase/embedding/embed-code.yml +go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml ``` Verify the configuration examples: ```bash -go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml -go run ./main.go -mode=check -config-path=examples/showcase/configuration/named-sources.yml -go run ./main.go -mode=check -config-path=examples/showcase/configuration/include-exclude.yml -go run ./main.go -mode=check -config-path=examples/showcase/configuration/multiple-embeddings.yml +go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml +go run ./main.go -mode=check -config-path=showcase/configuration/named-sources.yml +go run ./main.go -mode=check -config-path=showcase/configuration/include-exclude.yml +go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml ``` The negative examples are intentionally broken, so these commands should fail: ```bash -go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/processing-errors.yml -go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/stale.yml +go run ./main.go -mode=check -config-path=showcase/embedding/negative/processing-errors.yml +go run ./main.go -mode=check -config-path=showcase/embedding/negative/stale.yml ``` diff --git a/examples/showcase/code/java/org/showcase/CommentModes.java b/showcase/code/java/org/showcase/CommentModes.java similarity index 100% rename from examples/showcase/code/java/org/showcase/CommentModes.java rename to showcase/code/java/org/showcase/CommentModes.java diff --git a/examples/showcase/code/java/org/showcase/Greeting.java b/showcase/code/java/org/showcase/Greeting.java similarity index 100% rename from examples/showcase/code/java/org/showcase/Greeting.java rename to showcase/code/java/org/showcase/Greeting.java diff --git a/examples/showcase/code/java/org/showcase/MultiPartWorkflow.java b/showcase/code/java/org/showcase/MultiPartWorkflow.java similarity index 100% rename from examples/showcase/code/java/org/showcase/MultiPartWorkflow.java rename to showcase/code/java/org/showcase/MultiPartWorkflow.java diff --git a/examples/showcase/code/java/org/showcase/OverlappingFragments.java b/showcase/code/java/org/showcase/OverlappingFragments.java similarity index 100% rename from examples/showcase/code/java/org/showcase/OverlappingFragments.java rename to showcase/code/java/org/showcase/OverlappingFragments.java diff --git a/examples/showcase/code/java/org/showcase/PatternSamples.java b/showcase/code/java/org/showcase/PatternSamples.java similarity index 100% rename from examples/showcase/code/java/org/showcase/PatternSamples.java rename to showcase/code/java/org/showcase/PatternSamples.java diff --git a/examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt b/showcase/code/kotlin/org/showcase/KotlinGreeting.kt similarity index 100% rename from examples/showcase/code/kotlin/org/showcase/KotlinGreeting.kt rename to showcase/code/kotlin/org/showcase/KotlinGreeting.kt diff --git a/examples/showcase/code/text/glob-patterns.txt b/showcase/code/text/glob-patterns.txt similarity index 100% rename from examples/showcase/code/text/glob-patterns.txt rename to showcase/code/text/glob-patterns.txt diff --git a/examples/showcase/configuration/README.md b/showcase/configuration/README.md similarity index 64% rename from examples/showcase/configuration/README.md rename to showcase/configuration/README.md index 6aabe277..9bc69886 100644 --- a/examples/showcase/configuration/README.md +++ b/showcase/configuration/README.md @@ -8,8 +8,8 @@ working config, then add only the options your documentation needs. A configuration needs one source root and one documentation root: ```yaml -code-path: examples/showcase/code/java -docs-path: examples/showcase/configuration/docs/single-source +code-path: showcase/code/java +docs-path: showcase/configuration/docs/single-source ``` This config is shown by [single-source.yml](single-source.yml). @@ -24,7 +24,7 @@ from the command's current working directory. Run this example (from the project root): ```bash -go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml +go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml ``` ## Add Document Selection @@ -33,8 +33,8 @@ Add `doc-includes` when only some files under `docs-path` should be scanned. Add `doc-excludes` when selected files should be skipped: ```yaml -code-path: examples/showcase/code/java -docs-path: examples/showcase/configuration/docs/include-exclude +code-path: showcase/code/java +docs-path: showcase/configuration/docs/include-exclude doc-includes: - "**/*.md" doc-excludes: @@ -49,7 +49,7 @@ Use include and exclude patterns to skip drafts, generated docs, deprecated pages, or any file that should not be scanned for active instructions. ```bash -go run ./main.go -mode=check -config-path=examples/showcase/configuration/include-exclude.yml +go run ./main.go -mode=check -config-path=showcase/configuration/include-exclude.yml ``` ## Add Named Source Roots @@ -60,12 +60,12 @@ source trees: ```yaml code-path: - name: java - path: examples/showcase/code/java + path: showcase/code/java - name: kotlin - path: examples/showcase/code/kotlin + path: showcase/code/kotlin - name: text - path: examples/showcase/code/text -docs-path: examples/showcase/configuration/docs/named-sources + path: showcase/code/text +docs-path: showcase/configuration/docs/named-sources ``` This shape is shown by [named-sources.yml](named-sources.yml). Its docs live in @@ -80,7 +80,7 @@ Instructions choose a source root with the `$name` prefix: Run the named-source example: ```bash -go run ./main.go -mode=check -config-path=examples/showcase/configuration/named-sources.yml +go run ./main.go -mode=check -config-path=showcase/configuration/named-sources.yml ``` ## Add Multiple Documentation Targets @@ -92,13 +92,13 @@ and optional settings: ```yaml embeddings: - name: java-guide - code-path: examples/showcase/code/java - docs-path: examples/showcase/configuration/docs/multiple/java + code-path: showcase/code/java + docs-path: showcase/configuration/docs/multiple/java - name: kotlin-guide code-path: - name: kotlin - path: examples/showcase/code/kotlin - docs-path: examples/showcase/configuration/docs/multiple/kotlin + path: showcase/code/kotlin + docs-path: showcase/configuration/docs/multiple/kotlin ``` This shape is shown by [multiple-embeddings.yml](multiple-embeddings.yml). It @@ -106,7 +106,7 @@ processes [docs/multiple/java](docs/multiple/java/) and [docs/multiple/kotlin](docs/multiple/kotlin/) in one run. ```bash -go run ./main.go -mode=check -config-path=examples/showcase/configuration/multiple-embeddings.yml +go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml ``` ## All Configuration Checks @@ -114,8 +114,8 @@ go run ./main.go -mode=check -config-path=examples/showcase/configuration/multip Run commands from the project root. ```bash -go run ./main.go -mode=check -config-path=examples/showcase/configuration/single-source.yml -go run ./main.go -mode=check -config-path=examples/showcase/configuration/named-sources.yml -go run ./main.go -mode=check -config-path=examples/showcase/configuration/include-exclude.yml -go run ./main.go -mode=check -config-path=examples/showcase/configuration/multiple-embeddings.yml +go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml +go run ./main.go -mode=check -config-path=showcase/configuration/named-sources.yml +go run ./main.go -mode=check -config-path=showcase/configuration/include-exclude.yml +go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml ``` diff --git a/examples/showcase/configuration/docs/include-exclude/excluded.md b/showcase/configuration/docs/include-exclude/excluded.md similarity index 100% rename from examples/showcase/configuration/docs/include-exclude/excluded.md rename to showcase/configuration/docs/include-exclude/excluded.md diff --git a/examples/showcase/configuration/docs/include-exclude/included.md b/showcase/configuration/docs/include-exclude/included.md similarity index 100% rename from examples/showcase/configuration/docs/include-exclude/included.md rename to showcase/configuration/docs/include-exclude/included.md diff --git a/examples/showcase/configuration/docs/multiple/java/greeting.md b/showcase/configuration/docs/multiple/java/greeting.md similarity index 100% rename from examples/showcase/configuration/docs/multiple/java/greeting.md rename to showcase/configuration/docs/multiple/java/greeting.md diff --git a/examples/showcase/configuration/docs/multiple/kotlin/greeting.md b/showcase/configuration/docs/multiple/kotlin/greeting.md similarity index 100% rename from examples/showcase/configuration/docs/multiple/kotlin/greeting.md rename to showcase/configuration/docs/multiple/kotlin/greeting.md diff --git a/examples/showcase/configuration/docs/named-sources/java-greeting.md b/showcase/configuration/docs/named-sources/java-greeting.md similarity index 100% rename from examples/showcase/configuration/docs/named-sources/java-greeting.md rename to showcase/configuration/docs/named-sources/java-greeting.md diff --git a/examples/showcase/configuration/docs/named-sources/kotlin-greeting.md b/showcase/configuration/docs/named-sources/kotlin-greeting.md similarity index 100% rename from examples/showcase/configuration/docs/named-sources/kotlin-greeting.md rename to showcase/configuration/docs/named-sources/kotlin-greeting.md diff --git a/examples/showcase/configuration/docs/named-sources/text-line.md b/showcase/configuration/docs/named-sources/text-line.md similarity index 100% rename from examples/showcase/configuration/docs/named-sources/text-line.md rename to showcase/configuration/docs/named-sources/text-line.md diff --git a/examples/showcase/configuration/docs/single-source/greeting.md b/showcase/configuration/docs/single-source/greeting.md similarity index 86% rename from examples/showcase/configuration/docs/single-source/greeting.md rename to showcase/configuration/docs/single-source/greeting.md index 0fbb97fb..e337d196 100644 --- a/examples/showcase/configuration/docs/single-source/greeting.md +++ b/showcase/configuration/docs/single-source/greeting.md @@ -5,7 +5,7 @@ This config uses one unnamed `code-path`. ## How It Works [../../single-source.yml](../../single-source.yml) points `code-path` directly -at `examples/showcase/code/java`. The instruction therefore uses +at `showcase/code/java`. The instruction therefore uses `org/showcase/Greeting.java` instead of `$java/org/showcase/Greeting.java`. diff --git a/showcase/configuration/include-exclude.yml b/showcase/configuration/include-exclude.yml new file mode 100644 index 00000000..c5eec850 --- /dev/null +++ b/showcase/configuration/include-exclude.yml @@ -0,0 +1,6 @@ +code-path: showcase/code/java +docs-path: showcase/configuration/docs/include-exclude +doc-includes: + - "**/*.md" +doc-excludes: + - excluded.md diff --git a/showcase/configuration/multiple-embeddings.yml b/showcase/configuration/multiple-embeddings.yml new file mode 100644 index 00000000..1e05ee61 --- /dev/null +++ b/showcase/configuration/multiple-embeddings.yml @@ -0,0 +1,13 @@ +embeddings: + - name: java-guide + code-path: showcase/code/java + docs-path: showcase/configuration/docs/multiple/java + doc-includes: + - "**/*.md" + - name: kotlin-guide + code-path: + - name: kotlin + path: showcase/code/kotlin + docs-path: showcase/configuration/docs/multiple/kotlin + doc-includes: + - "**/*.md" diff --git a/showcase/configuration/named-sources.yml b/showcase/configuration/named-sources.yml new file mode 100644 index 00000000..eeb5f248 --- /dev/null +++ b/showcase/configuration/named-sources.yml @@ -0,0 +1,10 @@ +code-path: + - name: java + path: showcase/code/java + - name: kotlin + path: showcase/code/kotlin + - name: text + path: showcase/code/text +docs-path: showcase/configuration/docs/named-sources +doc-includes: + - "**/*.md" diff --git a/showcase/configuration/single-source.yml b/showcase/configuration/single-source.yml new file mode 100644 index 00000000..4180a08d --- /dev/null +++ b/showcase/configuration/single-source.yml @@ -0,0 +1,2 @@ +code-path: showcase/code/java +docs-path: showcase/configuration/docs/single-source diff --git a/examples/showcase/embedding/README.md b/showcase/embedding/README.md similarity index 88% rename from examples/showcase/embedding/README.md rename to showcase/embedding/README.md index 50854948..e137d861 100644 --- a/examples/showcase/embedding/README.md +++ b/showcase/embedding/README.md @@ -7,7 +7,7 @@ user should expect when an instruction is malformed or stale. Run the positive examples from the repository root: ```bash -go run ./main.go -mode=check -config-path=examples/showcase/embedding/embed-code.yml +go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml ``` ## Feature Examples @@ -56,8 +56,8 @@ The negative examples are intentionally broken and should fail. Use them to recognize common diagnostics: ```bash -go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/processing-errors.yml -go run ./main.go -mode=check -config-path=examples/showcase/embedding/negative/stale.yml +go run ./main.go -mode=check -config-path=showcase/embedding/negative/processing-errors.yml +go run ./main.go -mode=check -config-path=showcase/embedding/negative/stale.yml ``` The cases live in [negative/docs](negative/docs/). diff --git a/showcase/embedding/embed-code.yml b/showcase/embedding/embed-code.yml new file mode 100644 index 00000000..550a13e9 --- /dev/null +++ b/showcase/embedding/embed-code.yml @@ -0,0 +1,12 @@ +code-path: + - name: java + path: showcase/code/java + - name: kotlin + path: showcase/code/kotlin + - name: text + path: showcase/code/text +docs-path: showcase/embedding/positive +doc-includes: + - "**/*.md" + - "**/*.html" +separator: "// ..." diff --git a/examples/showcase/embedding/negative/docs/invalid-attributes.md b/showcase/embedding/negative/docs/invalid-attributes.md similarity index 100% rename from examples/showcase/embedding/negative/docs/invalid-attributes.md rename to showcase/embedding/negative/docs/invalid-attributes.md diff --git a/examples/showcase/embedding/negative/docs/missing-code-fence.md b/showcase/embedding/negative/docs/missing-code-fence.md similarity index 100% rename from examples/showcase/embedding/negative/docs/missing-code-fence.md rename to showcase/embedding/negative/docs/missing-code-fence.md diff --git a/examples/showcase/embedding/negative/docs/missing-fragment.md b/showcase/embedding/negative/docs/missing-fragment.md similarity index 100% rename from examples/showcase/embedding/negative/docs/missing-fragment.md rename to showcase/embedding/negative/docs/missing-fragment.md diff --git a/examples/showcase/embedding/negative/docs/missing-pattern.md b/showcase/embedding/negative/docs/missing-pattern.md similarity index 100% rename from examples/showcase/embedding/negative/docs/missing-pattern.md rename to showcase/embedding/negative/docs/missing-pattern.md diff --git a/examples/showcase/embedding/negative/docs/missing-source.md b/showcase/embedding/negative/docs/missing-source.md similarity index 100% rename from examples/showcase/embedding/negative/docs/missing-source.md rename to showcase/embedding/negative/docs/missing-source.md diff --git a/examples/showcase/embedding/negative/docs/stale-snippet.md b/showcase/embedding/negative/docs/stale-snippet.md similarity index 100% rename from examples/showcase/embedding/negative/docs/stale-snippet.md rename to showcase/embedding/negative/docs/stale-snippet.md diff --git a/examples/showcase/embedding/negative/docs/unclosed-code-fence.md b/showcase/embedding/negative/docs/unclosed-code-fence.md similarity index 100% rename from examples/showcase/embedding/negative/docs/unclosed-code-fence.md rename to showcase/embedding/negative/docs/unclosed-code-fence.md diff --git a/examples/showcase/embedding/negative/processing-errors.yml b/showcase/embedding/negative/processing-errors.yml similarity index 70% rename from examples/showcase/embedding/negative/processing-errors.yml rename to showcase/embedding/negative/processing-errors.yml index e04d3a5e..f6d85748 100644 --- a/examples/showcase/embedding/negative/processing-errors.yml +++ b/showcase/embedding/negative/processing-errors.yml @@ -2,8 +2,8 @@ code-path: - name: repo path: . - name: java - path: examples/showcase/code/java -docs-path: examples/showcase/embedding/negative/docs + path: showcase/code/java +docs-path: showcase/embedding/negative/docs doc-includes: - missing-source.md - missing-fragment.md diff --git a/showcase/embedding/negative/stale.yml b/showcase/embedding/negative/stale.yml new file mode 100644 index 00000000..17a77e0f --- /dev/null +++ b/showcase/embedding/negative/stale.yml @@ -0,0 +1,6 @@ +code-path: + - name: java + path: showcase/code/java +docs-path: showcase/embedding/negative/docs +doc-includes: + - stale-snippet.md diff --git a/examples/showcase/embedding/positive/comment-filtering.md b/showcase/embedding/positive/comment-filtering.md similarity index 100% rename from examples/showcase/embedding/positive/comment-filtering.md rename to showcase/embedding/positive/comment-filtering.md diff --git a/examples/showcase/embedding/positive/html-showcase.html b/showcase/embedding/positive/html-showcase.html similarity index 100% rename from examples/showcase/embedding/positive/html-showcase.html rename to showcase/embedding/positive/html-showcase.html diff --git a/examples/showcase/embedding/positive/instruction-tag.md b/showcase/embedding/positive/instruction-tag.md similarity index 100% rename from examples/showcase/embedding/positive/instruction-tag.md rename to showcase/embedding/positive/instruction-tag.md diff --git a/examples/showcase/embedding/positive/markdown-fence-shielding.md b/showcase/embedding/positive/markdown-fence-shielding.md similarity index 100% rename from examples/showcase/embedding/positive/markdown-fence-shielding.md rename to showcase/embedding/positive/markdown-fence-shielding.md diff --git a/examples/showcase/embedding/positive/multi-line-pattern.md b/showcase/embedding/positive/multi-line-pattern.md similarity index 100% rename from examples/showcase/embedding/positive/multi-line-pattern.md rename to showcase/embedding/positive/multi-line-pattern.md diff --git a/examples/showcase/embedding/positive/multi-part-fragment-separator.md b/showcase/embedding/positive/multi-part-fragment-separator.md similarity index 100% rename from examples/showcase/embedding/positive/multi-part-fragment-separator.md rename to showcase/embedding/positive/multi-part-fragment-separator.md diff --git a/examples/showcase/embedding/positive/named-fragment.md b/showcase/embedding/positive/named-fragment.md similarity index 100% rename from examples/showcase/embedding/positive/named-fragment.md rename to showcase/embedding/positive/named-fragment.md diff --git a/examples/showcase/embedding/positive/named-source-root.md b/showcase/embedding/positive/named-source-root.md similarity index 100% rename from examples/showcase/embedding/positive/named-source-root.md rename to showcase/embedding/positive/named-source-root.md diff --git a/examples/showcase/embedding/positive/overlapping-fragments.md b/showcase/embedding/positive/overlapping-fragments.md similarity index 100% rename from examples/showcase/embedding/positive/overlapping-fragments.md rename to showcase/embedding/positive/overlapping-fragments.md diff --git a/examples/showcase/embedding/positive/pattern-escaping.md b/showcase/embedding/positive/pattern-escaping.md similarity index 100% rename from examples/showcase/embedding/positive/pattern-escaping.md rename to showcase/embedding/positive/pattern-escaping.md diff --git a/examples/showcase/embedding/positive/source-line-pattern.md b/showcase/embedding/positive/source-line-pattern.md similarity index 100% rename from examples/showcase/embedding/positive/source-line-pattern.md rename to showcase/embedding/positive/source-line-pattern.md diff --git a/examples/showcase/embedding/positive/start-end-pattern.md b/showcase/embedding/positive/start-end-pattern.md similarity index 100% rename from examples/showcase/embedding/positive/start-end-pattern.md rename to showcase/embedding/positive/start-end-pattern.md diff --git a/examples/showcase/embedding/positive/whole-file-source.md b/showcase/embedding/positive/whole-file-source.md similarity index 100% rename from examples/showcase/embedding/positive/whole-file-source.md rename to showcase/embedding/positive/whole-file-source.md diff --git a/examples/showcase/showcase_test.go b/showcase/showcase_test.go similarity index 95% rename from examples/showcase/showcase_test.go rename to showcase/showcase_test.go index 2133368d..3dc5a164 100644 --- a/examples/showcase/showcase_test.go +++ b/showcase/showcase_test.go @@ -102,7 +102,7 @@ var _ = Describe("Showcase", func() { config := config It("should check "+config, func() { - configPath := filepath.Join("examples", "showcase", "configuration", config) + configPath := filepath.Join("showcase", "configuration", config) output, err := runEmbedCode(repoRoot, "check", configPath) Expect(err).ShouldNot(HaveOccurred(), "expected configuration example to pass:\n%s", output) @@ -188,7 +188,7 @@ func negativeShowcaseCases() []negativeShowcaseCase { func javaSource(repoRoot string) namedSource { return namedSource{ name: "java", - path: filepath.Join(repoRoot, "examples", "showcase", "code", "java"), + path: filepath.Join(repoRoot, "showcase", "code", "java"), } } @@ -199,14 +199,14 @@ func findRepoRoot() string { _, filePath, _, ok := runtime.Caller(0) Expect(ok).Should(BeTrue(), "could not locate showcase test file") - return filepath.Clean(filepath.Join(filepath.Dir(filePath), "..", "..")) + return filepath.Clean(filepath.Join(filepath.Dir(filePath), "..")) } // copyShowcaseDocs copies one showcase documentation folder to a temporary test directory. func copyShowcaseDocs(repoRoot string, relativeSource string) string { GinkgoHelper() - sourceRoot := filepath.Join(repoRoot, "examples", "showcase", relativeSource) + sourceRoot := filepath.Join(repoRoot, "showcase", relativeSource) tempRoot, err := os.MkdirTemp("", "embed-code-showcase-docs-*") Expect(err).ShouldNot(HaveOccurred()) DeferCleanup(os.RemoveAll, tempRoot) @@ -258,8 +258,8 @@ func writeShowcaseConfig(repoRoot string, docsRoot string) string { return writeConfig(docsRoot, []namedSource{ javaSource(repoRoot), - {name: "kotlin", path: filepath.Join(repoRoot, "examples", "showcase", "code", "kotlin")}, - {name: "text", path: filepath.Join(repoRoot, "examples", "showcase", "code", "text")}, + {name: "kotlin", path: filepath.Join(repoRoot, "showcase", "code", "kotlin")}, + {name: "text", path: filepath.Join(repoRoot, "showcase", "code", "text")}, }, []string{"**/*.md", "**/*.html"}) } From c50e77658aee8bdbfed989be4cd15287a06409f0 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 16 Jun 2026 21:00:35 +0200 Subject: [PATCH 17/22] Add E2E tests to the CI. --- .github/workflows/check.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 5f15d06b..43a8c361 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -31,3 +31,12 @@ jobs: # Tests must be run sequentially because they create temporary files that can cause issues. # Therefore, the "-p 1" argument is required. run: go test -v ./... -p 1 + + - name: Run E2E Tests + run: | + go test -v -tags showcase ./showcase -p 1 + go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml + go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml + go run ./main.go -mode=check -config-path=showcase/configuration/named-sources.yml + go run ./main.go -mode=check -config-path=showcase/configuration/include-exclude.yml + go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml From eaf0f2017c91d35a8d0cda8a3cdbfbe0d80f1a31 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 16 Jun 2026 21:13:39 +0200 Subject: [PATCH 18/22] Improve links readability. --- EMBEDDING.md | 2 +- README.md | 2 +- showcase/configuration/README.md | 12 ++++++------ .../configuration/docs/include-exclude/excluded.md | 2 +- .../configuration/docs/include-exclude/included.md | 2 +- .../configuration/docs/multiple/java/greeting.md | 2 +- .../configuration/docs/multiple/kotlin/greeting.md | 2 +- .../docs/named-sources/java-greeting.md | 2 +- .../docs/named-sources/kotlin-greeting.md | 2 +- .../configuration/docs/named-sources/text-line.md | 2 +- .../configuration/docs/single-source/greeting.md | 2 +- showcase/embedding/README.md | 2 +- showcase/embedding/negative/docs/missing-fragment.md | 2 +- showcase/embedding/negative/docs/missing-pattern.md | 2 +- showcase/embedding/negative/docs/missing-source.md | 2 +- showcase/embedding/negative/docs/stale-snippet.md | 2 +- showcase/embedding/positive/comment-filtering.md | 2 +- .../positive/multi-part-fragment-separator.md | 4 ++-- showcase/embedding/positive/named-fragment.md | 2 +- showcase/embedding/positive/named-source-root.md | 2 +- showcase/embedding/positive/overlapping-fragments.md | 2 +- showcase/embedding/positive/pattern-escaping.md | 2 +- showcase/embedding/positive/source-line-pattern.md | 2 +- showcase/embedding/positive/start-end-pattern.md | 2 +- showcase/embedding/positive/whole-file-source.md | 4 ++-- 25 files changed, 32 insertions(+), 32 deletions(-) diff --git a/EMBEDDING.md b/EMBEDDING.md index 2c372152..ca126b4b 100644 --- a/EMBEDDING.md +++ b/EMBEDDING.md @@ -234,7 +234,7 @@ The table below lists the supported languages and supported `comments` modes for A named fragment may consist of one or several pieces declared in a single file. When rendered, the pieces that belong to a single fragment are joined together. It is possible to specify a separator between the joined pieces, -see [Configuration](./README.md#arguments) for the corresponding parameter. +see [Configuration](README.md#arguments) for the corresponding parameter. Here is an example of how a multi-piece fragment is rendered. diff --git a/README.md b/README.md index 4b9e7361..05ec408e 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ These settings have the same role as the command-line arguments. ## Installation -* Go to https://go.dev/doc/install. Our Go version is `1.22.1`, which can be checked in the [go.mod](./go.mod) file +* Go to https://go.dev/doc/install. Our Go version is `1.22.1`, which can be checked in the [go.mod](go.mod) file * Make sure your Go installed successfully with the command ```bash go version diff --git a/showcase/configuration/README.md b/showcase/configuration/README.md index 9bc69886..465a1f44 100644 --- a/showcase/configuration/README.md +++ b/showcase/configuration/README.md @@ -16,7 +16,7 @@ This config is shown by [single-source.yml](single-source.yml). The application scans files under `docs-path`, finds `` instructions, and resolves each instruction's `file` path from `code-path`. For example, see -instruction in [docs/single-source/greeting.md](docs/single-source/greeting.md). +instruction in [greeting.md](docs/single-source/greeting.md). Relative paths in `code-path` and `docs-path` are resolved from the command's current working directory. @@ -42,8 +42,8 @@ doc-excludes: ``` This shape is shown by [include-exclude.yml](include-exclude.yml). It processes -[docs/include-exclude/included.md](docs/include-exclude/included.md) and skips -[docs/include-exclude/excluded.md](docs/include-exclude/excluded.md). +[included.md](docs/include-exclude/included.md) and skips +[excluded.md](docs/include-exclude/excluded.md). Use include and exclude patterns to skip drafts, generated docs, deprecated pages, or any file that should not be scanned for active instructions. @@ -69,7 +69,7 @@ docs-path: showcase/configuration/docs/named-sources ``` This shape is shown by [named-sources.yml](named-sources.yml). Its docs live in -[docs/named-sources](docs/named-sources/). +[showcase/configuration/docs/named-sources/](docs/named-sources). Instructions choose a source root with the `$name` prefix: @@ -102,8 +102,8 @@ embeddings: ``` This shape is shown by [multiple-embeddings.yml](multiple-embeddings.yml). It -processes [docs/multiple/java](docs/multiple/java/) and -[docs/multiple/kotlin](docs/multiple/kotlin/) in one run. +processes [showcase/configuration/docs/multiple/java/](docs/multiple/java) and +[showcase/configuration/docs/multiple/kotlin/](docs/multiple/kotlin) in one run. ```bash go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml diff --git a/showcase/configuration/docs/include-exclude/excluded.md b/showcase/configuration/docs/include-exclude/excluded.md index 991f2a33..224dd915 100644 --- a/showcase/configuration/docs/include-exclude/excluded.md +++ b/showcase/configuration/docs/include-exclude/excluded.md @@ -5,7 +5,7 @@ that excluded documents are not processed. ## How It Works -[../../include-exclude.yml](../../include-exclude.yml) lists this file in +[include-exclude.yml](../../include-exclude.yml) lists this file in `doc-excludes`. The instruction points at a missing source file, but the command still succeeds because excluded files are skipped before instruction parsing. diff --git a/showcase/configuration/docs/include-exclude/included.md b/showcase/configuration/docs/include-exclude/included.md index a7dd199a..965ec6c7 100644 --- a/showcase/configuration/docs/include-exclude/included.md +++ b/showcase/configuration/docs/include-exclude/included.md @@ -5,7 +5,7 @@ processed normally. ## How It Works -[../../include-exclude.yml](../../include-exclude.yml) includes Markdown files +[include-exclude.yml](../../include-exclude.yml) includes Markdown files under this docs root. Because this file is not listed in `doc-excludes`, check mode resolves the instruction and compares the rendered fence with the Java source fragment. diff --git a/showcase/configuration/docs/multiple/java/greeting.md b/showcase/configuration/docs/multiple/java/greeting.md index 727e27d1..5304fc3f 100644 --- a/showcase/configuration/docs/multiple/java/greeting.md +++ b/showcase/configuration/docs/multiple/java/greeting.md @@ -4,7 +4,7 @@ This document is processed by the `java-guide` entry in an `embeddings` config. ## How It Works -[../../../multiple-embeddings.yml](../../../multiple-embeddings.yml) contains a +[multiple-embeddings.yml](../../../multiple-embeddings.yml) contains a `java-guide` entry with its own `code-path` and `docs-path`. This document lives under that entry's docs root, so the unprefixed source path resolves against the Java source tree. diff --git a/showcase/configuration/docs/multiple/kotlin/greeting.md b/showcase/configuration/docs/multiple/kotlin/greeting.md index 6a78a25e..7c6d29f4 100644 --- a/showcase/configuration/docs/multiple/kotlin/greeting.md +++ b/showcase/configuration/docs/multiple/kotlin/greeting.md @@ -5,7 +5,7 @@ This document is processed by the `kotlin-guide` entry in the same ## How It Works -[../../../multiple-embeddings.yml](../../../multiple-embeddings.yml) contains a +[multiple-embeddings.yml](../../../multiple-embeddings.yml) contains a separate `kotlin-guide` entry. That entry defines a named `kotlin` source root, so this instruction uses `$kotlin` even though it is processed by the same top-level command as the Java entry. diff --git a/showcase/configuration/docs/named-sources/java-greeting.md b/showcase/configuration/docs/named-sources/java-greeting.md index 655e3925..bc5dda0e 100644 --- a/showcase/configuration/docs/named-sources/java-greeting.md +++ b/showcase/configuration/docs/named-sources/java-greeting.md @@ -5,7 +5,7 @@ root before resolving the relative path. ## How It Works -[../../named-sources.yml](../../named-sources.yml) defines a source root named +[named-sources.yml](../../named-sources.yml) defines a source root named `java`. The instruction must include `$java` so the resolver knows which source tree owns `org/showcase/Greeting.java`. diff --git a/showcase/configuration/docs/named-sources/kotlin-greeting.md b/showcase/configuration/docs/named-sources/kotlin-greeting.md index 9cb7f174..d35675a2 100644 --- a/showcase/configuration/docs/named-sources/kotlin-greeting.md +++ b/showcase/configuration/docs/named-sources/kotlin-greeting.md @@ -5,7 +5,7 @@ prefix in the instruction. ## How It Works -[../../named-sources.yml](../../named-sources.yml) also defines a source root +[named-sources.yml](../../named-sources.yml) also defines a source root named `kotlin`. Changing the prefix to `$kotlin` resolves the same relative package path under the Kotlin source tree. diff --git a/showcase/configuration/docs/named-sources/text-line.md b/showcase/configuration/docs/named-sources/text-line.md index 38d679f7..c69d9412 100644 --- a/showcase/configuration/docs/named-sources/text-line.md +++ b/showcase/configuration/docs/named-sources/text-line.md @@ -5,7 +5,7 @@ from the text source root. ## How It Works -[../../named-sources.yml](../../named-sources.yml) defines a source root named +[named-sources.yml](../../named-sources.yml) defines a source root named `text`. The `line` pattern matches one plain-text line from `glob-patterns.txt`, showing that source roots can point at any supported text fixture, not only programming language files. diff --git a/showcase/configuration/docs/single-source/greeting.md b/showcase/configuration/docs/single-source/greeting.md index e337d196..b74347a9 100644 --- a/showcase/configuration/docs/single-source/greeting.md +++ b/showcase/configuration/docs/single-source/greeting.md @@ -4,7 +4,7 @@ This config uses one unnamed `code-path`. ## How It Works -[../../single-source.yml](../../single-source.yml) points `code-path` directly +[single-source.yml](../../single-source.yml) points `code-path` directly at `showcase/code/java`. The instruction therefore uses `org/showcase/Greeting.java` instead of `$java/org/showcase/Greeting.java`. diff --git a/showcase/embedding/README.md b/showcase/embedding/README.md index e137d861..f9a20032 100644 --- a/showcase/embedding/README.md +++ b/showcase/embedding/README.md @@ -60,4 +60,4 @@ go run ./main.go -mode=check -config-path=showcase/embedding/negative/processing go run ./main.go -mode=check -config-path=showcase/embedding/negative/stale.yml ``` -The cases live in [negative/docs](negative/docs/). +The cases live in [showcase/embedding/negative/docs/](negative/docs). diff --git a/showcase/embedding/negative/docs/missing-fragment.md b/showcase/embedding/negative/docs/missing-fragment.md index 63ea22ab..084b8a4f 100644 --- a/showcase/embedding/negative/docs/missing-fragment.md +++ b/showcase/embedding/negative/docs/missing-fragment.md @@ -5,7 +5,7 @@ fragment does not. ## How It Fails -[../../../code/java/org/showcase/Greeting.java](../../../code/java/org/showcase/Greeting.java) +[Greeting.java](../../../code/java/org/showcase/Greeting.java) contains fragments such as `main()`, but it does not contain `does not exist`. Check mode reports the missing fragment. In a real guide, use an existing fragment name or add matching source markers. diff --git a/showcase/embedding/negative/docs/missing-pattern.md b/showcase/embedding/negative/docs/missing-pattern.md index a107ef8b..68f99447 100644 --- a/showcase/embedding/negative/docs/missing-pattern.md +++ b/showcase/embedding/negative/docs/missing-pattern.md @@ -5,7 +5,7 @@ This scenario shows what happens when a line pattern matches nothing. ## How It Fails The source file is found, but no line in -[../../../code/java/org/showcase/Greeting.java](../../../code/java/org/showcase/Greeting.java) +[Greeting.java](../../../code/java/org/showcase/Greeting.java) matches `doesNotExistPattern`. Check mode reports the unmatched pattern. In a real guide, loosen the glob pattern, add anchors only where needed, or point the instruction at the intended source file. diff --git a/showcase/embedding/negative/docs/missing-source.md b/showcase/embedding/negative/docs/missing-source.md index a2ac946a..5a2acc0e 100644 --- a/showcase/embedding/negative/docs/missing-source.md +++ b/showcase/embedding/negative/docs/missing-source.md @@ -5,7 +5,7 @@ This scenario shows what happens when the `file` attribute cannot be resolved. ## How It Fails The `$java` root exists, but `org/showcase/DoesNotExist.java` is not present -under [../../../code/java](../../../code/java/). Check mode reports the missing source +under [showcase/code/java/](../../../code/java). Check mode reports the missing source file and leaves the document unchanged. In a real guide, fix the path or add the missing source file. diff --git a/showcase/embedding/negative/docs/stale-snippet.md b/showcase/embedding/negative/docs/stale-snippet.md index f4b28cc9..35734bf4 100644 --- a/showcase/embedding/negative/docs/stale-snippet.md +++ b/showcase/embedding/negative/docs/stale-snippet.md @@ -5,7 +5,7 @@ This scenario is syntactically valid, but the rendered code is out of date. ## How It Fails Check mode resolves the `main()` fragment from -[../../../code/java/org/showcase/Greeting.java](../../../code/java/org/showcase/Greeting.java) +[Greeting.java](../../../code/java/org/showcase/Greeting.java) and compares it with the existing fence. The fence contains different text, so check mode reports the document as stale without rewriting it. Embed mode would replace the fence with the current source fragment. diff --git a/showcase/embedding/positive/comment-filtering.md b/showcase/embedding/positive/comment-filtering.md index fd5e12c0..27c6be18 100644 --- a/showcase/embedding/positive/comment-filtering.md +++ b/showcase/embedding/positive/comment-filtering.md @@ -23,7 +23,7 @@ Comment support depends on the source file extension. Unknown extensions are embedded unchanged. Not every supported language distinguishes documentation, regular, inline, and block comments, so unsupported categories simply have no comments to keep. See -[Comment filtering](../../../../EMBEDDING.md#comment-filtering) for the full +[Comment filtering](../../../EMBEDDING.md#comment-filtering) for the full language matrix. The examples below embed the same Java file with different `comments` modes so diff --git a/showcase/embedding/positive/multi-part-fragment-separator.md b/showcase/embedding/positive/multi-part-fragment-separator.md index 43c01b18..16cb0fe4 100644 --- a/showcase/embedding/positive/multi-part-fragment-separator.md +++ b/showcase/embedding/positive/multi-part-fragment-separator.md @@ -13,13 +13,13 @@ the selected parts in source order, normalizes common indentation across all of them, and inserts the configured `separator` between neighboring parts. The default separator is `...`. This showcase uses `// ...` in -[../embed-code.yml](../embed-code.yml) so the separator is valid inside Java +[embed-code.yml](../embed-code.yml) so the separator is valid inside Java snippets. Separator indentation follows the surrounding rendered code, which keeps skipped sections readable inside classes and methods. ## Embedding Instruction -[../../code/java/org/showcase/MultiPartWorkflow.java](../../code/java/org/showcase/MultiPartWorkflow.java) +[MultiPartWorkflow.java](../../code/java/org/showcase/MultiPartWorkflow.java) opens and closes the `Workflow` fragment several times. The instruction below renders those selected parts as one snippet. diff --git a/showcase/embedding/positive/named-fragment.md b/showcase/embedding/positive/named-fragment.md index dbcc4e4f..4554487b 100644 --- a/showcase/embedding/positive/named-fragment.md +++ b/showcase/embedding/positive/named-fragment.md @@ -20,7 +20,7 @@ source file, the run reports the missing fragment. ## Source Markers -[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +[Greeting.java](../../code/java/org/showcase/Greeting.java) declares the `main()` fragment like this: ```java diff --git a/showcase/embedding/positive/named-source-root.md b/showcase/embedding/positive/named-source-root.md index febeaee3..bd96d7e3 100644 --- a/showcase/embedding/positive/named-source-root.md +++ b/showcase/embedding/positive/named-source-root.md @@ -10,7 +10,7 @@ In config file, each entry in `code-path` can have a `name` and a `path`. When an instruction starts its `file` value with `$name/`, embed-code selects only that named root and then resolves the remaining relative path inside it. -[../embed-code.yml](../embed-code.yml) defines `java`, `kotlin`, and `text` +[embed-code.yml](../embed-code.yml) defines `java`, `kotlin`, and `text` source roots. The `$kotlin` prefix below chooses the Kotlin root before resolving `org/showcase/KotlinGreeting.kt`. diff --git a/showcase/embedding/positive/overlapping-fragments.md b/showcase/embedding/positive/overlapping-fragments.md index 62765415..eb828b95 100644 --- a/showcase/embedding/positive/overlapping-fragments.md +++ b/showcase/embedding/positive/overlapping-fragments.md @@ -11,7 +11,7 @@ A marker can open or close multiple fragments by listing several quoted names: ## Embedding Instruction -[../../code/java/org/showcase/OverlappingFragments.java](../../code/java/org/showcase/OverlappingFragments.java) +[OverlappingFragments.java](../../code/java/org/showcase/OverlappingFragments.java) uses marker lines that name both `Class wrapper` and `Greeting method`. The instruction asks only for `Greeting method`, so the rendered snippet keeps the shared class wrapper and the greeting method while replacing skipped details diff --git a/showcase/embedding/positive/pattern-escaping.md b/showcase/embedding/positive/pattern-escaping.md index adf9e068..bdfd16a3 100644 --- a/showcase/embedding/positive/pattern-escaping.md +++ b/showcase/embedding/positive/pattern-escaping.md @@ -18,7 +18,7 @@ line contains the literal characters `\n`. The pattern `Use \* to multiply` treats `*` as source text instead of a wildcard. It matches a line in -[../../code/text/glob-patterns.txt](../../code/text/glob-patterns.txt). +[glob-patterns.txt](../../code/text/glob-patterns.txt). ```text diff --git a/showcase/embedding/positive/source-line-pattern.md b/showcase/embedding/positive/source-line-pattern.md index 21e4ca07..e262af88 100644 --- a/showcase/embedding/positive/source-line-pattern.md +++ b/showcase/embedding/positive/source-line-pattern.md @@ -16,7 +16,7 @@ cannot be combined with `fragment`, `start`, or `end`. ## Embedding Instruction The instruction below searches -[../../code/java/org/showcase/Greeting.java](../../code/java/org/showcase/Greeting.java) +[Greeting.java](../../code/java/org/showcase/Greeting.java) and renders the first line that contains `Hello`. diff --git a/showcase/embedding/positive/start-end-pattern.md b/showcase/embedding/positive/start-end-pattern.md index 04682430..219a545e 100644 --- a/showcase/embedding/positive/start-end-pattern.md +++ b/showcase/embedding/positive/start-end-pattern.md @@ -18,7 +18,7 @@ If `end` is omitted, it continues to the end of the file. ## Embedding Instruction The instruction below finds the first `@Scenario` in -[../../code/java/org/showcase/PatternSamples.java](../../code/java/org/showcase/PatternSamples.java). +[PatternSamples.java](../../code/java/org/showcase/PatternSamples.java). It then stops at the next line that is exactly four spaces followed by `}`. From 2eeef93877e8cb6f115cf50c7b157f9474292332 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Tue, 16 Jun 2026 21:21:22 +0200 Subject: [PATCH 19/22] Fix tests under Windows. --- .github/workflows/check.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 43a8c361..196fa460 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -33,10 +33,4 @@ jobs: run: go test -v ./... -p 1 - name: Run E2E Tests - run: | - go test -v -tags showcase ./showcase -p 1 - go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml - go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml - go run ./main.go -mode=check -config-path=showcase/configuration/named-sources.yml - go run ./main.go -mode=check -config-path=showcase/configuration/include-exclude.yml - go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml + run: go test -v -tags showcase ./showcase -p 1 From 9602d757e71c8dee427f3783c6283530493ce4ae Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Wed, 17 Jun 2026 13:52:03 +0200 Subject: [PATCH 20/22] Improve readability. --- showcase/README.md | 2 +- showcase/embedding/positive/source-line-pattern.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/showcase/README.md b/showcase/README.md index a26378c9..fe6d12f7 100644 --- a/showcase/README.md +++ b/showcase/README.md @@ -4,7 +4,7 @@ This is an executable showcase guide to `embed-code-go` and the end-to-end tests ## How To Use This Guide -Guide is divided on two categories: +This guide is divided into two categories: 1. [Configuration](configuration/README.md) - describes how to configure the whole embed-code application. 2. [Embedding](embedding/README.md) - describes how to work with the embedding instructions. diff --git a/showcase/embedding/positive/source-line-pattern.md b/showcase/embedding/positive/source-line-pattern.md index e262af88..24f566a8 100644 --- a/showcase/embedding/positive/source-line-pattern.md +++ b/showcase/embedding/positive/source-line-pattern.md @@ -5,7 +5,7 @@ fragment or range. ## How It Works -The `line` attribute uses the same glob-style pattern syntax as `start` and`end`. +The `line` attribute uses the same glob-style pattern syntax as `start` and `end`. By default, embed-code behaves as if `*` exists at the beginning and end of the pattern, so `Hello` can match any source line that contains `Hello`. Use `^` or `$` when the match must start or end at a line boundary. From a4830e8833ed4c612c96797aab8303d4634c2931 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 18 Jun 2026 18:24:24 +0200 Subject: [PATCH 21/22] Make docs more separated. --- EMBEDDING.md | 335 ------------------ PROJECT.md | 43 ++- README.md | 203 ++--------- showcase/README.md | 29 +- showcase/configuration/README.md | 90 +++-- showcase/embedding/README.md | 55 +-- .../embedding/positive/comment-filtering.md | 19 +- .../embedding/positive/instruction-tag.md | 17 +- .../embedding/positive/named-source-root.md | 4 +- .../embedding/positive/source-line-pattern.md | 2 +- .../embedding/positive/start-end-pattern.md | 2 +- .../embedding/positive/whole-file-source.md | 2 +- 12 files changed, 221 insertions(+), 580 deletions(-) delete mode 100644 EMBEDDING.md diff --git a/EMBEDDING.md b/EMBEDDING.md deleted file mode 100644 index ca126b4b..00000000 --- a/EMBEDDING.md +++ /dev/null @@ -1,335 +0,0 @@ -# Setting Up Code Embedding - -The `embed-code` utility uses a custom `` tag to insert code snippets from source files into Markdown documentation. - -For executable examples of the embedding features described here, see the -[embed-code showcase](showcase/README.md). The showcase uses paired -instruction tags and dedicated source fixtures so it can double as a guide and -an opt-in end-to-end test. - -## Embedding options - -There are two ways to specify which code fragment to embed: - -### Option 1: Named fragments - -Use a named fragment defined within the source file. -```markdown - -``` - -### Option 2: Line patterns - -Use glob-style patterns to match the start and end lines of the fragment. -```markdown - -``` - -## Embedding instruction format - -An `` instruction must always be followed by a Markdown code fence -(opening and closing triple backticks). - -````markdown - -```java -// The utility will automatically overwrite this content. -``` -```` - -The content inside the code fence is irrelevant as it is automatically updated by the tool. -However, you should specify the language for syntax highlighting (e.g., ` ```java `). - -This is true even when embedding into HTML. - -## Named fragments - -### Marking up source code - -To define a named fragment in your source code, wrap the desired lines with -`#docfragment` and `#enddocfragment` comments: - -```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - - // #docfragment "Constructor" - public String() { - this.value = new char[0]; - } - // #enddocfragment "Constructor" -} -``` - -The `#docfragment` and `#enddocfragment` tags are excluded from the embedded snippet. - -### Usage in documentation - -To embed a named fragment, add the following to your Markdown file: - -````markdown - -```java -``` -```` - -- **`file`**: The path to the source file relative to the `code-path` defined in your configuration. -- **`fragment`**: The name of the fragment to embed. If omitted, the entire file will be embedded. -- **`comments`**: Optional comment filtering mode. If omitted, all comments are retained. - -Fragment names can be any string, but avoid using double quotes (`"`) or characters reserved by XML. - -## Pattern-based fragments - -Alternatively, you can specify a fragment using `start` and `end` patterns: - -````markdown - -```java -``` -```` - -Patterns match the first and last lines of the desired fragment. -If a pattern is omitted, the fragment will start at the beginning or end at the end of the file, respectively. - -To embed a single line, use `line` with the same pattern syntax: - -````markdown - -```java -``` -```` - -The `line` attribute cannot be combined with `start`, `end`, or `fragment`. - -### Pattern syntax - -The tool supports an extended glob syntax for matching lines: - -- `?` — Matches any single character. -- `*` — Matches zero or more characters. -- `[set]` — Matches any single character from the specified set (similar to regex character classes). -- `^` — When used at the start of a pattern, matches the beginning of the line. -- `$` — When used at the end of a pattern, matches the end of the line. - -**Note on anchors:** -By default, patterns imply a wildcard (`*`) at both the start and end. -Use `^` and `$` to disable this behavior and match the exact line start or end. - -#### Multi-line patterns - -Use `\n` inside a `start`, `end`, or `line` pattern to match consecutive source lines. -Spaces around `\n` are ignored, and each pattern line uses the same glob syntax as a -regular one-line pattern. - -````markdown - -```java -``` -```` - -This matches a source range like: - -```java -@Test -@DisplayName("adds two values") -void addsTwoValues() { - int value = 1 + 1; - - assertEquals(2, value); -} -``` - -The `start` pattern above is interpreted as two consecutive line patterns: -`Test` and `adds two values`. Because ordinary patterns imply `*` at both ends, -these match `@Test` and `@DisplayName("adds two values")`. - -Use `^` and `$` on each pattern line when you need exact line matching: - -````markdown - -```java -``` -```` - -Without `\n`, a `start`, `end`, or `line` pattern matches only one source line. - -#### Escaping - -Use a backslash to match glob control characters literally. For example: - -- `\*` matches a literal `*`. -- `\?` matches a literal `?`. -- `\[` matches a literal `[`. - -Since `^` is only special at the start of a pattern, use `^^` to match a literal -`^` there. Since `$` is only special at the end of a pattern, use `$$` to match a -literal `$` there. - -To match literal `\n` text in a source line, write it as `\\n` in the pattern. - -````markdown - -```java -``` -```` - -It's possible to write quote characters in patterns as `\"` instead of the XML entity `"`. -For example, `line="println(\"Hello\")"` is equivalent to `line="println("Hello")"`. - -## Comment filtering - -Use the optional `comments` attribute to reduce comment noise in the embedded snippet: - -````markdown - -```java -``` -```` - -Supported values: - -- `all` — retain all comments. This is the default. -- `none` — strip all recognized comments. -- `documentation` — retain documentation comments such as Javadoc. -- `regular` — retain non-documentation line and block comments. -- `inline` — retain non-documentation line comments such as `//`. -- `block` — retain non-documentation block comments such as `/* */`. - -Unknown extensions are embedded unchanged. - -Not all languages distinguish documentation from regular comments or inline from block comments. - -The table below lists the supported languages and supported `comments` modes for them: - -| Language | Extensions | Supported `comments` modes | -|------------------------|----------------------------------------------------------|--------------------------------------------------------------| -| Java, Kotlin, Groovy | `.java`, `.kt`, `.kts`, `.groovy` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | -| C# | `.cs` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | -| C, C++ | `.c`, `.h`, `.cc`, `.cpp`, `.cxx`, `.hh`, `.hpp`, `.hxx` | `all`, `none`, `inline`, `block` | -| JavaScript, TypeScript | `.js`, `.jsx`, `.ts`, `.tsx` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | -| Go | `.go` | `all`, `none`, `inline`, `block` | -| Protobuf | `.proto` | `all`, `none`, `inline`, `block` | -| Python | `.py`, `.pyi`, `.pyw` | `all`, `none` | -| YAML | `.yml`, `.yaml` | `all`, `none` | -| XML, HTML | `.xml`, `.html`, `.htm` | `all`, `none` | -| Visual Basic | `.vb`, `.bas`, `.vbs`, `.vbscript` | `all`, `none`, `documentation`, `regular` | - -## Advanced use cases - -### Joining several parts of code into one fragment - -A named fragment may consist of one or several pieces declared in a single file. -When rendered, the pieces that belong to a single fragment are joined together. -It is possible to specify a separator between the joined pieces, -see [Configuration](README.md#arguments) for the corresponding parameter. - -Here is an example of how a multi-piece fragment is rendered. - -**Code:** - -```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - - // #docfragment "Standard Object methods" - public int hashCode() { - // Method logic. - return hash; - } - // #enddocfragment "Standard Object methods" - - /* Here goes irrelevant code */ - - // #docfragment "Standard Object methods" - public boolean equals(Object anObject) { - // Method logic. - return false; - } - // #enddocfragment "Standard Object methods" - - /* Here goes more irrelevant code */ - - // #docfragment "Standard Object methods" - public String toString() { - return this; - } - // #enddocfragment "Standard Object methods" -} -``` - -**Result:** - -```java -public int hashCode() { - // Method logic. - return hash; -} -... -public boolean equals(Object anObject) { - // Method logic. - return false; -} -... -public String toString() { - return this; -} -``` - -### Declaring multiple fragments in one line - -Multiple fragments can start or end on a single line and may overlap: - -```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - - // #docfragment "Standard Object methods", "All methods" - public int hashCode() { - // Method logic. - return hash; - } - - public boolean equals(Object anObject) { - // Method logic. - return false; - } - - public String toString() { - return this; - } - // #enddocfragment "Standard Object methods" - - public boolean startsWith(String prefix, int toffset) { - // Method logic. - return true; - } - // #enddocfragment "All methods" -} -``` - -### Usage with other languages - -The fragments can also be used in other languages: - -```html - - - - - - - -``` diff --git a/PROJECT.md b/PROJECT.md index f52a40db..7807e430 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -1,8 +1,8 @@ # Project -This document gives agents and contributors the project overview and package -map. For agent operating policy, read [AGENTS.md](AGENTS.md). For implementation -details, use the matching discovered skill. +This document gives agents and contributors the project overview, package map, +documentation ownership, and CI notes. For agent operating policy, read +[AGENTS.md](AGENTS.md). ## Overview @@ -31,10 +31,37 @@ them inside code fences, and checks whether existing snippets are up-to-date. segment is `type`, but the Go package identifier is `_type` because `type` is a Go keyword. - `test/resources/`: parser, embedding, configuration, and source-code fixtures. +- `showcase/`: executable user guide and end-to-end example suite. -## Documentation +## Documentation Ownership -- `README.md`: user-facing usage. -- `EMBEDDING.md`: embedding syntax and examples. -- `.agents/skills/`: task-specific rules for implementation, testing, writing, - and review. +- `README.md`: project entry point, short run/build instructions, and links to + the complete guide. +- `showcase/README.md`: complete user guide entry point and runnable workflow. +- `showcase/configuration/README.md`: command-line flags, YAML configuration, + source roots, include/exclude patterns, and multiple embedding targets. +- `showcase/embedding/README.md`: `` instruction syntax, source + selection, fragments, patterns, comment filtering, and rendered examples. +- `PROJECT.md`: project map, package ownership, documentation ownership, and CI + notes for contributors and agents. +- `AGENTS.md`: repository operating policy for agents. + +Keep usage details in the showcase. Keep architecture and ownership details in +this file. Keep the root README short. + +## CI + +This repository is configured with these GitHub workflows: + +- `check`: runs linting, the normal Go test suite, and the showcase end-to-end + tests across supported platforms. +- `build_binaries`: builds binaries on pushes to `master`. + +`build_binaries` uses a deploy key instead of the default GitHub Actions bot so +it can push through branch protection. If it must be rotated: + +1. Generate an SSH key pair for GitHub: + `ssh -i ~/.ssh/workflow_deploy_key -T git@github.com`. +2. Add `workflow_deploy_key.pub` as a GitHub deploy key with write access. +3. Add `workflow_deploy_key` as a repository secret named + `WORKFLOW_DEPLOY_KEY`. diff --git a/README.md b/README.md index b4fa10c6..0885c20e 100644 --- a/README.md +++ b/README.md @@ -1,199 +1,68 @@ # Embed Code -Embed Code provides a way to embed code snippets into Markdown files. -This allows developers to easily include code examples within their documentation. +Embed Code is a Go command-line tool that keeps documentation snippets in sync +with source files. It scans Markdown and HTML documents for `` +instructions, resolves the requested source content, and manages the following +code fence. -Previously, we used the `embed-code` utility written in [Ruby for Jekyll][embed-code-jekyll]. -Since we standardize our sites on Hugo, we rewrote the utility in Go. -This project is the implementation of `embed-code` utility written in Go. +This project replaces the earlier [`embed-code` utility for Ruby/Jekyll][embed-code-jekyll]. -## Key features -- Extracts code fragments from source files and embeds them into documentation. -- Verifies that embedded code samples are up-to-date with the source. -- Supports configuration via command-line arguments or a YAML file. -- Allows embedding specific named fragments or matching code using line patterns. -- Maps multiple code sources to various documentation folders. +## Start Here -For the details of the usage in the documentation and the code, please refer to the [EMBEDDING.md](EMBEDDING.md). +The complete usage guide lives in the [showcase](showcase/README.md). It covers +configuration, embedding instructions, check mode, embed mode, expected +failures, and runnable examples. -For a runnable guide with positive examples, negative examples, and YAML -configuration shapes, see the [embed-code showcase](showcase/README.md). -The showcase is an opt-in end-to-end test and is not part of the normal -`go test ./...` flow. +## What It Does -## Running +- Embeds whole files, named fragments, source ranges, or matching source lines. +- Supports multiple named source roots for one documentation tree. +- Filters comments when examples should omit implementation notes. +- Processes Markdown and HTML documents. +- Runs in `check` mode for CI and `embed` mode to update documentation. -Embed Code operates in two modes: +## Run -1. **Embedding**: Scans documentation files for `` tags and performs the requested embeddings, - overwriting the content of the target documentation files. +Use a prebuilt binary from [Releases](): -2. **Up-to-Date Check**: Compares the content under `` tags with the corresponding source code fragments. - If they differ, the tool reports which files are out-of-date. - -The mode is selected using the mandatory `-mode` argument: -- `embed`: Performs the embedding process. -- `check`: Checks if embeddings are up-to-date. - -The tool can be run as a pre-compiled binary or via the Go compiler (requires Go [installed](#installation)). -Binaries are located in the `./bin` directory. - -The code and documentation files must be prepared for embedding. -The instructions are provided in the [Setting up documentation and code files](EMBEDDING.md) document. - -### Running the binary - -To run the binary, use: ```bash -./bin/ [arguments] +./bin/embed-code-linux -mode=check -config-path=showcase/embedding/embed-code.yml ``` -### Running the Go file +Or run it with Go: -#### Running with Go - -If you have Go installed (version `1.22.1` recommended), you can run the tool directly: ```bash -go run ./main.go [arguments] +go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml ``` -### Arguments - -The available arguments are: - * `-mode`: (Mandatory) The execution mode: `embed` or `check`. - * `-code-path`: (Optional) Path to the source code root directory. - * `-docs-path`: (Optional) Path to the documentation root directory. - * `-config-path`: (Optional) Path to a YAML configuration file containing `code-path` and `docs-path`. - * `-doc-includes`: (Optional) Comma-separated glob patterns for documentation files to include. Defaults to `"**/*.md,**/*.html"`. - * `-doc-excludes`: (Optional) Comma-separated glob patterns for documentation files to exclude. - * `-separator`: (Optional) String used to separate joined code fragments. Defaults to `...`. - * `-info`: (Optional) Enables info-level logging when set to `true`. - * `-stacktrace`: (Optional) Prints stack traces for panics when set to `true`. - -Even though the `code-path`, `docs-path`, and `config-path` arguments are optional, -Embed Code still requires the root directories for code and documentation to be set. -This can be done in one of two ways: - -1. Provide the `code-path` and `docs-path` arguments, in this case the roots are read directly from the provided paths. -2. Provide the `config-path` argument, in this case the roots are read from the given configuration file. +Use `-mode=embed` when documentation should be rewritten with current source +content. See the [configuration guide](showcase/configuration/README.md) for +all command-line flags and YAML options. -If neither of these options is provided, the embedding process will fail. -If both options are set, the embedding will also fail. +## Build -### Configuration file +Use Go `1.26.4`. -Optional settings can be defined in a YAML configuration file: - -```yaml -code-path: path/to/code/root -docs-path: path/to/docs/root -doc-excludes: "**/*-old.*,**/deprecated/*.*" -``` - -For multiple independent documentation targets, use `embeddings` instead: - -```yaml -embeddings: - - name: java - code-path: path/to/code/root/java - docs-path: path/to/java/docs - - name: kotlin - code-path: - - name: samples - path: path/to/code/root/kotlin-samples - - name: runtime - path: path/to/code/root/kotlin-runtime - docs-path: path/to/kotlin/docs - separator: "---" -``` - -The available fields for the configuration file are: - * `code-path`: (Mandatory) Path to the source code root. - May be represented as: - * single path - ```yaml - code-path: path/to/code/root - ``` - * multiple named paths: - ```yaml - code-path: - - name: examples - path: path/to/code/root1 - - name: production - path: path/to/code/root2 - ``` - When a named path is specified, fragments must be referenced in the embedding instructions - using the corresponding path name: - ```md - - ``` - **Do not forget the dollar sign (`$`) before the path name.** - - Code source names must be unique. A configuration may use either one unnamed - code source or one or more named code sources, but named and unnamed sources - cannot be mixed. - - * `docs-path`: (Mandatory) Path to the documentation root. - * `doc-excludes`: (Optional) Glob patterns for documentation files to exclude. - It may be represented as a comma-separated string list or as a YAML sequence. - * `doc-includes`: (Optional) Glob patterns for documentation files to include. - It may be represented as a comma-separated string list or as a YAML sequence. - * `separator`: (Optional) Separator for fragments. - * `info`: (Optional) Enables info-level logging. - * `stacktrace`: (Optional) Prints stack traces for panics. - * `embeddings`: (Optional) A list of complete embedding configurations for multiple - documentation targets. When `embeddings` is set, do not set root-level `code-path` - or `docs-path`. Define `code-path`, `docs-path`, and optional settings inside each entry. - Each entry must set a unique `name`. - -These settings have the same role as the command-line arguments. - -## Installation - -* Go to https://go.dev/doc/install. -* Make sure your Go installed successfully with the command - ```bash - go version - ``` - -## Compilation - -Pre-compiled binaries are available in the `./bin` directory. -However, you can also compile the utility manually if Go is [installed](#installation). - -Navigate to the project root and run: ```bash go build -trimpath -o embed-code main.go ``` -There may be issues when running `go build` outside of the directory containing `main.go`, -even if the path is specified correctly. +This creates `embed-code` on Unix-like systems or `embed-code.exe` on Windows. +The `-trimpath` flag prevents local absolute paths from appearing in stack +traces. -This command creates an executable named `embed-code` (or `embed-code.exe` on Windows). -For further information, please refer to the [docs](https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies). +## Development -Without the `-trimpath` flag, Go includes absolute file paths in stack traces -based on the system where the binary was built. +Run the normal test suite: -Run the following command to build binaries for macOS, Windows and Ubuntu: ```bash -mkdir -p bin && \ -GOOS=darwin GOARCH=amd64 go build -trimpath -o bin/embed-code-macos main.go && chmod +x bin/embed-code-macos && \ -GOOS=windows GOARCH=amd64 go build -trimpath -o bin/embed-code-windows.exe main.go && \ -GOOS=linux GOARCH=amd64 go build -trimpath -o bin/embed-code-linux main.go && chmod +x bin/embed-code-linux +go test ./... ``` -## Development Notes - -This repository is configured with the following GitHub workflows: -- `check` — runs tests across different platforms. -- `build_binaries` — builds binaries on push to the `master` branch. - > Note: This workflow uses a **Deploy Key** instead of the default GitHub Actions bot - > to bypass the `master` branch protection against direct pushes. - > - > If it is necessary to update the Deploy Key, follow these steps: - > 1. Generate an SSH key pair for GitHub: `ssh -i ~/.ssh/workflow_deploy_key -T git@github.com`. - > 2. Add the public key (`workflow_deploy_key.pub`) as a **Deploy Key** in GitHub with write access. - > 3. Add the private key (`workflow_deploy_key`) as a repository secret named `WORKFLOW_DEPLOY_KEY`. +Run the executable showcase: + +```bash +go test -tags showcase ./showcase +``` [embed-code-jekyll]: https://github.com/SpineEventEngine/embed-code diff --git a/showcase/README.md b/showcase/README.md index fe6d12f7..86eec3a2 100644 --- a/showcase/README.md +++ b/showcase/README.md @@ -1,31 +1,42 @@ # Embed Code Showcase -This is an executable showcase guide to `embed-code-go` and the end-to-end tests. +This is the complete usage guide for `embed-code-go`. It is also executable: +the showcase test runs the examples through the real CLI so the guide stays in +sync with the application. -## How To Use This Guide +## Workflow -This guide is divided into two categories: +1. Configure source roots and documentation roots. +2. Add an `` instruction followed by a managed code fence. +3. Run check mode in CI to detect stale snippets. +4. Run embed mode when documentation should be rewritten from source. -1. [Configuration](configuration/README.md) - describes how to configure the whole embed-code application. -2. [Embedding](embedding/README.md) - describes how to work with the embedding instructions. +## Guide Map -## How To Run Tests +- [Configuration](configuration/README.md): how the CLI finds source files and documentation files. +- [Embedding](embedding/README.md): how instructions select and render source content. +- [Positive examples](embedding/positive): runnable examples that should pass. +- [Negative examples](embedding/negative/docs): intentionally broken examples + that document diagnostics. + +## Run The Showcase Run commands from the repository root. -Run the opt-in end-to-end test with the `showcase` build tag: +The end-to-end suite checks positive examples, expected failures, and all +configuration shapes: ```bash go test -tags showcase ./showcase ``` -Verify the positive embedding examples: +Check the positive embedding examples directly: ```bash go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml ``` -Verify the configuration examples: +Check the configuration examples directly: ```bash go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml diff --git a/showcase/configuration/README.md b/showcase/configuration/README.md index 465a1f44..6d493fd3 100644 --- a/showcase/configuration/README.md +++ b/showcase/configuration/README.md @@ -1,11 +1,64 @@ -# Configuration Examples +# Configuration -This folder is a runnable guide to YAML configuration. Start with the smallest -working config, then add only the options your documentation needs. +This guide owns command-line and YAML configuration. Start with the smallest +working shape, then add only the options your documentation needs. + +Run commands from the repository root. + +## Configuration Sources + +Embed Code needs source roots and a documentation root. Provide them in exactly +one of these ways: + +- Direct command-line roots: `-code-path` and `-docs-path`. +- A YAML file selected with `-config-path`. + +Do not combine direct roots with `-config-path`. + +## Command-Line Arguments + +- `-mode`: required execution mode, either `embed` or `check`. +- `-code-path`: source root directory. Use with `-docs-path`. +- `-docs-path`: documentation root directory. Use with `-code-path`. +- `-config-path`: YAML configuration file. +- `-doc-includes`: comma-separated documentation glob patterns to include. + Defaults to `"**/*.md,**/*.html"`. +- `-doc-excludes`: comma-separated documentation glob patterns to exclude. +- `-separator`: text inserted between joined fragment parts. Defaults to `...`. +- `-info`: enables info-level logging when set to `true`. +- `-stacktrace`: prints stack traces for panics when set to `true`. + +Direct roots are useful for small projects: + +```bash +go run ./main.go \ + -mode=check \ + -code-path=showcase/code/java \ + -docs-path=showcase/configuration/docs/single-source +``` + +YAML is easier to maintain when you need named roots, include/exclude patterns, +or multiple documentation targets. + +## YAML Fields + +- `code-path`: source root. Use a string for one unnamed root or a list of + `{name, path}` entries for named roots. Do not mix named and unnamed roots. +- `docs-path`: documentation root. +- `doc-includes`: string or list of glob patterns to include. +- `doc-excludes`: string or list of glob patterns to exclude. +- `separator`: text inserted between joined fragment parts. +- `info`: enables info-level logging. +- `stacktrace`: prints stack traces for panics. +- `embeddings`: list of complete configurations for independent documentation + targets. When `embeddings` is set, define `code-path`, `docs-path`, and + optional settings inside each entry instead of at the root. + +Each `embeddings` entry must have a unique `name`. ## Minimal Config -A configuration needs one source root and one documentation root: +A YAML configuration needs one source root and one documentation root: ```yaml code-path: showcase/code/java @@ -14,14 +67,14 @@ docs-path: showcase/configuration/docs/single-source This config is shown by [single-source.yml](single-source.yml). -The application scans files under `docs-path`, finds `` instructions, -and resolves each instruction's `file` path from `code-path`. For example, see -instruction in [greeting.md](docs/single-source/greeting.md). +The application scans files under `docs-path`, finds `` +instructions, and resolves each instruction's `file` path from `code-path`. For +example, see the instruction in [greeting.md](docs/single-source/greeting.md). -Relative paths in `code-path` and `docs-path` are resolved -from the command's current working directory. +Relative paths in `code-path` and `docs-path` are resolved from the command's +current working directory. -Run this example (from the project root): +Run this example: ```bash go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml @@ -69,7 +122,7 @@ docs-path: showcase/configuration/docs/named-sources ``` This shape is shown by [named-sources.yml](named-sources.yml). Its docs live in -[showcase/configuration/docs/named-sources/](docs/named-sources). +[docs/named-sources](docs/named-sources). Instructions choose a source root with the `$name` prefix: @@ -102,20 +155,9 @@ embeddings: ``` This shape is shown by [multiple-embeddings.yml](multiple-embeddings.yml). It -processes [showcase/configuration/docs/multiple/java/](docs/multiple/java) and -[showcase/configuration/docs/multiple/kotlin/](docs/multiple/kotlin) in one run. - -```bash -go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml -``` - -## All Configuration Checks - -Run commands from the project root. +processes [docs/multiple/java](docs/multiple/java) and +[docs/multiple/kotlin](docs/multiple/kotlin) in one run. ```bash -go run ./main.go -mode=check -config-path=showcase/configuration/single-source.yml -go run ./main.go -mode=check -config-path=showcase/configuration/named-sources.yml -go run ./main.go -mode=check -config-path=showcase/configuration/include-exclude.yml go run ./main.go -mode=check -config-path=showcase/configuration/multiple-embeddings.yml ``` diff --git a/showcase/embedding/README.md b/showcase/embedding/README.md index f9a20032..09432fb9 100644 --- a/showcase/embedding/README.md +++ b/showcase/embedding/README.md @@ -1,8 +1,8 @@ -# Embedding Examples +# Embedding Instructions -This folder is a runnable guide to embedding instructions. The positive -examples show supported features, and the negative examples show the failures a -user should expect when an instruction is malformed or stale. +This folder is a runnable guide to `` instructions. The positive +examples show supported features, and the negative examples show failures users +should expect when an instruction is malformed or stale. Run the positive examples from the repository root: @@ -12,52 +12,53 @@ go run ./main.go -mode=check -config-path=showcase/embedding/embed-code.yml ## Feature Examples -### Simple Embedding +### Instruction Shape -- [Whole file source](positive/whole-file-source.md) - shows how to embed the whole source file. -- [Instruction tag](positive/instruction-tag.md) - shows the preferred way to use `` tag. -- [Named source root](positive/named-source-root.md) - shows how to use different configured source trees. +- [instruction-tag.md](positive/instruction-tag.md) + explains managed code fences, supported tag forms, attributes, and source + selection rules. +- [whole-file-source.md](positive/whole-file-source.md) + embeds a complete source file. +- [named-source-root.md](positive/named-source-root.md) + selects one configured source tree with `$name/`. ### Line, Range, And Glob Matching -- [Source line pattern](positive/source-line-pattern.md) +- [source-line-pattern.md](positive/source-line-pattern.md) embeds the first source line that matches a `line` pattern. -- [Start and end patterns](positive/start-end-pattern.md) +- [start-end-pattern.md](positive/start-end-pattern.md) embeds an inclusive source range selected by `start` and `end`. -- [Multi-line patterns](positive/multi-line-pattern.md) +- [multi-line-pattern.md](positive/multi-line-pattern.md) uses `\n` to match consecutive source lines. -- [Pattern escaping](positive/pattern-escaping.md) - shows how to escape special characters. +- [pattern-escaping.md](positive/pattern-escaping.md) + shows how to match literal glob characters. ### Fragments -- [Named fragment](positive/named-fragment.md) +- [named-fragment.md](positive/named-fragment.md) embeds a region wrapped with `#docfragment` and `#enddocfragment` markers. -- [Multi-part fragment separator](positive/multi-part-fragment-separator.md) +- [multi-part-fragment-separator.md](positive/multi-part-fragment-separator.md) joins repeated fragment parts with the configured separator. -- [Overlapping fragments](positive/overlapping-fragments.md) +- [overlapping-fragments.md](positive/overlapping-fragments.md) shows fragment markers that share source lines. ### Rendered Content And Documents -- [Comment filtering](positive/comment-filtering.md) - shows how to omit comments in the source code. -- [Markdown fence shielding](positive/markdown-fence-shielding.md) +- [comment-filtering.md](positive/comment-filtering.md) + compares `comments` modes and lists language support. +- [markdown-fence-shielding.md](positive/markdown-fence-shielding.md) shows that instruction-looking text inside ordinary code fences is inert. -- [HTML showcase](positive/html-showcase.html) - shows that HTML documents can be processed when the include patterns allow them. +- [html-showcase.html](positive/html-showcase.html) + shows that HTML documents can be processed when include patterns allow them. ## Negative Examples -The negative examples are intentionally broken and should fail. -Use them to recognize common diagnostics: +The negative examples are intentionally broken and should fail. Use them to +recognize common diagnostics: ```bash go run ./main.go -mode=check -config-path=showcase/embedding/negative/processing-errors.yml go run ./main.go -mode=check -config-path=showcase/embedding/negative/stale.yml ``` -The cases live in [showcase/embedding/negative/docs/](negative/docs). +The cases live in [negative/docs](negative/docs). diff --git a/showcase/embedding/positive/comment-filtering.md b/showcase/embedding/positive/comment-filtering.md index 27c6be18..371a8400 100644 --- a/showcase/embedding/positive/comment-filtering.md +++ b/showcase/embedding/positive/comment-filtering.md @@ -22,9 +22,22 @@ Supported modes are: Comment support depends on the source file extension. Unknown extensions are embedded unchanged. Not every supported language distinguishes documentation, regular, inline, and block comments, so unsupported categories simply have no -comments to keep. See -[Comment filtering](../../../EMBEDDING.md#comment-filtering) for the full -language matrix. +comments to keep. + +## Language Support + +| Language | Extensions | Supported `comments` modes | +|------------------------|----------------------------------------------------------|--------------------------------------------------------------| +| Java, Kotlin, Groovy | `.java`, `.kt`, `.kts`, `.groovy` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | +| C# | `.cs` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | +| C, C++ | `.c`, `.h`, `.cc`, `.cpp`, `.cxx`, `.hh`, `.hpp`, `.hxx` | `all`, `none`, `inline`, `block` | +| JavaScript, TypeScript | `.js`, `.jsx`, `.ts`, `.tsx` | `all`, `none`, `documentation`, `regular`, `inline`, `block` | +| Go | `.go` | `all`, `none`, `inline`, `block` | +| Protobuf | `.proto` | `all`, `none`, `inline`, `block` | +| Python | `.py`, `.pyi`, `.pyw` | `all`, `none` | +| YAML | `.yml`, `.yaml` | `all`, `none` | +| XML, HTML | `.xml`, `.html`, `.htm` | `all`, `none` | +| Visual Basic | `.vb`, `.bas`, `.vbs`, `.vbscript` | `all`, `none`, `documentation`, `regular` | The examples below embed the same Java file with different `comments` modes so the rendered output can be compared directly. diff --git a/showcase/embedding/positive/instruction-tag.md b/showcase/embedding/positive/instruction-tag.md index 71b09a4d..acd2ea48 100644 --- a/showcase/embedding/positive/instruction-tag.md +++ b/showcase/embedding/positive/instruction-tag.md @@ -4,8 +4,21 @@ An embedding instruction is an XML-like tag placed immediately before the code fence that embed-code should manage. The tag contains source-selection attributes such as `file`, `fragment`, `line`, etc. -The following Markdown fences keeps the language label used by renderers for -syntax highlighting. +## Managed Code Fence + +An instruction must be followed immediately by a Markdown code fence. Embed mode +replaces the fence content. Check mode compares the fence content with current +source and reports stale files without rewriting them. + +Use a language label on the fence for syntax highlighting: + +````markdown + +```java +``` +```` + +The same rule applies inside HTML documents when include patterns allow `.html` files. ## Paired Tag diff --git a/showcase/embedding/positive/named-source-root.md b/showcase/embedding/positive/named-source-root.md index bd96d7e3..5dc6edc1 100644 --- a/showcase/embedding/positive/named-source-root.md +++ b/showcase/embedding/positive/named-source-root.md @@ -6,8 +6,8 @@ configured root happens to contain a matching relative path. ## How It Works -In config file, each entry in `code-path` can have a `name` and a `path`. -When an instruction starts its `file` value with `$name/`, embed-code selects +In config file, each entry in `code-path` can have a `name` and a `path`. +When an instruction starts its `file` value with `$name/`, embed-code selects only that named root and then resolves the remaining relative path inside it. [embed-code.yml](../embed-code.yml) defines `java`, `kotlin`, and `text` diff --git a/showcase/embedding/positive/source-line-pattern.md b/showcase/embedding/positive/source-line-pattern.md index 24f566a8..3ebb831c 100644 --- a/showcase/embedding/positive/source-line-pattern.md +++ b/showcase/embedding/positive/source-line-pattern.md @@ -5,7 +5,7 @@ fragment or range. ## How It Works -The `line` attribute uses the same glob-style pattern syntax as `start` and `end`. +The `line` attribute uses the same glob-style pattern syntax as `start` and `end`. By default, embed-code behaves as if `*` exists at the beginning and end of the pattern, so `Hello` can match any source line that contains `Hello`. Use `^` or `$` when the match must start or end at a line boundary. diff --git a/showcase/embedding/positive/start-end-pattern.md b/showcase/embedding/positive/start-end-pattern.md index 219a545e..d50fc9ad 100644 --- a/showcase/embedding/positive/start-end-pattern.md +++ b/showcase/embedding/positive/start-end-pattern.md @@ -12,7 +12,7 @@ By default, embed-code behaves as if `*` exists at the beginning and end of the pattern, so `Hello` can match any source line that contains `Hello`. Use `^` or `$` when the match must start or end at a line boundary. -If `start` is omitted, the range starts at the beginning of the file. +If `start` is omitted, the range starts at the beginning of the file. If `end` is omitted, it continues to the end of the file. ## Embedding Instruction diff --git a/showcase/embedding/positive/whole-file-source.md b/showcase/embedding/positive/whole-file-source.md index 9bc3b2b8..f85a8136 100644 --- a/showcase/embedding/positive/whole-file-source.md +++ b/showcase/embedding/positive/whole-file-source.md @@ -6,7 +6,7 @@ small source file. This is the smallest useful instruction: it only needs ## How It Works -The `file` attribute is resolved from the configured source roots. +The `file` attribute is resolved from the configured source roots. In this example, the `$java` prefix selects the Java source root from [embed-code.yml](../embed-code.yml) before resolving `org/showcase/Greeting.java`. From cbe1a3ce5c968eb2a22496de85f581ca84f88756 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 18 Jun 2026 18:37:04 +0200 Subject: [PATCH 22/22] Fix merge issues. --- .agents/skills/review-docs/SKILL.md | 12 ++++---- .agents/skills/writer/SKILL.md | 22 ++++++++------ README.md | 3 +- .../embedding/positive/instruction-tag.md | 30 +++++++++++++++++++ 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/.agents/skills/review-docs/SKILL.md b/.agents/skills/review-docs/SKILL.md index 3f565412..d020f45d 100644 --- a/.agents/skills/review-docs/SKILL.md +++ b/.agents/skills/review-docs/SKILL.md @@ -2,8 +2,8 @@ name: review-docs description: > Reviews documentation changes in embed-code-go: Go doc comments, inline - comments, README.md, EMBEDDING.md, PROJECT.md, AGENTS.md, skills, Markdown - fixtures, and user-facing examples. Use when a diff touches prose, + comments, README.md, showcase guides, PROJECT.md, AGENTS.md, skills, + Markdown fixtures, and user-facing examples. Use when a diff touches prose, documentation comments, embedding syntax docs, command examples, architecture/package maps, or agent instructions. Read-only; does not run builds unless explicitly asked. @@ -61,10 +61,10 @@ and repository guidance. Do not duplicate `writer` for authoring strategy, root, check mode, embed mode. - **No orphans.** A paragraph, list item, or table cell must not end with a final line containing only one word. Flag it and propose a reflow or rewrite. -- **Project docs keep their ownership.** `README.md` is user-facing, - `EMBEDDING.md` owns embedding syntax, `PROJECT.md` owns project description, - map, and compact doc pointers, skills own implementation details, and - `AGENTS.md` owns agent policy. +- **Project docs keep their ownership.** `README.md` is the short user-facing + entry point, showcase docs own complete usage details, `PROJECT.md` owns + project description, map, and compact doc pointers, skills own implementation + details, and `AGENTS.md` owns agent policy. ### C. Embedding-Specific Documentation diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 9795f191..d8ef5523 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -2,10 +2,10 @@ name: writer description: > Writes, edits, and restructures embed-code-go documentation. Use when asked - to create or update README.md, EMBEDDING.md, PROJECT.md, AGENTS.md, skills, - Markdown fixtures, contributor notes, examples, command snippets, Go doc - comments, or inline explanatory comments. Verifies claims against current Go - code, tests, fixtures, and project flows. + to create or update README.md, showcase guides, PROJECT.md, AGENTS.md, + skills, Markdown fixtures, contributor notes, examples, command snippets, + Go doc comments, or inline explanatory comments. Verifies claims against + current Go code, tests, fixtures, and project flows. --- # Write documentation @@ -22,9 +22,13 @@ description: > Prefer updating an existing document over creating a new one. -- `README.md`: user-facing overview, setup, configuration, modes, and flags. -- `EMBEDDING.md`: embedding syntax, source markers, patterns, fences, - separators, and comment modes. +- `README.md`: user-facing overview, short run/build instructions, and links + to the complete guide. +- `showcase/README.md`: complete user guide entry point and runnable workflow. +- `showcase/configuration/README.md`: command-line flags, YAML configuration, + source roots, include/exclude patterns, and multiple embedding targets. +- `showcase/embedding/README.md`: embedding instruction map; detailed syntax + belongs in the related `showcase/embedding/positive/*.md` feature page. - `PROJECT.md`: project overview, project map, and compact doc pointers. - `AGENTS.md`: agent operating policy and repository-wide rules. - `.agents/skills//SKILL.md`: language, testing, writing, review, project @@ -39,7 +43,7 @@ Prefer updating an existing document over creating a new one. - For YAML configuration, check `cli/`, `configuration/`, and config fixtures under `test/resources/config_files/`. - For embedding syntax, check `embedding/parsing/`, `embedding/processor.go`, - `embedding/parsing/instruction.go`, and `EMBEDDING.md`. + `embedding/parsing/instruction.go`, and `showcase/embedding/`. - For source fragments and patterns, check `fragmentation/`, `embedding/parsing/pattern.go`, and relevant source fixtures. - For comments modes, check `embedding/commentfilter/`. @@ -60,7 +64,7 @@ Prefer updating an existing document over creating a new one. code fence, fragment, source root, docs root, include pattern, exclude pattern. - Do not leave orphans in prose: no paragraph, list item, or table cell should end with a final line containing only one word. Reflow or rewrite the text. -- Do not duplicate long explanations between `README.md`, `EMBEDDING.md`, and +- Do not duplicate long explanations between `README.md`, the showcase, and `AGENTS.md`; link to the owning document instead. ## Go Doc Comment Guidance diff --git a/README.md b/README.md index 0885c20e..7e175572 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ failures, and runnable examples. ## Run -Use a prebuilt binary from [Releases](): +Use a prebuilt binary from [GitHub Releases][releases]: ```bash ./bin/embed-code-linux -mode=check -config-path=showcase/embedding/embed-code.yml @@ -66,3 +66,4 @@ go test -tags showcase ./showcase ``` [embed-code-jekyll]: https://github.com/SpineEventEngine/embed-code +[releases]: https://github.com/SpineEventEngine/embed-code-go/releases diff --git a/showcase/embedding/positive/instruction-tag.md b/showcase/embedding/positive/instruction-tag.md index acd2ea48..7f080594 100644 --- a/showcase/embedding/positive/instruction-tag.md +++ b/showcase/embedding/positive/instruction-tag.md @@ -4,6 +4,36 @@ An embedding instruction is an XML-like tag placed immediately before the code fence that embed-code should manage. The tag contains source-selection attributes such as `file`, `fragment`, `line`, etc. +## Attributes And Selection Rules + +Every instruction needs a `file` attribute. The value is resolved from the +configured `code-path`. With named source roots, start the value with `$name/`, +such as `$java/org/showcase/Greeting.java`. With one unnamed source root, use a +path relative to that root. + +Use one source-selection shape per instruction: + +- `file` alone embeds the whole source file. +- `file` with `fragment` embeds one named fragment. +- `file` with `line` embeds the first matching source line, or consecutive + source lines when the pattern contains `\n`. +- `file` with `start`, `end`, or both embeds an inclusive source range. Without + `start`, the range begins at the first source line. Without `end`, it + continues through the last source line. + +The `fragment` attribute cannot be combined with `start`, `end`, or `line`. +The `line` attribute cannot be combined with `start` or `end`. + +Add `comments` when embedded snippets should keep only selected source comments. +Supported values are `all`, `none`, `documentation`, `regular`, `inline`, and +`block`; omitting `comments` is the same as `comments="all"`. See +[comment-filtering.md](comment-filtering.md) for language support and examples. + +Pattern details live in [source-line-pattern.md](source-line-pattern.md), +[start-end-pattern.md](start-end-pattern.md), +[multi-line-pattern.md](multi-line-pattern.md), and +[pattern-escaping.md](pattern-escaping.md). + ## Managed Code Fence An instruction must be followed immediately by a Markdown code fence. Embed mode