Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2e81a4f
Show direct base classes and interfaces in type signatures
Malcolmnixon Jun 8, 2026
4ed528c
Update README badges to match project template
Malcolmnixon Jun 8, 2026
b2b7073
Remove architecture qualifiers from Platform Support table
Malcolmnixon Jun 8, 2026
f446d41
Make Features section iconic with emoji icons
Malcolmnixon Jun 8, 2026
0aaabda
Cache CppGenerator clang runs in IClassFixture to speed up tests
Malcolmnixon Jun 8, 2026
c54b265
Add intra-doc links and External Types section for C# and C++
Malcolmnixon Jun 8, 2026
e0a52ee
Fix GenericParameter types being incorrectly linked in intra-doc reso…
Malcolmnixon Jun 8, 2026
90d17f4
Fix PR review issues: qualifier preservation, CompareTo tie-breaker, …
Malcolmnixon Jun 8, 2026
e4733f4
Add --search-paths and glob/exclude pattern support to cpp subcommand
Malcolmnixon Jun 8, 2026
03fd978
fix: normalize all generated markdown pages to start with H1 headings
Malcolmnixon Jun 8, 2026
44eabe6
fix: add missing inheritance signature tests for reqstream coverage
Malcolmnixon Jun 8, 2026
bdcaa8e
fix: only check stripped base name for std:: prefix in CppTypeLinkRes…
Jun 8, 2026
6b3e70b
refactor: replace --search-paths/--include-patterns/--exclude-pattern…
Jun 9, 2026
231fbe6
fix: update requirements traceability and design docs after header se…
Jun 9, 2026
bd1b695
fix: replace antipattern/unconfigured with cspell-clean terms and upd…
Jun 9, 2026
58f31c2
refactor: replace synthesized default globs and fix header pattern ma…
Jun 9, 2026
e5ae30f
fix: address PR review r4454762697 (6 comments)
Jun 9, 2026
325e284
feat: render delegate types with source-level delegate syntax
Jun 9, 2026
c2f535c
Strip arity suffix from generic type names in page files, headings, a…
Jun 9, 2026
63cb05e
Fix PR review r4454901364: namespace-prefix checks, FlattenArity, del…
Jun 9, 2026
88a4d99
feat: add C++ using type alias support
Jun 9, 2026
0e08fe5
feat: add APIMARK_CLANG_PATH env var; doc deleted functions and type …
Jun 9, 2026
b2e23f4
fix: PR review r4455192693 — delegate fallback, nsPath dots, test names
Jun 9, 2026
d33cf79
fix: linkify return types in WriteFreeFunctionContent and WriteFuncti…
Jun 9, 2026
ab38133
feat: render @note as blockquote; show default parameter values in si…
Jun 9, 2026
8bc9a1b
Add bool/int/float default parameter tests and fix bool literal parsing
Jun 9, 2026
71f6ff5
Fix type alias linkification and simplification
Jun 9, 2026
1781e53
Add nested class and class-scoped type alias support
Jun 9, 2026
096c108
Fix nullable array type rendering and reqstream test name mismatch
Jun 9, 2026
cd5bd24
Update output structure table with nested type and class-scoped alias…
Jun 9, 2026
198b0e4
Fix documentation issues flagged in PR #15 review
Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ words:
- fileassert
- frontmatter
- GHDL
- hrefs
- isysroot
- isystem
- libc
- libclang
- libclangsharp
- libext
- Linkification
- Linkify
- linkify
- misattributed
- msbuild
- MSBuild
Expand Down
33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# ApiMark

[![Build](https://github.com/DemaConsulting/ApiMark/actions/workflows/build_on_push.yaml/badge.svg)](https://github.com/DemaConsulting/ApiMark/actions/workflows/build_on_push.yaml)

<!-- IMPORTANT: All links in this file must be absolute URLs.
This file is distributed in packages and relative links will not resolve. -->

[![GitHub forks](https://img.shields.io/github/forks/demaconsulting/ApiMark?style=plastic)](https://github.com/demaconsulting/ApiMark/network/members)
[![GitHub stars](https://img.shields.io/github/stars/demaconsulting/ApiMark?style=plastic)](https://github.com/demaconsulting/ApiMark/stargazers)
[![GitHub contributors](https://img.shields.io/github/contributors/demaconsulting/ApiMark?style=plastic)](https://github.com/demaconsulting/ApiMark/graphs/contributors)
[![License](https://img.shields.io/github/license/demaconsulting/ApiMark?style=plastic)](https://github.com/demaconsulting/ApiMark/blob/main/LICENSE)
[![Build](https://img.shields.io/github/actions/workflow/status/demaconsulting/ApiMark/build_on_push.yaml)](https://github.com/demaconsulting/ApiMark/actions/workflows/build_on_push.yaml)
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=demaconsulting_ApiMark&metric=alert_status)](https://sonarcloud.io/dashboard?id=demaconsulting_ApiMark)
[![Security](https://sonarcloud.io/api/project_badges/measure?project=demaconsulting_ApiMark&metric=security_rating)](https://sonarcloud.io/dashboard?id=demaconsulting_ApiMark)
[![NuGet](https://img.shields.io/nuget/v/DemaConsulting.ApiMark.MSBuild?style=plastic)](https://www.nuget.org/packages/DemaConsulting.ApiMark.MSBuild)

## Overview

ApiMark generates compact, AI-friendly API reference documentation in Markdown
Expand All @@ -15,21 +22,23 @@ type page — consuming only as much context as the task requires.

## Features

- Generates compact Markdown API reference from source code and doc comments
- Gradual disclosure output: root index → namespace summary → type page → member detail
- C#/.NET support via Mono.Cecil and XML documentation comments
- C++ support via `clang -ast-dump=json` and Doxygen-style doc comments
- MSBuild task integration for `.csproj` and `.vcxproj`-based builds
- `dotnet tool` CLI (`apimark`) covering all supported languages
- Designed for AI consumption — minimal noise, explicit navigation links between levels
- 📄 **Compact Markdown Output** - AI-friendly API reference from source code
- 🔍 **Gradual Disclosure** - Index → namespace → type → member detail
- 🔷 **C#/.NET Support** - Mono.Cecil + XML documentation comments
- ➕ **C++ Support** - `clang -ast-dump=json` + Doxygen-style comments
- 🔧 **MSBuild Integration** - Auto-documents `.csproj` and `.vcxproj` builds
- 🖥️ **CLI Tool** - `apimark` dotnet tool covering all languages
- 🤖 **AI-Optimized** - Minimal noise, explicit navigation links
- 🌐 **Multi-Platform** - Windows, Linux, and macOS on .NET 8, 9, and 10
- ✅ **Self-Validation** - Built-in qualification tests for regulated environments

## Platform Support

| Platform | .NET | C++ |
| --- | --- | --- |
| Windows x64 | ✅ | ✅ |
| Linux x64 | ✅ | ✅ |
| macOS (Apple Silicon) | ✅ | ✅ |
| Windows | ✅ | ✅ |
| Linux | ✅ | ✅ |
| macOS | ✅ | ✅ |

## Prerequisites

Expand Down
46 changes: 42 additions & 4 deletions docs/design/api-mark-cpp/cpp-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ filter, and writes the full Markdown output tree.
the canonical `#include <path>` at the top, followed by the class
declaration, inheritance information, template parameters (for primary
templates), and grouped sub-tables with links to all member detail pages.
When the class is marked `final`, a `class ClassName final` declaration
line (including base class names when inheritance is present) is appended
to the signature block so consumers can see the constraint without opening
the header.
When the class is marked `final` or has direct base classes, a class
declaration line (e.g. `class FinalClass final`, `class Circle : public Shape`)
is appended to the signature block so consumers can see the constraint and
inheritance chain without opening the header.
- `factory.CreateMarkdown($"{qualifiedNamespace}/{typeName}", memberName)` —
dedicated page for every visible non-operator member. All non-operator members
always receive their own page, making navigation fully deterministic.
Expand Down Expand Up @@ -261,6 +261,44 @@ identified by the declaring class name.
function → `className`; non-constructor function → `fn.Name`; field → `field.Name`;
any other type → `className`.

**CppTypeLinkResolver** (internal): Resolves C++ type strings to Markdown link text
for use in table cells.

- *Constructor*: Accepts `IReadOnlyDictionary<string, string> knownTypes` — maps
fully-qualified C++ type names (e.g. `"fixtures::SampleClass"`) to documentation
page keys (e.g. `"fixtures/SampleClass"`). Built in `CppGenerator.Generate` from
the `namespaceDecls` dictionary.
- **Linkify** method: resolves a simplified C++ type string to a Markdown link or plain text.
- *Parameters*: `string cppTypeString`, `string currentFolder`, `ISet<CppExternalTypeInfo>
externalTypes` accumulator.
- *Returns*: a Markdown link `[Name](relative/path.md)` when the stripped base type
is in `knownTypes`; the original string unchanged otherwise; non-std external types
with a namespace are tracked in `externalTypes`.
- *Algorithm*: strip qualifiers (`const`, `volatile`, `*`, `&`, `&&`, trailing-const,
and template arguments) to isolate the base type name; reject primitives and `std::`
types immediately; look up the base type in `knownTypes` by exact qualified match,
then by short-name fallback; compute a relative path and return the link; if not
found and the type has a non-std namespace, track as external.
- **StripQualifiers** (internal static): removes C++ cv and reference qualifiers and
template arguments from a type string, returning the bare base type name.

**CppExternalTypeInfo** (internal record): Represents a non-standard external C++
type reference collected during table cell generation.

- *Properties*: `TypeString` (short type name without namespace), `Namespace`
(the C++ namespace using `::` separators).
- *Ordering*: implements `IComparable<CppExternalTypeInfo>` by `TypeString` so
`SortedSet<CppExternalTypeInfo>` produces alphabetically ordered tables.

**CppGenerator.WriteExternalTypesSection** (private static): Emits the
`## External Types` section at the bottom of a page when at least one external
type was referenced in table cells.

- *Parameters*: `IMarkdownWriter writer`, `SortedSet<CppExternalTypeInfo>
externalTypes`.
- *Algorithm*: Returns immediately when the set is empty; otherwise writes an
H2 heading `"External Types"` and a two-column table (`Type`, `Namespace`).

### Error Handling

CppGenerator throws `DirectoryNotFoundException` when a path in
Expand Down
53 changes: 53 additions & 0 deletions docs/design/api-mark-dot-net/dot-net-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ between members of different kinds or differently-cased method names) emit a
single combined page via `WriteCombinedMemberPage`; emit grouped sub-tables with
links; dispose the AssemblyDefinition.

**DotNetGenerator.BuildTypeSignature** (private): Builds a human-readable C#
declaration signature for a type definition, including direct base class and
interface names when present.

- *Parameters*: `TypeDefinition type` — the type to represent;
`string contextNamespace` — the namespace used to simplify base type and
interface names.
- *Returns*: `string` — a declaration of the form `public class Name`,
`public interface Name<T>`, or `public class Name : BaseClass, IInterface`
when direct inheritance is present.
- *Algorithm*: Determines the keyword (`class`, `interface`, `enum`, or `struct`)
from the type's flags; computes the `sealed` or `static` modifier for classes;
strips generic arity from the type name and appends generic parameter names when
present; collects the direct base class (skipping `System.Object`,
`System.ValueType`, `System.Enum`, and `System.MulticastDelegate`) and all
directly declared interfaces using TypeNameSimplifier to produce idiomatic C#
names; appends `: BaseClass, IInterface` when the collected list is non-empty.

**DotNetGenerator.WriteCombinedMemberPage** (private): Writes a single combined
Markdown page for a group of members whose sanitized file names collide on
case-insensitive filesystems.
Expand Down Expand Up @@ -120,6 +138,41 @@ a short human-readable kind string used in combined page H4 headings.
`MethodDefinition` with name `.ctor` → `"Constructor"`; `MethodDefinition` →
`"Method"`; all other types → `"Member"`.

**TypeLinkResolver** (internal): Resolves Mono.Cecil `TypeReference` instances
to Markdown link text for use in table cells.

- *Constructor*: Accepts `IReadOnlyList<string> rootNamespaces` — forwarded to
`DotNetGenerator.GetNamespaceFolderPath` when computing target page paths.
- **Linkify** method: resolves a `TypeReference` to a Markdown link string.
- *Parameters*: `TypeReference typeRef`, `string currentFolder` (path of the
containing file), `string contextNamespace`, `ISet<ExternalTypeInfo>
externalTypes` accumulator, optional `bool isNullableAnnotated`.
- *Returns*: a Markdown link when the type is intra-assembly; the original
simplified name otherwise; external non-System types are tracked in
`externalTypes`.
- *Rules*: `Nullable<T>` → `T?` via recursion; array types → `elementText[]`;
generic instance types linkify the container when intra-assembly; primitives
and `System.*` types render as plain text; non-System external types are
added to the accumulator.
- Intra-assembly detection: `TypeReference.Scope is ModuleDefinition`.

**ExternalTypeInfo** (internal record): Represents a non-standard external type
reference collected during table cell generation.

- *Properties*: `SimplifiedName` (display form, may include escaped generic
angle brackets), `Namespace` (the type's .NET namespace).
- *Ordering*: implements `IComparable<ExternalTypeInfo>` by `SimplifiedName` so
`SortedSet<ExternalTypeInfo>` produces alphabetically ordered tables.

**DotNetGenerator.WriteExternalTypesSection** (private static): Emits the
`## External Types` section at the bottom of a page when at least one external
type was referenced in table cells.

- *Parameters*: `IMarkdownWriter writer`, `SortedSet<ExternalTypeInfo>
externalTypes`.
- *Algorithm*: Returns immediately when the set is empty; otherwise writes an
H2 heading `"External Types"` and a two-column table (`Type`, `Namespace`).

### Error Handling

DotNetGenerator throws `FileNotFoundException` explicitly when XmlDocPath does not
Expand Down
39 changes: 28 additions & 11 deletions docs/design/api-mark-msbuild/api-mark-task.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ the `.targets` file when not explicitly set. If not set and the language is

**ApiMarkTask.ApiMarkIncludePaths**: `string` — MSBuild property
`$(ApiMarkIncludePaths)`; for the `cpp` language, a semicolon-separated list of
include paths. MSBuild uses semicolons as its standard list separator; the task
converts semicolons to commas when forwarding this value to the `--includes`
argument, which the tool parses as a comma-separated list.
include paths. Each entry is classified before forwarding: entries containing
`*` or `?` wildcards are forwarded via `--include-patterns`; entries starting
with `!` are stripped and forwarded via `--exclude-patterns`; plain directory
entries are forwarded via `--includes`.

**ApiMarkTask.ApiMarkLibraryName**: `string` — MSBuild property
`$(ApiMarkLibraryName)`; for the `cpp` language, the library name used as the
Expand All @@ -78,6 +79,18 @@ converted to commas when forwarding to the `--defines` argument.
passed to Clang (e.g. `c++17`, `c++20`). The `.targets` file defaults this to
`c++17` when not explicitly set.

**ApiMarkTask.ApiMarkClangPath**: `string` — MSBuild property
`$(ApiMarkClangPath)`; for the `cpp` language, the path to the clang
executable. Optional — when empty, clang is located automatically via PATH,
xcrun (macOS), or vswhere (Windows).
Comment thread
Malcolmnixon marked this conversation as resolved.
Outdated

**ApiMarkTask.ApiMarkSearchPaths**: `string` — MSBuild property
`$(ApiMarkSearchPaths)`; for the `cpp` language, a semicolon-separated list
of compiler-only search paths passed to Clang as `-I` flags for `#include`
resolution. Declarations from these paths are never documented. Semicolons are
converted to commas when forwarding to the `--search-paths` argument. Optional
— omitted when empty or not set.

**ApiMarkTask.ToolDllPath**: `string` — set by the `.targets` file to the path of
the bundled `ApiMark.Tool.dll` inside the NuGet package `tools/net8.0/` directory.
Not intended to be overridden by project authors.
Expand Down Expand Up @@ -105,14 +118,18 @@ language is `cpp` and `ApiMarkIncludePaths` is not set, return true (skip
generation with an informational log message); resolve the `dotnet` executable
path (check `DOTNET_HOST_PATH` environment variable first, then search `PATH`);
build the argument list from MSBuild properties according to language-specific
mapping (for `cpp`, semicolons in `ApiMarkIncludePaths` are converted to commas
because the tool's `--includes` argument accepts a comma-separated list; if
`ApiMarkLibraryName` is set, append `--library-name`; if `ApiMarkLibraryDescription`
is set, append `--library-description`; if `ApiMarkDefines` is set, convert
semicolons to commas and append `--defines`; if `ApiMarkCppStandard` is set,
append `--cpp-standard`); start the child process and pipe stdout lines as MSBuild
messages and stderr lines as MSBuild errors; wait for exit; return true if exit code
is zero, otherwise log an error with the exit code and return false.
mapping (for `cpp`, classify each `ApiMarkIncludePaths` entry: wildcard entries
with `*` or `?` are forwarded via `--include-patterns`; `!`-prefixed entries are
stripped and forwarded via `--exclude-patterns`; plain directory entries are
forwarded via `--includes`; if `ApiMarkLibraryName` is set, append
`--library-name`; if `ApiMarkLibraryDescription` is set, append
`--library-description`; if `ApiMarkDefines` is set, convert semicolons to commas
and append `--defines`; if `ApiMarkCppStandard` is set, append `--cpp-standard`;
if `ApiMarkClangPath` is set, append `--clang-path`; if `ApiMarkSearchPaths` is
set, convert semicolons to commas and append `--search-paths`); start the child
process and pipe stdout lines as MSBuild messages and stderr lines as MSBuild
errors; wait for exit; return true if exit code is zero, otherwise log an error
with the exit code and return false.

### Error Handling

Expand Down
6 changes: 4 additions & 2 deletions docs/design/api-mark-tool/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ can be tested independently.
file cannot be opened.
- `Context` (IDisposable) — exposes parsed flags and options as typed
properties (`Version`, `Help`, `Silent`, `Validate`, `Language`,
`Assembly`, `XmlDoc`, `Includes`, `Output`, `Visibility`, `IncludeObsolete`,
`Assembly`, `XmlDoc`, `Includes`, `SearchPaths`, `IncludePatterns`,
`ExcludePatterns`, `Output`, `Visibility`, `IncludeObsolete`,
`ResultsFile`, `HeadingDepth`, `ExitCode`) and provides `WriteLine` and
`WriteError` for all program output routing.

Expand All @@ -43,7 +44,8 @@ flags (`-v`, `--version`, `--help`, `--silent`, `--validate`, `--log`,
`--results`, `--depth`) are recognized anywhere in the argument list. The
first positional non-flag token (a token that does not start with `-`) is
captured as the language subcommand. Language-specific options (`--assembly`,
`--xml-doc`, `--includes`, `--output`, `--visibility`, `--include-obsolete`)
`--xml-doc`, `--includes`, `--search-paths`, `--include-patterns`,
`--exclude-patterns`, `--output`, `--visibility`, `--include-obsolete`)
may appear anywhere in the argument list after the language token is
recognized.

Expand Down
11 changes: 9 additions & 2 deletions docs/design/api-mark-tool/cli/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ argument array.
| `Language` | `string?` | `null` | First positional non-flag token |
| `Assembly` | `string?` | `null` | Path from `--assembly` |
| `XmlDoc` | `string?` | `null` | Path from `--xml-doc` |
| `Includes` | `string[]` | `[]` | Comma-split values from `--includes` |
| `Includes` | `string[]` | `[]` | Plain path entries from `--includes` (no wildcards, no `!`) |
| `SearchPaths` | `string[]` | `[]` | Comma-split values from `--search-paths` |
| `IncludePatterns` | `string[]` | `[]` | Glob patterns from `--include-patterns` or wildcard entries in `--includes` |
| `ExcludePatterns` | `string[]` | `[]` | Exclusion patterns from `--exclude-patterns` or `!`-prefixed entries in `--includes` |
| `Output` | `string?` | `null` | Directory from `--output` |
| `Visibility` | `string` | `"Public"` | Value from `--visibility` |
| `IncludeObsolete` | `bool` | `false` | `--include-obsolete` flag |
Expand All @@ -52,7 +55,11 @@ construction path.
- *Returns*: A new fully populated `Context` instance.
- *Algorithm*: Creates an `ArgumentParser`, calls `ParseArguments`, copies
all parsed values to a new `Context` via property initializers, and
optionally opens the log file.
optionally opens the log file. When processing `--includes`, the private
static `ClassifyIncludeEntries` helper is used to split entries into three
buckets: plain root paths (to `Includes`), wildcard entries (to
`IncludePatterns`), and `!`-prefixed exclusions (stripped and placed in
`ExcludePatterns`).
- *Preconditions*: `args` must be non-null.
- *Postconditions*: All properties reflect the parsed argument values;
log file is open if `--log` was specified.
Expand Down
5 changes: 5 additions & 0 deletions docs/design/api-mark-tool/program.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ the generator, and calls `Generate`.
- Throws `ArgumentException` for invalid `Visibility` values; throws
`NotSupportedException` for unrecognized or not-yet-implemented language
identifiers.
- For the `cpp` language, `CppGeneratorOptions` is populated with
`PublicIncludeRoots` (from `context.Includes`), `AdditionalIncludePaths`
(from `context.SearchPaths`), `IncludePatterns` (from
`context.IncludePatterns`), and `ExcludePatterns` (from
`context.ExcludePatterns`) in addition to the other cpp-specific options.
Comment thread
Malcolmnixon marked this conversation as resolved.
Outdated

**Program.PrintBanner** (private static): Prints the application banner (tool name,
version, copyright line, and a blank line).
Expand Down
Loading
Loading