Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 10 additions & 5 deletions docs/design/api-mark-vhdl/vhdl-emitter-gradual-disclosure.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,18 @@ index page.
**VhdlEmitterGradualDisclosure.EmitEntityPage** (private static): Writes a single
entity detail page.

- H1 entity name, summary, details, Generics table (Name/Type/Default/Description),
Ports table (Name/Direction/Type/Description), Architectures section (inline —
one bold entry per architecture with optional details paragraph).
- H1 entity name, `*Entity declared in \`{fileName}\`*` attribution paragraph,
summary, details, Generics section (H2 — table when generics are present,
`NoItemsPlaceholder` paragraph when empty), Ports table
(Name/Direction/Type/Description), Architectures section (inline — one bold
entry per architecture formatted as `**{name}** (\`{fileName}\`): {summary}`
with optional details paragraph).

**VhdlEmitterGradualDisclosure.EmitPackagePage** (private static): Writes a single
package detail page and calls `EmitSubprogramDetailPage` for each subprogram.

- H1 package name, summary, details, Types paragraphs, Constants paragraphs,
- H1 package name, `*Package declared in \`{fileName}\`*` attribution paragraph,
summary, details, Types paragraphs, Constants paragraphs,
Components as `**name** — summary` paragraphs, Subprograms section with links
to per-subprogram detail pages.

Expand All @@ -83,7 +87,8 @@ one `{packageName}/{subprogramName}.md` detail file.
### Dependencies

- **VhdlEmitter** (internal) — instantiates this class and supplies `Options` and
shared helpers (`GetSummary`, `DescriptionColumnHeader`, `NoDescriptionPlaceholder`).
shared helpers (`GetSummary`, `DescriptionColumnHeader`, `NoDescriptionPlaceholder`,
`NoItemsPlaceholder`).
- **IMarkdownWriterFactory** (ApiMarkCore) — used to create each per-file Markdown
writer.
- **VhdlAstModel** (internal) — consumes `VhdlFileModel`, `VhdlEntityDecl`,
Expand Down
15 changes: 10 additions & 5 deletions docs/design/api-mark-vhdl/vhdl-emitter-single-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,18 @@ file.
**VhdlEmitterSingleFile.EmitEntitySection** (private static): Writes the per-entity
block within the single-file output.

- H{depth+2} entity name, summary, details, optional Generics table (H{depth+3}),
optional Ports table (H{depth+3}), optional Architectures sub-section (H{depth+3},
one bold paragraph per architecture with optional details).
- H{depth+2} entity name, `*Entity declared in \`{fileName}\`*` attribution
paragraph, summary, details, Generics section (H{depth+3} — table when generics
are present, `NoItemsPlaceholder` paragraph when empty), optional Ports table
(H{depth+3}), optional Architectures sub-section (H{depth+3}, one bold paragraph
per architecture formatted as `**{name}** (\`{fileName}\`): {summary}` with
optional details).

**VhdlEmitterSingleFile.EmitPackageSection** (private static): Writes the
per-package block within the single-file output.

- H{depth+2} package name, summary, details, optional Types section (H{depth+3}),
- H{depth+2} package name, `*Package declared in \`{fileName}\`*` attribution
paragraph, summary, details, optional Types section (H{depth+3}),
optional Constants section (H{depth+3}), optional Components section (H{depth+3}),
then calls `EmitSubprogramSection` for each subprogram.

Expand All @@ -80,7 +84,8 @@ per-subprogram block within the single-file output.
### Dependencies

- **VhdlEmitter** (internal) — instantiates this class and supplies `Options` and
shared helpers (`GetSummary`, `DescriptionColumnHeader`, `NoDescriptionPlaceholder`).
shared helpers (`GetSummary`, `DescriptionColumnHeader`, `NoDescriptionPlaceholder`,
`NoItemsPlaceholder`).
- **IMarkdownWriterFactory** (ApiMarkCore) — used to create the single Markdown writer.
- **VhdlAstModel** (internal) — consumes `VhdlFileModel`, `VhdlEntityDecl`,
`VhdlArchitectureDecl`, and `VhdlPackageDecl` record types.
Expand Down
3 changes: 3 additions & 0 deletions src/ApiMark.Vhdl/VhdlEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ internal sealed class VhdlEmitter : IApiEmitter
/// <summary>Placeholder text for members without documentation.</summary>
internal const string NoDescriptionPlaceholder = "*No description provided.*";

/// <summary>Placeholder text for sections that have no items.</summary>
internal const string NoItemsPlaceholder = "*None.*";

/// <summary>Object-class keywords stripped from subprogram parameter types before display.</summary>
private static readonly HashSet<string> ObjectClassKeywords =
new(StringComparer.OrdinalIgnoreCase) { "SIGNAL", "VARIABLE", "CONSTANT", "FILE" };
Expand Down
53 changes: 36 additions & 17 deletions src/ApiMark.Vhdl/VhdlEmitterGradualDisclosure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,30 @@ internal void Emit(IMarkdownWriterFactory factory, EmitConfig config, IContext c
_ = config;
_ = context;

// Collect all entities, architectures, packages across all files
var allEntities = _fileModels.SelectMany(f => f.Entities).ToList();
var allArchitectures = _fileModels.SelectMany(f => f.Architectures).ToList();
var allPackages = _fileModels.SelectMany(f => f.Packages).ToList();
// Collect all entities, architectures (with source filename), packages across all files
var allEntities = _fileModels
.SelectMany(f => f.Entities.Select(e => (Entity: e, FileName: Path.GetFileName(f.FilePath))))
.ToList();
var allArchitectures = _fileModels
.SelectMany(f => f.Architectures.Select(a => (Arch: a, FileName: Path.GetFileName(f.FilePath))))
.ToList();
var allPackages = _fileModels
.SelectMany(f => f.Packages.Select(p => (Package: p, FileName: Path.GetFileName(f.FilePath))))
.ToList();

// Emit api.md index page
EmitApiIndexPage(factory, allEntities, allPackages);
EmitApiIndexPage(factory, allEntities.Select(t => t.Entity).ToList(), allPackages.Select(t => t.Package).ToList());

// Emit entity detail pages
foreach (var entity in allEntities)
foreach (var (entity, fileName) in allEntities)
{
EmitEntityPage(factory, entity, allArchitectures);
EmitEntityPage(factory, entity, fileName, allArchitectures);
}

// Emit package detail pages and per-subprogram detail files
foreach (var pkg in allPackages)
foreach (var (pkg, fileName) in allPackages)
{
EmitPackagePage(factory, pkg);
EmitPackagePage(factory, pkg, fileName);
}
}

Expand Down Expand Up @@ -94,18 +100,23 @@ private void EmitApiIndexPage(
/// </summary>
/// <param name="factory">Factory for creating the per-entity Markdown writer.</param>
/// <param name="entity">The entity declaration to emit.</param>
/// <param name="fileName">Base filename of the source file containing this entity declaration.</param>
/// <param name="allArchitectures">
/// All architecture declarations across all parsed files; filtered to those
/// whose entity name matches <paramref name="entity"/>.
/// </param>
private static void EmitEntityPage(
IMarkdownWriterFactory factory,
VhdlEntityDecl entity,
List<VhdlArchitectureDecl> allArchitectures)
string fileName,
List<(VhdlArchitectureDecl Arch, string FileName)> allArchitectures)
{
using var writer = factory.CreateMarkdown("", VhdlEmitter.SanitizeFileName(entity.Name));
writer.WriteHeading(1, entity.Name);

// Attribution: kind and source file
writer.WriteParagraph($"*Entity declared in `{fileName}`*");

var summary = VhdlEmitter.GetSummary(entity.Doc) ?? VhdlEmitter.NoDescriptionPlaceholder;
writer.WriteParagraph(summary);

Expand All @@ -115,9 +126,9 @@ private static void EmitEntityPage(
writer.WriteParagraph(details);
}

writer.WriteHeading(2, "Generics");
if (entity.Generics.Count > 0)
{
writer.WriteHeading(2, "Generics");
var headers = new[] { "Name", "Type", "Default", VhdlEmitter.DescriptionColumnHeader };
var rows = entity.Generics.Select(g => new[]
{
Expand All @@ -128,6 +139,10 @@ private static void EmitEntityPage(
});
writer.WriteTable(headers, rows);
}
else
{
writer.WriteParagraph(VhdlEmitter.NoItemsPlaceholder);
}

if (entity.Ports.Count > 0)
{
Expand All @@ -145,18 +160,18 @@ private static void EmitEntityPage(

// List architectures that implement this entity
var archsForEntity = allArchitectures
.Where(a => string.Equals(a.EntityName, entity.Name, StringComparison.OrdinalIgnoreCase))
.Where(t => string.Equals(t.Arch.EntityName, entity.Name, StringComparison.OrdinalIgnoreCase))
.ToList();
if (archsForEntity.Count > 0)
{
writer.WriteHeading(2, "Architectures");
foreach (var arch in archsForEntity)
foreach (var (arch, archFileName) in archsForEntity)
{
// Emit architecture summary as bold-name paragraph
// Emit architecture as: **name** (`filename`): summary or **name** (`filename`)
var archSummary = VhdlEmitter.GetSummary(arch.Doc);
writer.WriteParagraph(!string.IsNullOrEmpty(archSummary)
? $"**{arch.Name}**: {archSummary}"
: $"**{arch.Name}**");
? $"**{arch.Name}** (`{archFileName}`): {archSummary}"
: $"**{arch.Name}** (`{archFileName}`)");

// Emit extended architecture details as a follow-on paragraph if present
var archDetails = arch.Doc?.Details;
Expand All @@ -174,11 +189,15 @@ private static void EmitEntityPage(
/// </summary>
/// <param name="factory">Factory for creating Markdown writers for the package page and subprogram pages.</param>
/// <param name="pkg">The package declaration to emit.</param>
private static void EmitPackagePage(IMarkdownWriterFactory factory, VhdlPackageDecl pkg)
/// <param name="fileName">Base filename of the source file containing this package declaration.</param>
private static void EmitPackagePage(IMarkdownWriterFactory factory, VhdlPackageDecl pkg, string fileName)
{
using var writer = factory.CreateMarkdown("", VhdlEmitter.SanitizeFileName(pkg.Name));
writer.WriteHeading(1, pkg.Name);

// Attribution: kind and source file
writer.WriteParagraph($"*Package declared in `{fileName}`*");

// Emit package summary paragraph
var summary = VhdlEmitter.GetSummary(pkg.Doc);
writer.WriteParagraph(!string.IsNullOrEmpty(summary) ? summary : VhdlEmitter.NoDescriptionPlaceholder);
Expand Down
49 changes: 34 additions & 15 deletions src/ApiMark.Vhdl/VhdlEmitterSingleFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ internal void Emit(IMarkdownWriterFactory factory, EmitConfig config, IContext c

var depth = config.HeadingDepth;

var allEntities = _fileModels.SelectMany(f => f.Entities).ToList();
var allArchitectures = _fileModels.SelectMany(f => f.Architectures).ToList();
var allPackages = _fileModels.SelectMany(f => f.Packages).ToList();
var allEntities = _fileModels
.SelectMany(f => f.Entities.Select(e => (Entity: e, FileName: Path.GetFileName(f.FilePath))))
.ToList();
var allArchitectures = _fileModels
.SelectMany(f => f.Architectures.Select(a => (Arch: a, FileName: Path.GetFileName(f.FilePath))))
.ToList();
var allPackages = _fileModels
.SelectMany(f => f.Packages.Select(p => (Package: p, FileName: Path.GetFileName(f.FilePath))))
.ToList();

using var writer = factory.CreateMarkdown("", "api");

Expand All @@ -51,19 +57,19 @@ internal void Emit(IMarkdownWriterFactory factory, EmitConfig config, IContext c
if (allEntities.Count > 0)
{
writer.WriteHeading(depth + 1, "Entities");
foreach (var entity in allEntities)
foreach (var (entity, fileName) in allEntities)
{
EmitEntitySection(writer, entity, allArchitectures, depth);
EmitEntitySection(writer, entity, fileName, allArchitectures, depth);
}
}

// Packages section
if (allPackages.Count > 0)
{
writer.WriteHeading(depth + 1, "Packages");
foreach (var pkg in allPackages)
foreach (var (pkg, fileName) in allPackages)
{
EmitPackageSection(writer, pkg, depth);
EmitPackageSection(writer, pkg, fileName, depth);
}
}
}
Expand All @@ -74,6 +80,7 @@ internal void Emit(IMarkdownWriterFactory factory, EmitConfig config, IContext c
/// </summary>
/// <param name="writer">Markdown writer to emit into.</param>
/// <param name="entity">The entity declaration to emit.</param>
/// <param name="fileName">Base filename of the source file containing this entity declaration.</param>
/// <param name="allArchitectures">
/// All architecture declarations across all parsed files; filtered to those
/// whose entity name matches <paramref name="entity"/>.
Expand All @@ -82,11 +89,15 @@ internal void Emit(IMarkdownWriterFactory factory, EmitConfig config, IContext c
private static void EmitEntitySection(
IMarkdownWriter writer,
VhdlEntityDecl entity,
List<VhdlArchitectureDecl> allArchitectures,
string fileName,
List<(VhdlArchitectureDecl Arch, string FileName)> allArchitectures,
int depth)
{
writer.WriteHeading(depth + 2, entity.Name);

// Attribution: kind and source file
writer.WriteParagraph($"*Entity declared in `{fileName}`*");

var summary = VhdlEmitter.GetSummary(entity.Doc);
writer.WriteParagraph(!string.IsNullOrEmpty(summary) ? summary : VhdlEmitter.NoDescriptionPlaceholder);

Expand All @@ -96,9 +107,9 @@ private static void EmitEntitySection(
writer.WriteParagraph(details);
}

writer.WriteHeading(depth + 3, "Generics");
if (entity.Generics.Count > 0)
{
writer.WriteHeading(depth + 3, "Generics");
var headers = new[] { "Name", "Type", "Default", VhdlEmitter.DescriptionColumnHeader };
var rows = entity.Generics.Select(g => new[]
{
Expand All @@ -109,6 +120,10 @@ private static void EmitEntitySection(
});
writer.WriteTable(headers, rows);
}
else
{
writer.WriteParagraph(VhdlEmitter.NoItemsPlaceholder);
}

if (entity.Ports.Count > 0)
{
Expand All @@ -125,18 +140,18 @@ private static void EmitEntitySection(
}

var archsForEntity = allArchitectures
.Where(a => string.Equals(a.EntityName, entity.Name, StringComparison.OrdinalIgnoreCase))
.Where(t => string.Equals(t.Arch.EntityName, entity.Name, StringComparison.OrdinalIgnoreCase))
.ToList();
if (archsForEntity.Count > 0)
{
writer.WriteHeading(depth + 3, "Architectures");
foreach (var arch in archsForEntity)
foreach (var (arch, archFileName) in archsForEntity)
{
// Emit architecture summary as bold-name paragraph
// Emit architecture as: **name** (`filename`): summary or **name** (`filename`)
var archSummary = VhdlEmitter.GetSummary(arch.Doc);
writer.WriteParagraph(!string.IsNullOrEmpty(archSummary)
? $"**{arch.Name}**: {archSummary}"
: $"**{arch.Name}**");
? $"**{arch.Name}** (`{archFileName}`): {archSummary}"
: $"**{arch.Name}** (`{archFileName}`)");

// Emit extended architecture details as a follow-on paragraph if present
var archDetails = arch.Doc?.Details;
Expand All @@ -154,11 +169,15 @@ private static void EmitEntitySection(
/// </summary>
/// <param name="writer">Markdown writer to emit into.</param>
/// <param name="pkg">The package declaration to emit.</param>
/// <param name="fileName">Base filename of the source file containing this package declaration.</param>
/// <param name="depth">Base heading depth for the library root; package heading uses depth+2.</param>
private static void EmitPackageSection(IMarkdownWriter writer, VhdlPackageDecl pkg, int depth)
private static void EmitPackageSection(IMarkdownWriter writer, VhdlPackageDecl pkg, string fileName, int depth)
{
writer.WriteHeading(depth + 2, pkg.Name);

// Attribution: kind and source file
writer.WriteParagraph($"*Package declared in `{fileName}`*");

// Emit package summary paragraph
var summary = VhdlEmitter.GetSummary(pkg.Doc);
writer.WriteParagraph(!string.IsNullOrEmpty(summary) ? summary : VhdlEmitter.NoDescriptionPlaceholder);
Expand Down
Loading
Loading