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: 0 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1157,21 +1157,6 @@ jobs:
path: demos-diff
retention-days: 30

# Generate a static roadmap page from the git-tracked issue index.
#
# Output location:
# - Writes `build/website/roadmap/index.html`.
# - The generator may copy `styles.css` into `build/website/` if it's missing,
# so the report can reference `../styles.css`.
# - Since the website is published from `build/website`, this becomes
# `/roadmap/` on the deployed site.
- name: Generate Roadmap Report
if: env.PUBLISH_WEBSITE == 'true'
run: |
set -euvx
cd ${{ github.workspace }}
python -m util.roadmap report -o build/website/roadmap

- name: Publish Website to GitHub Pages
if: env.PUBLISH_WEBSITE == 'true'
uses: peaceiris/actions-gh-pages@v3
Expand Down
61 changes: 61 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#
# Licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
#
# Official repository: https://github.com/cppalliance/mrdocs
#

# Codecov Configuration
# https://docs.codecov.com/docs/codecovyml-reference
#
# Patch Coverage Threshold Rationale
# ==================================
#
# We allow a small threshold for patch coverage NOT because reduced coverage
# is acceptable, but because some lines are inherently uncoverable and
# produce false positives:
#
# - Class declarations (e.g., `class Foo : public Bar`)
# - Struct declarations (e.g., `struct MyTest`)
# - Access specifiers (`public:`, `private:`)
# - Pure virtual method declarations
# - Opening/closing braces on their own lines
#
# These are not executable code - they don't generate machine instructions -
# yet coverage tools (gcov) report them as "uncovered lines."
#
# Preferred Solutions (in order)
# ------------------------------
#
# 1st best: Configure gcov/lcov to exclude declarations from coverage
# tracking entirely, so they never appear as uncovered lines.
# This would eliminate false positives at the source.
# Status: Not currently available/configured.
#
# 2nd best: Use an absolute threshold (e.g., "allow up to 5 uncovered lines")
# rather than a percentage. This would be precise and predictable.
# Status: Codecov only supports percentage-based thresholds.
#
# 3rd best: Use a percentage-based threshold (what we implement below).
# The threshold is set small enough that any real logic
# (conditionals, loops, function bodies, error paths) that isn't
# covered will still trigger a failure. Only trivial declaration
# noise should pass through.
#
# With typical PR sizes, a 90% patch target achieves a similar effect to
# allowing ~5 uncovered lines in a 50-line change.

coverage:
status:
# Project-wide coverage should not decrease
project:
default:
threshold: 1%

# Patch coverage: only applies to new/modified lines in the PR
patch:
default:
target: 90%
41 changes: 41 additions & 0 deletions include/mrdocs/Generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//
Expand All @@ -19,6 +20,7 @@
#include <mrdocs/Config.hpp>
#include <mrdocs/Corpus.hpp>
#include <mrdocs/Support/Error.hpp>
#include <memory>
#include <ostream>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -201,11 +203,50 @@ class MRDOCS_VISIBLE
@param outputPath The specified output path, which can be a directory or file.
@param extension The file extension to use for single-page output.
*/
MRDOCS_DECL
Expected<std::string>
getSinglePageFullPath(
std::string_view outputPath,
std::string_view extension);

/** Install a custom generator.

This function registers a generator with the global
generator registry, making it available for use.

Plugins can use this function to register custom
generators.

@par Thread Safety
This function is thread-safe and may be called
concurrently from multiple threads.

@return An error if a generator with the same id
already exists.

@param G The generator to install. Ownership is
transferred to the registry.
*/
MRDOCS_DECL
Expected<void>
installGenerator(std::unique_ptr<Generator> G);

/** Find a generator by its id.

@par Thread Safety
This function is thread-safe and may be called
concurrently from multiple threads.

@return A pointer to the generator, or `nullptr`
if no generator with the given id exists.

@param id The symbolic name of the generator.
The name must be an exact match, including case.
*/
MRDOCS_DECL
Generator const*
findGenerator(std::string_view id) noexcept;

} // mrdocs


Expand Down
5 changes: 5 additions & 0 deletions share/mrdocs/addons/generator/html/layouts/wrapper.html.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
{{#if page.hasDefaultStyles}}
<script>
(function(){
// Enable anchor visibility (hidden by default for graceful degradation)
document.querySelectorAll("a.mrdocs-anchor").forEach(function(el){
el.style.visibility = "visible";
});
// Copy permalink URL to clipboard on anchor click
document.addEventListener("click", function(e){
var anchor = e.target.closest && e.target.closest("a.mrdocs-anchor");
if (!anchor) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<h{{or level 3}}{{#if class}} class="{{{class}}}"{{/if}}{{#if id}} id="{{{id}}}"{{/if}}>
{{> @partial-block }}
{{#if id}}
<a class="mrdocs-anchor" href="#{{id}}" aria-label="Permalink">#</a>
{{#if @root.config.multipage}}
{{#unless @root.config.embedded}}
<a class="mrdocs-anchor" style="visibility:hidden" href="#{{id}}" aria-label="Permalink">#</a>
{{/unless}}
{{/if}}
{{/if}}
</h{{or level 3}}>
14 changes: 14 additions & 0 deletions src/lib/Support/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#include <lib/AST/ExtractDocComment.hpp>
#include <lib/Support/Chrono.hpp>
#include <lib/Support/GeneratorRegistryImpl.hpp>
#include <lib/Support/Path.hpp>
#include <mrdocs/Generator.hpp>
#include <mrdocs/Support/Error.hpp>
Expand Down Expand Up @@ -156,5 +158,17 @@ getSinglePageFullPath(
return fileName.str().str();
}

Expected<void>
installGenerator(std::unique_ptr<Generator> G)
{
return getGeneratorRegistryImpl().insert(std::move(G));
}

Generator const*
findGenerator(std::string_view id) noexcept
{
return getGeneratorRegistryImpl().find(id);
}

} // mrdocs

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#ifndef MRDOCS_API_GENERATORS_HPP
#define MRDOCS_API_GENERATORS_HPP
#ifndef MRDOCS_LIB_SUPPORT_GENERATORREGISTRY_HPP
#define MRDOCS_LIB_SUPPORT_GENERATORREGISTRY_HPP

#include <mrdocs/Platform.hpp>
#include <mrdocs/Generator.hpp>
Expand All @@ -22,11 +23,11 @@ namespace mrdocs {
/** A dynamic list of @ref Generator elements.
*/
class MRDOCS_VISIBLE
Generators
GeneratorRegistry
{
protected:
/// Construct an empty generator registry; only derived singletons create this.
Generators() noexcept = default;
GeneratorRegistry() noexcept = default;

public:
/// Pointer type for generator entries.
Expand All @@ -48,7 +49,7 @@ class MRDOCS_VISIBLE
*/
MRDOCS_DECL
virtual
~Generators() noexcept;
~GeneratorRegistry() noexcept;

/** Return an iterator to the beginning.
*/
Expand Down Expand Up @@ -78,13 +79,7 @@ class MRDOCS_VISIBLE
std::string_view name) const noexcept = 0;
};

/** Return a reference to the global Generators instance.
*/
MRDOCS_DECL
Generators const&
getGenerators() noexcept;

} // mrdocs


#endif // MRDOCS_API_GENERATORS_HPP
#endif // MRDOCS_LIB_SUPPORT_GENERATORREGISTRY_HPP
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#include "GeneratorsImpl.hpp"
#include "GeneratorRegistryImpl.hpp"
#include <mrdocs/Support/Report.hpp>
#include <llvm/ADT/STLExtras.h>

Expand All @@ -27,11 +28,11 @@ extern
std::unique_ptr<Generator>
makeHTMLGenerator();

Generators::
~Generators() noexcept = default;
GeneratorRegistry::
~GeneratorRegistry() noexcept = default;

void
GeneratorsImpl::
GeneratorRegistryImpl::
refresh_plist()
{
plist_.clear();
Expand All @@ -40,19 +41,20 @@ refresh_plist()
plist_.push_back(g.get());
}

GeneratorsImpl::
GeneratorsImpl()
GeneratorRegistryImpl::
GeneratorRegistryImpl()
{
insert(makeAdocGenerator());
insert(makeXMLGenerator());
insert(makeHTMLGenerator());
}

Generator const*
GeneratorsImpl::
GeneratorRegistryImpl::
find(
std::string_view id) const noexcept
{
std::lock_guard<std::mutex> lock(mutex_);
for (auto const& li : list_)
{
if(li->id() == id)
Expand All @@ -64,30 +66,34 @@ find(
}

Expected<void>
GeneratorsImpl::
GeneratorRegistryImpl::
insert(
std::unique_ptr<Generator> G)
{
MRDOCS_CHECK(find(G->id()) == nullptr, formatError("generator id=\"{}\" already exists", G->id()));
if (!G)
{
return Unexpected(formatError("cannot install null generator"));
}
std::lock_guard<std::mutex> lock(mutex_);
for (auto const& li : list_)
{
if (li->id() == G->id())
{
return Unexpected(formatError("generator id=\"{}\" already exists", G->id()));
}
}
list_.emplace_back(std::move(G));
refresh_plist();
return {};
}

//------------------------------------------------

GeneratorsImpl&
getGeneratorsImpl() noexcept
GeneratorRegistryImpl&
getGeneratorRegistryImpl() noexcept
{
static GeneratorsImpl impl;
static GeneratorRegistryImpl impl;
return impl;
}

Generators const&
getGenerators() noexcept
{
return getGeneratorsImpl();
}

} // mrdocs

Loading
Loading