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
93 changes: 47 additions & 46 deletions src/lib/Gen/hbs/Builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@
//

#include "Builder.hpp"
#include "lib/Support/Radix.hpp"
#include <lib/Lib/ConfigImpl.hpp>
#include <mrdocs/Metadata/DomCorpus.hpp>
#include <mrdocs/Support/Path.hpp>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
#include <fmt/format.h>
#include <filesystem>
Expand Down Expand Up @@ -71,11 +69,11 @@
}
}

Builder::

Check warning on line 72 in src/lib/Gen/hbs/Builder.cpp

View workflow job for this annotation

GitHub Actions / GCC 14: C++20

Build Warning - g++-14 - [-Wreorder]

g++-14 - when initialized here ([-Wreorder])

Check warning on line 72 in src/lib/Gen/hbs/Builder.cpp

View workflow job for this annotation

GitHub Actions / GCC 14: C++20 (Coverage)

Build Warning - g++-14 - [-Wreorder]

g++-14 - when initialized here ([-Wreorder])
Builder(
HandlebarsCorpus const& corpus,
std::function<void(OutputRef&, std::string_view)> escapeFn)
: domCorpus(corpus)

Check warning on line 76 in src/lib/Gen/hbs/Builder.cpp

View workflow job for this annotation

GitHub Actions / Apple-Clang

Build Warning - clang++ - [-Wreorder-ctor]

clang++ - field 'domCorpus' will be initialized after field 'escapeFn_' ([-Wreorder-ctor])

Check warning on line 76 in src/lib/Gen/hbs/Builder.cpp

View workflow job for this annotation

GitHub Actions / Clang 18: C++20

Build Warning - clang++-18 - [-Wreorder-ctor]

clang++-18 - field 'domCorpus' will be initialized after field 'escapeFn_' ([-Wreorder-ctor])
, escapeFn_(std::move(escapeFn))
{
namespace fs = std::filesystem;
Expand Down Expand Up @@ -145,27 +143,53 @@
helpers::registerAntoraHelpers(hbs_);
helpers::registerLogicalHelpers(hbs_);
helpers::registerContainerHelpers(hbs_);

// load templates
exp = forEachFile(layoutDir(), false,
[&](std::string_view pathName) -> Expected<void>
{
// Get template relative path
std::filesystem::path relPath = pathName;
relPath = relPath.lexically_relative(layoutDir());

// Skip non-handlebars files
MRDOCS_CHECK_OR(relPath.extension() == ".hbs", {});

// Load template contents
MRDOCS_TRY(std::string text, files::getFileText(pathName));

// Register template
this->templates_.emplace(relPath.generic_string(), text);
return {};
});
if (!exp)
{
exp.error().Throw();
}
}

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

Expected<std::string>
Expected<void>
Builder::
callTemplate(
std::ostream& os,
std::string_view name,
dom::Value const& context)
{
auto pathName = files::appendPath(layoutDir(), name);
MRDOCS_TRY(auto fileText, files::getFileText(pathName));
auto it = templates_.find(name);
MRDOCS_CHECK(it != templates_.end(), formatError("Template {} not found", name));
std::string_view fileText = it->second;
HandlebarsOptions options;
options.escapeFunction = escapeFn_;
Expected<std::string, HandlebarsError> exp =
hbs_.try_render(fileText, context, options);
OutputRef out(os);
Expected<void, HandlebarsError> exp =
hbs_.try_render_to(out, fileText, context, options);
if (!exp)
{
return Unexpected(Error(exp.error().what()));
}
return *exp;
return {};
}

//------------------------------------------------
Expand Down Expand Up @@ -215,56 +239,35 @@
}

template<class T>
Expected<std::string>
requires std::derived_from<T, Info> || std::same_as<T, OverloadSet>
Expected<void>
Builder::
operator()(T const& I)
operator()(std::ostream& os, T const& I)
{
auto const templateFile = fmt::format("index.{}.hbs", domCorpus.fileExtension);
std::string const templateFile =
std::derived_from<T, Info> ?
fmt::format("index.{}.hbs", domCorpus.fileExtension) :
fmt::format("index-overload-set.{}.hbs", domCorpus.fileExtension);
dom::Object ctx = createContext(I);

auto& config = domCorpus->config;
bool isSinglePage = !config->multipage;
if (config->embedded ||
isSinglePage)
{
return callTemplate(templateFile, ctx);
return callTemplate(os, templateFile, ctx);
}

auto const wrapperFile = fmt::format("wrapper.{}.hbs", domCorpus.fileExtension);
dom::Object wrapperCtx = createFrame(ctx);
wrapperCtx.set("contents", dom::makeInvocable([this, &I, templateFile](
wrapperCtx.set("contents", dom::makeInvocable([this, &I, templateFile, &os](
dom::Value const& options) -> Expected<dom::Value>
{
// Helper to write contents directly to stream
return callTemplate(templateFile, createContext(I));
}));
return callTemplate(wrapperFile, wrapperCtx);
}

Expected<std::string>
Builder::
operator()(OverloadSet const& OS)
{
auto const templateFile = fmt::format("index-overload-set.{}.hbs", domCorpus.fileExtension);
dom::Object ctx = createContext(OS);

auto& config = domCorpus->config;
bool isSinglePage = !config->multipage;
if (config->embedded ||
isSinglePage)
{
return callTemplate(templateFile, ctx);
}

auto const wrapperFile = fmt::format("wrapper.{}.hbs", domCorpus.fileExtension);
dom::Object wrapperCtx = createFrame(ctx);
wrapperCtx.set("contents", dom::makeInvocable([this, &OS, templateFile](
dom::Value const& options) -> Expected<dom::Value>
{
// Helper to write contents directly to stream
return callTemplate(templateFile, createContext(OS));
MRDOCS_TRY(callTemplate(os, templateFile, createContext(I)));
return {};
}));
return callTemplate(wrapperFile, wrapperCtx);
return callTemplate(os, wrapperFile, wrapperCtx);
}

Expected<void>
Expand Down Expand Up @@ -352,14 +355,12 @@
subdir);
}


// Define Builder::operator() for each Info type
#define DEFINE(T) template Expected<std::string> \
Builder::operator()<T>(T const&)

#define INFO(Type) DEFINE(Type##Info);
#define INFO(T) template Expected<void> Builder::operator()<T##Info>(std::ostream&, T##Info const&);
#include <mrdocs/Metadata/InfoNodesPascal.inc>

template Expected<void> Builder::operator()<OverloadSet>(std::ostream&, OverloadSet const&);

} // hbs
} // mrdocs
} // clang
13 changes: 6 additions & 7 deletions src/lib/Gen/hbs/Builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@
{
js::Context ctx_;
Handlebars hbs_;
std::map<std::string, std::string, std::less<>> templates_;
std::function<void(OutputRef&, std::string_view)> escapeFn_;

Check warning on line 37 in src/lib/Gen/hbs/Builder.hpp

View workflow job for this annotation

GitHub Actions / GCC 14: C++20

Build Warning - g++-14 - [-Wreorder]

g++-14 - 'std::function<void(clang::mrdocs::OutputRef&, std::basic_string_view<char>)> clang::mrdocs::hbs::Builder::escapeFn_' ([-Wreorder])

Check warning on line 37 in src/lib/Gen/hbs/Builder.hpp

View workflow job for this annotation

GitHub Actions / GCC 14: C++20 (Coverage)

Build Warning - g++-14 - [-Wreorder]

g++-14 - 'std::function<void(clang::mrdocs::OutputRef&, std::basic_string_view<char>)> clang::mrdocs::hbs::Builder::escapeFn_' ([-Wreorder])

std::string
getRelPrefix(std::size_t depth);

public:
HandlebarsCorpus const& domCorpus;

Check warning on line 43 in src/lib/Gen/hbs/Builder.hpp

View workflow job for this annotation

GitHub Actions / GCC 14: C++20

Build Warning - g++-14 - [-Wreorder]

g++-14 - 'clang::mrdocs::hbs::Builder::domCorpus' will be initialized after ([-Wreorder])

Check warning on line 43 in src/lib/Gen/hbs/Builder.hpp

View workflow job for this annotation

GitHub Actions / GCC 14: C++20 (Coverage)

Build Warning - g++-14 - [-Wreorder]

g++-14 - 'clang::mrdocs::hbs::Builder::domCorpus' will be initialized after ([-Wreorder])

explicit
Builder(
Expand All @@ -57,12 +58,9 @@
with the index template as the contents.
*/
template<class T>
Expected<std::string>
operator()(T const&);

/// @copydoc operator()(T const&)
Expected<std::string>
operator()(OverloadSet const&);
requires std::derived_from<T, Info> || std::same_as<T, OverloadSet>
Expected<void>
operator()(std::ostream& os, T const&);

/** Render the contents in the wrapper layout.

Expand Down Expand Up @@ -123,8 +121,9 @@

/** Render a Handlebars template from the templates directory.
*/
Expected<std::string>
Expected<void>
callTemplate(
std::ostream& os,
std::string_view name,
dom::Value const& context);

Expand Down
123 changes: 68 additions & 55 deletions src/lib/Gen/hbs/MultiPageVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,77 +17,90 @@ namespace clang {
namespace mrdocs {
namespace hbs {

template <class T>
requires std::derived_from<T, Info> || std::same_as<T, OverloadSet>
void
MultiPageVisitor::
writePage(
std::string_view text,
std::string_view filename)
operator()(T const& I0)
{
std::string path = files::appendPath(outputPath_, filename);
std::string dir = files::getParentDir(path);
auto exp = files::createDirectory(dir);
if (!exp)
{
exp.error().Throw();
}
std::ofstream os;
try
// If T is an OverloadSet, we make a copy for the lambda because
// these are temporary objects that don't live in the corpus.
// Otherwise, the lambda will capture a reference to the corpus Info.
auto Ref = [&I0] {
if constexpr (std::derived_from<T, Info>)
{
return std::ref(I0);
}
else if constexpr (std::same_as<T, OverloadSet>)
{
return OverloadSet(I0);
}
}();
ex_.async([this, Ref](Builder& builder)
{
os.open(path,
std::ios_base::binary |
std::ios_base::out |
std::ios_base::trunc // | std::ios_base::noreplace
T const& I = Ref;

// ===================================
// Open the output file
// ===================================
std::string path = files::appendPath(outputPath_, builder.domCorpus.getXref(I));
std::string dir = files::getParentDir(path);
if (auto exp = files::createDirectory(dir); !exp)
{
exp.error().Throw();
}
std::ofstream os;
try
{
os.open(path,
std::ios_base::binary |
std::ios_base::out |
std::ios_base::trunc // | std::ios_base::noreplace
);
os.write(text.data(), static_cast<std::streamsize>(text.size()));
}
catch(std::exception const& ex)
{
formatError(R"(std::ofstream("{}") threw "{}")", path, ex.what()).Throw();
}
}
if (!os.is_open()) {
formatError(R"(std::ofstream("{}") failed)", path)
.Throw();
}
}
catch (std::exception const& ex)
{
formatError(R"(std::ofstream("{}") threw "{}")", path, ex.what())
.Throw();
}

template<std::derived_from<Info> T>
void
MultiPageVisitor::
operator()(T const& I)
{
ex_.async([this, &I](Builder& builder)
{
if(const auto r = builder(I))
writePage(*r, builder.domCorpus.getXref(I));
else
r.error().Throw();
if constexpr(
// ===================================
// Generate the output
// ===================================
if (auto exp = builder(os, I); !exp)
{
exp.error().Throw();
}

// ===================================
// Traverse the symbol members
// ===================================
if constexpr (std::derived_from<T, Info>)
{
if constexpr(
T::isNamespace() ||
T::isRecord() ||
T::isEnum())
{
corpus_.traverseOverloads(I, *this);
}
}
else if constexpr (std::same_as<T, OverloadSet>)
{
// corpus_.traverse(I, *this);
corpus_.traverseOverloads(I, *this);
corpus_.traverse(I, *this);
}
});
}

void
MultiPageVisitor::
operator()(OverloadSet const& OS)
{
ex_.async([this, OS](Builder& builder)
{
if(const auto r = builder(OS))
writePage(*r, builder.domCorpus.getXref(OS));
else
r.error().Throw();
corpus_.traverse(OS, *this);
});
}

#define DEFINE(T) template void \
MultiPageVisitor::operator()<T>(T const&)

#define INFO(Type) DEFINE(Type##Info);
#define INFO(T) template void MultiPageVisitor::operator()<T##Info>(T##Info const&);
#include <mrdocs/Metadata/InfoNodesPascal.inc>

template void MultiPageVisitor::operator()<OverloadSet>(OverloadSet const&);

} // hbs
} // mrdocs
} // clang
16 changes: 2 additions & 14 deletions src/lib/Gen/hbs/MultiPageVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ class MultiPageVisitor
std::string_view outputPath_;
Corpus const& corpus_;

void
writePage(
std::string_view text,
std::string_view filename);

public:
MultiPageVisitor(
ExecutorGroup<Builder>& ex,
Expand All @@ -54,16 +49,9 @@ class MultiPageVisitor
respective tasks are also pushed to the executor group.

*/
template <std::derived_from<Info> T>
template <class T>
requires std::derived_from<T, Info> || std::same_as<T, OverloadSet>
void operator()(T const& I);

/** Push a task for the specified OverloadSet to the executor group.

If the OverloadSet object refers to other Info objects, their
respective tasks are also pushed to the executor group.

*/
void operator()(OverloadSet const& OS);
};

} // hbs
Expand Down
Loading
Loading