summaryrefslogtreecommitdiff
path: root/source/slang/slang-doc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-doc.cpp')
-rw-r--r--source/slang/slang-doc.cpp389
1 files changed, 332 insertions, 57 deletions
diff --git a/source/slang/slang-doc.cpp b/source/slang/slang-doc.cpp
index fa3e11030..c72250122 100644
--- a/source/slang/slang-doc.cpp
+++ b/source/slang/slang-doc.cpp
@@ -3,6 +3,9 @@
#include "../core/slang-string-util.h"
+#include "slang-ast-builder.h"
+#include "slang-ast-print.h"
+
namespace Slang {
/* TODO(JS):
@@ -33,19 +36,23 @@ public:
};
};
+ // NOTE! Don't change order without fixing isBefore and isAfter
enum class MarkupType
{
None,
- BlockBefore, /// /** */ or /*! */.
- BlockAfter, /// /*!< */ or /**< */
+ BlockBefore, /// /** */ or /*! */.
LineBangBefore, /// //! Can be multiple lines
LineSlashBefore, /// /// Can be multiple lines
+ BlockAfter, /// /*!< */ or /**< */
LineBangAfter, /// //!< Can be multiple lines
LineSlashAfter, /// ///< Can be multiple lines
};
+ static bool isBefore(MarkupType type) { return Index(type) >= Index(MarkupType::BlockBefore) && Index(type) <= Index(MarkupType::LineSlashBefore); }
+ static bool isAfter(MarkupType type) { return Index(type) >= Index(MarkupType::BlockAfter); }
+
struct IndexRange
{
SLANG_FORCE_INLINE Index getCount() const { return end - start; }
@@ -60,8 +67,12 @@ public:
Before,
AfterParam, ///< Can have trailing , or )
AfterSemicolon, ///< Can have a trailing ;
+ AfterEnumCase, ///< Can have a , or before }
};
+ static bool isAfter(Location location) { return Index(location) >= Index(Location::AfterParam); }
+ static bool isBefore(Location location) { return location == Location::Before; }
+
struct FoundMarkup
{
void reset()
@@ -199,6 +210,7 @@ void DocMarkupExtractor::_addDeclRec(Decl* decl)
}
else
#endif
+
if (ContainerDecl* containerDecl = as<ContainerDecl>(decl))
{
// Add the container - which could be a class, struct, enum, namespace, extension, generic etc.
@@ -465,7 +477,7 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio
const TokenList& toks = *info.tokenList;
const Index tokIndex = info.declTokenIndex;
- Index direction = (location == Location::Before) ? -1 : 1;
+ Index direction = isBefore(location) ? -1 : 1;
const Index count = toks.m_tokens.getCount();
for (Index i = tokIndex; i >= 0 && i < count; i += direction)
@@ -474,6 +486,26 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio
switch (tok.type)
{
+ case TokenType::BlockComment:
+ case TokenType::LineComment:
+ {
+ if (openParensCount == 0 && openBracketCount == 0)
+ {
+ // Determine the markup type
+ const MarkupType markupType = findMarkupType(tok);
+ // If the location wanted is before and the markup is, we'll assume this is it
+ if (isBefore(location) && isBefore(markupType))
+ {
+ return i;
+ }
+ // If we are looking for enum cases, and the markup is after, we'll assume this is it
+ if (location == Location::AfterEnumCase && isAfter(markupType))
+ {
+ return i;
+ }
+ }
+ break;
+ }
case TokenType::LParent:
{
++openParensCount;
@@ -481,12 +513,12 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio
}
case TokenType::RBracket:
{
- openBracketCount += Index(location == Location::Before);
+ openBracketCount += Index(isBefore(location));
break;
}
case TokenType::LBracket:
{
- openBracketCount -= Index(location == Location::Before);
+ openBracketCount -= Index(isBefore(location));
break;
}
case TokenType::RParent:
@@ -507,7 +539,7 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio
}
case TokenType::Comma:
{
- if (location == Location::AfterParam)
+ if (location == Location::AfterParam || location == Location::AfterEnumCase)
{
return i + 1;
}
@@ -516,7 +548,7 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio
case TokenType::RBrace:
{
// If we haven't hit a candidate yet before hitting } it's not going to work
- if (location == Location::Before)
+ if (location == Location::Before || location == Location::AfterEnumCase)
{
return -1;
}
@@ -536,16 +568,6 @@ Index DocMarkupExtractor::_findStartIndex(const FindInfo& info, Location locatio
}
break;
}
- case TokenType::LineComment:
- case TokenType::BlockComment:
- {
- // We hit a comment this could be the markup
- if (location == Location::Before && openParensCount == 0 && openBracketCount == 0)
- {
- return i;
- }
- break;
- }
default: break;
}
}
@@ -600,13 +622,13 @@ SlangResult DocMarkupExtractor::_findMarkup(const FindInfo& info, Location locat
return SLANG_E_NOT_FOUND;
}
- const Index searchDirection = (location == Location::Before) ? -1 : 1;
+ const Index searchDirection = isBefore(location) ? -1 : 1;
// Get the type and flags
const MarkupType type = findMarkupType(toks[startIndex]);
const MarkupFlags flags = getFlags(type);
- const MarkupFlag::Enum requiredFlag = (location == Location::Before) ? MarkupFlag::Before : MarkupFlag::After;
+ const MarkupFlag::Enum requiredFlag = isBefore(location) ? MarkupFlag::Before : MarkupFlag::After;
if ((flags & requiredFlag) == 0)
{
return SLANG_E_NOT_FOUND;
@@ -714,6 +736,11 @@ SlangResult DocMarkupExtractor::_findMarkup(const FindInfo& info, const Location
SlangResult DocMarkupExtractor::_findMarkup(const FindInfo& info, Decl* decl, FoundMarkup& out)
{
+ if (auto enumCaseDecl = as<EnumCaseDecl>(decl))
+ {
+ Location locs[] = { Location::Before, Location::AfterEnumCase };
+ return _findMarkup(info, locs, SLANG_COUNT_OF(locs), out);
+ }
if (auto paramDecl = as<ParamDecl>(decl))
{
Location locs[] = { Location::Before, Location::AfterParam };
@@ -901,72 +928,320 @@ SlangResult DocMarkup::extract(ModuleDecl* moduleDecl, SourceManager* sourceMana
return context.extract(this, moduleDecl, sourceManager, sink);
}
-/* static */SlangResult DocumentationUtil::writeMarkdown(DocMarkup* markup, StringBuilder& out)
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DocMarkDownWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+
+struct DocMarkDownWriter
{
- for (const auto& entry : markup->getEntries())
+ typedef ASTPrinter::Part Part;
+ typedef ASTPrinter::PartPair PartPair;
+
+ struct Signature
{
- NodeBase* node = entry.m_node;
- Decl* decl = as<Decl>(node);
- if (!decl)
+ Part returnType;
+ List<PartPair> params;
+ Part name;
+ };
+
+ void write();
+
+ void writeCallable(const DocMarkup::Entry& entry, CallableDecl* callable);
+ void writeEnum(const DocMarkup::Entry& entry, EnumDecl* enumDecl);
+ void writeAggType(const DocMarkup::Entry& entry, AggTypeDecl* aggTypeDecl);
+
+ void writePreamble(const DocMarkup::Entry& entry);
+ void writeDescription(const DocMarkup::Entry& entry);
+
+ DocMarkDownWriter(DocMarkup* markup, ASTBuilder* astBuilder):
+ m_markup(markup),
+ m_astBuilder(astBuilder)
+ {
+ }
+
+ static void getSignature(const List<Part>& parts, Signature& outSig);
+
+ template <typename T>
+ void _appendAsBullets(FilteredMemberList<T>& in);
+
+ DocMarkup* m_markup;
+ ASTBuilder* m_astBuilder;
+ StringBuilder m_builder;
+};
+
+static void _appendAsSingleLine(const UnownedStringSlice& in, StringBuilder& out)
+{
+ List<UnownedStringSlice> lines;
+ StringUtil::calcLines(in, lines);
+
+ // Ideally we'd remove any extraneous whitespace, but for now just join
+ StringUtil::join(lines.getBuffer(), lines.getCount(), ' ', out);
+}
+
+template <typename T>
+void DocMarkDownWriter::_appendAsBullets(FilteredMemberList<T>& list)
+{
+ auto& out = m_builder;
+ for (auto element : list)
+ {
+ DocMarkup::Entry* paramEntry = m_markup->getEntry(element);
+
+ out << "* ";
+
+ Name* name = element->getName();
+ if (name)
{
- continue;
+ out << toSlice("_") << name->text << toSlice("_ ");
}
- // Skip these they will be output as part of their respective 'containers'
- if (as<ParamDecl>(decl) || as<EnumCaseDecl>(decl))
+ if (paramEntry)
{
- continue;
+ // Hmm, we'll want to make something multiline into a single line
+ _appendAsSingleLine(paramEntry->m_markup.getUnownedSlice(), out);
}
- if (CallableDecl* callableDecl = as<CallableDecl>(decl))
+ out << "\n";
+ }
+
+ out << toSlice("\n");
+}
+
+/* static */void DocMarkDownWriter::getSignature(const List<Part>& parts, Signature& outSig)
+{
+ const Index count = parts.getCount();
+ for (Index i = 0; i < count; ++i)
+ {
+ const auto& part = parts[i];
+ switch (part.type)
{
- out << entry.m_markup;
+ case Part::Type::ParamType:
+ {
+ PartPair pair;
+ pair.first = part;
+ if (parts[i + 1].type == Part::Type::ParamName)
+ {
+ pair.second = parts[i + 1];
+ i++;
+ }
+ outSig.params.add(pair);
+ break;
+ }
+ case Part::Type::ReturnType:
+ {
+ outSig.returnType = part;
+ break;
+ }
+ case Part::Type::DeclPath:
+ {
+ outSig.name = part;
+ break;
+ }
+ default: break;
+ }
+ }
+}
- // There's code to output sigs in the SemanticsVisitor - we probably need to extract that functionality
- // out so can be used here
+void DocMarkDownWriter::writeCallable(const DocMarkup::Entry& entry, CallableDecl* callableDecl)
+{
+ writePreamble(entry);
- // String declString = getDeclSignatureString(item);
+ auto& out = m_builder;
- auto params = callableDecl->getParameters();
- //const auto& returnType = callableDecl->returnType;
+ StringBuilder sigBuffer;
+ List<ASTPrinter::Part> parts;
+ ASTPrinter printer(m_astBuilder, ASTPrinter::OptionFlag::ParamNames, &parts);
- // Let's see if we can get markup on the parameters
- for (auto param : params)
- {
- DocMarkup::Entry* paramEntry = markup->getEntry(param);
+ printer.addDeclSignature(DeclRef<Decl>(callableDecl, nullptr));
- if (paramEntry)
- {
- out << paramEntry->m_markup;
+ Signature signature;
+ getSignature(parts, signature);
- auto type = param->getType();
+ const Index paramCount = signature.params.getCount();
- if (type)
- {
- out << type->toString();
- }
+ // Output the signature
+ {
+ // Extract the name
+ out << toSlice("# ") << printer.getPartSlice(signature.name) << toSlice("\n\n");
- Name* name = param->getName();
- if (name)
- {
- out << " ";
- out << name->text;
- }
- out << "\n\n";
+ out << toSlice("## Signature \n");
+ out << toSlice("```\n");
+ out << printer.getPartSlice(signature.returnType) << toSlice(" ");
+
+ out << printer.getPartSlice(signature.name);
+
+
+ if (paramCount > 0)
+ {
+ out << toSlice("(\n");
+
+ StringBuilder line;
+ for (Index i = 0; i < paramCount; ++i)
+ {
+ const auto& param = signature.params[i];
+ line.Clear();
+ // If we want to tab these over... we'll need to know how must space I have
+ line << " " << printer.getPartSlice(param.first);
+
+ Index indent = 25;
+ if (line.getLength() < indent)
+ {
+ line.appendRepeatedChar(' ', indent - line.getLength());
+ }
+ else
+ {
+ line.appendChar(' ');
}
+
+ line << printer.getPartSlice(param.second);
+ if (i < paramCount - 1)
+ {
+ line << ",\n";
+ }
+
+ out << line;
}
+
+ out << ");\n";
}
- else if (EnumDecl* enumDecl = as<EnumDecl>(decl))
+ else
+ {
+ out << toSlice("();\n");
+ }
+
+ out << "```\n\n";
+ }
+
+ // Only output params if there are any
+ if (paramCount)
+ {
+ out << "## Parameters\n\n";
+
+ auto params = callableDecl->getParameters();
+ _appendAsBullets(params);
+ }
+
+ writeDescription(entry);
+}
+
+void DocMarkDownWriter::writeEnum(const DocMarkup::Entry& entry, EnumDecl* enumDecl)
+{
+ writePreamble(entry);
+
+ auto& out = m_builder;
+
+ out << toSlice("# enum ");
+ Name* name = enumDecl->getName();
+ if (name)
+ {
+ out << name->text;
+ }
+ out << toSlice("\n\n");
+
+ out << toSlice("## Values \n\n");
+
+ auto cases = enumDecl->getMembersOfType<EnumCaseDecl>();
+ _appendAsBullets(cases);
+
+ writeDescription(entry);
+}
+
+void DocMarkDownWriter::writeAggType(const DocMarkup::Entry& entry, AggTypeDecl* aggTypeDecl)
+{
+ writePreamble(entry);
+
+ auto& out = m_builder;
+
+ // This could be lots of different things - struct/class/extension/interface/..
+
+ out << toSlice("# ");
+ if (as<StructDecl>(aggTypeDecl))
+ {
+ out << toSlice("struct ");
+ }
+ else if (as<ClassDecl>(aggTypeDecl))
+ {
+ out << toSlice("class ");
+ }
+ else
+ {
+ out << toSlice("?");
+ }
+
+ Name* name = aggTypeDecl->getName();
+ if (name)
+ {
+ out << name->text;
+ }
+ out << toSlice("\n\n");
+
+ out << "## Fields\n\n";
+
+ auto fields = aggTypeDecl->getMembersOfType<VarDecl>();
+ _appendAsBullets(fields);
+
+ writeDescription(entry);
+}
+
+void DocMarkDownWriter::writePreamble(const DocMarkup::Entry& entry)
+{
+ SLANG_UNUSED(entry);
+ auto& out = m_builder;
+
+ out << toSlice("\n");
+ out.appendRepeatedChar('-', 80);
+ out << toSlice("\n");
+}
+
+
+void DocMarkDownWriter::writeDescription(const DocMarkup::Entry& entry)
+{
+ auto& out = m_builder;
+
+ out << toSlice("\n## Description\n\n");
+ out << entry.m_markup;
+}
+
+void DocMarkDownWriter::write()
+{
+ for (const auto& entry : m_markup->getEntries())
+ {
+ NodeBase* node = entry.m_node;
+ Decl* decl = as<Decl>(node);
+ if (!decl)
+ {
+ continue;
+ }
+
+ // Skip these they will be output as part of their respective 'containers'
+ if (as<ParamDecl>(decl) || as<EnumCaseDecl>(decl))
{
+ continue;
+ }
+ if (CallableDecl* callableDecl = as<CallableDecl>(decl))
+ {
+ writeCallable(entry, callableDecl);
}
- else if (StructDecl* structDecl = as<StructDecl>(decl))
+ else if (EnumDecl* enumDecl = as<EnumDecl>(decl))
{
+ writeEnum(entry, enumDecl);
}
- else if (ClassDecl* classDecl = as<ClassDecl>(decl))
+ else if (AggTypeDecl* aggType = as<AggTypeDecl>(decl))
{
+ writeAggType(entry, aggType);
}
}
+}
+
+/* static */SlangResult DocumentationUtil::writeMarkdown(DocMarkup* markup, ASTBuilder* astBuilder, StringBuilder& out)
+{
+ // The ASTBuilder is needed in order to be able to create ast types that can then be printed.
+ // It is *assumed* here, that them being transient on this temporary ASTBuilder, doesn't mutate
+ // any of the nodes from the ASTBuilder/s for the things being documented
+
+ DocMarkDownWriter writer(markup, astBuilder);
+ writer.write();
+
+ Swap(out, writer.m_builder);
return SLANG_OK;
}